From db0d61decef3c288f5cb829db342acf805b1efec Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:11:54 -0500 Subject: [PATCH] Linux 2.0.36pre3 [ftp.uk.linux.org:/pub/linux/alan/...] Ok this took a bit longer than I intended. Im releasing 2.0.36pre3 now for two reasons 1. Its got the 5.0.20 AIC7xxx driver in it - this should help the adaptec afflicted 2. It has modular sound in it Now for the modular sound Im unsure if its a good idea or bad to switch to it for a 2.0.x kernel. Its always going to be available as an add on and I suspect be something most vendors will ship (bearing in mind the target markets are often not keen to recompile kernels). So I'd appreciate feedback on both if modular sound behaves for you (it should 8)) and also opinions on whether it should become a standard item. Apart from that all I have queued to do now are a few small bug fixes and to wait for the final version of the isdn updates. Those are pretty important as they should include code thats been certified with telco approval in some of the weird parts of the world (most of the EEC to start with) where code certification for the ISDN 'D' channel protocol is required for each card. If you have a new feature you desperately want for 2.0.36, then probably too late. If there are bug fixes floating around notin 2.0.36pre3 you think matter please send me them - I'd rather get duplicates than miss stuff. Finally - treat pre3 with a bit of care - its not as tested as the pre patches tend to be. Alan --- CREDITS | 5 + Documentation/Configure.help | 85 +- Documentation/sound/ALS007 | 40 + Documentation/sound/AWE32 | 125 + Documentation/sound/CS4232 | 23 + Documentation/sound/ESS1868 | 84 + Documentation/sound/MAD16 | 25 + Documentation/sound/MultiSound | 149 + Documentation/sound/OPL3 | 6 + Documentation/sound/OPL3-SA | 81 + Documentation/sound/Opti | 224 + Documentation/sound/Soundblaster | 41 + Documentation/sound/Tropez+ | 26 + Documentation/sound/Wavefront | 377 ++ Documentation/sound/es1370 | 62 + Documentation/sound/es1371 | 56 + Documentation/sound/mwave | 191 + Documentation/sound/sonicvibes | 73 + Documentation/sound/ultrasound | 30 + MAINTAINERS | 12 + Rules.make | 6 +- drivers/block/loop.c | 2 +- drivers/char/cyclades.c | 165 +- drivers/char/mem.c | 12 + drivers/isdn/Config.in | 10 +- drivers/isdn/pcbit/pcbit.h | 2 +- drivers/net/apricot.c | 12 +- drivers/net/plip.c | 28 +- drivers/net/shaper.c | 2 +- drivers/scsi/aha1542.c | 19 +- drivers/scsi/aic7xxx.c | 39 +- drivers/scsi/dtc.c | 6 +- drivers/scsi/wd7000.c | 4 +- drivers/sound/CHANGELOG | 52 +- drivers/sound/Config.in | 227 +- drivers/sound/Config.std | 17 - drivers/sound/Makefile | 394 +- drivers/sound/README.CONFIG | 75 + drivers/sound/README.FIRST | 7 + drivers/sound/README.blurb | 10 + drivers/sound/README.wavefront | 375 ++ drivers/sound/Readme | 55 +- drivers/sound/Readme.aedsp16 | 6 - drivers/sound/Readme.cards | 598 ++- drivers/sound/Readme.linux | 179 +- drivers/sound/Readme.modules | 151 +- drivers/sound/Readme.v30 | 140 - drivers/sound/ad1848.c | 3995 +++++++++-------- drivers/sound/ad1848_mixer.h | 136 +- drivers/sound/adlib_card.c | 68 +- drivers/sound/aedsp16.c | 869 ---- drivers/sound/audio.c | 1193 ++++-- drivers/sound/audio_syms.c | 25 + drivers/sound/bin2hex.c | 37 + drivers/sound/configure.c | 1602 ------- drivers/sound/coproc.h | 2 +- drivers/sound/cs4232.c | 597 +-- drivers/sound/dev_table.c | 973 ++--- drivers/sound/dev_table.h | 658 +-- drivers/sound/dm.h | 79 + drivers/sound/dmabuf.c | 2783 +++++------- drivers/sound/dmasound.c | 808 ++-- drivers/sound/es1370.c | 2776 ++++++++++++ drivers/sound/es1371.c | 3302 +++++++++++++++ drivers/sound/finetune.h | 4 +- drivers/sound/gus_card.c | 383 +- drivers/sound/gus_midi.c | 361 +- drivers/sound/gus_vol.c | 176 +- drivers/sound/gus_wave.c | 5888 ++++++++++++-------------- drivers/sound/hex2hex.c | 100 + drivers/sound/hex2hex.h | 112 - drivers/sound/ics2101.c | 380 +- drivers/sound/iwmem.h | 4 +- drivers/sound/legacy.h | 48 + drivers/sound/lowlevel/Config.in | 56 + drivers/sound/lowlevel/Config.tmpl | 5 - drivers/sound/lowlevel/Makefile | 31 +- drivers/sound/lowlevel/README | 13 - drivers/sound/lowlevel/aci.c | 107 +- drivers/sound/lowlevel/aedsp16.c | 1387 ++++++ drivers/sound/lowlevel/awe_compat.h | 190 + drivers/sound/lowlevel/awe_config.h | 145 + drivers/sound/lowlevel/awe_hw.h | 100 + drivers/sound/lowlevel/awe_version.h | 13 + drivers/sound/lowlevel/awe_voice.h | 490 +++ drivers/sound/lowlevel/awe_wave.c | 4581 ++++++++++++++++++++ drivers/sound/lowlevel/init.c | 26 - drivers/sound/lowlevel/lowlevel.h | 5 + drivers/sound/lowlevel/soundlow.c | 80 + drivers/sound/mad16.c | 1337 +++--- drivers/sound/maui.c | 734 ++-- drivers/sound/midi_ctrl.h | 6 +- drivers/sound/midi_syms.c | 36 + drivers/sound/midi_synth.c | 1068 +++-- drivers/sound/midi_synth.h | 3 +- drivers/sound/midibuf.c | 715 ++-- drivers/sound/mpu401.c | 2839 ++++++------- drivers/sound/msnd.c | 359 ++ drivers/sound/msnd.h | 260 ++ drivers/sound/msnd_classic.c | 1314 ++++++ drivers/sound/msnd_classic.h | 199 + drivers/sound/msnd_pinnacle.c | 1346 ++++++ drivers/sound/msnd_pinnacle.h | 255 ++ drivers/sound/opl3.c | 1926 +++++---- drivers/sound/opl3.h | 8 +- drivers/sound/opl3sa.c | 338 ++ drivers/sound/os.h | 45 +- drivers/sound/pas2_card.c | 572 +-- drivers/sound/pas2_midi.c | 412 +- drivers/sound/pas2_mixer.c | 534 ++- drivers/sound/pas2_pcm.c | 676 ++- drivers/sound/patmgr.c | 288 -- drivers/sound/pss.c | 1575 ++++--- drivers/sound/sb.h | 20 +- drivers/sound/sb_audio.c | 1830 ++++---- drivers/sound/sb_card.c | 154 +- drivers/sound/sb_common.c | 2042 ++++----- drivers/sound/sb_midi.c | 306 +- drivers/sound/sb_mixer.c | 738 ++-- drivers/sound/sb_mixer.h | 77 +- drivers/sound/sequencer.c | 3160 +++++++------- drivers/sound/sequencer_syms.c | 38 + drivers/sound/sgalaxy.c | 187 + drivers/sound/softoss.c | 1533 +++++++ drivers/sound/softoss.h | 161 + drivers/sound/softoss_rs.c | 133 + drivers/sound/sonicvibes.c | 2884 +++++++++++++ drivers/sound/sound_calls.h | 178 +- drivers/sound/sound_config.h | 172 +- drivers/sound/sound_core.c | 314 ++ drivers/sound/sound_firmware.c | 68 + drivers/sound/sound_firmware.h | 2 + drivers/sound/sound_switch.c | 598 --- drivers/sound/sound_syms.c | 62 + drivers/sound/sound_tables.h | 15 + drivers/sound/sound_timer.c | 503 ++- drivers/sound/soundcard.c | 1477 ++++--- drivers/sound/soundmodule.h | 31 + drivers/sound/soundvers.h | 4 +- drivers/sound/sscape.c | 1557 ++++--- drivers/sound/sys_timer.c | 413 +- drivers/sound/trix.c | 826 ++-- drivers/sound/uart401.c | 712 ++-- drivers/sound/uart6850.c | 445 +- drivers/sound/v_midi.c | 301 ++ drivers/sound/v_midi.h | 15 + drivers/sound/wavfront.c | 3617 ++++++++++++++++ drivers/sound/wf_midi.c | 982 +++++ drivers/sound/yss225.c | 317 ++ drivers/sound/yss225.h | 24 + fs/nfs/dir.c | 15 +- fs/nfs/proc.c | 10 + fs/pipe.c | 2 +- fs/smbfs/proc.c | 14 +- include/asm-i386/unistd.h | 2 + include/linux/awe_voice.h | 490 +++ include/linux/cyclades.h | 33 +- include/linux/fs.h | 1 + include/linux/if_wic.h | 102 - include/linux/kerneld.h | 2 + include/linux/rose.h | 1 - include/linux/sound.h | 13 + include/linux/soundcard.h | 543 ++- include/linux/wavefront.h | 670 +++ kernel/ksyms.c | 3 + scripts/Makefile | 11 +- 166 files changed, 56546 insertions(+), 27003 deletions(-) create mode 100644 Documentation/sound/ALS007 create mode 100644 Documentation/sound/AWE32 create mode 100644 Documentation/sound/CS4232 create mode 100644 Documentation/sound/ESS1868 create mode 100644 Documentation/sound/MAD16 create mode 100644 Documentation/sound/MultiSound create mode 100644 Documentation/sound/OPL3 create mode 100644 Documentation/sound/OPL3-SA create mode 100644 Documentation/sound/Opti create mode 100644 Documentation/sound/Soundblaster create mode 100644 Documentation/sound/Tropez+ create mode 100644 Documentation/sound/Wavefront create mode 100644 Documentation/sound/es1370 create mode 100644 Documentation/sound/es1371 create mode 100644 Documentation/sound/mwave create mode 100644 Documentation/sound/sonicvibes create mode 100644 Documentation/sound/ultrasound delete mode 100644 drivers/sound/Config.std create mode 100644 drivers/sound/README.CONFIG create mode 100644 drivers/sound/README.FIRST create mode 100644 drivers/sound/README.blurb create mode 100644 drivers/sound/README.wavefront delete mode 100644 drivers/sound/Readme.aedsp16 delete mode 100644 drivers/sound/Readme.v30 delete mode 100644 drivers/sound/aedsp16.c create mode 100644 drivers/sound/audio_syms.c create mode 100644 drivers/sound/bin2hex.c delete mode 100644 drivers/sound/configure.c create mode 100644 drivers/sound/dm.h create mode 100644 drivers/sound/es1370.c create mode 100644 drivers/sound/es1371.c create mode 100644 drivers/sound/hex2hex.c delete mode 100644 drivers/sound/hex2hex.h create mode 100644 drivers/sound/legacy.h create mode 100644 drivers/sound/lowlevel/Config.in delete mode 100644 drivers/sound/lowlevel/Config.tmpl delete mode 100644 drivers/sound/lowlevel/README create mode 100644 drivers/sound/lowlevel/aedsp16.c create mode 100644 drivers/sound/lowlevel/awe_compat.h create mode 100644 drivers/sound/lowlevel/awe_config.h create mode 100644 drivers/sound/lowlevel/awe_hw.h create mode 100644 drivers/sound/lowlevel/awe_version.h create mode 100644 drivers/sound/lowlevel/awe_voice.h create mode 100644 drivers/sound/lowlevel/awe_wave.c delete mode 100644 drivers/sound/lowlevel/init.c create mode 100644 drivers/sound/lowlevel/lowlevel.h create mode 100644 drivers/sound/lowlevel/soundlow.c create mode 100644 drivers/sound/midi_syms.c create mode 100644 drivers/sound/msnd.c create mode 100644 drivers/sound/msnd.h create mode 100644 drivers/sound/msnd_classic.c create mode 100644 drivers/sound/msnd_classic.h create mode 100644 drivers/sound/msnd_pinnacle.c create mode 100644 drivers/sound/msnd_pinnacle.h create mode 100644 drivers/sound/opl3sa.c delete mode 100644 drivers/sound/patmgr.c create mode 100644 drivers/sound/sequencer_syms.c create mode 100644 drivers/sound/sgalaxy.c create mode 100644 drivers/sound/softoss.c create mode 100644 drivers/sound/softoss.h create mode 100644 drivers/sound/softoss_rs.c create mode 100644 drivers/sound/sonicvibes.c create mode 100644 drivers/sound/sound_core.c create mode 100644 drivers/sound/sound_firmware.c create mode 100644 drivers/sound/sound_firmware.h delete mode 100644 drivers/sound/sound_switch.c create mode 100644 drivers/sound/sound_syms.c create mode 100644 drivers/sound/sound_tables.h create mode 100644 drivers/sound/soundmodule.h create mode 100644 drivers/sound/v_midi.c create mode 100644 drivers/sound/v_midi.h create mode 100644 drivers/sound/wavfront.c create mode 100644 drivers/sound/wf_midi.c create mode 100644 drivers/sound/yss225.c create mode 100644 drivers/sound/yss225.h create mode 100644 include/linux/awe_voice.h delete mode 100644 include/linux/if_wic.h create mode 100644 include/linux/sound.h create mode 100644 include/linux/wavefront.h diff --git a/CREDITS b/CREDITS index eb38b247c948..82257c788630 100644 --- a/CREDITS +++ b/CREDITS @@ -1496,6 +1496,11 @@ S: Schleiermacherstrasse 12 S: 90491 Nuernberg S: Germany +N: Andrew Veliath +E: andrewtv@usa.net +D: Turtle Beach MultiSound sound driver +S: USA + N: Dirk Verworner D: Co-author of german book ``Linux-Kernel-Programmierung'' D: Co-founder of Berlin Linux User Group diff --git a/Documentation/Configure.help b/Documentation/Configure.help index e836ee8c7676..ebda1d8f0124 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -4250,14 +4250,14 @@ CONFIG_SOUND to be extracted with "tar xzvf filename". ProAudioSpectrum 16 support -CONFIG_PAS +CONFIG_SOUND_PAS Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio 16 or Logitech SoundMan 16. Don't answer 'y' if you have some other card made by Media Vision or Logitech since they are not PAS16 compatible. SoundBlaster (SB, SBPro, SB16, clones) support -CONFIG_SB +CONFIG_SOUND_SB Answer "y" if you have an original SoundBlaster card made by Creative Labs or a 100% hardware compatible clone (like the Thunderboard or SM Games). If your card was in the list of supported @@ -4267,7 +4267,7 @@ CONFIG_SB SoundBlaster compatible. Generic OPL2/OPL3 FM synthesizer support -CONFIG_ADLIB +CONFIG_SOUND_ADLIB 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 @@ -4275,12 +4275,12 @@ CONFIG_ADLIB cards, however). Gravis Ultrasound support -CONFIG_GUS +CONFIG_SOUND_GUS Enable this option for any type of Gravis Ultrasound card, including the GUS or GUS MAX. MPU-401 support (NOT for SB16) -CONFIG_MPU401 +CONFIG_SOUND_MPU401 Be careful with this question. The MPU401 interface is supported by all soundcards. However, some natively supported cards have their own driver for MPU401. Enabling the MPU401 option with these cards @@ -4291,29 +4291,29 @@ CONFIG_MPU401 answer Y if you have a true MPU401 MIDI interface card. 6850 UART Midi support -CONFIG_UART6850 +CONFIG_SOUND_UART6850 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. PSS (ECHO-ADI2111) support -CONFIG_PSS +CONFIG_SOUND_PSS Answer Y only if you have Orchid SW32, Cardinal DSP16 or some other card based on the PSS chipset (AD1848 codec + ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). 16 bit sampling option of GUS (_NOT_ GUS MAX) -CONFIG_GUS16 +CONFIG_SOUND_GUS16 Answer Y if you have installed the 16 bit sampling daughtercard on your GUS. Answer N if you have a GUS MAX, since enabling this option disables GUS MAX support. GUS MAX support -CONFIG_GUSMAX +CONFIG_SOUND_GUSMAX Answer Y only if you have a Gravis Ultrasound MAX. Microsoft Sound System support -CONFIG_MSS +CONFIG_SOUND_MSS 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 @@ -4337,18 +4337,18 @@ CONFIG_MSS conflict. Ensoniq Soundscape support -CONFIG_SSCAPE +CONFIG_SOUND_SSCAPE Answer Y if you have a soundcard based on the Ensoniq SoundScape chipset. Such cards are being manufactured at least by Ensoniq, Spea and Reveal (Reveal makes also other cards). MediaTriX AudioTriX Pro support -CONFIG_TRIX +CONFIG_SOUND_TRIX Answer Y if you have the AudioTriX Pro sound card manufactured by MediaTrix. Support for MAD16 and/or Mozart based cards -CONFIG_MAD16 +CONFIG_SOUND_MAD16 Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi 82C928 or 82C929) audio interface chip. These chips are currently quite common so it's possible that many no-name cards @@ -4357,30 +4357,63 @@ CONFIG_MAD16 Reveal (some models) and Diamond (latest ones). Support for Crystal CS4232 based (PnP) cards -CONFIG_CS4232 +CONFIG_SOUND_CS4232 Enable this if you have a card based on the Crystal CS4232 chip set. Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers -CONFIG_MAUI +CONFIG_SOUND_MAUI Enable this option if you have a Turtle Beach Wave Front, Maui, or Tropez sound card. -/dev/dsp and /dev/audio support -CONFIG_AUDIO - Answering N disables /dev/dsp and /dev/audio, the A/D and D/A - converter devices. Answer N only if you know you will not need - the option. They are usually required. Answer Y. +Support for Turtle Beach MultiSound Classic, Tahiti, Monterey +CONFIG_SOUND_MSNDCLAS + Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or + Monterey (not for the Pinnacle or Fiji). This driver requires that + you have some firmware files located in /etc/sound. See + Documentation/sound/MultiSound for important information about this + driver. -MIDI interface support -CONFIG_MIDI - Answering N disables /dev/midixx devices and access to any MIDI - ports using /dev/sequencer and /dev/music. This option also affects - any MPU401 and/or General MIDI compatible devices. Answer Y. +Support for Turtle Beach MultiSound Pinnacle, Fiji +CONFIG_SOUND_MSNDPIN + Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji. + This driver requires that you have some firmware files located in + /etc/sound. See Documentation/sound/MultiSound for important + information about this driver. FM synthesizer (YM3812/OPL-3) support -CONFIG_YM3812 +CONFIG_SOUND_YM3812 Answer Y here, unless you know you will not need the option. +Ensoniq ES1370 based PCI soundcards +CONFIG_SOUND_ES1370 + Say Y or M if you have a PCI soundcard utilizing the Ensoniq + ES1370 chipset, such as Ensoniq's AudioPCI (non-97). To find + out if your soundcard uses an ES1370 without removing your + computer's cover, use cat /proc/pci and look for the PCI ID + 1274:5000. Since Ensoniq was bought by Creative Labs, + SoundBlaster 64/PCI models are either ES1370 or ES1371 based. + This driver differs slightly from OSS/Free, so PLEASE READ + Documentation/sound/es1370. + +Ensoniq ES1371 based PCI soundcards +CONFIG_SOUND_ES1371 + Say Y or M if you have a PCI soundcard utilizing the Ensoniq + ES1371 chipset, such as Ensoniq's AudioPCI97. To find out if + your soundcard uses an ES1371 without removing your computer's + cover, use cat /proc/pci and look for the PCI ID 1274:1371. Since + Ensoniq was bought by Creative Labs, SoundBlaster 64/PCI + models are either ES1370 or ES1371 based. This driver differs + slightly from OSS/Free, so PLEASE READ Documentation/sound/es1371. + +S3 SonicVibes based PCI soundcards +CONFIG_SOUND_SONICVIBES + Say Y or M if you have a PCI soundcard utilizing the S3 + SonicVibes chipset. To find out if your soundcard uses a + SonicVibes chip without removing your computer's cover, use + cat /proc/pci and look for the PCI ID 5333:CA00. This driver + differs slightly from OSS/Free, so PLEASE READ + Documentation/sound/sonicvibes. + Sun Audio support CONFIG_SUN_AUDIO This is support for the soundcards on Sun workstations. The code diff --git a/Documentation/sound/ALS007 b/Documentation/sound/ALS007 new file mode 100644 index 000000000000..69a264815ffa --- /dev/null +++ b/Documentation/sound/ALS007 @@ -0,0 +1,40 @@ +ALS-007 based sound cards +========================= + +Support for sound cards based around the Avance Logic ALS-007 chip is +included. The ALS-007 is a single chip PnP sound solution which is mostly +hardware compatible with the Sound Blaster 16 card, with most differences +occurring in the use of the mixer registers. For this reason the ALS-007 +code is integrated as part of the Sound Blaster 16 driver (adding only 800 +bytes to the SB16 driver). + +To use an ALS-007 sound card under Linux, enable the following options in the +sound configuration section of the kernel config: + - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support + - FM synthesizer (YM3812/OPL-3) support +Since the ALS-007 is a PnP card, the sound driver probably should be +compiled as a module, with the isapnptools used to wake up the sound card. +Set the "I/O base for SB", "Sound Blaster IRQ" and "Sound Blaster DMA" (8 bit - +either 0, 1 or 3) to the values used in your particular installation (they +should match the values used to configure the card using isapnp). The +ALS-007 does NOT implement 16 bit DMA, so the "Sound Blaster 16 bit DMA" +should be set to -1. If you wish to use the external MPU-401 interface on +the card, "MPU401 I/O base of SB16" and "SB MPU401 IRQ" should be set to +the appropriate values for your installation. (Note that the ALS-007 +requires a separate IRQ for the MPU-401, so don't specify -1 here). (Note +that the base port of the internal FM synth is fixed at 0x388 on the ALS007; +in any case the FM synth location cannot be set in the kernel configuration). + +The resulting sound driver will provide the following capabilities: + - 8 and 16 bit audio playback + - 8 and 16 bit audio recording + - Software selection of record source (line in, CD, FM, mic, master) + - Record and playback of midi data via the external MPU-401 + - Playback of midi data using inbuilt FM synthesizer + - Control of the ALS-007 mixer via any OSS-compatible mixer programs. + Controls available are Master (L&R), Line in (L&R), CD (L&R), + DSP/PCM/audio out (L&R), FM (L&R) and Mic in (mono). + +Jonathan Woithe +jwoithe@physics.adelaide.edu.au +30 March 1998 diff --git a/Documentation/sound/AWE32 b/Documentation/sound/AWE32 new file mode 100644 index 000000000000..6f37b887868a --- /dev/null +++ b/Documentation/sound/AWE32 @@ -0,0 +1,125 @@ + Installing and using Creative AWE midi sound under Linux. + +This documentation is devoted to the Creative Sound Blaster AWE32, AWE64 and +SB32. + +1) Make sure you have an ORIGINAL Creative SB32, AWE32 or AWE64 card. This is +important, because the driver works only with real Creative cards. + +2) If your card is NOT "Plug-n-Play" (I myself don't know Creative AWE non +plug'n'play cards however) then go to 5th step now. In the other case +proceed to step 3. + +3) You should obtain isapnptools. I looked through other PnP packages +for Linux, but all they are either in deep unstable beta/alpha releases or +they are much worse than isapnptools. In my case isapnptools were included in +a Linux distribution (Red Hat 5.0). If you also already have them then go to +step 4. + +The latest copy of isapnptools-1.15 is available from +ftp://ftp.demon.co.uk/pub/unix/linux/utils/ (I tested isapnptools-1.15.tgz) +You should gunzip/untar it to something like /usr/local/ +(cp isapnptools-1.15.tgz /usr/local/; cd /usr/local/; +tar -xzf isapnptools-1.15.tgz). + +Compile the package (make) and install it (make install). +If something goes wrong check the INSTALL file in isapnptools-1.15 directory. + +4) Now do a "pnpdump > /etc/isapnp.conf". File /etc/isapnp.conf will contain +info about PnP devices you may have. If you want you can read the manual page +about isapnp.conf file (man isapnp.conf). Most lines of your isapnp.conf file are +commented. You should uncomment lines which don't conflict with your +configuration. + +ATTENTION! Device Audio should have 1 IRQ, 2 DMA and 3 base I/O resources. +If you don't have such a configuration you should manually add the resources to +the isapnp.conf file. After editing I got these lines in the Audio device +section (I ripped out all the comments): + +"(CONFIGURE CTL0044/1132685 (LD 0 (INT 0 (IRQ 5 (MODE +E))) (DMA 0 (CHANNEL 1)) + (DMA 1 (CHANNEL 5)) (IO 0 (BASE 0x220)) (IO 1 (BASE 0x330)) (IO 2 (BASE 0x388)) + (ACT Y)))" + +(In your case CTL044/1132685 numbers may be other) + +Don't forget to uncomment (ACT Y)! + +The next device is the on-board IDE controller. You may enable it if you wish, +but it will not effect sound. + +Then WaveTable goes. For some reason Plug-n-Play detects only one I/O port, +but the wavetable needs THREE! My working string is: + +"(CONFIGURE CTL044/1132685 (LD 2 (IO 0 (BASE 0x0620)) (IO 1 (BASE 0x0A20)) +(IO 3 (BASE 0x0E20)) (ACT Y) ))" + +Resources 0x0620, 0x0A20 and 0x0E20 should work. Other on-board devices: +Gameport and StereoEnhance are not needed. In my case StereoEnhance didn't +even work! + +Now you can execute "isapnp /etc/isapnp.conf". No errors should be reported. +If you correctly installed isapnptools, then isapnp will run every boot time. + +5) Now you should recompile the kernel. I recommend using development kernels, +because the AWE32 driver is included in them. ATTENTION! In kernels 2.1.102, +2.1.103, 2.1.104-pre1 and 2.1.104 (not the others) the lowlevel sound driver +is not working. You should use the patch available at +http://members.xoom.com/yar. If you are using stable kernel releases +2.0.x (I am not sure about 2.0.34), then get the latest version (3.8s9) of +OSS/Free at http://members.xoom.com/yar/ossfree38s9-linux20x.tar.gz +and untar it in /usr/src/ (assuming you keep your kernel source in +/usr/src/linux). Then go to /usr/src/linux/ and view +the README file. That file contains info about kernel compilation and +installation. If you use kernel version 2.0.x or version <= 2.1.104 go +to substep a, on 2.1.105 or later go to step b. + +In "make (x,menu)config" select in "Sound": +"Sound card support", "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support", +"Generic OPL2/OPL3 FM synthesizer support" and "FM synthesizer (YM3812/OPL-3) +support" as (module). + +If you use kernel version 2.0.x or version <= 2.1.104 skip substep a, on +2.1.105 or later go through it. + +substep a: +In "make (x,menu)config" select in "Sound": +select "OSS sound modules" as (module) + +In "Additional low level sound drivers": +"Additional low level sound drivers", "AWE32 synth" as (module). +Select "Additional low level sound drivers" as [y] (or [*] (yes)) (If it is not +available as [y], select it as (module)) +Now recompile the kernel (make dep; make (b)zImage; make modules; +make modules_install) and boot new kernel. + +6) Now download awesfx program from +http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/index.html#Latest. Compile it. +Copy sfxload program to /bin (or /sbin if you wish). To enable AWE midi +synthesis you should also get the sound bank file for general midi from +http://members.xoom.com/yar/synthgm.sbk.gz. Copy it to +/usr and gunzip it there. + +7) Edit /etc/rc.d/rc.local, inserting at the end of the file: + +modprobe sound +insmod uart401 +insmod sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330 +insmod awe_wave +sfxload /usr/synthfm.sbk + +(on io=0xaaa irq=b.... you should use your own settings) +That will enable the Sound Blaster and AWE wave synthesis. + +To play midi files you should get one of these programs: + +Playmidi 2.4 or higher: http://playmidi.openprojects.net +Drvmidi 4.2.b: http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/index.html#Latest + +(These are available at all major Linux FTP sites and may already be + in your distribution) + +If something goes wrong please e-mail me. All comments and suggestions are +welcome. + + Yaroslav Rosomakho (alons55@dialup.ptt.ru) + http://members.xoom.com/yar diff --git a/Documentation/sound/CS4232 b/Documentation/sound/CS4232 new file mode 100644 index 000000000000..7d6af7a5c1c2 --- /dev/null +++ b/Documentation/sound/CS4232 @@ -0,0 +1,23 @@ +To configure the Crystal CS423x sound chip and activate its DSP functions, +modules may be loaded in this order: + + modprobe sound + insmod ad1848 + insmod uart401 + insmod cs4232 io=* irq=* dma=* dma2=* + +This is the meaning of the parameters: + + io--I/O address of the Windows Sound System (normally 0x534) + irq--IRQ of this device + dma and dma2--DMA channels (DMA2 may be 0) + +On some cards, the board attempts to do non-PnP setup, and fails. If you +have problems, use Linux' PnP facilities. + +To get MIDI facilities add + + insmod opl3 io=* + +where "io" is the I/O address of the OPL3 synthesizer. This will be shown +in /proc/sys/pnp and is normally 0x388. diff --git a/Documentation/sound/ESS1868 b/Documentation/sound/ESS1868 new file mode 100644 index 000000000000..d508349dcd10 --- /dev/null +++ b/Documentation/sound/ESS1868 @@ -0,0 +1,84 @@ +Documentation for the ESS1868F AudioDrive PnP sound card + +The ESS1868 sound card is a PnP ESS1688-compatible 16-bit sound card. + +Notes about configuring the sound card: + + * The ESS1868 does not allow use of a 16-bit DMA, thus DMA 0, 1, 2, and 3 + may only be used. + + * isapnptools version 1.14 does work with ESS1868. Earlier versions might + not. + + * Sound support MUST be compiled as MODULES, not statically linked + into the kernel. + +For configuring the sound card's I/O addresses, IRQ and DMA, here is a +sample copy of the isapnp.conf directives regarding the ESS1868: + +(CONFIGURE ESS1868/-1 (LD 1 +(IO 0 (BASE 0x0220)) +(IO 1 (BASE 0x0388)) +(IO 2 (BASE 0x0330)) +(DMA 0 (CHANNEL 1)) +(INT 0 (IRQ 5 (MODE +E))) +(ACT Y) +)) + +(for a full working isapnp.conf file, remember the +(ISOLATE) +(IDENTIFY *) +at the beginning and the +(WAITFORKEY) +at the end.) + +In this setup, the main card I/O is 0x0220, FM synthesizer is 0x0388, and +the MPU-401 MIDI port is located at 0x0330. IRQ is IRQ 5, DMA is channel 1. + +After configuring the sound card via isapnp, to use the card you must load +the sound modules with the proper I/O information. Here is my setup: + +# ESS1868F AudioDrive initialization + +/sbin/insmod sound +/sbin/insmod uart401 +/sbin/insmod sb io=0x220 irq=5 dma=1 dma16=-1 +/sbin/insmod mpu401 io=0x330 +/sbin/insmod opl3 io=0x388 +/sbin/insmod v_midi + +opl3 is the FM synthesizer--I have not tried the SoftOSS wavetable +synthesizer yet, but I assume it would work as well. Also, doing: +/sbin/insmod opl3 +/sbin/insmod adlib_card io=0x388 +works, but I believe the sound quality is a bit distorted when playing MIDI +files. + +When using the above setup, my /proc/sound gives the following: + +OSS/Free:3.8s2++-971130 +Load type: Driver loaded as a module +Kernel: Linux scitus.dyn.ml.org 2.1.104 #1 SMP Sun May 24 11:04:27 EDT 1998 i486 +Config options: 0 + +Installed drivers: + +Card config: + +Audio devices: +0: ESS ES1688 AudioDrive (rev 11) (3.1) + +Synth devices: +0: Yamaha OPL-3 + +Midi devices: +0: Loopback MIDI Port 1 +1: Loopback MIDI Port 2 + +Timers: +0: System clock + +Mixers: +0: Sound Blaster + + diff --git a/Documentation/sound/MAD16 b/Documentation/sound/MAD16 new file mode 100644 index 000000000000..319b1e17ae49 --- /dev/null +++ b/Documentation/sound/MAD16 @@ -0,0 +1,25 @@ +From: Shaw Carruthers + +I have been using mad16 sound for some time now with no problems, current +kernel 2.1.89 + +lsmod shows: + +mad16 5176 0 +sb 22044 0 [mad16] +uart401 5576 0 [mad16 sb] +ad1848 14176 1 [mad16] +sound 61928 0 [mad16 sb uart401 ad1848] + +.config has: + +CONFIG_SOUND=m +CONFIG_ADLIB=m +CONFIG_MAD16=m +CONFIG_YM3812=m + +modules.conf has: + +alias char-major-14 mad16 +options sb mad16=1 +options mad16 io=0x530 irq=7 dma=0 dma16=1 && /usr/local/bin/aumix -w 15 -p 20 -m 0 -1 0 -2 0 -3 0 -i 0 diff --git a/Documentation/sound/MultiSound b/Documentation/sound/MultiSound new file mode 100644 index 000000000000..7bc08fe7d83e --- /dev/null +++ b/Documentation/sound/MultiSound @@ -0,0 +1,149 @@ +Getting Firmware +~~~~~~~~~~~~~~~~ + +See the end of this document on how to obtain and create the necessary +firmware files. + + +Supported Features +~~~~~~~~~~~~~~~~~~ + +Currently digital audio and mixer functionality is supported. (memory +mapped digital audio is not yet supported). MultiSound support is +fully modularized, and can only be used as modules: + +msnd - MultiSound base (requires soundcore) +msnd_classic - Base audio/mixer support for Classic, Monetery and + Tahiti cards +msnd_pinnacle - Base audio/mixer support for Pinnacle and Fiji cards + + +Important Notes - Read Before Using +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* The firmware files are not included (may change in future). You +must obtain these images from Turtle Beach (they are included in the +MultiSound Development Kits), and place them in /etc/sound for +example, and give the full paths in the Linux configuration. Please +note these files must be binary files, not assembler. + +* You need the following information to use this driver: the card's +I/O base (i.e. 0x250), the IRQ (i.e. 5), and the shared memory area +(i.e. 0xd8000). + +* Probing is not currently implemented, and only the msnd_classic +driver will actually program the card's IRQ and SMA locations; the +msnd_pinnacle is primarily for use with the card in PnP mode, however +it should work if you know what the card's resource values are (this +will change in the future). + +* Note the Turtle Beach Pinnacle card contains a Kurzweil MA-1 +synthesizer with an MPU compatible interface, which should work with +the mpu401 module. You must know the resource values of the MA-1. + + +Examples +~~~~~~~~ + +* If you have a MultiSound Classic/Monterey/Tahiti: + +insmod soundcore +insmod msnd +insmod msnd_classic io=0x290 irq=7 mem=0xd0000 + +* If you have a MultiSound Pinnacle: + +insmod soundcore +insmod msnd +insmod msnd_pinnacle io=0x210 irq=5 mem=0xd8000 + +* To use the MPU-compatible Kurzweil synth on the Pinnacle, add the +following: + +insmod sound +insmod mpu401 io=0x330 irq=9 + + +msnd_classic, msnd_pinnacle Required Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the following options are not given, the module will not load. +Examine the kernel message log for informative error messages. +WARNING--probing isn't supported so try to make sure you have the +correct shared memory area, otherwise you may experience problems. + +io I/O base of DSP, e.g. io=0x210 +irq IRQ number, e.g. irq=5 +mem Shared memory area, e.g. mem=0xd8000 + + +msnd_classic, msnd_pinnacle Additional Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +fifosize The digital audio FIFOs, in kilobytes. The default is + 64kB (two FIFOs are allocated, so this uses up 128kB). + +calibrate_signal Setting this to one calibrates the ADCs to the + signal, zero calibrates to the card (defaults + to zero). + + +Obtaining and Creating Firmware Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + For the Classic/Tahiti/Monterey + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Download to /tmp and unzip the following file from Turtle Beach: + + ftp://ftp.tbeach.com/pub/tbs/pinn/msndvkit.zip + +When unzipped, unzip the file named MsndFiles.zip. Then copy the +following firmware files to /etc/sound (note the file renaming): + + cp DSPCODE/MSNDINIT.BIN /etc/sound/msndinit.bin + cp DSPCODE/MSNDPERM.REB /etc/sound/msndperm.bin + +When configuring the Linux kernel, specify /etc/sound/msndinit.bin and +/etc/sound/msndperm.bin for the two firmware files (Linux kernel +versions older than 2.2 do not ask for firmware paths, and are +hardcoded to /etc/sound). + + + For the Pinnacle/Fiji + ~~~~~~~~~~~~~~~~~~~~~ + +Download to /tmp and unzip the following file from Turtle Beach (be +sure to use the entire URL; some have had trouble navigating to the +URL): + + ftp://ftp.tbeach.com/oldpub/tbs/pinn/pnddk100.zip + +Put the following lines into a file named conv.l (between the start +and end lines): + +-- conv.l start -- +%% +[ \n\t,\r] ; +\;.* ; +DB ; +[0-9A-Fa-f]+H { int n; sscanf(yytext, "%xH", &n); printf("%c", n); } +-- conv.l end -- + +Then, compile the conv program with GNU make with the following +command: + + make LEX=flex LOADLIBES=-lfl conv + +This should give you an executable named conv. Now, we create the +binary firmware files by doing the following conversion (assuming the +archive unpacked into a directory named PINNDDK): + + ./conv < PINNDDK/dspcode/pndspini.asm > /etc/sound/pndspini.bin + ./conv < PINNDDK/dspcode/pndsperm.asm > /etc/sound/pndsperm.bin + +The conv (and conv.l) program is not needed after conversion and can +be safely deleted. Then, when configuring the Linux kernel, specify +/etc/sound/pndspini.bin and /etc/sound/pndsperm.bin for the two +firmware files (Linux kernel versions older than 2.2 do not ask for +firmware paths, and are hardcoded to /etc/sound). diff --git a/Documentation/sound/OPL3 b/Documentation/sound/OPL3 new file mode 100644 index 000000000000..2468ff827688 --- /dev/null +++ b/Documentation/sound/OPL3 @@ -0,0 +1,6 @@ +A pure OPL3 card is nice and easy to configure. Simply do + +insmod opl3 io=0x388 + +Change the I/O address in the very unlikely case this card is differently +configured diff --git a/Documentation/sound/OPL3-SA b/Documentation/sound/OPL3-SA new file mode 100644 index 000000000000..153082c463de --- /dev/null +++ b/Documentation/sound/OPL3-SA @@ -0,0 +1,81 @@ +OPL3-SA1 sound driver (opl3sa.o) + +--- +Note: This howto only describes how to setup the OPL3-SA1 chip; this info +does not apply to the SA2, SA3, or SA4. Contact hannu@opensound.com for +the support details of these other SAx chips. +--- + +The Yamaha OPL3-SA1 sound chip is usually found built into motherboards, and +it's a decent little chip offering a WSS mode, a SB Pro emulation mode, MPU401 +and OPL3 FM Synth capabilities. + +You can enable inclusion of the driver via CONFIG_SOUND_OPL3SA1=m, or +CONFIG_SOUND_OPL3SA1=y through 'make config/xconfig/menuconfig'. + +You'll need to know all of the relevant info (irq, dma, and io port) for the +chip's WSS mode, since that is the mode the kernel sound driver uses, and of +course you'll also need to know about where the MPU401 and OPL3 ports and +irq's are if you want to use those. + +Here's the skinny on how to load it as a module: + + modprobe opl3sa io=0x530 irq=11 dma=0 dma2=1 mpu_io=0x330 mpu_irq=5 + +Module options in detail: + + io: This is the WSS's port base. + irq: This is the WSS's irq. + dma: This is the WSS's dma line. In my BIOS setup screen this was + listed as "WSS Play DMA" + dma2: This is the WSS's secondary dma line. My BIOS calls it the + "WSS capture DMA" + + mpu_io: This is the MPU401's port base. + mpu_irq: This is the MPU401's irq. + +If you'd like to use the OPL3 FM Synthesizer, make sure you enable +CONFIG_YM3812 (in 'make config'). That'll build the opl3.o module. + +Then a simple 'insmod opl3 io=0x388', and you now have FM Synth. + +You can also use the SoftOSS software synthesizer instead of the builtin OPL3. +Here's how: + +Say 'y' or 'm' to "SoftOSS software wave table engine" in make config. + +If you said yes, the software synth is availible once you boot your new +kernel. + +If you chose to build it as a module, just insmod the resulting softoss2.o + +A 'cat /dev/sndstat' with all the above options should look similar to this: + + OSS/Free:3.8s2++-971130 + Load type: Driver loaded as a module + Kernel: Linux iniquity 2.1.105 #145 Mon Jun 8 11:40:47 MST 1998 i586 + Config options: 0 + + Installed drivers: + + Card config: + + Audio devices: + 0: MSS audio codec (CS4231) (DUPLEX) + + Synth devices: + 0: Yamaha OPL-3 + 1: SoftOSS + + Midi devices: + 0: OPL3-SA (MPU401) + + Timers: + 0: System clock + 1: MSS audio codec (CS4231) + + Mixers: + 0: MSS audio codec (CS4231) + +Questions? Comments? + diff --git a/Documentation/sound/Opti b/Documentation/sound/Opti new file mode 100644 index 000000000000..34b0f433127d --- /dev/null +++ b/Documentation/sound/Opti @@ -0,0 +1,224 @@ +Support for the OPTi 82C931 chip +-------------------------------- +Note: parts of this README file apply also to other +cards that use the mad16 driver. + +Some items in this README file are based on features +added to the sound driver after Linux-2.1.91 was out. +By the time of writing this I do not know which official +kernel release will include these features. +Please do not report inconsistencies on older Linux +kernels. + +The OPTi 82C931 is supported in its non-PnP mode. +Usually you do not need to set jumpers, etc. The sound driver +will check the card status and if it is required it will +force the card into a mode in which it can be programmed. + +If you have another OS installed on your computer it is recommended +that Linux and the other OS use the same resources. + +Also, it is recommended that resources specified in /etc/conf.modules +and resources specified in /etc/isapnp.conf agree. + +Compiling the sound driver +-------------------------- +I highly recommend that you build a modularized sound driver. +This document does not cover a sound-driver which is built in +the kernel. + +Sound card support should be enabled as a module (chose m). +Answer 'm' for these items: + Generic OPL2/OPL3 FM synthesizer support (CONFIG_ADLIB) + Microsoft Sound System support (CONFIG_MSS) + Support for OPTi MAD16 and/or Mozart based cards (CONFIG_MAD16) + FM synthesizer (YM3812/OPL-3) support (CONFIG_YM3812) + +The configuration menu may ask for addresses, IRQ lines or DMA +channels. If the card is used as a module the module loading +options will override these values. + +For the OPTi 931 you can answer 'n' to: + Support MIDI in older MAD16 based cards (requires SB) (CONFIG_MAD16_OLDCARD) +If you do need MIDI support in a Mozart or C928 based card you +need to answer 'm' to the above question. In that case you will +also need to answer 'm' to: + '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' (CONFIG_SB) + +Go on and compile your kernel and modules. Install the modules. Run depmod -a. + +Using isapnptools +----------------- +In most systems with a PnP BIOS you do not need to use isapnp. The +initialization provided by the BIOS is sufficient for the driver +to pick up the card and continue initialization. + +If that fails, or if you have other PnP cards, you need to use isapnp +to initialize the card. +This was tested with isapnptools-1.11 but I recommend that you use +isapnptools-1.13 (or newer). Run pnpdump to dump the information +about your PnP cards. Then edit the resulting file and select +the options of your choice. This file is normally installed as +/etc/isapnp.conf. + +The driver has one limitation with respect to I/O port resources: +IO3 base must be 0x0E0C. Although isapnp allows other ports, this +address is hard-coded into the driver. + +Using kmod and autoloading the sound driver +------------------------------------------- +Comment: as of linux-2.1.90 kmod is replacing kerneld. +The config file '/etc/conf.modules' is used as before. + +This is the sound part of my /etc/conf.modules file. +Following that I will explain each line. + +alias mixer0 mad16 +alias audio0 mad16 +alias midi0 mad16 +alias synth0 opl3 +options sb mad16=1 +options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 +options opl3 io=0x388 +post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 + +If you have an MPU daughtercard or onboard MPU you will want to add to the +"options mad16" line - eg + +options mad16 irq=5 dma=0 dma16=3 io=0x530 mpu_io=0x330 mpu_irq=9 + +To set the I/O and IRQ of the MPU. + + +Explain: + +alias mixer0 mad16 +alias audio0 mad16 +alias midi0 mad16 +alias synth0 opl3 + +When any sound device is opened the kernel requests auto-loading +of char-major-14. There is a built-in alias that translates this +request to loading the main sound module. The main sound module +contains only common code which is needed by all the sound drivers, +and the driver for /dev/sndstat. + +The sound module in its turn will request loading of a sub-driver +for mixer, audio, midi or synthesizer device. The first 3 are +supported by the mad16 driver. The synth device is supported +by the opl3 driver. + +There is currently no way to autoload the sound device driver +if more than one card is installed. + +options sb mad16=1 + +This is left for historical reasons. If you enable the +config option 'Support MIDI in older MAD16 based cards (requires SB)' +or if you use an older mad16 driver it will force loading of the +SoundBlaster driver. This option tells the SB driver not to look +for a SB card but to wait for the mad16 driver. + +options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 +options opl3 io=0x388 + +post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 + +This sets resources and options for the mad16 and opl3 drivers. +I use two DMA channels (only one is required) to enable full duplex. +joystick=1 enables the joystick port. cdtype=0 disables the cd port. +You can also set mpu_io and mpu_irq in the mad16 options for the +uart401 driver. + +This tells modprobe to run /sbin/ad1848_mixer_reroute after +mad16 is successfully loaded and initialized. The source +for ad1848_mixer_reroute is appended to the end of this readme +file. It is impossible for the sound driver to know the actual +connections to the mixer. The 3 inputs intended for cd, synth +and line-in are mapped to the generic inputs line1, line2 and +line3. This program reroutes these mixer channels to their +right names (note the right mapping depends on the actual sound +card that you use). +The numeric parameters mean: + 14=line1 8=cd - reroute line1 to the CD input. + 15=line2 3=synth - reroute line2 to the synthesizer input. + 16=line3 6=line - reroute line3 to the line input. +For reference on other input names look at the file +/usr/include/linux/soundcard.h. + +Using a joystick +----------------- +You must enable a joystick in the mad16 options. (also +in /etc/isapnp.conf if you use it). +Tested with regular analog joysticks. + +A CDROM drive connected to the sound card +----------------------------------------- +The 82C931 chip has support only for secondary ATAPI cdrom. +(cdtype=8). Loading the mad16 driver resets the C931 chip +and if a cdrom was already mounted it may cause a complete +system hang. Do not use the sound card if you have an alternative. +If you do use the sound card it is important that you load +the mad16 driver (use "modprobe mad16" to prevent auto-unloading) +before the cdrom is accessed the first time. + +Using the sound driver built-in to the kernel may help here, but... +Most new systems have a PnP BIOS and also two IDE controllers. +The IDE controller on the sound card may be needed only on older +systems (which have only one IDE controller) but these systems +also do not have a PnP BIOS - requiring isapnptools and a modularized +driver. + +Known problems +-------------- +1. See the section on "A CDROM drive connected to the sound card". + +2. On my system the codec cannot capture companded sound samples. + (eg., recording from /dev/audio). When any companded capture is + requested I get stereo-16 bit samples instead. Playback of + companded samples works well. Apparently this problem is not common + to all C931 based cards. I do not know how to identify cards that + have this problem. + +Source for ad1848_mixer_reroute.c +--------------------------------- +#include +#include +#include + +static char *mixer_names[SOUND_MIXER_NRDEVICES] = + SOUND_DEVICE_LABELS; + +int +main(int argc, char **argv) { + int val, from, to; + int i, fd; + + fd = open("/dev/mixer", O_RDWR); + if(fd < 0) { + perror("/dev/mixer"); + return 1; + } + + for(i = 2; i < argc; i += 2) { + from = atoi(argv[i-1]); + to = atoi(argv[i]); + + if(to == SOUND_MIXER_NONE) + fprintf(stderr, "%s: turning off mixer %s\n", + argv[0], mixer_names[to]); + else + fprintf(stderr, "%s: rerouting mixer %s to %s\n", + argv[0], mixer_names[from], mixer_names[to]); + + val = from << 8 | to; + + if(ioctl(fd, SOUND_MIXER_PRIVATE2, &val)) { + perror("AD1848 mixer reroute"); + return 1; + } + } + + return 0; +} + diff --git a/Documentation/sound/Soundblaster b/Documentation/sound/Soundblaster new file mode 100644 index 000000000000..ad4cb5567109 --- /dev/null +++ b/Documentation/sound/Soundblaster @@ -0,0 +1,41 @@ +modprobe sound +insmod uart401 +insmod sb ... + +This loads the driver for the Sound Blaster and assorted clones. Cards that +are covered by other drivers should not be using this driver. + +The Sound Blaster module takes the following arguments + +io I/O address of the Sound Blaster chip +irq IRQ of the Sound Blaster chip +dma 8-bit DMA channel for the Sound Blaster +dma16 16-bit DMA channel for SB16 and equivalent cards +mpu_io I/O for MPU chip if present + +mad16=1 Set when loading this as part of the MAD16 setup only +trix=1 Set when loading this as part of the Audiotrix setup only +pas2=1 Set when loading this as part of the Pas2 setup only +sm_games=1 Set if you have a Logitech soundman games +acer=1 Set this to detect cards in some ACER notebooks +mwave_bug=1 Set if you are trying to use this driver with mwave (see on) + +You may well want to load the opl3 driver for synth music on most SB and +clone SB devices + +insmod opl3 io=0x388 + +Using Mwave + +To make this driver work with Mwave you must set mwave_bug. You also need +to warm boot from DOS/Windows with the required firmware loaded under this +OS. IBM are being difficult about documenting how to load this firmware. + +Avance Logic ALS007 + +This card is supported; see the separate file ALS007 for full details. + +Avance Logic ALS100 + +This card is supported; setup should be as for a standard Sound Blaster 16. +The driver will identify the audio device as a "Sound Blaster 16 (ALS-100)". diff --git a/Documentation/sound/Tropez+ b/Documentation/sound/Tropez+ new file mode 100644 index 000000000000..b93a6b734fc0 --- /dev/null +++ b/Documentation/sound/Tropez+ @@ -0,0 +1,26 @@ +From: Paul Barton-Davis + +Here is the configuration I use with a Tropez+ and my modular +driver: + + alias char-major-14 wavefront + alias synth0 wavefront + alias mixer0 cs4232 + alias audio0 cs4232 + pre-install wavefront modprobe "-k" "cs4232" + post-install wavefront modprobe "-k" "opl3" + options wavefront io=0x200 irq=9 + options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 + options opl3 io=0x388 + +Things to note: + + the wavefront options "io" and "irq" ***MUST*** match the "synthio" + and "synthirq" cs4232 options. + + you can do without the opl3 module if you don't + want to use the OPL/[34] synth on the soundcard + + the opl3 io parameter is conventionally not adjustable. + +Please see drivers/sound/README.wavefront for more details. diff --git a/Documentation/sound/Wavefront b/Documentation/sound/Wavefront new file mode 100644 index 000000000000..4a2ad4b9eced --- /dev/null +++ b/Documentation/sound/Wavefront @@ -0,0 +1,377 @@ + An OSS/Free Driver for WaveFront soundcards + (Turtle Beach Maui, Tropez, Tropez Plus) + + Paul Barton-Davis, July 1998 + +Driver Status +------------- + +Requires: Kernel 2.1.106 or later + +As of 7/3/1998, this driver is currently in *BETA* state. This means +that it compiles and runs, and that I use it on my system (Linux +2.1.106) with some reasonably demanding applications and uses. I +believe the code is approaching an initial "finished" state that +provides bug-free support for the Tropez Plus. + +Please note that to date, the driver has ONLY been tested on a Tropez +Plus. I would very much like to hear (and help out) people with Tropez +and Maui cards, since I think the driver can support those cards as +well. + +Finally, the driver has not been tested as a static (non-modular) part +of the kernel. Alan Cox's good work in modularizing OSS/Free for Linux +makes this rather unnecessary. + +Some Questions +-------------- + +********************************************************************** +0) What does this driver do that the maui driver did not ? +********************************************************************** + +* can fully initialize a WaveFront card from cold boot - no DOS + utilities needed +* working patch/sample/program loading and unloading (the maui + driver didn't document how to make this work, and assumed + user-level preparation of the patch data for writing + to the board. ick.) +* full user-level access to all WaveFront commands +* for the Tropez Plus, (primitive) control of the YSS225 FX processor +* Virtual MIDI mode supported - 2 MIDI devices accessible via the + WaveFront's MPU401/UART emulation. One + accesses the WaveFront synth, the other accesses the + external MIDI connector. Full MIDI read/write semantics + for both devices. +* OSS-compliant /dev/sequencer interface for the WaveFront synth, + including native and GUS-format patch downloading. +* semi-intelligent patch management (prototypical at this point) + + +********************************************************************** +1) What to do about MIDI interfaces ? +********************************************************************** + +The Tropez Plus (and perhaps other WF cards) can in theory support up +to 2 physical MIDI interfaces. One of these is connected to the +ICS2115 chip (the WaveFront synth itself) and is controlled by +MPU/UART-401 emulation code running as part of the WaveFront OS. The +other is controlled by the CS4232 chip present on the board. However, +physical access to the CS4232 connector is difficult, and it is +unlikely (though not impossible) that you will want to use it. + +An older version of this driver introduced an additional kernel config +variable which controlled whether or not the CS4232 MIDI interface was +configured. Because of Alan Cox's work on modularizing the sound +drivers, and now backporting them to 2.0.34 kernels, there seems to be +little reason to support "static" configuration variables, and so this +has been abandoned in favor of *only* module parameters. Specifying +"mpuio" and "mpuirq" for the cs4232 parameter will result in the +CS4232 MIDI interface being configured; leaving them unspecified will +leave it unconfigured (and thus unusable). + +BTW, I have heard from one Tropez+ user that the CS4232 interface is +more reliable than the ICS2115 one. I have had no problems with the +latter, and I don't have the right cable to test the former one +out. Reports welcome. + +********************************************************************** +2) Why does line XXX of the code look like this .... ? +********************************************************************** + +Either because its not finished yet, or because you're a better coder +than I am, or because you don't understand some aspect of how the card +or the code works. + +I absolutely welcome comments, criticisms and suggestions about the +design and implementation of the driver. + +********************************************************************** +3) What files are included ? +********************************************************************** + + drivers/sound/README.wavefront -- this file + drivers/sound/wavefront.patch -- patches for the 2.1.106 sound driver +s + needed to make the rest of this work + drivers/sound/wavfront.c -- the driver + drivers/sound/ys225.h -- data declarations for FX config + drivers/sound/ys225.c -- data definitions for FX config + drivers/sound/wf_midi.c -- the "uart401" driver + to support virtual MIDI mode. + include/wavefront.h -- the header file + Documentation/sound/Tropez+ -- short docs on configuration + +********************************************************************** +4) How do I compile/install/use it ? +********************************************************************** + +PART ONE: install the source code into your sound driver directory + + cd + tar -zxvf + +PART TWO: apply the patches + + cd drivers/sound + patch < wavefront.patch + +PART THREE: configure your kernel + + cd + make xconfig (or whichever config option you use) + + - choose YES for Sound Support + - choose MODULE (M) for OSS Sound Modules + - choose MODULE(M) to Generic OPL2/OPL3 support + - choose MODULE(M) to YM3812/OPL3 support + - choose MODULE(M) for WaveFront support + - choose MODULE(M) for CS4232 support + + - choose "N" for everything else (unless you have other + soundcards you want support for) + + + make dep + make boot + . + . + . + + make modules + . + . + . + make modules_isntall + +Here's my autoconf.h SOUND section: + +/* + * Sound + */ +#define CONFIG_SOUND 1 +#undef CONFIG_SOUND_OSS +#define CONFIG_SOUND_OSS_MODULE 1 +#undef CONFIG_SOUND_PAS +#undef CONFIG_SOUND_SB +#undef CONFIG_SOUND_ADLIB +#define CONFIG_SOUND_ADLIB_MODULE 1 +#undef CONFIG_SOUND_GUS +#undef CONFIG_SOUND_MPU401 +#undef CONFIG_SOUND_PSS +#undef CONFIG_SOUND_MSS +#undef CONFIG_SOUND_SSCAPE +#undef CONFIG_SOUND_TRIX +#undef CONFIG_SOUND_MAD16 +#undef CONFIG_SOUND_WAVEFRONT +#define CONFIG_SOUND_WAVEFRONT_MODULE 1 +#undef CONFIG_SOUND_CS4232 +#define CONFIG_SOUND_CS4232_MODULE 1 +#undef CONFIG_SOUND_MAUI +#undef CONFIG_SOUND_SGALAXY +#undef CONFIG_SOUND_OPL3SA1 +#undef CONFIG_SOUND_SOFTOSS +#undef CONFIG_SOUND_YM3812 +#define CONFIG_SOUND_YM3812_MODULE 1 +#undef CONFIG_SOUND_VMIDI +#undef CONFIG_SOUND_UART6850 +/* + * Additional low level sound drivers + */ +#undef CONFIG_LOWLEVEL_SOUND + +************************************************************ +6) How do I configure my card ? +************************************************************ + +You need to edit /etc/conf.modules. Here's mine (edited to show the +relevant details): + + # Sound system + alias char-major-14 wavefront + alias synth0 wavefront + alias mixer0 cs4232 + pre-install wavefront modprobe "-k" "cs4232" + post-install wavefront modprobe "-k" "adlib_card" + options wavefront io=0x200 irq=9 + options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 + options adlib_card io=0x388 + +Things to note: + + the wavefront options "io" and "irq" ***MUST*** match the "synthio" + and "synthirq" cs4232 options. + + you can do without the adlib_card module if you don't + want to use the OPL/[34] synth on the soundcard + + the adlib_card io parameter is conventionally not adjustable. + In theory, any not-in-use IO port address would work, but + just use 0x388 and stick with the crowd. + +********************************************************************** +7) What about firmware ? +********************************************************************** + +Turtle Beach have not given me permission to distribute their firmware +for the ICS2115. However, if you have a WaveFront card, then you +almost certainly have the firmware, and if not, its freely available +on their website, at: + + http://www.tbeach.com/tbs/downloads/scardsdown.htm#tropezplus + +The file is called WFOS2001.MOT (for the Tropez+). + +This driver, however, doesn't use the pure firmware as distributed, +but instead relies on a somewhat processed form of it. You can +generate this very easily. Following an idea from Andrew Veliath's +Pinnacle driver, the following flex program will generate the +processed version: + +---- cut here ------------------------- +%option main +%% +^S[28].*\r$ printf ("%c%.*s", yyleng-1,yyleng-1,yytext); +<> { fputc ('\0', stdout); return; } +\n {} +. {} +---- cut here ------------------------- + +To use it, put the above in file (say, ws.l) compile it like this: + + shell> flex -ows.c ws.l + shell> cc -o ws ws.c + +and then use it like this: + + ws < my-copy-of-the-oswf.mot-file > /etc/sound/wavefront.os + +If you put it somewhere else, you'll always have to use the wf_ospath +module parameter (see below) or alter the source code. + +********************************************************************** +7) How do I get it working ? +********************************************************************** + +Optionally, you can reboot with the "new" kernel (even though the only +changes have really been made to a module). + +Then, as root do: + + modprobe wavefront + +You should get something like this, directly if you're on a console, and in +/var/log/messages: + + WaveFront: firmware 1.20 already loaded. + +or + + WaveFront: no response to firmware probe, assume raw. + +then: + + WaveFront: waiting for memory configuration ... + WaveFront: hardware version 1.64 + WaveFront: available DRAM 8191k + WaveFront: 332 samples used (266 real, 13 aliases, 53 multi), 180 empty + WaveFront: 128 programs slots in use + WaveFront: 256 patch slots filled, 142 in use + +The whole process takes about 16 seconds, the longest waits being +after reporting the hardware version (during the firmware download), +and after reporting program status (during patch status inquiry). Its +shorter (about 10 secs) if the firmware is already loaded (i.e. only +warm reboots since the last firmware load). + +The "available DRAM" line will vary depending on how much added RAM +your card has. Mine has 8MB. + +Next, check /dev/sndstat, which on my machine says: +--------------------------------------------------------------------- +OSS/Free:3.8s2++-971130 +Load type: Driver loaded as a module +Kernel: Linux bd 2.1.106 #12 SMP Fri Jul 3 00:37:34 EDT 1998 i486 +Config options: 0 + +Installed drivers: + +Card config: + +Audio devices: +0: Crystal audio controller (CS4232) (DUPLEX) + +Synth devices: +0: Turtle Beach WaveFront +1: Yamaha OPL-3 + +Midi devices: +0: WaveFront Internal MIDI +1: WaveFront External MIDI + +Timers: +0: System clock +1: Crystal audio controller (CS4232) + +Mixers: +0: Crystal audio controller (CS4232) +----------------------------------------------------------- + +To check basically functionality, use play(1) or splay(1) to send a +.WAV or other audio file through the audio portion. Then use playmidi +to play a General MIDI file. Try the "-D 0" to hear the +difference between sending MIDI to the WaveFront and using the OPL/3, +which is the default (I think ...). If you have an external synth(s) +hooked to the soundcard, you can use "-e" to route to the +external synth(s) (in theory, -D 1 should work as well, but I think +there is a bug in playmidi which prevents this from doing what it +should). + +********************************************************************** +8) What are the module parameters ? +********************************************************************** + +Its best to read wavefront.c for this, but here is a summary: + +integers: + wf_raw - if set, ignore apparent presence of firmware + loaded onto the ICS2115, reset the whole + board, and initialize it from scratch. (default = 0) + + fx_raw - if set, always initialize the YSS225 processor + on the Tropez plus. (default = 1) + + < The next 4 are basically for kernel hackers to allow + tweaking the driver for testing purposes. > + + wf_short_wait_count - loop counter used when waiting for + status conditions on the board. This + is CPU-specific. After this many + loops, the driver will sleep. + The default is 5000. I have a 66Mhz 486. + + wf_sleep_interval - the driver sleeps for + HZ/wf_sleep_interval seconds per sleep. + The default is 50. + + wf_sleep_tries - the number of times the driver will sleep + when waiting for a status condition on the + board. The default is 100 (2 secs, if + wf_sleep_interval is 50). + + wf_debug_default - debugging flags. See sound/wavefront.h + for WF_DEBUG_* values. Default is zero. + Setting this allows you to debug the + driver during module installation. +strings: + wf_ospath - path to get to the pre-processed OS firmware. + (default: /etc/sound/wavefront.os) + +********************************************************************** +9) Who should I contact if I have problems? +********************************************************************** + +Just me: Paul Barton-Davis + + + diff --git a/Documentation/sound/es1370 b/Documentation/sound/es1370 new file mode 100644 index 000000000000..8c37df406d90 --- /dev/null +++ b/Documentation/sound/es1370 @@ -0,0 +1,62 @@ +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +This soundcard does not have any hardware MIDI synthesizer; +MIDI synthesis has to be done in software. To allow this +the driver/soundcard supports two PCM (/dev/dsp) interfaces. +The second one goes to the mixer "synth" setting and supports +only a limited set of sampling rates (44100, 22050, 11025, 5512). +By setting lineout to 1 on the driver command line +(eg. insmod es1370 lineout=1) it is even possible on some +cards to convert the LINEIN jack into a second LINEOUT jack, thus +making it possible to output four independent audio channels! + +There is a freely available software package that allows +MIDI file playback on this soundcard called Timidity. +See http://www.cgs.fi/~tt/timidity/. + + + +Thomas Sailer +sailer@ife.ee.ethz.ch diff --git a/Documentation/sound/es1371 b/Documentation/sound/es1371 new file mode 100644 index 000000000000..a6b1c0257e95 --- /dev/null +++ b/Documentation/sound/es1371 @@ -0,0 +1,56 @@ +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +This soundcard does not have any hardware MIDI synthesizer; +MIDI synthesis has to be done in software. To allow this +the driver/soundcard supports two PCM (/dev/dsp) interfaces. + +There is a freely available software package that allows +MIDI file playback on this soundcard called Timidity. +See http://www.cgs.fi/~tt/timidity/. + + + +Thomas Sailer +sailer@ife.ee.ethz.ch diff --git a/Documentation/sound/mwave b/Documentation/sound/mwave new file mode 100644 index 000000000000..b0f847cd6387 --- /dev/null +++ b/Documentation/sound/mwave @@ -0,0 +1,191 @@ + How to try to survive an IBM Mwave under Linux SB drivers + + +* IBM refuses to provide documentation. If anyone can ever find out what + MWD50430.EXE actually does to load firmware then this comedy could go + away. + +* If you'd like to ask IBM why they don't release Mwave information. + phone IBM (425-556-8822) and ask them why they still haven't + released any documentation. + [http://204.200.238.31/cgi-bin/link.pl?co=i&cl=/ts/ibm/contact.html] + +---------------------------------------------------------------------------- + +OK, first thing - the IRQ problem IS a problem, whether the test is bypassed or +not. It is NOT a Linux problem, but an MWAVE problem that is fixed with the +latest MWAVE patches. So, in other words, don't bypass the test for MWAVES! + +I have Windows 95 on /dev/hda1, swap on /dev/hda2, and Red Hat 5 on /dev/hda3. + +The steps, then: + + Boot to Linux. + Mount Windows 95 file system (assume mount point = /dos95). + mkdir /dos95/linux + mkdir /dos95/linux/boot + mkdir /dos95/linux/boot/parms + + Copy the kernel, any initrd image, and loadlin to /dos95/linux/boot/. + + Reboot to Windows 95. + + Edit C:/msdos.sys and add or change the following: + + Logo=0 + BootGUI=0 + + Note that msdos.sys is a text file but it needs to be made 'unhidden', + readable and writable before it can be edited. This can be done with + DOS' "attrib" command. + + Edit config.sys to have multiple config menus. I have one for windows 95 and + five for Linux, like this: +------------ +[menu] +menuitem=W95, Windows 95 +menuitem=LINTP, Linux - ThinkPad +menuitem=LINTP3, Linux - ThinkPad Console +menuitem=LINDOC, Linux - Docked +menuitem=LINDOC3, Linux - Docked Console +menuitem=LIN1, Linux - Single User Mode +REM menudefault=W95,10 + +[W95] + +[LINTP] + +[LINDOC] + +[LINTP3] + +[LINDOC3] + +[LIN1] + +[COMMON] +FILES=30 +REM Please read README.TXT in C:\MWW subdirectory before changing the DOS= statement. +DOS=HIGH,UMB +DEVICE=C:\MWW\MANAGER\MWD50430.EXE +SHELL=c:\command.com /e:2048 +------------------- + +The important things are the SHELL and DEVICE statements. + + Then change autoexec.bat. Basically everything in there originally should be + done ONLY when Windows 95 is booted. Then you add new things specifically + for Linux. Mine is as follows + +--------------- +@ECHO OFF +if "%CONFIG%" == "W95" goto W95 + +REM +REM Linux stuff +REM +SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP +SET BLASTER=A220 I5 D1 +SET MWROOT=C:\MWW +SET LIBPATH=C:\MWW\DLL +SET PATH=C:\WINDOWS;C:\MWW\DLL; +CALL MWAVE START NOSHOW +c:\linux\boot\loadlin.exe @c:\linux\boot\parms\%CONFIG%.par + +:W95 +REM +REM Windows 95 stuff +REM +c:\toolkit\guard +SET MSINPUT=C:\MSINPUT +SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP +REM The following is used by DOS games to recognize Sound Blaster hardware. +REM If hardware settings are changed, please change this line as well. +REM See the Mwave README file for instructions. +SET BLASTER=A220 I5 D1 +SET MWROOT=C:\MWW +SET LIBPATH=C:\MWW\DLL +SET PATH=C:\WINDOWS;C:\WINDOWS\COMMAND;E:\ORAWIN95\BIN;f:\msdev\bin;e:\v30\bin.dbg;v:\devt\v30\bin;c:\JavaSDK\Bin;C:\MWW\DLL; +SET INCLUDE=f:\MSDEV\INCLUDE;F:\MSDEV\MFC\INCLUDE +SET LIB=F:\MSDEV\LIB;F:\MSDEV\MFC\LIB +win + +------------------------ + +Now build a file in c:\linux\boot\parms for each Linux config that you have. + +For example, my LINDOC3 config is for a docked Thinkpad at runlevel 3 with no +initrd image, and has a parameter file named LINDOC3.PAR in c:\linux\boot\parms: + +----------------------- +# LOADLIN @param_file image=other_image root=/dev/other +# +# Linux Console in docking station +# +c:\linux\boot\zImage.krn # First value must be filename of Linux kernel. +root=/dev/hda3 # device which gets mounted as root FS +ro # Other kernel arguments go here. +apm=off +doc=yes +3 +----------------------- + +The doc=yes parameter is an environment variable used by my init scripts, not +a kernel argument. + +However, the apm=off parameter IS a kernel argument! APM, at least in my setup, +causes the kernel to crash when loaded via loadlin (but NOT when loaded via +LILO). The APM stuff COULD be forced out of the kernel via the kernel compile +options. Instead, I got an unofficial patch to the APM drivers that allows them +to be dynamically deactivated via kernel arguments. Whatever you chose to +document, APM, it seems, MUST be off for setups like mine. + +Now make sure C:\MWW\MWCONFIG.REF looks like this: + +---------------------- +[NativeDOS] +Default=SB1.5 +SBInputSource=CD +SYNTH=FM +QSound=OFF +Reverb=OFF +Chorus=OFF +ReverbDepth=5 +ChorusDepth=5 +SBInputVolume=5 +SBMainVolume=10 +SBWaveVolume=10 +SBSynthVolume=10 +WaveTableVolume=10 +AudioPowerDriver=ON + +[FastCFG] +Show=No +HideOption=Off +----------------------------- + +OR the Default= line COULD be + +Default=SBPRO + +Reboot to Windows 95 and choose Linux. When booted, use sndconfig to configure +the sound modules and voilà - ThinkPad sound with Linux. + +Now the gotchas - you can either have CD sound OR Mixers but not both. That's a +problem with the SB1.5 (CD sound) or SBPRO (Mixers) settings. No one knows why +this is! + +For some reason MPEG3 files, when played through mpg123, sound like they +are playing at 1/8th speed - not very useful! If you have ANY insight +on why this second thing might be happening, I would be grateful. + +=========================================================== + _/ _/_/_/_/ + _/_/ _/_/ _/ + _/ _/_/ _/_/_/_/ Martin John Bartlett + _/ _/ _/ _/ (martin@nitram.demon.co.uk) +_/ _/_/_/_/ + _/ +_/ _/ + _/_/ +=========================================================== diff --git a/Documentation/sound/sonicvibes b/Documentation/sound/sonicvibes new file mode 100644 index 000000000000..4a4eb2526690 --- /dev/null +++ b/Documentation/sound/sonicvibes @@ -0,0 +1,73 @@ +ALaw/uLaw sample formats +------------------------ + +This driver does not support the ALaw/uLaw sample formats. +ALaw is the default mode when opening a sound device +using OSS/Free. The reason for the lack of support is +that the hardware does not support these formats, and adding +conversion routines to the kernel would lead to very ugly +code in the presence of the mmap interface to the driver. +And since xquake uses mmap, mmap is considered important :-) +and no sane application uses ALaw/uLaw these days anyway. +In short, playing a Sun .au file as follows: + +cat my_file.au > /dev/dsp + +does not work. Instead, you may use the play script from +Chris Bagwell's sox-12.14 package (available from the URL +below) to play many different audio file formats. +The script automatically determines the audio format +and does do audio conversions if necessary. +http://home.sprynet.com/sprynet/cbagwell/projects.html + + +Blocking vs. nonblocking IO +--------------------------- + +Unlike OSS/Free this driver honours the O_NONBLOCK file flag +not only during open, but also during read and write. +This is an effort to make the sound driver interface more +regular. Timidity has problems with this; a patch +is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html. +(Timidity patched will also run on OSS/Free). + + +MIDI UART +--------- + +The driver supports a simple MIDI UART interface, with +no ioctl's supported. + + +MIDI synthesizer +---------------- + +The card both has an OPL compatible FM synthesizer as well as +a wavetable synthesizer. + +I haven't managed so far to get the OPL synth running. + +Using the wavetable synthesizer requires allocating +1-4MB of physically contiguous memory, which isn't possible +currently on Linux without ugly hacks like the bigphysarea +patch. Therefore, the driver doesn't support wavetable +synthesis. + + +No support from S3 +------------------ + +I do not get any support from S3. Therefore, the driver +still has many problems. For example, although the manual +states that the chip should be able to access the sample +buffer anywhere in 32bit address space, I haven't managed to +get it working with buffers above 16M. Therefore, the card +has the same disadvantages as ISA soundcards. + +Given that the card is also very noisy, and if you haven't +already bought it, you should strongly opt for one of the +comparatively priced Ensoniq products. + + +Thomas Sailer +sailer@ife.ee.ethz.ch diff --git a/Documentation/sound/ultrasound b/Documentation/sound/ultrasound new file mode 100644 index 000000000000..32cd50478b36 --- /dev/null +++ b/Documentation/sound/ultrasound @@ -0,0 +1,30 @@ +modprobe sound +insmod ad1848 +insmod gus io=* irq=* dma=* ... + +This loads the driver for the Gravis Ultrasound family of sound cards. + +The gus module takes the following arguments + +io I/O address of the Ultrasound card (eg. io=0x220) +irq IRQ of the Sound Blaster card +dma DMA channel for the Sound Blaster +dma16 2nd DMA channel, only needed for full duplex operation +type 1 for PnP card +gus16 1 for using 16 bit sampling daughter board +no_wave_dma Set to disable DMA usage for wavetable (see note) +db16 ??? + + +no_wave_dma option + +This option defaults to a value of 0, which allows the Ultrasound wavetable +DSP to use DMA for for playback and downloading samples. This is the same +as the old behaviour. If set to 1, no DMA is needed for downloading samples, +and allows owners of a GUS MAX to make use of simultaneous digital audio +(/dev/dsp), MIDI, and wavetable playback. + + +If you have problems in recording with GUS MAX, you could try to use +just one 8 bit DMA channel. Recording will not work with one DMA +channel if it's a 16 bit one. diff --git a/MAINTAINERS b/MAINTAINERS index e2552bc2347a..5f9f24c32530 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -267,6 +267,11 @@ M: layes@loran.com L: linux-net@vger.rutgers.edu S: Maintained +MULTISOUND SOUND DRIVER +P: Andrew Veliath +M: andrewtv@usa.net +S: Maintained + NCP FILESYSTEM: P: Volker Lendecke M: lendecke@namu01.Num.Math.Uni-Goettingen.de @@ -285,6 +290,13 @@ M: net-patches@lxorguk.ukuu.org.uk L: linux-net@vger.rutgers.edu S: Odd Fixes <-> Maintained subject to workloads +PCI SOUND DRIVERS (ES1370, ES1371 and SONICVIBES) +P: Thomas Sailer +M: sailer@ife.ee.ethz.ch +L: linux-sound@vger.rutgers.edu +W: http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html +S: Maintained + PPP PROTOCOL DRIVERS AND COMPRESSORS P: Al Longyear M: longyear@netcom.com, Cc: longyear@sii.com diff --git a/Rules.make b/Rules.make index b023d0b43ba4..a32cb0b451f1 100644 --- a/Rules.make +++ b/Rules.make @@ -22,11 +22,13 @@ unexport O_TARGET unexport O_OBJS unexport L_OBJS unexport M_OBJS +unexport MI_OBJS unexport ALL_MOBJS # objects that export symbol tables unexport OX_OBJS unexport LX_OBJS unexport MX_OBJS +unexport MIX_OBJS unexport SYMTAB_OBJS unexport MOD_LIST_NAME @@ -103,7 +105,7 @@ ALL_MOBJS = $(MX_OBJS) $(M_OBJS) ifneq "$(strip $(ALL_MOBJS))" "" PDWN=$(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh) endif -modules: $(ALL_MOBJS) dummy +modules: $(ALL_MOBJS) $(MIX_OBJS) $(MI_OBJS) dummy ifdef MOD_SUB_DIRS set -e; for i in $(MOD_SUB_DIRS); do $(MAKE) -C $$i modules; done endif @@ -141,7 +143,7 @@ script: # Exporting objects are: all objects that define symbol tables # ifdef CONFIG_MODVERSIONS -SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS) +SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS) $(MIX_OBJS) ifneq "$(strip $(SYMTAB_OBJS))" "" MODINCL = $(TOPDIR)/include/linux/modules diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 228c0049f18b..ccceaf0048f2 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -365,7 +365,7 @@ static int loop_set_status(struct loop_device *lo, struct loop_info *arg) case LO_CRYPT_NONE: break; case LO_CRYPT_XOR: - if (info.lo_encrypt_key_size < 0) + if (info.lo_encrypt_key_size <= 0) return -EINVAL; break; #ifdef DES_AVAILABLE diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 77603e60b420..a3f168350157 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,7 +1,7 @@ #define BLOCKMOVE #define Z_WAKE static char rcsid[] = -"$Revision: 2.1.1.4 $$Date: 1998/06/01 18:01:19 $"; +"$Revision: 2.1.1.6 $$Date: 1998/08/04 12:19:36 $"; /* * linux/drivers/char/cyclades.c @@ -31,6 +31,16 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 2.1.1.6 1998/08/04 12:19:36 ivan + * cyy_interrupt changed once more to avoid occurence of kernel + * oopses during PPP operation. + * + * Revision 2.1.1.5 1998/08/03 16:55:59 ivan + * /proc/cyclades implementation (only for monolythic driver) + * with great collaboration of Marc Lewis ; + * cyy_interrupt was changed to avoid occurence of kernel oopses + * during PPP operation. + * * Revision 2.1.1.4 1998/06/01 18:01:19 ivan * data loss prevention revisited (cy_wait_until_sent created); * MOD_COUNT bug (which caused the usage decrementing not to work @@ -141,7 +151,7 @@ static char rcsid[] = * Price for help on this) * * Revision 1.36.4.21 1996/09/10 17:00:10 bentson - * shift from cpu-bound to memcopy in cyz_polling operation + * shift from CPU-bound to memcopy in cyz_polling operation * * Revision 1.36.4.20 1996/09/09 18:30:32 Bentson * Added support to set and report higher speeds. @@ -553,6 +563,11 @@ static char rcsid[] = #include +#ifdef CONFIG_PROC_FS +#include +#include +#endif + #define __initfunc(__arginit) __arginit #define copy_from_user memcpy_fromfs #define copy_to_user memcpy_tofs @@ -583,6 +598,8 @@ static char rcsid[] = #define SERIAL_TYPE_NORMAL 1 #define SERIAL_TYPE_CALLOUT 2 +#define JIFFIES_DIFF(n, j) ((n) >= (j) ? (n) - (j) : ULONG_MAX - (n) + (j)) + DECLARE_TASK_QUEUE(tq_cyclades); struct tty_driver cy_serial_driver, cy_callout_driver; @@ -592,7 +609,6 @@ static volatile int cy_triggered; static int cy_wild_int_mask; static volatile ucchar *intr_base_addr; - /* This is the address lookup table. The driver will probe for Cyclom-Y/ISA boards at all addresses in here. If you want the driver to probe addresses at a different address, add it to @@ -745,6 +761,22 @@ static void cyz_poll(unsigned long); static void show_status(int); #endif +#if defined(CONFIG_PROC_FS) && !defined(MODULE) +static int cyclades_get_proc_info(char *, char **, off_t, int, int); +static struct proc_dir_entry cyclades_proc_entry = { + 0, + 8, + "cyclades", + S_IFREG | S_IRUGO, + 1, + 0, + 0, + 0, + 0, + cyclades_get_proc_info +}; +#endif + /* The Cyclades-Z polling cycle is defined by this variable */ static long cyz_polling_cycle = CZ_DEF_POLL; @@ -1239,11 +1271,13 @@ printk("cy_interrupt: rcvd intr, chip %d\n\r", chip); TTY_FRAME; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<idle_stats.frame_errs++; }else if(data & CyPARITY){ *tty->flip.flag_buf_ptr++ = TTY_PARITY; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<idle_stats.parity_errs++; }else if(data & CyOVERRUN){ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; @@ -1260,6 +1294,7 @@ printk("cy_interrupt: rcvd intr, chip %d\n\r", chip); *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<idle_stats.overruns++; /* These two conditions may imply */ /* a normal read should be done. */ /* }else if(data & CyTIMEOUT){ */ @@ -1276,6 +1311,7 @@ printk("cy_interrupt: rcvd intr, chip %d\n\r", chip); /* there was a software buffer overrun and nothing could be done about it!!! */ + info->idle_stats.overruns++; } } else { /* normal character reception */ /* load # chars available from the chip */ @@ -1288,6 +1324,9 @@ printk("cy_interrupt: rcvd intr, chip %d\n\r", chip); info->mon.char_max = char_count; info->mon.char_last = char_count; #endif + info->idle_stats.recv_bytes += char_count; + info->idle_stats.recv_idle = jiffies; + while(char_count--){ if (tty->flip.count >= TTY_FLIPBUF_SIZE){ break; @@ -1382,25 +1421,25 @@ printk("cy_interrupt: xmit intr, chip %d\n\r", chip); info->x_break = 0; } - if (!info->xmit_cnt){ - cy_writeb((u_long)base_addr+(CySRER<xmit_buf == 0){ - cy_writeb((u_long)base_addr+(CySRER<tty->stopped || info->tty->hw_stopped){ - cy_writeb((u_long)base_addr+(CySRER< 0){ - if (!info->xmit_cnt){ - goto txdone; - } + if (!info->xmit_cnt){ + cy_writeb((u_long)base_addr+(CySRER<xmit_buf == 0){ + cy_writeb((u_long)base_addr+(CySRER<tty->stopped || info->tty->hw_stopped){ + cy_writeb((u_long)base_addr+(CySRER<mon.char_max = char_count; info->mon.char_last = char_count; #endif + info->idle_stats.recv_bytes += char_count; + info->idle_stats.recv_idle = jiffies; if( tty == 0){ /* flush received characters */ rx_get = (rx_get + char_count) & (rx_bufsize - 1); @@ -1994,6 +2035,10 @@ startup(struct cyclades_port * info) clear_bit(TTY_IO_ERROR, &info->tty->flags); } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); + info->idle_stats.in_use = + info->idle_stats.recv_idle = + info->idle_stats.xmit_idle = jiffies; restore_flags(flags); } else { struct FIRM_ID *firm_id; @@ -2054,7 +2099,10 @@ startup(struct cyclades_port * info) clear_bit(TTY_IO_ERROR, &info->tty->flags); } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - + memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); + info->idle_stats.in_use = + info->idle_stats.recv_idle = + info->idle_stats.xmit_idle = jiffies; } #ifdef SERIAL_DEBUG_OPEN @@ -2434,7 +2482,6 @@ block_til_ready(struct tty_struct *tty, struct file * filp, return 0; } /* block_til_ready */ - /* * This routine is called whenever a serial port is opened. It * performs the serial-specific initialization for the tty structure. @@ -2806,6 +2853,10 @@ cy_write(struct tty_struct * tty, int from_user, SP(" "); #endif } + + info->idle_stats.xmit_bytes += total; + info->idle_stats.xmit_idle = jiffies; + if (from_user) up(&tmp_buf_sem); if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { @@ -2848,6 +2899,8 @@ cy_put_char(struct tty_struct *tty, unsigned char ch) info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE - 1; info->xmit_cnt++; + info->idle_stats.xmit_bytes++; + info->idle_stats.xmit_idle = jiffies; restore_flags(flags); #if 0 SP("+"); @@ -4031,10 +4084,13 @@ cy_ioctl(struct tty_struct *tty, struct file * file, case CYGETCD1400VER: ret_val = info->chip_rev; break; - case CYZPOLLCYCLE: - cyz_polling_cycle = (HZ * arg) / 1000; + case CYZSETPOLLCYCLE: + cyz_polling_cycle = (arg * HZ) / 1000; ret_val = 0; break; + case CYZGETPOLLCYCLE: + ret_val = (cyz_polling_cycle * 1000) / HZ; + break; case CYSETWAIT: info->closing_wait = (unsigned short)arg * HZ/100; ret_val = 0; @@ -5035,6 +5091,61 @@ show_version(void) __DATE__, __TIME__); } /* show_version */ +#if defined(CONFIG_PROC_FS) && !defined(MODULE) +int cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, + int dummy) +{ + struct cyclades_port *info; + int i; + int len=0; + off_t begin=0; + off_t pos=0; + int size; + __u32 cur_jifs = jiffies; + + size = sprintf(buf+len, "Dev TimeOpen BytesOut IdleOut BytesIn IdleIn Overruns Ldisc\n"); + + len += size; + pos += size; + + /* Output one line for each known port */ + for (i = 0; i < NR_PORTS && cy_port[i].line >= 0; i++) { + info = &cy_port[i]; + + if (info->count) + size = sprintf(buf+len, + "%3d %8lu %10lu %8lu %10lu %8lu %9lu %6ld\n", + info->line, + JIFFIES_DIFF(info->idle_stats.in_use, cur_jifs) / HZ, + info->idle_stats.xmit_bytes, + JIFFIES_DIFF(info->idle_stats.xmit_idle, cur_jifs) / HZ, + info->idle_stats.recv_bytes, + JIFFIES_DIFF(info->idle_stats.recv_idle, cur_jifs) / HZ, + info->idle_stats.overruns, + info->tty->ldisc.num); + else + size = sprintf(buf+len, + "%3d %8lu %10lu %8lu %10lu %8lu %9lu %6ld\n", + info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); + len += size; + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + break; + } + + *start = buf + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; /* Ending slop */ + return len; +} +#endif + /* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a @@ -5299,6 +5410,10 @@ cy_init(void)) #endif } +#if defined(CONFIG_PROC_FS) && !defined(MODULE) + proc_register_dynamic(&proc_root, &cyclades_proc_entry); +#endif + return 0; } /* cy_init */ diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 79387fbd1bad..5bbf23ab4fb0 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -24,8 +24,14 @@ #include #ifdef CONFIG_SOUND +void soundcore_init(void); +#ifdef CONFIG_SOUND_OSS void soundcard_init(void); #endif +#ifdef CONFIG_DMASOUND +void dmasound_init(void); +#endif +#endif #ifdef CONFIG_ISDN void isdn_init(void); #endif @@ -405,7 +411,13 @@ int chr_dev_init(void) misc_init(); #endif #ifdef CONFIG_SOUND + soundcore_init(); +#ifdef CONFIG_SOUND_OSS soundcard_init(); +#endif +#ifdef CONFIG_DMASOUND + dmasound_init(); +#endif #endif #if CONFIG_QIC02_TAPE qic02_tape_init(); diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in index f7928f15030c..6691b728a14d 100644 --- a/drivers/isdn/Config.in +++ b/drivers/isdn/Config.in @@ -7,11 +7,11 @@ if [ "$CONFIG_INET" != "n" ]; then bool 'Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ bool 'Support generic MP (RFC 1717)' CONFIG_ISDN_MPP fi - # bool 'Support dynamic timeout-rules' CONFIG_ISDN_TIMEOUT_RULES - # if [ "$CONFIG_ISDN_TIMEOUT_RULES" != "n" ]; then - # bool 'Use masqueraded addresses for rule-matching' CONFIG_ISDN_TIMRU_USE_MASQ - # fi - # bool 'Support budget-accounting' CONFIG_ISDN_BUDGET + bool 'Support dynamic timeout-rules' CONFIG_ISDN_TIMEOUT_RULES + if [ "$CONFIG_ISDN_TIMEOUT_RULES" != "n" ]; then + bool 'Use masqueraded addresses for rule-matching' CONFIG_ISDN_TIMRU_USE_MASQ + fi + bool 'Support budget-accounting' CONFIG_ISDN_BUDGET fi bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN diff --git a/drivers/isdn/pcbit/pcbit.h b/drivers/isdn/pcbit/pcbit.h index 89a608904d7d..20051bf3ec13 100644 --- a/drivers/isdn/pcbit/pcbit.h +++ b/drivers/isdn/pcbit/pcbit.h @@ -98,7 +98,7 @@ struct pcbit_dev { }; #define STATS_TIMER (10*HZ) -#define ERRTIME (0.1*HZ) +#define ERRTIME (HZ/10) /* MRU */ #define MAXBUFSIZE 1534 diff --git a/drivers/net/apricot.c b/drivers/net/apricot.c index 023c310e7bf4..a20a9f654426 100644 --- a/drivers/net/apricot.c +++ b/drivers/net/apricot.c @@ -147,14 +147,14 @@ struct i596_scp { }; struct i596_private { - struct i596_scp scp; - struct i596_iscp iscp; - struct i596_scb scb; - struct i596_cmd set_add; + volatile struct i596_scp scp; + volatile struct i596_iscp iscp; + volatile struct i596_scb scb; + volatile struct i596_cmd set_add; char eth_addr[8]; - struct i596_cmd set_conf; + volatile struct i596_cmd set_conf; char i596_config[16]; - struct i596_cmd tdr; + volatile struct i596_cmd tdr; unsigned long stat; int last_restart; struct i596_rfd *rx_tail; diff --git a/drivers/net/plip.c b/drivers/net/plip.c index 15338f15b6f3..8852654dd8e2 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -390,7 +390,7 @@ plip_bh_timeout_error(struct device *dev, struct net_local *nl, return TIMEOUT; } c0 = inb(PAR_STATUS(dev)); - printk("%s: transmit timeout(%d,%02x)\n", + printk(KERN_WARNING "%s: transmit timeout(%d,%02x)\n", dev->name, snd->state, c0); } nl->enet_stats.tx_errors++; @@ -408,7 +408,7 @@ plip_bh_timeout_error(struct device *dev, struct net_local *nl, return TIMEOUT; } c0 = inb(PAR_STATUS(dev)); - printk("%s: receive timeout(%d,%02x)\n", + printk(KERN_WARNING "%s: receive timeout(%d,%02x)\n", dev->name, rcv->state, c0); } nl->enet_stats.rx_dropped++; @@ -538,13 +538,13 @@ plip_receive_packet(struct device *dev, struct net_local *nl, return TIMEOUT; if (rcv->length.h > dev->mtu + dev->hard_header_len || rcv->length.h < 8) { - printk("%s: bogus packet size %d.\n", dev->name, rcv->length.h); + printk(KERN_WARNING "%s: bogus packet size %d.\n", dev->name, rcv->length.h); return ERROR; } /* Malloc up new buffer. */ rcv->skb = dev_alloc_skb(rcv->length.h); if (rcv->skb == NULL) { - printk("%s: Memory squeeze.\n", dev->name); + printk(KERN_WARNING "%s: Memory squeeze.\n", dev->name); return ERROR; } skb_put(rcv->skb,rcv->length.h); @@ -668,7 +668,7 @@ plip_send_packet(struct device *dev, struct net_local *nl, unsigned int cx; if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) { - printk("%s: send skb lost\n", dev->name); + printk(KERN_ERR "%s: send skb lost\n", dev->name); snd->state = PLIP_PK_DONE; snd->skb = NULL; return ERROR; @@ -811,7 +811,7 @@ plip_interrupt(int irq, void *dev_id, struct pt_regs * regs) unsigned char c0; if (dev == NULL) { - printk ("plip_interrupt: irq %d for unknown device.\n", irq); + printk (KERN_ERR "plip_interrupt: irq %d for unknown device.\n", irq); return; } @@ -845,12 +845,12 @@ plip_interrupt(int irq, void *dev_id, struct pt_regs * regs) case PLIP_CN_RECEIVE: sti(); - printk("%s: receive interrupt when receiving packet\n", dev->name); + printk(KERN_WARNING "%s: receive interrupt when receiving packet\n", dev->name); break; case PLIP_CN_ERROR: sti(); - printk("%s: receive interrupt in error state\n", dev->name); + printk(KERN_WARNING "%s: receive interrupt in error state\n", dev->name); break; } } @@ -868,7 +868,7 @@ plip_rebuild_header(void *buff, struct device *dev, unsigned long dst, return nl->orig_rebuild_header(buff, dev, dst, skb); if (eth->h_proto != htons(ETH_P_IP)) { - printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto); + printk(KERN_WARNING "plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto); memcpy(eth->h_source, dev->dev_addr, dev->addr_len); return 0; } @@ -897,12 +897,12 @@ plip_tx_packet(struct sk_buff *skb, struct device *dev) } if (set_bit(0, (void*)&dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); + printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name); return 1; } if (skb->len > dev->mtu + dev->hard_header_len) { - printk("%s: packet too big, %d.\n", dev->name, (int)skb->len); + printk(KERN_WARNING "%s: packet too big, %d.\n", dev->name, (int)skb->len); dev->tbusy = 0; dev_kfree_skb(skb, FREE_WRITE); return 0; @@ -940,13 +940,13 @@ plip_open(struct device *dev) int i; if (dev->irq == 0) { - printk("%s: IRQ is not set. Please set it by ifconfig.\n", dev->name); + printk(KERN_INFO "%s: IRQ is not set. Please set it by ifconfig.\n", dev->name); return -EAGAIN; } cli(); if (request_irq(dev->irq , plip_interrupt, 0, dev->name, NULL) != 0) { sti(); - printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq); + printk(KERN_WARNING "%s: couldn't get IRQ %d.\n", dev->name, dev->irq); return -EAGAIN; } irq2dev_map[dev->irq] = dev; @@ -1029,7 +1029,7 @@ plip_config(struct device *dev, struct ifmap *map) if (map->base_addr != (unsigned long)-1 && map->base_addr != dev->base_addr) - printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name); + printk(KERN_WARNING "%s: You cannot change base_addr of this interface (ignored).\n", dev->name); if (map->irq != (unsigned char)-1) dev->irq = map->irq; diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c index 2b2085602ca7..3422a88cb8dd 100644 --- a/drivers/net/shaper.c +++ b/drivers/net/shaper.c @@ -125,7 +125,7 @@ static int shaper_clocks(struct shaper *shaper, struct sk_buff *skb) static void shaper_setspeed(struct shaper *shaper, int bitspersec) { - shaper->bitspersdc=bitspersec; + shaper->bitspersec=bitspersec; shaper->bytespertick=(bitspersec/HZ)/8; if(!shaper->bytespertick) shaper->bytespertick++; diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c index 21eaff620bb0..e130317512a6 100644 --- a/drivers/scsi/aha1542.c +++ b/drivers/scsi/aha1542.c @@ -16,6 +16,8 @@ * Modified by Mike McLagan * Recognise extended mode on AHA1542CP, different bit than 1542CF * 1-Jan-97 + * Modified by Bjorn L. Thordarson and Einar Thor Einarsson + * Recognize that DMA0 is valid DMA channel -- 13-Jul-98 */ #include @@ -89,13 +91,6 @@ static char *setup_str[MAXBOARDS] = {(char *)NULL,(char *)NULL}; * Factory default is 5 MB/s. */ - -/* The DMA-Controller. We need to fool with this because we want to - be able to use the aha1542 without having to have the bios enabled */ -#define DMA_MODE_REG 0xd6 -#define DMA_MASK_REG 0xd4 -#define CASCADE 0xc0 - #define BIOS_TRANSLATION_1632 0 /* Used by some old 1542A boards */ #define BIOS_TRANSLATION_6432 1 /* Default case these days */ #define BIOS_TRANSLATION_25563 2 /* Big disk case */ @@ -748,8 +743,8 @@ static int aha1542_getconfig(int base_io, unsigned char * irq_level, unsigned ch *dma_chan = 5; break; case 0x01: - printk("DMA priority 0 not available for Adaptec driver\n"); - return -1; + *dma_chan = 0; + break; case 0: /* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel. Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */ @@ -1019,9 +1014,9 @@ int aha1542_detect(Scsi_Host_Template * tpnt) goto unregister; } - if (dma_chan >= 5) { - outb((dma_chan - 4) | CASCADE, DMA_MODE_REG); - outb(dma_chan - 4, DMA_MASK_REG); + if (dma_chan == 0 || dma_chan >= 5) { + set_dma_mode(dma_chan, DMA_MODE_CASCADE); + enable_dma(dma_chan); } } aha_host[irq_level - 9] = shpnt; diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c index f9f9d0e07350..2f456c4a36c6 100644 --- a/drivers/scsi/aic7xxx.c +++ b/drivers/scsi/aic7xxx.c @@ -209,7 +209,7 @@ struct proc_dir_entry proc_scsi_aic7xxx = { 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "5.0.19" +#define AIC7XXX_C_VERSION "5.0.20" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -1620,16 +1620,7 @@ aic7xxx_loadseq(struct aic7xxx_host *p) static void aic7xxx_delay(int seconds) { - unsigned int i; - - /* - * Call udelay() for 1 millisecond inside a loop for - * the requested amount of seconds. - */ - for (i=0; i < seconds*1000; i++) - { - udelay(1000); /* Delay for 1 millisecond. */ - } + mdelay(seconds*1000); } /*+F************************************************************************* @@ -3179,17 +3170,17 @@ aic7xxx_reset_current_bus(struct aic7xxx_host *p) scsiseq = aic_inb(p, SCSISEQ); aic_outb(p, scsiseq | SCSIRSTO, SCSISEQ); - udelay(5000); + mdelay(5); /* Turn off the bus reset. */ aic_outb(p, scsiseq & ~SCSIRSTO, SCSISEQ); - aic7xxx_clear_intstat(p); + mdelay(2); + aic7xxx_clear_intstat(p); /* Re-enable reset interrupts. */ aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1); - udelay(2000); } /*+F************************************************************************* @@ -4816,17 +4807,8 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) Scsi_Cmnd *cmd; scbptr = aic_inb(p, WAITING_SCBH); - if (scbptr >= p->scb_data->maxhscbs) - { - scb_index = SCB_LIST_NULL; - printk(WARN_LEAD "Bad scbptr %d during SELTO.\n", - p->host_no, -1, -1, -1, scbptr); - } - else - { - aic_outb(p, scbptr, SCBPTR); - scb_index = aic_inb(p, SCB_TAG); - } + aic_outb(p, scbptr, SCBPTR); + scb_index = aic_inb(p, SCB_TAG); scb = NULL; if (scb_index < p->scb_data->numscbs) @@ -4892,6 +4874,7 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) * Restarting the sequencer will stop the selection and make sure devices * are allowed to reselect in. */ + aic_outb(p, 0, SCSISEQ); aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1); p->flags &= ~AHC_HANDLING_REQINITS; aic_outb(p, CLRSELTIMEO | CLRBUSFREE | CLRREQINIT, CLRSINT1); @@ -5695,7 +5678,7 @@ acquire_seeprom(struct aic7xxx_host *p) while ((wait > 0) && ((aic_inb(p, SEECTL) & SEERDY) == 0)) { wait--; - udelay(1000); /* 1 msec */ + mdelay(1); /* 1 msec */ } if ((aic_inb(p, SEECTL) & SEERDY) == 0) { @@ -6727,7 +6710,7 @@ aic7xxx_chip_reset(struct aic7xxx_host *p) wait = 1000; /* 1 second (1000 * 1000 usec) */ while ((wait > 0) && ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0)) { - udelay(1000); /* 1 msec = 1000 usec */ + mdelay(1); /* 1 msec = 1000 usec */ wait = wait - 1; } @@ -9377,6 +9360,7 @@ aic7xxx_release(struct Scsi_Host *host) if(p->irq) free_irq(p->irq, p); release_region(p->base, MAXREG - MINREG); +#ifdef MMAPIO if(p->maddr) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) @@ -9385,6 +9369,7 @@ aic7xxx_release(struct Scsi_Host *host) iounmap((void *) (((unsigned long) p->maddr) & PAGE_MASK)); #endif } +#endif /* MMAPIO */ prev = NULL; next = first_aic7xxx; while(next != NULL) diff --git a/drivers/scsi/dtc.c b/drivers/scsi/dtc.c index d951420643d7..94c3e33353c3 100644 --- a/drivers/scsi/dtc.c +++ b/drivers/scsi/dtc.c @@ -176,7 +176,7 @@ int dtc_detect(Scsi_Host_Template * tpnt) { base = NULL; if (overrides[current_override].address) - base = overrides[current_override].address; + base = (unsigned char *)overrides[current_override].address; else for (; !base && (current_base < NO_BASES); ++current_base) { #if (DTCDEBUG & DTCDEBUG_INIT) @@ -184,9 +184,9 @@ int dtc_detect(Scsi_Host_Template * tpnt) { #endif for (sig = 0; sig < NO_SIGNATURES; ++sig) if (!bases[current_base].noauto && !memcmp - (bases[current_base].address + signatures[sig].offset, + ((unsigned char *)(bases[current_base].address + signatures[sig].offset), signatures[sig].string, strlen(signatures[sig].string))) { - base = bases[current_base].address; + base = (unsigned char *)bases[current_base].address; #if (DTCDEBUG & DTCDEBUG_INIT) printk("scsi-dtc : detected board.\n"); #endif diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c index 9f9560a27f0b..5670b56f5acc 100644 --- a/drivers/scsi/wd7000.c +++ b/drivers/scsi/wd7000.c @@ -660,7 +660,7 @@ void wd7000_setup (char *str, int *ints) configs[wd7000_card_num].bus_on = BUS_ON; } else - configs[wd7000_card_num].bus_on = ints[4] / 125.0; + configs[wd7000_card_num].bus_on = ints[4] / 125; } else configs[wd7000_card_num].bus_on = BUS_ON; @@ -672,7 +672,7 @@ void wd7000_setup (char *str, int *ints) configs[wd7000_card_num].bus_off = BUS_OFF; } else - configs[wd7000_card_num].bus_off = ints[5] / 125.0; + configs[wd7000_card_num].bus_off = ints[5] / 125; } else configs[wd7000_card_num].bus_off = BUS_OFF; diff --git a/drivers/sound/CHANGELOG b/drivers/sound/CHANGELOG index ccb6032dee1f..8706cd66ca1f 100644 --- a/drivers/sound/CHANGELOG +++ b/drivers/sound/CHANGELOG @@ -1,5 +1,47 @@ -Changelog for version 3.5.4 ---------------------------- +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 @@ -37,7 +79,7 @@ Since 3.5.4-beta2 - 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 soundcards. See lowlevel/aci.readme for more info. + miroPCM sound cards. See lowlevel/aci.readme for more info. - Support for Aztech Washington chipset (AZT2316 ASIC). Since 3.5.4-beta1 @@ -229,7 +271,7 @@ Since pre-3.0-940712 GUS MAX, but it doesn't work yet. Since pre-3.0-940426 - AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc). -This codec chip is used in various soundcards. This version is developed +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 @@ -294,7 +336,7 @@ Since 2.2b - 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 soundcard is available. + 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. diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index b1bc3607904b..597f8d9a07b7 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -1,17 +1,220 @@ +# drivers/sound/Config.in # -# Sound driver configuration +# 18 Apr 1998, Michael Elizabeth Chastain, +# More hacking for modularisation. # -#-------- -# There is another config script which is compatible with rest of -# the kernel. It can be activated by running 'make mkscript' in this -# directory. Please note that this is an _experimental_ feature which -# doesn't work with all cards (PSS, SM Wave, AudioTrix Pro, Maui). -#-------- -# -$MAKE -C drivers/sound config || exit 1 +# See drivers/sound/README.CONFIG for more information. + + + +# Prompt user for primary drivers. +if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_PCI" = "y" ]; then + dep_tristate 'Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND + dep_tristate 'Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ES1371 $CONFIG_SOUND + dep_tristate 'S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND +fi -bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate 'Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND + if [ "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then + int 'MSND Classic IRQ 5,7,9,10,11,12' CONFIG_MSNDCLAS_IRQ 5 + hex 'MSND Classic Memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDCLAS_MEM D0000 + hex 'MSND Classic I/O 210,220,230,240,250,260,290,3E0' CONFIG_MSNDCLAS_IO 290 + fi + dep_tristate 'Support for Turtle Beach MultiSound Pinnacle, Fiji' CONFIG_SOUND_MSNDPIN $CONFIG_SOUND + if [ "$CONFIG_SOUND_MSNDPIN" = "y" ]; then + int 'MSND Pinnacle IRQ 5,7,9,10,11,12' CONFIG_MSNDPIN_IRQ 5 + hex 'MSND Pinnacle Memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDPIN_MEM D0000 + hex 'MSND Pinnacle I/O 210,220,230,240,250,260,290,3E0' CONFIG_MSNDPIN_IO 290 + fi +fi + +dep_tristate 'OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND + +if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then + dep_tristate 'ProAudioSpectrum 16 support' CONFIG_SOUND_PAS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_PAS" = "y" ]; then + int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' CONFIG_PAS_IRQ 10 + int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' CONFIG_PAS_DMA 3 + bool 'Enable PAS16 joystick port' CONFIG_PAS_JOYSTICK + fi + + dep_tristate '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SOUND_SB $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_SB" = "y" ]; then + hex 'I/O base for SB Check from manual of the card' CONFIG_SB_BASE 220 + int 'Sound Blaster IRQ Check from manual of the card' CONFIG_SB_IRQ 7 + int 'Sound Blaster DMA 0, 1 or 3' CONFIG_SB_DMA 1 + int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' CONFIG_SB_DMA2 5 + hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' CONFIG_SB_MPU_BASE 330 + comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.' + comment 'Enter -1 to the following question if you have something else such as SB16/32.' + int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' CONFIG_SB_MPU_IRQ -1 + fi + + dep_tristate 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS + + dep_tristate 'Gravis Ultrasound support' CONFIG_SOUND_GUS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_GUS" = "y" -o "$CONFIG_SOUND_GUS" = "m" ]; then + bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 + bool 'GUS MAX support' CONFIG_GUSMAX + fi + if [ "$CONFIG_SOUND_GUS" = "y" ]; then + hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' CONFIG_GUS_BASE 220 + int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' CONFIG_GUS_IRQ 15 + int 'GUS DMA 1, 3, 5, 6 or 7' CONFIG_GUS_DMA 6 + int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' CONFIG_GUS_DMA2 -1 + if [ "$CONFIG_GUS16" = "y" ]; then + hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' CONFIG_GUS16_BASE 530 + int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' CONFIG_GUS16_IRQ 7 + int 'GUS DMA 0, 1 or 3' CONFIG_GUS16_DMA 3 + fi + fi + + dep_tristate 'MPU-401 support (NOT for SB16)' CONFIG_SOUND_MPU401 $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_MPU401" = "y" ]; then + hex 'I/O base for MPU401 Check from manual of the card' CONFIG_MPU_BASE 330 + int 'MPU401 IRQ Check from manual of the card' CONFIG_MPU_IRQ 9 + fi + + dep_tristate 'PSS (AD1848, ADSP-2115, ESC614) support' CONFIG_SOUND_PSS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_PSS" = "y" ]; then + hex 'PSS I/O base 220 or 240' CONFIG_PSS_BASE 220 + hex 'PSS audio I/O base 530, 604, E80 or F40' CONFIG_PSS_MSS_BASE 530 + int 'PSS audio IRQ 7, 9, 10 or 11' CONFIG_PSS_MSS_IRQ 11 + int 'PSS audio DMA 0, 1 or 3' CONFIG_PSS_MSS_DMA 3 + hex 'PSS MIDI I/O base ' CONFIG_PSS_MPU_BASE 330 + int 'PSS MIDI IRQ 3, 4, 5, 7, 9, 10, 11, 12' CONFIG_PSS_MPU_IRQ 9 + bool ' Have DSPxxx.LD firmware file' CONFIG_PSS_HAVE_BOOT + if [ "$CONFIG_PSS_HAVE_BOOT" = "y" ]; then + string ' Full pathname of DSPxxx.LD firmware file' CONFIG_PSS_BOOT_FILE + fi + fi + if [ "$CONFIG_SOUND_PSS" = "y" -o "$CONFIG_SOUND_PSS" = "m" ]; then + bool ' Enable PSS mixer (Beethoven ADSP-16 and other compatibile)' CONFIG_PSS_MIXER + fi + + dep_tristate 'Microsoft Sound System support' CONFIG_SOUND_MSS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_MSS" = "y" ]; then + hex 'MSS/WSS I/O base 530, 604, E80 or F40' CONFIG_MSS_BASE 530 + int 'MSS/WSS IRQ 7, 9, 10 or 11' CONFIG_MSS_IRQ 11 + int 'MSS/WSS DMA 0, 1 or 3' CONFIG_MSS_DMA 3 + int 'MSS/WSS second DMA (if possible) 0, 1 or 3' CONFIG_MSS_DMA2 -1 + fi + + dep_tristate 'Ensoniq SoundScape support' CONFIG_SOUND_SSCAPE $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_SSCAPE" = "y" ]; then + hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' CONFIG_SSCAPE_BASE 330 + int 'SoundScape MIDI IRQ ' CONFIG_SSCAPE_IRQ 9 + int 'SoundScape initialization DMA 0, 1 or 3' CONFIG_SSCAPE_DMA 3 + hex 'SoundScape audio I/O base 534, 608, E84 or F44' CONFIG_SSCAPE_MSS_BASE 534 + int 'SoundScape audio IRQ 7, 9, 10 or 11' CONFIG_SSCAPE_MSS_IRQ 11 + fi + + dep_tristate 'MediaTrix AudioTrix Pro support' CONFIG_SOUND_TRIX $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_TRIX" = "y" ]; then + hex 'TRIX audio I/O base 530, 604, E80 or F40' CONFIG_TRIX_BASE 530 + int 'TRIX audio IRQ 7, 9, 10 or 11' CONFIG_TRIX_IRQ 11 + int 'TRIX audio DMA 0, 1 or 3' CONFIG_TRIX_DMA 0 + int 'TRIX second (duplex) DMA 0, 1 or 3' CONFIG_TRIX_DMA2 3 + hex 'TRIX MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_TRIX_MPU_BASE 330 + int 'TRIX MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_TRIX_MPU_IRQ 9 + hex 'TRIX SB I/O base 220, 210, 230, 240, 250, 260 or 270' CONFIG_TRIX_SB_BASE 220 + int 'TRIX SB IRQ 3, 4, 5 or 7' CONFIG_TRIX_SB_IRQ 7 + int 'TRIX SB DMA 1 or 3' CONFIG_TRIX_SB_DMA 1 + bool ' Have TRXPRO.HEX firmware file' CONFIG_TRIX_HAVE_BOOT + if [ "$CONFIG_TRIX_HAVE_BOOT" = "y" ]; then + string ' Full pathname of TRXPRO.HEX firmware file' CONFIG_TRIX_BOOT_FILE + fi + fi + + dep_tristate 'Support for OPTi MAD16 and/or Mozart based cards' CONFIG_SOUND_MAD16 $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_MAD16" = "y" -o "$CONFIG_SOUND_MAD16" = "m" ]; then + bool 'Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD + fi + if [ "$CONFIG_SOUND_MAD16" = "y" ]; then + hex 'MAD16 audio I/O base 530, 604, E80 or F40' CONFIG_MAD16_BASE 530 + int 'MAD16 audio IRQ 7, 9, 10 or 11' CONFIG_MAD16_IRQ 11 + int 'MAD16 audio DMA 0, 1 or 3' CONFIG_MAD16_DMA 3 + int 'MAD16 second (duplex) DMA 0, 1 or 3' CONFIG_MAD16_DMA2 0 + hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' CONFIG_MAD16_MPU_BASE 330 + int 'MAD16 MIDI IRQ 5, 7, 9 or 10' CONFIG_MAD16_MPU_IRQ 9 + fi + + dep_tristate 'Full support for Turtle Beach WaveFront (Tropez Plus, Tropez, Maui) synth/soundcards' CONFIG_SOUND_WAVEFRONT $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_WAVEFRONT" = "y" ]; then + hex 'I/O base for WaveFront 210, 230, 260, 290, 300, 320, 338 or 330' CONFIG_WAVEFRONT_BASE 330 + int 'WaveFront IRQ 5, 9, 12 or 15' CONFIG_WAVEFRONT_IRQ 9 + fi -if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then - bool 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER + dep_tristate 'Support for Crystal CS4232 based (PnP) cards' CONFIG_SOUND_CS4232 $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_CS4232" = "y" ]; then + hex 'CS4232 audio I/O base 530, 604, E80 or F40' CONFIG_CS4232_BASE 530 + int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_CS4232_IRQ 11 + int 'CS4232 audio DMA 0, 1 or 3' CONFIG_CS4232_DMA 0 + int 'CS4232 second (duplex) DMA 0, 1 or 3' CONFIG_CS4232_DMA2 3 + hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_CS4232_MPU_BASE 330 + int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CONFIG_CS4232_MPU_IRQ 9 + fi + + dep_tristate 'Limited support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_SOUND_MAUI $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_MAUI" = "y" ]; then + hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' CONFIG_MAUI_BASE 330 + int 'Maui IRQ 5, 9, 12 or 15' CONFIG_MAUI_IRQ 9 + bool ' Have OSWF.MOT firmware file' CONFIG_MAUI_HAVE_BOOT + if [ "$CONFIG_MAUI_HAVE_BOOT" = "y" ]; then + string ' Full pathname of OSWF.MOT firmware file' CONFIG_MAUI_BOOT_FILE + fi + fi + + dep_tristate 'Support for Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_SGALAXY $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_SGALAXY" = "y" ]; then + hex 'SGALAXY audio I/O base 530, 604, E80 or F40' CONFIG_SGALAXY_BASE 530 + int 'SGALAXY audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_SGALAXY_IRQ 11 + int 'SGALAXY audio DMA 0, 1 or 3' CONFIG_SGALAXY_DMA 0 + int 'SGALAXY second (duplex) DMA 0, 1 or 3' CONFIG_SGALAXY_DMA2 3 + hex 'SGALAXY SB I/O base 220 or 240' CONFIG_SGALAXY_SGBASE 220 + fi + + dep_tristate 'Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_OPL3SA1 $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_OPL3SA1" = "y" ]; then + hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' CONFIG_OPL3SA1_BASE 530 + int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' CONFIG_OPL3SA1_IRQ 11 + int 'OPL3-SA1 audio DMA 0, 1 or 3' CONFIG_OPL3SA1_DMA 0 + int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' CONFIG_OPL3SA1_DMA2 3 + hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_OPL3SA1_MPU_BASE 330 + int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_OPL3SA1_MPU_IRQ 9 + fi + + dep_tristate 'SoftOSS software wave table engine' CONFIG_SOUND_SOFTOSS $CONFIG_SOUND_OSS + if [ "$CONFIG_SOUND_SOFTOSS" = "y" ]; then + int 'Sampling rate for SoftOSS 8000 to 48000' CONFIG_SOFTOSS_RATE 22050 + int 'Max # of concurrent voices for SoftOSS 4 to 32' CONFIG_SOFTOSS_VOICES 32 + fi + + dep_tristate 'FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_YM3812 $CONFIG_SOUND_OSS + + dep_tristate 'Loopback MIDI device support' CONFIG_SOUND_VMIDI $CONFIG_SOUND_OSS + + dep_tristate '6850 UART support' CONFIG_SOUND_UART6850 $CONFIG_SOUND_OSS + if [ "$CONFIG_UART6850" = "y" ]; then + hex 'I/O base for UART 6850 MIDI port (Unknown)' CONFIG_U6850_BASE 0 + int 'UART6850 IRQ (Unknown)' CONFIG_U6850_IRQ -1 + fi + + if [ "$CONFIG_ARM" = "y" ]; then + bool 'VIDC 16-bit sound' CONFIG_VIDC_SOUND + fi + + + + # Additional low level drivers. + + mainmenu_option next_comment + comment 'Additional low level sound drivers' + bool 'Additional low level sound drivers' CONFIG_LOWLEVEL_SOUND + if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then + source drivers/sound/lowlevel/Config.in + fi + endmenu fi + diff --git a/drivers/sound/Config.std b/drivers/sound/Config.std deleted file mode 100644 index b1bc3607904b..000000000000 --- a/drivers/sound/Config.std +++ /dev/null @@ -1,17 +0,0 @@ -# -# Sound driver configuration -# -#-------- -# There is another config script which is compatible with rest of -# the kernel. It can be activated by running 'make mkscript' in this -# directory. Please note that this is an _experimental_ feature which -# doesn't work with all cards (PSS, SM Wave, AudioTrix Pro, Maui). -#-------- -# -$MAKE -C drivers/sound config || exit 1 - -bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND - -if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then - bool 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER -fi diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index d5dfa70aedc0..881eafbb2948 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -1,162 +1,288 @@ # Makefile for the Linux sound card driver # -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes. (hopefully) -# -# +# 18 Apr 1998, Michael Elizabeth Chastain, +# Rewritten to use lists instead of if-statements. + -.PHONY: dummy -SUB_DIRS = lowlevel -VERSION = `head -1 .version` -TARGET_OS = linux -USRINCDIR = /usr/include -MODULEDIR = /lib/modules/misc -FIXEDOBJS = soundcard.o dev_table.o sound_switch.o +# My subdirectories. -ifndef NO_LOWLEVEL - FIXEDOBJS := $(FIXEDOBJS) lowlevel/lowlevel.o +SUB_DIRS := +MOD_SUB_DIRS := +MOD_IN_SUB_DIRS := +ALL_SUB_DIRS := $(SUB_DIRS) lowlevel + +ifeq ($(CONFIG_LOWLEVEL_SOUND),y) + ifeq ($(CONFIG_SOUND_OSS),y) + SUB_DIRS += lowlevel + else + MOD_SUB_DIRS += lowlevel + endif + MOD_IN_SUB_DIRS += lowlevel endif -ifeq (.defines,$(wildcard .defines)) -include .defines -include .objects + + +# All of the (potential) objects that export symbols. +# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. + +export-objs := ad1848.o audio_syms.o midi_syms.o mpu401.o \ + msnd.o opl3.o sb_card.o sequencer_syms.o \ + sound_core.o sound_firmware.o sound_syms.o \ + uart401.o + + + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + + + +# Each configuration option enables a list of files. + +ifeq ($(ARCH),m68k) + +obj-$(CONFIG_DMASOUND) += dmasound.o + else -OBJS = `cat .object_files` + +obj-$(CONFIG_SOUND) += soundcore.o +obj-$(CONFIG_SOUND_OSS) += sound.o +obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o +obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o + +# In theory, there's probably no reason to include the uart401 code +# to support a WaveFront card's CS4232 module. However, it makes +# reconfiguring things require a recompile, so just leave this +# here and try not to worry about the extra uart401 module. + +obj-$(CONFIG_SOUND_CS4232) += uart401.o +obj-$(CONFIG_SOUND_GUS) += gus.o ad1848.o +obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb.o uart401.o +obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o +obj-$(CONFIG_SOUND_MPU401) += mpu401.o +obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o +obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o +obj-$(CONFIG_SOUND_MSS) += ad1848.o +obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o +obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o uart401.o +obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_SB) += sb.o uart401.o +obj-$(CONFIG_SOUND_SOFTOSS) += softoss2.o +obj-$(CONFIG_SOUND_SGALAXY) += sgalaxy.o +obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o +obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb.o uart401.o +obj-$(CONFIG_SOUND_UART6850) += uart6850.c +obj-$(CONFIG_SOUND_VMIDI) += v_midi.o +obj-$(CONFIG_SOUND_YM3812) += adlib_card.o opl3.o +obj-$(CONFIG_SOUND_WAVEFRONT) += wavefront.o + +obj-$(CONFIG_SOUND_ES1370) += es1370.o +obj-$(CONFIG_SOUND_ES1371) += es1371.o +obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o + endif -ifndef TOPDIR -TOPDIR=/usr/src/linux + + +# Declare multi-part drivers. + +list-multi := sound.o gus.o pas2.o sb.o softoss2.o soundcore.o wavefront.o + +sound-objs := \ + dev_table.o soundcard.o sound_syms.o \ + audio.o audio_syms.o dmabuf.o \ + midi_syms.o midi_synth.o midibuf.o \ + sequencer.o sequencer_syms.o sound_timer.o sys_timer.o + +soundcore-objs := sound_core.o sound_firmware.o + +gus-objs := gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o +pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o +sb-objs := sb_audio.o sb_card.o sb_common.o sb_midi.o sb_mixer.o +softoss2-objs := softoss.o softoss_rs.o +wavefront-objs := wavfront.o wf_midi.o yss225.o + + +# Extract lists of the multi-part drivers. +# The 'int-*' lists are the intermediate files used to build the multi's. + +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + + + +# Files that are both resident and modular: remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + + + +# Set flags for secondary drivers. +# I have to do this before I reduce obj-y to components. + +EXTRA_CFLAGS := $(sort \ + $(patsubst ad1848.o, -DCONFIG_SOUND_AD1848, \ + $(patsubst mpu401.o, -DCONFIG_SOUND_MPU_EMU, \ + $(patsubst sb.o, -DCONFIG_SOUND_SBDSP, \ + $(patsubst uart401.o, -DCONFIG_SOUND_UART401, \ + $(filter ad1848.o mpu401.o sb.o uart401.o, $(obj-y)) \ + ))))) + + + +# Take multi-part drivers out of obj-y and put components in. + +obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) + + + +# Translate to Rules.make lists. + +L_TARGET := sound.a +MOD_LIST_NAME := SOUND_MODULES + +L_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +LX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) + +ifeq ($(CONFIG_LOWLEVEL_SOUND),y) + L_OBJS += lowlevel/lowlevel.o endif +include $(TOPDIR)/Rules.make + + + +# Link rules for multi-part drivers. + +sound.o: $(sound-objs) + $(LD) -r -o $@ $(sound-objs) + +soundcore.o: $(soundcore-objs) + $(LD) -r -o $@ $(soundcore-objs) + +gus.o: $(gus-objs) + $(LD) -r -o $@ $(gus-objs) + +pas2.o: $(pas2-objs) + $(LD) -r -o $@ $(pas2-objs) + +sb.o: $(sb-objs) + $(LD) -r -o $@ $(sb-objs) + +softoss2.o: $(softoss2-objs) + $(LD) -r -o $@ $(softoss2-objs) + + + +# 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. +# +# First make the utilities. + +bin2hex: bin2hex.c + $(HOSTCC) $(HOSTCFLAGS) -o bin2hex bin2hex.c + +hex2hex: hex2hex.c + $(HOSTCC) $(HOSTCFLAGS) -o hex2hex hex2hex.c + +wavefront.o: $(wavefront-objs) + $(LD) -r -o $@ $(wavefront-objs) -ifndef HOSTCC -build: - @echo Compiling modularized sound driver - @make sound.o - @echo Sound module compiled. -install: sound.o - cp sound.o $(MODULEDIR) +# Turtle Beach Maui / Tropez + +maui.o: maui_boot.h + +ifeq ($(CONFIG_MAUI_HAVE_BOOT),y) + maui_boot.h: $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) bin2hex + ./bin2hex maui_os < $(CONFIG_MAUI_BOOT_FILE) > $@ +else + maui_boot.h: + ( \ + echo 'static unsigned char * maui_os = NULL;'; \ + echo 'static int maui_osLen = 0;'; \ + ) > $@ endif + @ ( \ + echo 'ifeq ($(strip $(CONFIG_MAUI_HAVE_BOOT) $(CONFIG_MAUI_BOOT_FILE)),$$(strip $$(CONFIG_MAUI_HAVE_BOOT) $$(CONFIG_MAUI_BOOT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot -.c.o: - $(CC) $(CFLAGS) -c $< -ifeq ($(CONFIG_SOUND),y) -all: local.h sound.a +# PSS (ECHO-ADI2111) -OBJS += $(FIXEDOBJS) +pss.o: pss_boot.h +ifeq ($(CONFIG_PSS_HAVE_BOOT),y) + pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) bin2hex + ./bin2hex pss_synth < $(CONFIG_PSS_BOOT_FILE) > $@ else -all: + pss_boot.h: + ( \ + echo 'static unsigned char * pss_synth = NULL;'; \ + echo 'static int pss_synthLen = 0;'; \ + ) > $@ endif + @ ( \ + echo 'ifeq ($(strip $(CONFIG_PSS_HAVE_BOOT) $(CONFIG_PSS_BOOT_FILE)),$$(strip $$(CONFIG_PSS_HAVE_BOOT) $$(CONFIG_PSS_BOOT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot -ifndef HOSTCC -# -# Running outside the kernel build. -# -CC = gcc -HOSTCC = gcc -CFLAGS = -O2 -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe -m486 -USE_DEPEND=y + + +# MediaTrix AudioTrix Pro + +trix.o: trix_boot.h + +ifeq ($(CONFIG_TRIX_HAVE_BOOT),y) + trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) hex2hex + hex2hex trix_boot < $(CONFIG_TRIX_BOOT_FILE) > $@ else -include $(TOPDIR)/Rules.make + trix_boot.h: + ( \ + echo 'static unsigned char * trix_boot = NULL;'; \ + echo 'static int trix_boot_len = 0;'; \ + ) > $@ endif + @ ( \ + echo 'ifeq ($(strip $(CONFIG_TRIX_HAVE_BOOT) $(CONFIG_TRIX_BOOT_FILE)),$$(strip $$(CONFIG_TRIX_HAVE_BOOT) $$(CONFIG_TRIX_BOOT_FILE)))'; \ + echo 'FILES_BOOT_UP_TO_DATE += $@'; \ + echo 'endif' \ + ) > .$@.boot -sound.a: $(OBJS) - -rm -f sound.a - $(AR) rcs sound.a $(OBJS) - sync - -clean: - rm -f core core.* *.o *.a tmp_make *~ x y z *% - rm -f configure sound_stub.c objects/*.o - cd lowlevel;make clean - -indent: - for n in *.c;do echo indent $$n;indent $$n;done - -local.h: - $(MAKE) clean - $(MAKE) setup-$(TARGET_OS) - $(MAKE) oldconfig - $(MAKE) dep - @echo - @echo - @echo - @echo NOTE! Object file dependencies may not be up to date. Run - @echo make again if kernel/driver doesn''t link properly. Restarting - @echo it now may save some time. - @echo - @echo - -config: configure - @$(MAKE) setup-$(TARGET_OS) - @./configure > local.h - @echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h - @echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h -# @echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null -# @echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null - @echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h - -oldconfig: setup-$(TARGET_OS) configure - @./configure -o > local.h - @echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h - @echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h -# @echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null -# @echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null - @echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h - -kernelconfig: setup-$(TARGET_OS) - rm -f configure - $(HOSTCC) -o configure configure.c - ./configure fixedlocal > local.h - ./configure fixeddefines > .defines - @echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h - @echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h -# @echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null -# @echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null - @echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h - -mkscript: setup-$(TARGET_OS) - rm -f configure - $(HOSTCC) -o configure configure.c - ./configure script > Config.in - cat lowlevel/Config.tmpl >> Config.in - ./configure fixedlocal > local.h - ./configure fixeddefines > .defines - -clrconf: - rm -f local.h .depend synth-ld.h trix_boot.h smw-midi0001.h maui_boot.h .defines - -configure: configure.c - $(HOSTCC) -o configure configure.c - @cat .blurb - -dep: - $(CPP) -M *.c > .depend - -setup-linux: - @echo Compiling Sound Driver v $(VERSION) for Linux - -sound.o: local.h $(FIXEDOBJS) sound.a - -rm -f sound.o - $(LD) -r -o sound.o $(FIXEDOBJS) sound.a - -modules: local.h sound.o - ln -fs `pwd`/sound.o $(TOPDIR)/modules/sound.o - - -lowlevel/lowlevel.o: dummy - cd lowlevel;make - -ifdef USE_DEPEND -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend + + +# Find boot files whose source file names have changed and force rebuild. + +FILES_BOOT_UP_TO_DATE := + +FILES_BOOT_EXIST := $(wildcard .*.boot) +ifneq ($(FILES_BOOT_EXIST),) +include $(FILES_BOOT_EXIST) endif + +FILES_BOOT_CHANGED := $(strip \ + $(filter-out $(FILES_BOOT_UP_TO_DATE), \ + maui_boot.h pss_boot.h trix_boot.h)) + +ifneq ($(FILES_BOOT_CHANGED),) +$(FILES_BOOT_CHANGED): dummy endif diff --git a/drivers/sound/README.CONFIG b/drivers/sound/README.CONFIG new file mode 100644 index 000000000000..5cd199230cd8 --- /dev/null +++ b/drivers/sound/README.CONFIG @@ -0,0 +1,75 @@ +Sound Driver Configuration Notes +Michael Chastain, +18 Apr 1998 + +The Linux sound driver is derived from OSS/Free, a multi-platform +Unix sound driver by Hannu Savolainen. You can find out +more about OSS/Free and the commercial version, OSS/Linux, at +. + +OSS/Free comes with the configuration program 'configure.c'. We have +discarded that program in favor of a standard Linux configuration file +Config.in. + +Config.in defines a set of symbols with the form CONFIG_SOUND_*. +These are the -native symbols-. Here is a description: + + CONFIG_SOUND + + This is the master symbol. It controls whether the basic + sound-driver code is resident, modular, or not present at all. + + If the basic driver is resident, each primary and secondary + driver can be resident, modular, or not present. + + If the basic driver is modular, each primary and secondary driver + can be modular or not present. + + And if the basic driver is not present, all other drivers are + not present, too. + + Primary drivers + + These are symbols such as CONFIG_SOUND_SB, CONFIG_SOUND_SB_MODULE, + CONFIG_SOUND_TRIX, or CONFIG_SOUND_TRIX_MODULE. Each driver + that the user can directly select is a primary driver and has + the usual pair of symbols: one resident and one modular. + + Each primary driver can be either resident or modular. + + Secondary drivers + + Primary drivers require the support of secondary drivers, such + as ad1848.o and uart401.o. + + In Makefile, each primary driver has a list of required secondary + drivers. The secondary driver requirements are merged and a + single definition is emitted at the end. + + For each secondary driver: if any resident primary driver + requires it, that secondary driver will be resident. If no + resident primary driver requires it but some modular primary + driver requires it, then that secondary driver will be modular. + Otherwise that secondary driver will be not present. + + OSS/Free also contains tests for secondary drivers. The Makefile + defines symbols for these drivers in EXTRA_CFLAGS. + + CONFIG_AUDIO, CONFIG_MIDI, CONFIG_SEQUENCER + + These three drivers are like secondary drivers, but not quite. + They can not yet be separated into modules. They are always + linked into the basic sound driver, whether they are needed + or not. (This is in case a primary driver is added to the + system later, as a module, and needs these facilities. If it + were possible to modularise them, then they would get built as + additional modules at that time). + +The OSS/Free code does not use the native symbols directly, primarily +because it does not know about modules. I could edit the code, but that +would make it harder to upgrade to new versions of OSS/Free. Instead, +the OSS/Free code continues to use -legacy symbols-. + +legacy.h defines all the legacy symbols to 1. This is because, whenever +OSS/Free tests a symbol, the Makefile has already arranged for that +driver to be included. diff --git a/drivers/sound/README.FIRST b/drivers/sound/README.FIRST new file mode 100644 index 000000000000..3a6b60483497 --- /dev/null +++ b/drivers/sound/README.FIRST @@ -0,0 +1,7 @@ +The modular sound driver patches where 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 + diff --git a/drivers/sound/README.blurb b/drivers/sound/README.blurb new file mode 100644 index 000000000000..5203d3fbe24d --- /dev/null +++ b/drivers/sound/README.blurb @@ -0,0 +1,10 @@ +********************************************************* +* Readme.cards (this directory) contains some card * +* specific instructions. * +* See http://www.4front-tech.com/ossfree for most up * +* to date info. * +* (European mirror http://personal.eunet.fi/pp/voxware) * +* * +* DON'T USE PROGRAMS FROM snd-util PACKAGE EARLIER THAN * +* snd-util-3.5 WITH THIS SOUND DRIVER VERSION. * +********************************************************* diff --git a/drivers/sound/README.wavefront b/drivers/sound/README.wavefront new file mode 100644 index 000000000000..9e39f5d8d885 --- /dev/null +++ b/drivers/sound/README.wavefront @@ -0,0 +1,375 @@ + An OSS/Free Driver for WaveFront Sound Cards + (Turtle Beach Maui, Tropez, Tropez Plus) + + Paul Barton-Davis, July 1998 + + VERSION 0.2.2 + +Driver Status +------------- + +Requires: Kernel 2.1.106 or later + +As of 7/13/1998, this driver is currently in *BETA* state. This means +that it compiles and runs, and that I use it on my system (Linux +2.1.106) with some reasonably demanding applications and uses. I +believe the code is approaching an initial "finished" state that +provides bug-free support for the Tropez Plus. + +Please note that to date, the driver has ONLY been tested on a Tropez +Plus. I would very much like to hear (and help out) people with Tropez +and Maui cards, since I think the driver can support those cards as +well. + +Finally, the driver has not been tested as a static (non-modular) part +of the kernel. Alan Cox's good work in modularizing OSS/Free for Linux +makes this rather unnecessary. + +Some Questions +-------------- + +********************************************************************** +0) What does this driver do that the maui driver did not ? +********************************************************************** + +* can fully initialize a WaveFront card from cold boot - no DOS + utilities needed +* working patch/sample/program loading and unloading (the maui + driver didn't document how to make this work, and assumed + user-level preparation of the patch data for writing + to the board. ick.) +* full user-level access to all WaveFront commands +* for the Tropez Plus, (primitive) control of the YSS225 FX processor +* Virtual MIDI mode supported - 2 MIDI devices accessible via the + WaveFront's MPU401/UART emulation. One + accesses the WaveFront synth, the other accesses the + external MIDI connector. Full MIDI read/write semantics + for both devices. +* OSS-compliant /dev/sequencer interface for the WaveFront synth, + including native and GUS-format patch downloading. +* semi-intelligent patch management (prototypical at this point) + + +********************************************************************** +1) What to do about MIDI interfaces ? +********************************************************************** + +The Tropez Plus (and perhaps other WF cards) can in theory support up +to 2 physical MIDI interfaces. One of these is connected to the +ICS2115 chip (the WaveFront synth itself) and is controlled by +MPU/UART-401 emulation code running as part of the WaveFront OS. The +other is controlled by the CS4232 chip present on the board. However, +physical access to the CS4232 connector is difficult, and it is +unlikely (though not impossible) that you will want to use it. + +An older version of this driver introduced an additional kernel config +variable which controlled whether or not the CS4232 MIDI interface was +configured. Because of Alan Cox's work on modularizing the sound +drivers, and now backporting them to 2.0.34 kernels, there seems to be +little reason to support "static" configuration variables, and so this +has been abandoned in favor of *only* module parameters. Specifying +"mpuio" and "mpuirq" for the cs4232 parameter will result in the +CS4232 MIDI interface being configured; leaving them unspecified will +leave it unconfigured (and thus unusable). + +BTW, I have heard from one Tropez+ user that the CS4232 interface is +more reliable than the ICS2115 one. I have had no problems with the +latter, and I don't have the right cable to test the former one +out. Reports welcome. + +********************************************************************** +2) Why does line XXX of the code look like this .... ? +********************************************************************** + +Either because its not finished yet, or because you're a better coder +than I am, or because you don't understand some aspect of how the card +or the code works. + +I absolutely welcome comments, criticisms and suggestions about the +design and implementation of the driver. + +********************************************************************** +3) What files are included ? +********************************************************************** + + drivers/sound/README.wavefront -- this file + drivers/sound/wavefront.patch -- patches for the 2.1.106 sound drivers + needed to make the rest of this work + drivers/sound/wavfront.c -- the driver + drivers/sound/ys225.h -- data declarations for FX config + drivers/sound/ys225.c -- data definitions for FX config + drivers/sound/wf_midi.c -- the "uart401" driver + to support virtual MIDI mode. + include/wavefront.h -- the header file + Documentation/sound/Tropez+ -- short docs on configuration + +********************************************************************** +4) How do I compile/install/use it ? +********************************************************************** + +PART ONE: install the source code into your sound driver directory + + cd + tar -zxvf + +PART TWO: apply the patches + + cd drivers/sound + patch < wavefront.patch + +PART THREE: configure your kernel + + cd + make xconfig (or whichever config option you use) + + - choose YES for Sound Support + - choose MODULE (M) for OSS Sound Modules + - choose MODULE(M) to YM3812/OPL3 support + - choose MODULE(M) for WaveFront support + - choose MODULE(M) for CS4232 support + + - choose "N" for everything else (unless you have other + sound cards you want support for) + + + make dep + make boot + . + . + . + + make modules + . + . + . + make modules_install + +Here's my autoconf.h SOUND section: + +/* + * Sound + */ +#define CONFIG_SOUND 1 +#undef CONFIG_SOUND_OSS +#define CONFIG_SOUND_OSS_MODULE 1 +#undef CONFIG_SOUND_PAS +#undef CONFIG_SOUND_SB +#undef CONFIG_SOUND_ADLIB +#undef CONFIG_SOUND_GUS +#undef CONFIG_SOUND_MPU401 +#undef CONFIG_SOUND_PSS +#undef CONFIG_SOUND_MSS +#undef CONFIG_SOUND_SSCAPE +#undef CONFIG_SOUND_TRIX +#undef CONFIG_SOUND_MAD16 +#undef CONFIG_SOUND_WAVEFRONT +#define CONFIG_SOUND_WAVEFRONT_MODULE 1 +#undef CONFIG_SOUND_CS4232 +#define CONFIG_SOUND_CS4232_MODULE 1 +#undef CONFIG_SOUND_MAUI +#undef CONFIG_SOUND_SGALAXY +#undef CONFIG_SOUND_OPL3SA1 +#undef CONFIG_SOUND_SOFTOSS +#undef CONFIG_SOUND_YM3812 +#define CONFIG_SOUND_YM3812_MODULE 1 +#undef CONFIG_SOUND_VMIDI +#undef CONFIG_SOUND_UART6850 +/* + * Additional low level sound drivers + */ +#undef CONFIG_LOWLEVEL_SOUND + +************************************************************ +6) How do I configure my card ? +************************************************************ + +You need to edit /etc/conf.modules. Here's mine (edited to show the +relevant details): + + # Sound system + alias char-major-14 wavefront + alias synth0 wavefront + alias mixer0 cs4232 + alias audio0 cs4232 + pre-install wavefront modprobe "-k" "cs4232" + post-install wavefront modprobe "-k" "opl3" + options wavefront io=0x200 irq=9 + options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 + options opl3 io=0x388 + +Things to note: + + the wavefront options "io" and "irq" ***MUST*** match the "synthio" + and "synthirq" cs4232 options. + + you can do without the adlib_card module if you don't + want to use the OPL/[34] synth on the sound card + + the adlib_card io parameter is conventionally not adjustable. + In theory, any not-in-use IO port address would work, but + just use 0x388 and stick with the crowd. + +********************************************************************** +7) What about firmware ? +********************************************************************** + +Turtle Beach have not given me permission to distribute their firmware +for the ICS2115. However, if you have a WaveFront card, then you +almost certainly have the firmware, and if not, its freely available +on their website, at: + + http://www.tbeach.com/tbs/downloads/scardsdown.htm#tropezplus + +The file is called WFOS2001.MOT (for the Tropez+). + +This driver, however, doesn't use the pure firmware as distributed, +but instead relies on a somewhat processed form of it. You can +generate this very easily. Following an idea from Andrew Veliath's +Pinnacle driver, the following flex program will generate the +processed version: + +---- cut here ------------------------- +%option main +%% +^S[28].*\r$ printf ("%c%.*s", yyleng-1,yyleng-1,yytext); +<> { fputc ('\0', stdout); return; } +\n {} +. {} +---- cut here ------------------------- + +To use it, put the above in file (say, ws.l) compile it like this: + + shell> flex -ows.c ws.l + shell> cc -o ws ws.c + +and then use it like this: + + ws < my-copy-of-the-oswf.mot-file > /etc/sound/wavefront.os + +If you put it somewhere else, you'll always have to use the wf_ospath +module parameter (see below) or alter the source code. + +********************************************************************** +7) How do I get it working ? +********************************************************************** + +Optionally, you can reboot with the "new" kernel (even though the only +changes have really been made to a module). + +Then, as root do: + + modprobe wavefront + +You should get something like this in /var/log/messages: + + WaveFront: firmware 1.20 already loaded. + +or + + WaveFront: no response to firmware probe, assume raw. + +then: + + WaveFront: waiting for memory configuration ... + WaveFront: hardware version 1.64 + WaveFront: available DRAM 8191k + WaveFront: 332 samples used (266 real, 13 aliases, 53 multi), 180 empty + WaveFront: 128 programs slots in use + WaveFront: 256 patch slots filled, 142 in use + +The whole process takes about 16 seconds, the longest waits being +after reporting the hardware version (during the firmware download), +and after reporting program status (during patch status inquiry). Its +shorter (about 10 secs) if the firmware is already loaded (i.e. only +warm reboots since the last firmware load). + +The "available DRAM" line will vary depending on how much added RAM +your card has. Mine has 8MB. + +Next, check /dev/sndstat, which on my machine says: +--------------------------------------------------------------------- +OSS/Free:3.8s2++-971130 +Load type: Driver loaded as a module +Kernel: Linux bd 2.1.106 #12 SMP Fri Jul 3 00:37:34 EDT 1998 i486 +Config options: 0 + +Installed drivers: + +Card config: + +Audio devices: +0: Crystal audio controller (CS4232) (DUPLEX) + +Synth devices: +0: Turtle Beach WaveFront +1: Yamaha OPL-3 + +Midi devices: +0: WaveFront Internal MIDI +1: WaveFront External MIDI + +Timers: +0: System clock +1: Crystal audio controller (CS4232) + +Mixers: +0: Crystal audio controller (CS4232) +----------------------------------------------------------- + +To check basically functionality, use play(1) or splay(1) to send a +.WAV or other audio file through the audio portion. Then use playmidi +to play a General MIDI file. Try the "-D 0" to hear the +difference between sending MIDI to the WaveFront and using the OPL/3, +which is the default (I think ...). If you have an external synth(s) +hooked to the sound card, you can use "-e" to route to the +external synth(s) (in theory, -D 1 should work as well, but I think +there is a bug in playmidi which prevents this from doing what it +should). + +********************************************************************** +8) What are the module parameters ? +********************************************************************** + +Its best to read wavefront.c for this, but here is a summary: + +integers: + wf_raw - if set, ignore apparent presence of firmware + loaded onto the ICS2115, reset the whole + board, and initialize it from scratch. (default = 0) + + fx_raw - if set, always initialize the YSS225 processor + on the Tropez plus. (default = 1) + + < The next 4 are basically for kernel hackers to allow + tweaking the driver for testing purposes. > + + wf_short_wait_count - loop counter used when waiting for + status conditions on the board. This + is CPU-specific. After this many + loops, the driver will sleep. + The default is 5000. I have a 66Mhz 486. + + wf_sleep_interval - the driver sleeps for + HZ/wf_sleep_interval seconds per sleep. + The default is 50. + + wf_sleep_tries - the number of times the driver will sleep + when waiting for a status condition on the + board. The default is 100 (2 secs, if + wf_sleep_interval is 50). + + wf_debug_default - debugging flags. See sound/wavefront.h + for WF_DEBUG_* values. Default is zero. + Setting this allows you to debug the + driver during module installation. +strings: + wf_ospath - path to get to the pre-processed OS firmware. + (default: /etc/sound/wavefront.os) + +********************************************************************** +9) Who should I contact if I have problems? +********************************************************************** + +Just me: Paul Barton-Davis + + diff --git a/drivers/sound/Readme b/drivers/sound/Readme index faf1b40517ca..2bb76549f5f8 100644 --- a/drivers/sound/Readme +++ b/drivers/sound/Readme @@ -1,41 +1,38 @@ -USS Lite version 3.5.4 release notes ------------------------------------- +OSS/Free version 3.8 release notes +---------------------------------- Most up to date information about this driver is available from -http://www.4front-tech.com/ossfree. +http://www.opensound.com/ossfree. Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP sites). It gives instructions about using sound with Linux. It's bit out of date but still very useful. Information about bug fixes and such things -is available from the web page (see below). +is available from the web page (see above). -New Programmer's Guide is currently under work (June/July 96). Please -check http://www.4front-tech.com/pguide for more info. +Please check http://www.opensound.com/pguide for more info about programming +with OSS API. ==================================================== -- THIS VERSION ____REQUIRES____ Linux 1.3.70 OR LATER. +- THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER. ==================================================== -It's very likely that this driver version is incompatible with -Linux versions later than 2.0.x. - -Packages "snd-util-3.5.tar.gz" and "snd-data-0.1.tar.Z" +Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z" contain useful utilities to be used with this driver. -See http://www.4front-tech.com/ossfree/getting.html for +See http://www.opensound.com/ossfree/getting.html for download instructions. If you are looking for the installation instructions, please look at Readme.linux. -Supported soundcards --------------------- +Supported sound cards +--------------------- See Readme.cards. -Please check http://www.4front-tech.com/ossfree if you don't find -your soundcard there. +Please check http://www.opensound.com/ossfree if you don't find +your sound card there. Contributors ------------ @@ -72,12 +69,26 @@ contributors. (I could have forgotten some names.) Gregor Hoffleit Mozart support (initial version) Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support James Hightower Spotting a tiny but important bug in CS423x support. + Denis Sablic OPTi 82C924 spesific enhancements (non PnP mode) + Tim MacKenzie Full duplex support for OPTi 82C930. Please look at lowlevel/README for more contributors. There are probably many other names missing. If you have sent me some patches and your name is not in the above list, please inform me. +Sending your contributions or patches +------------------------------------- + +First of all it's highly recommended to contact me before sending anything +or before even starting to do any work. Tell me what you suggest to be +changed or what you have planned to do. Also ensure you are using the +very latest (development) version of OSS/Free since the change may already be +implemented there. In general it's major waste of time to try to improve +several months old version. Information about the latest version can be found +from http://www.opensound.com/ossfree. In general there is no point in +sending me patches relative to production kernels. + Sponsors etc. ------------- @@ -112,13 +123,13 @@ If you have some problems ========================= Read the sound HOWTO (sunsite.unc.edu:/pub/Linux/docs/...?). -Also look at the home page (http://www.4front-tech.com/ossfree). It may +Also look at the home page (http://www.opensound.com/ossfree). It may contain info about some recent bug fixes. It's likely that you have some problems when trying to use the sound driver -first time. Soundcards don't have standard configuration so there are no +first time. Sound cards don't have standard configuration so there are no good default configuration to use. Please try to use same I/O, DMA and IRQ -values for the soundcard than with DOS. +values for the sound card than with DOS. If you get an error message when trying to use the driver, please look at /var/adm/messages for more verbose error message. @@ -148,7 +159,7 @@ The following errors are likely with /dev/dsp and /dev/audio. work if "digitized voice support" was not enabled during "make config". - "Device or resource busy". Probably the IRQ (or DMA) channel - required by the soundcard is in use by some other device/driver. + required by the sound card is in use by some other device/driver. - "I/O error". Almost certainly (99%) it's an IRQ or DMA conflict. Look at the kernel messages in /var/adm/notice for more info. @@ -166,8 +177,8 @@ Best regards, Hannu Hannu Savolainen -hannu@4front-tech.com -(Please check http://www.4front-tech.com/ossfree before mailing me). +hannu@opensound.com +(Please check http://www.opensound.com/ossfree before mailing me). Snail mail: Hannu Savolainen Hiekkalaiturintie 3 A 8 diff --git a/drivers/sound/Readme.aedsp16 b/drivers/sound/Readme.aedsp16 deleted file mode 100644 index 13575eb22007..000000000000 --- a/drivers/sound/Readme.aedsp16 +++ /dev/null @@ -1,6 +0,0 @@ -Information about Audio Excel DSP 16 can be found in the source -file aedsp16.c -Please, read the head of the source before using it. It contain useful -information. - - Riccardo diff --git a/drivers/sound/Readme.cards b/drivers/sound/Readme.cards index aeee06572921..45cad259e400 100644 --- a/drivers/sound/Readme.cards +++ b/drivers/sound/Readme.cards @@ -1,119 +1,160 @@ -Configuring version 3.5.4 (for Linux) with some most common soundcards -====================================================================== +Configuring version 3.8 (for Linux) with some common sound cards +================================================================ + +This document describes configuring sound cards with the freeware version of +Open Sound Systems (OSS/Free). Information about the commercial version +(OSS/Linux) and its configuration is available from +http://www.opensound.com/linux.html. Information presented here is +not valid for OSS/Linux. + +If you are unsure about how to configure OSS/Free +you can download the free evaluation version of OSS/Linux from the above +address. There is a chance that it can autodetect your sound card. In this case +you can use the information included in soundon.log when configuring OSS/Free. + IMPORTANT! This document covers only cards that were "known" when this driver version was released. Please look at - http://www.4front-tech.com/ossfree for info about + http://www.opensound.com/ossfree for info about cards introduced recently. - The following covers mainly the "old" configuration - method (make config). Most of it is valid for the "new" - configuration (make menuconfig/xconfig) too. - - Cards having some kind of loadable "microcode" such as - PSS, SM Wave, AudioTrix Pro and Maui/Tropez must be - configured using the old method. The new one will not - work with them. + When configuring the sound driver, you should carefully + check each sound configuration option (particularly + "Support for /dev/dsp and /dev/audio"). The default values + offered by these programs are not necessarily valid. + + +THE BIGGEST MISTAKES YOU CAN MAKE +================================= + +1. Assuming that the card is Sound Blaster compatible when it's not. +-------------------------------------------------------------------- + +The number one mistake is to assume that your card is compatible with +Sound Blaster. Only the cards made by Creative Technology or which have +one or more chips labeled by Creative are SB compatible. In addition there +are few sound chipsets which are SB compatible in Linux such as ESS1688 or +Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything +in Linux. + +IF YOU REALLY ARE 150% SURE YOU HAVE A SOUND BLASTER YOU CAN SKIP THE REST OF +THIS CHAPTER. + +For most other "supposed to be SB compatible" cards you have to use other +than SB drivers (see below). It is possible to get most sound cards to work +in SB mode but in general it's a complete waste of time. There are several +problems which you will encounter by using SB mode with cards that are not +truly SB compatible: + +- The SB emulation is at most SB Pro (DSP version 3.x) which means that +you get only 8 bit audio (there is always an another ("native") mode which +gives the 16 bit capability). The 8 bit only operation is the reason why +many users claim that sound quality in Linux is much worse than in DOS. +In addition some applications require 16 bit mode and they produce just +noise with a 8 bit only device. +- The card may work only in some cases but refuse to work most of the +time. The SB compatible mode always requires special initialization which is +done by the DOS/Windows drivers. This kind of cards work in Linux after +you have warm booted it after DOS but they don't work after cold boot +(power on or reset). +- You get the famous "DMA timed out" messages. Usually all SB clones have +software selectable IRQ and DMA settings. If the (power on default) values +currently used by the card don't match configuration of the driver you will +get the above error message whenever you try to record or play. There are +few other reasons to the DMA timeout message but using the SB mode seems +to be the most common cause. + +2. Trying to use a PnP (Plug & Play) card just like an ordinary sound card +-------------------------------------------------------------------------- + +Plug & Play is a protocol defined by Intel and Microsoft. It lets operating +systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA +cards. The problem with PnP cards is that the standard Linux doesn't currently +(versions 2.1.x and earlier) don't support PnP. This means that you will have +to use some special tricks (see later) to get a PnP card alive. Many PnP cards +work after they have been initialized but this is not always the case. + +There are sometimes both PnP and non-PnP versions of the same sound card. +The non-PnP version is the original model which usually has been discontinued +more than an year ago. The PnP version has the same name but with "PnP" +appended to it (sometimes not). This causes major confusion since the non-PnP +model works with Linux but the PnP one doesn't. + +You should carefully check if "Plug & Play" or "PnP" is mentioned in the name +of the card or in the documentation or package that came with the card. +Everything described in the rest of this document is not necessarily valid for +PnP models of sound cards even you have managed to wake up the card properly. +Many PnP cards are simply too different from their non-PnP ancestors which are +covered by this document. - When using make xconfig and/or make menuconfig, you should - carefully check each sound configuration option (particularly - "Support for /dev/dsp and /dev/audio"). Cards that are not (fully) supported by this driver ---------------------------------------------------- - -There are many soundcards which don't work with this driver -version (v3.5). Support for some of them is expected to be -available during/after summer 1996 (in version 3.6). Please check -http://www.4front-tech.com/ossfree for latest news. Please don't -mail me and ask about these cards. The unsupported cards are: - - - All PnP soundcards (SB PnP, GUS PnP, Soundscape PnP etc.) - Schedule for availability of PnP soundcard support in - OSS/Free depends on progress made by kernel PnP team - (probably in Linux 2.1.xx versions). With Linux 2.0.x - versions there are two ways to get PnP soundcards to work: - - Use isapnptools, DOS, Win95 or PnP aware BIOS to wake up the - card before starting the sound driver. See "Configuring PnP - soundcards" below for some hints. - - Support for SB PnP and GUS PnP is present in OSS/Linux (the - commercial version of this driver). - - Mwave soundcards and motherboards - (Version 3.6 or 3.7. Depends on how fast I get - suitable documents for Mwave). - - Emu8k (SB 32/AWE) - (Probably not _before_ summer 97. I know the unofficial - AWE programmers guide so don't send me more copies of it). - - Diamond Edge 3D - (ASAP. In practice this may take relatively long time). - - Compaq Deskpro - (Version 3.5.4-beta6 (already released)) - - Sound Galaxy Washington/Waverider - (Audio features already in OSS/Linux (OSS/Free soon). - Can't promise the waverider synth since - availability of chip specs is uncertain). - - Yamaha OPL4 (on cards having _RAM_ for samples) - (Late 96?. Works as OPL3 with current driver versions) +=================================================== + +See http://www.opensound.com/ossfree for information about sound cards +to be supported in future. + How to use sound without recompiling kernel and/or sound driver ---------------------------------------------------------------- +=============================================================== + +There is a commercial sound driver which comes in precompiled form and doesn't +require recompiling of the kernel. See http://www.4Front-tech.com/oss.html for +more info. -There is commercial sound driver which should be released during Apr 96. -It comes in precompiled form and doesn't require recompiling of kernel. See -http://www.4Front-tech.com/uss.html for more info. Configuring PnP cards ---------------------- +===================== -New versions of most soundcards use so called ISA PnP protocol for +New versions of most sound cards use the so-called ISA PnP protocol for soft configuring their I/O, IRQ, DMA and shared memory resources. Currently at least cards made by Creative Technology (SB32 and SB32AWE PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and -Aztech (some Sound Galaxy models) use PnP technology. The CS4232 audio +Aztech (some Sound Galaxy models) use PnP technology. The CS4232/4236 audio chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other motherboards) is also based on PnP technology but there is a "native" driver available for it (see information about CS4232 later in this document). -PnP soundcards (as well as most other PnP ISA cards) are not supported -by version 3.5 of this driver (Linux 1.3.xx and Linux 2.0.x). Proper -support for them should be released during spring 96 -(see http://www.4front-tech.com/ossfree for latest info). +PnP sound cards (as well as most other PnP ISA cards) are not supported +by this version of the driver . Proper +support for them should be released during 97 once the kernel level +PnP support is available. There is a method to get most of the PnP cards to work. The basic method is the following: -1) Boot DOS so that card's DOS drivers have chance to initialize the -card. -2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del -works with older machines but causes hard reset of all cards on latest +1) Boot DOS so the card's DOS drivers have a chance to initialize it. +2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del +works with older machines but causes a hard reset of all cards on recent (Pentium) machines. -3) If you have sound driver in Linux configured properly, the card should work -now. "Proper" means here that I/O, IRQ and DMA settings are the same than in -DOS. The hard part is to find which settings were used. See documentation of +3) If you have the sound driver in Linux configured properly, the card should +work now. "Proper" means that I/O, IRQ and DMA settings are the same as in +DOS. The hard part is to find which settings were used. See the documentation of your card for more info. -Windows 95 could work as well as DOS but running loadlin may be somehow -difficult. Probably you should "shut down" your machine to MS-DOS mode -before running it. +Windows 95 could work as well as DOS but running loadlin may be difficult. +Probably you should "shut down" your machine to MS-DOS mode before running it. -Some machines have BIOS utility for setting PnP resources. This is a good +Some machines have a BIOS utility for setting PnP resources. This is a good way to configure some cards. In this case you don't need to boot DOS/Win95 -prior starting Linux. +before starting Linux. + Another way to initialize PnP cards without DOS/Win95 is a Linux based PnP isolation tool. When writing this there is a pre alpha test version -of such tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The +of such a tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The file is called isapnptools-*. Please note that this tool is just a temporary solution which may be incompatible with future kernel versions having proper support for PnP cards. There are bugs in setting DMA channels in earlier -versions of isapnptools so at least version 1.6 is required with soundcards. -You can find latest version of isapnptools from -ftp://ftp.demon.co.uk/pub/unix/linux/utils/ +versions of isapnptools so at least version 1.6 is required with sound cards. + +Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers. See +http://www.opensound.com/linux.html for more info. This is probably the way you +should do it if you don't want to spend time recompiling the kernel and +required tools. -These two methods don't work with GUS PnP which requires some additional -initialization (cards DOS/Win95 driver does it). Read this before trying to configure the driver ------------------------------------------------ +=============================================== There are currently many cards that work with this driver. Some of the cards have native support while others work since they emulate some other @@ -138,31 +179,57 @@ Sound Blasters SB 1.0 to 2.0 SB Pro SB 16 - NOTE! The ASP chip and the EMU synth of AWE32 are not supported - since their manufacturer doesn't release information about - the card. However, both SB16ASP and AWE32 work with - the driver just like a SB16. Also see the comment about some - unsupported cards at the end of this file. - (The OPL3 FM chip of SB32/AWE works but you cannot hear it). + SB32/64/AWE + Configure SB32/64/AWE just like SB16. See lowlevel/README.awe + for information about using the wave table synth. + NOTE! AWE63/Gold and 16/32/AWE "PnP" cards need to be activated + using isapnptools before they work with OSS/Free. SB16 compatible cards by other manufacturers than Creative. - You have been fooled since there are no SB16 compatible - cards on the market (Feb 96). It's likely that your card + You have been fooled since there are _no_ SB16 compatible + cards on the market (as of May 1997). It's likely that your card is compatible just with SB Pro but there is also a non-SB- compatible 16 bit mode. Usually it's MSS/WSS but it could also - be a proprietary one like MV Jazz16 or ESS ES688. + be a proprietary one like MV Jazz16 or ESS ES688. OPTi + MAD16 chips are very common in so called "SB 16 bit cards" + (try with the MAD16 driver). + + ====================================================================== + "Supposed to be SB compatible" cards. + Forget the SB compatibility and check for other alternatives + first. The only cards that work with the SB driver in + Linux have been made by Creative Technology (there is at least + one chip on the card with "CREATIVE" printed on it). The + only other SB compatible chips are ESS and Jazz16 chips + (maybe ALSxxx chips too but they probably don't work). + Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or + "Crystal" are _NOT_ SB compatible in Linux. + + Practically all sound cards have some kind of SB emulation mode + in addition to their native (16 bit) mode. In most cases this + (8 bit only) SB compatible mode doesn't work with Linux. If + you get it working it may cause problems with games and + applications which require 16 bit audio. Some 16 bit only + applications don't check if the card actually supports 16 bits. + They just dump 16 bit data to a 8 bit card which produces just + noise. + + In most cases the 16 bit native mode is supported by Linux. + Use the SB mode with "clones" only if you don't find anything + better from the rest of this doc. + ====================================================================== Gravis Ultrasound (GUS) GUS GUS + the 16 bit option GUS MAX GUS ACE (No MIDI port and audio recording) - GUS PnP (Partially supported) + GUS PnP (with RAM) MPU-401 and compatibles The driver works both with the full (intelligent mode) MPU-401 cards (such as MPU IPC-T and MQX-32M) and with the UART only dumb MIDI ports. MPU-401 is currently the most common MIDI - interface. Most soundcards are compatible with it. However, + interface. Most sound cards are compatible with it. However, don't enable MPU401 mode blindly. Many cards with native support in the driver have their own MPU401 driver. Enabling the standard one will cause a conflict with these cards. So check if your card is @@ -173,7 +240,7 @@ Windows Sound System (MSS/WSS) they managed to make it a standard. MSS compatible cards are based on a codec chip which is easily available from at least two manufacturers (AD1848 by Analog Devices and CS4231/CS4248 by Crystal Semiconductor). - Currently most soundcards are based on one of the MSS compatible codec + Currently most sound cards are based on one of the MSS compatible codec chips. The CS4231 is used in the high quality cards such as GUS MAX, MediaTrix AudioTrix Pro and TB Tropez (GUS MAX is not MSS compatible). @@ -186,12 +253,8 @@ Windows Sound System (MSS/WSS) cause a conflict. So check if your card is listed in this file before enabling the MSS support. -6850 UART MIDI - This UART chip is used in the MIDI interface of some (rare) - soundcards. It's supported by the driver in case you need it. - -Yamaha FM synthesizers (OPL2, OPL3 and OPL4) - Most soundcards have a FM synthesizer chip. The OPL2 is a 2 +Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4) + Most sound cards have a FM synthesizer chip. The OPL2 is a 2 operator chip used in the original AdLib card. Currently it's used only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator FM chip which provides better sound quality and/or more available @@ -206,8 +269,73 @@ Yamaha FM synthesizers (OPL2, OPL3 and OPL4) card has a FM chip made by Yamaha. Don't enable it if your card has a software (TRS) based FM emulator. + ---------------------------------------------------------------- + NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition + to the FM synth this chip has also digital audio (WSS) and + MIDI (MPU401) capabilities. Support for OPL3-SA is described below. + ---------------------------------------------------------------- + +Yamaha OPL3-SA1 + + Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some + (Intel) motherboards and on cheap sound cards. It should not be + confused with the original OPL3 chip (YMF278) which is entirely + different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro + (not used in OSS/Free) in addition to the OPL3 FM synth. + + There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They + are PnP chips and will not work with the OPL3-SA1 driver. You should + use the standard MSS, MPU401 and OPL3 options with thses chips and to + activate the card using isapnptools. + +4Front Technologies SoftOSS + + SoftOSS is a software based wave table emulation which works with + any 16 bit stereo sound card. Due to its nature a fast CPU is + required (P133 is minimum). Although SoftOSS does _not_ use MMX + instructions it has proven out that recent processors (which appear + to have MMX) perform significantly better with SoftOSS than earlier + ones. For example a P166MMX beats a PPro200. SoftOSS should not be used + on 486 or 386 machines. + + The amount of CPU load caused by SoftOSS can be controlled by + selecting the CONFIG_SOFTOSS_RATE and CONFIG_SOFTOSS_VOICES + parameters properly (they will be prompted by make config). It's + recommended to set CONFIG_SOFTOSS_VOICES to 32. If you have a + P166MMX or faster (PPro200 is not faster) you can set + CONFIG_SOFTOSS_RATE to 44100 (kHz). However with slower systems it + recommended to use sampling rates around 22050 or even 16000 kHz. + Selecting too high values for these parameters may hang your + system when playing MIDI files with hight degree of polyphony + (number of concurrently playing notes). It's also possible to + decrease CONFIG_SOFTOSS_VOICES. This makes it possible to use + higher sampling rates. However using fewer voices decreases + playback quality more than decreasing the sampling rate. + + SoftOSS keeps the samples loaded on the system's RAM so much RAM is + required. SoftOSS should never be used on machines with less than 16 MB + of RAM since this is potentially dangerous (you may accidently run out + of memory which probably crashes the machine). + + SoftOSS implements the wave table API originally designed for GUS. For + this reason all applications designed for GUS should work (at least + after minor modifications). For example gmod/xgmod and playmidi -g are + known to work. + + To work SoftOSS will require GUS compatible + patch files to be installed on the system (in /dos/ultrasnd/midi). You + can use the public domain MIDIA patchset available from several ftp + sites. + + ********************************************************************* + IMPORTANT NOTICE! The original patch set distributed with the Gravis + Ultrasound card is not in public domain (even though it's available from + some FTP sites). You should contact Voice Crystal (www.voicecrystal.com) + if you like to use these patches with SoftOSS included in OSS/Free. + ********************************************************************* + PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC) - Analog Devices and Echo Speech have together defined a soundcard + Analog Devices and Echo Speech have together defined a sound card architecture based on the above chips. The DSP chip is used for emulation of SB Pro, FM and General MIDI/MT32. @@ -216,53 +344,75 @@ PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC) The driver supports downloading DSP algorithms to these cards. + NOTE! You will have to use the "old" config script when configuring + PSS cards. + MediaTrix AudioTrix Pro The ATP card is built around a CS4231 codec and an OPL4 synthesizer chips. The OPL4 mode is supported by a microcontroller running a General MIDI emulator. There is also a SB 1.5 compatible playback mode. Ensoniq SoundScape and compatibles - Ensoniq has designed a soundcard architecture based on the + Ensoniq has designed a sound card architecture based on the OTTO synthesizer chip used in their professional MIDI synthesizers. Several companies (including Ensoniq, Reveal and Spea) are selling cards based on this architecture. - NOTE! The new PnP SoundScape is not supported yet. + NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and + VIVO90 cards are not compatible with Soundscapes so the Soundscape + driver will not work with them. You may want to use OSS/Linux with these + cards. -MAD16 and Mozart based cards - The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929) - and OPTi 82C930 interface - chips are used in many different soundcards, including some +OPTi MAD16 and Mozart based cards + The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929), + OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface + chips are used in many different sound cards, including some cards by Reveal miro and Turtle Beach (Tropez). The purpose of these chips is to connect other audio components to the PC bus. The interface chip performs address decoding for the other chips. NOTE! Tropez Plus is not MAD16 but CS4232 based. + NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible + in the PnP mode. You will have to use them in MSS mode after having + initialized them using isapnptools or DOS. 82C931 probably requires + initialization using DOS/Windows (running isapnptools is not enough). + It's possible to use 82C931 with OSS/Free by jumpering it to non-PnP + mode (provided that the card has a jumper for this). In non-PnP mode + 82C931 is compatible with 82C930 and should work with the MAD16 driver + (without need to use isapnptools or DOS to initialize it). All OPTi + chips are supported by OSS/Linux (both in PnP and non-PnP modes). Audio Excel DSP16 Support for this card was written by Riccardo Faccetti - (riccardo@cdc8g5.cdc.polimi.it). See aedsp16.c for more info. - (This driver is not functional in version 3.5 of this driver. A - patch should be made available during April 96 (sunsite.unc.edu)). - -Crystal CS4232 based cards such as AcerMagic S23, TB Tropez _Plus_ and + (riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in + the lowlevel/ directory. To use it you should enable the + "Additional low level drivers" option. + +Crystal CS4232 and CS4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and many PC motherboards (Compaq, HP, Intel, ...) CS4232 is a PnP multimedia chip which contains a CS3231A codec, SB and MPU401 emulations. There is support for OPL3 too. - This is a temporary driver which uses the chip in non PnP mode - (The final driver should be included in version 3.6 of the driver). Unfortunately the MPU401 mode doesn't work (I don't know how to - initialize it). - -Turtle Beach Maui and Tropez + initialize it). CS4236 is an enhanced (compatible) version of CS4232. + NOTE! Don't ever try to use isapnptools with CS4232 since this will just + freeze your machine (due to chip bugs). If you have problems in getting + CS4232 working you could try initializing it with DOS (CS4232C.EXE) and + then booting Linux using loadlin. CS4232C.EXE loads a secret firmware + patch which is not documented by Crystal. + +Turtle Beach Maui and Tropez "classic" This driver version supports sample, patch and program loading commands described in the Maui/Tropez User's manual. There is now full initialization support too. The audio side of the Tropez is based on the MAD16 chip (see above). + NOTE! Tropez Plus is different card than Tropez "classic" and will not + work fully in Linux. You can get audio features working by configuring + the card as a CS4232 based card (above). + Jumpers and software configuration ----------------------------------- +================================== -Some of the earliest soundcards were jumper configurable. You have to +Some of the earliest sound cards were jumper configurable. You have to configure the driver use I/O, IRQ and DMA settings that match the jumpers. Just few 8 bit cards are fully jumper configurable (SB 1.x/2.x, SB Pro and clones). @@ -285,18 +435,19 @@ when installing new hardware to the machine). Sound driver sets the soft configurable parameters of the card automatically during boot. Usually you don't need to run any extra initialization programs when booting Linux but there are some exceptions. See the -card specific instructions (below) for more info. +card-specific instructions below for more info. The drawback of software configuration is that the driver needs to know how the card must be initialized. It cannot initialize unknown cards even if they are otherwise compatible with some other cards (like SB, MPU401 or Windows Sound System). + What if your card was not listed above? ---------------------------------------- +======================================= The first thing to do is to look at the major IC chips on the card. -Many of the latest soundcards are based on some standard chips. If you +Many of the latest sound cards are based on some standard chips. If you are lucky, all of them could be supported by the driver. The most common ones are the OPTi MAD16, Mozart, SoundScape (Ensoniq) and the PSS architectures listed above. Also look at the end of this file for list of unsupported @@ -306,21 +457,21 @@ The last resort is to send _exact_ name and model information of the card to me together with a list of the major IC chips (manufactured, model) to me. I could then try to check if your card looks like something familiar. -There are much more cards in the word than listed above. The first thing to -do with these cards is to check if they emulate some other card/interface +There are many more cards in the world than listed above. The first thing to +do with these cards is to check if they emulate some other card or interface such as SB, MSS and/or MPU401. In this case there is a chance to get the card to work by booting DOS before starting Linux (boot DOS, hit ctrl-alt-del and boot Linux without hard resetting the machine). In this method the -DOS based driver initializes the hardware to use a known I/O, IRQ and DMA -settings. If sound driver is configured to use the same settings, everything should -work OK. +DOS based driver initializes the hardware to use known I/O, IRQ and DMA +settings. If sound driver is configured to use the same settings, everything +should work OK. Configuring sound driver (with Linux) ===================================== -Sound driver is currently a part of Linux kernel distribution. The -driver files are located in directory /usr/src/linux/drivers/sound. +The sound driver is currently distributed as part of the Linux kernel. The +files are in /usr/src/linux/drivers/sound/. **************************************************************************** * ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH * @@ -339,9 +490,9 @@ driver files are located in directory /usr/src/linux/drivers/sound. **************************************************************************** To configure the driver, run "make config" in the kernel source directory -(/usr/src/linux). Answer y to the question about Sound card support (after -questions about mouse, CD-ROM, ftape, etc. supports). Sound config options -will then be asked after some additional questions. +(/usr/src/linux). Answer "y" or "m" to the question about Sound card support +(after the questions about mouse, CD-ROM, ftape, etc. support). Questions +about options for sound will then be asked. After configuring the kernel and sound driver, run "make dep" and compile the kernel following instructions in the kernel README. @@ -349,29 +500,20 @@ the kernel following instructions in the kernel README. The sound driver configuration dialog ------------------------------------- -All config information of the sound driver is written to file -linux/drivers/sound/local.h. You may save the old version is this file and -use it again in case you want to use the same config later. In this case -just answer n to each question made by the sound config program and put -the original local.h back before running "make dep". -Don't do this if the version number of the sound driver has changed. In this -case you have to enter the configuration information again. - -If you already have the sound driver installed, consult printout of +If you already have the sound driver installed, consult a printout of "cat /dev/sndstat" when configuring the driver again. It gives the I/O, -IRQ and DMA settings you have used earlier. - - -The sound config program (linux/drivers/sound/configure) starts by making -some yes/no questions. Be careful when answering to these questions since -answering y to a question may prevent some later ones from being asked. For -example don't answer y to the first question (PAS16) if you don't really -have a PAS16. Don't enable more cards than you really need since they -just consume memory. Also some drivers (like MPU401) may conflict with your -SCSI controller and prevent kernel from booting. If you card was in the list -of supported cards (above), please look at the card specific config -instructions (later in this file) before starting to configure. Some cards -must be configured in way which is not obvious. +IRQ and DMA settings you used earlier. + +Sound configuration starts by making some yes/no questions. Be careful +when answering to these questions since answering y to a question may +prevent some later ones from being asked. For example don't answer y to +the first question (PAS16) if you don't really have a PAS16. Don't enable +more cards than you really need since they just consume memory. Also +some drivers (like MPU401) may conflict with your SCSI controller and +prevent kernel from booting. If you card was in the list of supported +cards (above), please look at the card specific config instructions +(later in this file) before starting to configure. Some cards must be +configured in way which is not obvious. So here is the beginning of the config dialog. Answer 'y' or 'n' to these questions. The default answer is shown so that (y/n) means 'y' by default and @@ -416,14 +558,25 @@ select some options automatically as well. know what to answer with it. "MPU-401 support (NOT for SB16)", - Be careful with this question. The MPU401 interface is supported - by almost any soundcard today. However some natively supported cards + by almost any sound card today. However some natively supported cards have their own driver for MPU401. Enabling the 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 (above), please look at the card specific instructions later in this file. + + In MOST cases this MPU401 driver should only be used with "true" + MIDI-only MPU401 professional cards. In most other cases there + is another way to get the MPU401 compatible interface of a + sound card to work. + Support for the MPU401 compatible MIDI port of SB16, ESS1688 + and MV Jazz16 cards is included in the SB driver. Use it instead + of this separate MPU401 driver with these cards. As well + Soundscape, PSS and Maui drivers include their own MPU401 + options. + It's safe to answer 'y' if you have a true MPU401 MIDI interface - card. + card. "6850 UART Midi support", - It's safe to answer 'n' to this question in all cases. The 6850 UART interface is so rarely used. @@ -445,33 +598,41 @@ select some options automatically as well. in this file. For cards having native support in the driver, consult the card specific instructions later in this file. Some drivers have their own MSS support and enabling this option will cause a - conflict. + conflict. + Note! The MSS driver permits configuring two DMA channels. This is a + "nonstandard" feature and works only with very few cards (if any). + In most cases the second DMA channel should be disabled or set to + the same channel than the first one. Trying to configure two separate + channels with cards that don't support this feature will prevent + audio (at least recording) from working. "Ensoniq Soundscape support", - - Answer 'y' if you have a soundcard based on the Ensoniq SoundScape + - Answer 'y' if you have a sound card based on the Ensoniq SoundScape chipset. Such cards are being manufactured at least by Ensoniq, - Spea and Reveal (note that Reveal makes other cards also). + Spea and Reveal (note that Reveal makes other cards also). The oldest + cards made by Spea don't work properly with Linux. + Soundscape PnP as well as Ensoniq VIVO work only with the commercial + OSS/Linux version. "MediaTrix AudioTrix Pro support", - Answer 'y' if you have the AudioTrix Pro. "Support for MAD16 and/or Mozart based cards", - Answer y if your card has a Mozart (OAK OTI-601) or MAD16 - (OPTi 82C928, 82C929 or 82C930) audio interface chip. These chips are + (OPTi 82C928, 82C929, 82C924/82C925 or 82C930) audio interface chip. + These chips are currently quite common so it's possible that many no-name cards have one of them. In addition the MAD16 chip is used in some cards made by known manufacturers such as Turtle Beach (Tropez), Reveal (some models) and Diamond (some recent models). + Note OPTi 82C924 and 82C925 are MAD16 compatible only in non PnP + mode (jumper selectable on many cards). "Support for TB Maui" - This enables TB Maui specific initialization. Works with TB Maui and TB Tropez (may not work with Tropez Plus). - "Audio Excel DSP 16 initialization support", - - Don't know much about this card. Look at aedsp16.c for more info. Then the configuration program asks some y/n questions about the higher level services. It's recommended to answer 'y' to each of these questions. Answer 'n' only if you know you will not need the option. - "/dev/dsp and /dev/audio supports (usually required)", - - Answering 'n' disables /dev/dsp and /dev/audio. Answer 'y'. "MIDI interface support", - Answering 'n' disables /dev/midi## devices and access to any MIDI ports using /dev/sequencer and /dev/music. This option @@ -489,9 +650,7 @@ card specific configuration information. Usually just a set of I/O address, IRQ and DMA numbers are asked. With some cards the program asks for some files to be used during initialization of the card. For example many cards have a DSP chip or microprocessor which must be initialized by -downloading a program (microcode) file to the card. In some cases this file -is written to a .h file by the config program and then included to the driver -during compile. +downloading a program (microcode) file to the card. Instructions for answering these questions are given in the next section. @@ -508,8 +667,12 @@ different operating systems. Sound Blasters (the original ones by Creative) --------------------------------------------- +NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995 + are almost certainly PnP ones). With PnP cards you should use isapnptools + to activate them (see above). + It's possible to configure these cards to use different I/O, IRQ and -DMA settings. Since the available settings have changed between various +DMA settings. Since the possible/default settings have changed between various models, you have to consult manual of your card for the proper ones. It's a good idea to use the same values than with DOS/Windows. With SB and SB Pro it's the only choice. SB16 has software selectable IRQ and DMA channels but @@ -526,6 +689,15 @@ it's possible to use just one (8 bit) DMA channel by answering the 8 bit one when the configuration program asks for the 16 bit one. This may work in some systems but is likely to cause terrible noise on some other systems. +It's possible to use two SB16/32/64 at the same time. To do this you should +first configure OSS/Free for one card. Then edit local.h manually and define +SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get +the OPL3, MIDI and EMU8000 devices of the second card to work. If you are +going to use two PnP Sound Blasters, ensure that they are of different model +and have different PnP IDs. There is no way to get two cards with the same +card ID and serial number to work. The easiest way to check this is trying +if isapnptools can see both cards or just one. + NOTE! Don't enable the SM Games option (asked by the configuration program) if you are not 101% sure that your card is a Logitech Soundman Games (not a SM Wave or SM16). @@ -537,7 +709,7 @@ First of all: There are no SB16 clones. There are SB Pro clones with a 16 bit mode which is not SB16 compatible. The most likely alternative is that the 16 bit mode means MSS/WSS. -There are just few fully 100% hardware SB or SB Pro compatible cards. +There are just a few fully 100% hardware SB or SB Pro compatible cards. I know just Thunderboard and SM Games. Other cards require some kind of hardware initialization before they become SB compatible. Check if your card was listed in the beginning of this file. In this case you should follow @@ -705,7 +877,10 @@ when using the (future) binary distribution version of the driver. Ensoniq SoundScape ------------------ -NOTE! The new PnP SoundScape is not supported yet. +NOTE! The new PnP SoundScape is not supported yet. Soundscape compatible + cards made by Reveal don't work with Linux. They use older revision + of the Soundscape chipset which is not fully compatible with + newer cards made by Ensoniq. The SoundScape driver handles initialization of MSS and MPU supports itself so you don't need to enable other drivers than SoundScape @@ -757,6 +932,10 @@ an old card and you will need to use sndscape.co1. For other cards use soundscape.co0. New Soundscape revisions such as Elite and PnP use code files with higher numbers (.co2, .co3, etc.). +NOTE! Ensoniq Soundscape VIVO is not compatible with other Soundscape cards. + Currently it's possible to use it in Linux only with OSS/Linux + drivers. + Check /var/adm/messages after running ssinit. The driver prints the board version after downloading the microcode file. That version number must match the number in the name of the microcode file (extension). @@ -784,9 +963,8 @@ TB Tropez is based on the 82C929 chip. It has two MIDI ports. The one connected to the MAD16 chip is the second one (there is a second MIDI connector/pins somewhere??). If you have not connected the second MIDI port, just disable the MIDI port of MAD16. The 'Maui' compatible synth of -Tropez is jumper configurable and not connected to the MAD16 chip. -It can be used by enabling the stand alone MPU401 support but you have -to initialize it by using the MS-DOS SNDSETUP program. +Tropez is jumper configurable and not connected to the MAD16 chip (the +Maui driver can be used with it). Some MAD16 based cards may cause feedback, whistle or terrible noise if the line3 mixer channel is turned too high. This happens at least with Shuttle @@ -801,6 +979,10 @@ MAD16 cards having a CS4231 codec support full duplex mode. This mode can be enabled by configuring the card to use two DMA channels. Possible DMA channel pairs are: 0&1, 1&0 and 3&0. +NOTE! Cards having an OPTi 82C924/82C925 chip work with OSS/Free only in +non-PnP mode (usually jumper selectable). The PnP mode is supported only +by OSS/Linux. + MV Jazz (ProSonic) ------------------ @@ -821,7 +1003,7 @@ Logitech Soundman Wave Read the above MV Jazz specific instructions first. -The Logitech SoundMan Wave (don't confuse with the SM16 or SM Games) is +The Logitech SoundMan Wave (don't confuse this with the SM16 or SM Games) is a MV Jazz based card which has an additional OPL4 based wave table synthesizer. The OPL4 chip is handled by an on board microcontroller which must be initialized during boot. The config program asks if @@ -855,10 +1037,6 @@ is powered on. These cards actually behave just like they have jumpers for all of the settings. Configure driver for MSS, MPU, SB/SB Pro and OPL3 supports with these cards. -The config program asks if you want support for the mixer of -SG NX Pro. Answer 'y' to these questions if you have one of the above 8 or -16 bit Aztech cards. - There are some new Sound Galaxies in the market. I have no experience with them so read the card's manual carefully. @@ -871,19 +1049,32 @@ if you want to use MIDI features of ES1688. ES688 doesn't have MPU mode so you don't need to enable it (the driver uses normal SB MIDI automatically with ES688). -NOTE! ESS cards are not compatible with MSS/WSS. +NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support +of OSS doesn't work with it. + +There are some ES1688/688 based sound cards and (particularily) motherboards +which use software configurable I/O port relocation feature of the chip. +This ESS proprietary feature is supported only by OSS/Linux. + +There are ES1688 based cards which use different interrupt pin assignment than +recommended by ESS (5, 7, 9/2 and 10). In this case all IRQ's don't work. +At least a card called (Pearl?) Hypersound 16 supports IRQ15 but it doesn't +work. + +ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688 +brobably works with OSS/Free after initialization using isapnptools. Reveal cards ------------ There are several different cards made/marketed by Reveal. Some of them are compatible with SoundScape and some use the MAD16 chip. You may have -to look at the card and try to identify origin of the card. +to look at the card and try to identify its origin. Diamond ------- -The oldest (Sierra Aria based) soundcards made by Diamond are not supported +The oldest (Sierra Aria based) sound cards made by Diamond are not supported (they may work if the card is initialized using DOS). The recent (LX?) models are based on the MAD16 chip which is supported by the driver. @@ -934,9 +1125,9 @@ the ioctl() for activating the "solo" mode. The following configuration parameters have worked fine for the PCM12 in Markus Kuhn's system, many other configurations might work, too: -MAD16_BASE=0x530, MAD16_IRQ=11, MAD16_DMA=3, MAD16_DMA2=0, -MAD16_MPU_BASE=0x330, MAD16_MPU_IRQ=10, DSP_BUFFSIZE=65536, -SELECTED_SOUND_OPTIONS=0x00281000. +CONFIG_MAD16_BASE=0x530, CONFIG_MAD16_IRQ=11, CONFIG_MAD16_DMA=3, +CONFIG_MAD16_DMA2=0, CONFIG_MAD16_MPU_BASE=0x330, CONFIG_MAD16_MPU_IRQ=10, +DSP_BUFFSIZE=65536, SELECTED_SOUND_OPTIONS=0x00281000. The miroSOUND PCM1 pro and the PCM20 are very similar to the PCM12. Perhaps the same ACI driver also works for these cards, however this @@ -958,7 +1149,7 @@ make menuconfig/xconfig. Others? ------- -Since there are so many different soundcards, it's likely that I have +Since there are so many different sound cards, it's likely that I have forgotten to mention many of them. Please inform me if you know yet another card which works with Linux, please inform me (or is anybody else willing to maintain a database of supported cards (just like in XF86)?). @@ -966,18 +1157,18 @@ willing to maintain a database of supported cards (just like in XF86)?). Cards not supported yet ======================= -Please check which version of sound driver you are using before -complaining that your card is not supported. It's possible that you are +Please check the version of sound driver you are using before +complaining that your card is not supported. It's possible you are using a driver version which was released months before your card was -introduced. Driver's release date is listed after its version number -in "cat /dev/sndstat" printout and in file linux/drivers/sound/soundvers.h. +introduced. The driver's release date is listed after its version number in a +"cat /dev/sndstat" printout and in the file linux/drivers/sound/soundvers.h. -First of all. There is an easy way to make most soundcards to work -with Linux. Just use the DOS based driver to initialize the card -to a _known_ state. Then use loadlin.exe to boot Linux. If Linux is configured -to use the same I/O, IRQ and DMA numbers than DOS, the card could work. +First of all, there is an easy way to make most sound cards work with Linux. +Just use the DOS based driver to initialize the card to a known state, then use +loadlin.exe to boot Linux. If Linux is configured to use the same I/O, IRQ and +DMA numbers as DOS, the card could work. (ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with -new motherboards). This method works also with all/most PnP soundcards. +new motherboards). This method works also with all/most PnP sound cards. Don't get fooled with SB compatibility. Most cards are compatible with SB but that may require a TSR which is not possible with Linux. If @@ -986,20 +1177,18 @@ don't work in the SB and MSS modes at the same time. Then there are cards which are no longer manufactured and/or which are relatively rarely used (such as the 8 bit ProAudioSpectrum -models). It's extremely unlikely that such cards never get supported. +models). It's extremely unlikely that such cards ever get supported. Adding support for a new card requires much work and increases time required in maintaining the driver (some changes need to be done to all low level drivers and be tested too, maybe with multiple operating systems). For this reason I have made a decision to not support obsolete cards. It's possible that someone else makes a separately -distributed driver (diffs) for the card. Version v3.6 will be much more -modular so making separately distributed drivers will be easier with it. -(The bad news is that v3.6 will not be available before summer -96). +distributed driver (diffs) for the card. Writing a driver for a new card is not possible if there are no programming information available about the card. If you don't find your new card from this file, look from the home page -(http://www.4front-tech.com/ossfree). Then please contact +(http://www.opensound.com/ossfree). Then please contact manufacturer of the card and ask if they have (or are willing to) released technical details of the card. Do this before contacting me. I can only answer 'no' if there are no programming information available. @@ -1015,25 +1204,24 @@ Some companies don't give low level technical information about their products to public or at least their require signing a NDA. It's not possible to implement a freeware driver for them. However it's possible that support for such cards become available in the commercial version -of this driver (see http://www.4Front-tech.com/uss.html for more info). +of this driver (see http://www.4Front-tech.com/oss.html for more info). There are some common audio chipsets that are not supported yet. For example Sierra Aria and IBM Mwave. It's possible that these architectures get some support in future but I can't make any promises. Just look -at the home page (http://www.4front-tech.com/ossfree/new_cards.html) +at the home page (http://www.opensound.com/ossfree/new_cards.html) for latest info. -Information about unsupported soundcards and chipsets is welcome as well -as free copies of soundcards, SDKs and operating systems. +Information about unsupported sound cards and chipsets is welcome as well +as free copies of sound cards, SDKs and operating systems. If you have any corrections and/or comments, please contact me. Hannu Savolainen -hannu@4front-tech.com - -Personal home page: http://www.4front-tech.com/hannu.html -www home page of OSS/Free: http://www.4front-tech.com/ossfree +hannu@opensound.com -www home page of commercial -Open Sound System drivers: http://www.4front-tech.com/oss.html +Personal home page: http://www.compusonic.fi/~hannu +home page of OSS/Free: http://www.opensound.com/ossfree +home page of commercial OSS +(Open Sound System) drivers: http://www.opensound.com/oss.html diff --git a/drivers/sound/Readme.linux b/drivers/sound/Readme.linux index 8cc7a89e7fbd..b3fa3a833099 100644 --- a/drivers/sound/Readme.linux +++ b/drivers/sound/Readme.linux @@ -28,7 +28,7 @@ IMPORTANT! Read this if you are installing a separately sound support during "make config"). Please refer to kernel documentation for instructions about configuring and compiling kernel. File Readme.cards contains card specific instructions for configuring this driver for - use with various sound cards. + use with various soundcards. Boot time configuration (using lilo and insmod) ----------------------------------------------- @@ -67,172 +67,19 @@ Readme.cards for info about configuring the driver with your card. Also check for possible boot (insmod) time error messages in /var/adm/messages. - Other messages or problems -Please check http://www.4front-tech.com/ossfree for more info. +Please check http://www.opensound.com/ossfree for more info. Hannu Savolainen -hannu@4front-tech.com +hannu@opensound.com ----------------- cut here ------------------------------ -#!/bin/sh -echo Error: Read the notice in the beginning of this script before executing. -exit -1 -Notice! Remove this notice and the above two lines before trying to execute. - Trying to run this script is normally useless. The device files are - almost certainly already set up correctly. -# ***************************************** -# * NOTICE! -# * -# * For security reasons read access to /dev/dsp* and /dev/audio* has been -# * disabled from other than root. Otherwise any user may be able to spy -# * what is being talked about near the microphone. -# * This effectively disables audio recording by other than root. In case -# * this capability is required, you should change AUDIOPERMS (below) to 666 -# * before executing this script. -# ***************************************** -AUDIOPERMS=622 -# -# -# -# -# Create the devices -# -# Mixer devices -# -if [ -e /dev/mixer ]; then - rm -f /dev/mixer -fi - -if [ -e /dev/mixer0 ]; then - rm -f /dev/mixer0 -fi - -mknod -m 666 /dev/mixer0 c 14 0 -ln -sf /dev/mixer0 /dev/mixer - -if [ -e /dev/mixer1 ]; then - rm -f /dev/mixer1 -fi -mknod -m 666 /dev/mixer1 c 14 16 - - -# Sequencer (14, 1) -# -if [ -e /dev/sequencer ]; then - rm -f /dev/sequencer -fi -mknod -m 666 /dev/sequencer c 14 1 - -if [ -e /dev/patmgr0 ]; then - rm -f /dev/patmgr0 -fi -mknod -m 666 /dev/patmgr0 c 14 17 -if [ -e /dev/patmgr1 ]; then - rm -f /dev/patmgr1 -fi -mknod -m 666 /dev/patmgr1 c 14 33 - - # music (14, 8) - # - if [ -e /dev/music ]; then - rm -f /dev/music - fi - - mknod -m 666 /dev/music c 14 8 - if [ -e /dev/sequencer2 ]; then - rm -f /dev/sequencer2 - fi - ln -s /dev/music /dev/sequencer2 - -# Midi devices -# -if [ -e /dev/midi ]; then - rm -f /dev/midi # Old name. Don't use it -fi - if [ -e /dev/midi00 ]; then - rm -f /dev/midi00 - fi - mknod -m 666 /dev/midi00 c 14 2 - ln -sf /dev/midi00 /dev/midi - - if [ -e /dev/midi01 ]; then - rm -f /dev/midi01 - fi - mknod -m 666 /dev/midi01 c 14 18 - - if [ -e /dev/midi02 ]; then - rm -f /dev/midi02 - fi - mknod -m 666 /dev/midi02 c 14 34 - - if [ -e /dev/midi03 ]; then - rm -f /dev/midi03 - fi - mknod -m 666 /dev/midi03 c 14 50 -# -# DSP (14, 3) -# -if [ -e /dev/dsp ]; then - rm -f /dev/dsp -fi -if [ -e /dev/dsp0 ]; then - rm -f /dev/dsp0 -fi -mknod -m $AUDIOPERMS /dev/dsp0 c 14 3 -ln -s /dev/dsp0 /dev/dsp - -# -# DSPW (14, 5) -# -if [ -e /dev/dspW ]; then - rm -f /dev/dspW -fi -if [ -e /dev/dspW0 ]; then - rm -f /dev/dspW0 -fi -mknod -m $AUDIOPERMS /dev/dspW0 c 14 5 -ln -s /dev/dspW0 /dev/dspW - -if [ -e /dev/dspW1 ]; then - rm -f /dev/dspW1 -fi -mknod -m $AUDIOPERMS /dev/dspW1 c 14 37 - -# -# SPARC compatible /dev/audio (14, 4) -# -if [ -e /dev/audio ]; then - rm -f /dev/audio -fi -if [ -e /dev/audio0 ]; then - rm -f /dev/audio0 -fi -mknod -m $AUDIOPERMS /dev/audio0 c 14 4 -ln -s /dev/audio0 /dev/audio - -# -# DSP1 (14, 19) /dev/dsp for the second soundcard. -# Also the SB emulation part of the -# PAS16 card. -# -if [ -e /dev/dsp1 ]; then - rm -f /dev/dsp1 -fi -mknod -m $AUDIOPERMS /dev/dsp1 c 14 19 -# -# SPARC audio1 (14, 20) -# /dev/audio for the second soundcard. -# Also the SB emulation part of the -# PAS16 card. -# -if [ -e /dev/audio1 ]; then - rm -f /dev/audio1 -fi -mknod -m $AUDIOPERMS /dev/audio1 c 14 20 -# -# /dev/sndstat (14,6) For debugging purposes -# -if [ -e /dev/sndstat ]; then - rm -f /dev/sndstat -fi -mknod -m 666 /dev/sndstat c 14 6 -exit 0 +SURPRISE SURPRISE!!! + +The device file creation script that used to be here earlier is +obviously not here any more. + +Why? + +Because you do not need it. All Linux distributions have the +device files properly created (yes they are) so you should not +try to run any scripts which create them. diff --git a/drivers/sound/Readme.modules b/drivers/sound/Readme.modules index 778fbc3a023a..052156552c4c 100644 --- a/drivers/sound/Readme.modules +++ b/drivers/sound/Readme.modules @@ -1,84 +1,71 @@ -Building a loadable sound driver +Building a modular sound driver ================================ -Loadable module support in version 3.5 of the driver is mostly rewritten since -the previous version (3.0.1). This means that some things have changed. - -To compile the sound driver as a loadable module you have to perform -the following steps: - -1) Install modules-1.2.8.tar.gz package (or later if available). -2a) Check that symbol remap_page_range is defined in linux/init/ksyms.c. -Insert a line containing "X(remap_page_range)," if required. The driver will -not load if this line is missing. -2b) Recompile kernel with soundcard support disabled. -3) Boot the new kernel. -4) cd to the sound driver source directory (this directory). It's no -longer required that the sound driver sources are installed in the -kernel source tree (linux/drivers/sound). When installing a separately -distributed sound driver you may install the sources for example to -/usr/src/sound. -5) Execute make in the sound driver source directory. Enter -configuration parameters as described in Readme.cards. Then just wait until -the driver is compiled OK. -6) Copy sound.o to the directory where insmod expects to find it. -("make install" copies it to /lib/modules/misc). -7) Use command "insmod sound" to load the driver. - -8) The sound driver can be removed using command "rmmod sound". - - -Parameters accepted by the loadable sound driver -================================================ - -Setting DMA buffer size ------------------------ - -The driver allocates a DMA buffer (or two for full duplex devices) -every time the audio device (/dev/dsp or /dev/audio) is opened -and frees it when the device is closed. Size of this buffer is defined -when the driver is configured (the last question). The buffer size -can be redefined when loading the driver if required (note that this is -an optional feature which is not normally required). The buffer size -is redefined by adding dma_pagesize= parameter to the insmod command line. -For example: - - insmod sound dma_buffsize=32768 - -Minimum buffer size is 4096 and the maximum depends on the DMA channel. -For 8 bit channels (0 to 3) the limit is 64k and for 16 bit ones (5 to 7) -it's 128k. Driver selects a suitable buffer size automatically in case -you try to specify an invalid size. - -Q: What is the right DMA buffer size? - -A: It depends on the sampling rate, machine speed and the load of the system. -Large buffers are required on slow machines, when recording/playing CD-quality -audio or when there are other processes running on the same system. Also -recording to hard disk is likely to require large buffers. - -Very small buffers are sufficient when you are just playing 8kHz audio files -on an empty P133 system. Using a 128k buffer just wastes 120k (or 250k) -of valuable physical RAM memory. - -The right buffer size can be easily found by making some experiments -with the dma_buffsize= parameter. I use usually 16k buffers on a DX4/100 system -and 64k on an old 386 system. - -NOTE! DMA buffers are used only by /dev/audio# and /dev/dsp# devices. - Other device files don't use them but there are two exceptions: - GUS driver uses DMA buffers when loading samples to the card. - Ensoniq SoundScape driver uses them when downloading the microcode - file (sndscape.co[012]) to the card. Using large buffers doesn't - increase performance in these cases. - -Debugging and tracing ---------------------- - -Modularized sound driver doesn't display messages during initialization as -the kernel compiled one does. This feature can be turned on by adding -trace_init=1 to the insmod command line. - -For example: - - insmod sound trace_init=1 + The following information is current as of linux-2.1.85. Check the other +readme files, especially Readme.cards, for information not specific to +making sound modular. + + First, configure your kernel. This is an idea of what you should be +setting in the sound section: + + Sound card support + + 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support + + I have SoundBlaster. Select your card from the list. + + Generic OPL2/OPL3 FM synthesizer support + FM synthesizer (YM3812/OPL-3) support + + If you don't set these, you will probably find you can play .wav files +but not .midi. As the help for them says, set them unless you know your +card does not use one of these chips for FM support. + + Once you are configured, make zlilo, modules, modules_install; reboot. +Note that it is no longer necessary or possible to configure sound in the +drivers/sound dir. Now one simply configures and makes one's kernel and +modules in the usual way. + + Then, add to your /etc/modules.conf or /etc/conf.modules something like: + +alias char-major-14 sb +post-install sb modprobe "-k" "adlib_card" +options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 +options adlib_card io=0x388 # FM synthesizer + + The effect of this is that the sound driver and all necessary bits and +pieces autoload on demand, assuming you use kerneld (a sound choice) and +autoclean when not in use. Also, options for the device drivers are +set. They will not work without them. Change as appropriate for your card. +If you are not yet using the very cool kerneld, you will have to "modprobe +-k sb" yourself to get things going. Eventually things may be fixed so +that this kludgery is not necessary; for the time being, it seems to work +well. + + Replace 'sb' with the driver for your card, and give it the right +options. To find the filename of the driver, look in +/lib/modules//misc. Mine looks like: + +adlib_card.o # This is the generic OPLx driver +opl3.o # The OPL3 driver +sb.o # <> +sound.o # The sound driver +uart401.o # Used by sb, maybe other cards + + Whichever card you have, try feeding it the options that would be the +default if you were making the driver wired, not as modules. You can look +at the init_module() code for the card to see what args are expected. + + Note that at present there is no way to configure the io, irq and other +parameters for the modular drivers as one does for the wired drivers.. One +needs to pass the modules the necessary parameters as arguments, either +with /etc/modules.conf or with command-line args to modprobe, e.g. + +modprobe -k sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 +modprobe -k adlib_card io=0x388 + + recommend using /etc/modules.conf. + + I'm afraid I know nothing about anything but my setup, being more of a +text-mode guy anyway. If you have options for other cards or other helpful +hints, send them to me, Jim Bray, jb@as220.org, http://as220.org/jb. diff --git a/drivers/sound/Readme.v30 b/drivers/sound/Readme.v30 deleted file mode 100644 index a12da12726fd..000000000000 --- a/drivers/sound/Readme.v30 +++ /dev/null @@ -1,140 +0,0 @@ -Sound driver version v3.0 (and later) -------------------------------------- - -All features of v2.90-2 should work as earlier. There could be some -omissions but they are unintentional. I started this version thread -after v2.3 so all features implemented before it are there. - -New features -============ - -There are now two new device interfaces. The /dev/midi## is a raw -tty like interface to MIDI ports. There is a device file for each MIDI -port on your system. They are named (/dev/midi00 to /dev/midiNN). -The second addition is the /dev/music which is higher level interface -than the old /dev/sequencer. It's intended for writing device independent -applications like sequencers. - -/dev/midi## ------------ - -This interface should be useful for applications like MIDI sysex librarians. -There are (currently) no timing features so making music could be impossible. - -There are as many /dev/midi## devices as there are MIDI ports in the system. -The /dev/midi00 is connected to the first one, /dev/midi01 to the second etc. - -These devices work like tty devices in raw mode. Everything written to them is -sent out to the MIDI port. There is currently an extra delay of at most -1/100th of sec but it will be removed later. - -The reading algorithm is little bit more complicated. There are two different -cases: - -1) There is at least one byte in the input buffer. - -The read returns as many bytes as it can without waiting for more bytes. -For example when a process reads 100 bytes and there are 10 bytes in the -buffer, the read returns just 10 bytes. - -2) The input buffer is empty when the process calls read. - -The read waits for the first byte and then continues as in case 1. By -default it waits infinitely but there is an ioctl for setting a timeout -for this. The ioctl(fd, SNDCTL_MIDI_PRETIME, &time) changes the timeout. -The time is given in 1/10th of seconds (10 means one second). - -Other ioctl calls: - -ioctl(fd, SNDCTL_MIDI_MPUMODE, &mode) is available for full MPU-401 -compatible devices such as MPU-IPC-T, MQ PC Midi Card or MQX-32. -It's not available for the so called MPU UART ports of some soundcards -(PAS16, SB16 etc). By default the MIDI port is in UART mode after open. -If this ioctl is called with mode=1, the interface is put to the intelligent -(coprocessor) mode. NOTE! The MIDI port will be reset when this ioctl is called. -It could have some strange effects if not called immediately after open. This -call returns EINVAL if the midi port doesn't support the MPU-401 intelligent -mode. - -ioctl(fd, SNDCTL_MIDI_MPUCMD, &cmdstruct) is valid only if the MIDI port -is put to the coprocessor mode using ioctl(SNDCTL_MIDI_MPUMODE). It's used to -send commands to a MPU-401 compatible MIDI cards. Please refer to the -MPU-401 Technical Reference Manual (or Music Quest Technical Reference -Manual) for descriptions of the commands. - -The argument of SNDCTL_MIDI_MPUCOMMAND is of type mpu_command_rec. It -has the following fields: - -typedef struct { - unsigned char cmd; - - char nr_args, nr_returns; - unsigned char data[30]; - } mpu_command_rec; - -where: - cmd Contains the command number. - nr_args Number of arguments of the command. - MUST BE INITIALIZED BEFORE CALL - nr_returns Number of bytes returned by the command. - MUST BE INITIALIZED BEFORE CALL - data Buffer for the command arguments and returned - data. - -Be extremely careful with the nr_args and nr_returns fields. They -must match the command. An incorrect value will put the card and -the driver out of sync. Refer to the MPU-401/MQX-32M documentation for further -details. - - - -/dev/music (/dev/sequencer2) ----------------------------- - -This device file works much like the /dev/sequencer which has been present -since the beginning. The main differences are the following: - -- /dev/sequencer makes the MIDI ports to look like the synth devices. In fact -the result is somewhere between the MIDI specification and the synth devices of -/dev/sequencer. Both kind of devices are accessed using the SEQ_START_NOTE() -like macros. The voice number parameters of the API macros have been redefined -to denote MIDI channels. This means that the driver allocates voices for -the channels automatically (this is a responsibility/right of an application -with /dev/sequencer). The result is that a SEQ_START_NOTE() macro has -similar effects for a synth channel than on a MIDI port. This kind of -solution provides better device independence than the /dev/sequencer. The -drawback is that the new interface doesn't permit so low level access to the -device as the /dev/sequencer does. An application developer must choose between -these two interfaces. I think the old /dev/sequencer is better for applications -like module players while the new one is better for making generic sequencer -programs. - -- There are no separate MIDI devices with the /dev/sequencer2. The -ioctl(SNDCTL_SEQ_NRMIDIS) returns always zero. Instead the MIDI ports are -shown as synth devices. ioctl(SNDCTL_SEQ_NRSYNTHS) on /dev/sequencer2 will -return sum of internal synthesizers (GUS, OPL3) and MIDI ports in the systems. - -- The new interface is used much like the ordinary /dev/sequencer. The -event format is new so you have to use the API macros defined in the -sys/soundcard.h. The interface is will probably change before the final 3.0 -release but using the API macros should ensure compatibility in source level. -The new event format is not recognized by version 2.X so don't try to -distribute binaries compiled with soundcard.h of v3.X. - -- The basic API usage is similar to the current one. There are some new -macros but the older ones should work as earlier. The most important -incompatibility is that the /dev/sequencer2 driver allocates voices itself. -The other one is that the application must send SEQ_START_TIMER() as its -first event. Otherwise the timer is not started and the application waits -infinitely. - - -There are several new features but I don't document them here. There are -some info in the soundcard.h (near the end). I have also included some -sample code in the directory v30. Full documentation will -appear in the Hacker's Guide later. - -Don't hesitate to contact me in case you have questions or comments. - -Hannu Savolainen -hannu@4front-tech.com diff --git a/drivers/sound/ad1848.c b/drivers/sound/ad1848.c index b329ff9d366d..a98b3996ec55 100644 --- a/drivers/sound/ad1848.c +++ b/drivers/sound/ad1848.c @@ -12,47 +12,58 @@ * * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU). * CS4232A is an improved version of CS4232. - */ - -/* - * Copyright (C) by Hannu Savolainen 1993-1996 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * + * + * 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) + * general sleep/wakeup clean up. + * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free + * of irqs. Use dev_id. + * + * Status: + * Tested. Believed fully functional. */ + #include +#include +#include +#include "soundmodule.h" #define DEB(x) #define DEB1(x) #include "sound_config.h" -#if defined(CONFIG_AD1848) +#ifdef CONFIG_AD1848 #include "ad1848_mixer.h" typedef struct - { - int base; - int irq; - int dual_dma; /* 1, when two DMA channels allocated */ - unsigned char MCE_bit; - unsigned char saved_regs[16]; - int debug_flag; - - int speed; - unsigned char speed_bits; - int channels; - int audio_format; - unsigned char format_bits; - - int xfer_count; - int audio_mode; - int intr_active; - int opened; - char *chip_name; - int model; +{ + int base; + int irq; + int dma1, dma2; + int dual_dma; /* 1, when two DMA channels allocated */ + unsigned char MCE_bit; + unsigned char saved_regs[16]; + int debug_flag; + + int audio_flags; + int record_dev, playback_dev; + + int xfer_count; + int audio_mode; + int open_mode; + int intr_active; + char *chip_name, *name; + int model; #define MD_1848 1 #define MD_4231 2 #define MD_4231A 3 @@ -61,2198 +72,2516 @@ typedef struct #define MD_C930 6 #define MD_IWAVE 7 - /* Mixer parameters */ - int recmask; - int supported_devices; - int supported_rec_devices; - unsigned short levels[32]; - int dev_no; - volatile unsigned long timer_ticks; - int timer_running; - int irq_ok; - int *osp; - } + /* Mixer parameters */ + int recmask; + int supported_devices, orig_devices; + int supported_rec_devices, orig_rec_devices; + int *levels; + short mixer_reroute[32]; + int dev_no; + volatile unsigned long timer_ticks; + int timer_running; + int irq_ok; + mixer_ents *mix_devices; + int mixer_output_port; +} ad1848_info; + +typedef struct ad1848_port_info +{ + int open_mode; + int speed; + unsigned char speed_bits; + int channels; + int audio_format; + unsigned char format_bits; +} +ad1848_port_info; + +static int nr_ad1848_devs = 0; +int deskpro_xl = 0; -ad1848_info; +static volatile char irq2dev[17] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; -static int nr_ad1848_devs = 0; -static volatile char irq2dev[17] = -{-1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1}; +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) || defined(MODULE) -static int timer_installed = -1; +static int timer_installed = -1; -static int ad_format_mask[8 /*devc->model */ ] = +#endif + +static int ad_format_mask[8 /*devc->model */ ] = { - 0, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */ - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM + 0, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */ + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, + AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM }; -static ad1848_info dev_info[MAX_AUDIO_DEV]; +static ad1848_info adev_info[MAX_AUDIO_DEV]; #define io_Index_Addr(d) ((d)->base) #define io_Indexed_Data(d) ((d)->base+1) #define io_Status(d) ((d)->base+2) #define io_Polled_IO(d) ((d)->base+3) -static int ad1848_open (int dev, int mode); -static void ad1848_close (int dev); -static int ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg, int local); -static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart); -static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart); -static int ad1848_prepare_for_output (int dev, int bsize, int bcount); -static int ad1848_prepare_for_IO (int dev, int bsize, int bcount); -static void ad1848_reset (int dev); -static void ad1848_halt (int dev); -static void ad1848_halt_input (int dev); -static void ad1848_halt_output (int dev); -static void ad1848_trigger (int dev, int bits); -static int ad1848_tmr_install (int dev); -static void ad1848_tmr_reprogram (int dev); - -static int -ad_read (ad1848_info * devc, int reg) +static int ad1848_open(int dev, int mode); +static void ad1848_close(int dev); +static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag); +static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag); +static int ad1848_prepare_for_output(int dev, int bsize, int bcount); +static int ad1848_prepare_for_input(int dev, int bsize, int bcount); +static void ad1848_halt(int dev); +static void ad1848_halt_input(int dev); +static void ad1848_halt_output(int dev); +static void ad1848_trigger(int dev, int bits); + +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) +static int ad1848_tmr_install(int dev); +static void ad1848_tmr_reprogram(int dev); + +#endif + +static int ad_read(ad1848_info * devc, int reg) { - unsigned long flags; - int x; - int timeout = 900000; + unsigned long flags; + int x; + int timeout = 900000; - while (timeout > 0 && inb (devc->base) == 0x80) /*Are we initializing */ - timeout--; + while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ + timeout--; - save_flags (flags); - cli (); - outb ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc)); - x = inb (io_Indexed_Data (devc)); - /* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */ - restore_flags (flags); + save_flags(flags); + cli(); + outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); + x = inb(io_Indexed_Data(devc)); +/* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */ + restore_flags(flags); - return x; + return x; } -static void -ad_write (ad1848_info * devc, int reg, int data) +static void ad_write(ad1848_info * devc, int reg, int data) { - unsigned long flags; - int timeout = 900000; - - while (timeout > 0 && - inb (devc->base) == 0x80) /*Are we initializing */ - timeout--; - - save_flags (flags); - cli (); - outb ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc)); - outb ((unsigned char) (data & 0xff), io_Indexed_Data (devc)); - /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */ - restore_flags (flags); + unsigned long flags; + int timeout = 900000; + + while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */ + timeout--; + + save_flags(flags); + cli(); + outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); + outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc)); + /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */ + restore_flags(flags); } -static void -wait_for_calibration (ad1848_info * devc) +static void wait_for_calibration(ad1848_info * devc) { - int timeout = 0; - - /* - * Wait until the auto calibration process has finished. - * - * 1) Wait until the chip becomes ready (reads don't return 0x80). - * 2) Wait until the ACI bit of I11 gets on and then off. - */ - - timeout = 100000; - while (timeout > 0 && inb (devc->base) == 0x80) - timeout--; - if (inb (devc->base) & 0x80) - printk ("ad1848: Auto calibration timed out(1).\n"); - - timeout = 100; - while (timeout > 0 && !(ad_read (devc, 11) & 0x20)) - timeout--; - if (!(ad_read (devc, 11) & 0x20)) - return; - - timeout = 80000; - while (timeout > 0 && ad_read (devc, 11) & 0x20) - timeout--; - if (ad_read (devc, 11) & 0x20) - if (devc->model != MD_1845) - printk ("ad1848: Auto calibration timed out(3).\n"); + int timeout = 0; + + /* + * Wait until the auto calibration process has finished. + * + * 1) Wait until the chip becomes ready (reads don't return 0x80). + * 2) Wait until the ACI bit of I11 gets on and then off. + */ + + timeout = 100000; + while (timeout > 0 && inb(devc->base) == 0x80) + timeout--; + if (inb(devc->base) & 0x80) + printk(KERN_WARNING "ad1848: Auto calibration timed out(1).\n"); + + timeout = 100; + while (timeout > 0 && !(ad_read(devc, 11) & 0x20)) + timeout--; + if (!(ad_read(devc, 11) & 0x20)) + return; + + timeout = 80000; + while (timeout > 0 && ad_read(devc, 11) & 0x20) + timeout--; + if (ad_read(devc, 11) & 0x20) + if (devc->model != MD_1845) + printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n"); } -static void -ad_mute (ad1848_info * devc) +static void ad_mute(ad1848_info * devc) { - int i; - unsigned char prev; - - /* - * Save old register settings and mute output channels - */ - for (i = 6; i < 8; i++) - { - prev = devc->saved_regs[i] = ad_read (devc, i); - ad_write (devc, i, prev | 0x80); - } + int i; + unsigned char prev; + + /* + * Save old register settings and mute output channels + */ + + for (i = 6; i < 8; i++) + { + prev = devc->saved_regs[i] = ad_read(devc, i); + } -/* - * Let's have some delay - */ - for (i = 0; i < 1000; i++) - inb (devc->base); } -static void -ad_unmute (ad1848_info * devc) +static void ad_unmute(ad1848_info * devc) { - int i; - -/* - * Let's have some delay - */ - for (i = 0; i < 1000; i++) - inb (devc->base); - - /* - * Restore back old volume registers (unmute) - */ - for (i = 6; i < 8; i++) - { - ad_write (devc, i, devc->saved_regs[i] & ~0x80); - } } -static void -ad_enter_MCE (ad1848_info * devc) +static void ad_enter_MCE(ad1848_info * devc) { - unsigned long flags; - int timeout = 1000; - unsigned short prev; - - while (timeout > 0 && inb (devc->base) == 0x80) /*Are we initializing */ - timeout--; - - save_flags (flags); - cli (); - - devc->MCE_bit = 0x40; - prev = inb (io_Index_Addr (devc)); - if (prev & 0x40) - { - restore_flags (flags); - return; - } - - outb (devc->MCE_bit, io_Index_Addr (devc)); - restore_flags (flags); + unsigned long flags; + int timeout = 1000; + unsigned short prev; + + while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ + timeout--; + + save_flags(flags); + cli(); + + devc->MCE_bit = 0x40; + prev = inb(io_Index_Addr(devc)); + if (prev & 0x40) + { + restore_flags(flags); + return; + } + outb((devc->MCE_bit), io_Index_Addr(devc)); + restore_flags(flags); } -static void -ad_leave_MCE (ad1848_info * devc) +static void ad_leave_MCE(ad1848_info * devc) { - unsigned long flags; - unsigned char prev; - int timeout = 1000; + unsigned long flags; + unsigned char prev, acal; + int timeout = 1000; - while (timeout > 0 && inb (devc->base) == 0x80) /*Are we initializing */ - timeout--; + while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ + timeout--; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - devc->MCE_bit = 0x00; - prev = inb (io_Index_Addr (devc)); - outb (0x00, io_Index_Addr (devc)); /* Clear the MCE bit */ + acal = ad_read(devc, 9); - if ((prev & 0x40) == 0) /* Not in MCE mode */ - { - restore_flags (flags); - return; - } + devc->MCE_bit = 0x00; + prev = inb(io_Index_Addr(devc)); + outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ - outb (0x00, io_Index_Addr (devc)); /* Clear the MCE bit */ - wait_for_calibration (devc); - restore_flags (flags); + if ((prev & 0x40) == 0) /* Not in MCE mode */ + { + restore_flags(flags); + return; + } + outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ + if (acal & 0x08) /* Auto calibration is enabled */ + wait_for_calibration(devc); + restore_flags(flags); } - -static int -ad1848_set_recmask (ad1848_info * devc, int mask) +static int ad1848_set_recmask(ad1848_info * devc, int mask) { - unsigned char recdev; - int i, n; - - mask &= devc->supported_rec_devices; - - n = 0; - for (i = 0; i < 32; i++) /* Count selected device bits */ - if (mask & (1 << i)) - n++; - - if (n == 0) - mask = SOUND_MASK_MIC; - else if (n != 1) /* Too many devices selected */ - { - mask &= ~devc->recmask; /* Filter out active settings */ - - n = 0; - for (i = 0; i < 32; i++) /* Count selected device bits */ - if (mask & (1 << i)) - n++; - - if (n != 1) - mask = SOUND_MASK_MIC; - } - - switch (mask) - { - case SOUND_MASK_MIC: - recdev = 2; - break; - - case SOUND_MASK_LINE: - case SOUND_MASK_LINE3: - recdev = 0; - break; - - case SOUND_MASK_CD: - case SOUND_MASK_LINE1: - recdev = 1; - break; - - case SOUND_MASK_IMIX: - recdev = 3; - break; - - default: - mask = SOUND_MASK_MIC; - recdev = 2; - } - - recdev <<= 6; - ad_write (devc, 0, (ad_read (devc, 0) & 0x3f) | recdev); - ad_write (devc, 1, (ad_read (devc, 1) & 0x3f) | recdev); - - devc->recmask = mask; - return mask; + unsigned char recdev; + int i, n; + + mask &= devc->supported_rec_devices; + + /* Rename the mixer bits if necessary */ + for (i = 0; i < 32; i++) + { + if (devc->mixer_reroute[i] != i) + { + if (mask & (1 << i)) + { + mask &= ~(1 << i); + mask |= (1 << devc->mixer_reroute[i]); + } + } + } + + n = 0; + for (i = 0; i < 32; i++) /* Count selected device bits */ + if (mask & (1 << i)) + n++; + + if (n == 0) + mask = SOUND_MASK_MIC; + else if (n != 1) /* Too many devices selected */ + { + mask &= ~devc->recmask; /* Filter out active settings */ + + n = 0; + for (i = 0; i < 32; i++) /* Count selected device bits */ + if (mask & (1 << i)) + n++; + + if (n != 1) + mask = SOUND_MASK_MIC; + } + switch (mask) + { + case SOUND_MASK_MIC: + recdev = 2; + break; + + case SOUND_MASK_LINE: + case SOUND_MASK_LINE3: + recdev = 0; + break; + + case SOUND_MASK_CD: + case SOUND_MASK_LINE1: + recdev = 1; + break; + + case SOUND_MASK_IMIX: + recdev = 3; + break; + + default: + mask = SOUND_MASK_MIC; + recdev = 2; + } + + recdev <<= 6; + ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev); + ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev); + + /* Rename the mixer bits back if necessary */ + for (i = 0; i < 32; i++) + { + if (devc->mixer_reroute[i] != i) + { + if (mask & (1 << devc->mixer_reroute[i])) + { + mask &= ~(1 << devc->mixer_reroute[i]); + mask |= (1 << i); + } + } + } + devc->recmask = mask; + return mask; } -static void -change_bits (unsigned char *regval, int dev, int chn, int newval) +static void change_bits(ad1848_info * devc, unsigned char *regval, int dev, int chn, int newval) { - unsigned char mask; - int shift; + unsigned char mask; + int shift; + int mute; + int mutemask; + int set_mute_bit; + + set_mute_bit = (newval == 0); - if (mix_devices[dev][chn].polarity == 1) /* Reverse */ - newval = 100 - newval; + if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */ + newval = 100 - newval; - mask = (1 << mix_devices[dev][chn].nbits) - 1; - shift = mix_devices[dev][chn].bitpos; - newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ + mask = (1 << devc->mix_devices[dev][chn].nbits) - 1; + shift = devc->mix_devices[dev][chn].bitpos; - *regval &= ~(mask << shift); /* Clear bits */ - *regval |= (newval & mask) << shift; /* Set new value */ + if (devc->mix_devices[dev][chn].mutepos == 8) + { /* if there is no mute bit */ + mute = 0; /* No mute bit; do nothing special */ + mutemask = ~0; /* No mute bit; do nothing special */ + } + else + { + mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos); + mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos); + } + + newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ + *regval &= (~(mask << shift)) & (mutemask); /* Clear bits */ + *regval |= ((newval & mask) << shift) | mute; /* Set new value */ } -static int -ad1848_mixer_get (ad1848_info * devc, int dev) +static int ad1848_mixer_get(ad1848_info * devc, int dev) { - if (!((1 << dev) & devc->supported_devices)) - return -(EINVAL); + if (!((1 << dev) & devc->supported_devices)) + return -EINVAL; - return devc->levels[dev]; + dev = devc->mixer_reroute[dev]; + + return devc->levels[dev]; } -static int -ad1848_mixer_set (ad1848_info * devc, int dev, int value) +static int ad1848_mixer_set(ad1848_info * devc, int dev, int value) { - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int retvol; + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int retvol; + + int regoffs; + unsigned char val; - int regoffs; - unsigned char val; + if (dev > 31) + return -EINVAL; - if (left > 100) - left = 100; - if (right > 100) - right = 100; + if (!(devc->supported_devices & (1 << dev))) + return -EINVAL; - if (mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */ - right = left; + dev = devc->mixer_reroute[dev]; - retvol = left | (right << 8); + if (left > 100) + left = 100; + if (right > 100) + right = 100; - /* Scale volumes */ - left = mix_cvt[left]; - right = mix_cvt[right]; + if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */ + right = left; - /* Scale it again */ - left = mix_cvt[left]; - right = mix_cvt[right]; + retvol = left | (right << 8); - if (dev > 31) - return -(EINVAL); + /* Scale volumes */ + left = mix_cvt[left]; + right = mix_cvt[right]; - if (!(devc->supported_devices & (1 << dev))) - return -(EINVAL); + /* Scale it again */ + left = mix_cvt[left]; + right = mix_cvt[right]; - if (mix_devices[dev][LEFT_CHN].nbits == 0) - return -(EINVAL); + if (devc->mix_devices[dev][LEFT_CHN].nbits == 0) + return -EINVAL; - devc->levels[dev] = retvol; + devc->levels[dev] = retvol; - /* - * Set the left channel - */ + /* + * Set the left channel + */ - regoffs = mix_devices[dev][LEFT_CHN].regno; - val = ad_read (devc, regoffs); - change_bits (&val, dev, LEFT_CHN, left); - ad_write (devc, regoffs, val); - devc->saved_regs[regoffs] = val; + regoffs = devc->mix_devices[dev][LEFT_CHN].regno; + val = ad_read(devc, regoffs); + change_bits(devc, &val, dev, LEFT_CHN, left); + ad_write(devc, regoffs, val); + devc->saved_regs[regoffs] = val; - /* - * Set the right channel - */ + /* + * Set the right channel + */ - if (mix_devices[dev][RIGHT_CHN].nbits == 0) - return retvol; /* Was just a mono channel */ + if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) + return retvol; /* Was just a mono channel */ - regoffs = mix_devices[dev][RIGHT_CHN].regno; - val = ad_read (devc, regoffs); - change_bits (&val, dev, RIGHT_CHN, right); - ad_write (devc, regoffs, val); - devc->saved_regs[regoffs] = val; + regoffs = devc->mix_devices[dev][RIGHT_CHN].regno; + val = ad_read(devc, regoffs); + change_bits(devc, &val, dev, RIGHT_CHN, right); + ad_write(devc, regoffs, val); + devc->saved_regs[regoffs] = val; - return retvol; + return retvol; } -static void -ad1848_mixer_reset (ad1848_info * devc) +static void ad1848_mixer_reset(ad1848_info * devc) { - int i; - - switch (devc->model) - { - case MD_4231: - case MD_4231A: - case MD_C930: - case MD_IWAVE: - case MD_1845: - devc->supported_devices = MODE2_MIXER_DEVICES; - break; - - case MD_4232: - devc->supported_devices = MODE3_MIXER_DEVICES; - break; - - default: - devc->supported_devices = MODE1_MIXER_DEVICES; - } - - devc->supported_rec_devices = MODE1_REC_DEVICES; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (devc->supported_devices & (1 << i)) - ad1848_mixer_set (devc, i, default_mixer_levels[i]); - ad1848_set_recmask (devc, SOUND_MASK_MIC); + int i; + char name[32]; + + devc->mix_devices = &(ad1848_mix_devices[0]); + + sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs); + + for (i = 0; i < 32; i++) + devc->mixer_reroute[i] = i; + + switch (devc->model) + { + case MD_4231: + case MD_4231A: + case MD_1845: + devc->supported_devices = MODE2_MIXER_DEVICES; + break; + + case MD_C930: + devc->supported_devices = C930_MIXER_DEVICES; + devc->mix_devices = &(c930_mix_devices[0]); + break; + + case MD_IWAVE: + devc->supported_devices = MODE3_MIXER_DEVICES; + devc->mix_devices = &(iwave_mix_devices[0]); + break; + + case MD_4232: + devc->supported_devices = MODE3_MIXER_DEVICES; + break; + + default: + devc->supported_devices = MODE1_MIXER_DEVICES; + } + + devc->supported_rec_devices = MODE1_REC_DEVICES; + devc->orig_devices = devc->supported_devices; + devc->orig_rec_devices = devc->supported_rec_devices; + + devc->levels = load_mixer_volumes(name, default_mixer_levels, 1); + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + { + if (devc->supported_devices & (1 << i)) + ad1848_mixer_set(devc, i, devc->levels[i]); + } + + ad1848_set_recmask(devc, SOUND_MASK_MIC); + + devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT; + if (devc->mixer_output_port & AUDIO_SPEAKER) + ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ + else + ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ } -static int -ad1848_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) { - ad1848_info *devc = mixer_devs[dev]->devc; - - if (((cmd >> 8) & 0xff) == 'M') - { - - if (_IOC_DIR (cmd) & _IOC_WRITE) - switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - return snd_ioctl_return ((int *) arg, ad1848_set_recmask (devc, get_user ((int *) arg))); - break; - - default: - return snd_ioctl_return ((int *) arg, ad1848_mixer_set (devc, cmd & 0xff, get_user ((int *) arg))); - } - else - switch (cmd & 0xff) /* + ad1848_info *devc = mixer_devs[dev]->devc; + int val; + + if (cmd == SOUND_MIXER_PRIVATE1) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (val != 0xffff) + { + val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT); + devc->mixer_output_port = val; + val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */ + devc->mixer_output_port = val; + if (val & AUDIO_SPEAKER) + ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ + else + ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ + } + val = devc->mixer_output_port; + return put_user(val, (int *)arg); + } + if (cmd == SOUND_MIXER_PRIVATE2) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + return(ad1848_control(AD1848_MIXER_REROUTE, val)); + } + if (((cmd >> 8) & 0xff) == 'M') + { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + { + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = ad1848_set_recmask(devc, val); + break; + + default: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = ad1848_mixer_set(devc, cmd & 0xff, val); + break; + } + return put_user(val, (int *)arg); + } + else + { + switch (cmd & 0xff) + { + /* * Return parameters */ - { - - case SOUND_MIXER_RECSRC: - return snd_ioctl_return ((int *) arg, devc->recmask); - break; - - case SOUND_MIXER_DEVMASK: - return snd_ioctl_return ((int *) arg, devc->supported_devices); - break; - - case SOUND_MIXER_STEREODEVS: - return snd_ioctl_return ((int *) arg, devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX)); - break; - - case SOUND_MIXER_RECMASK: - return snd_ioctl_return ((int *) arg, devc->supported_rec_devices); - break; - - case SOUND_MIXER_CAPS: - return snd_ioctl_return ((int *) arg, SOUND_CAP_EXCL_INPUT); - break; - - default: - return snd_ioctl_return ((int *) arg, ad1848_mixer_get (devc, cmd & 0xff)); - } - } - else - return -(EINVAL); + + case SOUND_MIXER_RECSRC: + val = devc->recmask; + break; + + case SOUND_MIXER_DEVMASK: + val = devc->supported_devices; + break; + + case SOUND_MIXER_STEREODEVS: + val = devc->supported_devices; + if (devc->model != MD_C930) + val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); + break; + + case SOUND_MIXER_RECMASK: + val = devc->supported_rec_devices; + break; + + case SOUND_MIXER_CAPS: + val=SOUND_CAP_EXCL_INPUT; + break; + + default: + val = ad1848_mixer_get(devc, cmd & 0xff); + break; + } + return put_user(val, (int *)arg); + } + } + else + return -EINVAL; } -static int -ad1848_set_speed (int dev, int arg) +static int ad1848_set_speed(int dev, int arg) { - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - /* - * The sampling speed is encoded in the least significant nibble of I8. The - * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other - * three bits select the divisor (indirectly): - * - * The available speeds are in the following table. Keep the speeds in - * the increasing order. - */ - typedef struct - { - int speed; - unsigned char bits; - } - speed_struct; - - static speed_struct speed_table[] = - { - {5510, (0 << 1) | 1}, - {5510, (0 << 1) | 1}, - {6620, (7 << 1) | 1}, - {8000, (0 << 1) | 0}, - {9600, (7 << 1) | 0}, - {11025, (1 << 1) | 1}, - {16000, (1 << 1) | 0}, - {18900, (2 << 1) | 1}, - {22050, (3 << 1) | 1}, - {27420, (2 << 1) | 0}, - {32000, (3 << 1) | 0}, - {33075, (6 << 1) | 1}, - {37800, (4 << 1) | 1}, - {44100, (5 << 1) | 1}, - {48000, (6 << 1) | 0} - }; - - int i, n, selected = -1; - - n = sizeof (speed_table) / sizeof (speed_struct); - - if (arg == 0) - return devc->speed; - - if (devc->model == MD_1845) /* AD1845 has different timer than others */ - { - if (arg < 4000) - arg = 4000; - if (arg > 50000) - arg = 50000; - - devc->speed = arg; - devc->speed_bits = speed_table[3].bits; - return devc->speed; - } - - if (arg < speed_table[0].speed) - selected = 0; - if (arg > speed_table[n - 1].speed) - selected = n - 1; - - for (i = 1 /*really */ ; selected == -1 && i < n; i++) - if (speed_table[i].speed == arg) - selected = i; - else if (speed_table[i].speed > arg) - { - int diff1, diff2; - - diff1 = arg - speed_table[i - 1].speed; - diff2 = speed_table[i].speed - arg; - - if (diff1 < diff2) - selected = i - 1; - else - selected = i; - } - - if (selected == -1) - { - printk ("ad1848: Can't find speed???\n"); - selected = 3; - } - - devc->speed = speed_table[selected].speed; - devc->speed_bits = speed_table[selected].bits; - return devc->speed; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + + /* + * The sampling speed is encoded in the least significant nibble of I8. The + * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other + * three bits select the divisor (indirectly): + * + * The available speeds are in the following table. Keep the speeds in + * the increasing order. + */ + typedef struct + { + int speed; + unsigned char bits; + } + speed_struct; + + static speed_struct speed_table[] = + { + {5510, (0 << 1) | 1}, + {5510, (0 << 1) | 1}, + {6620, (7 << 1) | 1}, + {8000, (0 << 1) | 0}, + {9600, (7 << 1) | 0}, + {11025, (1 << 1) | 1}, + {16000, (1 << 1) | 0}, + {18900, (2 << 1) | 1}, + {22050, (3 << 1) | 1}, + {27420, (2 << 1) | 0}, + {32000, (3 << 1) | 0}, + {33075, (6 << 1) | 1}, + {37800, (4 << 1) | 1}, + {44100, (5 << 1) | 1}, + {48000, (6 << 1) | 0} + }; + + int i, n, selected = -1; + + n = sizeof(speed_table) / sizeof(speed_struct); + + if (arg <= 0) + return portc->speed; + + if (devc->model == MD_1845) /* AD1845 has different timer than others */ + { + if (arg < 4000) + arg = 4000; + if (arg > 50000) + arg = 50000; + + portc->speed = arg; + portc->speed_bits = speed_table[3].bits; + return portc->speed; + } + if (arg < speed_table[0].speed) + selected = 0; + if (arg > speed_table[n - 1].speed) + selected = n - 1; + + for (i = 1 /*really */ ; selected == -1 && i < n; i++) + { + if (speed_table[i].speed == arg) + selected = i; + else if (speed_table[i].speed > arg) + { + int diff1, diff2; + + diff1 = arg - speed_table[i - 1].speed; + diff2 = speed_table[i].speed - arg; + + if (diff1 < diff2) + selected = i - 1; + else + selected = i; + } + } + if (selected == -1) + { + printk(KERN_WARNING "ad1848: Can't find speed???\n"); + selected = 3; + } + portc->speed = speed_table[selected].speed; + portc->speed_bits = speed_table[selected].bits; + return portc->speed; } -static short -ad1848_set_channels (int dev, short arg) +static short ad1848_set_channels(int dev, short arg) { - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - if (arg != 1 && arg != 2) - return devc->channels; + if (arg != 1 && arg != 2) + return portc->channels; - devc->channels = arg; - return arg; + portc->channels = arg; + return arg; } -static unsigned int -ad1848_set_bits (int dev, unsigned int arg) +static unsigned int ad1848_set_bits(int dev, unsigned int arg) { - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - static struct format_tbl - { - int format; - unsigned char bits; - } - format2bits[] = - { - { - 0, 0 - } - , - { - AFMT_MU_LAW, 1 - } - , - { - AFMT_A_LAW, 3 - } - , - { - AFMT_IMA_ADPCM, 5 - } - , - { - AFMT_U8, 0 - } - , - { - AFMT_S16_LE, 2 - } - , - { - AFMT_S16_BE, 6 - } - , - { - AFMT_S8, 0 - } - , - { - AFMT_U16_LE, 0 - } - , - { - AFMT_U16_BE, 0 - } - }; - int i, n = sizeof (format2bits) / sizeof (struct format_tbl); - - if (arg == 0) - return devc->audio_format; - - if (!(arg & ad_format_mask[devc->model])) - arg = AFMT_U8; - - devc->audio_format = arg; - - for (i = 0; i < n; i++) - if (format2bits[i].format == arg) - { - if ((devc->format_bits = format2bits[i].bits) == 0) - return devc->audio_format = AFMT_U8; /* Was not supported */ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - return arg; - } + static struct format_tbl + { + int format; + unsigned char bits; + } + format2bits[] = + { + { + 0, 0 + } + , + { + AFMT_MU_LAW, 1 + } + , + { + AFMT_A_LAW, 3 + } + , + { + AFMT_IMA_ADPCM, 5 + } + , + { + AFMT_U8, 0 + } + , + { + AFMT_S16_LE, 2 + } + , + { + AFMT_S16_BE, 6 + } + , + { + AFMT_S8, 0 + } + , + { + AFMT_U16_LE, 0 + } + , + { + AFMT_U16_BE, 0 + } + }; + int i, n = sizeof(format2bits) / sizeof(struct format_tbl); + + if (arg == 0) + return portc->audio_format; + + if (!(arg & ad_format_mask[devc->model])) + arg = AFMT_U8; + + portc->audio_format = arg; - /* Still hanging here. Something must be terribly wrong */ - devc->format_bits = 0; - return devc->audio_format = AFMT_U8; + for (i = 0; i < n; i++) + if (format2bits[i].format == arg) + { + if ((portc->format_bits = format2bits[i].bits) == 0) + return portc->audio_format = AFMT_U8; /* Was not supported */ + + return arg; + } + /* Still hanging here. Something must be terribly wrong */ + portc->format_bits = 0; + return portc->audio_format = AFMT_U8; } static struct audio_driver ad1848_audio_driver = { - ad1848_open, - ad1848_close, - ad1848_output_block, - ad1848_start_input, - ad1848_ioctl, - ad1848_prepare_for_IO, - ad1848_prepare_for_output, - ad1848_reset, - ad1848_halt, - NULL, - NULL, - ad1848_halt_input, - ad1848_halt_output, - ad1848_trigger, - ad1848_set_speed, - ad1848_set_bits, - ad1848_set_channels + ad1848_open, + ad1848_close, + ad1848_output_block, + ad1848_start_input, + NULL, + ad1848_prepare_for_input, + ad1848_prepare_for_output, + ad1848_halt, + NULL, + NULL, + ad1848_halt_input, + ad1848_halt_output, + ad1848_trigger, + ad1848_set_speed, + ad1848_set_bits, + ad1848_set_channels }; static struct mixer_operations ad1848_mixer_operations = { - "SOUNDPORT", - "AD1848/CS4248/CS4231", - ad1848_mixer_ioctl + "SOUNDPORT", + "AD1848/CS4248/CS4231", + ad1848_mixer_ioctl }; -static int -ad1848_open (int dev, int mode) +static int ad1848_open(int dev, int mode) { - ad1848_info *devc = NULL; - unsigned long flags; + ad1848_info *devc = NULL; + ad1848_port_info *portc; + unsigned long flags; - if (dev < 0 || dev >= num_audiodevs) - return -(ENXIO); + if (dev < 0 || dev >= num_audiodevs) + return -ENXIO; - devc = (ad1848_info *) audio_devs[dev]->devc; + devc = (ad1848_info *) audio_devs[dev]->devc; + portc = (ad1848_port_info *) audio_devs[dev]->portc; + save_flags(flags); + cli(); + if (portc->open_mode || (devc->open_mode & mode)) + { + restore_flags(flags); + return -EBUSY; + } + devc->dual_dma = 0; - save_flags (flags); - cli (); - if (devc->opened) - { - restore_flags (flags); - return -(EBUSY); - } - - devc->dual_dma = 0; - - if (audio_devs[dev]->flags & DMA_DUPLEX) - { - devc->dual_dma = 1; - } - - devc->intr_active = 0; - devc->opened = 1; - devc->audio_mode = 0; - ad1848_trigger (dev, 0); - restore_flags (flags); + if (audio_devs[dev]->flags & DMA_DUPLEX) + { + devc->dual_dma = 1; + } + devc->intr_active = 0; + devc->audio_mode = 0; + devc->open_mode |= mode; + portc->open_mode = mode; + ad1848_trigger(dev, 0); + + if (mode & OPEN_READ) + devc->record_dev = dev; + if (mode & OPEN_WRITE) + devc->playback_dev = dev; + restore_flags(flags); /* * Mute output until the playback really starts. This decreases clicking (hope so). */ - ad_mute (devc); + ad_mute(devc); - return 0; + return 0; } -static void -ad1848_close (int dev) +static void ad1848_close(int dev) { - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - DEB (printk ("ad1848_close(void)\n")); + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - save_flags (flags); - cli (); + DEB(printk("ad1848_close(void)\n")); - devc->intr_active = 0; - ad1848_reset (dev); + save_flags(flags); + cli(); + devc->intr_active = 0; + ad1848_halt(dev); - devc->opened = 0; - devc->audio_mode = 0; + devc->audio_mode = 0; + devc->open_mode &= ~portc->open_mode; + portc->open_mode = 0; - ad_unmute (devc); - restore_flags (flags); + ad_unmute(devc); + restore_flags(flags); } -static int -ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg, int local) +static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag) { - return -(EINVAL); -} + unsigned long flags, cnt; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; -static void -ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart) -{ - unsigned long flags, cnt; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - if (!dma_restart) - return; - - cnt = count; - - if (devc->audio_format == AFMT_IMA_ADPCM) - { - cnt /= 4; - } - else - { - if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ - cnt >>= 1; - } - if (devc->channels > 1) - cnt >>= 1; - cnt--; - - if (devc->audio_mode & PCM_ENABLE_OUTPUT && audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - cnt == devc->xfer_count) - { - devc->audio_mode |= PCM_ENABLE_OUTPUT; - devc->intr_active = 1; - return; /* - * Auto DMA mode on. No need to react - */ - } - save_flags (flags); - cli (); + cnt = count; - /* ad_write (devc, 9, ad_read (devc, 9) & ~ 0x01); / * Playback disable */ - ad1848_halt (dev); - DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + if (portc->audio_format == AFMT_IMA_ADPCM) + { + cnt /= 4; + } + else + { + if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ + cnt >>= 1; + } + if (portc->channels > 1) + cnt >>= 1; + cnt--; - ad_write (devc, 15, (unsigned char) (cnt & 0xff)); - ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); + if (devc->audio_mode & PCM_ENABLE_OUTPUT && audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == devc->xfer_count) + { + devc->audio_mode |= PCM_ENABLE_OUTPUT; + devc->intr_active = 1; + return; /* + * Auto DMA mode on. No need to react + */ + } + save_flags(flags); + cli(); + ad_write(devc, 15, (unsigned char) (cnt & 0xff)); + ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); - devc->xfer_count = cnt; - devc->audio_mode |= PCM_ENABLE_OUTPUT; - devc->intr_active = 1; - restore_flags (flags); + devc->xfer_count = cnt; + devc->audio_mode |= PCM_ENABLE_OUTPUT; + devc->intr_active = 1; + restore_flags(flags); } -static void -ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart) +static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag) { - unsigned long flags, cnt; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - if (!dma_restart) - return; - - cnt = count; - if (devc->audio_format == AFMT_IMA_ADPCM) - { - cnt /= 4; - } - else - { - if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ - cnt >>= 1; - } - if (devc->channels > 1) - cnt >>= 1; - cnt--; - - if (devc->audio_mode & PCM_ENABLE_INPUT && audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - cnt == devc->xfer_count) - { - devc->audio_mode |= PCM_ENABLE_INPUT; - devc->intr_active = 1; - return; /* - * Auto DMA mode on. No need to react - */ - } - save_flags (flags); - cli (); - - if (dma_restart) - { - /* ad1848_halt (dev); */ - ad_write (devc, 9, ad_read (devc, 9) & ~0x02); /* Capture disable */ - DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); - } - - if (devc->model == MD_1848 || !devc->dual_dma) /* Single DMA channel mode */ - { - ad_write (devc, 15, (unsigned char) (cnt & 0xff)); - ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); - } - else - /* Dual DMA channel mode */ - { - ad_write (devc, 31, (unsigned char) (cnt & 0xff)); - ad_write (devc, 30, (unsigned char) ((cnt >> 8) & 0xff)); - } - - ad_unmute (devc); - - devc->xfer_count = cnt; - devc->audio_mode |= PCM_ENABLE_INPUT; - devc->intr_active = 1; - restore_flags (flags); -} + unsigned long flags, cnt; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; -static int -ad1848_prepare_for_output (int dev, int bsize, int bcount) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + cnt = count; + if (portc->audio_format == AFMT_IMA_ADPCM) + { + cnt /= 4; + } + else + { + if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ + cnt >>= 1; + } + if (portc->channels > 1) + cnt >>= 1; + cnt--; + + if (devc->audio_mode & PCM_ENABLE_INPUT && audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == devc->xfer_count) + { + devc->audio_mode |= PCM_ENABLE_INPUT; + devc->intr_active = 1; + return; /* + * Auto DMA mode on. No need to react + */ + } + save_flags(flags); + cli(); - ad_mute (devc); + if (devc->model == MD_1848) + { + ad_write(devc, 15, (unsigned char) (cnt & 0xff)); + ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); + } + else + { + ad_write(devc, 31, (unsigned char) (cnt & 0xff)); + ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff)); + } - return ad1848_prepare_for_IO (dev, bsize, bcount); + ad_unmute(devc); + + devc->xfer_count = cnt; + devc->audio_mode |= PCM_ENABLE_INPUT; + devc->intr_active = 1; + restore_flags(flags); } -static int -ad1848_prepare_for_IO (int dev, int bsize, int bcount) +static int ad1848_prepare_for_output(int dev, int bsize, int bcount) { - int timeout; - unsigned char fs, old_fs, tmp = 0; - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + int timeout; + unsigned char fs, old_fs, tmp = 0; + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - if (devc->audio_mode) - return 0; + ad_mute(devc); - save_flags (flags); - cli (); - fs = devc->speed_bits | (devc->format_bits << 5); + save_flags(flags); + cli(); + fs = portc->speed_bits | (portc->format_bits << 5); - if (devc->channels > 1) - fs |= 0x10; + if (portc->channels > 1) + fs |= 0x10; - if (devc->model == MD_1845) /* Use alternate speed select registers */ - { - fs &= 0xf0; /* Mask off the rate select bits */ + ad_enter_MCE(devc); /* Enables changes to the format select reg */ - ad_write (devc, 22, (devc->speed >> 8) & 0xff); /* Speed MSB */ - ad_write (devc, 23, devc->speed & 0xff); /* Speed LSB */ - } + if (devc->model == MD_1845) /* Use alternate speed select registers */ + { + fs &= 0xf0; /* Mask off the rate select bits */ - old_fs = ad_read (devc, 8); + ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */ + ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */ + } + old_fs = ad_read(devc, 8); - if (devc->model != MD_4232) - if (fs == old_fs) /* No change */ - { - restore_flags (flags); - devc->xfer_count = 0; - return 0; - } - - ad_enter_MCE (devc); /* Enables changes to the format select reg */ - - if (devc->model == MD_4232) - { - tmp = ad_read (devc, 16); - ad_write (devc, 16, tmp | 0x30); - } - - ad_write (devc, 8, fs); - /* - * Write to I8 starts resynchronization. Wait until it completes. - */ - timeout = 10000; - while (timeout > 0 && inb (devc->base) == 0x80) - timeout--; - - /* - * If mode >= 2 (CS4231), set I28 also. It's the capture format register. - */ - if (devc->model != MD_1848) - { - ad_write (devc, 28, fs); - - /* - * Write to I28 starts resynchronization. Wait until it completes. - */ - timeout = 10000; - while (timeout > 0 && inb (devc->base) == 0x80) - timeout--; - - } - - if (devc->model == MD_4232) - ad_write (devc, 16, tmp & ~0x30); - - ad_leave_MCE (devc); /* + if (devc->model == MD_4232) + { + tmp = ad_read(devc, 16); + ad_write(devc, 16, tmp | 0x30); + } + if (devc->model == MD_IWAVE) + ad_write(devc, 17, 0xc2); /* Disable variable frequency select */ + + ad_write(devc, 8, fs); + + /* + * Write to I8 starts resynchronization. Wait until it completes. + */ + + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + + if (devc->model == MD_4232) + ad_write(devc, 16, tmp & ~0x30); + + ad_leave_MCE(devc); /* * Starts the calibration process. */ - restore_flags (flags); - devc->xfer_count = 0; - -#ifdef CONFIG_SEQUENCER - if (dev == timer_installed && devc->timer_running) - if ((fs & 0x01) != (old_fs & 0x01)) - { - ad1848_tmr_reprogram (dev); - } -#endif - return 0; -} + restore_flags(flags); + devc->xfer_count = 0; -static void -ad1848_reset (int dev) -{ - ad1848_halt (dev); +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) + if (dev == timer_installed && devc->timer_running) + if ((fs & 0x01) != (old_fs & 0x01)) + { + ad1848_tmr_reprogram(dev); + } +#endif + ad1848_halt_output(dev); + return 0; } -static void -ad1848_halt (int dev) +static int ad1848_prepare_for_input(int dev, int bsize, int bcount) { - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - unsigned char bits = ad_read (devc, 9); + int timeout; + unsigned char fs, old_fs, tmp = 0; + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - if (bits & 0x01) - ad1848_halt_output (dev); + if (devc->audio_mode) + return 0; - if (bits & 0x02) - ad1848_halt_input (dev); -} + save_flags(flags); + cli(); + fs = portc->speed_bits | (portc->format_bits << 5); -static void -ad1848_halt_input (int dev) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - unsigned long flags; + if (portc->channels > 1) + fs |= 0x10; - save_flags (flags); - cli (); + ad_enter_MCE(devc); /* Enables changes to the format select reg */ - ad_mute (devc); + if (devc->model == MD_1845) /* Use alternate speed select registers */ + { + fs &= 0xf0; /* Mask off the rate select bits */ - if (devc->model == MD_4232) /* Use applied black magic */ - { - int tmout; + ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */ + ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */ + } + if (devc->model == MD_4232) + { + tmp = ad_read(devc, 16); + ad_write(devc, 16, tmp | 0x30); + } + if (devc->model == MD_IWAVE) + ad_write(devc, 17, 0xc2); /* Disable variable frequency select */ + + /* + * If mode >= 2 (CS4231), set I28. It's the capture format register. + */ + + if (devc->model != MD_1848) + { + old_fs = ad_read(devc, 28); + ad_write(devc, 28, fs); + + /* + * Write to I28 starts resynchronization. Wait until it completes. + */ + + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + + if (devc->model != MD_1848 && devc->model != MD_1845) + { + /* + * CS4231 compatible devices don't have separate sampling rate selection + * register for recording an playback. The I8 register is shared so we have to + * set the speed encoding bits of it too. + */ + unsigned char tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0); + + ad_write(devc, 8, tmp); + /* + * Write to I8 starts resynchronization. Wait until it completes. + */ + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + } + } + else + { /* For AD1848 set I8. */ + + old_fs = ad_read(devc, 8); + ad_write(devc, 8, fs); + /* + * Write to I8 starts resynchronization. Wait until it completes. + */ + timeout = 0; + while (timeout < 100 && inb(devc->base) != 0x80) + timeout++; + timeout = 0; + while (timeout < 10000 && inb(devc->base) == 0x80) + timeout++; + } - disable_dma (audio_devs[dev]->dmachan1); + if (devc->model == MD_4232) + ad_write(devc, 16, tmp & ~0x30); - for (tmout = 0; tmout < 100000; tmout++) - if (ad_read (devc, 11) & 0x10) - break; - ad_write (devc, 9, ad_read (devc, 9) & ~0x01); /* Stop playback */ + ad_leave_MCE(devc); /* + * Starts the calibration process. + */ + restore_flags(flags); + devc->xfer_count = 0; - enable_dma (audio_devs[dev]->dmachan1); - restore_flags (flags); - return; - } - ad_write (devc, 9, ad_read (devc, 9) & ~0x02); /* Stop capture */ +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) + if (dev == timer_installed && devc->timer_running) + { + if ((fs & 0x01) != (old_fs & 0x01)) + { + ad1848_tmr_reprogram(dev); + } + } +#endif + ad1848_halt_input(dev); + return 0; +} +static void ad1848_halt(int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - outb (0, io_Status (devc)); /* Clear interrupt status */ - outb (0, io_Status (devc)); /* Clear interrupt status */ + unsigned char bits = ad_read(devc, 9); - devc->audio_mode &= ~PCM_ENABLE_INPUT; + if (bits & 0x01 && portc->open_mode & OPEN_WRITE) + ad1848_halt_output(dev); - restore_flags (flags); + if (bits & 0x02 && portc->open_mode & OPEN_READ) + ad1848_halt_input(dev); + devc->audio_mode = 0; } -static void -ad1848_halt_output (int dev) +static void ad1848_halt_input(int dev) { - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned long flags; - save_flags (flags); - cli (); + if (!(ad_read(devc, 9) & 0x02)) + return; /* Capture not enabled */ - ad_mute (devc); - if (devc->model == MD_4232) /* Use applied black magic */ - { - int tmout; + save_flags(flags); + cli(); - disable_dma (audio_devs[dev]->dmachan1); + ad_mute(devc); - for (tmout = 0; tmout < 100000; tmout++) - if (ad_read (devc, 11) & 0x10) - break; - ad_write (devc, 9, ad_read (devc, 9) & ~0x01); /* Stop playback */ + { + int tmout; - enable_dma (audio_devs[dev]->dmachan1); - restore_flags (flags); - return; - } - ad_write (devc, 9, ad_read (devc, 9) & ~0x01); /* Stop playback */ + disable_dma(audio_devs[dev]->dmap_in->dma); + for (tmout = 0; tmout < 100000; tmout++) + if (ad_read(devc, 11) & 0x10) + break; + ad_write(devc, 9, ad_read(devc, 9) & ~0x02); /* Stop capture */ - outb (0, io_Status (devc)); /* Clear interrupt status */ - outb (0, io_Status (devc)); /* Clear interrupt status */ + enable_dma(audio_devs[dev]->dmap_in->dma); + devc->audio_mode &= ~PCM_ENABLE_INPUT; + } - devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + outb(0, io_Status(devc)); /* Clear interrupt status */ + outb(0, io_Status(devc)); /* Clear interrupt status */ - restore_flags (flags); + devc->audio_mode &= ~PCM_ENABLE_INPUT; + + restore_flags(flags); } -static void -ad1848_trigger (int dev, int state) +static void ad1848_halt_output(int dev) { - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - unsigned long flags; - unsigned char tmp, old; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned long flags; + + if (!(ad_read(devc, 9) & 0x01)) + return; /* Playback not enabled */ + + save_flags(flags); + cli(); - save_flags (flags); - cli (); - state &= devc->audio_mode; + ad_mute(devc); + { + int tmout; + + disable_dma(audio_devs[dev]->dmap_out->dma); - tmp = (old = ad_read (devc, 9)) & ~0x03; - if (state & PCM_ENABLE_INPUT) - tmp |= 0x02; - if (state & PCM_ENABLE_OUTPUT) - tmp |= 0x01; + for (tmout = 0; tmout < 100000; tmout++) + if (ad_read(devc, 11) & 0x10) + break; + ad_write(devc, 9, ad_read(devc, 9) & ~0x01); /* Stop playback */ - if (!(state & PCM_ENABLE_OUTPUT) && old & 0x01); - ad_mute (devc); + enable_dma(audio_devs[dev]->dmap_out->dma); + devc->audio_mode &= ~PCM_ENABLE_OUTPUT; + } - ad_write (devc, 9, tmp); + outb((0), io_Status(devc)); /* Clear interrupt status */ + outb((0), io_Status(devc)); /* Clear interrupt status */ - if (state & PCM_ENABLE_OUTPUT && !(old & 0x01)); - ad_unmute (devc); + devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - restore_flags (flags); + restore_flags(flags); } -int -ad1848_detect (int io_base, int *ad_flags, int *osp) +static void ad1848_trigger(int dev, int state) { - /* - * Initial values for the indirect registers of CS4248/AD1848. - */ - static int init_values[] = - { - 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00, - - /* Positions 16 to 31 just for CS4231/2 and ad1845 */ - 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - unsigned char tmp; - ad1848_info *devc = &dev_info[nr_ad1848_devs]; - unsigned char tmp1 = 0xff, tmp2 = 0xff; - int optiC930 = 0; /* OPTi 82C930 flag */ - int interwave = 0; - int ad1847_flag = 0; - - int i; - - DDB (printk ("ad1848_detect(%x)\n", io_base)); - - if (ad_flags) - { - if (*ad_flags == 0x12345678) - interwave = 1; - *ad_flags = 0; - } - - if (nr_ad1848_devs >= MAX_AUDIO_DEV) - { - DDB (printk ("ad1848 detect error - step 0\n")); - return 0; - } - if (check_region (io_base, 4)) - { - printk ("\n\nad1848.c: Port %x not free.\n\n", io_base); - return 0; - } - - devc->base = io_base; - devc->irq_ok = 0; - devc->timer_running = 0; - devc->MCE_bit = 0x40; - devc->irq = 0; - devc->opened = 0; - devc->chip_name = "AD1848"; - devc->model = MD_1848; /* AD1848 or CS4248 */ - devc->osp = osp; - devc->debug_flag = 0; - - /* - * Check that the I/O address is in use. - * - * The bit 0x80 of the base I/O port is known to be 0 after the - * chip has performed its power on initialization. Just assume - * this has happened before the OS is starting. - * - * If the I/O address is unused, it typically returns 0xff. - */ - - DDB (printk ("ad1848_detect() - step A\n")); - if ((inb (devc->base) & 0x80) != 0x00) /* Not a AD1848 */ - { - DDB (printk ("ad1848 detect error - step A (%02x)\n", - inb (devc->base))); - return 0; - } - - DDB (printk ("ad1848: regs: ")); - for (i = 0; i < 32; i++) - DDB (printk ("%02x ", ad_read (devc, i))); - DDB (printk ("\n")); - - /* - * Test if it's possible to change contents of the indirect registers. - * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only - * so try to avoid using it. - */ - - DDB (printk ("ad1848_detect() - step B\n")); - ad_write (devc, 0, 0xaa); - ad_write (devc, 1, 0x45); /* 0x55 with bit 0x10 clear */ - - if ((tmp1 = ad_read (devc, 0)) != 0xaa || (tmp2 = ad_read (devc, 1)) != 0x45) - if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */ - ad1847_flag = 1; - else - { - DDB (printk ("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2)); - return 0; - } - - DDB (printk ("ad1848_detect() - step C\n")); - ad_write (devc, 0, 0x45); - ad_write (devc, 1, 0xaa); - - if ((tmp1 = ad_read (devc, 0)) != 0x45 || (tmp2 = ad_read (devc, 1)) != 0xaa) - if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */ - ad1847_flag = 1; - else - { - DDB (printk ("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2)); - return 0; - } - - /* - * The indirect register I12 has some read only bits. Lets - * try to change them. - */ - - DDB (printk ("ad1848_detect() - step D\n")); - tmp = ad_read (devc, 12); - ad_write (devc, 12, (~tmp) & 0x0f); - - if ((tmp & 0x0f) != ((tmp1 = ad_read (devc, 12)) & 0x0f)) - { - DDB (printk ("ad1848 detect error - step D (%x)\n", tmp1)); - return 0; - } - - /* - * NOTE! Last 4 bits of the reg I12 tell the chip revision. - * 0x01=RevB and 0x0A=RevC. - */ - - /* - * The original AD1848/CS4248 has just 15 indirect registers. This means - * that I0 and I16 should return the same value (etc.). - * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails - * with CS4231. - */ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; + unsigned long flags; + unsigned char tmp, old; -/* - * OPTi 82C930 has mode2 control bit in another place. This test will fail - * with it. Accept this situation as a possible indication of this chip. - */ + save_flags(flags); + cli(); + state &= devc->audio_mode; - DDB (printk ("ad1848_detect() - step F\n")); - ad_write (devc, 12, 0); /* Mode2=disabled */ - - for (i = 0; i < 16; i++) - if ((tmp1 = ad_read (devc, i)) != (tmp2 = ad_read (devc, i + 16))) - { - DDB (printk ("ad1848 detect step F(%d/%x/%x) - OPTi chip?\n", i, tmp1, tmp2)); - if (!ad1847_flag) - optiC930 = 1; - break; - } - - /* - * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40). - * The bit 0x80 is always 1 in CS4248 and CS4231. - */ - - DDB (printk ("ad1848_detect() - step G\n")); - ad_write (devc, 12, 0x40); /* Set mode2, clear 0x80 */ - - tmp1 = ad_read (devc, 12); - if (tmp1 & 0x80) - { - if (ad_flags) - *ad_flags |= AD_F_CS4248; - - devc->chip_name = "CS4248"; /* Our best knowledge just now */ - } - - if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40)) - { - /* - * CS4231 detected - is it? - * - * Verify that setting I0 doesn't change I16. - */ - DDB (printk ("ad1848_detect() - step H\n")); - ad_write (devc, 16, 0); /* Set I16 to known value */ - - ad_write (devc, 0, 0x45); - if ((tmp1 = ad_read (devc, 16)) != 0x45) /* No change -> CS4231? */ - { - - ad_write (devc, 0, 0xaa); - if ((tmp1 = ad_read (devc, 16)) == 0xaa) /* Rotten bits? */ - { - DDB (printk ("ad1848 detect error - step H(%x)\n", tmp1)); - return 0; - } - - /* - * Verify that some bits of I25 are read only. - */ - - DDB (printk ("ad1848_detect() - step I\n")); - tmp1 = ad_read (devc, 25); /* Original bits */ - ad_write (devc, 25, ~tmp1); /* Invert all bits */ - if ((ad_read (devc, 25) & 0xe7) == (tmp1 & 0xe7)) - { - int id; - - /* - * It's at least CS4231 - */ - devc->chip_name = "CS4231"; - - devc->model = MD_4231; - - /* - * It could be an AD1845 or CS4231A as well. - * CS4231 and AD1845 report the same revision info in I25 - * while the CS4231A reports different. - */ - - DDB (printk ("ad1848_detect() - step I\n")); - id = ad_read (devc, 25) & 0xe7; - - switch (id) - { + tmp = old = ad_read(devc, 9); - case 0xa0: - devc->chip_name = "CS4231A"; - devc->model = MD_4231A; - break; - - case 0xa2: - devc->chip_name = "CS4232"; - devc->model = MD_4232; - break; - - case 0xb2: - devc->chip_name = "CS4232A"; - devc->model = MD_4232; - break; - - case 0x80: - { - /* - * It must be a CS4231 or AD1845. The register I23 of - * CS4231 is undefined and it appears to be read only. - * AD1845 uses I23 for setting sample rate. Assume - * the chip is AD1845 if I23 is changeable. - */ - - unsigned char tmp = ad_read (devc, 23); - - ad_write (devc, 23, ~tmp); - if (ad_read (devc, 23) != tmp) /* AD1845 ? */ - { - devc->chip_name = "AD1845"; - devc->model = MD_1845; - } - else if (interwave) - { - devc->model = MD_IWAVE; - devc->chip_name = "IWave"; - } - - ad_write (devc, 23, tmp); /* Restore */ - } - break; - - default: /* Assume CS4231 or OPTi 82C930 */ - if (optiC930) - { - devc->chip_name = "82C930"; - devc->model = MD_C930; - } + if (portc->open_mode & OPEN_READ) + { + if (state & PCM_ENABLE_INPUT) + tmp |= 0x02; else - { - devc->model = MD_4231; - } - - } - } - ad_write (devc, 25, tmp1); /* Restore bits */ - - DDB (printk ("ad1848_detect() - step K\n")); + tmp &= ~0x02; + } + if (portc->open_mode & OPEN_WRITE) + { + if (state & PCM_ENABLE_OUTPUT) + tmp |= 0x01; + else + tmp &= ~0x01; } - } + /* ad_mute(devc); */ + if (tmp != old) + { + ad_write(devc, 9, tmp); + ad_unmute(devc); + } + restore_flags(flags); +} - DDB (printk ("ad1848_detect() - step L\n")); - if (ad_flags) - { - if (devc->model != MD_1848) - *ad_flags |= AD_F_CS4231; - } +static void ad1848_init_hw(ad1848_info * devc) +{ + int i; - DDB (printk ("ad1848_detect() - Detected OK\n")); + /* + * Initial values for the indirect registers of CS4248/AD1848. + */ + static int init_values[] = + { + 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00, - if (devc->model == MD_1848 && ad1847_flag) - devc->chip_name = "AD1847"; + /* Positions 16 to 31 just for CS4231/2 and ad1845 */ + 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; - for (i = 0; i < 16; i++) - ad_write (devc, i, init_values[i]); - ad_mute (devc); /* Initialize some variables */ - ad_unmute (devc); /* Leave it unmuted now */ + for (i = 0; i < 16; i++) + ad_write(devc, i, init_values[i]); - if (devc->model > MD_1848) - { - ad_write (devc, 12, ad_read (devc, 12) | 0x40); /* Mode2 = enabled */ - if (devc->model == MD_IWAVE) - ad_write (devc, 12, 0x6c); /* Select codec mode 3 */ + ad_mute(devc); /* Initialize some variables */ + ad_unmute(devc); /* Leave it unmuted now */ - for (i = 16; i < 32; i++) - ad_write (devc, i, init_values[i]); - } + if (devc->model > MD_1848) + { + ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ - return 1; -} + if (devc->model == MD_IWAVE) + ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ -void -ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *osp) -{ - /* - * NOTE! If irq < 0, there is another driver which has allocated the IRQ - * so that this driver doesn't need to allocate/deallocate it. - * The actually used IRQ is ABS(irq). - */ + for (i = 16; i < 32; i++) + ad_write(devc, i, init_values[i]); + if (devc->model == MD_IWAVE) + ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ + } + if (devc->model > MD_1848) + { + if (devc->audio_flags & DMA_DUPLEX) + ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */ + else + ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ + + if (devc->model == MD_1845) + ad_write(devc, 27, ad_read(devc, 27) | 0x08); /* Alternate freq select enabled */ + + if (devc->model == MD_IWAVE) + { /* Some magic Interwave specific initialization */ + ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ + ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ + ad_write(devc, 17, 0xc2); /* Alternate feature enable */ + } + } + else + { + devc->audio_flags &= ~DMA_DUPLEX; + ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ + } - int my_dev; - char dev_name[100]; + outb((0), io_Status(devc)); /* Clear pending interrupts */ - ad1848_info *devc = &dev_info[nr_ad1848_devs]; + /* + * Toggle the MCE bit. It completes the initialization phase. + */ - int audio_flags = DMA_AUTOMODE; + ad_enter_MCE(devc); /* In case the bit was off */ + ad_leave_MCE(devc); + ad1848_mixer_reset(devc); +} + +int ad1848_detect(int io_base, int *ad_flags, int *osp) +{ + unsigned char tmp; + ad1848_info *devc = &adev_info[nr_ad1848_devs]; + unsigned char tmp1 = 0xff, tmp2 = 0xff; + int optiC930 = 0; /* OPTi 82C930 flag */ + int interwave = 0; + int ad1847_flag = 0; + int cs4248_flag = 0; - request_region (devc->base, 4, devc->chip_name); + int i; - devc->irq = (irq > 0) ? irq : 0; - devc->opened = 0; - devc->timer_ticks = 0; - devc->osp = osp; + DDB(printk("ad1848_detect(%x)\n", io_base)); - if (devc->model > MD_1848) - { - if (dma_capture == dma_playback || dma_capture == -1 || dma_playback == -1) + if (ad_flags) { - ad_write (devc, 9, ad_read (devc, 9) | 0x04); /* Single DMA mode */ - audio_flags &= ~DMA_DUPLEX; + if (*ad_flags == 0x12345678) + { + interwave = 1; + *ad_flags = 0; + } + if (*ad_flags == 0x12345677) + { + cs4248_flag = 1; + *ad_flags = 0; + } } - else + if (nr_ad1848_devs >= MAX_AUDIO_DEV) { - ad_write (devc, 9, ad_read (devc, 9) & ~0x04); /* Dual DMA mode */ - audio_flags |= DMA_DUPLEX; + printk(KERN_ERR "ad1848 - Too many audio devices\n"); + return 0; } - - if (devc->model == MD_1845) - ad_write (devc, 27, ad_read (devc, 27) | 0x08); /* Alternate freq select enabled */ - - if (devc->model == MD_IWAVE) - { /* Some magic Interwave specific initialization */ - ad_write (devc, 12, 0x6c); /* Select codec mode 3 */ - ad_write (devc, 17, 0xc2); /* Alternate feature enable */ + if (check_region(io_base, 4)) + { + printk(KERN_ERR "ad1848.c: Port %x not free.\n", io_base); + return 0; + } + devc->base = io_base; + devc->irq_ok = 0; + devc->timer_running = 0; + devc->MCE_bit = 0x40; + devc->irq = 0; + devc->open_mode = 0; + devc->chip_name = devc->name = "AD1848"; + devc->model = MD_1848; /* AD1848 or CS4248 */ + devc->levels = NULL; + devc->debug_flag = 0; + + /* + * Check that the I/O address is in use. + * + * The bit 0x80 of the base I/O port is known to be 0 after the + * chip has performed its power on initialization. Just assume + * this has happened before the OS is starting. + * + * If the I/O address is unused, it typically returns 0xff. + */ + + if (inb(devc->base) == 0xff) + { + DDB(printk("ad1848_detect: The base I/O address appears to be dead\n")); } - } - else - { - audio_flags &= ~DMA_DUPLEX; - ad_write (devc, 9, ad_read (devc, 9) | 0x04); /* Single DMA mode */ - } - outb (0, io_Status (devc)); /* Clear pending interrupts */ + /* + * Wait for the device to stop initialization + */ + + DDB(printk("ad1848_detect() - step 0\n")); - if (name != NULL && name[0] != 0) - sprintf (dev_name, - "%s (%s)", name, devc->chip_name); - else - sprintf (dev_name, - "Generic audio codec (%s)", devc->chip_name); + for (i = 0; i < 10000000; i++) + { + unsigned char x = inb(devc->base); - conf_printf2 (dev_name, - devc->base, devc->irq, dma_playback, dma_capture); + if (x == 0xff || !(x & 0x80)) + break; + } - if (devc->model == MD_1848) - audio_flags |= DMA_HARDSTOP; + DDB(printk("ad1848_detect() - step A\n")); - if ((my_dev = sound_install_audiodrv (AUDIO_DRIVER_VERSION, - dev_name, - &ad1848_audio_driver, - sizeof (struct audio_driver), - audio_flags, - ad_format_mask[devc->model], - devc, - dma_playback, - dma_capture)) < 0) - { - return; - } + if (inb(devc->base) == 0x80) /* Not ready. Let's wait */ + ad_leave_MCE(devc); - if (irq > 0) - { - irq2dev[irq] = devc->dev_no = my_dev; - if (snd_set_irq_handler (devc->irq, ad1848_interrupt, - "SoundPort", - devc->osp) < 0) + if ((inb(devc->base) & 0x80) != 0x00) /* Not a AD1848 */ { - printk ("ad1848: IRQ in use\n"); + DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base))); + return 0; } - - if (devc->model != MD_1848) + + /* + * Test if it's possible to change contents of the indirect registers. + * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only + * so try to avoid using it. + */ + + DDB(printk("ad1848_detect() - step B\n")); + ad_write(devc, 0, 0xaa); + ad_write(devc, 1, 0x45); /* 0x55 with bit 0x10 clear */ + + if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45) { - int x; - unsigned char tmp = ad_read (devc, 16); + if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */ + ad1847_flag = 1; + else + { + DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2)); + return 0; + } + } + DDB(printk("ad1848_detect() - step C\n")); + ad_write(devc, 0, 0x45); + ad_write(devc, 1, 0xaa); - devc->timer_ticks = 0; + if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa) + { + if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */ + ad1847_flag = 1; + else + { + DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2)); + return 0; + } + } - ad_write (devc, 21, 0x00); /* Timer MSB */ - ad_write (devc, 20, 0x10); /* Timer LSB */ + /* + * The indirect register I12 has some read only bits. Let's + * try to change them. + */ - ad_write (devc, 16, tmp | 0x40); /* Enable timer */ - for (x = 0; x < 100000 && devc->timer_ticks == 0; x++); - ad_write (devc, 16, tmp & ~0x40); /* Disable timer */ + DDB(printk("ad1848_detect() - step D\n")); + tmp = ad_read(devc, 12); + ad_write(devc, 12, (~tmp) & 0x0f); - if (devc->timer_ticks == 0) - printk ("ad1848: Interrupt test failed (IRQ%d)\n", devc->irq); - else - devc->irq_ok = 1; + if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f)) + { + DDB(printk("ad1848 detect error - step D (%x)\n", tmp1)); + return 0; + } + + /* + * NOTE! Last 4 bits of the reg I12 tell the chip revision. + * 0x01=RevB and 0x0A=RevC. + */ + + /* + * The original AD1848/CS4248 has just 15 indirect registers. This means + * that I0 and I16 should return the same value (etc.). + * However this doesn't work with CS4248. Actually it seems to be impossible + * to detect if the chip is a CS4231 or CS4248. + * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails + * with CS4231. + */ + + /* + * OPTi 82C930 has mode2 control bit in another place. This test will fail + * with it. Accept this situation as a possible indication of this chip. + */ + + DDB(printk("ad1848_detect() - step F\n")); + ad_write(devc, 12, 0); /* Mode2=disabled */ + + for (i = 0; i < 16; i++) + { + if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) + { + DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2)); + if (!ad1847_flag) + optiC930 = 1; + break; + } } - else - devc->irq_ok = 1; /* Couldn't test. assume it's OK */ - } - else if (irq < 0) - irq2dev[-irq] = devc->dev_no = my_dev; - nr_ad1848_devs++; -#ifdef CONFIG_SEQUENCER - if (devc->model != MD_1848 && devc->model != MD_1845 && devc->irq_ok) - ad1848_tmr_install (my_dev); -#endif + /* + * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40). + * The bit 0x80 is always 1 in CS4248 and CS4231. + */ - if (!share_dma) - { - if (sound_alloc_dma (dma_playback, "Sound System")) - printk ("ad1848.c: Can't allocate DMA%d\n", dma_playback); - - if (dma_capture != dma_playback) - if (sound_alloc_dma (dma_capture, "Sound System (capture)")) - printk ("ad1848.c: Can't allocate DMA%d\n", dma_capture); - } - - /* - * Toggle the MCE bit. It completes the initialization phase. - */ - - ad_enter_MCE (devc); /* In case the bit was off */ - ad_leave_MCE (devc); - ad1848_mixer_reset (devc); - - if (sound_install_mixer (MIXER_DRIVER_VERSION, - dev_name, - &ad1848_mixer_operations, - sizeof (struct mixer_operations), - devc) >= 0) - { - audio_devs[my_dev]->mixer_dev = num_mixers - 1; - } -} + DDB(printk("ad1848_detect() - step G\n")); -void -ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma) -{ - int i, dev = 0; - ad1848_info *devc = NULL; + if (ad_flags && *ad_flags == 400) + *ad_flags = 0; + else + ad_write(devc, 12, 0x40); /* Set mode2, clear 0x80 */ - for (i = 0; devc == NULL && i < nr_ad1848_devs; i++) - if (dev_info[i].base == io_base) - { - devc = &dev_info[i]; - dev = devc->dev_no; - } - if (devc != NULL) - { - release_region (devc->base, 4); + if (ad_flags) + *ad_flags = 0; - if (!share_dma) + tmp1 = ad_read(devc, 12); + if (tmp1 & 0x80) { - if (irq > 0) - snd_release_irq (devc->irq); + if (ad_flags) + *ad_flags |= AD_F_CS4248; - sound_free_dma (audio_devs[dev]->dmachan1); + devc->chip_name = "CS4248"; /* Our best knowledge just now */ + } + if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40)) + { + /* + * CS4231 detected - is it? + * + * Verify that setting I0 doesn't change I16. + */ + + DDB(printk("ad1848_detect() - step H\n")); + ad_write(devc, 16, 0); /* Set I16 to known value */ + + ad_write(devc, 0, 0x45); + if ((tmp1 = ad_read(devc, 16)) != 0x45) /* No change -> CS4231? */ + { + ad_write(devc, 0, 0xaa); + if ((tmp1 = ad_read(devc, 16)) == 0xaa) /* Rotten bits? */ + { + DDB(printk("ad1848 detect error - step H(%x)\n", tmp1)); + return 0; + } + + /* + * Verify that some bits of I25 are read only. + */ + + DDB(printk("ad1848_detect() - step I\n")); + tmp1 = ad_read(devc, 25); /* Original bits */ + ad_write(devc, 25, ~tmp1); /* Invert all bits */ + if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7)) + { + int id, full_id; + + /* + * It's at least CS4231 + */ + + devc->chip_name = "CS4231"; + devc->model = MD_4231; + + /* + * It could be an AD1845 or CS4231A as well. + * CS4231 and AD1845 report the same revision info in I25 + * while the CS4231A reports different. + */ - if (audio_devs[dev]->dmachan2 != audio_devs[dev]->dmachan1) - sound_free_dma (audio_devs[dev]->dmachan2); + id = ad_read(devc, 25) & 0xe7; + full_id = ad_read(devc, 25); + if (id == 0x80) /* Device busy??? */ + id = ad_read(devc, 25) & 0xe7; + if (id == 0x80) /* Device still busy??? */ + id = ad_read(devc, 25) & 0xe7; + DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25))); + + switch (id) + { + + case 0xa0: + devc->chip_name = "CS4231A"; + devc->model = MD_4231A; + break; + + case 0xa2: + devc->chip_name = "CS4232"; + devc->model = MD_4232; + break; + + case 0xb2: + devc->chip_name = "CS4232A"; + devc->model = MD_4232; + break; + + case 0x03: + case 0x83: + devc->chip_name = "CS4236"; + devc->model = MD_4232; + break; + + case 0x41: + devc->chip_name = "CS4236B"; + devc->model = MD_4232; + break; + + case 0x80: + { + /* + * It must be a CS4231 or AD1845. The register I23 of + * CS4231 is undefined and it appears to be read only. + * AD1845 uses I23 for setting sample rate. Assume + * the chip is AD1845 if I23 is changeable. + */ + + unsigned char tmp = ad_read(devc, 23); + ad_write(devc, 23, ~tmp); + + if (interwave) + { + devc->model = MD_IWAVE; + devc->chip_name = "IWave"; + } + else if (ad_read(devc, 23) != tmp) /* AD1845 ? */ + { + devc->chip_name = "AD1845"; + devc->model = MD_1845; + } + else if (cs4248_flag) + { + if (ad_flags) + *ad_flags |= AD_F_CS4248; + devc->chip_name = "CS4248"; + devc->model = MD_1848; + ad_write(devc, 12, ad_read(devc, 12) & ~0x40); /* Mode2 off */ + } + ad_write(devc, 23, tmp); /* Restore */ + } + break; + + default: /* Assume CS4231 or OPTi 82C930 */ + DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7)); + if (optiC930) + { + devc->chip_name = "82C930"; + devc->model = MD_C930; + } + else + { + devc->model = MD_4231; + } + } + } + ad_write(devc, 25, tmp1); /* Restore bits */ + + DDB(printk("ad1848_detect() - step K\n")); + } + } + DDB(printk("ad1848_detect() - step L\n")); + if (ad_flags) + { + if (devc->model != MD_1848) + *ad_flags |= AD_F_CS4231; } - } - else - printk ("ad1848: Can't find device to be unloaded. Base=%x\n", - io_base); + DDB(printk("ad1848_detect() - Detected OK\n")); + + if (devc->model == MD_1848 && ad1847_flag) + devc->chip_name = "AD1847"; + + + return 1; } -void -ad1848_interrupt (int irq, void *dev_id, struct pt_regs *dummy) +int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *osp) { - unsigned char status; - ad1848_info *devc; - int dev; - int alt_stat = 0xff; + /* + * NOTE! If irq < 0, there is another driver which has allocated the IRQ + * so that this driver doesn't need to allocate/deallocate it. + * The actually used IRQ is ABS(irq). + */ + + + int my_dev; + char dev_name[100]; + int e; + + ad1848_info *devc = &adev_info[nr_ad1848_devs]; + + ad1848_port_info *portc = NULL; + + devc->irq = (irq > 0) ? irq : 0; + devc->open_mode = 0; + devc->timer_ticks = 0; + devc->dma1 = dma_playback; + devc->dma2 = dma_capture; + devc->audio_flags = DMA_AUTOMODE; + devc->playback_dev = devc->record_dev = 0; + if (name != NULL) + devc->name = name; + + if (name != NULL && name[0] != 0) + sprintf(dev_name, + "%s (%s)", name, devc->chip_name); + else + sprintf(dev_name, + "Generic audio codec (%s)", devc->chip_name); + + request_region(devc->base, 4, devc->name); - if (irq < 0 || irq > 15) - { - dev = -1; - } - else - dev = irq2dev[irq]; + conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture); - if (dev < 0 || dev >= num_audiodevs) - { - for (irq = 0; irq < 17; irq++) - if (irq2dev[irq] != -1) - break; + if (devc->model == MD_1848 || devc->model == MD_C930) + devc->audio_flags |= DMA_HARDSTOP; - if (irq > 15) + if (devc->model > MD_1848) { - /* printk ("ad1848.c: Bogus interrupt %d\n", irq); */ - return; + if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1) + devc->audio_flags &= ~DMA_DUPLEX; + else + devc->audio_flags |= DMA_DUPLEX; } - dev = irq2dev[irq]; - devc = (ad1848_info *) audio_devs[dev]->devc; - } - else - devc = (ad1848_info *) audio_devs[dev]->devc; + portc = (ad1848_port_info *) kmalloc(sizeof(ad1848_port_info), GFP_KERNEL); + if(portc==NULL) + return -1; + + if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + dev_name, + &ad1848_audio_driver, + sizeof(struct audio_driver), + devc->audio_flags, + ad_format_mask[devc->model], + devc, + dma_playback, + dma_capture)) < 0) + { + kfree(portc); + portc=NULL; + return -1; + } + + audio_devs[my_dev]->portc = portc; + audio_devs[my_dev]->mixer_dev = -1; + memset((char *) portc, 0, sizeof(*portc)); - status = inb (io_Status (devc)); + nr_ad1848_devs++; - if (status == 0x80) - printk ("ad1848_interrupt: Why?\n"); + ad1848_init_hw(devc); - if (status & 0x01) - { + if (irq > 0) + { + devc->dev_no = my_dev; + if (request_irq(devc->irq, adintr, 0, devc->name, (void *)my_dev) < 0) + { + printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n"); + /* Don't free it either then.. */ + devc->irq = 0; + } + if (devc->model != MD_1848 && devc->model != MD_C930) + { +#ifndef __SMP__ + int x; + unsigned char tmp = ad_read(devc, 16); +#endif + + devc->timer_ticks = 0; + + ad_write(devc, 21, 0x00); /* Timer MSB */ + ad_write(devc, 20, 0x10); /* Timer LSB */ +#ifndef __SMP__ + ad_write(devc, 16, tmp | 0x40); /* Enable timer */ + for (x = 0; x < 100000 && devc->timer_ticks == 0; x++); + ad_write(devc, 16, tmp & ~0x40); /* Disable timer */ + + if (devc->timer_ticks == 0) + printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", devc->irq); + else + { + DDB(printk("Interrupt test OK\n")); + devc->irq_ok = 1; + } +#else + devc->irq_ok=1; +#endif + } + else + devc->irq_ok = 1; /* Couldn't test. assume it's OK */ + } else if (irq < 0) + irq2dev[-irq] = devc->dev_no = my_dev; + +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) + if (devc->model != MD_1848 && + devc->model != MD_C930 && devc->irq_ok) + ad1848_tmr_install(my_dev); +#endif - if (devc->model != MD_1848) - alt_stat = ad_read (devc, 24); + if (!share_dma) + { + if (sound_alloc_dma(dma_playback, devc->name)) + printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_playback); - if (devc->opened && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20) + if (dma_capture != dma_playback) + if (sound_alloc_dma(dma_capture, devc->name)) + printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_capture); + } + if ((e = sound_install_mixer(MIXER_DRIVER_VERSION, + dev_name, + &ad1848_mixer_operations, + sizeof(struct mixer_operations), + devc)) >= 0) { - DMAbuf_inputintr (dev); + audio_devs[my_dev]->mixer_dev = e; } + return my_dev; +} + +int ad1848_control(int cmd, int arg) +{ + ad1848_info *devc; + + if (nr_ad1848_devs < 1) + return -ENODEV; - if (devc->opened && devc->audio_mode & PCM_ENABLE_OUTPUT && - alt_stat & 0x10) + devc = &adev_info[nr_ad1848_devs - 1]; + + switch (cmd) { - DMAbuf_outputintr (dev, 1); + case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */ + if (devc->model != MD_1845) + return -EINVAL; + ad_enter_MCE(devc); + ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5)); + ad_leave_MCE(devc); + break; + + case AD1848_MIXER_REROUTE: + { + int o = (arg >> 8) & 0xff; + int n = arg & 0xff; + + if (o < 0 || o >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + + if (!(devc->supported_devices & (1 << o)) && + !(devc->supported_rec_devices & (1 << o))) + return -EINVAL; + + if (n == SOUND_MIXER_NONE) + { /* Just hide this control */ + ad1848_mixer_set(devc, o, 0); /* Shut up it */ + devc->supported_devices &= ~(1 << o); + devc->supported_rec_devices &= ~(1 << o); + break; + } + + /* Make the mixer control identified by o to appear as n */ + if (n < 0 || n >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + + devc->mixer_reroute[n] = o; /* Rename the control */ + if (devc->supported_devices & (1 << o)) + devc->supported_devices |= (1 << n); + if (devc->supported_rec_devices & (1 << o)) + devc->supported_rec_devices |= (1 << n); + + devc->supported_devices &= ~(1 << o); + devc->supported_rec_devices &= ~(1 << o); + } + break; } + return 0; +} - if (devc->model != MD_1848 && alt_stat & 0x40) /* Timer interrupt */ +void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma) +{ + int i, mixer, dev = 0; + ad1848_info *devc = NULL; + + for (i = 0; devc == NULL && i < nr_ad1848_devs; i++) { - devc->timer_ticks++; -#ifdef CONFIG_SEQUENCER - if (timer_installed == dev && devc->timer_running) - sound_timer_interrupt (); -#endif + if (adev_info[i].base == io_base) + { + devc = &adev_info[i]; + dev = devc->dev_no; + } } - } + + if (devc != NULL) + { + if(audio_devs[dev]->portc!=NULL) + kfree(audio_devs[dev]->portc); + release_region(devc->base, 4); - if (devc->model != MD_1848) - ad_write (devc, 24, ad_read (devc, 24) & ~alt_stat); /* Selective ack */ - else - outb (0, io_Status (devc)); /* Clear interrupt status */ -} + if (!share_dma) + { + if (irq > 0) + free_irq(devc->irq, NULL); -/* - * Some extra code for the MS Sound System - */ + sound_free_dma(audio_devs[dev]->dmap_out->dma); + + if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma) + sound_free_dma(audio_devs[dev]->dmap_in->dma); + } + mixer = audio_devs[devc->dev_no]->mixer_dev; + if(mixer>=0) + sound_unload_mixerdev(mixer); + } + else + printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base); +} -void -check_opl3 (int base, struct address_info *hw_config) +void adintr(int irq, void *dev_id, struct pt_regs *dummy) { + unsigned char status; + ad1848_info *devc; + int dev; + int alt_stat = 0xff; + unsigned char c930_stat = 0; + int cnt = 0; -#ifdef CONFIG_YM3812 - if (check_region (base, 4)) - { - printk ("\n\nopl3.c: I/O port %x already in use\n\n", base); - return; - } + dev = (int)dev_id; + devc = (ad1848_info *) audio_devs[dev]->devc; - if (!opl3_detect (base, hw_config->osp)) - return; +interrupt_again: /* Jump back here if int status doesn't reset */ - opl3_init (base, hw_config->osp); - request_region (base, 4, "OPL3/OPL2"); -#endif -} + status = inb(io_Status(devc)); -#ifdef DESKPROXL -/* - * Very experimental initialization sequence for the integrated sound system - * of Compaq Deskpro XL. Will be moved somewhere else in future. - */ + if (status == 0x80) + printk(KERN_DEBUG "adintr: Why?\n"); + if (devc->model == MD_1848) + outb((0), io_Status(devc)); /* Clear interrupt status */ -static int -init_deskpro (struct address_info *hw_config) -{ - unsigned char tmp; + if (status & 0x01) + { + if (devc->model == MD_C930) + { /* 82C930 has interrupt status register in MAD16 register MC11 */ + unsigned long flags; - if ((tmp = inb (0xc44)) == 0xff) - { - DDB (printk ("init_deskpro: Dead port 0xc44\n")); - return 0; - } + save_flags(flags); + cli(); - outb (tmp | 0x04, 0xc44); /* Select bank 1 */ - if (inb (0xc44) != 0x04) - { - DDB (printk ("init_deskpro: Invalid bank1 signature in port 0xc44\n")); - return 0; - } + /* 0xe0e is C930 address port + * 0xe0f is C930 data port + */ + outb(11, 0xe0e); + c930_stat = inb(0xe0f); + outb((~c930_stat), 0xe0f); + restore_flags(flags); + + alt_stat = (c930_stat << 2) & 0x30; + } + else if (devc->model != MD_1848) + { + alt_stat = ad_read(devc, 24); + ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */ + } + + if (devc->open_mode & OPEN_READ && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20) + { + DMAbuf_inputintr(devc->record_dev); + } + if (devc->open_mode & OPEN_WRITE && devc->audio_mode & PCM_ENABLE_OUTPUT && + alt_stat & 0x10) + { + DMAbuf_outputintr(devc->playback_dev, 1); + } + if (devc->model != MD_1848 && alt_stat & 0x40) /* Timer interrupt */ + { + devc->timer_ticks++; +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) + if (timer_installed == dev && devc->timer_running) + sound_timer_interrupt(); +#endif + } + } /* - * OK. It looks like a Deskpro so let's proceed. + * Sometimes playback or capture interrupts occur while a timer interrupt + * is being handled. The interrupt will not be retriggered if we don't + * handle it now. Check if an interrupt is still pending and restart + * the handler in this case. */ + if (inb(io_Status(devc)) & 0x01 && cnt++ < 4) + { + goto interrupt_again; + } +} /* - * I/O port 0xc44 Audio configuration register. - * - * bits 0xc0: Audio revision bits - * 0x00 = Compaq Business Audio - * 0x40 = MS Sound System Compatible (reset default) - * 0x80 = Reserved - * 0xc0 = Reserved - * bit 0x20: No Wait State Enable - * 0x00 = Disabled (reset default, DMA mode) - * 0x20 = Enabled (programmed I/O mode) - * bit 0x10: MS Sound System Decode Enable - * 0x00 = Decoding disabled (reset default) - * 0x10 = Decoding enabled - * bit 0x08: FM Synthesis Decode Enable - * 0x00 = Decoding Disabled (reset default) - * 0x08 = Decoding enabled - * bit 0x04 Bank select - * 0x00 = Bank 0 - * 0x04 = Bank 1 - * bits 0x03 MSS Base address - * 0x00 = 0x530 (reset default) - * 0x01 = 0x604 - * 0x02 = 0xf40 - * 0x03 = 0xe80 + * Experimental initialization sequence for the integrated sound system + * of Compaq Deskpro XL. */ +static int init_deskpro(struct address_info *hw_config) +{ + unsigned char tmp; + + if ((tmp = inb(0xc44)) == 0xff) + { + DDB(printk("init_deskpro: Dead port 0xc44\n")); + return 0; + } + outb((tmp | 0x04), 0xc44); /* Select bank 1 */ + if (inb(0xc44) != 0x04) + { + DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n")); + return 0; + } + /* + * OK. It looks like a Deskpro so let's proceed. + */ + + /* + * I/O port 0xc44 Audio configuration register. + * + * bits 0xc0: Audio revision bits + * 0x00 = Compaq Business Audio + * 0x40 = MS Sound System Compatible (reset default) + * 0x80 = Reserved + * 0xc0 = Reserved + * bit 0x20: No Wait State Enable + * 0x00 = Disabled (reset default, DMA mode) + * 0x20 = Enabled (programmed I/O mode) + * bit 0x10: MS Sound System Decode Enable + * 0x00 = Decoding disabled (reset default) + * 0x10 = Decoding enabled + * bit 0x08: FM Synthesis Decode Enable + * 0x00 = Decoding Disabled (reset default) + * 0x08 = Decoding enabled + * bit 0x04 Bank select + * 0x00 = Bank 0 + * 0x04 = Bank 1 + * bits 0x03 MSS Base address + * 0x00 = 0x530 (reset default) + * 0x01 = 0x604 + * 0x02 = 0xf40 + * 0x03 = 0xe80 + */ + #ifdef DEBUGXL - /* Debug printing */ - printk ("Port 0xc44 (before): "); - outb (tmp & ~0x04, 0xc44); - printk ("%02x ", inb (0xc44)); - outb (tmp | 0x04, 0xc44); - printk ("%02x\n", inb (0xc44)); + /* Debug printing */ + printk("Port 0xc44 (before): "); + outb((tmp & ~0x04), 0xc44); + printk("%02x ", inb(0xc44)); + outb((tmp | 0x04), 0xc44); + printk("%02x\n", inb(0xc44)); #endif - /* Set bank 1 of the register */ - tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */ - - switch (hw_config->io_base) - { - case 0x530: - tmp |= 0x00; - break; - case 0x604: - tmp |= 0x01; - break; - case 0xf40: - tmp |= 0x02; - break; - case 0xe80: - tmp |= 0x03; - break; - default: - DDB (printk ("init_deskpro: Invalid MSS port %x\n", - hw_config->io_base)); - return 0; - } - outb (tmp & ~0x04, 0xc44); /* Write to bank=0 */ + /* Set bank 1 of the register */ + tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */ + + switch (hw_config->io_base) + { + case 0x530: + tmp |= 0x00; + break; + case 0x604: + tmp |= 0x01; + break; + case 0xf40: + tmp |= 0x02; + break; + case 0xe80: + tmp |= 0x03; + break; + default: + DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base)); + return 0; + } + outb((tmp & ~0x04), 0xc44); /* Write to bank=0 */ #ifdef DEBUGXL - /* Debug printing */ - printk ("Port 0xc44 (after): "); - outb (tmp & ~0x04, 0xc44); /* Select bank=0 */ - printk ("%02x ", inb (0xc44)); - outb (tmp | 0x04, 0xc44); /* Select bank=1 */ - printk ("%02x\n", inb (0xc44)); + /* Debug printing */ + printk("Port 0xc44 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc44)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc44)); #endif -/* - * I/O port 0xc45 FM Address Decode/MSS ID Register. - * - * bank=0, bits 0xfe: FM synthesis Decode Compare bits 7:1 (default=0x88) - * bank=0, bit 0x01: SBIC Power Control Bit - * 0x00 = Powered up - * 0x01 = Powered down - * bank=1, bits 0xfc: MSS ID (default=0x40) - */ + /* + * I/O port 0xc45 FM Address Decode/MSS ID Register. + * + * bank=0, bits 0xfe: FM synthesis Decode Compare bits 7:1 (default=0x88) + * bank=0, bit 0x01: SBIC Power Control Bit + * 0x00 = Powered up + * 0x01 = Powered down + * bank=1, bits 0xfc: MSS ID (default=0x40) + */ #ifdef DEBUGXL - /* Debug printing */ - printk ("Port 0xc45 (before): "); - outb (tmp & ~0x04, 0xc44); /* Select bank=0 */ - printk ("%02x ", inb (0xc45)); - outb (tmp | 0x04, 0xc44); /* Select bank=1 */ - printk ("%02x\n", inb (0xc45)); + /* Debug printing */ + printk("Port 0xc45 (before): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc45)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc45)); #endif - outb (tmp & ~0x04, 0xc44); /* Select bank=0 */ - outb (0x88, 0xc45); /* FM base 7:0 = 0x88 */ - outb (tmp | 0x04, 0xc44); /* Select bank=1 */ - outb (0x10, 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */ + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + outb((0x88), 0xc45); /* FM base 7:0 = 0x88 */ + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + outb((0x10), 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */ #ifdef DEBUGXL - /* Debug printing */ - printk ("Port 0xc45 (after): "); - outb (tmp & ~0x04, 0xc44); /* Select bank=0 */ - printk ("%02x ", inb (0xc45)); - outb (tmp | 0x04, 0xc44); /* Select bank=1 */ - printk ("%02x\n", inb (0xc45)); + /* Debug printing */ + printk("Port 0xc45 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc45)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc45)); #endif -/* - * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register. - * - * bank=0, bits 0xff: FM synthesis Decode Compare bits 15:8 (default=0x03) - * bank=1, bits 0xff: Audio addressing ASIC id - */ + /* + * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register. + * + * bank=0, bits 0xff: FM synthesis Decode Compare bits 15:8 (default=0x03) + * bank=1, bits 0xff: Audio addressing ASIC id + */ #ifdef DEBUGXL - /* Debug printing */ - printk ("Port 0xc46 (before): "); - outb (tmp & ~0x04, 0xc44); /* Select bank=0 */ - printk ("%02x ", inb (0xc46)); - outb (tmp | 0x04, 0xc44); /* Select bank=1 */ - printk ("%02x\n", inb (0xc46)); + /* Debug printing */ + printk("Port 0xc46 (before): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc46)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc46)); #endif - outb (tmp & ~0x04, 0xc44); /* Select bank=0 */ - outb (0x03, 0xc46); /* FM base 15:8 = 0x03 */ - outb (tmp | 0x04, 0xc44); /* Select bank=1 */ - outb (0x11, 0xc46); /* ASIC ID = 0x11 */ + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + outb((0x03), 0xc46); /* FM base 15:8 = 0x03 */ + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + outb((0x11), 0xc46); /* ASIC ID = 0x11 */ #ifdef DEBUGXL - /* Debug printing */ - printk ("Port 0xc46 (after): "); - outb (tmp & ~0x04, 0xc44); /* Select bank=0 */ - printk ("%02x ", inb (0xc46)); - outb (tmp | 0x04, 0xc44); /* Select bank=1 */ - printk ("%02x\n", inb (0xc46)); + /* Debug printing */ + printk("Port 0xc46 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc46)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc46)); #endif -/* - * I/O port 0xc47 FM Address Decode Register. - * - * bank=0, bits 0xff: Decode enable selection for various FM address bits - * bank=1, bits 0xff: Reserved - */ + /* + * I/O port 0xc47 FM Address Decode Register. + * + * bank=0, bits 0xff: Decode enable selection for various FM address bits + * bank=1, bits 0xff: Reserved + */ #ifdef DEBUGXL - /* Debug printing */ - printk ("Port 0xc47 (before): "); - outb (tmp & ~0x04, 0xc44); /* Select bank=0 */ - printk ("%02x ", inb (0xc47)); - outb (tmp | 0x04, 0xc44); /* Select bank=1 */ - printk ("%02x\n", inb (0xc47)); + /* Debug printing */ + printk("Port 0xc47 (before): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc47)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc47)); #endif - outb (tmp & ~0x04, 0xc44); /* Select bank=0 */ - outb (0x7c, 0xc47); /* FM decode enable bits = 0x7c */ - outb (tmp | 0x04, 0xc44); /* Select bank=1 */ - outb (0x00, 0xc47); /* Reserved bank1 = 0x00 */ + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + outb((0x7c), 0xc47); /* FM decode enable bits = 0x7c */ + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + outb((0x00), 0xc47); /* Reserved bank1 = 0x00 */ #ifdef DEBUGXL - /* Debug printing */ - printk ("Port 0xc47 (after): "); - outb (tmp & ~0x04, 0xc44); /* Select bank=0 */ - printk ("%02x ", inb (0xc47)); - outb (tmp | 0x04, 0xc44); /* Select bank=1 */ - printk ("%02x\n", inb (0xc47)); + /* Debug printing */ + printk("Port 0xc47 (after): "); + outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ + printk("%02x ", inb(0xc47)); + outb((tmp | 0x04), 0xc44); /* Select bank=1 */ + printk("%02x\n", inb(0xc47)); #endif -/* - * I/O port 0xc6f = Audio Disable Function Register - */ + /* + * I/O port 0xc6f = Audio Disable Function Register + */ #ifdef DEBUGXL - printk ("Port 0xc6f (before) = %02x\n", inb (0xc6f)); + printk("Port 0xc6f (before) = %02x\n", inb(0xc6f)); #endif - outb (0x80, 0xc6f); + outb((0x80), 0xc6f); #ifdef DEBUGXL - printk ("Port 0xc6f (after) = %02x\n", inb (0xc6f)); + printk("Port 0xc6f (after) = %02x\n", inb(0xc6f)); #endif - return 1; + return 1; } -#endif -int -probe_ms_sound (struct address_info *hw_config) +int probe_ms_sound(struct address_info *hw_config) { - unsigned char tmp; - - DDB (printk ("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype)); - - if (check_region (hw_config->io_base, 8)) - { - printk ("MSS: I/O port conflict\n"); - return 0; - } - - if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ - { - /* check_opl3(0x388, hw_config); */ - return ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp); - } - -#ifdef DESKPROXL - if (hw_config->card_subtype == 2) /* Compaq Deskpro XL */ - { - if (!init_deskpro (hw_config)) - return 0; - } -#endif + unsigned char tmp; - /* - * Check if the IO port returns valid signature. The original MS Sound - * system returns 0x04 while some cards (AudioTrix Pro for example) - * return 0x00 or 0x0f. - */ + DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype)); - if ((tmp = inb (hw_config->io_base + 3)) == 0xff) /* Bus float */ - { - int ret; + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "MSS: I/O port conflict\n"); + return 0; + } + if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ + { + /* check_opl3(0x388, hw_config); */ + return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); + } - DDB (printk ("I/O address is inactive (%x)\n", tmp)); - if (!(ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp))) - return 0; - return 1; - } - DDB (printk ("MSS signature = %x\n", tmp & 0x3f)); - if ((tmp & 0x3f) != 0x04 && - (tmp & 0x3f) != 0x0f && - (tmp & 0x3f) != 0x00) - { - int ret; - - DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n", - hw_config->io_base, inb (hw_config->io_base + 3))); - DDB (printk ("Trying to detect codec anyway but IRQ/DMA may not work\n")); - if (!(ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp))) - return 0; + if (deskpro_xl && hw_config->card_subtype == 2) /* Compaq Deskpro XL */ + { + if (!init_deskpro(hw_config)) + return 0; + } - return 1; - } - - if (hw_config->irq > 11) - { - printk ("MSS: Bad IRQ %d\n", hw_config->irq); - return 0; - } - - if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) - { - printk ("MSS: Bad DMA %d\n", hw_config->dma); - return 0; - } - - /* - * Check that DMA0 is not in use with a 8 bit board. - */ - - if (hw_config->dma == 0 && inb (hw_config->io_base + 3) & 0x80) - { - printk ("MSS: Can't use DMA0 with a 8 bit card/slot\n"); - return 0; - } - - if (hw_config->irq > 7 && hw_config->irq != 9 && inb (hw_config->io_base + 3) & 0x80) - { - printk ("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); - return 0; - } - - return ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp); -} + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (AudioTrix Pro for example) + * return 0x00 or 0x0f. + */ -void -attach_ms_sound (struct address_info *hw_config) -{ - static char interrupt_bits[12] = - { - -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 - }; - char bits, dma2_bit = 0; - int ad_flags = 0; - - static char dma_bits[4] = - { - 1, 2, 0, 3 - }; - - int config_port = hw_config->io_base + 0; - int version_port = hw_config->io_base + 3; - int dma = hw_config->dma; - int dma2 = hw_config->dma2; - - if (!ad1848_detect (hw_config->io_base + 4, &ad_flags, hw_config->osp)) - return; - - if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ - { - ad1848_init ("MS Sound System", hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - hw_config->dma2, 0, hw_config->osp); - request_region (hw_config->io_base, 4, "WSS config"); - return; - } - - /* - * Set the IRQ and DMA addresses. - */ - - bits = interrupt_bits[hw_config->irq]; - if (bits == -1) - return; - - outb (bits | 0x40, config_port); - if ((inb (version_port) & 0x40) == 0) - printk ("[IRQ Conflict?]"); + if ((tmp = inb(hw_config->io_base + 3)) == 0xff) /* Bus float */ + { + int ret; -/* - * Handle the capture DMA channel - */ + DDB(printk("I/O address is inactive (%x)\n", tmp)); + if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp))) + return 0; + return 1; + } + DDB(printk("MSS signature = %x\n", tmp & 0x3f)); + if ((tmp & 0x3f) != 0x04 && + (tmp & 0x3f) != 0x0f && + (tmp & 0x3f) != 0x00) + { + int ret; - if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma) - { - if (!((dma == 0 && dma2 == 1) || - (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0))) - { /* Unsupported combination. Try to swap channels */ - int tmp = dma; + MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3))); + DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n")); + if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp))) + return 0; - dma = dma2; - dma2 = tmp; + hw_config->card_subtype = 1; + return 1; } - - if ((dma == 0 && dma2 == 1) || - (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0)) + if (hw_config->irq > 11) { - dma2_bit = 0x04; /* Enable capture DMA */ + printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); + return 0; } - else + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) { - printk ("MSS: Invalid capture DMA\n"); - dma2 = dma; + printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma); + return 0; } - } - else - dma2 = dma; - - outb (bits | dma_bits[dma] | dma2_bit, config_port); /* Write IRQ+DMA setup */ + /* + * Check that DMA0 is not in use with a 8 bit board. + */ - ad1848_init ("MSS audio codec", hw_config->io_base + 4, - hw_config->irq, - dma, - dma2, 0, - hw_config->osp); - request_region (hw_config->io_base, 4, "WSS config"); + if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "MSS: Can't use DMA0 with a 8 bit card/slot\n"); + return 0; + } + if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); + return 0; + } + return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); } -void -unload_ms_sound (struct address_info *hw_config) +void attach_ms_sound(struct address_info *hw_config) { - ad1848_unload (hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - hw_config->dma, 0); - release_region (hw_config->io_base, 4); -} + static char interrupt_bits[12] = + { + -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 + }; + char bits, dma2_bit = 0; + + static char dma_bits[4] = + { + 1, 2, 0, 3 + }; + + int config_port = hw_config->io_base + 0; + int version_port = hw_config->io_base + 3; + int dma = hw_config->dma; + int dma2 = hw_config->dma2; + + if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ + { + hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0, hw_config->osp); + request_region(hw_config->io_base, 4, "WSS config"); + return; + } + /* + * Set the IRQ and DMA addresses. + */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == -1) + { + printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); + return; + } + outb((bits | 0x40), config_port); + if ((inb(version_port) & 0x40) == 0) + printk(KERN_ERR "[MSS: IRQ Conflict?]\n"); /* - * WSS compatible PnP codec support + * Handle the capture DMA channel */ -int -probe_pnp_ad1848 (struct address_info *hw_config) -{ - return ad1848_detect (hw_config->io_base, NULL, hw_config->osp); -} + if (dma2 != -1 && dma2 != dma) + { + if (!((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0))) + { /* Unsupported combination. Try to swap channels */ + int tmp = dma; + + dma = dma2; + dma2 = tmp; + } + if ((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0)) + { + dma2_bit = 0x04; /* Enable capture DMA */ + } + else + { + printk(KERN_WARNING "MSS: Invalid capture DMA\n"); + dma2 = dma; + } + } + else + { + dma2 = dma; + } -void -attach_pnp_ad1848 (struct address_info *hw_config) -{ + hw_config->dma = dma; + hw_config->dma2 = dma2; + + outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ - ad1848_init (hw_config->name, hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma2, 0, hw_config->osp); + hw_config->slots[0] = ad1848_init("MSS audio codec", hw_config->io_base + 4, + hw_config->irq, + dma, + dma2, 0, + hw_config->osp); + request_region(hw_config->io_base, 4, "WSS config"); } -void -unload_pnp_ad1848 (struct address_info *hw_config) +void unload_ms_sound(struct address_info *hw_config) { - ad1848_unload (hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma2, 0); - release_region (hw_config->io_base, 4); + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0); + sound_unload_audiodev(hw_config->slots[0]); + release_region(hw_config->io_base, 4); } -#ifdef CONFIG_SEQUENCER +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) + /* * Timer stuff (for /dev/music). */ static unsigned int current_interval = 0; -static unsigned int -ad1848_tmr_start (int dev, unsigned int usecs) +static unsigned int ad1848_tmr_start(int dev, unsigned int usecs) { - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */ - unsigned long divider; - - save_flags (flags); - cli (); - -/* - * Length of the timer interval (in nanoseconds) depends on the - * selected crystal oscillator. Check this from bit 0x01 of I8. - * - * AD1845 has just one oscillator which has cycle time of 10.050 us - * (when a 24.576 MHz xtal oscillator is used). - * - * Convert requested interval to nanoseconds before computing - * the timer divider. - */ - - if (devc->model == MD_1845) - xtal_nsecs = 10050; - else if (ad_read (devc, 8) & 0x01) - xtal_nsecs = 9920; - else - xtal_nsecs = 9969; + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */ + unsigned long divider; + + save_flags(flags); + cli(); + + /* + * Length of the timer interval (in nanoseconds) depends on the + * selected crystal oscillator. Check this from bit 0x01 of I8. + * + * AD1845 has just one oscillator which has cycle time of 10.050 us + * (when a 24.576 MHz xtal oscillator is used). + * + * Convert requested interval to nanoseconds before computing + * the timer divider. + */ + + if (devc->model == MD_1845) + xtal_nsecs = 10050; + else if (ad_read(devc, 8) & 0x01) + xtal_nsecs = 9920; + else + xtal_nsecs = 9969; - divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs; + divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs; - if (divider < 100) /* Don't allow shorter intervals than about 1ms */ - divider = 100; + if (divider < 100) /* Don't allow shorter intervals than about 1ms */ + divider = 100; - if (divider > 65535) /* Overflow check */ - divider = 65535; + if (divider > 65535) /* Overflow check */ + divider = 65535; - ad_write (devc, 21, (divider >> 8) & 0xff); /* Set upper bits */ - ad_write (devc, 20, divider & 0xff); /* Set lower bits */ - ad_write (devc, 16, ad_read (devc, 16) | 0x40); /* Start the timer */ - devc->timer_running = 1; - restore_flags (flags); + ad_write(devc, 21, (divider >> 8) & 0xff); /* Set upper bits */ + ad_write(devc, 20, divider & 0xff); /* Set lower bits */ + ad_write(devc, 16, ad_read(devc, 16) | 0x40); /* Start the timer */ + devc->timer_running = 1; + restore_flags(flags); - return current_interval = (divider * xtal_nsecs + 500) / 1000; + return current_interval = (divider * xtal_nsecs + 500) / 1000; } -static void -ad1848_tmr_reprogram (int dev) +static void ad1848_tmr_reprogram(int dev) { -/* - * Audio driver has changed sampling rate so that a different xtal - * oscillator was selected. We have to reprogram the timer rate. - */ + /* + * Audio driver has changed sampling rate so that a different xtal + * oscillator was selected. We have to reprogram the timer rate. + */ - ad1848_tmr_start (dev, current_interval); - sound_timer_syncinterval (current_interval); + ad1848_tmr_start(dev, current_interval); + sound_timer_syncinterval(current_interval); } -static void -ad1848_tmr_disable (int dev) +static void ad1848_tmr_disable(int dev) { - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - save_flags (flags); - cli (); - ad_write (devc, 16, ad_read (devc, 16) & ~0x40); - devc->timer_running = 0; - restore_flags (flags); + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + save_flags(flags); + cli(); + ad_write(devc, 16, ad_read(devc, 16) & ~0x40); + devc->timer_running = 0; + restore_flags(flags); } -static void -ad1848_tmr_restart (int dev) +static void ad1848_tmr_restart(int dev) { - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + unsigned long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - if (current_interval == 0) - return; + if (current_interval == 0) + return; - save_flags (flags); - cli (); - ad_write (devc, 16, ad_read (devc, 16) | 0x40); - devc->timer_running = 1; - restore_flags (flags); + save_flags(flags); + cli(); + ad_write(devc, 16, ad_read(devc, 16) | 0x40); + devc->timer_running = 1; + restore_flags(flags); } static struct sound_lowlev_timer ad1848_tmr = { - 0, - ad1848_tmr_start, - ad1848_tmr_disable, - ad1848_tmr_restart + 0, + 2, + ad1848_tmr_start, + ad1848_tmr_disable, + ad1848_tmr_restart }; -static int -ad1848_tmr_install (int dev) +static int ad1848_tmr_install(int dev) { + if (timer_installed != -1) + return 0; /* Don't install another timer */ + + timer_installed = ad1848_tmr.dev = dev; + sound_timer_init(&ad1848_tmr, audio_devs[dev]->name); + + return 1; +} +#endif + + +struct symbol_table ad1848_symbol_table = { +#include + X(ad1848_detect), + X(ad1848_init), + X(ad1848_unload), + X(ad1848_control), + X(adintr), + X(probe_ms_sound), + X(attach_ms_sound), + X(unload_ms_sound), +#include +}; + +#ifdef MODULE + +MODULE_PARM(io, "i"); /* I/O for a raw AD1848 card */ +MODULE_PARM(irq, "i"); /* IRQ to use */ +MODULE_PARM(dma, "i"); /* First DMA channel */ +MODULE_PARM(dma2, "i"); /* Second DMA channel */ +MODULE_PARM(type, "i"); /* Card type */ +MODULE_PARM(deskpro_xl, "i"); /* Special magic for Deskpro XL boxen */ + +int io = -1; +int irq = -1; +int dma = -1; +int dma2 = -1; +int type = 0; - if (timer_installed != -1) - return 0; /* Don't install another timer */ +static int loaded = 0; - timer_installed = ad1848_tmr.dev = dev; - sound_timer_init (&ad1848_tmr, audio_devs[dev]->name); +struct address_info hw_config; + +int init_module(void) +{ + printk(KERN_INFO "ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + if(io != -1) + { + if(irq == -1 || dma == -1) + { + printk(KERN_WARNING "ad1848: must give I/O , IRQ and DMA.\n"); + return -EINVAL; + } + hw_config.irq = irq; + hw_config.io_base = io; + hw_config.dma = dma; + hw_config.dma2 = dma2; + hw_config.card_subtype = type; + if(!probe_ms_sound(&hw_config)) + return -ENODEV; + attach_ms_sound(&hw_config); + loaded=1; + } + register_symtab(&ad1848_symbol_table); + SOUND_LOCK; + return 0; +} - return 1; +void cleanup_module(void) +{ + SOUND_LOCK_END; + if(loaded) + unload_ms_sound(&hw_config); } + #endif #endif diff --git a/drivers/sound/ad1848_mixer.h b/drivers/sound/ad1848_mixer.h index 073c14d95bbc..ab8eb2035088 100644 --- a/drivers/sound/ad1848_mixer.h +++ b/drivers/sound/ad1848_mixer.h @@ -1,13 +1,13 @@ /* * sound/ad1848_mixer.h - * + * * Definitions for the mixer of AD1848 and compatible codecs. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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. */ @@ -15,7 +15,7 @@ /* * The AD1848 codec has generic input lines called Line, Aux1 and Aux2. - * Soundcard manufacturers have connected actual inputs (CD, synth, line, + * 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 to these inputs correctly. The following * contains two alternative mappings. The first one is for GUS MAX and @@ -31,18 +31,28 @@ SOUND_MASK_IGAIN | \ SOUND_MASK_PCM | SOUND_MASK_IMIX) -#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | SOUND_MASK_MIC | \ +#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) + struct mixer_def { - unsigned int regno: 7; + unsigned int regno: 5; unsigned int polarity:1; /* 0=normal, 1=reversed */ - unsigned int bitpos:4; - unsigned int nbits:4; + unsigned int bitpos:3; + unsigned int nbits:3; + unsigned int mutepos:4; }; static char mix_cvt[101] = { @@ -55,59 +65,115 @@ static char mix_cvt[101] = { }; 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 soundcards is not fixed. Some + * 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) \ - {{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}} - -mixer_ent mix_devices[32][2] = { -MIX_ENT(SOUND_MIXER_VOLUME, 27, 1, 0, 4, 29, 1, 0, 4), -MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5), -MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6), -MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5), -MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1), -MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5), -MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4), -MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5) +#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit) \ + {{reg_l, pola_l, pos_l, len_l, mute_bit}, {reg_r, pola_r, pos_r, len_r, mute_bit}} + +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 unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] = +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) +}; + +/* 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 int default_mixer_levels[32] = { 0x3232, /* Master Volume */ 0x3232, /* Bass */ 0x3232, /* Treble */ 0x4b4b, /* FM */ 0x3232, /* PCM */ - 0x4b4b, /* PC Speaker */ + 0x1515, /* PC Speaker */ 0x2020, /* Ext Line */ 0x1010, /* Mic */ 0x4b4b, /* CD */ 0x0000, /* Recording monitor */ - 0x4b4b, /* SB PCM */ + 0x4b4b, /* Second PCM */ 0x4b4b, /* Recording level */ 0x4b4b, /* Input gain */ 0x4b4b, /* Output gain */ - 0x4040, /* Line1 */ - 0x4040, /* Line2 */ + 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 diff --git a/drivers/sound/adlib_card.c b/drivers/sound/adlib_card.c index 692f43fdb629..58ee0f7e5b3f 100644 --- a/drivers/sound/adlib_card.c +++ b/drivers/sound/adlib_card.c @@ -2,48 +2,70 @@ * sound/adlib_card.c * * Detection routine for the AdLib card. - */ - -/* - * Copyright (C) by Hannu Savolainen 1993-1996 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 +#include +#include #include "sound_config.h" +#include "soundmodule.h" -#if defined(CONFIG_YM3812) +#ifdef CONFIG_YM3812 -void -attach_adlib_card (struct address_info *hw_config) +void attach_adlib_card(struct address_info *hw_config) { + hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp); + request_region(hw_config->io_base, 4, "OPL3/OPL2"); +} - opl3_init (hw_config->io_base, hw_config->osp); - request_region (hw_config->io_base, 4, "OPL3/OPL2"); +int probe_adlib(struct address_info *hw_config) +{ + if (check_region(hw_config->io_base, 4)) + { + DDB(printk("opl3.c: I/O port %x already in use\n", hw_config->io_base)); + return 0; + } + return opl3_detect(hw_config->io_base, hw_config->osp); } -int -probe_adlib (struct address_info *hw_config) +void unload_adlib(struct address_info *hw_config) { + release_region(hw_config->io_base, 4); + sound_unload_synthdev(hw_config->slots[0]); +} - if (check_region (hw_config->io_base, 4)) - { - printk ("\n\nopl3.c: I/O port %x already in use\n\n", hw_config->io_base); - return 0; - } +#ifdef MODULE - return opl3_detect (hw_config->io_base, hw_config->osp); -} +int io = -1; +MODULE_PARM(io, "i"); + +struct address_info cfg; -void -unload_adlib (struct address_info *hw_config) +int init_module(void) { - release_region (hw_config->io_base, 4); + if (io == -1) { + printk(KERN_ERR "adlib: must specify I/O address.\n"); + return -EINVAL; + } + cfg.io_base = io; + if (probe_adlib(&cfg) == 0) + return -ENODEV; + attach_adlib_card(&cfg); + SOUND_LOCK; + return 0; } +void cleanup_module(void) +{ + unload_adlib(&cfg); + SOUND_LOCK_END; +} #endif +#endif diff --git a/drivers/sound/aedsp16.c b/drivers/sound/aedsp16.c deleted file mode 100644 index 78f32ecc1501..000000000000 --- a/drivers/sound/aedsp16.c +++ /dev/null @@ -1,869 +0,0 @@ -/* - sound/aedsp16.c - - Audio Excel DSP 16 software configuration routines - - Copyright (C) 1995 Riccardo Facchetti (riccardo@cdc8g5.cdc.polimi.it) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. 2. - Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. - */ -/* - * Include the main voxware header file. It include all the os/voxware/etc - * headers needed by this source. - */ -#include -#include "sound_config.h" - -#ifndef AEDSP16_BASE -#undef CONFIG_AEDSP16 -#endif - -#if defined(CONFIG_AEDSP16) -/* - - READ THIS - - This module is intended for Audio Excel DSP 16 Sound Card. - - Audio Excel DSP 16 is an SB pro II, Microsoft Sound System - and MPU-401 compatible card. - It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq), - so before this module, the only way to configure the DSP under linux was - boot the MS-BAU loading the sound.sys device driver (this driver soft- - configure the sound board hardware by massaging someone of its registers), - and then ctrl-alt-del to boot linux with the DSP configured by the DOG - driver. - - This module works configuring your Audio Excel DSP 16's - irq, dma and mpu-401-irq. The voxware probe routines rely on the - fact that if the hardware is there, they can detect it. The problem - with AEDSP16 is that no hardware can be found by the probe routines - if the sound card is not well configured. Sometimes the kernel probe - routines can find an SBPRO even when the card is not configured (this - is the standard setup of the card), but the SBPRO emulation don't work - well if the card is not properly initialized. For this reason - - InitAEDSP16_...() - - routines are called before the voxware probe routines try to detect the - hardware. - - NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS) - - The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS; - the voxware sound driver can be configured for SBPRO and MSS cards - at the same time, but the AEDSP16 can't be two cards!! - When we configure it, we have to choose the SBPRO or the MSS emulation - for AEDSP16. We also can install a *REAL* card of the other type - (see [1], not tested but I can't see any reason for it to fail). - - NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO - please let me know if it works. - - The MPU-401 support can be compiled in together with one of the other - two operating modes. - - The board configuration calls, are in the probe_...() routines because - we have to configure the board before probing it for a particular - hardware. After card configuration, we can probe the hardware. - - NOTE: This is something like plug-and-play: we have only to plug - the AEDSP16 board in the socket, and then configure and compile - a kernel that uses the AEDSP16 software configuration capability. - No jumper setting is needed! - - For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3 - you have just to make config the voxware package, configuring - the SBPro sound card with that parameters, then when configure - asks if you have an AEDSP16, answer yes. That's it. - Compile the kernel and run it. - - NOTE: This means that you can choose irq and dma, but not the - I/O addresses. To change I/O addresses you have to set them - with jumpers. - - NOTE: InitAEDSP16_...() routines get as parameter the hw_config, - the hardware configuration of the - to be configured - board. - The InitAEDSP16() routine, configure the board following our - wishes, that are in the hw_config structure. - - You can change the irq/dma/mirq settings WITHOUT THE NEED to open - your computer and massage the jumpers (there are no irq/dma/mirq - jumpers to be configured anyway, only I/O port ones have to be - configured with jumpers) - - For some ununderstandable reason, the card default of irq 7, dma 1, - don't work for me. Seems to be an IRQ or DMA conflict. Under heavy - HDD work, the kernel start to erupt out a lot of messages like: - - 'Sound: DMA timed out - IRQ/DRQ config error?' - - For what I can say, I have NOT any conflict at irq 7 (under linux I'm - using the lp polling driver), and dma line 1 is unused as stated by - /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so - I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows! - Anyway a setting of irq 10, dma 3 works really fine. - - NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know - the emulation mode, all the installed hardware and the hardware - configuration (irq and dma settings of all the hardware). - - This init module should work with SBPRO+MSS, when one of the two is - the AEDSP16 emulation and the other the real card. (see [1]) - For example: - - AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other - AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other - - MPU401 should work. (see [1]) - - [1] Not tested by me for lack of hardware. - - TODO, WISHES AND TECH - - May be there's lot of redundant delays, but for now I want to leave it - this way. - - Should be interesting eventually write down a new ioctl for the - AEDSP16, to let the suser() change the irq/dma/mirq on the fly. - The thing is not trivial. - In the real world, there's no need to have such an ioctl because - when we configure the kernel for compile, we can choose the config - parameters. If we change our mind, we can easily re-config the kernel - and re-compile. - Why let the suser() change the config parameters on the fly ? - If anyone have a reasonable answer to this question, I will write down - the code to do it. - - More integration with voxware, using voxware low level routines to - read-write DSP is not possible because you may want to have MSS - support and in that case we can not rely on the functions included - in sb_dsp.c to control 0x2yy I/O ports. I will continue to use my - own I/O functions. - - - About I/O ports allocation - - - The request_region should be done at device probe in every sound card - module. This module is not the best site for requesting regions. - When the request_region code will be added to the main modules such as - sb, adlib, gus, ad1848, etc, the requesting code in this module should - go away. - - I think the request regions should be done this way: - - if (check_region(...)) - return ERR; // I/O region already reserved - device_probe(...); - device_attach(...); - request_region(...); // reserve only when we are sure all is okay - - Request the 2x0h region in any case if we are using this card. - - NOTE: the "(SBPro)" string with which we are requesting the AEDSP16 region - (see code) does not mean necessarily that we are emulating SBPro. - It mean that the region is the SBPro I/O ports region. We use this - region to access the control registers of the card, and if emulating - SBPro, I/O SBPro registers too. If we are emulating MSS, the SBPro - registers are not used, in no way, to emulate an SBPro: they are - used only for configuration purposes. - - Someone pointed out that should be possible use both the SBPRO and MSS - modes because the sound card have all the two chipsets, supposing that - the card is really two cards. I have tried something to have the two - modes work together, but, for some reason unknown to me, without success. - - I think all the soft-config only cards have an init sequence similar to - this. If you have a card that is not an AEDSP16, you can try to start - with this module changing it (mainly in the CMD? I think) to fit your - needs. - - Started Fri Mar 17 16:13:18 MET 1995 - - v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c) - - Initial code. - v0.2 (ALPHA) - - Cleanups. - - Integrated with Linux voxware v 2.90-2 kernel sound driver. - - Sound Blaster Pro mode configuration. - - Microsoft Sound System mode configuration. - - MPU-401 mode configuration. - v0.3 (ALPHA) - - Cleanups. - - Rearranged the code to let InitAEDSP16 be more general. - - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h - inclusion too. We rely on os.h - - Used the to get a variable - len string (we are not sure about the len of Copyright string). - This works with any SB and compatible. - - Added the code to request_region at device init (should go in - the main body of voxware). - v0.4 (BETA) - - Better configure.c patch for AEDSP16 configuration (better - logic of inclusion of AEDSP16 support) - - Modified the conditional compilation to better support more than - one sound card of the emulated type (read the NOTES above) - - Moved the sb init routine from the attach to the very first - probe in sb_card.c - - Rearrangements and cleanups - - Wiped out some unnecessary code and variables: this is kernel - code so it is better save some TEXT and DATA - - Fixed the request_region code. We must allocate the AEDSP16 (SBPro) - I/O ports in any case because they are used to access the DSP - configuration registers and we can not allow anyone to get them. - v0.5 - - cleanups on comments - - prep for diffs against v3.0-proto-950402 - v0.6 - - removed the request_region()s when compiling the MODULE sound.o - because we are not allowed (by the actual voxware structure) to - release_region() - - */ - - -#define VERSION "0.6" /* Version of Audio Excel DSP 16 driver */ - -#undef AEDSP16_DEBUG /* Define this to enable debug code */ -/* Actually no debug code is activated */ - -/* - * Hardware related defaults - */ -#define IRQ 7 /* 5 7(default) 9 10 11 */ -#define MIRQ 0 /* 5 7 9 10 0(default), 0 means disable */ -#define DMA 1 /* 0 1(default) 3 */ - -/* - * Commands of AEDSP16's DSP (SBPRO+special). - * For now they are CMDn, in the future they may change. - */ -#define CMD1 0xe3 /* Get DSP Copyright */ -#define CMD2 0xe1 /* Get DSP Version */ -#define CMD3 0x88 /* */ -#define CMD4 0x5c /* */ -#define CMD5 0x50 /* Set M&I&DRQ mask (the real config) */ -#define CMD6 0x8c /* Enable Microsoft Sound System mode */ - -/* - * Offsets of AEDSP16 DSP I/O ports. The offset is added to portbase - * to have the actual I/O port. - * Register permissions are: - * (wo) == Write Only - * (ro) == Read Only - * (w-) == Write - * (r-) == Read - */ -#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */ -#define DSP_READ 0x0a /* offset of DSP READ (ro) */ -#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */ -#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */ -#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */ -#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */ - - -#define RETRY 10 /* Various retry values on I/O opera- */ -#define STATUSRETRY 1000 /* tions. Sometimes we have to */ -#define HARDRETRY 500000 /* wait for previous cmd to complete */ - -/* - * Size of character arrays that store name and version of sound card - */ -#define CARDNAMELEN 15 /* Size of the card's name in chars */ -#define CARDVERLEN 2 /* Size of the card's version in chars */ - -/* - * Bit mapped flags for calling InitAEDSP16(), and saving the current - * emulation mode. - */ -#define INIT_NONE (0 ) -#define INIT_SBPRO (1<<0) -#define INIT_MSS (1<<1) -#define INIT_MPU401 (1<<2) -#define RESET_DSP16 (1<<3) - -/* Base HW Port for Audio Card */ -static int portbase = AEDSP16_BASE; -static int irq = IRQ; /* irq for DSP I/O */ -static int mirq = MIRQ; /* irq for MPU-401 I/O */ -static int dma = DMA; /* dma for DSP I/O */ - -/* Init status of the card */ -static int ae_init = INIT_NONE; /* (bitmapped variable) */ -static int oredparams = 0; /* Will contain or'ed values of params */ -static int gc = 0; /* generic counter (utility counter) */ -struct orVals - { /* Contain the values to be or'ed */ - int val; /* irq|mirq|dma */ - int or; /* oredparams |= TheStruct.or */ - }; - -/* - * Magic values that the DSP will eat when configuring irq/mirq/dma - */ -/* DSP IRQ conversion array */ -static struct orVals orIRQ[] = -{ - {0x05, 0x28}, - {0x07, 0x08}, - {0x09, 0x10}, - {0x0a, 0x18}, - {0x0b, 0x20}, - {0x00, 0x00} -}; - -/* MPU-401 IRQ conversion array */ -static struct orVals orMIRQ[] = -{ - {0x05, 0x04}, - {0x07, 0x44}, - {0x09, 0x84}, - {0x0a, 0xc4}, - {0x00, 0x00} -}; - -/* DMA Channels conversion array */ -static struct orVals orDMA[] = -{ - {0x00, 0x01}, - {0x01, 0x02}, - {0x03, 0x03}, - {0x00, 0x00} -}; - -/* - * Buffers to store audio card information - */ -static char AudioExcelName[CARDNAMELEN + 1]; -static char AudioExcelVersion[CARDVERLEN + 1]; - -static void -tenmillisec (void) -{ - - for (gc = 0; gc < 1000; gc++) - tenmicrosec (); -} - -static int -WaitForDataAvail (int port) -{ - int loop = STATUSRETRY; - unsigned char ret = 0; - - do - { - ret = inb (port + DSP_DATAVAIL); - /* - * Wait for data available (bit 7 of ret == 1) - */ - } - while (!(ret & 0x80) && loop--); - - if (ret & 0x80) - return 0; - - return -1; -} - -static int -ReadData (int port) -{ - if (WaitForDataAvail (port)) - return -1; - return inb (port + DSP_READ); -} - -static int -CheckDSPOkay (int port) -{ - return ((ReadData (port) == 0xaa) ? 0 : -1); -} - -static int -ResetBoard (int port) -{ - /* - * Reset DSP - */ - outb (1, (port + DSP_RESET)); - tenmicrosec (); - outb (0, (port + DSP_RESET)); - tenmicrosec (); - tenmicrosec (); - return CheckDSPOkay (port); -} - -static int -WriteDSPCommand (int port, int cmd) -{ - unsigned char ret; - int loop = HARDRETRY; - - do - { - ret = inb (port + DSP_STATUS); - /* - * DSP ready to receive data if bit 7 of ret == 0 - */ - if (!(ret & 0x80)) - { - outb (cmd, port + DSP_COMMAND); - return 0; - } - } - while (loop--); - - printk ("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd); - return -1; -} - -int -InitMSS (int port) -{ - - tenmillisec (); - - if (WriteDSPCommand (port, CMD6)) - { - printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD6); - return -1; - } - - tenmillisec (); - - return 0; -} - -static int -SetUpBoard (int port) -{ - int loop = RETRY; - - do - { - if (WriteDSPCommand (portbase, CMD3)) - { - printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD3); - return -1; - } - - tenmillisec (); - - } - while (WaitForDataAvail (port) && loop--); - -#if defined(THIS_SHOULD_GO_AWAY) - if (CheckDSPOkay (port)) - { - printk ("[AEDSP16] CheckDSPOkay: failed\n"); - return -1; - } -#else - if (ReadData (port) == -1) - { - printk ("[AEDSP16] ReadData after CMD 0x%x: failed\n", CMD3); - return -1; - } -#endif - - if (WriteDSPCommand (portbase, CMD4)) - { - printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD4); - return -1; - } - - if (WriteDSPCommand (portbase, CMD5)) - { - printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD5); - return -1; - } - - if (WriteDSPCommand (portbase, oredparams)) - { - printk ("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n"); - return -1; - } - return 0; -} - -static int -GetCardVersion (int port) -{ - int len = 0; - int ret; - int ver[3]; - - do - { - if ((ret = ReadData (port)) == -1) - return -1; - /* - * We already know how many int are stored (2), so we know when the - * string is finished. - */ - ver[len++] = ret; - } - while (len < CARDVERLEN); - sprintf (AudioExcelVersion, "%d.%d", ver[0], ver[1]); - return 0; -} - -static int -GetCardName (int port) -{ - int len = 0; - int ret; - - do - { - if ((ret = ReadData (port)) == -1) - /* - * If no more data available, return to the caller, no error if len>0. - * We have no other way to know when the string is finished. - */ - return (len ? 0 : -1); - - AudioExcelName[len++] = ret; - - } - while (len < CARDNAMELEN); - return 0; -} - -static void -InitializeHardParams (void) -{ - - memset (AudioExcelName, 0, CARDNAMELEN + 1); - memset (AudioExcelVersion, 0, CARDVERLEN + 1); - - for (gc = 0; orIRQ[gc].or; gc++) - if (orIRQ[gc].val == irq) - oredparams |= orIRQ[gc].or; - - for (gc = 0; orMIRQ[gc].or; gc++) - if (orMIRQ[gc].or == mirq) - oredparams |= orMIRQ[gc].or; - - for (gc = 0; orDMA[gc].or; gc++) - if (orDMA[gc].val == dma) - oredparams |= orDMA[gc].or; -} - -static int -InitAEDSP16 (int which) -{ - static char *InitName = NULL; - - InitializeHardParams (); - - if (ResetBoard (portbase)) - { - printk ("[AEDSP16] ResetBoard: failed!\n"); - return -1; - } - -#if defined(THIS_SHOULD_GO_AWAY) - if (CheckDSPOkay (portbase)) - { - printk ("[AEDSP16] CheckDSPOkay: failed!\n"); - return -1; - } -#endif - - if (WriteDSPCommand (portbase, CMD1)) - { - printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD1); - return -1; - } - - if (GetCardName (portbase)) - { - printk ("[AEDSP16] GetCardName: failed!\n"); - return -1; - } - - /* - * My AEDSP16 card return SC-6000 in AudioExcelName, so - * if we have something different, we have to be warned. - */ - if (strcmp ("SC-6000", AudioExcelName)) - printk ("[AEDSP16] Warning: non SC-6000 audio card!\n"); - - if (WriteDSPCommand (portbase, CMD2)) - { - printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD2); - return -1; - } - - if (GetCardVersion (portbase)) - { - printk ("[AEDSP16] GetCardVersion: failed!\n"); - return -1; - } - - if (SetUpBoard (portbase)) - { - printk ("[AEDSP16] SetUpBoard: failed!\n"); - return -1; - } - - if (which == INIT_MSS) - { - if (InitMSS (portbase)) - { - printk ("[AEDSP16] Can't initialize Microsoft Sound System mode.\n"); - return -1; - } - } - - /* - * If we are resetting, do not print any message because we may be - * in playing and we do not want lost too much time. - */ - if (!(which & RESET_DSP16)) - { - if (which & INIT_MPU401) - InitName = "MPU401"; - else if (which & INIT_SBPRO) - InitName = "SBPro"; - else if (which & INIT_MSS) - InitName = "MSS"; - else - InitName = "None"; - - printk ("Audio Excel DSP 16 init v%s (%s %s) [%s]\n", - VERSION, AudioExcelName, - AudioExcelVersion, InitName); - } - - tenmillisec (); - - return 0; -} - -#if defined(AEDSP16_SBPRO) - -int -InitAEDSP16_SBPRO (struct address_info *hw_config) -{ - /* - * If the card is already init'ed MSS, we can not init it to SBPRO too - * because the board can not emulate simultaneously MSS and SBPRO. - */ - if (ae_init & INIT_MSS) - return -1; - if (ae_init & INIT_SBPRO) - return 0; - - /* - * For now we will leave this - * code included only when INCLUDE_AEDSP16 is configured in, but it should - * be better include it every time. - */ - if (!(ae_init & INIT_MPU401)) - { - if (check_region (hw_config->io_base, 0x0f)) - { - printk ("AEDSP16/SBPRO I/O port region is already in use.\n"); - return -1; - } - } - - /* - * Set up the internal hardware parameters, to let the driver reach - * the Sound Card. - */ - portbase = hw_config->io_base; - irq = hw_config->irq; - dma = hw_config->dma; - - if (InitAEDSP16 (INIT_SBPRO)) - return -1; - -#if !defined(MODULE) - /* - * If we are compiling sound.o (MODULAR version) we can not - * request any region because there is not a uninit routine that - * can allow me to release the requested region. - */ - if (!(ae_init & INIT_MPU401)) - request_region (hw_config->io_base, 0x0f, "AEDSP16 (SBPro)"); -#endif - - ae_init |= INIT_SBPRO; - return 0; -} - -#endif /* AEDSP16_SBPRO */ - -#if defined(AEDSP16_MSS) - -int -InitAEDSP16_MSS (struct address_info *hw_config) -{ - /* - * If the card is already init'ed SBPRO, we can not init it to MSS too - * because the board can not emulate simultaneously MSS and SBPRO. - */ - if (ae_init & INIT_SBPRO) - return -1; - if (ae_init & INIT_MSS) - return 0; - - /* - * For now we will leave this - * code included only when INCLUDE_AEDSP16 is configured in, but it should - * be better include it every time. - */ - if (check_region (hw_config->io_base, 0x08)) - { - printk ("MSS I/O port region is already in use.\n"); - return -1; - } - - /* - * We must allocate the AEDSP16 region too because these are the I/O ports - * to access card's control registers. - */ - if (!(ae_init & INIT_MPU401)) - { - if (check_region (AEDSP16_BASE, 0x0f)) - { - printk ("AEDSP16 I/O port region is already in use.\n"); - return -1; - } - } - - - /* - * If we are configuring the card for MSS, the portbase for card - * configuration is the default one (0x220 unless you have changed the - * factory default with board switches), so no need to modify the - * portbase variable. - * The default is AEDSP16_BASE, that is the right value. - */ - irq = hw_config->irq; - dma = hw_config->dma; - - if (InitAEDSP16 (INIT_MSS)) - return -1; - -#if !defined(MODULE) - /* - * If we are compiling sound.o (MODULAR version) we can not - * request any region because there is not a uninit routine that - * can allow me to release the requested region. So when unloading - * and then reloading it, we are going to have some nice Oops! - */ - request_region (hw_config->io_base, 0x08, "AEDSP16 (MSS)"); -#endif - - if (!(ae_init & INIT_MPU401)) - request_region (AEDSP16_BASE, 0x0f, "AEDSP16 (SBPro)"); - - ae_init |= INIT_MSS; - return 0; -} - -#endif /* AEDSP16_MSS */ - -#if defined(AEDSP16_MPU401) - -int -InitAEDSP16_MPU401 (struct address_info *hw_config) -{ - if (ae_init & INIT_MPU401) - return 0; - - /* - * For now we will leave this - * code included only when INCLUDE_AEDSP16 is configured in, but it should - * be better include it every time. - */ - if (check_region (hw_config->io_base, 0x02)) - { - printk ("SB I/O port region is already in use.\n"); - return -1; - } - - /* - * We must allocate the AEDSP16 region too because these are the I/O ports - * to access card's control registers. - */ - if (!(ae_init & (INIT_MSS | INIT_SBPRO))) - { - if (check_region (AEDSP16_BASE, 0x0f)) - { - printk ("AEDSP16 I/O port region is already in use.\n"); - return -1; - } - } - - /* - * If mpu401, the irq and dma are not important, do not touch it - * because we may use the default if SBPro is not yet configured, - * we may use the SBPro ones if configured, and nothing wrong - * should happen. - * - * The mirq default is 0, but once set it to non-0 value, we should - * not touch it anymore (unless I write an ioctl to do it, of course). - */ - mirq = hw_config->irq; - if (InitAEDSP16 (INIT_MPU401)) - return -1; - -#if !defined(MODULE) - /* - * If we are compiling sound.o (MODULAR version) we can not - * request any region because there is not a uninit routine that - * can allow me to release the requested region. - */ - request_region (hw_config->io_base, 0x02, "AEDSP16 (mpu401)"); -#endif - - if (!(ae_init & (INIT_MSS | INIT_SBPRO))) - request_region (AEDSP16_BASE, 0x0f, "AEDSP16 (SBPro)"); - - ae_init |= INIT_MPU401; - return 0; -} - -#endif /* AEDSP16_MPU401 */ - -#if 0 /* Leave it out for now. We are not using this portion of code. */ - -/* - * Entry point for a reset function. - * May be I will write the infamous ioctl :) - */ -int -ResetAEDSP16 (void) -{ -#if defined(AEDSP16_DEBUG) - printk ("[AEDSP16] ResetAEDSP16 called.\n"); -#endif - return InitAEDSP16 (RESET_DSP16); -} - -#endif /* 0 */ - -#endif /* CONFIG_AEDSP16 */ diff --git a/drivers/sound/audio.c b/drivers/sound/audio.c index 62027a4ca659..fdc68107bf89 100644 --- a/drivers/sound/audio.c +++ b/drivers/sound/audio.c @@ -5,505 +5,928 @@ */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 +/* + * 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 + */ +#include +#include +#include #include "sound_config.h" #ifdef CONFIG_AUDIO - #include "ulaw.h" #include "coproc.h" -#define ON 1 -#define OFF 0 - -static int audio_mode[MAX_AUDIO_DEV]; -static int dev_nblock[MAX_AUDIO_DEV]; /* 1 if in nonblocking mode */ +#define NEUTRAL8 0x80 +#define NEUTRAL16 0x00 -#define AM_NONE 0 -#define AM_WRITE 1 -#define AM_READ 2 -static int audio_format[MAX_AUDIO_DEV]; -static int local_conversion[MAX_AUDIO_DEV]; +int dma_ioctl(int dev, unsigned int cmd, caddr_t arg); -static int -set_format (int dev, long fmt) +static int set_format(int dev, int fmt) { - if (fmt != AFMT_QUERY) - { - - local_conversion[dev] = 0; - - if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ - if (fmt == AFMT_MU_LAW) - { - fmt = AFMT_U8; - local_conversion[dev] = AFMT_MU_LAW; - } + 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 - fmt = AFMT_U8; /* This is always supported */ + return audio_devs[dev]->local_format; - audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) fmt, 1); - } - - if (local_conversion[dev]) /* This shadows the HW format */ - return local_conversion[dev]; - - return audio_format[dev]; + return audio_devs[dev]->local_format; } -int -audio_open (int dev, struct fileinfo *file) +int audio_open(int dev, struct file *file) { - int ret; - long bits; - int dev_type = dev & 0x0f; - int mode = file->mode & O_ACCMODE; - - dev = dev >> 4; + int ret; + int bits; + int dev_type = dev & 0x0f; + int mode = translate_mode(file); - if (dev_type == SND_DEV_DSP16) - bits = 16; - else - bits = 8; + dev = dev >> 4; - if ((ret = DMAbuf_open (dev, mode)) < 0) - return ret; + if (dev_type == SND_DEV_DSP16) + bits = 16; + else + bits = 8; - if (audio_devs[dev]->coproc) - if ((ret = audio_devs[dev]->coproc-> - open (audio_devs[dev]->coproc->devc, COPR_PCM)) < 0) - { - audio_release (dev, file); - printk ("Sound: Can't access coprocessor device\n"); + if (dev < 0 || dev >= num_audiodevs) + return -ENXIO; - return ret; - } + if ((ret = DMAbuf_open(dev, mode)) < 0) + return ret; - local_conversion[dev] = 0; + if (audio_devs[dev]->coproc) + { + if ((ret = audio_devs[dev]->coproc-> + open(audio_devs[dev]->coproc->devc, COPR_PCM)) < 0) + { + audio_release(dev, file); + printk(KERN_WARNING "Sound: Can't access coprocessor device\n"); + return ret; + } + } + + audio_devs[dev]->local_conversion = 0; - if (DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) bits, 1) != bits) - { - printk ("audio: Can't set number of bits on device %d\n", dev); - audio_release (dev, file); - return -(ENXIO); - } + if (dev_type == SND_DEV_AUDIO) + set_format(dev, AFMT_MU_LAW); + else + set_format(dev, bits); - if (dev_type == SND_DEV_AUDIO) - { - set_format (dev, AFMT_MU_LAW); - } - else - set_format (dev, bits); + audio_devs[dev]->audio_mode = AM_NONE; + audio_devs[dev]->dev_nblock = 0; - audio_mode[dev] = AM_NONE; - dev_nblock[dev] = 0; - return ret; + return ret; } -void -sync_output (int dev) +static void sync_output(int dev) { - int buf_no, buf_ptr, buf_size, p, i; - char *dma_buf; - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + int p, i; + int l; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0) - { - DMAbuf_start_output (dev, buf_no, buf_ptr); - } + if (dmap->fragment_size <= 0) + return; + dmap->flags |= DMA_POST; -/* - * Clean all unused buffer fragments. - */ + /* 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; - p = dmap->qtail; + len = dmap->fragment_size - l; + memset(dmap->raw_buf + offs, dmap->neutral_byte, len); + DMAbuf_move_wrpointer(dev, len); + } + + /* + * Clean all unused buffer fragments. + */ - for (i = dmap->qlen + 1; i < dmap->nbufs; i++) - { - memset (dmap->raw_buf + p * dmap->fragment_size, - dmap->neutral_byte, - dmap->fragment_size); + p = dmap->qtail; + dmap->flags |= DMA_POST; - p = (p + 1) % dmap->nbufs; - } + 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_CLEAN; + dmap->flags |= DMA_DIRTY; } -void -audio_release (int dev, struct fileinfo *file) +void audio_release(int dev, struct file *file) { - int mode; + int mode = translate_mode(file); - dev = dev >> 4; - mode = file->mode & O_ACCMODE; + dev = dev >> 4; - audio_devs[dev]->dmap_out->closing = 1; - audio_devs[dev]->dmap_in->closing = 1; + audio_devs[dev]->dmap_out->closing = 1; + audio_devs[dev]->dmap_in->closing = 1; - sync_output (dev); + sync_output(dev); - if (audio_devs[dev]->coproc) - audio_devs[dev]->coproc->close (audio_devs[dev]->coproc->devc, COPR_PCM); - DMAbuf_release (dev, mode); + if (audio_devs[dev]->coproc) + audio_devs[dev]->coproc->close(audio_devs[dev]->coproc->devc, COPR_PCM); + DMAbuf_release(dev, mode); } -#if defined(NO_INLINE_ASM) || !defined(i386) -static void -translate_bytes (const unsigned char *table, unsigned char *buff, int n) +static void translate_bytes(const unsigned char *table, unsigned char *buff, int n) { - unsigned long i; - - if (n <= 0) - return; + unsigned long i; - for (i = 0; i < n; ++i) - buff[i] = table[buff[i]]; -} + if (n <= 0) + return; -#else -extern inline void -translate_bytes (const void *table, void *buff, int n) -{ - if (n > 0) - { - __asm__ ("cld\n" - "1:\tlodsb\n\t" - "xlatb\n\t" - "stosb\n\t" - "loop 1b\n\t": - : "b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff) - : "bx", "cx", "di", "si", "ax"); - } + for (i = 0; i < n; ++i) + buff[i] = table[buff[i]]; } -#endif - -int -audio_write (int dev, struct fileinfo *file, const char *buf, int count) +int audio_write(int dev, struct file *file, const char *buf, int count) { - int c, p, l, buf_no, buf_ptr, buf_size; - int err; - char *dma_buf; + int c, p, l, buf_size; + int err; + char *dma_buf; - dev = dev >> 4; + dev = dev >> 4; - p = 0; - c = count; + p = 0; + c = count; - if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - { /* Direction change */ - } - - if (audio_devs[dev]->flags & DMA_DUPLEX) - audio_mode[dev] |= AM_WRITE; - else - audio_mode[dev] = AM_WRITE; + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EPERM; - if (!count) /* Flush output */ - { - sync_output (dev); - return 0; - } + if (audio_devs[dev]->flags & DMA_DUPLEX) + audio_devs[dev]->audio_mode |= AM_WRITE; + else + audio_devs[dev]->audio_mode = AM_WRITE; - while (c) - { - if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) < 0) + if (!count) /* Flush output */ { - if ((buf_no = DMAbuf_getwrbuffer (dev, &dma_buf, - &buf_size, - dev_nblock[dev])) < 0) - { - /* Handle nonblocking mode */ - if (dev_nblock[dev] && buf_no == -(EAGAIN)) - return p; /* No more space. Return # of accepted bytes */ - return buf_no; - } - buf_ptr = 0; + sync_output(dev); + return 0; } - - l = c; - if (l > (buf_size - buf_ptr)) - l = (buf_size - buf_ptr); - - if (!audio_devs[dev]->d->copy_from_user) - { /* - * No device specific copy routine - */ - memcpy_fromfs (&dma_buf[buf_ptr], &(buf)[p], l); - } - else - audio_devs[dev]->d->copy_from_user (dev, - dma_buf, buf_ptr, buf, p, l); - - if (local_conversion[dev] == AFMT_MU_LAW) + + while (c) { - /* - * This just allows interrupts while the conversion is running - */ - sti (); - translate_bytes (ulaw_dsp, (unsigned char *) &dma_buf[buf_ptr], l); - } - - c -= l; - p += l; - buf_ptr += l; - - if (buf_ptr >= buf_size) - { - if ((err = DMAbuf_start_output (dev, buf_no, buf_ptr)) < 0) - { - return err; - } + if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, audio_devs[dev]->dev_nblock)) < 0) + { + /* Handle nonblocking mode */ + if (audio_devs[dev]->dev_nblock && err == -EAGAIN) + return p; /* No more space. Return # of accepted bytes */ + return err; + } + l = c; + + if (l > buf_size) + l = buf_size; + + 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, l); + + if (audio_devs[dev]->local_conversion & CNV_MU_LAW) + { + /* + * This just allows interrupts while the conversion is running + */ + sti(); + translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l); + } + c -= l; + p += l; + DMAbuf_move_wrpointer(dev, l); } - else - DMAbuf_set_count (dev, buf_no, buf_ptr); - - } - return count; + return count; } -int -audio_read (int dev, struct fileinfo *file, char *buf, int count) +int audio_read(int dev, struct file *file, char *buf, int count) { - int c, p, l; - char *dmabuf; - int buf_no; - - dev = dev >> 4; - p = 0; - c = count; - - if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - { - sync_output (dev); - } - - if (audio_devs[dev]->flags & DMA_DUPLEX) - audio_mode[dev] |= AM_READ; - else - audio_mode[dev] = AM_READ; - - while (c) - { - if ((buf_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l, - dev_nblock[dev])) < 0) - { - /* Nonblocking mode handling. Return current # of bytes */ + int c, p, l; + char *dmabuf; + int buf_no; - if (dev_nblock[dev] && buf_no == -(EAGAIN)) - return p; + dev = dev >> 4; + p = 0; + c = count; - return buf_no; - } + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return -EPERM; - if (l > c) - l = c; + if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) + sync_output(dev); - /* - * Insert any local processing here. - */ + if (audio_devs[dev]->flags & DMA_DUPLEX) + audio_devs[dev]->audio_mode |= AM_READ; + else + audio_devs[dev]->audio_mode = AM_READ; - if (local_conversion[dev] == AFMT_MU_LAW) + while(c) { - /* - * This just allows interrupts while the conversion is running - */ - sti (); - - translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l); + if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, + audio_devs[dev]->dev_nblock)) < 0) + { + /* + * Nonblocking mode handling. Return current # of bytes + */ + + if (audio_devs[dev]->dev_nblock && buf_no == -EAGAIN) + return p; + + if (p > 0) /* Avoid throwing away data */ + return p; /* Return it instead */ + + return buf_no; + } + if (l > c) + l = c; + + /* + * Insert any local processing here. + */ + + if (audio_devs[dev]->local_conversion & CNV_MU_LAW) + { + /* + * This just allows interrupts while the conversion is running + */ + sti(); + + 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; } - memcpy_tofs (&(buf)[p], dmabuf, l); - - DMAbuf_rmchars (dev, buf_no, l); - - p += l; - c -= l; - } - - return count - c; + return count - c; } -int -audio_ioctl (int dev, struct fileinfo *file, - unsigned int cmd, caddr_t arg) +int audio_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg) { + int val, count; + unsigned long flags; + struct dma_buffparms *dmap; + + 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, (caddr_t) 0); + 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; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + 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: + audio_devs[dev]->dev_nblock = 1; + 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, (int *)arg)) + 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, (int *)arg)) + 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, (int *)arg)) + 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, (int *)arg)) + 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; + } + + save_flags (flags); + cli(); + /* 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; + + /* Substract current count from the number of bytes written by app */ + count = dmap->user_counter - count; + if (count < 0) + count = 0; + restore_flags (flags); + val = count; + break; + + default: + return dma_ioctl(dev, cmd, arg); + } + return put_user(val, (int *)arg); +} - dev = dev >> 4; - - if (((cmd >> 8) & 0xff) == 'C') - { - if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ - return audio_devs[dev]->coproc->ioctl (audio_devs[dev]->coproc->devc, cmd, arg, 0); - else - printk ("/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; +void audio_init_devices(void) +{ + /* + * NOTE! This routine could be called several times during boot. + */ +} - sync_output (dev); - return DMAbuf_ioctl (dev, cmd, arg, 0); - break; +#endif - case SNDCTL_DSP_POST: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return 0; - sync_output (dev); - return 0; - break; +void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording) +{ + /* + * This routine breaks the physical device buffers to logical ones. + */ - case SNDCTL_DSP_RESET: - audio_mode[dev] = AM_NONE; - return DMAbuf_ioctl (dev, cmd, arg, 0); - break; + struct audio_operations *dsp_dev = audio_devs[dev]; - case SNDCTL_DSP_GETFMTS: - return snd_ioctl_return ((int *) arg, audio_devs[dev]->format_mask | AFMT_MU_LAW); - break; + unsigned i, n; + unsigned sr, nc, sz, bsz; - case SNDCTL_DSP_SETFMT: - return snd_ioctl_return ((int *) arg, set_format (dev, get_user ((int *) arg))); + sr = dsp_dev->d->set_speed(dev, 0); + nc = dsp_dev->d->set_channels(dev, 0); + sz = dsp_dev->d->set_bits(dev, 0); - case SNDCTL_DSP_GETISPACE: - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return 0; - if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - return -(EBUSY); + if (sz == 8) + dmap->neutral_byte = NEUTRAL8; + else + dmap->neutral_byte = NEUTRAL16; + if (sr < 1 || nc < 1 || sz < 1) { - audio_buf_info info; - - int err = DMAbuf_ioctl (dev, cmd, (caddr_t) & info, 1); - - if (err < 0) - return err; - - memcpy_tofs (&((char *) arg)[0], (char *) &info, sizeof (info)); - return 0; +/* 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; } - - case SNDCTL_DSP_GETOSPACE: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EPERM; - if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - return -(EBUSY); - + + 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 { - audio_buf_info info; - char *dma_buf; - int buf_no, buf_ptr, buf_size; + /* + * 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; + } - int err = DMAbuf_ioctl (dev, cmd, (caddr_t) & info, 1); + 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 - if (err < 0) - return err; + n = dmap->buffsize / bsz; + if (n > MAX_SUB_BUFFERS) + n = MAX_SUB_BUFFERS; + if (n > dmap->max_fragments) + n = dmap->max_fragments; - if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0) - info.bytes -= buf_ptr; + 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 */ - memcpy_tofs (&((char *) arg)[0], (char *) &info, sizeof (info)); - return 0; + 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; } - case SNDCTL_DSP_NONBLOCK: - dev_nblock[dev] = 1; - return 0; - break; + dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY; +} - case SNDCTL_DSP_GETCAPS: +static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact) +{ + if (fact == 0) { - int info = 1; /* Revision level of this ioctl() */ + fact = dmap->subdivision; + if (fact == 0) + fact = 1; + return fact; + } + if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */ + return -EINVAL; - if (audio_devs[dev]->flags & DMA_DUPLEX) - info |= DSP_CAP_DUPLEX; + if (fact > MAX_REALTIME_FACTOR) + return -EINVAL; - if (audio_devs[dev]->coproc) - info |= DSP_CAP_COPROC; + if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) + return -EINVAL; - if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ - info |= DSP_CAP_BATCH; + dmap->subdivision = fact; + return fact; +} - if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */ - info |= DSP_CAP_TRIGGER; +static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact) +{ + int bytes, count; - info |= DSP_CAP_MMAP; + if (fact == 0) + return -EIO; - memcpy_tofs (&((char *) arg)[0], (char *) &info, sizeof (info)); - return 0; - } - break; + if (dmap->subdivision != 0 || + dmap->fragment_size) /* Too late to change */ + return -EINVAL; - default: - return DMAbuf_ioctl (dev, cmd, arg, 0); - } -} + bytes = fact & 0xffff; + count = (fact >> 16) & 0x7fff; -void -audio_init (void) -{ - /* - * NOTE! This routine could be called several times during boot. - */ -} + if (count == 0) + count = MAX_SUB_BUFFERS; + else if (count < MAX_SUB_BUFFERS) + count++; -int -audio_select (int dev, struct fileinfo *file, int sel_type, select_table_handle * wait) -{ - char *dma_buf; - int buf_no, buf_ptr, buf_size; + if (bytes < 4 || bytes > 17) /* <16 || > 512k */ + return -EINVAL; - dev = dev >> 4; + if (count < 2) + return -EINVAL; - switch (sel_type) - { - case SEL_IN: - if (audio_mode[dev] & AM_WRITE && !(audio_devs[dev]->flags & DMA_DUPLEX)) - { - return 0; /* Not recording */ - } + if (audio_devs[dev]->min_fragment > 0) + if (bytes < audio_devs[dev]->min_fragment) + bytes = audio_devs[dev]->min_fragment; - return DMAbuf_select (dev, file, sel_type, wait); - break; + if (audio_devs[dev]->max_fragment > 0) + if (bytes > audio_devs[dev]->max_fragment) + bytes = audio_devs[dev]->max_fragment; - case SEL_OUT: - if (audio_mode[dev] & AM_READ && !(audio_devs[dev]->flags & DMA_DUPLEX)) - { - return 0; /* Wrong direction */ - } +#ifdef OS_DMA_MINBITS + if (bytes < OS_DMA_MINBITS) + bytes = OS_DMA_MINBITS; +#endif - if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0) - { - return 1; /* There is space in the current buffer */ - } + dmap->fragment_size = (1 << bytes); + dmap->max_fragments = count; - return DMAbuf_select (dev, file, sel_type, wait); - break; + if (dmap->fragment_size > dmap->buffsize) + dmap->fragment_size = dmap->buffsize; - case SEL_EX: - return 0; - } + if (dmap->fragment_size == dmap->buffsize && + audio_devs[dev]->flags & DMA_AUTOMODE) + dmap->fragment_size /= 2; /* Needs at least 2 buffers */ - return 0; + dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ + return bytes | ((count - 1) << 16); } - -#endif +int dma_ioctl(int dev, unsigned int cmd, caddr_t 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 *)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 *)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; + save_flags(flags); + cli(); + changed = audio_devs[dev]->enable_bits ^ bits; + if ((changed & bits) & PCM_ENABLE_INPUT && 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) + return -err; + dmap_in->dma_mode = DMODE_INPUT; + audio_devs[dev]->enable_bits = bits; + DMAbuf_activate_recording(dev, dmap_in); + } + if ((changed & bits) & PCM_ENABLE_OUTPUT && + (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 = bits; + dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; + DMAbuf_launch_output(dev, dmap_out); + } + audio_devs[dev]->enable_bits = bits; +#if 0 + if (changed && audio_devs[dev]->d->trigger) + audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go); +#endif + restore_flags(flags); + /* 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; + save_flags(flags); + cli(); + 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 */ + restore_flags(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; + + save_flags(flags); + cli(); + 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 */ + restore_flags(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; + } + save_flags(flags); + cli(); + /* 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; + /* Substract current count from the number of bytes written by app */ + count = dmap_out->user_counter - count; + if (count < 0) + count = 0; + restore_flags (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 *)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 *)arg); +} diff --git a/drivers/sound/audio_syms.c b/drivers/sound/audio_syms.c new file mode 100644 index 000000000000..62d406150526 --- /dev/null +++ b/drivers/sound/audio_syms.c @@ -0,0 +1,25 @@ +/* + * Exported symbols for audio driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include + +char audio_syms_symbol; + +#include "sound_config.h" +#include "sound_calls.h" + +struct symbol_table audio_symbol_table = { +#include + X(DMAbuf_start_dma), + X(DMAbuf_open_dma), + X(DMAbuf_close_dma), + X(DMAbuf_inputintr), + X(DMAbuf_outputintr), + X(dma_ioctl), + X(audio_open), + X(audio_release), +#include +}; \ No newline at end of file diff --git a/drivers/sound/bin2hex.c b/drivers/sound/bin2hex.c new file mode 100644 index 000000000000..fc49c99d9fd1 --- /dev/null +++ b/drivers/sound/bin2hex.c @@ -0,0 +1,37 @@ +#include + +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( "};\n#define %sLen %d\n", varname, i ); + return 0; +} diff --git a/drivers/sound/configure.c b/drivers/sound/configure.c deleted file mode 100644 index 2a64fd8d2b24..000000000000 --- a/drivers/sound/configure.c +++ /dev/null @@ -1,1602 +0,0 @@ -/* - * PnP soundcard support is not included in this version. - * - * AEDSP16 will not work without significant changes. - */ -#define DISABLED_OPTIONS (B(OPT_SPNP)|B(OPT_AEDSP16)|B(OPT_UNUSED1)|B(OPT_UNUSED2)) -/* - * sound/configure.c - Configuration program for the Linux Sound Driver - */ -/* - * 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. - */ - - -#include -#include -#include -#include -#include - -#define B(x) (1 << (x)) - -/* - * Option numbers - */ - -#define OPT_PAS 0 -#define OPT_SB 1 -#define OPT_ADLIB 2 -#define OPT_LAST_MUTUAL 2 - -#define OPT_GUS 3 -#define OPT_MPU401 4 -#define OPT_UART6850 5 -#define OPT_PSS 6 -#define OPT_GUS16 7 -#define OPT_GUSMAX 8 -#define OPT_MSS 9 -#define OPT_SSCAPE 10 -#define OPT_TRIX 11 -#define OPT_MAD16 12 -#define OPT_CS4232 13 -#define OPT_MAUI 14 -#define OPT_SPNP 15 - -#define OPT_HIGHLEVEL 16 /* This must be same than the next one */ -#define OPT_UNUSED1 16 -#define OPT_UNUSED2 17 -#define OPT_AEDSP16 18 -#define OPT_AUDIO 19 -#define OPT_MIDI_AUTO 20 -#define OPT_MIDI 21 -#define OPT_YM3812_AUTO 22 -#define OPT_YM3812 23 -#define OPT_LAST 23 /* Last defined OPT number */ - -#define DUMMY_OPTS (B(OPT_MIDI_AUTO)|B(OPT_YM3812_AUTO)) - -#define ANY_DEVS (B(OPT_AUDIO)|B(OPT_MIDI)|B(OPT_GUS)| \ - B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)| \ - B(OPT_MSS)|B(OPT_SSCAPE)|B(OPT_UART6850)|B(OPT_TRIX)| \ - B(OPT_MAD16)|B(OPT_CS4232)|B(OPT_MAUI)|B(OPT_ADLIB)) -#define AUDIO_CARDS (B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_GUS) | \ - B (OPT_MSS) | B (OPT_GUS16) | B (OPT_GUSMAX) | B (OPT_TRIX) | \ - B (OPT_SSCAPE)| B(OPT_MAD16) | B(OPT_CS4232)) -#define MPU_DEVS (B(OPT_PSS)|\ - B(OPT_CS4232)|B(OPT_SPNP)|B(OPT_MAUI)|B(OPT_SSCAPE)) -#define UART401_DEVS (SBDSP_DEVS|B(OPT_TRIX)|B(OPT_MAD16)) -#define MIDI_CARDS (MPU_DEVS | UART401_DEVS | \ - B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_MPU401) | \ - B (OPT_GUS) | B (OPT_TRIX) | B (OPT_SSCAPE)|B(OPT_MAD16) | \ - B (OPT_CS4232)|B(OPT_MAUI)) -#define AD1848_DEVS (B(OPT_GUS16)|B(OPT_MSS)|B(OPT_PSS)|B(OPT_GUSMAX)|\ - B(OPT_SSCAPE)|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_CS4232)|\ - B(OPT_SPNP)) -#define SBDSP_DEVS (B(OPT_SB)|B(OPT_SPNP)|B(OPT_MAD16)|B(OPT_TRIX)) -#define SEQUENCER_DEVS (OPT_MIDI|OPT_YM3812|OPT_ADLIB|OPT_GUS|OPT_MAUI|MIDI_CARDS) -/* - * Options that have been disabled for some reason (incompletely implemented - * and/or tested). Don't remove from this list before looking at file - * experimental.txt for further info. - */ - -typedef struct - { - unsigned long conditions; - unsigned long exclusive_options; - char macro[20]; - int verify; - int alias; - int default_answ; - } - -hw_entry; - - -/* - * The rule table for the driver options. The first field defines a set of - * options which must be selected before this entry can be selected. The - * second field is a set of options which are not allowed with this one. If - * the fourth field is zero, the option is selected without asking - * confirmation from the user. - * - * With this version of the rule table it is possible to select just one type of - * hardware. - * - * NOTE! Keep the following table and the questions array in sync with the - * option numbering! - */ - -hw_entry hw_table[] = -{ -/* - * 0 - */ - {0, 0, "PAS", 1, 0, 0}, - {0, 0, "SB", 1, 0, 0}, - {0, B (OPT_PAS) | B (OPT_SB), "ADLIB", 1, 0, 0}, - - {0, 0, "GUS", 1, 0, 0}, - {0, 0, "MPU401", 1, 0, 0}, - {0, 0, "UART6850", 1, 0, 0}, - {0, 0, "PSS", 1, 0, 0}, - {B (OPT_GUS), 0, "GUS16", 1, 0, 0}, - {B (OPT_GUS), B (OPT_GUS16), "GUSMAX", 1, 0, 0}, - {0, 0, "MSS", 1, 0, 0}, - {0, 0, "SSCAPE", 1, 0, 0}, - {0, 0, "TRIX", 1, 0, 0}, - {0, 0, "MAD16", 1, 0, 0}, - {0, 0, "CS4232", 1, 0, 0}, - {0, 0, "MAUI", 1, 0, 0}, - {0, 0, "SPNP", 1, 0, 0}, - - {B (OPT_SB), B (OPT_PAS), "UNUSED1", 1, 0, 1}, - {B (OPT_SB) | B (OPT_UNUSED1), B (OPT_PAS), "UNUSED2", 1, 0, 1}, - {B (OPT_UNUSED1) | B (OPT_MSS) | B (OPT_MPU401), 0, "AEDSP16", 1, 0, 0}, - {AUDIO_CARDS, 0, "AUDIO", 1, 0, 1}, - {B (OPT_MPU401) | B (OPT_MAUI), 0, "MIDI_AUTO", 0, OPT_MIDI, 0}, - {MIDI_CARDS, 0, "MIDI", 1, 0, 1}, - {B (OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812, 0}, - {B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_ADLIB) | B (OPT_MSS) | B (OPT_PSS), B (OPT_YM3812_AUTO), "YM3812", 1, 0, 1} -}; - -char *questions[] = -{ - "ProAudioSpectrum 16 support", - "Sound Blaster (SB, SBPro, SB16, clones) support", - "Generic OPL2/OPL3 FM synthesizer support", - "Gravis Ultrasound support", - "MPU-401 support (NOT for SB16)", - "6850 UART Midi support", - "PSS (ECHO-ADI2111) support", - "16 bit sampling option of GUS (_NOT_ GUS MAX)", - "GUS MAX support", - "Microsoft Sound System support", - "Ensoniq SoundScape support", - "MediaTrix AudioTrix Pro support", - "Support for MAD16 and/or Mozart based cards", - "Support for Crystal CS4232 based (PnP) cards", - "Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers", - "Support for PnP sound cards (_EXPERIMENTAL_)", - - "*** Unused option 1 ***", - "*** Unused option 2 ***", - "Audio Excel DSP 16 initialization support", - "/dev/dsp and /dev/audio support", - "This should not be asked", - "MIDI interface support", - "This should not be asked", - "FM synthesizer (YM3812/OPL-3) support", - "Is the sky really falling" -}; - -/* help text for each option */ -char *help[] = -{ - "Enable this option only if you have a Pro Audio Spectrum 16,\n" - "Pro Audio Studio 16, or Logitech SoundMan 16. Don't enable this if\n" - "you have some other card made by MediaVision or Logitech as\n" - "they are not PAS16 compatible.\n", - - "Enable this if you have an original Sound Blaster card made by\n" - "Creative Labs or a 100%% hardware compatible clone. For an\n" - "unknown card you may want to try this if it claims to be\n" - "Sound Blaster compatible.\n", - - "Enable this option if your sound card has a Yamaha OPL2 or OPL3\n" - "FM synthesizer chip.\n", - - "Enable this option for any type of Gravis Ultrasound card\n" - "including the GUS or GUS MAX.\n", - - "The MPU401 interface is supported by almost all sound cards. However,\n" - "some natively supported cards have their own driver for\n" - "MPU401. Enabling the MPU401 option with these cards will cause a\n" - "conflict. Also enabling MPU401 on a system that doesn't really have a\n" - "MPU401 could cause some trouble. It's safe to enable this if you have a\n" - "true MPU401 MIDI interface card.\n", - - "This option enables support for MIDI interfaces based on the 6850\n" - "UART chip. This interface is rarely found on sound cards.\n", - - "Enable this option if you have an Orchid SW32, Cardinal DSP16 or other\n" - "sound card based on the PSS chipset (AD1848 codec, ADSP-2115 DSP chip,\n" - "and Echo ESC614 ASIC CHIP).\n", - - "Enable this if you have installed the 16-bit sampling daughtercard on\n" - "your GUS card. Do not use if you have a GUS MAX as enabling this option\n" - "disables GUS MAX support.\n", - - "Enable this option if you have a Gravis Ultrasound MAX sound\n" - "card\n", - - "Enable this option if you have the original Windows Sound System\n" - "card made by Microsoft or the Aztech SG 16 Pro or NX16 Pro.\n", - - "Enable this if you have a sound card based on the Ensoniq\n" - "SoundScape chipset. Such cards are being manufactured by Ensoniq,\n" - "Spea and Reveal (Reveal makes other cards as well).\n", - - "Enable this option if you have the AudioTrix Pro sound card\n" - "manufactured by MediaTrix.\n", - - "Enable this if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi\n" - "82C928 or 82C929) audio interface chip. These chips are currently\n" - "quite common so it's possible that many no-name cards have one of\n" - "them. In addition the MAD16 chip is used in some cards made by known\n" - "manufacturers such as Turtle Beach (Tropez), Reveal (some models) and\n" - "Diamond (latest ones).\n", - - "Enable this if you have a card based on the Crystal CS4232 chip set.\n", - - "Enable this option if you have a Turtle Beach Wave Front, Maui,\n" - "or Tropez sound card.\n", - - "Use this option to enable experimental support for cards that\n" - "use the Plug and Play protocol.\n", - - "Enable this option if your card is a Sound Blaster Pro or\n" - "Sound Blaster 16. It also works with many Sound Blaster Pro clones.\n", - - "Enable this if you have a Sound Blaster 16, including the AWE32.\n", - - "Enable this if you have an Audio Excel DSP16 card. See the file\n" - "Readme.aedsp16 for more information.\n", - - "This option enables the A/D and D/A converter (PCM) devices\n" - "supported by almost all sound cards.\n", - - "This should not be asked", - - "This enables the dev/midixx devices and access to any MIDI ports\n" - "using /dev/sequencer and /dev/music. This option also affects any\n" - "MPU401 and/or General MIDI compatible devices.\n", - - "This should not be asked", - - "This enables the Yamaha FM synthesizer chip used on many sound\n" - "cards.\n", - - "Is the sky really falling" -}; - -struct kludge - { - char *name; - int mask; - } -extra_options[] = -{ - { - "MPU_EMU", MPU_DEVS - } - , - { - "AD1848", AD1848_DEVS - } - , - { - "SBDSP", SBDSP_DEVS - } - , - { - "UART401", UART401_DEVS - } - , - { - "SEQUENCER", SEQUENCER_DEVS - } - , - { - NULL, 0 - } -}; - -char *oldconf = "/etc/soundconf"; - -int old_config_used = 0; -int def_size, sb_base = 0; - -unsigned long selected_options = 0; -int sb_dma = 0; - -int dump_only = 0; - -void build_defines (void); - -#include "hex2hex.h" -int bin2hex (char *path, char *target, char *varname); - -int -can_select_option (int nr) -{ - - if (hw_table[nr].conditions) - if (!(hw_table[nr].conditions & selected_options)) - return 0; - - if (hw_table[nr].exclusive_options) - if (hw_table[nr].exclusive_options & selected_options) - return 0; - - if (DISABLED_OPTIONS & B (nr)) - return 0; - - return 1; -} - -int -think_positively (char *prompt, int def_answ, char *help) -{ - char answ[512]; - int len; - -response: - fprintf (stderr, prompt); - if (def_answ) - fprintf (stderr, " [Y/n/?] "); - else - fprintf (stderr, " [N/y/?] "); - - if ((len = read (0, answ, sizeof (answ))) < 1) - { - fprintf (stderr, "\n\nERROR! Cannot read stdin\n"); - - perror ("stdin"); - printf ("invalid_configuration__run_make_config_again\n"); - exit (-1); - } - - if (len < 2) /* - * There is an additional LF at the end - */ - return def_answ; - - if (answ[0] == '?') - { /* display help message */ - fprintf (stderr, "\n"); - fprintf (stderr, help); - fprintf (stderr, "\n"); - goto response; - } - - answ[len - 1] = 0; - - if (!strcmp (answ, "y") || !strcmp (answ, "Y")) - return 1; - - return 0; -} - -int -ask_value (char *format, int default_answer) -{ - char answ[512]; - int len, num; - -play_it_again_Sam: - - if ((len = read (0, answ, sizeof (answ))) < 1) - { - fprintf (stderr, "\n\nERROR! Cannot read stdin\n"); - - perror ("stdin"); - printf ("invalid_configuration__run_make_config_again\n"); - exit (-1); - } - - if (len < 2) /* - * There is an additional LF at the end - */ - return default_answer; - - answ[len - 1] = 0; - - if (sscanf (answ, format, &num) != 1) - { - fprintf (stderr, "Illegal format. Try again: "); - goto play_it_again_Sam; - } - - return num; -} - -#define FMT_HEX 1 -#define FMT_INT 2 - -void -ask_int_choice (int mask, char *macro, - char *question, - int format, - int defa, - char *choices) -{ - int num, i; - - if (dump_only) - { - - for (i = 0; i < OPT_LAST; i++) - if (mask == B (i)) - { - unsigned int j; - - for (j = 0; j < strlen (choices); j++) - if (choices[j] == '\'') - choices[j] = '_'; - - printf ("\nif [ \"$CONFIG_%s\" = \"y\" ]; then\n", - hw_table[i].macro); - if (format == FMT_INT) - printf ("int '%s %s' %s %d\n", question, choices, macro, defa); - else - printf ("hex '%s %s' %s %x\n", question, choices, macro, defa); - printf ("fi\n"); - } - } - else - { - if (!(mask & selected_options)) - return; - - fprintf (stderr, "\n%s\n", question); - if (strcmp (choices, "")) - fprintf (stderr, "Possible values are: %s\n", choices); - - if (format == FMT_INT) - { - if (defa == -1) - fprintf (stderr, "\t(-1 disables this feature)\n"); - fprintf (stderr, "The default value is %d\n", defa); - fprintf (stderr, "Enter the value: "); - num = ask_value ("%d", defa); - if (num == -1) - return; - fprintf (stderr, "%s set to %d.\n", question, num); - printf ("#define %s %d\n", macro, num); - } - else - { - if (defa == 0) - fprintf (stderr, "\t(0 disables this feature)\n"); - fprintf (stderr, "The default value is %x\n", defa); - fprintf (stderr, "Enter the value: "); - num = ask_value ("%x", defa); - if (num == 0) - return; - fprintf (stderr, "%s set to %x.\n", question, num); - printf ("#define %s 0x%x\n", macro, num); - } - } -} - -void -rebuild_file (char *line) -{ - char *method, *next, *old, *var, *p; - - method = p = line; - - while (*p && *p != ' ') - p++; - *p++ = 0; - - old = p; - while (*p && *p != ' ') - p++; - *p++ = 0; - - next = p; - while (*p && *p != ' ') - p++; - *p++ = 0; - - var = p; - while (*p && *p != ' ') - p++; - *p++ = 0; - - fprintf (stderr, "Rebuilding file `%s' (%s %s)\n", next, method, old); - - if (strcmp (method, "bin2hex") == 0) - { - if (!bin2hex (old, next, var)) - { - fprintf (stderr, "Rebuild failed\n"); - exit (-1); - } - } - else if (strcmp (method, "hex2hex") == 0) - { - if (!hex2hex (old, next, var)) - { - fprintf (stderr, "Rebuild failed\n"); - exit (-1); - } - } - else - { - fprintf (stderr, "Failed to build `%s' - unknown method %s\n", - next, method); - exit (-1); - } -} - -int -use_old_config (char *filename) -{ - char buf[1024]; - int i = 0; - - FILE *oldf; - - fprintf (stderr, "Copying old configuration from `%s'\n", filename); - - if ((oldf = fopen (filename, "r")) == NULL) - { - fprintf (stderr, "Couldn't open previous configuration file\n"); - perror (filename); - return 0; - } - - while (fgets (buf, 1024, oldf) != NULL) - { - char tmp[100]; - - if (buf[0] != '#') - { - printf ("%s", buf); - - strncpy (tmp, buf, 8); - tmp[8] = 0; - - if (strcmp (tmp, "/*build ") == 0) - rebuild_file (&buf[8]); - - continue; - } - - strncpy (tmp, buf, 8); - tmp[8] = 0; - - if (strcmp (tmp, "#define ") == 0) - { - char *id = &buf[8]; - - i = 0; - while (id[i] && id[i] != ' ' && - id[i] != '\t' && id[i] != '\n') - i++; - - strncpy (tmp, id, i); - tmp[i] = 0; - - if (strcmp (tmp, "SELECTED_SOUND_OPTIONS") == 0) - continue; - - if (strcmp (tmp, "KERNEL_SOUNDCARD") == 0) - continue; - - if (strcmp (tmp, "JAZZ_DMA16") == 0) /* Rename it (hack) */ - { - printf ("#define SB_DMA2 %s\n", - &buf[18]); - continue; - } - - if (strcmp (tmp, "SB16_DMA") == 0) /* Rename it (hack) */ - { - printf ("#define SB_DMA2 %s\n", - &buf[16]); - continue; - } - - tmp[8] = 0; /* Truncate the string */ - if (strcmp (tmp, "EXCLUDE_") == 0) - continue; /* Skip excludes */ - - strncpy (tmp, id, i); - tmp[7] = 0; /* Truncate the string */ - - if (strcmp (tmp, "CONFIG_") == 0) - { - strncpy (tmp, &id[7], i - 7); - tmp[i - 7] = 0; - - for (i = 0; i <= OPT_LAST; i++) - if (strcmp (hw_table[i].macro, tmp) == 0) - { - selected_options |= (1 << i); - break; - } - continue; - } - - printf ("%s", buf); - continue; - } - - if (strcmp (tmp, "#undef ") == 0) - { - char *id = &buf[8]; - - i = 0; - while (id[i] && id[i] != ' ' && - id[i] != '\t' && id[i] != '\n') - i++; - - strncpy (tmp, id, i); - tmp[7] = 0; /* Truncate the string */ - if (strcmp (tmp, "CONFIG_") == 0) - continue; - - strncpy (tmp, id, i); - - tmp[8] = 0; /* Truncate the string */ - if (strcmp (tmp, "EXCLUDE_") != 0) - continue; /* Not a #undef EXCLUDE_ line */ - strncpy (tmp, &id[8], i - 8); - tmp[i - 8] = 0; - - for (i = 0; i <= OPT_LAST; i++) - if (strcmp (hw_table[i].macro, tmp) == 0) - { - selected_options |= (1 << i); - break; - } - continue; - } - - printf ("%s", buf); - } - fclose (oldf); - - for (i = 0; i <= OPT_LAST; i++) - if (!hw_table[i].alias) - if (selected_options & B (i)) - printf ("#define CONFIG_%s\n", hw_table[i].macro); - else - printf ("#undef CONFIG_%s\n", hw_table[i].macro); - - - printf ("\n"); - - i = 0; - - while (extra_options[i].name != NULL) - { - if (selected_options & extra_options[i].mask) - printf ("#define CONFIG_%s\n", extra_options[i].name); - else - printf ("#undef CONFIG_%s\n", extra_options[i].name); - i++; - } - - printf ("\n"); - - printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options); - fprintf (stderr, "Old configuration copied.\n"); - -#if defined(linux) || defined(Solaris) - build_defines (); -#endif - old_config_used = 1; - return 1; -} - -#if defined(linux) || defined(Solaris) -void -build_defines (void) -{ - FILE *optf; - int i; - - if ((optf = fopen (".defines", "w")) == NULL) - { - perror (".defines"); - exit (-1); - } - - - for (i = 0; i <= OPT_LAST; i++) - if (!hw_table[i].alias) - if (selected_options & B (i)) - fprintf (optf, "CONFIG_%s=y\n", hw_table[i].macro); - - - fprintf (optf, "\n"); - - i = 0; - - while (extra_options[i].name != NULL) - { - if (selected_options & extra_options[i].mask) - fprintf (optf, "CONFIG_%s=y\n", extra_options[i].name); - i++; - } - - fprintf (optf, "\n"); - fclose (optf); -} -#endif - -void -ask_parameters (void) -{ - int num; - - build_defines (); - /* - * IRQ and DMA settings - */ - -#if 0 /* Disable this broken question. */ - ask_int_choice (B (OPT_AEDSP16), "AEDSP16_BASE", - "I/O base for Audio Excel DSP 16", - FMT_HEX, - 0x220, - "220 or 240"); -#endif - - ask_int_choice (B (OPT_SB), "SBC_BASE", - "I/O base for SB", - FMT_HEX, - 0x220, - "Check from manual of the card"); - - ask_int_choice (B (OPT_SB), "SBC_IRQ", - "Sound Blaster IRQ", - FMT_INT, - 7, - "Check from manual of the card"); - - ask_int_choice (B (OPT_SB), "SBC_DMA", - "Sound Blaster DMA", - FMT_INT, - 1, - "0, 1 or 3"); - - ask_int_choice (B (OPT_SB), "SB_DMA2", - "Sound Blaster 16 bit DMA (_REQUIRED_for SB16, Jazz16, SMW)", - FMT_INT, - 5, - "5, 6 or 7 (use 1 for 8 bit cards)"); - - ask_int_choice (B (OPT_SB), "SB_MPU_BASE", - "MPU401 I/O base of SB16, Jazz16 and ES1688", - FMT_HEX, - 0, - "Check from manual of the card"); - - ask_int_choice (B (OPT_SB), "SB_MPU_IRQ", - "SB MPU401 IRQ (Jazz16, SM Wave and ES1688)", - FMT_INT, - -1, - "Use -1 with SB16"); - - ask_int_choice (B (OPT_PAS), "PAS_IRQ", - "PAS16 IRQ", - FMT_INT, - 10, - "3, 4, 5, 7, 9, 10, 11, 12, 14 or 15"); - - ask_int_choice (B (OPT_PAS), "PAS_DMA", - "PAS16 DMA", - FMT_INT, - 3, - "0, 1, 3, 5, 6 or 7"); - - if (selected_options & B (OPT_PAS)) - { - if (think_positively ("Enable Joystick port on ProAudioSpectrum", 0, - "Enable this option if you want to use the joystick port provided\n" - "on the PAS sound card.\n")) - printf ("#define PAS_JOYSTICK_ENABLE\n"); - - if (think_positively ("Enable PAS16 bus clock option", 0, - "The PAS16 can be noisy with some motherboards. There is a command\n" - "line switch (:T?) in the DOS driver for PAS16 which solves this.\n" - "Don't enable this feature unless you have problems and have to use\n" - "this switch with DOS\n")) - printf ("#define BROKEN_BUS_CLOCK\n"); - - if (think_positively ("Disable SB mode of PAS16", 0, - "You should disable SB emulation of PAS16 if you want to use\n" - "Another SB compatible card in the same system\n")) - printf ("#define DISABLE_SB_EMULATION\n"); - } - - ask_int_choice (B (OPT_GUS), "GUS_BASE", - "I/O base for GUS", - FMT_HEX, - 0x220, - "210, 220, 230, 240, 250 or 260"); - - - ask_int_choice (B (OPT_GUS), "GUS_IRQ", - "GUS IRQ", - FMT_INT, - 15, - "3, 5, 7, 9, 11, 12 or 15"); - - ask_int_choice (B (OPT_GUS), "GUS_DMA", - "GUS DMA", - FMT_INT, - 6, - "1, 3, 5, 6 or 7"); - - ask_int_choice (B (OPT_GUS), "GUS_DMA2", - "Second DMA channel for GUS", - FMT_INT, - -1, - "1, 3, 5, 6 or 7"); - - ask_int_choice (B (OPT_GUS16), "GUS16_BASE", - "I/O base for the 16 bit daughtercard of GUS", - FMT_HEX, - 0x530, - "530, 604, E80 or F40"); - - - ask_int_choice (B (OPT_GUS16), "GUS16_IRQ", - "GUS 16 bit daughtercard IRQ", - FMT_INT, - 7, - "3, 4, 5, 7, or 9"); - - ask_int_choice (B (OPT_GUS16), "GUS16_DMA", - "GUS DMA", - FMT_INT, - 3, - "0, 1 or 3"); - - ask_int_choice (B (OPT_MPU401), "MPU_BASE", - "I/O base for MPU401", - FMT_HEX, - 0x330, - "Check from manual of the card"); - - ask_int_choice (B (OPT_MPU401), "MPU_IRQ", - "MPU401 IRQ", - FMT_INT, - 9, - "Check from manual of the card"); - - ask_int_choice (B (OPT_MAUI), "MAUI_BASE", - "I/O base for Maui", - FMT_HEX, - 0x330, - "210, 230, 260, 290, 300, 320, 338 or 330"); - - ask_int_choice (B (OPT_MAUI), "MAUI_IRQ", - "Maui IRQ", - FMT_INT, - 9, - "5, 9, 12 or 15"); - - ask_int_choice (B (OPT_UART6850), "U6850_BASE", - "I/O base for UART 6850 MIDI port", - FMT_HEX, - 0, - "(Unknown)"); - - ask_int_choice (B (OPT_UART6850), "U6850_IRQ", - "UART6850 IRQ", - FMT_INT, - -1, - "(Unknown)"); - - ask_int_choice (B (OPT_PSS), "PSS_BASE", - "PSS I/O base", - FMT_HEX, - 0x220, - "220 or 240"); - - ask_int_choice (B (OPT_PSS), "PSS_MSS_BASE", - "PSS audio I/O base", - FMT_HEX, - 0x530, - "530, 604, E80 or F40"); - - ask_int_choice (B (OPT_PSS), "PSS_MSS_IRQ", - "PSS audio IRQ", - FMT_INT, - 11, - "7, 9, 10 or 11"); - - ask_int_choice (B (OPT_PSS), "PSS_MSS_DMA", - "PSS audio DMA", - FMT_INT, - 3, - "0, 1 or 3"); - - ask_int_choice (B (OPT_PSS), "PSS_MPU_BASE", - "PSS MIDI I/O base", - FMT_HEX, - 0x330, - ""); - - ask_int_choice (B (OPT_PSS), "PSS_MPU_IRQ", - "PSS MIDI IRQ", - FMT_INT, - 9, - "3, 4, 5, 7 or 9"); - - ask_int_choice (B (OPT_MSS), "MSS_BASE", - "MSS/WSS I/O base", - FMT_HEX, - 0x530, - "530, 604, E80 or F40"); - - ask_int_choice (B (OPT_MSS), "MSS_IRQ", - "MSS/WSS IRQ", - FMT_INT, - 11, - "7, 9, 10 or 11"); - - ask_int_choice (B (OPT_MSS), "MSS_DMA", - "MSS/WSS DMA", - FMT_INT, - 3, - "0, 1 or 3"); - - ask_int_choice (B (OPT_SSCAPE), "SSCAPE_BASE", - "SoundScape MIDI I/O base", - FMT_HEX, - 0x330, - "320, 330, 340 or 350"); - - ask_int_choice (B (OPT_SSCAPE), "SSCAPE_IRQ", - "SoundScape MIDI IRQ", - FMT_INT, - 9, - ""); - - ask_int_choice (B (OPT_SSCAPE), "SSCAPE_DMA", - "SoundScape initialization DMA", - FMT_INT, - 3, - "0, 1 or 3"); - - ask_int_choice (B (OPT_SSCAPE), "SSCAPE_MSS_BASE", - "SoundScape audio I/O base", - FMT_HEX, - 0x534, - "534, 608, E84 or F44"); - - ask_int_choice (B (OPT_SSCAPE), "SSCAPE_MSS_IRQ", - "SoundScape audio IRQ", - FMT_INT, - 11, - "7, 9, 10 or 11"); - - - if (selected_options & B (OPT_SSCAPE)) - { - int reveal_spea; - - reveal_spea = think_positively ( - "Is your SoundScape card made/marketed by Reveal or Spea", - 0, - "Enable if you have a SoundScape card with the Reveal or\n" - "Spea name on it.\n"); - if (reveal_spea) - printf ("#define REVEAL_SPEA\n"); - - } - - ask_int_choice (B (OPT_TRIX), "TRIX_BASE", - "AudioTrix audio I/O base", - FMT_HEX, - 0x530, - "530, 604, E80 or F40"); - - ask_int_choice (B (OPT_TRIX), "TRIX_IRQ", - "AudioTrix audio IRQ", - FMT_INT, - 11, - "7, 9, 10 or 11"); - - ask_int_choice (B (OPT_TRIX), "TRIX_DMA", - "AudioTrix audio DMA", - FMT_INT, - 0, - "0, 1 or 3"); - - ask_int_choice (B (OPT_TRIX), "TRIX_DMA2", - "AudioTrix second (duplex) DMA", - FMT_INT, - 3, - "0, 1 or 3"); - - ask_int_choice (B (OPT_TRIX), "TRIX_MPU_BASE", - "AudioTrix MIDI I/O base", - FMT_HEX, - 0x330, - "330, 370, 3B0 or 3F0"); - - ask_int_choice (B (OPT_TRIX), "TRIX_MPU_IRQ", - "AudioTrix MIDI IRQ", - FMT_INT, - 9, - "3, 4, 5, 7 or 9"); - - ask_int_choice (B (OPT_TRIX), "TRIX_SB_BASE", - "AudioTrix SB I/O base", - FMT_HEX, - 0x220, - "220, 210, 230, 240, 250, 260 or 270"); - - ask_int_choice (B (OPT_TRIX), "TRIX_SB_IRQ", - "AudioTrix SB IRQ", - FMT_INT, - 7, - "3, 4, 5 or 7"); - - ask_int_choice (B (OPT_TRIX), "TRIX_SB_DMA", - "AudioTrix SB DMA", - FMT_INT, - 1, - "1 or 3"); - - ask_int_choice (B (OPT_CS4232), "CS4232_BASE", - "CS4232 audio I/O base", - FMT_HEX, - 0x530, - "530, 604, E80 or F40"); - - ask_int_choice (B (OPT_CS4232), "CS4232_IRQ", - "CS4232 audio IRQ", - FMT_INT, - 11, - "5, 7, 9, 11, 12 or 15"); - - ask_int_choice (B (OPT_CS4232), "CS4232_DMA", - "CS4232 audio DMA", - FMT_INT, - 0, - "0, 1 or 3"); - - ask_int_choice (B (OPT_CS4232), "CS4232_DMA2", - "CS4232 second (duplex) DMA", - FMT_INT, - 3, - "0, 1 or 3"); - - ask_int_choice (B (OPT_CS4232), "CS4232_MPU_BASE", - "CS4232 MIDI I/O base", - FMT_HEX, - 0x330, - "330, 370, 3B0 or 3F0"); - - ask_int_choice (B (OPT_CS4232), "CS4232_MPU_IRQ", - "CS4232 MIDI IRQ", - FMT_INT, - 9, - "5, 7, 9, 11, 12 or 15"); - - ask_int_choice (B (OPT_MAD16), "MAD16_BASE", - "MAD16 audio I/O base", - FMT_HEX, - 0x530, - "530, 604, E80 or F40"); - - ask_int_choice (B (OPT_MAD16), "MAD16_IRQ", - "MAD16 audio IRQ", - FMT_INT, - 11, - "7, 9, 10 or 11"); - - ask_int_choice (B (OPT_MAD16), "MAD16_DMA", - "MAD16 audio DMA", - FMT_INT, - 3, - "0, 1 or 3"); - - ask_int_choice (B (OPT_MAD16), "MAD16_DMA2", - "MAD16 second (duplex) DMA", - FMT_INT, - 0, - "0, 1 or 3"); - - ask_int_choice (B (OPT_MAD16), "MAD16_MPU_BASE", - "MAD16 MIDI I/O base", - FMT_HEX, - 0x330, - "300, 310, 320 or 330 (0 disables)"); - - ask_int_choice (B (OPT_MAD16), "MAD16_MPU_IRQ", - "MAD16 MIDI IRQ", - FMT_INT, - 9, - "5, 7, 9 or 10"); - ask_int_choice (B (OPT_AUDIO), "DSP_BUFFSIZE", - "Audio DMA buffer size", - FMT_INT, - 65536, - "4096, 16384, 32768 or 65536"); -} - -void -dump_script (void) -{ - int i; - - for (i = 0; i <= OPT_LAST; i++) - if (!(DUMMY_OPTS & B (i))) - if (!(DISABLED_OPTIONS & B (i))) - { - printf ("bool '%s' CONFIG_%s\n", questions[i], hw_table[i].macro); - } - -/* - * Some "hardcoded" options - */ - - dump_only = 1; - selected_options = 0; - ask_parameters (); - - printf ("#\n$MAKE -C drivers/sound kernelconfig || exit 1\n"); -} - -void -dump_fixed_local (void) -{ - int i = 0; - - printf ("/* Computer generated file. Please don't edit! */\n\n"); - printf ("#define KERNEL_COMPATIBLE_CONFIG\n\n"); - printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n\n", selected_options); - - while (extra_options[i].name != NULL) - { - int n = 0, j; - - printf ("#if "); - - for (j = 0; j < OPT_LAST; j++) - if (!(DISABLED_OPTIONS & B (j))) - if (extra_options[i].mask & B (j)) - { - if (n) - printf (" || "); - if (!(n++ % 2)) - printf ("\\\n "); - - printf ("defined(CONFIG_%s)", hw_table[j].macro); - } - - printf ("\n"); - printf ("#\tdefine CONFIG_%s\n", extra_options[i].name); - printf ("#endif\n\n"); - i++; - } -} - -void -dump_fixed_defines (void) -{ - int i = 0; - - printf ("# Computer generated file. Please don't edit\n\n"); - - while (extra_options[i].name != NULL) - { - int j; - - for (j = 0; j < OPT_LAST; j++) - if (!(DISABLED_OPTIONS & B (j))) - if (extra_options[i].mask & B (j)) - { - printf ("ifdef CONFIG_%s\n", hw_table[j].macro); - printf ("CONFIG_%s=y\n", extra_options[i].name); - printf ("endif\n\n"); - } - - i++; - } -} - -int -main (int argc, char *argv[]) -{ - int i, full_driver = 1; - char old_config_file[200]; - - if (getuid () != 0) /* Not root */ - { - char *home; - - if ((home = getenv ("HOME")) != NULL) - { - sprintf (old_config_file, "%s/.soundconf", home); - oldconf = old_config_file; - } - } - - if (argc > 1) - { - if (strcmp (argv[1], "-o") == 0 && - use_old_config (oldconf)) - exit (0); - else if (strcmp (argv[1], "script") == 0) - { - dump_script (); - exit (0); - } - else if (strcmp (argv[1], "fixedlocal") == 0) - { - dump_fixed_local (); - exit (0); - } - else if (strcmp (argv[1], "fixeddefines") == 0) - { - dump_fixed_defines (); - exit (0); - } - } - - fprintf (stderr, "\nConfiguring Sound Support\n\n"); - - if (access (oldconf, R_OK) == 0) - { - char str[255]; - - sprintf (str, "Old configuration exists in `%s'. Use it", oldconf); - if (think_positively (str, 1, - "Enable this option to load the previously saved configuration file\n" - "for all of the sound driver parameters.\n")) - if (use_old_config (oldconf)) - exit (0); - } - - printf ("/*\tGenerated by configure. Don't edit!!!!\t*/\n"); - printf ("/*\tMaking changes to this file is not as simple as it may look.\t*/\n\n"); - printf ("/*\tIf you change the CONFIG_ settings in local.h you\t*/\n"); - printf ("/*\t_have_ to edit .defines too.\t*/\n\n"); - - { - /* - * Partial driver - */ - - full_driver = 0; - - for (i = 0; i <= OPT_LAST; i++) - if (can_select_option (i)) - { - if (!(selected_options & B (i))) /* - * Not selected yet - */ - if (!hw_table[i].verify) - { - if (hw_table[i].alias) - selected_options |= B (hw_table[i].alias); - else - selected_options |= B (i); - } - else - { - int def_answ = hw_table[i].default_answ; - - if (think_positively (questions[i], def_answ, help[i])) - if (hw_table[i].alias) - selected_options |= B (hw_table[i].alias); - else - selected_options |= B (i); - } - } - } - - if (selected_options & B (OPT_SB)) - { - if (think_positively ( - "Support for the SG NX Pro mixer", 0, - "Enable this if you want to support the additional mixer functions\n" - "provided on Sound Galaxy NX Pro sound cards.\n")) - printf ("#define __SGNXPRO__\n"); - } - - if (selected_options & B (OPT_SB)) - { - if (think_positively ("Support for the MV Jazz16 (ProSonic etc.)", 0, - "Enable this if you have an MV Jazz16 or ProSonic sound card.\n")) - { - if (think_positively ("Do you have SoundMan Wave", 0, - "Enable this option of you have the Logitech SoundMan Wave sound card.\n")) - { - printf ("#define SM_WAVE\n"); - - midi0001_again: - if (think_positively ( - "Do you have access to the MIDI0001.BIN file", 1, - "The Logitech SoundMan Wave has a microcontroller which must be\n" - "initialized before MIDI emulation works. This is possible only if the\n" - "microcode file is compiled into the driver.\n")) - { - char path[512]; - - fprintf (stderr, - "Enter full name of the MIDI0001.BIN file (pwd is sound): "); - scanf ("%s", path); - fprintf (stderr, "including microcode file %s\n", path); - - if (!bin2hex (path, "smw-midi0001.h", "smw_ucode")) - { - fprintf (stderr, "Couldn't open file %s\n", - path); - if (think_positively ("Try again with correct path", 1, - "The specified file could not be opened. Enter the correct path to the\n" - "file.\n")) - goto midi0001_again; - } - else - { - printf ("#define SMW_MIDI0001_INCLUDED\n"); - printf ("/*build bin2hex %s smw-midi0001.h smw_ucode */\n", path); - } - } - } - } - } - - if (selected_options & B (OPT_SB)) - { - if (think_positively ("Do you have a Logitech SoundMan Games", 0, - "The Logitech SoundMan Games supports 44 kHz in stereo while the\n" - "standard SB Pro supports just 22 kHz stereo. You have the option of\n" - "enabling SM Games mode. However, enable it only if you are sure that\n" - "your card is an SM Games. Enabling this feature with a plain old SB\n" - "Pro will cause troubles with stereo mode.\n\n" - "DANGER! Read the above once again before answering 'y'\n" - "Answer 'n' if you are unsure what to do!\n")) - printf ("#define SM_GAMES\n"); - } - - if (selected_options & B (OPT_AEDSP16)) - { - int sel1 = 0; - - if (selected_options & B (OPT_SB)) - { - - if (think_positively ( - "Do you want support for the Audio Excel Sound Blaster Pro mode", - 1, - "Enable this option if you want the Audio Excel sound card to operate\n" - "in Sound Blaster Pro mode.\n")) - { - printf ("#define AEDSP16_SBPRO\n"); - sel1 = 1; - } - } - - if ((selected_options & B (OPT_MSS)) && (sel1 == 0)) - { - - if (think_positively ( - "Do you want support for the Audio Excel Microsoft Sound System mode", - 1, - "Enable this option if you want the Audio Excel sound card to operate\n" - "in Microsoft Sound System mode.\n")) - { - printf ("#define AEDSP16_MSS\n"); - sel1 = 1; - } - } - - if (sel1 == 0) - { - printf ("invalid_configuration__run_make_config_again\n"); - fprintf (stderr, "ERROR!!!!!\nYou must select at least one mode when using Audio Excel!\n"); - exit (-1); - } - if (selected_options & B (OPT_MPU401)) - printf ("#define AEDSP16_MPU401\n"); - } - - if (selected_options & B (OPT_PSS)) - { - genld_again: - if (think_positively ("Do you wish to include an LD file", 1, - "If you want to emulate the Sound Blaster card and you have a DSPxxx.LD\n" - "file then you must include the LD in the kernel.\n")) - { - char path[512]; - - fprintf (stderr, - "Enter the path to your LD file (pwd is sound): "); - scanf ("%s", path); - fprintf (stderr, "including LD file %s\n", path); - - if (!bin2hex (path, "synth-ld.h", "pss_synth")) - { - fprintf (stderr, "couldn't open `%s' as the LD file\n", path); - if (think_positively ("try again with correct path", 1, - "The given LD file could not opened.\n")) - goto genld_again; - } - else - { - printf ("#define PSS_HAVE_LD\n"); - printf ("/*build bin2hex %s synth-ld.h pss_synth */\n", path); - } - } - else - { - FILE *sf = fopen ("synth-ld.h", "w"); - - fprintf (sf, "/* automatically generated by configure */\n"); - fprintf (sf, "unsigned char pss_synth[1];\n" - "#define pss_synthLen 0\n"); - fclose (sf); - } - } - - if (selected_options & B (OPT_TRIX)) - { - hex2hex_again: - - if (think_positively ("Do you want to include TRXPRO.HEX in your kernel", - 1, - "The MediaTrix AudioTrix Pro has an on-board microcontroller which\n" - "needs to be initialized by downloading the code from the file TRXPRO.HEX\n" - "in the DOS driver directory. If you don't have the TRXPRO.HEX file handy\n" - "you may skip this step. However, the SB and MPU-401 modes of AudioTrix\n" - "Pro will not work without this file!\n")) - { - char path[512]; - - fprintf (stderr, - "Enter the path to your TRXPRO.HEX file (pwd is sound): "); - scanf ("%s", path); - fprintf (stderr, "including HEX file `%s'\n", path); - - if (!hex2hex (path, "trix_boot.h", "trix_boot")) - goto hex2hex_again; - printf ("/*build hex2hex %s trix_boot.h trix_boot */\n", path); - printf ("#define INCLUDE_TRIX_BOOT\n"); - } - } - - if (selected_options & B (OPT_MSS)) - { - if (think_positively ("Support for builtin sound of Compaq Deskpro XL", 0, - "Enable this if you have Compaq Deskpro XL.\n")) - { - printf ("#define DESKPROXL\n"); - } - } - - if (selected_options & B (OPT_MAUI)) - { - oswf_again: - if (think_positively ( - "Do you have access to the OSWF.MOT file", 1, - "TB Maui and Tropez have a microcontroller which needs to be initialized\n" - "prior use. OSWF.MOT is a file distributed with card's DOS/Windows drivers\n" - "which is required during initialization\n")) - { - char path[512]; - - fprintf (stderr, - "Enter full name of the OSWF.MOT file (pwd is sound): "); - scanf ("%s", path); - fprintf (stderr, "including microcode file %s\n", path); - - if (!bin2hex (path, "maui_boot.h", "maui_os")) - { - fprintf (stderr, "Couldn't open file %s\n", - path); - if (think_positively ("Try again with correct path", 1, - "The specified file could not be opened. Enter the correct path to the\n" - "file.\n")) - goto oswf_again; - } - else - { - printf ("#define HAVE_MAUI_BOOT\n"); - printf ("/*build bin2hex %s maui_boot.h maui_os */\n", path); - } - } - } - - if (!(selected_options & ANY_DEVS)) - { - printf ("invalid_configuration__run_make_config_again\n"); - fprintf (stderr, "\n*** This combination is useless. Sound driver disabled!!! ***\n*** You need to enable support for at least one device ***\n\n"); - exit (0); - } - - for (i = 0; i <= OPT_LAST; i++) - if (!hw_table[i].alias) - if (selected_options & B (i)) - printf ("#define CONFIG_%s\n", hw_table[i].macro); - else - printf ("#undef CONFIG_%s\n", hw_table[i].macro); - - printf ("\n"); - - i = 0; - - while (extra_options[i].name != NULL) - { - if (selected_options & extra_options[i].mask) - printf ("#define CONFIG_%s\n", extra_options[i].name); - else - printf ("#undef CONFIG_%s\n", extra_options[i].name); - i++; - } - - printf ("\n"); - - ask_parameters (); - - printf ("#define SELECTED_SOUND_OPTIONS\t0x%08lx\n", selected_options); - fprintf (stderr, "\nThe sound driver is now configured.\n"); - -#if defined(SCO) || defined(ISC) || defined(SYSV) - fprintf (stderr, "Remember to update the System file\n"); -#endif - - if (!old_config_used) - { - char str[255]; - - sprintf (str, "Save copy of this configuration to `%s'", oldconf); - if (think_positively (str, 1, - "If you enable this option then the sound driver configuration is\n" - "saved to a file. If you later need to recompile the kernel you have\n" - "the option of using the saved configuration.\n")) - { - char cmd[200]; - - sprintf (cmd, "cp local.h %s", oldconf); - - fclose (stdout); - if (system (cmd) != 0) - perror (cmd); - } - } - exit (0); -} - -int -bin2hex (char *path, char *target, char *varname) -{ - int fd; - int count; - char c; - int i = 0; - - if ((fd = open (path, 0)) > 0) - { - FILE *sf = fopen (target, "w"); - - fprintf (sf, "/* automatically generated by configure */\n"); - fprintf (sf, "static unsigned char %s[] = {\n", varname); - while (1) - { - count = read (fd, &c, 1); - if (count == 0) - break; - if (i != 0 && (i % 10) == 0) - fprintf (sf, "\n"); - fprintf (sf, "0x%02lx,", c & 0xFFL); - i++; - } - fprintf (sf, "};\n" - "#define %sLen %d\n", varname, i); - fclose (sf); - close (fd); - return 1; - } - - return 0; -} diff --git a/drivers/sound/coproc.h b/drivers/sound/coproc.h index f9023821d3f2..7306346e9ac4 100644 --- a/drivers/sound/coproc.h +++ b/drivers/sound/coproc.h @@ -1,5 +1,5 @@ /* - * Definitions for various on board processors on the soundcards. For + * Definitions for various on board processors on the sound cards. For * example DSP processors. */ diff --git a/drivers/sound/cs4232.c b/drivers/sound/cs4232.c index 989f0e550886..38751fdcb512 100644 --- a/drivers/sound/cs4232.c +++ b/drivers/sound/cs4232.c @@ -7,331 +7,404 @@ * interfaces. This is just a temporary driver until full PnP support * gets implemented. Just the WSS codec, FM synth and the MIDI ports are * supported. Other interfaces are left uninitialized. + * + * ifdef ...WAVEFRONT... + * + * Support is provided for initializing the WaveFront synth + * interface as well, which is logical device #4. Note that if + * you have a Tropez+ card, you probably don't need to setup + * the CS4232-supported MIDI interface, since it corresponds to + * the internal 26-pin header that's hard to access. Using this + * requires an additional IRQ, a resource none too plentiful in + * this environment. Just don't set module parameters mpuio and + * mpuirq, and the MIDI port will be left uninitialized. You can + * still use the ICS2115 hosted MIDI interface which corresponds + * to the 9-pin D connector on the back of the card. + * + * endif ...WAVEFRONT... + * + * Supported chips are: + * CS4232 + * CS4236 + * CS4236B + * + * Note: You will need a PnP config setup to initialise some CS4232 boards + * anyway. + * + * Changes + * Alan Cox Modularisation, Basic cleanups. + * Paul Barton-Davis Separated MPU configuration, added + * Tropez+ (WaveFront) support */ + /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 +#include +#include #include "sound_config.h" - -#if defined(CONFIG_CS4232) +#include "soundmodule.h" #define KEY_PORT 0x279 /* Same as LPT1 status port */ #define CSN_NUM 0x99 /* Just a random number */ -static int *osp; - -static void -CS_OUT (unsigned char a) +static void CS_OUT(unsigned char a) { - outb (a, KEY_PORT); + outb(a, KEY_PORT); } + #define CS_OUT2(a, b) {CS_OUT(a);CS_OUT(b);} #define CS_OUT3(a, b, c) {CS_OUT(a);CS_OUT(b);CS_OUT(c);} -static int mpu_base = 0, mpu_irq = 0; -static int mpu_detected = 0; +static int mpu_base = 0, mpu_irq = 0; +#ifdef CONFIG_SOUND_WAVEFRONT_MODULE +static int synth_base = 0, synth_irq = 0; +#endif CONFIG_SOUND_WAVEFRONT_MODULE +static int mpu_detected = 0; -int -probe_cs4232_mpu (struct address_info *hw_config) +int probe_cs4232_mpu(struct address_info *hw_config) { -/* - * Just write down the config values. - */ + /* + * Just write down the config values. + */ - mpu_base = hw_config->io_base; - mpu_irq = hw_config->irq; + mpu_base = hw_config->io_base; + mpu_irq = hw_config->irq; - return 0; + return 1; } -void -attach_cs4232_mpu (struct address_info *hw_config) +void attach_cs4232_mpu(struct address_info *hw_config) { + /* Nothing needs doing */ } static unsigned char crystal_key[] = /* A 32 byte magic key sequence */ { - 0x96, 0x35, 0x9a, 0xcd, 0xe6, 0xf3, 0x79, 0xbc, - 0x5e, 0xaf, 0x57, 0x2b, 0x15, 0x8a, 0xc5, 0xe2, - 0xf1, 0xf8, 0x7c, 0x3e, 0x9f, 0x4f, 0x27, 0x13, - 0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a + 0x96, 0x35, 0x9a, 0xcd, 0xe6, 0xf3, 0x79, 0xbc, + 0x5e, 0xaf, 0x57, 0x2b, 0x15, 0x8a, 0xc5, 0xe2, + 0xf1, 0xf8, 0x7c, 0x3e, 0x9f, 0x4f, 0x27, 0x13, + 0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a }; -int -probe_cs4232 (struct address_info *hw_config) +static void sleep(unsigned howlong) { - int i, n; - int base = hw_config->io_base, irq = hw_config->irq; - int dma1 = hw_config->dma, dma2 = hw_config->dma2; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + howlong; + schedule(); + current->timeout = 0; +} - static wait_handle *cs_sleeper = NULL; - static volatile struct snd_wait cs_sleep_flag = - {0}; +int probe_cs4232(struct address_info *hw_config) +{ + int i, n; + int base = hw_config->io_base, irq = hw_config->irq; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; - osp = hw_config->osp; + /* + * Verify that the I/O port range is free. + */ -/* - * Verify that the I/O port range is free. - */ + if (check_region(base, 4)) + { + printk(KERN_ERR "cs4232.c: I/O port 0x%03x not free\n", base); + return 0; + } + if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) + return 1; /* The card is already active */ + + /* + * This version of the driver doesn't use the PnP method when configuring + * the card but a simplified method defined by Crystal. This means that + * just one CS4232 compatible device can exist on the system. Also this + * method conflicts with possible PnP support in the OS. For this reason + * driver is just a temporary kludge. + * + * Also the Cirrus/Crystal method doesnt always work. Try ISA PnP first ;) + */ + + /* + * Repeat initialization few times since it doesn't always succeed in + * first time. + */ + + for (n = 0; n < 4; n++) + { + /* + * Wake up the card by sending a 32 byte Crystal key to the key port. + */ + + for (i = 0; i < 32; i++) + CS_OUT(crystal_key[i]); + + sleep(HZ / 10); + + /* + * Now set the CSN (Card Select Number). + */ + + CS_OUT2(0x06, CSN_NUM); + + /* + * Then set some config bytes. First logical device 0 + */ + + CS_OUT2(0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */ + CS_OUT3(0x47, (base >> 8) & 0xff, base & 0xff); /* WSS base */ + + if (check_region(0x388, 4)) /* Not free */ + CS_OUT3(0x48, 0x00, 0x00) /* FM base off */ + else + CS_OUT3(0x48, 0x03, 0x88); /* FM base 0x388 */ + + CS_OUT3(0x42, 0x00, 0x00); /* SB base off */ + CS_OUT2(0x22, irq); /* SB+WSS IRQ */ + CS_OUT2(0x2a, dma1); /* SB+WSS DMA */ + + if (dma2 != -1) + CS_OUT2(0x25, dma2) /* WSS DMA2 */ + else + CS_OUT2(0x25, 4); /* No WSS DMA2 */ + + CS_OUT2(0x33, 0x01); /* Activate logical dev 0 */ + + sleep(HZ / 10); + + /* + * Initialize logical device 3 (MPU) + */ + +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + if (mpu_base != 0 && mpu_irq != 0) + { + CS_OUT2(0x15, 0x03); /* Select logical device 3 (MPU) */ + CS_OUT3(0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPU base */ + CS_OUT2(0x22, mpu_irq); /* MPU IRQ */ + CS_OUT2(0x33, 0x01); /* Activate logical dev 3 */ + } +#endif - if (check_region (base, 4)) - { - printk ("cs4232.c: I/O port 0x%03x not free\n", base); - return 0; - } +#if defined(CONFIG_SOUND_WAVEFRONT) || defined(CONFIG_SOUND_WAVEFRONT_MODULE) + { + CS_OUT2 (0x15, 0x04); /* logical device 4 (WaveFront) */ + CS_OUT3 (0x47, (synth_base >> 8) & 0xff, + synth_base & 0xff); /* base */ + CS_OUT2 (0x22, synth_irq); /* IRQ */ + CS_OUT2 (0x33, 0x01); /* Activate logical dev 4 */ + } +#endif + /* + * Finally activate the chip + */ + + CS_OUT(0x79); + + sleep(HZ / 5); + + /* + * Then try to detect the codec part of the chip + */ + + if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) + return 1; + + sleep(HZ); + } + return 0; +} - if (ad1848_detect (hw_config->io_base, NULL, hw_config->osp)) - return 1; /* The card is already active */ +void attach_cs4232(struct address_info *hw_config) +{ + int base = hw_config->io_base, irq = hw_config->irq; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = dma1; + + hw_config->slots[0] = ad1848_init("Crystal audio controller", base, + irq, + dma1, /* Playback DMA */ + dma2, /* Capture DMA */ + 0, + hw_config->osp); + + if (hw_config->slots[0] != -1 && + audio_devs[hw_config->slots[0]]->mixer_dev!=-1) + { + /* Assume the mixer map is as suggested in the CS4232 databook */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM synth */ + } +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + if (mpu_base != 0 && mpu_irq != 0) + { + static struct address_info hw_config2 = { + 0 + }; /* Ensure it's initialized */ + + hw_config2.io_base = mpu_base; + hw_config2.irq = mpu_irq; + hw_config2.dma = -1; + hw_config2.dma2 = -1; + hw_config2.always_detect = 0; + hw_config2.name = NULL; + hw_config2.driver_use_1 = 0; + hw_config2.driver_use_2 = 0; + hw_config2.card_subtype = 0; + + if (probe_uart401(&hw_config2)) + { + mpu_detected = 1; + attach_uart401(&hw_config2); + } + else + { + mpu_base = mpu_irq = 0; + } + hw_config->slots[1] = hw_config2.slots[1]; + } +#endif +} -/* - * This version of the driver doesn't use the PnP method when configuring - * the card but a simplified method defined by Crystal. This means that - * just one CS4232 compatible device can exist on the system. Also this - * method conflicts with possible PnP support in the OS. For this reason - * driver is just a temporary kludge. - */ +void unload_cs4232(struct address_info *hw_config) +{ + int base = hw_config->io_base, irq = hw_config->irq; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; -/* - * Repeat initialization few times since it doesn't always succeed in - * first time. - */ + if (dma2 == -1) + dma2 = dma1; - for (n = 0; n < 4; n++) - { - cs_sleep_flag.flags = WK_NONE; -/* - * Wake up the card by sending a 32 byte Crystal key to the key port. - */ - for (i = 0; i < 32; i++) - CS_OUT (crystal_key[i]); - - - { - unsigned long tlimit; - - if (HZ / 10) - current_set_timeout (tlimit = jiffies + (HZ / 10)); - else - tlimit = (unsigned long) -1; - cs_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&cs_sleeper); - if (!(cs_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - cs_sleep_flag.flags |= WK_TIMEOUT; - } - cs_sleep_flag.flags &= ~WK_SLEEP; - }; /* Delay */ + ad1848_unload(base, + irq, + dma1, /* Playback DMA */ + dma2, /* Capture DMA */ + 0); -/* - * Now set the CSN (Card Select Number). - */ + sound_unload_audiodev(hw_config->slots[0]); +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + if (mpu_base != 0 && mpu_irq != 0 && mpu_detected) + { + static struct address_info hw_config2 = + { + 0 + }; /* Ensure it's initialized */ + + hw_config2.io_base = mpu_base; + hw_config2.irq = mpu_irq; + hw_config2.dma = -1; + hw_config2.dma2 = -1; + hw_config2.always_detect = 0; + hw_config2.name = NULL; + hw_config2.driver_use_1 = 0; + hw_config2.driver_use_2 = 0; + hw_config2.card_subtype = 0; + hw_config2.slots[1] = hw_config->slots[1]; + + unload_uart401(&hw_config2); + } +#endif +} - CS_OUT2 (0x06, CSN_NUM); +void unload_cs4232_mpu(struct address_info *hw_config) +{ + /* Not required. Handled by cs4232_unload */ +} +#ifdef MODULE + +int io = -1; +int irq = -1; +int dma = -1; +int dma2 = -1; +int mpuio = -1; +int mpuirq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(mpuio,"i"); +MODULE_PARM(mpuirq,"i"); + +#ifdef CONFIG_SOUND_WAVEFRONT_MODULE +int synthio = -1; +int synthirq = -1; +MODULE_PARM(synthio,"i"); +MODULE_PARM(synthirq,"i"); +#endif CONFIG_SOUND_WAVEFRONT_MODULE + +struct address_info cfg; +struct address_info mpu_cfg; /* - * Then set some config bytes. First logical device 0 + * Install a CS4232 based card. Need to have ad1848 and mpu401 + * loaded ready. */ - CS_OUT2 (0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */ - CS_OUT3 (0x47, (base >> 8) & 0xff, base & 0xff); /* WSS base */ - - if (check_region (0x388, 4)) /* Not free */ - CS_OUT3 (0x48, 0x00, 0x00) /* FM base off */ - else - CS_OUT3 (0x48, 0x03, 0x88); /* FM base 0x388 */ - - CS_OUT3 (0x42, 0x00, 0x00); /* SB base off */ - CS_OUT2 (0x22, irq); /* SB+WSS IRQ */ - CS_OUT2 (0x2a, dma1); /* SB+WSS DMA */ - - if (dma2 != -1) - CS_OUT2 (0x25, dma2) /* WSS DMA2 */ - else - CS_OUT2 (0x25, 4); /* No WSS DMA2 */ - - CS_OUT2 (0x33, 0x01); /* Activate logical dev 0 */ - - - { - unsigned long tlimit; - - if (HZ / 10) - current_set_timeout (tlimit = jiffies + (HZ / 10)); - else - tlimit = (unsigned long) -1; - cs_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&cs_sleeper); - if (!(cs_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - cs_sleep_flag.flags |= WK_TIMEOUT; - } - cs_sleep_flag.flags &= ~WK_SLEEP; - }; /* Delay */ +int init_module(void) +{ -/* - * Initialize logical device 3 (MPU) - */ +#ifndef CONFIG_SOUND_WAVEFRONT_MODULE -#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) - if (mpu_base != 0 && mpu_irq != 0) + if (io == -1 || irq == -1 || dma == -1 || dma2 == -1) { - CS_OUT2 (0x15, 0x03); /* Select logical device 3 (MPU) */ - CS_OUT3 (0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPU base */ - CS_OUT2 (0x22, mpu_irq); /* MPU IRQ */ - CS_OUT2 (0x33, 0x01); /* Activate logical dev 3 */ + printk(KERN_ERR "cs4232: dma, dma2, irq and io must be set.\n"); + return -EINVAL; + } +#else + if (synthio == -1 || synthirq == -1 || + io == -1 || irq == -1 || dma == -1 || dma2 == -1) + { + printk(KERN_ERR "cs4232: synthio, synthirq, dma, dma2, " + "irq and io must be set.\n"); + return -EINVAL; } -#endif -/* - * Finally activate the chip - */ - CS_OUT (0x79); - - - { - unsigned long tlimit; - - if (HZ / 5) - current_set_timeout (tlimit = jiffies + (HZ / 5)); - else - tlimit = (unsigned long) -1; - cs_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&cs_sleeper); - if (!(cs_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - cs_sleep_flag.flags |= WK_TIMEOUT; - } - cs_sleep_flag.flags &= ~WK_SLEEP; - }; /* Delay */ +#endif CONFIG_SOUND_WAVEFRONT_MODULE -/* - * Then try to detect the codec part of the chip - */ + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; - if (ad1848_detect (hw_config->io_base, NULL, hw_config->osp)) - return 1; +#ifdef CONFIG_SOUND_WAVEFRONT_MODULE + synth_base = synthio; + synth_irq = synthirq; +#endif CONFIG_SOUND_WAVEFRONT_MODULE + if (probe_cs4232(&cfg) == 0) + return -ENODEV; - { - unsigned long tlimit; - - if (HZ) - current_set_timeout (tlimit = jiffies + (HZ)); - else - tlimit = (unsigned long) -1; - cs_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&cs_sleeper); - if (!(cs_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - cs_sleep_flag.flags |= WK_TIMEOUT; - } - cs_sleep_flag.flags &= ~WK_SLEEP; - }; /* Longer delay */ - } - - return 0; -} + mpu_cfg.io_base = -1; + mpu_cfg.irq = -1; -void -attach_cs4232 (struct address_info *hw_config) -{ - int base = hw_config->io_base, irq = hw_config->irq; - int dma1 = hw_config->dma, dma2 = hw_config->dma2; - - if (dma2 == -1) - dma2 = dma1; - - ad1848_init ("CS4232", base, - irq, - dma1, /* Playback DMA */ - dma2, /* Capture DMA */ - 0, - hw_config->osp); - -#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) - if (mpu_base != 0 && mpu_irq != 0) - { - static struct address_info hw_config2 = - {0}; /* Ensure it's initialized */ - - hw_config2.io_base = mpu_base; - hw_config2.irq = mpu_irq; - hw_config2.dma = -1; - hw_config2.dma2 = -1; - hw_config2.always_detect = 0; - hw_config2.name = NULL; - hw_config2.driver_use_1 = 0; - hw_config2.driver_use_2 = 0; - hw_config2.card_subtype = 0; - hw_config2.osp = hw_config->osp; - - if (probe_mpu401 (&hw_config2)) - { - mpu_detected = 1; - attach_mpu401 (&hw_config2); + if (mpuio != -1 && mpuirq != -1) { + mpu_cfg.io_base = mpuio; + mpu_cfg.irq = mpuirq; + probe_cs4232_mpu(&mpu_cfg); /* Bug always returns 0 not OK -- AC */ } - else - { - mpu_base = mpu_irq = 0; + + attach_cs4232(&cfg); + + if (mpuio != -1 && mpuirq != -1) { + attach_cs4232_mpu(&mpu_cfg); } - } -#endif -} -void -unload_cs4232 (struct address_info *hw_config) -{ - int base = hw_config->io_base, irq = hw_config->irq; - int dma1 = hw_config->dma, dma2 = hw_config->dma2; - - if (dma2 == -1) - dma2 = dma1; - - ad1848_unload (base, - irq, - dma1, /* Playback DMA */ - dma2, /* Capture DMA */ - 0); - -#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) - if (mpu_base != 0 && mpu_irq != 0 && mpu_detected) - { - static struct address_info hw_config2 = - {0}; /* Ensure it's initialized */ - - hw_config2.io_base = mpu_base; - hw_config2.irq = mpu_irq; - hw_config2.dma = -1; - hw_config2.dma2 = -1; - hw_config2.always_detect = 0; - hw_config2.name = NULL; - hw_config2.driver_use_1 = 0; - hw_config2.driver_use_2 = 0; - hw_config2.card_subtype = 0; - hw_config2.osp = hw_config->osp; - - unload_mpu401 (&hw_config2); - } -#endif + SOUND_LOCK; + return 0; } -void -unload_cs4232_mpu (struct address_info *hw_config) +void cleanup_module(void) { - /* Not required. Handled by cs4232_unload */ + unload_cs4232(&cfg); /* unloads MPU as well, if needed */ + SOUND_LOCK_END; } - #endif diff --git a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c index 0191620b2839..99e15cb22514 100644 --- a/drivers/sound/dev_table.c +++ b/drivers/sound/dev_table.c @@ -3,599 +3,618 @@ * * Device call tables. */ + /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 #define _DEV_TABLE_C_ #include "sound_config.h" -int sound_started = 0; +int softoss_dev = 0; +int sound_started = 0; +int sndtable_get_cardcount(void); -int sndtable_get_cardcount (void); - -int -snd_find_driver (int type) +int snd_find_driver(int type) { - int i, n = num_sound_drivers; + int i, n = num_sound_drivers; - for (i = 0; i < n; i++) - if (sound_drivers[i].card_type == type) - return i; + for (i = 0; i < n; i++) + if (sound_drivers[i].card_type == type) + return i; - return -1; + return -1; } -static void -start_services (void) +static void start_services(void) { - int soundcards_installed; +#ifdef FIXME + int soundcards_installed; - if (!(soundcards_installed = sndtable_get_cardcount ())) - return; /* No cards detected */ - -#ifdef CONFIG_AUDIO - if (num_audiodevs) /* Audio devices present */ - { - DMAbuf_init (); - audio_init (); - } + if (!(soundcards_installed = sndtable_get_cardcount())) + return; /* No cards detected */ #endif -#ifdef CONFIG_MIDI - if (num_midis) - MIDIbuf_init (); +#ifdef CONFIG_AUDIO + if (num_audiodevs) /* Audio devices present */ + { + int dev; + for (dev = 0; dev < num_audiodevs; dev++) + { + } + audio_init_devices(); + } #endif -#ifdef CONFIG_SEQUENCER - if (num_midis + num_synths) - sequencer_init (); -#endif - return; + return; } static void -start_cards (void) +start_cards(void) { - int i, n = num_sound_cards; - int drv; + int i, n = num_sound_cards; + int drv; - sound_started = 1; - if (trace_init) - printk ("Sound initialization started\n"); + sound_started = 1; + if (trace_init) + printk(KERN_DEBUG "Sound initialization started\n"); + +#ifdef CONFIG_LOWLEVEL_SOUND + { + extern void sound_preinit_lowlevel_drivers(void); + sound_preinit_lowlevel_drivers(); + } +#endif /* * Check the number of cards actually defined in the table */ - for (i = 0; i < n && snd_installed_cards[i].card_type; i++) - num_sound_cards = i + 1; + for (i = 0; i < n && snd_installed_cards[i].card_type; i++) + num_sound_cards = i + 1; - for (i = 0; i < n && snd_installed_cards[i].card_type; i++) - if (snd_installed_cards[i].enabled) - { - snd_installed_cards[i].for_driver_use = NULL; + for (i = 0; i < n && snd_installed_cards[i].card_type; i++) + { + if (snd_installed_cards[i].enabled) + { + snd_installed_cards[i].for_driver_use = NULL; + + if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) == -1) + { + snd_installed_cards[i].enabled = 0; /* + * Mark as not detected + */ + continue; + } + snd_installed_cards[i].config.card_subtype = + sound_drivers[drv].card_subtype; + + if (sound_drivers[drv].probe(&snd_installed_cards[i].config)) + sound_drivers[drv].attach(&snd_installed_cards[i].config); + else + snd_installed_cards[i].enabled = 0; /* + * Mark as not detected + */ + } + } +#ifdef CONFIG_LOWLEVEL_SOUND + { + extern void sound_init_lowlevel_drivers(void); + sound_init_lowlevel_drivers(); + } +#endif + if (trace_init) + printk(KERN_DEBUG "Sound initialization complete\n"); +} - if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1) - { - snd_installed_cards[i].enabled = 0; /* - * Mark as not detected - */ - continue; - } +void sndtable_init(void) +{ + start_cards(); +} - snd_installed_cards[i].config.card_subtype = - sound_drivers[drv].card_subtype; - if (sound_drivers[drv].probe (&snd_installed_cards[i].config)) - { +void sound_unload_drivers(void) +{ + int i, n = num_sound_cards; + int drv; - sound_drivers[drv].attach (&snd_installed_cards[i].config); + if (!sound_started) + return; - } - else - snd_installed_cards[i].enabled = 0; /* - * Mark as not detected - */ - } - - if (trace_init) - printk ("Sound initialization complete\n"); -} + if (trace_init) + printk(KERN_DEBUG "Sound unload started\n"); -void -sndtable_init (void) -{ - return start_cards (); -} -void -sound_unload_drivers (void) -{ - int i, n = num_sound_cards; - int drv; - - if (!sound_started) - return; - - if (trace_init) - printk ("Sound unload started\n"); - - for (i = 0; i < n && snd_installed_cards[i].card_type; i++) - if (snd_installed_cards[i].enabled) - if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1) - if (sound_drivers[drv].unload) - { - sound_drivers[drv].unload (&snd_installed_cards[i].config); - snd_installed_cards[i].enabled = 0; - } + for (i = 0; i < n && snd_installed_cards[i].card_type; i++) + { + if (snd_installed_cards[i].enabled) + { + if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) != -1) + { + if (sound_drivers[drv].unload) + { + sound_drivers[drv].unload(&snd_installed_cards[i].config); + snd_installed_cards[i].enabled = 0; + } + } + } + } + + for (i=0;iio_base; + snd_installed_cards[sel].config.irq = hw_config->irq; + snd_installed_cards[sel].config.dma = hw_config->dma; + snd_installed_cards[sel].config.dma2 = hw_config->dma2; + snd_installed_cards[sel].config.name = hw_config->name; + snd_installed_cards[sel].config.always_detect = hw_config->always_detect; + snd_installed_cards[sel].config.driver_use_1 = hw_config->driver_use_1; + snd_installed_cards[sel].config.driver_use_2 = hw_config->driver_use_2; + snd_installed_cards[sel].config.card_subtype = hw_config->card_subtype; + + if ((drv = snd_find_driver(snd_installed_cards[sel].card_type)) == -1) + { + snd_installed_cards[sel].enabled = 0; + DEB(printk(KERN_DEBUG "Failed to find driver\n")); + return 0; + } + DEB(printk(KERN_DEBUG "Driver name '%s'\n", sound_drivers[drv].name)); + + hw_config->card_subtype = snd_installed_cards[sel].config.card_subtype = sound_drivers[drv].card_subtype; + + if (sound_drivers[drv].probe(hw_config)) + { + DEB(printk(KERN_DEBUG "Hardware probed OK\n")); + return 1; + } + DEB(printk("Failed to find hardware\n")); + snd_installed_cards[sel].enabled = 0; /* + * Mark as not detected + */ + return 0; + } + return 0; +} - snd_installed_cards[sel].card_type = unit; - snd_installed_cards[sel].enabled = 1; - } - if (sel != -1) - { - int drv; +int sndtable_init_card(int unit, struct address_info *hw_config) +{ + int i, n = num_sound_cards; - snd_installed_cards[sel].for_driver_use = NULL; - snd_installed_cards[sel].config.io_base = hw_config->io_base; - snd_installed_cards[sel].config.irq = hw_config->irq; - snd_installed_cards[sel].config.dma = hw_config->dma; - snd_installed_cards[sel].config.dma2 = hw_config->dma2; - snd_installed_cards[sel].config.name = hw_config->name; - snd_installed_cards[sel].config.always_detect = hw_config->always_detect; - snd_installed_cards[sel].config.driver_use_1 = hw_config->driver_use_1; - snd_installed_cards[sel].config.driver_use_2 = hw_config->driver_use_2; - snd_installed_cards[sel].config.card_subtype = hw_config->card_subtype; - snd_installed_cards[sel].config.osp = hw_config->osp; + DEB(printk("sndtable_init_card(%d) entered\n", unit)); - if ((drv = snd_find_driver (snd_installed_cards[sel].card_type)) == -1) + if (!unit) { - snd_installed_cards[sel].enabled = 0; - DDB (printk ("Failed to find driver\n")); - return FALSE; + sndtable_init(); + return 1; } - DDB (printk ("Driver name '%s'\n", sound_drivers[drv].name)); - - hw_config->card_subtype = - snd_installed_cards[sel].config.card_subtype = - sound_drivers[drv].card_subtype; - - if (sound_drivers[drv].probe (hw_config)) + for (i = 0; i < n && snd_installed_cards[i].card_type; i++) { - DDB (printk ("Hardware probed OK\n")); - return TRUE; + if (snd_installed_cards[i].card_type == unit) + { + int drv; + + snd_installed_cards[i].config.io_base = hw_config->io_base; + snd_installed_cards[i].config.irq = hw_config->irq; + snd_installed_cards[i].config.dma = hw_config->dma; + snd_installed_cards[i].config.dma2 = hw_config->dma2; + snd_installed_cards[i].config.name = hw_config->name; + snd_installed_cards[i].config.always_detect = hw_config->always_detect; + snd_installed_cards[i].config.driver_use_1 = hw_config->driver_use_1; + snd_installed_cards[i].config.driver_use_2 = hw_config->driver_use_2; + snd_installed_cards[i].config.card_subtype = hw_config->card_subtype; + + if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) == -1) + snd_installed_cards[i].enabled = 0; /* + * Mark as not detected + */ + else + { + DEB(printk(KERN_DEBUG "Located card - calling attach routine\n")); + sound_drivers[drv].attach(hw_config); + + DEB(printk("attach routine finished\n")); + } + start_services(); + return 1; + } } - - DDB (printk ("Failed to find hardware\n")); - snd_installed_cards[sel].enabled = 0; /* - * Mark as not detected - */ - return FALSE; - } - - return FALSE; + DEB(printk("sndtable_init_card: No card defined with type=%d, num cards: %d\n", unit, num_sound_cards)); + return 0; } -int -sndtable_start_card (int unit, struct address_info *hw_config) +int sndtable_get_cardcount(void) { - int sel = -1; - - DDB (printk ("sndtable_probe(%d)\n", unit)); + return num_audiodevs + num_mixers + num_synths + num_midis; +} - if (!unit) - return TRUE; +int sndtable_identify_card(char *name) +{ + int i, n = num_sound_drivers; - sound_started = 1; + if (name == NULL) + return 0; - if (sel == -1 && num_sound_cards < max_sound_cards) - { - int i; + for (i = 0; i < n; i++) + { + if (sound_drivers[i].driver_id != NULL) + { + char *id = sound_drivers[i].driver_id; + int j; + + for (j = 0; j < 80 && name[j] == id[j]; j++) + if (id[j] == 0 && name[j] == 0) /* Match */ + return sound_drivers[i].card_type; + } + } + return 0; +} - i = sel = (num_sound_cards++); +void sound_setup(char *str, int *ints) +{ + int i, n = num_sound_cards; - snd_installed_cards[sel].card_type = unit; - snd_installed_cards[sel].enabled = 1; - } + /* + * First disable all drivers + */ - if (sel != -1) - { - int drv; + for (i = 0; i < n && snd_installed_cards[i].card_type; i++) + snd_installed_cards[i].enabled = 0; - snd_installed_cards[sel].for_driver_use = NULL; - snd_installed_cards[sel].config.io_base = hw_config->io_base; - snd_installed_cards[sel].config.irq = hw_config->irq; - snd_installed_cards[sel].config.dma = hw_config->dma; - snd_installed_cards[sel].config.dma2 = hw_config->dma2; - snd_installed_cards[sel].config.name = hw_config->name; - snd_installed_cards[sel].config.always_detect = hw_config->always_detect; - snd_installed_cards[sel].config.driver_use_1 = hw_config->driver_use_1; - snd_installed_cards[sel].config.driver_use_2 = hw_config->driver_use_2; - snd_installed_cards[sel].config.card_subtype = hw_config->card_subtype; - snd_installed_cards[sel].config.osp = hw_config->osp; + if (ints[0] == 0 || ints[1] == 0) + return; + /* + * Then enable them one by time + */ - if ((drv = snd_find_driver (snd_installed_cards[sel].card_type)) == -1) + for (i = 1; i <= ints[0]; i++) { - snd_installed_cards[sel].enabled = 0; - DDB (printk ("Failed to find driver\n")); - return FALSE; + int card_type, ioaddr, irq, dma, dma2, ptr, j; + unsigned int val; + + val = (unsigned int) ints[i]; + card_type = (val & 0x0ff00000) >> 20; + + if (card_type > 127) + { + /* + * Add any future extensions here + */ + return; + } + ioaddr = (val & 0x000fff00) >> 8; + irq = (val & 0x000000f0) >> 4; + dma = (val & 0x0000000f); + dma2 = (val & 0xf0000000) >> 28; + + ptr = -1; + for (j = 0; j < n && ptr == -1; j++) + { + if (snd_installed_cards[j].card_type == card_type && + !snd_installed_cards[j].enabled)/* + * Not already found + */ + ptr = j; + } + + if (ptr == -1) + printk(KERN_ERR "Sound: Invalid setup parameter 0x%08x\n", val); + else + { + snd_installed_cards[ptr].enabled = 1; + snd_installed_cards[ptr].config.io_base = ioaddr; + snd_installed_cards[ptr].config.irq = irq; + snd_installed_cards[ptr].config.dma = dma; + snd_installed_cards[ptr].config.dma2 = dma2; + snd_installed_cards[ptr].config.name = NULL; + snd_installed_cards[ptr].config.always_detect = 0; + snd_installed_cards[ptr].config.driver_use_1 = 0; + snd_installed_cards[ptr].config.driver_use_2 = 0; + snd_installed_cards[ptr].config.card_subtype = 0; + } } - DDB (printk ("Driver name '%s'\n", sound_drivers[drv].name)); +} - hw_config->card_subtype = - snd_installed_cards[sel].config.card_subtype = - sound_drivers[drv].card_subtype; - if (sound_drivers[drv].probe (hw_config)) +struct address_info * sound_getconf(int card_type) +{ + int j, ptr; + int n = num_sound_cards; + + ptr = -1; + for (j = 0; j < n && ptr == -1 && snd_installed_cards[j].card_type; j++) { - DDB (printk ("Hardware probed OK\n")); - sound_drivers[drv].attach (hw_config); - start_services (); - return TRUE; + if (snd_installed_cards[j].card_type == card_type) + ptr = j; } + if (ptr == -1) + return (struct address_info *) NULL; - DDB (printk ("Failed to find hardware\n")); - snd_installed_cards[sel].enabled = 0; /* - * Mark as not detected - */ - return FALSE; - } - - return FALSE; + return &snd_installed_cards[ptr].config; } -int -sndtable_init_card (int unit, struct address_info *hw_config) -{ - int i, n = num_sound_cards; - - DDB (printk ("sndtable_init_card(%d) entered\n", unit)); - - if (!unit) - { - sndtable_init (); - return TRUE; - } - - for (i = 0; i < n && snd_installed_cards[i].card_type; i++) - if (snd_installed_cards[i].card_type == unit) - { - int drv; - - snd_installed_cards[i].config.io_base = hw_config->io_base; - snd_installed_cards[i].config.irq = hw_config->irq; - snd_installed_cards[i].config.dma = hw_config->dma; - snd_installed_cards[i].config.dma2 = hw_config->dma2; - snd_installed_cards[i].config.name = hw_config->name; - snd_installed_cards[i].config.always_detect = hw_config->always_detect; - snd_installed_cards[i].config.driver_use_1 = hw_config->driver_use_1; - snd_installed_cards[i].config.driver_use_2 = hw_config->driver_use_2; - snd_installed_cards[i].config.card_subtype = hw_config->card_subtype; - snd_installed_cards[i].config.osp = hw_config->osp; - - if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1) - snd_installed_cards[i].enabled = 0; /* - * Mark as not detected - */ - else - { - - DDB (printk ("Located card - calling attach routine\n")); - sound_drivers[drv].attach (hw_config); - - DDB (printk ("attach routine finished\n")); - } - start_services (); - return TRUE; - } - DDB (printk ("sndtable_init_card: No card defined with type=%d, num cards: %d\n", - unit, num_sound_cards)); - return FALSE; -} -int -sndtable_get_cardcount (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) { - return num_audiodevs + num_mixers + num_synths + num_midis; -} +#ifdef CONFIG_AUDIO + struct audio_driver *d; + struct audio_operations *op; + int l, num; -int -sndtable_identify_card (char *name) -{ - int i, n = num_sound_drivers; + 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 (name == NULL) - return 0; + 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))); - for (i = 0; i < n; i++) - if (sound_drivers[i].driver_id != NULL) - { - char *id = sound_drivers[i].driver_id; - int j; + if (sound_nblocks < 1024) + sound_nblocks++; - for (j = 0; j < 80 && name[j] == id[j]; j++) - if (id[j] == 0 && name[j] == 0) /* Match */ - return sound_drivers[i].card_type; - } + op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_operations))); - return 0; + if (sound_nblocks < 1024) + sound_nblocks++; + if (d == NULL || op == NULL) + { + printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name); + sound_unload_audiodev(num); + return -(ENOMEM); + } + memset((char *) op, 0, sizeof(struct audio_operations)); + init_waitqueue(&op->in_sleeper); + init_waitqueue(&op->out_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; + l = strlen(name) + 1; + if (l > sizeof(op->name)) + l = sizeof(op->name); + strncpy(op->name, name, l); + op->name[l - 1] = 0; + 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; +#else + return -EINVAL; +#endif } -void -sound_setup (char *str, int *ints) +int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, + int driver_size, void *devc) { - int i, n = num_sound_cards; - - /* - * First disable all drivers - */ - - for (i = 0; i < n && snd_installed_cards[i].card_type; i++) - snd_installed_cards[i].enabled = 0; + struct mixer_operations *op; + int l; - if (ints[0] == 0 || ints[1] == 0) - return; - /* - * Then enable them one by time - */ + int n = sound_alloc_mixerdev(); - for (i = 1; i <= ints[0]; i++) - { - int card_type, ioaddr, irq, dma, dma2, ptr, j; - unsigned int val; - - val = (unsigned int) ints[i]; - - card_type = (val & 0x0ff00000) >> 20; - - if (card_type > 127) + 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] = vmalloc(sizeof(struct mixer_operations))); + + if (sound_nblocks < 1024) + sound_nblocks++; + if (op == NULL) { - /* - * Add any future extensions here - */ - return; + printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name); + return -ENOMEM; } + memset((char *) op, 0, sizeof(struct mixer_operations)); + memcpy((char *) op, (char *) driver, driver_size); + + l = strlen(name) + 1; + if (l > sizeof(op->name)) + l = sizeof(op->name); + strncpy(op->name, name, l); + op->name[l - 1] = 0; + op->devc = devc; + + mixer_devs[n] = op; + return n; +} - ioaddr = (val & 0x000fff00) >> 8; - irq = (val & 0x000000f0) >> 4; - dma = (val & 0x0000000f); - dma2 = (val & 0xf0000000) >> 28; - - ptr = -1; - for (j = 0; j < n && ptr == -1; j++) - if (snd_installed_cards[j].card_type == card_type && - !snd_installed_cards[j].enabled) /* - * Not already found - */ - ptr = j; - - if (ptr == -1) - printk ("Sound: Invalid setup parameter 0x%08x\n", val); - else +void sound_unload_audiodev(int dev) +{ + if (dev != -1) { - snd_installed_cards[ptr].enabled = 1; - snd_installed_cards[ptr].config.io_base = ioaddr; - snd_installed_cards[ptr].config.irq = irq; - snd_installed_cards[ptr].config.dma = dma; - snd_installed_cards[ptr].config.dma2 = dma2; - snd_installed_cards[ptr].config.name = NULL; - snd_installed_cards[ptr].config.always_detect = 0; - snd_installed_cards[ptr].config.driver_use_1 = 0; - snd_installed_cards[ptr].config.driver_use_2 = 0; - snd_installed_cards[ptr].config.card_subtype = 0; - snd_installed_cards[ptr].config.osp = NULL; + audio_devs[dev] = NULL; + unregister_sound_dsp((dev<<4)+3); } - } } +int sound_alloc_audiodev(void) +{ + int i = register_sound_dsp(&oss_sound_fops); + if(i==-1) + return i; + i>>=4; + if(i>=num_audiodevs) + num_audiodevs = i + 1; + return i; +} -struct address_info - * -sound_getconf (int card_type) +int sound_alloc_mididev(void) { - int j, ptr; - int n = num_sound_cards; + int i = register_sound_midi(&oss_sound_fops); + if(i==-1) + return i; + i>>=4; + if(i>=num_midis) + num_midis = i + 1; + return i; +} - ptr = -1; - for (j = 0; j < n && ptr == -1 && snd_installed_cards[j].card_type; j++) - if (snd_installed_cards[j].card_type == card_type) - ptr = j; +int sound_alloc_synthdev(void) +{ + int i; - if (ptr == -1) - return (struct address_info *) NULL; + for (i = 0; i < MAX_SYNTH_DEV; i++) + { + if (synth_devs[i] == NULL) + { + if (i >= num_synths) + num_synths++; + return i; + } + } + return -1; +} - return &snd_installed_cards[ptr].config; +int sound_alloc_mixerdev(void) +{ + int i = register_sound_mixer(&oss_sound_fops); + if(i==-1) + return -1; + i>>=4; + if(i>=num_mixers) + num_mixers = i + 1; + return i; } +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; +} -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) +void sound_unload_mixerdev(int dev) { - struct audio_driver *d; - struct audio_operations *op; - int l, num; - - if (num_audiodevs >= MAX_AUDIO_DEV) - { - printk ("Sound: Too many audio drivers\n"); - return -(EIO); - } - - if (vers != AUDIO_DRIVER_VERSION || - driver_size > sizeof (struct audio_driver)) - { - printk ("Sound: Incompatible audio driver for %s\n", name); - return -(EIO); - } - - - d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct audio_driver))); - - if (sound_nblocks < 1024) - sound_nblocks++;; - - op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct audio_operations))); - - if (sound_nblocks < 1024) - sound_nblocks++;; - if (d == NULL || op == NULL) - { - printk ("Sound: Can't allocate driver for (%s)\n", name); - return -(ENOSPC); - } - - memset ((char *) op, 0, sizeof (struct audio_operations)); - 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; - - l = strlen (name) + 1; - if (l > sizeof (op->name)) - l = sizeof (op->name); - strncpy (op->name, name, l); - op->name[l - 1] = 0; - op->flags = flags; - op->format_mask = format_mask; - op->devc = devc; - op->dmachan1 = dma1; - op->dmachan2 = dma2; - -/* - * Hardcoded defaults - */ - op->buffsize = DSP_BUFFSIZE; + if (dev != -1) + { + mixer_devs[dev] = NULL; + unregister_sound_mixer(dev<<4); + } +} - audio_devs[num_audiodevs] = op; - num = num_audiodevs++; +void sound_unload_mididev(int dev) +{ +#ifdef CONFIG_MIDI + if (dev != -1) + { + midi_devs[dev] = NULL; + unregister_sound_midi((dev<<4)+2); + } +#endif +} - DMAbuf_init (); - audio_init (); - return num; +void sound_unload_synthdev(int dev) +{ + if (dev != -1) + synth_devs[dev] = NULL; } -int -sound_install_mixer (int vers, - char *name, - struct mixer_operations *driver, - int driver_size, - void *devc) +void sound_unload_timerdev(int dev) { - struct mixer_operations *op; - int l; - - if (num_mixers >= MAX_MIXER_DEV) - { - printk ("Sound: Too many mixer drivers\n"); - return -(EIO); - } - - if (vers != MIXER_DRIVER_VERSION || - driver_size > sizeof (struct mixer_operations)) - { - printk ("Sound: Incompatible mixer driver for %s\n", name); - return -(EIO); - } - - - op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct mixer_operations))); - - if (sound_nblocks < 1024) - sound_nblocks++;; - if (op == NULL) - { - printk ("Sound: Can't allocate mixer driver for (%s)\n", name); - return -(ENOSPC); - } - - memset ((char *) op, 0, sizeof (struct mixer_operations)); - - memcpy ((char *) op, (char *) driver, driver_size); - - l = strlen (name) + 1; - if (l > sizeof (op->name)) - l = sizeof (op->name); - strncpy (op->name, name, l); - op->name[l - 1] = 0; - op->devc = devc; - - mixer_devs[num_mixers] = op; - return num_mixers++; + if (dev != -1) + sound_timer_devs[dev] = NULL; } + diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h index 3ce91d341e5f..bf5d62bffd86 100644 --- a/drivers/sound/dev_table.h +++ b/drivers/sound/dev_table.h @@ -2,11 +2,11 @@ * dev_table.h * * Global definitions for device call tables - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1996 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * + * 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. */ @@ -21,7 +21,29 @@ * 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_OPL3SA1 38 +#define SNDCARD_OPL3SA1_SB 39 +#define SNDCARD_OPL3SA1_MPU 40 +#define SNDCARD_SOFTOSS 36 +#define SNDCARD_VMIDI 37 +#define SNDCARD_WAVEFRONT 41 + +void attach_opl3sa_wss (struct address_info *hw_config); +int probe_opl3sa_wss (struct address_info *hw_config); +void attach_opl3sa_sb (struct address_info *hw_config); +int probe_opl3sa_sb (struct address_info *hw_config); +void attach_opl3sa_mpu (struct address_info *hw_config); +int probe_opl3sa_mpu (struct address_info *hw_config); +void unload_opl3sa_wss(struct address_info *hw_info); +void unload_opl3sa_sb(struct address_info *hw_info); +void unload_opl3sa_mpu(struct address_info *hw_info); +void attach_softsyn_card (struct address_info *hw_config); +int probe_softsyn (struct address_info *hw_config); +void unload_softsyn (struct address_info *hw_config); /* * NOTE! NOTE! NOTE! NOTE! @@ -33,7 +55,8 @@ extern int sound_started; -struct driver_info { +struct driver_info +{ char *driver_id; int card_subtype; /* Driver specific. Usually 0 */ int card_type; /* From soundcard.h */ @@ -43,19 +66,14 @@ struct driver_info { void (*unload) (struct address_info *hw_config); }; -struct card_info { +struct card_info +{ int card_type; /* Link (search key) to the driver list */ struct address_info config; int enabled; void *for_driver_use; }; -typedef struct pnp_sounddev -{ - int id; - void (*setup)(void *dev); - char *driver_name; -}pnp_sounddev; /* * Device specific parameters (used only by dmabuf.c) @@ -66,7 +84,8 @@ typedef struct pnp_sounddev #define DMODE_OUTPUT PCM_ENABLE_OUTPUT #define DMODE_INPUT PCM_ENABLE_INPUT -struct dma_buffparms { +struct dma_buffparms +{ int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */ int closing; @@ -76,6 +95,7 @@ struct dma_buffparms { char *raw_buf; unsigned long raw_buf_phys; + int buffsize; /* * Device state tables @@ -89,7 +109,10 @@ struct dma_buffparms { #define DMA_EMPTY 0x00000010 #define DMA_ALLOC_DONE 0x00000020 #define DMA_SYNCING 0x00000040 -#define DMA_CLEAN 0x00000080 +#define DMA_DIRTY 0x00000080 +#define DMA_POST 0x00000100 +#define DMA_NODMA 0x00000200 +#define DMA_NOTIMEOUT 0x00000400 int open_mode; @@ -106,49 +129,64 @@ struct dma_buffparms { int subdivision; int fragment_size; + int needs_reorg; int max_fragments; int bytes_in_use; int underrun_count; - int byte_counter; + 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 */ + #ifdef OS_DMA_PARMS OS_DMA_PARMS #endif + 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 soundcards. + * in the recent sound cards. */ -typedef struct coproc_operations { - char name[32]; - int (*open) (void *devc, int sub_device); - void (*close) (void *devc, int sub_device); - int (*ioctl) (void *devc, unsigned int cmd, caddr_t arg, int local); - void (*reset) (void *devc); +typedef struct coproc_operations +{ + char name[64]; + int (*open) (void *devc, int sub_device); + void (*close) (void *devc, int sub_device); + int (*ioctl) (void *devc, unsigned int cmd, caddr_t arg, int local); + void (*reset) (void *devc); - void *devc; /* Driver specific info */ - } coproc_operations; + void *devc; /* Driver specific info */ +} coproc_operations; -struct audio_driver { +struct audio_driver +{ int (*open) (int dev, int mode); void (*close) (int dev); void (*output_block) (int dev, unsigned long buf, - int count, int intrflag, int dma_restart); + int count, int intrflag); void (*start_input) (int dev, unsigned long buf, - int count, int intrflag, int dma_restart); - int (*ioctl) (int dev, unsigned int cmd, caddr_t arg, int local); + int count, int intrflag); + int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); int (*prepare_for_input) (int dev, int bufsize, int nbufs); int (*prepare_for_output) (int dev, int bufsize, int nbufs); - void (*reset) (int dev); - void (*halt_xfer) (int dev); + void (*halt_io) (int dev); int (*local_qlen)(int dev); - void (*copy_from_user)(int dev, char *localbuf, int localoffs, + void (*copy_user)(int dev, char *localbuf, int localoffs, const char *userbuf, int useroffs, int len); void (*halt_input) (int dev); void (*halt_output) (int dev); @@ -156,10 +194,13 @@ struct audio_driver { 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 */ }; -struct audio_operations { - char name[32]; +struct audio_operations +{ + char name[128]; int flags; #define NOTHING_SPECIAL 0x00 #define NEEDS_RESTART 0x01 @@ -167,11 +208,12 @@ struct audio_operations { #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; - long buffsize; - int dmachan1, dmachan2; + void *portc; /* Driver spesific info */ struct dma_buffparms *dmap_in, *dmap_out; struct coproc_operations *coproc; int mixer_dev; @@ -179,17 +221,46 @@ struct audio_operations { 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 */ + struct wait_queue *in_sleeper; + struct wait_queue *out_sleeper; + + /* fields formerly in audio.c */ + int audio_mode; + /* why don't we use file->f_flags & O_NONBLOCK for the following? - ts */ + int dev_nblock; /* 1 if in nonblocking 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]; }; -struct mixer_operations { +int *load_mixer_volumes(char *name, int *levels, int present); + +struct mixer_operations +{ char id[16]; - char name[32]; + char name[64]; int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); void *devc; + int modify_counter; }; -struct synth_operations { +struct synth_operations +{ + char *id; /* Unique identifier (ASCII) max 29 char */ struct synth_info *info; int midi_dev; int synth_type; @@ -209,7 +280,6 @@ struct synth_operations { 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); - int (*pmgr_interface) (int dev, struct patmgr_info *info); 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); @@ -217,22 +287,31 @@ struct synth_operations { 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 */ +struct midi_input_info +{ + /* MIDI input scanner variables */ #define MI_MAX 10 - int m_busy; - unsigned char m_buf[MI_MAX]; - unsigned char m_prev_status; /* For running status */ - int m_ptr; + 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; - }; + int m_state; + int m_left; +}; -struct midi_operations { +struct midi_operations +{ struct midi_info info; struct synth_operations *converter; struct midi_input_info in_info; @@ -242,7 +321,7 @@ struct midi_operations { ); void (*close) (int dev); int (*ioctl) (int dev, unsigned int cmd, caddr_t arg); - int (*putc) (int dev, unsigned char data); + int (*outputc) (int dev, unsigned char data); int (*start_read) (int dev); int (*end_read) (int dev); void (*kick)(int dev); @@ -253,14 +332,17 @@ struct midi_operations { void *devc; }; -struct sound_lowlev_timer { - int dev; - unsigned int (*tmr_start)(int dev, unsigned int usecs); - void (*tmr_disable)(int dev); - void (*tmr_restart)(int dev); - }; +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 sound_timer_operations +{ struct sound_timer_info info; int priority; int devlink; @@ -273,253 +355,335 @@ struct sound_timer_operations { }; #ifdef _DEV_TABLE_C_ - struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL}; int num_audiodevs = 0; - struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0; - struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0; - struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0; - -#ifdef CONFIG_SEQUENCER - extern struct sound_timer_operations default_sound_timer; - struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = - {&default_sound_timer, NULL}; - int num_sound_timers = 1; + +struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL}; int num_audiodevs = 0; +struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0; +struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0; +struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0; + +#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS) && !defined(VMIDI) +extern struct sound_timer_operations default_sound_timer; +struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { + &default_sound_timer, NULL +}; +int num_sound_timers = 1; #else - struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = - {NULL}; - int num_sound_timers = 0; +struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { + NULL +}; +int num_sound_timers = 0; #endif /* * List of low level drivers compiled into the kernel. */ - struct driver_info sound_drivers[] = { -#ifdef CONFIG_PSS - {"PSS", 0, SNDCARD_PSS, "Echo Personal Sound System PSS (ESC614)", attach_pss, probe_pss, unload_pss}, - {"PSSMPU", 0, SNDCARD_PSS_MPU, "PSS-MPU", attach_pss_mpu, probe_pss_mpu, unload_pss_mpu}, - {"PSSMSS", 0, SNDCARD_PSS_MSS, "PSS-MSS", attach_pss_mss, probe_pss_mss, unload_pss_mss}, +struct driver_info sound_drivers[] = +{ +#ifdef CONFIG_SOUND_PSS + {"PSS", 0, SNDCARD_PSS, "Echo Personal Sound System PSS (ESC614)", attach_pss, probe_pss, unload_pss}, + {"PSSMPU", 0, SNDCARD_PSS_MPU, "PSS-MPU", attach_pss_mpu, probe_pss_mpu, unload_pss_mpu}, + {"PSSMSS", 0, SNDCARD_PSS_MSS, "PSS-MSS", attach_pss_mss, probe_pss_mss, unload_pss_mss}, +#endif + +#ifdef CONFIG_SOUND_GUS +#ifdef CONFIG_GUS16 + {"GUS16", 0, SNDCARD_GUS16, "Ultrasound 16-bit opt.", attach_gus_db16, probe_gus_db16, unload_gus_db16}, +#endif +#ifdef CONFIG_GUS + {"GUS", 0, SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus, unload_gus}, + {"GUSPNP", 1, SNDCARD_GUSPNP, "GUS PnP", attach_gus_card, probe_gus, unload_gus}, #endif -#ifdef CONFIG_MSS - {"MSS", 0, SNDCARD_MSS, "MS Sound System", attach_ms_sound, probe_ms_sound, unload_ms_sound}, - /* MSS without IRQ/DMA config registers (for DEC Alphas) */ - {"PCXBJ", 1, SNDCARD_PSEUDO_MSS, "MS Sound System (AXP)", attach_ms_sound, probe_ms_sound, unload_ms_sound}, +#endif + +#ifdef CONFIG_SOUND_MSS + {"MSS", 0, SNDCARD_MSS, "MS Sound System", attach_ms_sound, probe_ms_sound, unload_ms_sound}, /* Compaq Deskpro XL */ - {"DESKPROXL", 2, SNDCARD_DESKPROXL, "Compaq Deskpro XL", attach_ms_sound, probe_ms_sound, unload_ms_sound}, + {"DESKPROXL", 2, SNDCARD_DESKPROXL, "Compaq Deskpro XL", attach_ms_sound, probe_ms_sound, unload_ms_sound}, +#endif + +#ifdef CONFIG_SOUND_MAD16 + {"MAD16", 0, SNDCARD_MAD16, "MAD16/Mozart (MSS)", attach_mad16, probe_mad16, unload_mad16}, + {"MAD16MPU", 0, SNDCARD_MAD16_MPU, "MAD16/Mozart (MPU)", attach_mad16_mpu, probe_mad16_mpu, unload_mad16_mpu}, +#endif + +#ifdef CONFIG_SOUND_CS4232 + {"CS4232", 0, SNDCARD_CS4232, "CS4232", attach_cs4232, probe_cs4232, unload_cs4232}, +#endif +#ifdef CONFIG_CS4232_MPU_BASE + {"CS4232MPU", 0, SNDCARD_CS4232_MPU, "CS4232 MIDI", attach_cs4232_mpu, probe_cs4232_mpu, unload_cs4232_mpu}, #endif -#ifdef CONFIG_MAD16 - {"MAD16", 0, SNDCARD_MAD16, "MAD16/Mozart (MSS)", attach_mad16, probe_mad16, unload_mad16}, - {"MAD16MPU", 0, SNDCARD_MAD16_MPU, "MAD16/Mozart (MPU)", attach_mad16_mpu, probe_mad16_mpu, unload_mad16_mpu}, + +#ifdef CONFIG_SGALAXY + {"SGALAXY", 0, SNDCARD_SGALAXY, "Sound Galaxy WSS", attach_sgalaxy, probe_sgalaxy, unload_sgalaxy}, +#endif + +#ifdef CONFIG_SOUND_YM3812 + {"OPL3", 0, SNDCARD_ADLIB, "OPL-2/OPL-3 FM", attach_adlib_card, probe_adlib, unload_adlib}, #endif -#ifdef CONFIG_CS4232 - {"CS4232", 0, SNDCARD_CS4232, "CS4232", attach_cs4232, probe_cs4232, unload_cs4232}, - {"CS4232MPU", 0, SNDCARD_CS4232_MPU, "CS4232 MIDI", attach_cs4232_mpu, probe_cs4232_mpu, unload_cs4232_mpu}, + +#ifdef CONFIG_SOUND_PAS + {"PAS16", 0, SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas, unload_pas}, #endif -#ifdef CONFIG_YM3812 - {"OPL3", 0, SNDCARD_ADLIB, "OPL-2/OPL-3 FM", attach_adlib_card, probe_adlib, unload_adlib}, + +#if (defined(CONFIG_SOUND_MPU401) || defined(CONFIG_SOUND_MPU_EMU)) && defined(CONFIG_MIDI) + {"MPU401", 0, SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401, unload_mpu401}, #endif -#ifdef CONFIG_PAS - {"PAS16", 0, SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas, unload_pas}, + +#if defined(CONFIG_SOUND_UART401) && defined(CONFIG_MIDI) + {"UART401", 0, SNDCARD_UART401,"MPU-401 (UART)", + attach_uart401, probe_uart401, unload_uart401}, #endif -#if defined(CONFIG_MPU401) && defined(CONFIG_MIDI) - {"MPU401", 0, SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401, unload_mpu401}, + +#if defined(CONFIG_SOUND_WAVEFRONT) + {"WAVEFRONT", 0, SNDCARD_WAVEFRONT,"TB WaveFront", attach_wavefront, probe_wavefront, unload_wavefront}, #endif -#if defined(CONFIG_MAUI) - {"MAUI", 0, SNDCARD_MAUI,"TB Maui", attach_maui, probe_maui, unload_maui}, + +#if defined(CONFIG_SOUND_MAUI) + {"MAUI", 0, SNDCARD_MAUI,"TB Maui", attach_maui, probe_maui, unload_maui}, #endif -#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) - {"MIDI6850", 0, SNDCARD_UART6850,"6860 UART Midi", attach_uart6850, probe_uart6850, unload_uart6850}, + +#if defined(CONFIG_SOUND_UART6850) && defined(CONFIG_MIDI) + {"MIDI6850", 0, SNDCARD_UART6850,"6860 UART Midi", attach_uart6850, probe_uart6850, unload_uart6850}, #endif -#ifdef CONFIG_SB - {"SBLAST", 0, SNDCARD_SB, "Sound Blaster", attach_sb_card, probe_sb, unload_sb}, + + + + +#ifdef CONFIG_SOUND_SBDSP + {"SBLAST", 0, SNDCARD_SB, "Sound Blaster", attach_sb_card, probe_sb, unload_sb}, + {"SBPNP", 6, SNDCARD_SBPNP, "Sound Blaster PnP", attach_sb_card, probe_sb, unload_sb}, + #ifdef CONFIG_MIDI - {"SBMPU", 0, SNDCARD_SB16MIDI,"SB MPU-401", attach_sbmpu, probe_sbmpu, unload_sbmpu}, + {"SBMPU", 0, SNDCARD_SB16MIDI,"SB MPU-401", attach_sbmpu, probe_sbmpu, unload_sbmpu}, #endif #endif -#ifdef CONFIG_GUS16 - {"GUS16", 0, SNDCARD_GUS16, "Ultrasound 16-bit opt.", attach_gus_db16, probe_gus_db16, unload_gus_db16}, + +#ifdef CONFIG_SOUND_SSCAPE + {"SSCAPE", 0, SNDCARD_SSCAPE, "Ensoniq SoundScape", attach_sscape, probe_sscape, unload_sscape}, + {"SSCAPEMSS", 0, SNDCARD_SSCAPE_MSS, "MS Sound System (SoundScape)", attach_ss_ms_sound, probe_ss_ms_sound, unload_ss_ms_sound}, #endif -#ifdef CONFIG_GUS - {"GUS", 0, SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus, unload_gus}, - {"GUSPNP", 1, SNDCARD_GUSPNP, "GUS PnP", attach_gus_card, probe_gus, unload_gus}, + +#ifdef CONFIG_SOUND_OPL3SA1 + {"OPL3SA", 0, SNDCARD_OPL3SA1, "Yamaha OPL3-SA", attach_opl3sa_wss, probe_opl3sa_wss, unload_opl3sa_wss}, +/* {"OPL3SASB", 0, SNDCARD_OPL3SA1_SB, "OPL3-SA (SB mode)", attach_opl3sa_sb, probe_opl3sa_sb, unload_opl3sa_sb}, */ + {"OPL3SAMPU", 0, SNDCARD_OPL3SA1_MPU, "OPL3-SA MIDI", attach_opl3sa_mpu, probe_opl3sa_mpu, unload_opl3sa_mpu}, #endif -#ifdef CONFIG_SSCAPE - {"SSCAPE", 0, SNDCARD_SSCAPE, "Ensoniq SoundScape", attach_sscape, probe_sscape, unload_sscape}, - {"SSCAPEMSS", 0, SNDCARD_SSCAPE_MSS, "MS Sound System (SoundScape)", attach_ss_ms_sound, probe_ss_ms_sound, unload_ss_ms_sound}, + +#ifdef CONFIG_SOUND_TRIX + {"TRXPRO", 0, SNDCARD_TRXPRO, "MediaTrix AudioTrix Pro", attach_trix_wss, probe_trix_wss, unload_trix_wss}, + {"TRXPROSB", 0, SNDCARD_TRXPRO_SB, "AudioTrix (SB mode)", attach_trix_sb, probe_trix_sb, unload_trix_sb}, + {"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTrix MIDI", attach_trix_mpu, probe_trix_mpu, unload_trix_mpu}, #endif -#ifdef CONFIG_TRIX - {"TRXPRO", 0, SNDCARD_TRXPRO, "MediaTrix AudioTrix Pro", attach_trix_wss, probe_trix_wss, unload_trix_wss}, - {"TRXPROSB", 0, SNDCARD_TRXPRO_SB, "AudioTrix (SB mode)", attach_trix_sb, probe_trix_sb, unload_trix_sb}, - {"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTrix MIDI", attach_trix_mpu, probe_trix_mpu, unload_trix_mpu}, + +#ifdef CONFIG_SOUND_SOFTOSS + {"SOFTSYN", 0, SNDCARD_SOFTOSS, "SoftOSS Virtual Wave Table", + attach_softsyn_card, probe_softsyn, unload_softsyn}, #endif - {NULL, 0, 0, "*?*", NULL, NULL, NULL} - }; - int num_sound_drivers = - sizeof(sound_drivers) / sizeof (struct driver_info); - int max_sound_drivers = - sizeof(sound_drivers) / sizeof (struct driver_info); +#if defined(CONFIG_SOUND_VMIDI) && defined(CONFIG_MIDI) + {"VMIDI", 0, SNDCARD_VMIDI,"Loopback MIDI Device", attach_v_midi, probe_v_midi, unload_v_midi}, +#endif +#ifdef CONFIG_VIDC_SOUND + {"VIDC", 0, SNDCARD_VIDC, "ARM VIDC 16-bit D/A", attach_vidc, probe_vidc, unload_vidc }, +#endif + {NULL, 0, 0, "*?*", NULL, NULL, NULL} +}; + +int num_sound_drivers = sizeof(sound_drivers) / sizeof (struct driver_info); #ifndef FULL_SOUND + /* * List of devices actually configured in the system. * * Note! The detection order is significant. Don't change it. */ - struct card_info snd_installed_cards[] = { -#ifdef CONFIG_PSS - {SNDCARD_PSS, {PSS_BASE, 0, -1, -1}, SND_DEFAULT_ENABLE}, -# ifdef PSS_MPU_BASE - {SNDCARD_PSS_MPU, {PSS_MPU_BASE, PSS_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, -# endif -# ifdef PSS_MSS_BASE - {SNDCARD_PSS_MSS, {PSS_MSS_BASE, PSS_MSS_IRQ, PSS_MSS_DMA, -1}, SND_DEFAULT_ENABLE}, -# endif +struct card_info snd_installed_cards[] = +{ +#ifdef CONFIG_SOUND_PSS + {SNDCARD_PSS, {CONFIG_PSS_BASE, 0, -1, -1}, SND_DEFAULT_ENABLE}, +#ifdef CONFIG_PSS_MPU_BASE + {SNDCARD_PSS_MPU, {CONFIG_PSS_MPU_BASE, CONFIG_PSS_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +#endif +#ifdef CONFIG_PSS_MSS_BASE + {SNDCARD_PSS_MSS, {CONFIG_PSS_MSS_BASE, CONFIG_PSS_MSS_IRQ, CONFIG_PSS_MSS_DMA, -1}, SND_DEFAULT_ENABLE}, +#endif +#endif + +#ifdef CONFIG_SOUND_TRIX +#ifndef CONFIG_TRIX_DMA2 +#define CONFIG_TRIX_DMA2 CONFIG_TRIX_DMA +#endif + {SNDCARD_TRXPRO, {CONFIG_TRIX_BASE, CONFIG_TRIX_IRQ, CONFIG_TRIX_DMA, CONFIG_TRIX_DMA2}, SND_DEFAULT_ENABLE}, +#ifdef CONFIG_TRIX_SB_BASE + {SNDCARD_TRXPRO_SB, {CONFIG_TRIX_SB_BASE, CONFIG_TRIX_SB_IRQ, CONFIG_TRIX_SB_DMA, -1}, SND_DEFAULT_ENABLE}, +#endif +#ifdef CONFIG_TRIX_MPU_BASE + {SNDCARD_TRXPRO_MPU, {CONFIG_TRIX_MPU_BASE, CONFIG_TRIX_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +#endif +#endif + +#ifdef CONFIG_SOUND_OPL3SA1 + {SNDCARD_OPL3SA1, {CONFIG_OPL3SA1_BASE, CONFIG_OPL3SA1_IRQ, CONFIG_OPL3SA1_DMA, CONFIG_OPL3SA1_DMA2}, SND_DEFAULT_ENABLE}, +#ifdef CONFIG_OPL3SA1_MPU_BASE + {SNDCARD_OPL3SA1_MPU, {CONFIG_OPL3SA1_MPU_BASE, CONFIG_OPL3SA1_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +#endif +#endif + +#ifdef CONFIG_SOUND_SOFTOSS + {SNDCARD_SOFTOSS, {0, 0, -1, -1}, SND_DEFAULT_ENABLE}, #endif -#ifdef CONFIG_TRIX -#ifndef TRIX_DMA2 -#define TRIX_DMA2 TRIX_DMA + +#ifdef CONFIG_SOUND_SSCAPE + {SNDCARD_SSCAPE, {CONFIG_SSCAPE_BASE, CONFIG_SSCAPE_IRQ, CONFIG_SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE}, + {SNDCARD_SSCAPE_MSS, {CONFIG_SSCAPE_MSS_BASE, CONFIG_SSCAPE_MSS_IRQ, CONFIG_SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE}, +#endif + +#ifdef CONFIG_SOUND_MAD16 +#ifndef CONFIG_MAD16_DMA2 +#define CONFIG_MAD16_DMA2 CONFIG_MAD16_DMA #endif - {SNDCARD_TRXPRO, {TRIX_BASE, TRIX_IRQ, TRIX_DMA, TRIX_DMA2}, SND_DEFAULT_ENABLE}, -# ifdef TRIX_SB_BASE - {SNDCARD_TRXPRO_SB, {TRIX_SB_BASE, TRIX_SB_IRQ, TRIX_SB_DMA, -1}, SND_DEFAULT_ENABLE}, -# endif -# ifdef TRIX_MPU_BASE - {SNDCARD_TRXPRO_MPU, {TRIX_MPU_BASE, TRIX_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, -# endif + {SNDCARD_MAD16, {CONFIG_MAD16_BASE, CONFIG_MAD16_IRQ, CONFIG_MAD16_DMA, CONFIG_MAD16_DMA2}, SND_DEFAULT_ENABLE}, +#ifdef CONFIG_MAD16_MPU_BASE + {SNDCARD_MAD16_MPU, {CONFIG_MAD16_MPU_BASE, CONFIG_MAD16_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif -#ifdef CONFIG_SSCAPE - {SNDCARD_SSCAPE, {SSCAPE_BASE, SSCAPE_IRQ, SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE}, - {SNDCARD_SSCAPE_MSS, {SSCAPE_MSS_BASE, SSCAPE_MSS_IRQ, SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE}, #endif -#ifdef CONFIG_MAD16 -#ifndef MAD16_DMA2 -#define MAD16_DMA2 MAD16_DMA + +#ifdef CONFIG_SOUND_CS4232 +#ifndef CONFIG_CS4232_DMA2 +#define CONFIG_CS4232_DMA2 CONFIG_CS4232_DMA #endif - {SNDCARD_MAD16, {MAD16_BASE, MAD16_IRQ, MAD16_DMA, MAD16_DMA2}, SND_DEFAULT_ENABLE}, -# ifdef MAD16_MPU_BASE - {SNDCARD_MAD16_MPU, {MAD16_MPU_BASE, MAD16_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, -# endif +#ifdef CONFIG_CS4232_MPU_BASE + {SNDCARD_CS4232_MPU, {CONFIG_CS4232_MPU_BASE, CONFIG_CS4232_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +#endif + {SNDCARD_CS4232, {CONFIG_CS4232_BASE, CONFIG_CS4232_IRQ, CONFIG_CS4232_DMA, CONFIG_CS4232_DMA2}, SND_DEFAULT_ENABLE}, #endif -#ifdef CONFIG_CS4232 -#ifndef CS4232_DMA2 -#define CS4232_DMA2 CS4232_DMA +#ifdef CONFIG_SGALAXY +#ifndef CONFIG_SGALAXY_DMA2 +#define CONFIG_SGALAXY_DMA2 CONFIG_SGALAXY_DMA +#endif + {SNDCARD_SGALAXY, {CONFIG_SGALAXY_BASE, CONFIG_SGALAXY_IRQ, CONFIG_SGALAXY_DMA, CONFIG_SGALAXY_DMA2, 0, NULL, CONFIG_SGALAXY_SGBASE}, SND_DEFAULT_ENABLE}, #endif -# ifdef CS4232_MPU_BASE - {SNDCARD_CS4232_MPU, {CS4232_MPU_BASE, CS4232_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, -# endif - {SNDCARD_CS4232, {CS4232_BASE, CS4232_IRQ, CS4232_DMA, CS4232_DMA2}, SND_DEFAULT_ENABLE}, + + +#ifdef CONFIG_SOUND_MSS +#ifndef CONFIG_MSS_DMA2 +#define CONFIG_MSS_DMA2 -1 #endif +#ifdef DESKPROXL + {SNDCARD_DESKPROXL, {CONFIG_MSS_BASE, CONFIG_MSS_IRQ, CONFIG_MSS_DMA, CONFIG_MSS_DMA2}, SND_DEFAULT_ENABLE}, +#else + {SNDCARD_MSS, {CONFIG_MSS_BASE, CONFIG_MSS_IRQ, CONFIG_MSS_DMA, CONFIG_MSS_DMA2}, SND_DEFAULT_ENABLE}, +#endif -#ifdef CONFIG_MSS -# ifdef DESKPROXL - {SNDCARD_DESKPROXL, {MSS_BASE, MSS_IRQ, MSS_DMA, -1}, SND_DEFAULT_ENABLE}, -# else - {SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA, -1}, SND_DEFAULT_ENABLE}, -# endif -# ifdef MSS2_BASE - {SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA, -1}, SND_DEFAULT_ENABLE}, -# endif +#ifdef MSS2_BASE + {SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA, MSS2_DMA2}, SND_DEFAULT_ENABLE}, +#endif #endif +#ifdef CONFIG_SOUND_PAS + {SNDCARD_PAS, {CONFIG_PAS_BASE, CONFIG_PAS_IRQ, CONFIG_PAS_DMA, -1}, SND_DEFAULT_ENABLE}, +#endif -#ifdef CONFIG_PAS - {SNDCARD_PAS, {PAS_BASE, PAS_IRQ, PAS_DMA, -1}, SND_DEFAULT_ENABLE}, +#ifdef CONFIG_SOUND_SB +#ifndef CONFIG_SB_DMA +#define CONFIG_SB_DMA 1 +#endif +#ifndef CONFIG_SB_DMA2 +#define CONFIG_SB_DMA2 -1 +#endif + {SNDCARD_SB, {CONFIG_SB_BASE, CONFIG_SB_IRQ, CONFIG_SB_DMA, CONFIG_SB_DMA2}, SND_DEFAULT_ENABLE}, +#ifdef SB2_BASE + {SNDCARD_SB, {SB2_BASE, SB2_IRQ, SB2_DMA, SB2_DMA2}, SND_DEFAULT_ENABLE}, +#endif #endif -#ifdef CONFIG_SB -# ifndef SBC_DMA -# define SBC_DMA 1 -# endif -# ifndef SB_DMA2 -# define SB_DMA2 -1 -# endif - {SNDCARD_SB, {SBC_BASE, SBC_IRQ, SBC_DMA, SB_DMA2}, SND_DEFAULT_ENABLE}, -# ifdef SB2_BASE - {SNDCARD_SB, {SB2_BASE, SB2_IRQ, SB2_DMA, SB2_DMA2}, SND_DEFAULT_ENABLE}, -# endif +#if defined(CONFIG_WAVEFRONT) + {SNDCARD_WAVEFRONT, {WAVEFRONT_BASE, WAVEFRONT_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif -#if defined(CONFIG_MAUI) - {SNDCARD_MAUI, {MAUI_BASE, MAUI_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, + +#ifdef CONFIG_SOUND_MAUI + {SNDCARD_MAUI, {CONFIG_MAUI_BASE, CONFIG_MAUI_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif -#if defined(CONFIG_MPU401) && defined(CONFIG_MIDI) - {SNDCARD_MPU401, {MPU_BASE, MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +#if defined(CONFIG_SOUND_MPU401) && defined(CONFIG_MIDI) + {SNDCARD_MPU401, {CONFIG_MPU_BASE, CONFIG_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #ifdef MPU2_BASE - {SNDCARD_MPU401, {MPU2_BASE, MPU2_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, + {SNDCARD_MPU401, {MPU2_BASE, MPU2_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif #ifdef MPU3_BASE - {SNDCARD_MPU401, {MPU3_BASE, MPU2_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, + {SNDCARD_MPU401, {MPU3_BASE, MPU3_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif #endif -#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) - {SNDCARD_UART6850, {U6850_BASE, U6850_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +#if defined(CONFIG_SOUND_UART6850) && defined(CONFIG_MIDI) + {SNDCARD_UART6850, {CONFIG_U6850_BASE, CONFIG_U6850_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif -#if defined(CONFIG_SB) -#if defined(CONFIG_MIDI) && defined(SB_MPU_BASE) - {SNDCARD_SB16MIDI,{SB_MPU_BASE, SB_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +#ifdef CONFIG_SOUND_SB +#if defined(CONFIG_MIDI) && defined(CONFIG_SB_MPU_BASE) + {SNDCARD_SB16MIDI,{CONFIG_SB_MPU_BASE, CONFIG_SB_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, #endif #endif -#ifdef CONFIG_GUS -#ifndef GUS_DMA2 -#define GUS_DMA2 GUS_DMA +#ifdef CONFIG_SOUND_GUS +#ifndef CONFIG_GUS_DMA2 +#define CONFIG_GUS_DMA2 CONFIG_GUS_DMA #endif #ifdef CONFIG_GUS16 - {SNDCARD_GUS16, {GUS16_BASE, GUS16_IRQ, GUS16_DMA, -1}, SND_DEFAULT_ENABLE}, + {SNDCARD_GUS16, {CONFIG_GUS16_BASE, CONFIG_GUS16_IRQ, CONFIG_GUS16_DMA, -1}, SND_DEFAULT_ENABLE}, +#endif + {SNDCARD_GUS, {CONFIG_GUS_BASE, CONFIG_GUS_IRQ, CONFIG_GUS_DMA, CONFIG_GUS_DMA2}, SND_DEFAULT_ENABLE}, #endif - {SNDCARD_GUS, {GUS_BASE, GUS_IRQ, GUS_DMA, GUS_DMA2}, SND_DEFAULT_ENABLE}, + +#ifdef CONFIG_SOUND_YM3812 + {SNDCARD_ADLIB, {FM_MONO, 0, 0, -1}, SND_DEFAULT_ENABLE}, +#endif + +#if defined(CONFIG_SOUND_VMIDI) && defined(CONFIG_MIDI) + {SNDCARD_VMIDI, {0, 0, 0, -1}, SND_DEFAULT_ENABLE}, #endif -#ifdef CONFIG_YM3812 - {SNDCARD_ADLIB, {FM_MONO, 0, 0, -1}, SND_DEFAULT_ENABLE}, +#ifdef CONFIG_VIDC_SOUND + { SNDCARD_VIDC, {0, 0, 0, 0}, SND_DEFAULT_ENABLE }, #endif -/* Define some expansion space */ - {0, {0}, 0}, - {0, {0}, 0}, - {0, {0}, 0}, - {0, {0}, 0}, - {0, {0}, 0} - }; + {0, {0}, 0} +}; - int num_sound_cards = - sizeof(snd_installed_cards) / sizeof (struct card_info); - int max_sound_cards = - sizeof(snd_installed_cards) / sizeof (struct card_info); +int num_sound_cards = sizeof(snd_installed_cards) / sizeof (struct card_info); +static int max_sound_cards = sizeof(snd_installed_cards) / sizeof (struct card_info); #else - int num_sound_cards = 0; - struct card_info snd_installed_cards[20] = {{0}}; - int max_sound_cards = 20; +int num_sound_cards = 0; +struct card_info snd_installed_cards[20] = {{0}}; +static int max_sound_cards = 20; #endif -# ifdef MODULE - int trace_init = 0; -# else - int trace_init = 1; -# endif +#if defined(MODULE) || (!defined(linux) && !defined(_AIX)) +int trace_init = 0; #else - 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 struct driver_info sound_drivers[]; - extern int num_sound_drivers; - extern int max_sound_drivers; - extern struct card_info snd_installed_cards[]; - extern int num_sound_cards; - extern int max_sound_cards; - - extern int trace_init; -#endif /* _DEV_TABLE_C_ */ +int trace_init = 1; +#endif +#else +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 struct driver_info sound_drivers[]; +extern int num_sound_drivers; +extern struct card_info snd_installed_cards[]; +extern int num_sound_cards; + +extern int trace_init; +#endif /* _DEV_TABLE_C_ */ void sndtable_init(void); int sndtable_get_cardcount (void); struct address_info *sound_getconf(int card_type); @@ -530,34 +694,30 @@ void sound_unload_driver(int type); int sndtable_identify_card(char *name); void sound_setup (char *str, int *ints); -int sound_alloc_dmap (int dev, struct dma_buffparms *dmap, int chan); -void sound_free_dmap (int dev, struct dma_buffparms *dmap); extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info); -void install_pnp_sounddrv(struct pnp_sounddev *drv); int sndtable_probe (int unit, struct address_info *hw_config); int sndtable_init_card (int unit, struct address_info *hw_config); int sndtable_start_card (int unit, struct address_info *hw_config); void sound_timer_init (struct sound_lowlev_timer *t, char *name); -int sound_start_dma ( int dev, struct dma_buffparms *dmap, int chan, - unsigned long physaddr, - int count, int dma_mode, int autoinit); void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan); -#define AUDIO_DRIVER_VERSION 1 -#define MIXER_DRIVER_VERSION 1 -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); - +#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_audiodev(void); +int sound_alloc_mixerdev(void); +int sound_alloc_timerdev(void); +int sound_alloc_synthdev(void); +int sound_alloc_mididev(void); #endif /* _DEV_TABLE_H_ */ + diff --git a/drivers/sound/dm.h b/drivers/sound/dm.h new file mode 100644 index 000000000000..14a90593c44f --- /dev/null +++ b/drivers/sound/dm.h @@ -0,0 +1,79 @@ +#ifndef _DRIVERS_SOUND_DM_H +#define _DRIVERS_SOUND_DM_H + +/* + * Definitions of the 'direct midi sound' interface used + * by the newer commercial OSS package. We should export + * this to userland somewhere in glibc later. + */ + +/* + * Data structure composing an FM "note" or sound event. + */ + +struct dm_fm_voice +{ + u8 op; + u8 voice; + u8 am; + u8 vibrato; + u8 do_sustain; + u8 kbd_scale; + u8 harmonic; + u8 scale_level; + u8 volume; + u8 attack; + u8 decay; + u8 sustain; + u8 release; + u8 feedback; + u8 connection; + u8 left; + u8 right; + u8 waveform; +}; + +/* + * This describes an FM note by its voice, octave, frequency number (10bit) + * and key on/off. + */ + +struct dm_fm_note +{ + u8 voice; + u8 octave; + u32 fnum; + u8 key_on; +}; + +/* + * FM parameters that apply globally to all voices, and thus are not "notes" + */ + +struct dm_fm_params +{ + u8 am_depth; + u8 vib_depth; + u8 kbd_split; + u8 rhythm; + + /* This block is the percussion instrument data */ + u8 bass; + u8 snare; + u8 tomtom; + u8 cymbal; + u8 hihat; +}; + +/* + * FM mode ioctl settings + */ + +#define FM_IOCTL_RESET 0x20 +#define FM_IOCTL_PLAY_NOTE 0x21 +#define FM_IOCTL_SET_VOICE 0x22 +#define FM_IOCTL_SET_PARAMS 0x23 +#define FM_IOCTL_SET_MODE 0x24 +#define FM_IOCTL_SET_OPL 0x25 + +#endif diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c index 7f0fcf4aa99d..071cdb4764ee 100644 --- a/drivers/sound/dmabuf.c +++ b/drivers/sound/dmabuf.c @@ -4,1920 +4,1253 @@ * The DMA buffer manager for digitized voice applications */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 : 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 : remove {in,out}_sleep_flag. It was used for the sleeper to + * determine if it was woken up by the expiring timeout or by + * an explicit wake_up. current->timeout can be used instead; + * if 0, the wakeup was due to the timeout. */ #include +#define BE_CONSERVATIVE +#define SAMPLE_ROUNDUP 0 #include "sound_config.h" #if defined(CONFIG_AUDIO) || defined(CONFIG_GUS) -static wait_handle *in_sleeper[MAX_AUDIO_DEV] = -{NULL}; -static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] = -{ - {0}}; -static wait_handle *out_sleeper[MAX_AUDIO_DEV] = -{NULL}; -static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] = -{ - {0}}; -#define NEUTRAL8 0x80 -#define NEUTRAL16 0x00 +static void dma_reset_output(int dev); +static void dma_reset_input(int dev); +static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode); -static int ndmaps = 0; -#define MAX_DMAP (MAX_AUDIO_DEV*2) - -static struct dma_buffparms dmaps[MAX_DMAP] = -{ - {0}}; -static int space_in_queue (int dev); +static int debugmem = 0; /* switched off by default */ +static int dma_buffsize = DSP_BUFFSIZE; -static void dma_reset_output (int dev); -static void dma_reset_input (int dev); -static int dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact); - -static void -reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording) +static void dmabuf_set_timeout(struct dma_buffparms *dmap) { - /* - * 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; - - if (dmap->fragment_size == 0) - { /* Compute the fragment size using the default algorithm */ - - 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; + unsigned long tmout; + + tmout = (dmap->fragment_size * HZ) / dmap->data_rate; + tmout += HZ / 5; /* Some safety distance */ + if (tmout < (HZ / 2)) + tmout = HZ / 2; + if (tmout > 20 * HZ) + tmout = 20 * HZ; + current->timeout = jiffies + tmout; +} - if (sr < 1 || nc < 1 || sz < 1) - { - printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", - dev, sr, nc, sz); - sr = DSP_DEFAULT_SPEED; - nc = 1; - sz = 8; +static int sound_alloc_dmap(struct dma_buffparms *dmap) +{ + char *start_addr, *end_addr; + int i, dma_pagesize; + int sz, size; + + dmap->mapping_flags &= ~DMA_MAP_MAPPED; + + if (dmap->raw_buf != NULL) + return 0; /* Already done */ + if (dma_buffsize < 4096) + dma_buffsize = 4096; + dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024); + dmap->raw_buf = NULL; + dmap->buffsize = dma_buffsize; + if (dmap->buffsize > dma_pagesize) + dmap->buffsize = dma_pagesize; + start_addr = NULL; + /* + * Now loop until we get a free buffer. Try to get smaller buffer if + * it fails. Don't accept smaller than 8k buffer for performance + * reasons. + */ + while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) { + for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); + dmap->buffsize = PAGE_SIZE * (1 << sz); + start_addr = (char *) __get_free_pages(GFP_KERNEL, sz, 1); + if (start_addr == NULL) + dmap->buffsize /= 2; } - sz = sr * nc * sz; - - sz /= 8; /* #bits -> #bytes */ - - /* - * 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 = dsp_dev->buffsize; - while (bsz > sz) - bsz /= 2; - - if (bsz == dsp_dev->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 = 1; /* Init to the default value */ -#ifndef V35A9_COMPATIBLE - if (recording) - dmap->subdivision = 4; /* Use shorter fragments when recording */ -#endif + if (start_addr == NULL) { + printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n"); + return -ENOMEM; + } else { + /* make some checks */ + end_addr = start_addr + dmap->buffsize - 1; + + if (debugmem) + printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr); + + /* now check if it fits into the same dma-pagesize */ + + if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1)) + || end_addr >= (char *) (MAX_DMA_ADDRESS)) { + printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize); + return -EFAULT; + } } + dmap->raw_buf = start_addr; + dmap->raw_buf_phys = virt_to_bus(start_addr); - 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 > (audio_devs[dev]->buffsize / 2)) - dmap->fragment_size = (audio_devs[dev]->buffsize / 2); - bsz = dmap->fragment_size; - } - - bsz &= ~0x03; /* Force size which is multiple of 4 bytes */ -#ifdef OS_DMA_ALIGN_CHECK - OS_DMA_ALIGN_CHECK (bsz); -#endif - - n = dsp_dev->buffsize / bsz; - if (n > MAX_SUB_BUFFERS) - n = MAX_SUB_BUFFERS; - if (n > dmap->max_fragments) - n = dmap->max_fragments; - dmap->nbufs = n; - dmap->bytes_in_use = n * bsz; - - 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 void -dma_init_buffers (int dev, struct dma_buffparms *dmap) -{ - if (dmap == audio_devs[dev]->dmap_out) - { - out_sleep_flag[dev].flags = WK_NONE; - } - else - { - in_sleep_flag[dev].flags = WK_NONE; - } - - dmap->flags = DMA_BUSY; /* Other flags off */ - dmap->qlen = dmap->qhead = dmap->qtail = 0; - dmap->nbufs = 1; - dmap->bytes_in_use = audio_devs[dev]->buffsize; - - dmap->dma_mode = DMODE_NONE; - dmap->mapping_flags = 0; - dmap->neutral_byte = NEUTRAL8; - dmap->cfrag = -1; - dmap->closing = 0; + for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++) + set_bit(PG_reserved, &mem_map[i].flags);; + return 0; } -static int -open_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan) +static void sound_free_dmap(struct dma_buffparms *dmap) { - if (dmap->flags & DMA_BUSY) - return -(EBUSY); - - { - int err; + int sz, size, i; + unsigned long start_addr, end_addr; - if ((err = sound_alloc_dmap (dev, dmap, chan)) < 0) - return err; - } + if (dmap->raw_buf == NULL) + return; + if (dmap->mapping_flags & DMA_MAP_MAPPED) + return; /* Don't free mmapped buffer. Will use it next time */ + for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); - if (dmap->raw_buf == NULL) - return -(ENOSPC); /* Memory allocation failed during boot */ + start_addr = (unsigned long) dmap->raw_buf; + end_addr = start_addr + dmap->buffsize; - if (sound_open_dma (chan, audio_devs[dev]->name)) - { - printk ("Unable to grab(2) DMA%d for the audio driver\n", chan); - return -(EBUSY); - } + for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++) + clear_bit(PG_reserved, &mem_map[i].flags);; - dmap->open_mode = mode; - dmap->subdivision = dmap->underrun_count = 0; - dmap->fragment_size = 0; - dmap->max_fragments = 65536; /* Just a large value */ - dmap->byte_counter = 0; + free_pages((unsigned long) dmap->raw_buf, sz); + dmap->raw_buf = NULL; +} - dma_init_buffers (dev, dmap); - return 0; -} +/* Intel version !!!!!!!!! */ -static void -close_dmap (int dev, struct dma_buffparms *dmap, int chan) +static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode) { - sound_close_dma (chan); + unsigned long flags; + int chan = dmap->dma; + + /* printk( "Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */ + save_flags(flags); + cli(); + disable_dma(chan); + clear_dma_ff(chan); + set_dma_mode(chan, dma_mode); + set_dma_addr(chan, physaddr); + set_dma_count(chan, count); + enable_dma(chan); + restore_flags(flags); - if (dmap->flags & DMA_BUSY) - dmap->dma_mode = DMODE_NONE; - dmap->flags &= ~DMA_BUSY; - - disable_dma (chan); - sound_free_dmap (dev, dmap); + return 0; } -static unsigned int -default_set_bits (int dev, unsigned int bits) +static void dma_init_buffers(struct dma_buffparms *dmap) { - return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) (long) bits, 1); + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; + dmap->byte_counter = 0; + dmap->max_byte_counter = 8000 * 60 * 60; + dmap->bytes_in_use = dmap->buffsize; + + dmap->dma_mode = DMODE_NONE; + dmap->mapping_flags = 0; + dmap->neutral_byte = 0x80; + dmap->data_rate = 8000; + dmap->cfrag = -1; + dmap->closing = 0; + dmap->nbufs = 1; + dmap->flags = DMA_BUSY; /* Other flags off */ } -static int -default_set_speed (int dev, int speed) +static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap) { - return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) (long) speed, 1); + int err; + + if (dmap->flags & DMA_BUSY) + return -EBUSY; + if ((err = sound_alloc_dmap(dmap)) < 0) + return err; + + if (dmap->raw_buf == NULL) { + printk(KERN_WARNING "Sound: DMA buffers not available\n"); + return -ENOSPC; /* Memory allocation failed during boot */ + } + if (sound_open_dma(dmap->dma, adev->name)) { + printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma); + return -EBUSY; + } + dma_init_buffers(dmap); + dmap->open_mode = mode; + dmap->subdivision = dmap->underrun_count = 0; + dmap->fragment_size = 0; + dmap->max_fragments = 65536; /* Just a large value */ + dmap->byte_counter = 0; + dmap->max_byte_counter = 8000 * 60 * 60; + dmap->applic_profile = APF_NORMAL; + dmap->needs_reorg = 1; + dmap->audio_callback = NULL; + dmap->callback_parm = 0; + return 0; } -static short -default_set_channels (int dev, short channels) +static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap) { - int c = channels; - - return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) (long) c, 1); + sound_close_dma(dmap->dma); + if (dmap->flags & DMA_BUSY) + dmap->dma_mode = DMODE_NONE; + dmap->flags &= ~DMA_BUSY; + disable_dma(dmap->dma); } -static void -check_driver (struct audio_driver *d) -{ - if (d->set_speed == NULL) - d->set_speed = default_set_speed; - if (d->set_bits == NULL) - d->set_bits = default_set_bits; - if (d->set_channels == NULL) - d->set_channels = default_set_channels; -} -int -DMAbuf_open (int dev, int mode) +static unsigned int default_set_bits(int dev, unsigned int bits) { - int retval; - struct dma_buffparms *dmap_in = NULL; - struct dma_buffparms *dmap_out = NULL; - - if (dev >= num_audiodevs) - { - /* printk ("PCM device %d not installed.\n", dev); */ - return -(ENXIO); - } - - if (!audio_devs[dev]) - { - /* printk ("PCM device %d not initialized\n", dev); */ - return -(ENXIO); - } - - if (!(audio_devs[dev]->flags & DMA_DUPLEX)) - { - audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out; - audio_devs[dev]->dmachan2 = audio_devs[dev]->dmachan1; - } - - if ((retval = audio_devs[dev]->d->open (dev, mode)) < 0) - return retval; - - check_driver (audio_devs[dev]->d); - - dmap_out = audio_devs[dev]->dmap_out; - dmap_in = audio_devs[dev]->dmap_in; - - if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmachan1)) < 0) - { - audio_devs[dev]->d->close (dev); - return retval; - } - - audio_devs[dev]->enable_bits = mode; - if (mode & OPEN_READ && - audio_devs[dev]->flags & DMA_DUPLEX && dmap_out != dmap_in) - if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmachan2)) < 0) - { - audio_devs[dev]->d->close (dev); - close_dmap (dev, dmap_out, audio_devs[dev]->dmachan1); - return retval; - } - audio_devs[dev]->open_mode = mode; - audio_devs[dev]->go = 1; - in_sleep_flag[dev].flags = WK_NONE; - out_sleep_flag[dev].flags = WK_NONE; - - audio_devs[dev]->d->set_bits (dev, 8); - audio_devs[dev]->d->set_channels (dev, 1); - audio_devs[dev]->d->set_speed (dev, DSP_DEFAULT_SPEED); - - return 0; + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (caddr_t)&bits); + set_fs(fs); + return bits; } -static void -dma_reset (int dev) +static int default_set_speed(int dev, int speed) { - unsigned long flags; - - save_flags (flags); - cli (); - audio_devs[dev]->d->reset (dev); - restore_flags (flags); - - dma_reset_output (dev); + mm_segment_t fs = get_fs(); - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ) - dma_reset_input (dev); + set_fs(get_ds()); + audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (caddr_t)&speed); + set_fs(fs); + return speed; } -static void -dma_reset_output (int dev) +static short default_set_channels(int dev, short channels) { - unsigned long flags; - - save_flags (flags); - cli (); - if (!(audio_devs[dev]->flags & DMA_DUPLEX) || - !audio_devs[dev]->d->halt_output) - audio_devs[dev]->d->reset (dev); - else - audio_devs[dev]->d->halt_output (dev); - restore_flags (flags); - - dma_init_buffers (dev, audio_devs[dev]->dmap_out); - reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0); + int c = channels; + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (caddr_t)&c); + set_fs(fs); + return c; } -static void -dma_reset_input (int dev) +static void check_driver(struct audio_driver *d) { - unsigned long flags; - - save_flags (flags); - cli (); - if (!(audio_devs[dev]->flags & DMA_DUPLEX) || - !audio_devs[dev]->d->halt_input) - audio_devs[dev]->d->reset (dev); - else - audio_devs[dev]->d->halt_input (dev); - restore_flags (flags); - - dma_init_buffers (dev, audio_devs[dev]->dmap_in); - reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1); + if (d->set_speed == NULL) + d->set_speed = default_set_speed; + if (d->set_bits == NULL) + d->set_bits = default_set_bits; + if (d->set_channels == NULL) + d->set_channels = default_set_channels; } -static int -dma_sync (int dev) +int DMAbuf_open(int dev, int mode) { - unsigned long flags; - - if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT)) - return 0; - - if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT) - { - save_flags (flags); - cli (); - - audio_devs[dev]->dmap_out->flags |= DMA_SYNCING; - - audio_devs[dev]->dmap_out->underrun_count = 0; - while (!current_got_fatal_signal () - && audio_devs[dev]->dmap_out->qlen - && audio_devs[dev]->dmap_out->underrun_count == 0) - { - - { - unsigned long tlimit; - - if (HZ) - current_set_timeout (tlimit = jiffies + (HZ)); - else - tlimit = (unsigned long) -1; - out_sleep_flag[dev].flags = WK_SLEEP; - module_interruptible_sleep_on (&out_sleeper[dev]); - if (!(out_sleep_flag[dev].flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - out_sleep_flag[dev].flags |= WK_TIMEOUT; - } - out_sleep_flag[dev].flags &= ~WK_SLEEP; - }; - if ((out_sleep_flag[dev].flags & WK_TIMEOUT)) - { - audio_devs[dev]->dmap_out->flags &= ~DMA_SYNCING; - restore_flags (flags); - return audio_devs[dev]->dmap_out->qlen; - } + struct audio_operations *adev = audio_devs[dev]; + int retval; + struct dma_buffparms *dmap_in = NULL; + struct dma_buffparms *dmap_out = NULL; + + if (!adev) + return -ENXIO; + if (!(adev->flags & DMA_DUPLEX)) + adev->dmap_in = adev->dmap_out; + check_driver(adev->d); + + if ((retval = adev->d->open(dev, mode)) < 0) + return retval; + dmap_out = adev->dmap_out; + dmap_in = adev->dmap_in; + if (dmap_in == dmap_out) + adev->flags &= ~DMA_DUPLEX; + + if (mode & OPEN_WRITE) { + if ((retval = open_dmap(adev, mode, dmap_out)) < 0) { + adev->d->close(dev); + return retval; + } } - audio_devs[dev]->dmap_out->flags &= ~DMA_SYNCING; - restore_flags (flags); - - /* - * Some devices such as GUS have huge amount of on board RAM for the - * audio data. We have to wait until the device has finished playing. - */ - - save_flags (flags); - cli (); - if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ - { - while (!(current_got_fatal_signal ()) - && audio_devs[dev]->d->local_qlen (dev)) - { - - { - unsigned long tlimit; - - if (HZ) - current_set_timeout (tlimit = jiffies + (HZ)); - else - tlimit = (unsigned long) -1; - out_sleep_flag[dev].flags = WK_SLEEP; - module_interruptible_sleep_on (&out_sleeper[dev]); - if (!(out_sleep_flag[dev].flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - out_sleep_flag[dev].flags |= WK_TIMEOUT; - } - out_sleep_flag[dev].flags &= ~WK_SLEEP; - }; - } + adev->enable_bits = mode; + + if (mode == OPEN_READ || (mode != OPEN_WRITE && adev->flags & DMA_DUPLEX)) { + if ((retval = open_dmap(adev, mode, dmap_in)) < 0) { + adev->d->close(dev); + if (mode & OPEN_WRITE) + close_dmap(adev, dmap_out); + return retval; + } } - restore_flags (flags); - } - return audio_devs[dev]->dmap_out->qlen; + adev->open_mode = mode; + adev->go = 1; + + adev->d->set_bits(dev, 8); + adev->d->set_channels(dev, 1); + adev->d->set_speed(dev, DSP_DEFAULT_SPEED); + if (adev->dmap_out->dma_mode == DMODE_OUTPUT) + memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, + adev->dmap_out->bytes_in_use); + return 0; } -int -DMAbuf_release (int dev, int mode) +void DMAbuf_reset(int dev) { - unsigned long flags; - - audio_devs[dev]->dmap_out->closing = 1; - audio_devs[dev]->dmap_in->closing = 1; - - if (!(current_got_fatal_signal ()) - && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)) - { - dma_sync (dev); - } - - if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT) - memset (audio_devs[dev]->dmap_out->raw_buf, - audio_devs[dev]->dmap_out->neutral_byte, - audio_devs[dev]->dmap_out->bytes_in_use); - - save_flags (flags); - cli (); - - audio_devs[dev]->d->halt_xfer (dev); - audio_devs[dev]->d->close (dev); + if (audio_devs[dev]->open_mode & OPEN_WRITE) + dma_reset_output(dev); - close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); - - if (audio_devs[dev]->open_mode & OPEN_READ && - audio_devs[dev]->flags & DMA_DUPLEX) - close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); - audio_devs[dev]->open_mode = 0; - - restore_flags (flags); - - return 0; + if (audio_devs[dev]->open_mode & OPEN_READ) + dma_reset_input(dev); } -static int -activate_recording (int dev, struct dma_buffparms *dmap) +static void dma_reset_output(int dev) { - int prepare = 0; - - if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT)) - return 0; - - if (dmap->flags & DMA_RESTART) - { - dma_reset_input (dev); - dmap->flags &= ~DMA_RESTART; - prepare = 1; - } - - if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */ - { - dma_sync (dev); - dma_reset (dev); - dmap->dma_mode = DMODE_NONE; - } - - if (!(dmap->flags & DMA_ALLOC_DONE)) - reorganize_buffers (dev, dmap, 1); - - if (prepare || !dmap->dma_mode) - { - int err; - - if ((err = audio_devs[dev]->d->prepare_for_input (dev, - dmap->fragment_size, dmap->nbufs)) < 0) - { - return err; - } - dmap->dma_mode = DMODE_INPUT; - } - - if (!(dmap->flags & DMA_ACTIVE)) - { - audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys + - dmap->qtail * dmap->fragment_size, - dmap->fragment_size, 0, - !(audio_devs[dev]->flags & DMA_AUTOMODE) || - !(dmap->flags & DMA_STARTED)); - dmap->flags |= DMA_ACTIVE | DMA_STARTED; - if (audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger (dev, - audio_devs[dev]->enable_bits * audio_devs[dev]->go); - } - return 0; -} - -int -DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) -{ - unsigned long flags; - int err = EIO; - struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; - - save_flags (flags); - cli (); - if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) - { - printk ("Sound: Can't read from mmapped device (1)\n"); - return -(EINVAL); - } - else if (!dmap->qlen) - { - int tmout; - - if ((err = activate_recording (dev, dmap)) < 0) - { - restore_flags (flags); - return err; + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + struct dma_buffparms *dmap = adev->dmap_out; + + if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */ + return; + + /* + * First wait until the current fragment has been played completely + */ + save_flags(flags); + cli(); + adev->dmap_out->flags |= DMA_SYNCING; + + adev->dmap_out->underrun_count = 0; + if (!signal_pending(current) && adev->dmap_out->qlen && + adev->dmap_out->underrun_count == 0) { + dmabuf_set_timeout(dmap); + interruptible_sleep_on(&adev->out_sleeper); + current->timeout = 0; } + adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); - /* Wait for the next block */ - - if (dontblock) - { - restore_flags (flags); - return -(EAGAIN); - } - - if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) & - audio_devs[dev]->go) - { - restore_flags (flags); - return -(EAGAIN); - } - - if (!audio_devs[dev]->go) - tmout = 0; - else - tmout = 10 * HZ; - - - { - unsigned long tlimit; - - if (tmout) - current_set_timeout (tlimit = jiffies + (tmout)); + /* + * Finally shut the device off + */ + if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output) + adev->d->halt_io(dev); else - tlimit = (unsigned long) -1; - in_sleep_flag[dev].flags = WK_SLEEP; - module_interruptible_sleep_on (&in_sleeper[dev]); - if (!(in_sleep_flag[dev].flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - in_sleep_flag[dev].flags |= WK_TIMEOUT; - } - in_sleep_flag[dev].flags &= ~WK_SLEEP; - }; - if ((in_sleep_flag[dev].flags & WK_TIMEOUT)) - { - printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); - err = EIO; - audio_devs[dev]->d->reset (dev); - ; - } - else - err = EINTR; - } - restore_flags (flags); - - if (!dmap->qlen) - return -(err); - - *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]]; - *len = dmap->fragment_size - dmap->counts[dmap->qhead]; - - return dmap->qhead; + adev->d->halt_output(dev); + adev->dmap_out->flags &= ~DMA_STARTED; + clear_dma_ff(dmap->dma); + disable_dma(dmap->dma); + restore_flags(flags); + dmap->byte_counter = 0; + reorganize_buffers(dev, adev->dmap_out, 0); + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; } -int -DMAbuf_rmchars (int dev, int buff_no, int c) +static void dma_reset_input(int dev) { - struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; - - int p = dmap->counts[dmap->qhead] + c; - - if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) - { - printk ("Sound: Can't read from mmapped device (2)\n"); - return -(EINVAL); - } - else if (p >= dmap->fragment_size) - { /* This buffer is completely empty */ - dmap->counts[dmap->qhead] = 0; - if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) - printk ("\nSound: Audio queue1 corrupted for dev%d (%d/%d)\n", - dev, dmap->qlen, dmap->nbufs); - dmap->qlen--; - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - } - else - dmap->counts[dmap->qhead] = p; - - return 0; + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + struct dma_buffparms *dmap = adev->dmap_in; + + save_flags(flags); + cli(); + if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input) + adev->d->halt_io(dev); + else + adev->d->halt_input(dev); + adev->dmap_in->flags &= ~DMA_STARTED; + restore_flags(flags); + + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; + dmap->byte_counter = 0; + reorganize_buffers(dev, adev->dmap_in, 1); } -static int -dma_subdivide (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact) +void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap) { - if (fact == 0) - { - fact = dmap->subdivision; - if (fact == 0) - fact = 1; - return snd_ioctl_return ((int *) arg, 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 snd_ioctl_return ((int *) arg, fact); + struct audio_operations *adev = audio_devs[dev]; + + if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT)) + return; /* Don't start DMA yet */ + dmap->dma_mode = DMODE_OUTPUT; + + if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA) { + if (!(dmap->flags & DMA_STARTED)) { + reorganize_buffers(dev, dmap, 0); + if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs)) + return; + if (!(dmap->flags & DMA_NODMA)) + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE); + dmap->flags |= DMA_STARTED; + } + if (dmap->counts[dmap->qhead] == 0) + dmap->counts[dmap->qhead] = dmap->fragment_size; + dmap->dma_mode = DMODE_OUTPUT; + adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size, + dmap->counts[dmap->qhead], 1); + if (adev->d->trigger) + adev->d->trigger(dev,adev->enable_bits * adev->go); + } + dmap->flags |= DMA_ACTIVE; } -static int -dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact) +int DMAbuf_sync(int dev) { - 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; - - 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; - -#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 > audio_devs[dev]->buffsize) - dmap->fragment_size = audio_devs[dev]->buffsize; - - if (dmap->fragment_size == audio_devs[dev]->buffsize && - audio_devs[dev]->flags & DMA_AUTOMODE) - dmap->fragment_size /= 2; /* Needs at least 2 buffers */ - - dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ - if (arg) - return snd_ioctl_return ((int *) arg, bytes | (count << 16)); - else - return 0; + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + int n = 0; + struct dma_buffparms *dmap; + + if (!adev->go && (!adev->enable_bits & PCM_ENABLE_OUTPUT)) + return 0; + + if (adev->dmap_out->dma_mode == DMODE_OUTPUT) { + dmap = adev->dmap_out; + save_flags(flags); + cli(); + if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE)) + DMAbuf_launch_output(dev, dmap); + adev->dmap_out->flags |= DMA_SYNCING; + adev->dmap_out->underrun_count = 0; + while (!signal_pending(current) && n++ <= adev->dmap_out->nbufs && + adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) { + dmabuf_set_timeout(dmap); + interruptible_sleep_on(&adev->out_sleeper); + if (!current->timeout) { + adev->dmap_out->flags &= ~DMA_SYNCING; + restore_flags(flags); + return adev->dmap_out->qlen; + } + current->timeout = 0; + } + adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); + restore_flags(flags); + + /* + * Some devices such as GUS have huge amount of on board RAM for the + * audio data. We have to wait until the device has finished playing. + */ + + save_flags(flags); + cli(); + if (adev->d->local_qlen) { /* Device has hidden buffers */ + while (!signal_pending(current) && adev->d->local_qlen(dev)) { + dmabuf_set_timeout(dmap); + interruptible_sleep_on(&adev->out_sleeper); + current->timeout = 0; + } + } + restore_flags(flags); + } + adev->dmap_out->dma_mode = DMODE_NONE; + return adev->dmap_out->qlen; } -static int -get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap) +int DMAbuf_release(int dev, int mode) { - int pos; - unsigned long flags; - - save_flags (flags); - cli (); - if (!(dmap->flags & DMA_ACTIVE)) - pos = 0; - else - { - clear_dma_ff (chan); - disable_dma (chan); - pos = get_dma_residue (chan); - enable_dma (chan); - } - restore_flags (flags); - /* printk ("%04x ", pos); */ - - if (audio_devs[dev]->flags & DMA_AUTOMODE) - return dmap->bytes_in_use - pos; - else - { - pos = dmap->fragment_size - pos; - if (pos < 0) + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + + if (adev->open_mode & OPEN_WRITE) + adev->dmap_out->closing = 1; + if (adev->open_mode & OPEN_READ) + adev->dmap_in->closing = 1; + + if (adev->open_mode & OPEN_WRITE) + if (!(adev->dmap_in->mapping_flags & DMA_MAP_MAPPED)) + if (!signal_pending(current) && (adev->dmap_out->dma_mode == DMODE_OUTPUT)) + DMAbuf_sync(dev); + if (adev->dmap_out->dma_mode == DMODE_OUTPUT) + memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use); + save_flags(flags); + cli(); + + DMAbuf_reset(dev); + adev->d->close(dev); + + if (adev->open_mode & OPEN_WRITE) + close_dmap(adev, adev->dmap_out); + + if (adev->open_mode == OPEN_READ || + (adev->open_mode != OPEN_WRITE && + adev->flags & DMA_DUPLEX)) + close_dmap(adev, adev->dmap_in); + adev->open_mode = 0; + restore_flags(flags); return 0; - return pos; - } } - -int -DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local) +int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap) { - struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out; - struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in; - long larg = (long) arg; - - switch (cmd) - { - case SOUND_PCM_WRITE_RATE: - if (local) - return audio_devs[dev]->d->set_speed (dev, larg); - return snd_ioctl_return ((int *) arg, audio_devs[dev]->d->set_speed (dev, get_user ((int *) arg))); - - case SOUND_PCM_READ_RATE: - if (local) - return audio_devs[dev]->d->set_speed (dev, 0); - return snd_ioctl_return ((int *) arg, audio_devs[dev]->d->set_speed (dev, 0)); - - case SNDCTL_DSP_STEREO: - if (local) - return audio_devs[dev]->d->set_channels (dev, larg + 1) - 1; - return snd_ioctl_return ((int *) arg, audio_devs[dev]->d->set_channels (dev, get_user ((int *) arg) + 1) - 1); - - case SOUND_PCM_WRITE_CHANNELS: - if (local) - return audio_devs[dev]->d->set_channels (dev, (short) larg); - return snd_ioctl_return ((int *) arg, audio_devs[dev]->d->set_channels (dev, get_user ((int *) arg))); - - case SOUND_PCM_READ_CHANNELS: - if (local) - return audio_devs[dev]->d->set_channels (dev, 0); - return snd_ioctl_return ((int *) arg, audio_devs[dev]->d->set_channels (dev, 0)); - - case SNDCTL_DSP_SAMPLESIZE: - if (local) - return audio_devs[dev]->d->set_bits (dev, larg); - return snd_ioctl_return ((int *) arg, audio_devs[dev]->d->set_bits (dev, get_user ((int *) arg))); - - case SOUND_PCM_READ_BITS: - if (local) - return audio_devs[dev]->d->set_bits (dev, 0); - return snd_ioctl_return ((int *) arg, audio_devs[dev]->d->set_bits (dev, 0)); - - case SNDCTL_DSP_RESET: - dma_reset (dev); - return 0; - break; - - case SNDCTL_DSP_SYNC: - dma_sync (dev); - dma_reset (dev); - return 0; - break; - - case SNDCTL_DSP_GETBLKSIZE: - if (!(dmap_out->flags & DMA_ALLOC_DONE)) - { - reorganize_buffers (dev, dmap_out, - (audio_devs[dev]->open_mode == OPEN_READ)); - if (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)); + struct audio_operations *adev = audio_devs[dev]; + int err; + + if (!(adev->open_mode & OPEN_READ)) + return 0; + if (!(adev->enable_bits & PCM_ENABLE_INPUT)) + return 0; + if (dmap->dma_mode == DMODE_OUTPUT) { /* Direction change */ + DMAbuf_sync(dev); + DMAbuf_reset(dev); + dmap->dma_mode = DMODE_NONE; } - - return snd_ioctl_return ((int *) arg, dmap_out->fragment_size); - break; - - case SNDCTL_DSP_SUBDIVIDE: - { - int fact = get_user ((int *) arg); - int ret; - - ret = dma_subdivide (dev, dmap_out, arg, fact); - if (ret < 0) - return ret; - - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ) - ret = dma_subdivide (dev, dmap_in, arg, fact); - - return ret; - } - break; - - case SNDCTL_DSP_SETDUPLEX: - if (audio_devs[dev]->flags & DMA_DUPLEX) - return 0; - else - return -(EIO); - break; - - case SNDCTL_DSP_SETFRAGMENT: - { - int fact = get_user ((int *) arg); - int ret; - - ret = dma_set_fragment (dev, dmap_out, arg, fact); - if (ret < 0) - return ret; - - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ) - ret = dma_set_fragment (dev, dmap_in, arg, fact); - - return ret; - } - break; - - case SNDCTL_DSP_GETISPACE: - case SNDCTL_DSP_GETOSPACE: - if (!local) - return -(EINVAL); - else - { - struct dma_buffparms *dmap = dmap_out; - - audio_buf_info *info = (audio_buf_info *) arg; - - if (cmd == SNDCTL_DSP_GETISPACE && - !(audio_devs[dev]->open_mode & OPEN_READ)) - 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 (!space_in_queue (dev)) - info->fragments = 0; - else - { - info->fragments = dmap->nbufs - dmap->qlen; - 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]; + if (!dmap->dma_mode) { + reorganize_buffers(dev, dmap, 1); + if ((err = adev->d->prepare_for_input(dev, + dmap->fragment_size, dmap->nbufs)) < 0) + return err; + dmap->dma_mode = DMODE_INPUT; + } + if (!(dmap->flags & DMA_ACTIVE)) { + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); + adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, + dmap->fragment_size, 0); + dmap->flags |= DMA_ACTIVE; + if (adev->d->trigger) + adev->d->trigger(dev, adev->enable_bits * adev->go); } - return 0; - - case SNDCTL_DSP_SETTRIGGER: - { - unsigned long flags; - - int bits = get_user ((int *) arg) & audio_devs[dev]->open_mode; - int changed; - - if (audio_devs[dev]->d->trigger == NULL) - return -(EINVAL); - - if (!(audio_devs[dev]->flags & DMA_DUPLEX)) - if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT)) - { - printk ("Sound: Device doesn't have full duplex capability\n"); - return -(EINVAL); - } - - save_flags (flags); - cli (); - changed = audio_devs[dev]->enable_bits ^ bits; - - if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) - { - int err; - - if (!(dmap_in->flags & DMA_ALLOC_DONE)) - { - reorganize_buffers (dev, dmap_in, 1); - } - - if ((err = audio_devs[dev]->d->prepare_for_input (dev, - dmap_in->fragment_size, dmap_in->nbufs)) < 0) - return -(err); - - audio_devs[dev]->enable_bits = bits; - activate_recording (dev, dmap_in); - } - - if ((changed & bits) & PCM_ENABLE_OUTPUT && - dmap_out->mapping_flags & DMA_MAP_MAPPED && - audio_devs[dev]->go) - { - int err; - - if (!(dmap_out->flags & DMA_ALLOC_DONE)) - { - reorganize_buffers (dev, dmap_out, 0); - } - - if ((err = audio_devs[dev]->d->prepare_for_output (dev, - dmap_out->fragment_size, dmap_out->nbufs)) < 0) - return -(err); - - dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; - DMAbuf_start_output (dev, 0, dmap_out->fragment_size); - } - - audio_devs[dev]->enable_bits = bits; - if (changed && audio_devs[dev]->d->trigger) - { - audio_devs[dev]->d->trigger (dev, bits * audio_devs[dev]->go); - } - restore_flags (flags); - } - case SNDCTL_DSP_GETTRIGGER: - return snd_ioctl_return ((int *) arg, 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; - break; - - case SNDCTL_DSP_GETIPTR: - { - count_info info; - unsigned long flags; - - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return -(EINVAL); - - save_flags (flags); - cli (); - info.bytes = audio_devs[dev]->dmap_in->byte_counter; - info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan2, audio_devs[dev]->dmap_in); - info.blocks = audio_devs[dev]->dmap_in->qlen; - info.bytes += info.ptr; - memcpy_tofs (&((char *) arg)[0], (char *) &info, sizeof (info)); - - if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) - audio_devs[dev]->dmap_in->qlen = 0; /* Acknowledge interrupts */ - restore_flags (flags); - return 0; - } - break; - - case SNDCTL_DSP_GETOPTR: - { - count_info info; - unsigned long flags; - - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -(EINVAL); - - save_flags (flags); - cli (); - info.bytes = audio_devs[dev]->dmap_out->byte_counter; - info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan1, audio_devs[dev]->dmap_out); - info.blocks = audio_devs[dev]->dmap_out->qlen; - info.bytes += info.ptr; - memcpy_tofs (&((char *) arg)[0], (char *) &info, sizeof (info)); - - if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED) - audio_devs[dev]->dmap_out->qlen = 0; /* Acknowledge interrupts */ - restore_flags (flags); return 0; - } - break; - - - default: - return audio_devs[dev]->d->ioctl (dev, cmd, arg, local); - } - -} - -/* - * DMAbuf_start_devices() is called by the /dev/music driver to start - * one or more audio devices at desired moment. - */ - -void -DMAbuf_start_devices (unsigned int devmask) -{ - int dev; - - for (dev = 0; dev < num_audiodevs; dev++) - if (devmask & (1 << dev)) - if (audio_devs[dev]->open_mode != 0) - if (!audio_devs[dev]->go) - { - /* OK to start the device */ - audio_devs[dev]->go = 1; - - if (audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger (dev, - audio_devs[dev]->enable_bits * audio_devs[dev]->go); - } } -static int -space_in_queue (int dev) +int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) { - int len, max, tmp; - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - - if (dmap->qlen >= dmap->nbufs) /* No space at all */ - return 0; - - /* - * Verify that there are no more pending buffers than the limit - * defined by the process. - */ - - max = dmap->max_fragments; - len = dmap->qlen; - - if (audio_devs[dev]->d->local_qlen) - { - tmp = audio_devs[dev]->d->local_qlen (dev); - if (tmp && len) - tmp--; /* - * This buffer has been counted twice - */ - len += tmp; - } - - if (len >= max) - return 0; - return 1; -} - -int -DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock) -{ - unsigned long flags; - int abort, err = EIO; - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - - dmap->flags &= ~DMA_CLEAN; - - if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED) - { - printk ("Sound: Can't write to mmapped device (3)\n"); - return -(EINVAL); - } - - if (dmap->dma_mode == DMODE_INPUT) /* Direction change */ - { - dma_reset (dev); - dmap->dma_mode = DMODE_NONE; - } - else if (dmap->flags & DMA_RESTART) /* Restart buffering */ - { - dma_sync (dev); - dma_reset_output (dev); - } - - dmap->flags &= ~(DMA_RESTART | DMA_EMPTY); - - if (!(dmap->flags & DMA_ALLOC_DONE)) - reorganize_buffers (dev, dmap, 0); - - if (!dmap->dma_mode) - { - int err; - - dmap->dma_mode = DMODE_OUTPUT; - if ((err = audio_devs[dev]->d->prepare_for_output (dev, - dmap->fragment_size, dmap->nbufs)) < 0) - return err; - } - - save_flags (flags); - cli (); - - abort = 0; - while (!space_in_queue (dev) && - !abort) - { - int tmout; - - if (dontblock) - { - restore_flags (flags); - return -(EAGAIN); - } + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + int err = 0, n = 0; + struct dma_buffparms *dmap = adev->dmap_in; + int go; + + if (!(adev->open_mode & OPEN_READ)) + return -EIO; + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + save_flags(flags); + cli(); + if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) { +/* printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/ + restore_flags(flags); + return -EINVAL; + } else while (dmap->qlen <= 0 && n++ < 10) { + if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) { + restore_flags(flags); + return -EAGAIN; + } + if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) { + restore_flags(flags); + return err; + } + /* Wait for the next block */ - if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT) && - audio_devs[dev]->go) - { - restore_flags (flags); - return -(EAGAIN); + if (dontblock) { + restore_flags(flags); + return -EAGAIN; + } + if (!(go = adev->go)) + current->timeout = 0; + else + dmabuf_set_timeout(dmap); + interruptible_sleep_on(&adev->in_sleeper); + if (go && !current->timeout) { + /* FIXME: include device name */ + err = -EIO; + printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); + dma_reset_input(dev); + } else + err = -EINTR; + current->timeout = 0; } + restore_flags(flags); - /* - * Wait for free space - */ - if (!audio_devs[dev]->go) - tmout = 0; - else - tmout = 10 * HZ; + if (dmap->qlen <= 0) + return err ? err : -EINTR; + *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]]; + *len = dmap->fragment_size - dmap->counts[dmap->qhead]; + return dmap->qhead; +} - { - unsigned long tlimit; +int DMAbuf_rmchars(int dev, int buff_no, int c) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; + int p = dmap->counts[dmap->qhead] + c; - if (tmout) - current_set_timeout (tlimit = jiffies + (tmout)); - else - tlimit = (unsigned long) -1; - out_sleep_flag[dev].flags = WK_SLEEP; - module_interruptible_sleep_on (&out_sleeper[dev]); - if (!(out_sleep_flag[dev].flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - out_sleep_flag[dev].flags |= WK_TIMEOUT; - } - out_sleep_flag[dev].flags &= ~WK_SLEEP; - }; - if ((out_sleep_flag[dev].flags & WK_TIMEOUT)) + if (dmap->mapping_flags & DMA_MAP_MAPPED) { - printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); - err = EIO; - abort = 1; - ; - if (audio_devs[dev]->flags & DMA_AUTOMODE) - dmap->flags |= DMA_RESTART; - else - dmap->flags &= ~DMA_RESTART; - audio_devs[dev]->d->reset (dev); +/* printk("Sound: Can't read from mmapped device (2)\n");*/ + return -EINVAL; } - else if (current_got_fatal_signal ()) - { - err = EINTR; - abort = 1; + else if (dmap->qlen <= 0) + return -EIO; + else if (p >= dmap->fragment_size) { /* This buffer is completely empty */ + dmap->counts[dmap->qhead] = 0; + dmap->qlen--; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; } - } - restore_flags (flags); + else dmap->counts[dmap->qhead] = p; - if (!space_in_queue (dev)) - { - return -(err); /* Caught a signal ? */ - } - - *buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size; - *size = dmap->fragment_size; - dmap->counts[dmap->qtail] = 0; - - return dmap->qtail; + return 0; } -int -DMAbuf_get_curr_buffer (int dev, int *buf_no, char **dma_buf, int *buf_ptr, int *buf_size) +int DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction) { - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - - if (dmap->cfrag < 0) - return -1; - - *dma_buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size; - *buf_ptr = dmap->counts[dmap->qtail]; - *buf_size = dmap->fragment_size; - return *buf_no = dmap->cfrag; -} + /* + * Try to approximate the active byte position of the DMA pointer within the + * buffer area as well as possible. + */ + + int pos; + unsigned long flags; + + save_flags(flags); + cli(); + if (!(dmap->flags & DMA_ACTIVE)) + pos = 0; + else { + int chan = dmap->dma; + clear_dma_ff(chan); + disable_dma(dmap->dma); + pos = get_dma_residue(chan); + pos = dmap->bytes_in_use - pos; + + if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) { + if (direction == DMODE_OUTPUT) { + if (dmap->qhead == 0) + if (pos > dmap->fragment_size) + pos = 0; + } else { + if (dmap->qtail == 0) + if (pos > dmap->fragment_size) + pos = 0; + } + } + if (pos < 0) + pos = 0; + if (pos >= dmap->bytes_in_use) + pos = 0; + enable_dma(dmap->dma); + } + restore_flags(flags); + /* printk( "%04x ", pos); */ -int -DMAbuf_set_count (int dev, int buff_no, int l) -{ - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - - if (buff_no == dmap->qtail) - { - dmap->cfrag = buff_no; - dmap->counts[buff_no] = l; - } - else - dmap->cfrag = -1; - return 0; + return pos; } -int -DMAbuf_start_output (int dev, int buff_no, int l) -{ - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - int restart = 0; - - dmap->cfrag = -1; - if (dmap->flags & DMA_RESTART) - restart = 1; - /* - * Bypass buffering if using mmapped access + * DMAbuf_start_devices() is called by the /dev/music driver to start + * one or more audio devices at desired moment. */ - if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED) - { - l = dmap->fragment_size; - dmap->counts[dmap->qtail] = l; - dmap->flags &= ~DMA_RESTART; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - } - else - { - - dmap->qlen++; - if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) - printk ("\nSound: Audio queue2 corrupted for dev%d (%d/%d)\n", - dev, dmap->qlen, dmap->nbufs); - - dmap->counts[dmap->qtail] = l; - if (l < dmap->fragment_size) - { - int p = dmap->fragment_size * dmap->qtail; - - dmap->neutral_byte = dmap->raw_buf[p + l - 1]; - - memset (dmap->raw_buf + p + l, - dmap->neutral_byte, - dmap->fragment_size - l); - } - else - dmap->neutral_byte = - dmap->raw_buf[dmap->fragment_size * dmap->qtail - 1]; - - if ((l != dmap->fragment_size) && - ((audio_devs[dev]->flags & DMA_AUTOMODE) && - audio_devs[dev]->flags & NEEDS_RESTART)) - dmap->flags |= DMA_RESTART; - else - dmap->flags &= ~DMA_RESTART; - - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - } - - if (!(dmap->flags & DMA_ACTIVE)) - { - dmap->flags |= DMA_ACTIVE; - - if (restart) - audio_devs[dev]->d->prepare_for_output (dev, - dmap->fragment_size, dmap->nbufs); - - audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys + - dmap->qhead * dmap->fragment_size, - dmap->counts[dmap->qhead], 0, - !(audio_devs[dev]->flags & DMA_AUTOMODE) || - !(dmap->flags & DMA_STARTED)); - dmap->flags |= DMA_STARTED; - if (audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger (dev, - audio_devs[dev]->enable_bits * audio_devs[dev]->go); - } - - return 0; -} - -int -DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) +void DMAbuf_start_devices(unsigned int devmask) { - int chan; - struct dma_buffparms *dmap; - unsigned long flags; - - if (dma_mode == DMA_MODE_WRITE) - { - chan = audio_devs[dev]->dmachan1; - dmap = audio_devs[dev]->dmap_out; - } - else - { - chan = audio_devs[dev]->dmachan2; - dmap = audio_devs[dev]->dmap_in; - } - - if (dmap->raw_buf_phys == 0) - { - printk ("sound: DMA buffer == NULL\n"); - return 0; - } - - /* - * The count must be one less than the actual size. This is handled by - * set_dma_addr() - */ - - if (audio_devs[dev]->flags & DMA_AUTOMODE) - { /* - * Auto restart mode. Transfer the whole * - * buffer - */ - save_flags (flags); - cli (); - disable_dma (chan); - clear_dma_ff (chan); - set_dma_mode (chan, dma_mode | DMA_AUTOINIT); - set_dma_addr (chan, dmap->raw_buf_phys); - set_dma_count (chan, dmap->bytes_in_use); - enable_dma (chan); - restore_flags (flags); - } - else - { - save_flags (flags); - cli (); - disable_dma (chan); - clear_dma_ff (chan); - set_dma_mode (chan, dma_mode); - set_dma_addr (chan, physaddr); - set_dma_count (chan, count); - enable_dma (chan); - restore_flags (flags); - } - - return count; + struct audio_operations *adev; + int dev; + + for (dev = 0; dev < num_audiodevs; dev++) { + if (!(devmask & (1 << dev))) + continue; + if (!(adev = audio_devs[dev])) + continue; + if (adev->open_mode == 0) + continue; + if (adev->go) + continue; + /* OK to start the device */ + adev->go = 1; + if (adev->d->trigger) + adev->d->trigger(dev,adev->enable_bits * adev->go); + } } -void -DMAbuf_init (void) +int DMAbuf_space_in_queue(int dev) { - int dev; - - - /* - * NOTE! This routine could be called several times. - */ - - for (dev = 0; dev < num_audiodevs; dev++) - if (audio_devs[dev]->dmap_out == NULL) - { - audio_devs[dev]->dmap_out = - audio_devs[dev]->dmap_in = - &dmaps[ndmaps++]; - - if (audio_devs[dev]->flags & DMA_DUPLEX) - audio_devs[dev]->dmap_in = - &dmaps[ndmaps++]; - } -} + struct audio_operations *adev = audio_devs[dev]; + int len, max, tmp; + struct dma_buffparms *dmap = adev->dmap_out; + int lim = dmap->nbufs; + + if (lim < 2) + lim = 2; + + if (dmap->qlen >= lim) /* No space at all */ + return 0; + + /* + * Verify that there are no more pending buffers than the limit + * defined by the process. + */ + + max = dmap->max_fragments; + if (max > lim) + max = lim; + len = dmap->qlen; + + if (adev->d->local_qlen) { + tmp = adev->d->local_qlen(dev); + if (tmp && len) + tmp--; /* This buffer has been counted twice */ + len += tmp; + } + if (dmap->byte_counter % dmap->fragment_size) /* There is a partial fragment */ + len = len + 1; -static void -polish_buffers (struct dma_buffparms *dmap) -{ - int i; - int p, l; - - i = dmap->qhead; - - p = dmap->fragment_size * i; - - if (i == dmap->cfrag) - { - l = dmap->fragment_size - dmap->counts[i]; - } - else - l = dmap->fragment_size; - - if (l) - { - memset (dmap->raw_buf + p, - dmap->neutral_byte, - l); - } + if (len >= max) + return 0; + return max - len; } -static void -force_restart (int dev, struct dma_buffparms *dmap) +static int output_sleep(int dev, int dontblock) { - if ((audio_devs[dev]->flags & DMA_DUPLEX) && - audio_devs[dev]->d->halt_output) - audio_devs[dev]->d->halt_output (dev); - else - audio_devs[dev]->d->halt_xfer (dev); - - dmap->flags &= ~(DMA_ACTIVE | DMA_STARTED); - if (audio_devs[dev]->flags & DMA_AUTOMODE) - dmap->flags |= DMA_RESTART; - else - dmap->flags &= ~DMA_RESTART; + struct audio_operations *adev = audio_devs[dev]; + int err = 0; + struct dma_buffparms *dmap = adev->dmap_out; + int timeout; + + if (dontblock) + return -EAGAIN; + if (!(adev->enable_bits & PCM_ENABLE_OUTPUT)) + return -EAGAIN; + + /* + * Wait for free space + */ + if (signal_pending(current)) + return -EIO; + timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT)); + if (timeout) + dmabuf_set_timeout(dmap); + else + current->timeout = 0; + interruptible_sleep_on(&adev->out_sleeper); + if (timeout && !current->timeout) { + printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); + dma_reset_output(dev); + } else { + current->timeout = 0; + if (signal_pending(current)) + err = -EINTR; + } + return err; } -void -DMAbuf_outputintr (int dev, int event_type) +static int find_output_space(int dev, char **buf, int *size) { - /* - * Event types: - * 0 = DMA transfer done. Device still has more data in the local - * buffer. - * 1 = DMA transfer done. Device doesn't have local buffer or it's - * empty now. - * 2 = No DMA transfer but the device has now more space in it's local - * buffer. - */ - - unsigned long flags; - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - int this_fragment; - - dmap->byte_counter += dmap->counts[dmap->qhead]; - -#ifdef OS_DMA_INTR - sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + unsigned long flags; + unsigned long active_offs; + long len, offs; + int maxfrags; + int occupied_bytes = (dmap->user_counter % dmap->fragment_size); + + *buf = dmap->raw_buf; + if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes) + return 0; + save_flags(flags); + cli(); + +#ifdef BE_CONSERVATIVE + active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size; +#else + active_offs = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT); + /* Check for pointer wrapping situation */ + if (active_offs < 0 || active_offs >= dmap->bytes_in_use) + active_offs = 0; + active_offs += dmap->byte_counter; #endif - if (dmap->raw_buf == NULL) - { - printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n"); - return; - } - - if (dmap->mapping_flags & DMA_MAP_MAPPED) - { - /* mmapped access */ - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - dmap->qlen++; /* Yes increment it (don't decrement) */ - dmap->flags &= ~DMA_ACTIVE; - dmap->counts[dmap->qhead] = dmap->fragment_size; - - if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) - { - audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys + - dmap->qhead * dmap->fragment_size, - dmap->counts[dmap->qhead], 1, - !(audio_devs[dev]->flags & DMA_AUTOMODE)); - if (audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger (dev, - audio_devs[dev]->enable_bits * audio_devs[dev]->go); - } - dmap->flags |= DMA_ACTIVE; - } - else if (event_type != 2) - { - if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) - { - printk ("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n", - dev, dmap->qlen, dmap->nbufs); - return; + offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP; + if (offs < 0 || offs >= dmap->bytes_in_use) { + restore_flags(flags); + printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs); + printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use); + return 0; } + *buf = dmap->raw_buf + offs; - dmap->qlen--; - this_fragment = dmap->qhead; - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - dmap->flags &= ~DMA_ACTIVE; + len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */ - if (event_type == 1 && dmap->qlen < 1) - { - dmap->underrun_count++; - - if ((!(dmap->flags & DMA_CLEAN) && - (audio_devs[dev]->dmap_out->flags & DMA_SYNCING || - dmap->underrun_count > 5 || dmap->flags & DMA_EMPTY)) || - audio_devs[dev]->flags & DMA_HARDSTOP) - - { - dmap->qlen = 0; - force_restart (dev, dmap); - } - else - /* Ignore underrun. Just move the tail pointer forward and go */ - if (dmap->closing) - { - polish_buffers (dmap); - audio_devs[dev]->d->halt_xfer (dev); - } - else - { - dmap->qlen++; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - - if (!(dmap->flags & DMA_EMPTY)) - polish_buffers (dmap); - - dmap->cfrag = -1; - dmap->flags |= DMA_EMPTY; - dmap->counts[dmap->qtail] = dmap->fragment_size; - } + if ((offs + len) > dmap->bytes_in_use) + len = dmap->bytes_in_use - offs; + if (len < 0) { + restore_flags(flags); + return 0; } + if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes)) + len = (maxfrags * dmap->fragment_size) - occupied_bytes; + *size = len & ~SAMPLE_ROUNDUP; + restore_flags(flags); + return (*size > 0); +} - if (dmap->qlen) - { - if (dmap->flags & DMA_CLEAN) - { - int p = dmap->fragment_size * this_fragment; - - memset (dmap->raw_buf + p, - dmap->neutral_byte, - dmap->fragment_size); - } - - if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) - { +int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock) +{ + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + int err = -EIO; + struct dma_buffparms *dmap = adev->dmap_out; - if (dmap->counts[dmap->qhead] == 0) - dmap->counts[dmap->qhead] = dmap->fragment_size; + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); - audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys + - dmap->qhead * dmap->fragment_size, - dmap->counts[dmap->qhead], 1, - !(audio_devs[dev]->flags & DMA_AUTOMODE)); - if (audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger (dev, - audio_devs[dev]->enable_bits * audio_devs[dev]->go); - } - dmap->flags |= DMA_ACTIVE; + if (dmap->mapping_flags & DMA_MAP_MAPPED) { +/* printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/ + return -EINVAL; } - } /* event_type != 2 */ - - save_flags (flags); - cli (); - if ((out_sleep_flag[dev].flags & WK_SLEEP)) - { - { - out_sleep_flag[dev].flags = WK_WAKEUP; - module_wake_up (&out_sleeper[dev]); - }; - } - restore_flags (flags); + if (dmap->dma_mode == DMODE_INPUT) { /* Direction change */ + DMAbuf_reset(dev); + dmap->dma_mode = DMODE_NONE; + } + dmap->dma_mode = DMODE_OUTPUT; + + save_flags(flags); + cli(); + while (find_output_space(dev, buf, size) <= 0) { + if ((err = output_sleep(dev, dontblock)) < 0) { + restore_flags(flags); + return err; + } + } + restore_flags(flags); + + return 0; } -void -DMAbuf_inputintr (int dev) +int DMAbuf_move_wrpointer(int dev, int l) { - unsigned long flags; - struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + unsigned long ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; + unsigned long end_ptr, p; + int post = (dmap->flags & DMA_POST); + + dmap->flags &= ~DMA_POST; + dmap->cfrag = -1; + dmap->user_counter += l; + dmap->flags |= DMA_DIRTY; + + if (dmap->byte_counter >= dmap->max_byte_counter) { + /* Wrap the byte counters */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; - dmap->byte_counter += dmap->fragment_size; + p = (dmap->user_counter - 1) % dmap->bytes_in_use; + dmap->neutral_byte = dmap->raw_buf[p]; -#ifdef OS_DMA_INTR - sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); -#endif + /* Update the fragment based bookkeeping too */ + while (ptr < end_ptr) { + dmap->counts[dmap->qtail] = dmap->fragment_size; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + dmap->qlen++; + ptr += dmap->fragment_size; + } - if (dmap->raw_buf == NULL) - { - printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n"); - return; - } + dmap->counts[dmap->qtail] = dmap->user_counter - ptr; - if (dmap->mapping_flags & DMA_MAP_MAPPED) - { - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - dmap->qlen++; + /* + * Let the low level driver to perform some postprocessing to + * the written data. + */ + if (adev->d->postprocess_write) + adev->d->postprocess_write(dev); - if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) - { - audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys + - dmap->qtail * dmap->fragment_size, - dmap->fragment_size, 1, - !(audio_devs[dev]->flags & DMA_AUTOMODE)); - if (audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger (dev, - audio_devs[dev]->enable_bits * audio_devs[dev]->go); - } + if (!(dmap->flags & DMA_ACTIVE)) + if (dmap->qlen > 1 || (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1))) + DMAbuf_launch_output(dev, dmap); + return 0; +} - dmap->flags |= DMA_ACTIVE; - } - else if (dmap->qlen == (dmap->nbufs - 1)) - { - printk ("Sound: Recording overrun\n"); - dmap->underrun_count++; +int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in; - if (audio_devs[dev]->flags & DMA_AUTOMODE) - { - /* Force restart on next read */ - if ((audio_devs[dev]->flags & DMA_DUPLEX) && - audio_devs[dev]->d->halt_input) - audio_devs[dev]->d->halt_input (dev); - else - audio_devs[dev]->d->halt_xfer (dev); - - dmap->flags &= ~DMA_ACTIVE; - if (audio_devs[dev]->flags & DMA_AUTOMODE) - dmap->flags |= DMA_RESTART; - else - dmap->flags &= ~DMA_RESTART; + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "sound: DMA buffer(1) == NULL\n"); + printk("Device %d, chn=%s\n", dev, (dmap == adev->dmap_out) ? "out" : "in"); + return 0; } - } - else - { - dmap->qlen++; - if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) - printk ("\nSound: Audio queue4 corrupted for dev%d (%d/%d)\n", - dev, dmap->qlen, dmap->nbufs); - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - } - - if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) - { - audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys + - dmap->qtail * dmap->fragment_size, - dmap->fragment_size, 1, - !(audio_devs[dev]->flags & DMA_AUTOMODE)); - if (audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger (dev, - audio_devs[dev]->enable_bits * audio_devs[dev]->go); - } - - dmap->flags |= DMA_ACTIVE; - - save_flags (flags); - cli (); - if ((in_sleep_flag[dev].flags & WK_SLEEP)) - { - { - in_sleep_flag[dev].flags = WK_WAKEUP; - module_wake_up (&in_sleeper[dev]); - }; - } - restore_flags (flags); + if (dmap->dma < 0) + return 0; + sound_start_dma(dmap, physaddr, count, dma_mode); + return count; } -int -DMAbuf_open_dma (int dev) +static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode) { -/* - * NOTE! This routine opens only the primary DMA channel (output). - */ + struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in; - int chan = audio_devs[dev]->dmachan1; - int err; - unsigned long flags; - - if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1)) < 0) - { - return -(EBUSY); - } - dma_init_buffers (dev, audio_devs[dev]->dmap_out); - audio_devs[dev]->dmap_out->flags |= DMA_ALLOC_DONE; - audio_devs[dev]->dmap_out->fragment_size = audio_devs[dev]->buffsize; - - save_flags (flags); - cli (); - disable_dma (chan); - clear_dma_ff (chan); - restore_flags (flags); - - return 0; + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "sound: DMA buffer(2) == NULL\n"); + printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in"); + return 0; + } + if (dmap->flags & DMA_NODMA) + return 1; + if (dmap->dma < 0) + return 0; + sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT); + dmap->flags |= DMA_STARTED; + return count; } -void -DMAbuf_close_dma (int dev) +static void finish_output_interrupt(int dev, struct dma_buffparms *dmap) { - DMAbuf_reset_dma (dev); - close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); -} + struct audio_operations *adev = audio_devs[dev]; -void -DMAbuf_reset_dma (int dev) -{ + if (dmap->audio_callback != NULL) + dmap->audio_callback(dev, dmap->callback_parm); + wake_up(&adev->out_sleeper); } -int -DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table_handle * wait) +static void do_outputintr(int dev, int dummy) { - struct dma_buffparms *dmap; - unsigned long flags; - - switch (sel_type) - { - case SEL_IN: - dmap = audio_devs[dev]->dmap_in; - - if (dmap->mapping_flags & DMA_MAP_MAPPED) - { - if (dmap->qlen) - return 1; - - save_flags (flags); - cli (); - - in_sleep_flag[dev].flags = WK_SLEEP; - module_select_wait (&in_sleeper[dev], wait); - restore_flags (flags); - return 0; - } - - if (dmap->dma_mode != DMODE_INPUT) - { - if ((audio_devs[dev]->flags & DMA_DUPLEX) && !dmap->qlen && - audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT && - audio_devs[dev]->go) - { - unsigned long flags; - - save_flags (flags); - cli (); - activate_recording (dev, dmap); - restore_flags (flags); - } - return 0; - } - - if (!dmap->qlen) - { - save_flags (flags); - cli (); - - in_sleep_flag[dev].flags = WK_SLEEP; - module_select_wait (&in_sleeper[dev], wait); - restore_flags (flags); - return 0; - } - return 1; - break; - - case SEL_OUT: - dmap = audio_devs[dev]->dmap_out; - - if (dmap->mapping_flags & DMA_MAP_MAPPED) - { - if (dmap->qlen) - return 1; - - save_flags (flags); - cli (); - - out_sleep_flag[dev].flags = WK_SLEEP; - module_select_wait (&out_sleeper[dev], wait); - restore_flags (flags); - return 0; + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + struct dma_buffparms *dmap = adev->dmap_out; + int this_fragment; + + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev); + return; } - - if (dmap->dma_mode == DMODE_INPUT) - { - return 0; + if (dmap->mapping_flags & DMA_MAP_MAPPED) { /* Virtual memory mapped access */ + /* mmapped access */ + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + if (dmap->qhead == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + dmap->qlen++; /* Yes increment it (don't decrement) */ + if (!(adev->flags & DMA_AUTOMODE)) + dmap->flags &= ~DMA_ACTIVE; + dmap->counts[dmap->qhead] = dmap->fragment_size; + DMAbuf_launch_output(dev, dmap); + finish_output_interrupt(dev, dmap); + return; } - - if (dmap->dma_mode == DMODE_NONE) - { - return 1; + save_flags(flags); + cli(); + + dmap->qlen--; + this_fragment = dmap->qhead; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + + if (dmap->qhead == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } } - - if (!space_in_queue (dev)) - { - save_flags (flags); - cli (); - - out_sleep_flag[dev].flags = WK_SLEEP; - module_select_wait (&out_sleeper[dev], wait); - restore_flags (flags); - return 0; + if (!(adev->flags & DMA_AUTOMODE)) + dmap->flags &= ~DMA_ACTIVE; + while (dmap->qlen <= 0) { + dmap->underrun_count++; + dmap->qlen++; + if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS) { + dmap->flags &= ~DMA_DIRTY; + memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, + adev->dmap_out->buffsize); + } + dmap->user_counter += dmap->fragment_size; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; } - return 1; - break; - - case SEL_EX: - return 0; - } - - return 0; + if (dmap->qlen > 0) + DMAbuf_launch_output(dev, dmap); + restore_flags(flags); + finish_output_interrupt(dev, dmap); } - -#else /* CONFIG_AUDIO */ -/* - * Stub versions if audio services not included - */ - -int -DMAbuf_open (int dev, int mode) +void DMAbuf_outputintr(int dev, int notify_only) { - return -(ENXIO); + struct audio_operations *adev = audio_devs[dev]; + unsigned long flags; + struct dma_buffparms *dmap = adev->dmap_out; + + save_flags(flags); + cli(); + if (!(dmap->flags & DMA_NODMA)) { + int chan = dmap->dma, pos, n; + clear_dma_ff(chan); + disable_dma(dmap->dma); + pos = dmap->bytes_in_use - get_dma_residue(chan); + enable_dma(dmap->dma); + pos = pos / dmap->fragment_size; /* Actual qhead */ + if (pos < 0 || pos >= dmap->nbufs) + pos = 0; + n = 0; + while (dmap->qhead != pos && n++ < dmap->nbufs) + do_outputintr(dev, notify_only); + } + else + do_outputintr(dev, notify_only); + restore_flags(flags); } -int -DMAbuf_release (int dev, int mode) +static void do_inputintr(int dev) { - return 0; -} + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; -int -DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock) -{ - return -(EIO); + if (dmap->raw_buf == NULL) { + printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n"); + return; + } + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + if (dmap->qtail == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + dmap->qlen++; + + if (!(adev->flags & DMA_AUTOMODE)) { + if (dmap->needs_reorg) + reorganize_buffers(dev, dmap, 0); + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ); + adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, + dmap->fragment_size, 1); + if (adev->d->trigger) + adev->d->trigger(dev, adev->enable_bits * adev->go); + } + dmap->flags |= DMA_ACTIVE; + } else if (dmap->qlen >= (dmap->nbufs - 1)) { + printk(KERN_WARNING "Sound: Recording overrun\n"); + dmap->underrun_count++; + + /* Just throw away the oldest fragment but keep the engine running */ + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + } else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) { + dmap->qlen++; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + if (dmap->qtail == 0) { /* Wrapped */ + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ + long decr = dmap->byte_counter; + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + } + if (!(adev->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA) { + local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); + adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1); + if (adev->d->trigger) + adev->d->trigger(dev,adev->enable_bits * adev->go); + } + dmap->flags |= DMA_ACTIVE; + if (dmap->qlen > 0) + wake_up(&adev->in_sleeper); } -int -DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) +void DMAbuf_inputintr(int dev) { - return -(EIO); + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_in; + unsigned long flags; + + save_flags(flags); + cli(); + + if (!(dmap->flags & DMA_NODMA)) { + int chan = dmap->dma, pos, n; + clear_dma_ff(chan); + disable_dma(dmap->dma); + pos = dmap->bytes_in_use - get_dma_residue(chan); + enable_dma(dmap->dma); + + pos = pos / dmap->fragment_size; /* Actual qhead */ + if (pos < 0 || pos >= dmap->nbufs) + pos = 0; + + n = 0; + while (dmap->qtail != pos && ++n < dmap->nbufs) + do_inputintr(dev); + } else + do_inputintr(dev); + restore_flags(flags); } -int -DMAbuf_rmchars (int dev, int buff_no, int c) +int DMAbuf_open_dma(int dev) { - return -(EIO); + /* + * NOTE! This routine opens only the primary DMA channel (output). + */ + struct audio_operations *adev = audio_devs[dev]; + int err; + + if ((err = open_dmap(adev, OPEN_READWRITE, adev->dmap_out)) < 0) + return -EBUSY; + dma_init_buffers(adev->dmap_out); + adev->dmap_out->flags |= DMA_ALLOC_DONE; + adev->dmap_out->fragment_size = adev->dmap_out->buffsize; + + if (adev->dmap_out->dma >= 0) { + unsigned long flags; + + save_flags(flags); + cli(); + clear_dma_ff(adev->dmap_out->dma); + disable_dma(adev->dmap_out->dma); + restore_flags(flags); + } + return 0; } -int -DMAbuf_start_output (int dev, int buff_no, int l) +void DMAbuf_close_dma(int dev) { - return -(EIO); + close_dmap(audio_devs[dev], audio_devs[dev]->dmap_out); } -int -DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local) +void DMAbuf_init(int dev, int dma1, int dma2) { - return -(EIO); -} + struct audio_operations *adev = audio_devs[dev]; + /* + * NOTE! This routine could be called several times. + */ -void -DMAbuf_init (void) -{ -} + /* drag in audio_syms.o */ + { + extern char audio_syms_symbol; + audio_syms_symbol = 0; + } -int -DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) -{ - return -(EIO); + if (adev && adev->dmap_out == NULL) { + if (adev->d == NULL) + panic("OSS: audio_devs[%d]->d == NULL\n", dev); + + if (adev->parent_dev) { /* Use DMA map of the parent dev */ + int parent = adev->parent_dev - 1; + adev->dmap_out = audio_devs[parent]->dmap_out; + adev->dmap_in = audio_devs[parent]->dmap_in; + } else { + adev->dmap_out = adev->dmap_in = &adev->dmaps[0]; + adev->dmap_out->dma = dma1; + if (adev->flags & DMA_DUPLEX) { + adev->dmap_in = &adev->dmaps[1]; + adev->dmap_in->dma = dma2; + } + } + } } -int -DMAbuf_open_dma (int dev) +unsigned int DMAbuf_select (struct file *file, int dev, int sel_type, select_table *wait) { - return -(ENXIO); -} + struct dma_buffparms *dmap; + unsigned long flags; + struct audio_operations *adev = audio_devs[dev]; -void -DMAbuf_close_dma (int dev) -{ - return; + switch (sel_type) + { + case SEL_IN: + dmap = adev->dmap_in; + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + { + if (dmap->qlen) + return 1; + select_wait (&adev->in_sleeper, wait); + return 0; + } + + if (dmap->dma_mode != DMODE_INPUT) + { + if ((adev->flags & DMA_DUPLEX) && !dmap->qlen && + adev->enable_bits & PCM_ENABLE_INPUT && + adev->go) + { + save_flags(flags); + cli(); + DMAbuf_activate_recording(dev, dmap); + restore_flags(flags); + } + return 0; + } + + if (!dmap->qlen) + { + save_flags(flags); + cli(); + select_wait(&adev->in_sleeper, wait); + restore_flags(flags); + return 0; + } + return 1; + + case SEL_OUT: + dmap = adev->dmap_out; + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + { + if (dmap->qlen) + return 1; + select_wait (&adev->out_sleeper, wait); + return 0; + } + + if (dmap->dma_mode == DMODE_INPUT) + { + return 0; + } + + if (dmap->dma_mode == DMODE_NONE) + { + return 1; + } + + if (!DMAbuf_space_in_queue (dev)) + { + save_flags(flags); + cli(); + + select_wait (&adev->out_sleeper, wait); + return 0; + } + return 1; + + case SEL_EX: + return 0; + } + return 0; } -void -DMAbuf_reset_dma (int dev) -{ - return; -} -void -DMAbuf_inputintr (int dev) -{ - return; -} -void -DMAbuf_outputintr (int dev, int underrun_flag) +void DMAbuf_deinit(int dev) { - return; + struct audio_operations *adev = audio_devs[dev]; + /* This routine is called when driver is being unloaded */ + if (!adev) + return; +#ifdef RUNTIME_DMA_ALLOC + sound_free_dmap(adev->dmap_out); + + if (adev->flags & DMA_DUPLEX) + sound_free_dmap(adev->dmap_in); +#endif } + #endif diff --git a/drivers/sound/dmasound.c b/drivers/sound/dmasound.c index dd6f6155611e..7805bed434e6 100644 --- a/drivers/sound/dmasound.c +++ b/drivers/sound/dmasound.c @@ -3,7 +3,7 @@ /* -VoxWare compatible Atari TT DMA sound driver for 680x0 Linux +OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for Linux/m68k (c) 1995 by Michael Schlueter & Michael Marte @@ -62,10 +62,20 @@ History: 1996/3/9 ++geert: support added for Amiga, A-law, 16-bit little endian. Unification to drivers/sound/dmasound.c. + 1996/4/6 ++Martin Mitchell: updated to 1.3 kernel. + +1996/6/13 ++topi: fixed things that were broken (mainly the amiga + 14-bit routines), /dev/sndstat shows now the real + hardware frequency, the lowpass filter is disabled + by default now. + +1996/9/25 ++geert: modularization + */ +#include #include #include #include @@ -75,14 +85,16 @@ History: #include #include +#include #include #include #include -#include +#include #ifdef CONFIG_ATARI #include #include +#include #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA #include @@ -93,13 +105,20 @@ History: #include +#ifdef MODULE +static int chrdev_registered = 0; +static int irq_installed = 0; +#endif /* MODULE */ +static char **sound_buffers = NULL; + + #ifdef CONFIG_ATARI extern void atari_microwire_cmd(int cmd); #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA /* - * The minimum period for audio depends on total (for OCS/ECS/AGA) + * The minimum period for audio depends on htotal (for OCS/ECS/AGA) * (Imported from arch/m68k/amiga/amisound.c) */ @@ -145,8 +164,11 @@ static int catchRadius = 0, numBufs = 4, bufSize = 32; #define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff)) #define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff)) -#define IOCTL_IN(arg) get_user((int *)(arg)) -#define IOCTL_OUT(arg, ret) ioctl_return((int *)arg, ret) +#define IOCTL_IN(arg, ret) \ + do { int error = get_user(ret, (int *)(arg)); \ + if (error) return error; \ + } while (0) +#define IOCTL_OUT(arg, ret) ioctl_return((int *)(arg), ret) /*** Some low level helpers **************************************************/ @@ -387,51 +409,51 @@ static char alaw2dma14l[] = { #ifdef CONFIG_ATARI -static long ata_ct_law(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ct_s8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ct_u8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ct_s16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ct_u16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ct_s16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ct_u16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ctx_law(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ctx_s8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ctx_u8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ctx_s16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ctx_u16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ctx_s16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ata_ctx_u16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); +static long ata_ct_law(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ct_s8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ct_u8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ct_s16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ct_u16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ct_s16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ct_u16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ctx_law(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ctx_s8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ctx_u8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ctx_s16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ctx_u16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ctx_s16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ata_ctx_u16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA -static long ami_ct_law(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ami_ct_s8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ami_ct_u8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ami_ct_s16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ami_ct_u16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ami_ct_s16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); -static long ami_ct_u16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft); +static long ami_ct_law(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ami_ct_s8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ami_ct_u8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ami_ct_s16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ami_ct_u16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ami_ct_s16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); +static long ami_ct_u16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft); #endif /* CONFIG_AMIGA */ @@ -443,12 +465,16 @@ typedef struct { void *(*dma_alloc)(unsigned int, int); void (*dma_free)(void *, unsigned int); int (*irqinit)(void); +#ifdef MODULE + void (*irqcleanup)(void); +#endif /* MODULE */ void (*init)(void); void (*silence)(void); int (*setFormat)(int); int (*setVolume)(int); int (*setBass)(int); int (*setTreble)(int); + int (*setGain)(int); void (*play)(void); } MACHINE; @@ -464,14 +490,14 @@ typedef struct { } SETTINGS; typedef struct { - long (*ct_ulaw)(const u_char *, long, u_char *, long *, long); - long (*ct_alaw)(const u_char *, long, u_char *, long *, long); - long (*ct_s8)(const u_char *, long, u_char *, long *, long); - long (*ct_u8)(const u_char *, long, u_char *, long *, long); - long (*ct_s16be)(const u_char *, long, u_char *, long *, long); - long (*ct_u16be)(const u_char *, long, u_char *, long *, long); - long (*ct_s16le)(const u_char *, long, u_char *, long *, long); - long (*ct_u16le)(const u_char *, long, u_char *, long *, long); + long (*ct_ulaw)(const u_char *, unsigned long, u_char *, long *, long); + long (*ct_alaw)(const u_char *, unsigned long, u_char *, long *, long); + long (*ct_s8)(const u_char *, unsigned long, u_char *, long *, long); + long (*ct_u8)(const u_char *, unsigned long, u_char *, long *, long); + long (*ct_s16be)(const u_char *, unsigned long, u_char *, long *, long); + long (*ct_u16be)(const u_char *, unsigned long, u_char *, long *, long); + long (*ct_s16le)(const u_char *, unsigned long, u_char *, long *, long); + long (*ct_u16le)(const u_char *, unsigned long, u_char *, long *, long); } TRANS; struct sound_settings { @@ -484,6 +510,7 @@ struct sound_settings { int volume_right; int bass; /* tone (range is machine dependent) */ int treble; + int gain; int minDev; /* minor device number currently open */ #ifdef CONFIG_ATARI int bal; /* balance factor for expanding (not volume!) */ @@ -498,25 +525,32 @@ static struct sound_settings sound; static void *AtaAlloc(unsigned int size, int flags); static void AtaFree(void *, unsigned int size); static int AtaIrqInit(void); +#ifdef MODULE +static void AtaIrqCleanUp(void); +#endif /* MODULE */ static int AtaSetBass(int bass); static int AtaSetTreble(int treble); static void TTSilence(void); static void TTInit(void); static int TTSetFormat(int format); static int TTSetVolume(int volume); +static int TTSetGain(int gain); static void FalconSilence(void); static void FalconInit(void); static int FalconSetFormat(int format); static int FalconSetVolume(int volume); static void ata_sq_play_next_frame(int index); static void AtaPlay(void); -static void ata_sq_interrupt(int irq, struct pt_regs *fp, void *dummy); +static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp); #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA static void *AmiAlloc(unsigned int size, int flags); static void AmiFree(void *, unsigned int); static int AmiIrqInit(void); +#ifdef MODULE +static void AmiIrqCleanUp(void); +#endif /* MODULE */ static void AmiSilence(void); static void AmiInit(void); static int AmiSetFormat(int format); @@ -524,7 +558,7 @@ static int AmiSetVolume(int volume); static int AmiSetTreble(int treble); static void ami_sq_play_next_frame(int index); static void AmiPlay(void); -static void ami_sq_interrupt(int irq, struct pt_regs *fp, void *dummy); +static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp); #endif /* CONFIG_AMIGA */ @@ -541,7 +575,8 @@ static int sound_set_volume(int volume); static int sound_set_bass(int bass); #endif /* CONFIG_ATARI */ static int sound_set_treble(int treble); -static long sound_copy_translate(const u_char *userPtr, long userCount, +static long sound_copy_translate(const u_char *userPtr, + unsigned long userCount, u_char frame[], long *frameUsed, long frameLeft); @@ -597,7 +632,7 @@ struct sound_queue { static struct sound_queue sq; #define sq_block_address(i) (sq.buffers[i]) -#define SIGNAL_RECEIVED (current->signal & ~current->blocked) +#define SIGNAL_RECEIVED (signal_pending(current)) #define NON_BLOCKING(open_mode) (open_mode & O_NONBLOCK) #define ONE_SECOND HZ /* in jiffies (100ths of a second) */ #define NO_TIME_LIMIT 0xffffffff @@ -608,7 +643,7 @@ static struct sound_queue sq; static void sq_init(int numBufs, int bufSize, char **buffers); static void sq_play(void); -static int sq_write(const char *src, int uLeft); +static long sq_write(const char *src, unsigned long uLeft); static int sq_open(int open_mode); static void sq_reset(void); static int sq_sync(void); @@ -630,22 +665,27 @@ static struct sound_state state; static void state_init(void); static int state_open(int open_mode); static int state_release(void); -static int state_read(char *dest, int count); +static long state_read(char *dest, unsigned long count); /*** High level stuff ********************************************************/ static int sound_open(struct inode *inode, struct file *file); -static int sound_fsync(struct inode *inode, struct file *filp); -static void sound_release(struct inode *inode, struct file *file); -static int sound_lseek(struct inode *inode, struct file *file, off_t offset, - int orig); -static int sound_read(struct inode *inode, struct file *file, char *buf, - int count); -static int sound_write(struct inode *inode, struct file *file, const char *buf, - int count); -static int ioctl_return(int *addr, int value); +static int sound_fsync(struct file *filp, struct dentry *dentry); +static int sound_release(struct inode *inode, struct file *file); +static long long sound_lseek(struct file *file, long long offset, int orig); +static ssize_t sound_read(struct file *file, char *buf, size_t count, + loff_t *ppos); +static ssize_t sound_write(struct file *file, const char *buf, size_t count, + loff_t *ppos); +static inline int ioctl_return(int *addr, int value) +{ + if (value < 0) + return(value); + + return put_user(value, addr); +} static int unknown_minor_dev(char *fname, int dev); static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg); @@ -687,8 +727,8 @@ void sound_setup(char *str, int *ints); /* ++Martin: stub for now */ */ #ifdef CONFIG_ATARI -static long ata_ct_law(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ct_law(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; long count, used; @@ -699,7 +739,9 @@ static long ata_ct_law(const u_char *userPtr, long userCount, u_char frame[], count &= ~1; used = count; while (count > 0) { - *p++ = table[get_user(userPtr++)]; + u_char data; + get_user(data, userPtr++); + *p++ = table[data]; count--; } *frameUsed += used; @@ -707,8 +749,8 @@ static long ata_ct_law(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ct_s8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ct_s8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; void *p = &frame[*frameUsed]; @@ -717,14 +759,14 @@ static long ata_ct_s8(const u_char *userPtr, long userCount, u_char frame[], if (sound.soft.stereo) count &= ~1; used = count; - memcpy_fromfs(p, userPtr, count); + copy_from_user(p, userPtr, count); *frameUsed += used; return(used); } -static long ata_ct_u8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ct_u8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; @@ -733,7 +775,9 @@ static long ata_ct_u8(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft); used = count; while (count > 0) { - *p++ = get_user(userPtr++) ^ 0x80; + u_char data; + get_user(data, userPtr++); + *p++ = data ^ 0x80; count--; } } else { @@ -741,7 +785,9 @@ static long ata_ct_u8(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1; used = count*2; while (count > 0) { - *p++ = get_user(((u_short *)userPtr)++) ^ 0x8080; + u_short data; + get_user(data, ((u_short *)userPtr)++); + *p++ = data ^ 0x8080; count--; } } @@ -750,8 +796,8 @@ static long ata_ct_u8(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ct_s16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ct_s16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; u_long data; @@ -761,7 +807,7 @@ static long ata_ct_s16be(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1; used = count*2; while (count > 0) { - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); *p++ = data; *p++ = data; count--; @@ -771,15 +817,15 @@ static long ata_ct_s16be(const u_char *userPtr, long userCount, u_char frame[], void *p = (u_short *)&frame[*frameUsed]; count = min(userCount, frameLeft) & ~3; used = count; - memcpy_fromfs(p, userPtr, count); + copy_from_user(p, userPtr, count); *frameUsed += used; } return(used); } -static long ata_ct_u16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ct_u16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; u_long data; @@ -789,7 +835,8 @@ static long ata_ct_u16be(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1; used = count*2; while (count > 0) { - data = get_user(((u_short *)userPtr)++) ^ 0x8000; + get_user(data, ((u_short *)userPtr)++); + data ^= 0x8000; *p++ = data; *p++ = data; count--; @@ -800,7 +847,8 @@ static long ata_ct_u16be(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>2; used = count*4; while (count > 0) { - *p++ = get_user(((u_int *)userPtr)++) ^ 0x80008000; + get_user(data, ((u_int *)userPtr)++); + *p++ = data ^ 0x80008000; count--; } *frameUsed += used; @@ -809,8 +857,8 @@ static long ata_ct_u16be(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ct_s16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ct_s16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; u_long data; @@ -821,7 +869,7 @@ static long ata_ct_s16le(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1; used = count*2; while (count > 0) { - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); data = le2be16(data); *p++ = data; *p++ = data; @@ -833,7 +881,7 @@ static long ata_ct_s16le(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>2; used = count*4; while (count > 0) { - data = get_user(((u_int *)userPtr)++); + get_user(data, ((u_int *)userPtr)++); data = le2be16dbl(data); *p++ = data; count--; @@ -844,8 +892,8 @@ static long ata_ct_s16le(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ct_u16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ct_u16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; u_long data; @@ -856,7 +904,7 @@ static long ata_ct_u16le(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1; used = count*2; while (count > 0) { - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); data = le2be16(data) ^ 0x8000; *p++ = data; *p++ = data; @@ -867,7 +915,7 @@ static long ata_ct_u16le(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>2; used = count; while (count > 0) { - data = get_user(((u_int *)userPtr)++); + get_user(data, ((u_int *)userPtr)++); data = le2be16dbl(data) ^ 0x80008000; *p++ = data; count--; @@ -878,8 +926,8 @@ static long ata_ct_u16le(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ctx_law(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ctx_law(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; /* this should help gcc to stuff everything into registers */ @@ -893,10 +941,12 @@ static long ata_ctx_law(const u_char *userPtr, long userCount, u_char frame[], if (!sound.soft.stereo) { u_char *p = &frame[*frameUsed]; while (frameLeft) { + u_char c; if (bal < 0) { if (!userCount) break; - data = table[get_user(userPtr++)]; + get_user(c, userPtr++); + data = table[c]; userCount--; bal += hSpeed; } @@ -907,11 +957,14 @@ static long ata_ctx_law(const u_char *userPtr, long userCount, u_char frame[], } else { u_short *p = (u_short *)&frame[*frameUsed]; while (frameLeft >= 2) { + u_char c; if (bal < 0) { if (userCount < 2) break; - data = table[get_user(userPtr++)] << 8; - data |= table[get_user(userPtr++)]; + get_user(c, userPtr++); + data = table[c] << 8; + get_user(c, userPtr++); + data |= table[c]; userCount -= 2; bal += hSpeed; } @@ -928,8 +981,8 @@ static long ata_ctx_law(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ctx_s8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ctx_s8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { /* this should help gcc to stuff everything into registers */ u_long data = sound.data; @@ -945,7 +998,7 @@ static long ata_ctx_s8(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (!userCount) break; - data = get_user(userPtr++); + get_user(data, userPtr++); userCount--; bal += hSpeed; } @@ -959,7 +1012,7 @@ static long ata_ctx_s8(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (userCount < 2) break; - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); userCount -= 2; bal += hSpeed; } @@ -976,8 +1029,8 @@ static long ata_ctx_s8(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ctx_u8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ctx_u8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { /* this should help gcc to stuff everything into registers */ u_long data = sound.data; @@ -993,7 +1046,8 @@ static long ata_ctx_u8(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (!userCount) break; - data = get_user(userPtr++) ^ 0x80; + get_user(data, userPtr++); + data ^= 0x80; userCount--; bal += hSpeed; } @@ -1007,7 +1061,8 @@ static long ata_ctx_u8(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (userCount < 2) break; - data = get_user(((u_short *)userPtr)++) ^ 0x8080; + get_user(data, ((u_short *)userPtr)++); + data ^= 0x8080; userCount -= 2; bal += hSpeed; } @@ -1024,8 +1079,8 @@ static long ata_ctx_u8(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ctx_s16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ctx_s16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { /* this should help gcc to stuff everything into registers */ u_long data = sound.data; @@ -1041,7 +1096,7 @@ static long ata_ctx_s16be(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (userCount < 2) break; - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); userCount -= 2; bal += hSpeed; } @@ -1056,7 +1111,7 @@ static long ata_ctx_s16be(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (userCount < 4) break; - data = get_user(((u_int *)userPtr)++); + get_user(data, ((u_int *)userPtr)++); userCount -= 4; bal += hSpeed; } @@ -1073,8 +1128,8 @@ static long ata_ctx_s16be(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ctx_u16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ctx_u16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { /* this should help gcc to stuff everything into registers */ u_long data = sound.data; @@ -1090,7 +1145,8 @@ static long ata_ctx_u16be(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (userCount < 2) break; - data = get_user(((u_short *)userPtr)++) ^ 0x8000; + get_user(data, ((u_short *)userPtr)++); + data ^= 0x8000; userCount -= 2; bal += hSpeed; } @@ -1105,7 +1161,8 @@ static long ata_ctx_u16be(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (userCount < 4) break; - data = get_user(((u_int *)userPtr)++) ^ 0x80008000; + get_user(data, ((u_int *)userPtr)++); + data ^= 0x80008000; userCount -= 4; bal += hSpeed; } @@ -1122,8 +1179,8 @@ static long ata_ctx_u16be(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ctx_s16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ctx_s16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { /* this should help gcc to stuff everything into registers */ u_long data = sound.data; @@ -1139,7 +1196,7 @@ static long ata_ctx_s16le(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (userCount < 2) break; - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); data = le2be16(data); userCount -= 2; bal += hSpeed; @@ -1155,7 +1212,7 @@ static long ata_ctx_s16le(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (userCount < 4) break; - data = get_user(((u_int *)userPtr)++); + get_user(data, ((u_int *)userPtr)++); data = le2be16dbl(data); userCount -= 4; bal += hSpeed; @@ -1173,8 +1230,8 @@ static long ata_ctx_s16le(const u_char *userPtr, long userCount, u_char frame[], } -static long ata_ctx_u16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ata_ctx_u16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { /* this should help gcc to stuff everything into registers */ u_long data = sound.data; @@ -1190,7 +1247,7 @@ static long ata_ctx_u16le(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (userCount < 2) break; - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); data = le2be16(data) ^ 0x8000; userCount -= 2; bal += hSpeed; @@ -1206,7 +1263,7 @@ static long ata_ctx_u16le(const u_char *userPtr, long userCount, u_char frame[], if (bal < 0) { if (userCount < 4) break; - data = get_user(((u_int *)userPtr)++); + get_user(data, ((u_int *)userPtr)++); data = le2be16dbl(data) ^ 0x80008000; userCount -= 4; bal += hSpeed; @@ -1226,8 +1283,8 @@ static long ata_ctx_u16le(const u_char *userPtr, long userCount, u_char frame[], #ifdef CONFIG_AMIGA -static long ami_ct_law(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ami_ct_law(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8; long count, used; @@ -1237,7 +1294,9 @@ static long ami_ct_law(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft) & ~1; used = count; while (count > 0) { - *p++ = table[get_user(userPtr++)]; + u_char data; + get_user(data, userPtr++); + *p++ = table[data]; count--; } } else { @@ -1246,8 +1305,11 @@ static long ami_ct_law(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1 & ~1; used = count*2; while (count > 0) { - *left++ = table[get_user(userPtr++)]; - *right++ = table[get_user(userPtr++)]; + u_char data; + get_user(data, userPtr++); + *left++ = table[data]; + get_user(data, userPtr++); + *right++ = table[data]; count--; } } @@ -1256,8 +1318,8 @@ static long ami_ct_law(const u_char *userPtr, long userCount, u_char frame[], } -static long ami_ct_s8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ami_ct_s8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; @@ -1265,15 +1327,15 @@ static long ami_ct_s8(const u_char *userPtr, long userCount, u_char frame[], void *p = &frame[*frameUsed]; count = min(userCount, frameLeft) & ~1; used = count; - memcpy_fromfs(p, userPtr, count); + copy_from_user(p, userPtr, count); } else { u_char *left = &frame[*frameUsed>>1]; u_char *right = left+sq.block_size_half; count = min(userCount, frameLeft)>>1 & ~1; used = count*2; while (count > 0) { - *left++ = get_user(userPtr++); - *right++ = get_user(userPtr++); + get_user(*left++, userPtr++); + get_user(*right++, userPtr++); count--; } } @@ -1282,8 +1344,8 @@ static long ami_ct_s8(const u_char *userPtr, long userCount, u_char frame[], } -static long ami_ct_u8(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ami_ct_u8(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; @@ -1292,7 +1354,9 @@ static long ami_ct_u8(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft) & ~1; used = count; while (count > 0) { - *p++ = get_user(userPtr++) ^ 0x80; + u_char data; + get_user(data, userPtr++); + *p++ = data ^ 0x80; count--; } } else { @@ -1301,18 +1365,21 @@ static long ami_ct_u8(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1 & ~1; used = count*2; while (count > 0) { - *left++ = get_user(userPtr++) ^ 0x80; - *right++ = get_user(userPtr++) ^ 0x80; + u_char data; + get_user(data, userPtr++); + *left++ = data ^ 0x80; + get_user(data, userPtr++); + *right++ = data ^ 0x80; count--; - } + } } *frameUsed += used; return(used); } -static long ami_ct_s16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ami_ct_s16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; u_long data; @@ -1323,9 +1390,9 @@ static long ami_ct_s16be(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1 & ~1; used = count*2; while (count > 0) { - data = get_user(((u_short *)userPtr)++); - *high = data>>8; - *low = (data>>2) & 0x3f; + get_user(data, ((u_short *)userPtr)++); + *high++ = data>>8; + *low++ = (data>>2) & 0x3f; count--; } } else { @@ -1336,22 +1403,22 @@ static long ami_ct_s16be(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>2 & ~1; used = count*4; while (count > 0) { - data = get_user(((u_short *)userPtr)++); - *lefth = data>>8; - *leftl = (data>>2) & 0x3f; - data = get_user(((u_short *)userPtr)++); - *righth = data>>8; - *rightl = (data>>2) & 0x3f; + get_user(data, ((u_short *)userPtr)++); + *lefth++ = data>>8; + *leftl++ = (data>>2) & 0x3f; + get_user(data, ((u_short *)userPtr)++); + *righth++ = data>>8; + *rightl++ = (data>>2) & 0x3f; count--; - } + } } *frameUsed += used; return(used); } -static long ami_ct_u16be(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ami_ct_u16be(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; u_long data; @@ -1362,9 +1429,10 @@ static long ami_ct_u16be(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1 & ~1; used = count*2; while (count > 0) { - data = get_user(((u_short *)userPtr)++) ^ 0x8000; - *high = data>>8; - *low = (data>>2) & 0x3f; + get_user(data, ((u_short *)userPtr)++); + data ^= 0x8000; + *high++ = data>>8; + *low++ = (data>>2) & 0x3f; count--; } } else { @@ -1375,12 +1443,14 @@ static long ami_ct_u16be(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>2 & ~1; used = count*4; while (count > 0) { - data = get_user(((u_short *)userPtr)++) ^ 0x8000; - *lefth = data>>8; - *leftl = (data>>2) & 0x3f; - data = get_user(((u_short *)userPtr)++) ^ 0x8000; - *righth = data>>8; - *rightl = (data>>2) & 0x3f; + get_user(data, ((u_short *)userPtr)++); + data ^= 0x8000; + *lefth++ = data>>8; + *leftl++ = (data>>2) & 0x3f; + get_user(data, ((u_short *)userPtr)++); + data ^= 0x8000; + *righth++ = data>>8; + *rightl++ = (data>>2) & 0x3f; count--; } } @@ -1389,8 +1459,8 @@ static long ami_ct_u16be(const u_char *userPtr, long userCount, u_char frame[], } -static long ami_ct_s16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ami_ct_s16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; u_long data; @@ -1401,10 +1471,10 @@ static long ami_ct_s16le(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1 & ~1; used = count*2; while (count > 0) { - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); data = le2be16(data); - *high = data>>8; - *low = (data>>2) & 0x3f; + *high++ = data>>8; + *low++ = (data>>2) & 0x3f; count--; } } else { @@ -1415,14 +1485,14 @@ static long ami_ct_s16le(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>2 & ~1; used = count*4; while (count > 0) { - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); data = le2be16(data); - *lefth = data>>8; - *leftl = (data>>2) & 0x3f; - data = get_user(((u_short *)userPtr)++); + *lefth++ = data>>8; + *leftl++ = (data>>2) & 0x3f; + get_user(data, ((u_short *)userPtr)++); data = le2be16(data); - *righth = data>>8; - *rightl = (data>>2) & 0x3f; + *righth++ = data>>8; + *rightl++ = (data>>2) & 0x3f; count--; } } @@ -1431,8 +1501,8 @@ static long ami_ct_s16le(const u_char *userPtr, long userCount, u_char frame[], } -static long ami_ct_u16le(const u_char *userPtr, long userCount, u_char frame[], - long *frameUsed, long frameLeft) +static long ami_ct_u16le(const u_char *userPtr, unsigned long userCount, + u_char frame[], long *frameUsed, long frameLeft) { long count, used; u_long data; @@ -1443,10 +1513,10 @@ static long ami_ct_u16le(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>1 & ~1; used = count*2; while (count > 0) { - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); data = le2be16(data) ^ 0x8000; - *high = data>>8; - *low = (data>>2) & 0x3f; + *high++ = data>>8; + *low++ = (data>>2) & 0x3f; count--; } } else { @@ -1457,14 +1527,14 @@ static long ami_ct_u16le(const u_char *userPtr, long userCount, u_char frame[], count = min(userCount, frameLeft)>>2 & ~1; used = count*4; while (count > 0) { - data = get_user(((u_short *)userPtr)++); + get_user(data, ((u_short *)userPtr)++); data = le2be16(data) ^ 0x8000; - *lefth = data>>8; - *leftl = (data>>2) & 0x3f; - data = get_user(((u_short *)userPtr)++); + *lefth++ = data>>8; + *leftl++ = (data>>2) & 0x3f; + get_user(data, ((u_short *)userPtr)++); data = le2be16(data) ^ 0x8000; - *righth = data>>8; - *rightl = (data>>2) & 0x3f; + *righth++ = data>>8; + *rightl++ = (data>>2) & 0x3f; count--; } } @@ -1513,28 +1583,12 @@ static TRANS transAmiga = { static void *AtaAlloc(unsigned int size, int flags) { - int order; - unsigned int a_size; - order = 0; - a_size = PAGE_SIZE; - while (a_size < size) { - order++; - a_size <<= 1; - } - return (void *) __get_dma_pages(flags, order); + return( atari_stram_alloc( size, NULL, "dmasound" )); } static void AtaFree(void *obj, unsigned int size) { - int order; - unsigned int a_size; - order = 0; - a_size = PAGE_SIZE; - while (a_size < size) { - order++; - a_size <<= 1; - } - free_pages ((unsigned long) obj, order); + atari_stram_free( obj ); } static int AtaIrqInit(void) @@ -1550,12 +1604,22 @@ static int AtaIrqInit(void) mfp.tim_dt_a = 1; /* Cause interrupt after first event. */ mfp.tim_ct_a = 8; /* Turn on event counting. */ /* Register interrupt handler. */ - add_isr(IRQ_MFP_TIMA, ata_sq_interrupt, IRQ_TYPE_SLOW, NULL, "DMA sound"); + request_irq(IRQ_MFP_TIMA, ata_sq_interrupt, IRQ_TYPE_SLOW, + "DMA sound", ata_sq_interrupt); mfp.int_en_a |= 0x20; /* Turn interrupt on. */ mfp.int_mk_a |= 0x20; return(1); } +#ifdef MODULE +static void AtaIrqCleanUp(void) +{ + mfp.tim_ct_a = 0; /* stop timer */ + mfp.int_en_a &= ~0x20; /* turn interrupt off */ + free_irq(IRQ_MFP_TIMA, ata_sq_interrupt); +} +#endif /* MODULE */ + #define TONE_VOXWARE_TO_DB(v) \ (((v) < 0) ? -12 : ((v) > 100) ? 12 : ((v) - 50) * 6 / 25) @@ -1685,6 +1749,18 @@ static int TTSetVolume(int volume) } +#define GAIN_VOXWARE_TO_DB(v) \ + (((v) < 0) ? -80 : ((v) > 100) ? 0 : ((v) * 4) / 5 - 80) +#define GAIN_DB_TO_VOXWARE(v) ((((v) + 80) * 5 + 1) / 4) + +static int TTSetGain(int gain) +{ + sound.gain = GAIN_VOXWARE_TO_DB(gain); + atari_microwire_cmd(MW_LM1992_VOLUME(sound.gain)); + return GAIN_DB_TO_VOXWARE(sound.gain); +} + + /* * Falcon @@ -1927,7 +2003,7 @@ static void AtaPlay(void) } -static void ata_sq_interrupt(int irq, struct pt_regs *fp, void *dummy) +static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp) { #if 0 /* ++TeSche: if you should want to test this... */ @@ -2018,11 +2094,21 @@ static int AmiIrqInit(void) custom.dmacon = AMI_AUDIO_OFF; /* Register interrupt handler. */ - if (!add_isr(IRQ_AMIGA_AUD0, ami_sq_interrupt, 0, NULL, "DMA sound")) - panic("Couldn't add audio interrupt"); + if (request_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt, 0, + "DMA sound", ami_sq_interrupt)) + return(0); return(1); } +#ifdef MODULE +static void AmiIrqCleanUp(void) +{ + /* turn off DMA for audio channels */ + custom.dmacon = AMI_AUDIO_OFF; + /* release the interrupt */ + free_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt); +} +#endif /* MODULE */ static void AmiSilence(void) { @@ -2047,14 +2133,16 @@ static void AmiInit(void) if (period < amiga_audio_min_period) { /* we would need to squeeze the sound, but we won't do that */ period = amiga_audio_min_period; - sound.hard.speed = amiga_colorclock/(period+1); } else if (period > 65535) { period = 65535; - sound.hard.speed = amiga_colorclock/(period+1); } + sound.hard.speed = amiga_colorclock/(period+1); + for (i = 0; i < 4; i++) custom.aud[i].audper = period; amiga_audio_period = period; + + AmiSetTreble(50); /* recommended for newer amiga models */ } @@ -2113,10 +2201,10 @@ static int AmiSetVolume(int volume) static int AmiSetTreble(int treble) { sound.treble = treble; - if (treble > 50) - ciaa.pra |= 0x02; - else + if (treble < 50) ciaa.pra &= ~0x02; + else + ciaa.pra |= 0x02; return(treble); } @@ -2161,6 +2249,8 @@ static void ami_sq_play_next_frame(int index) /* We can play pseudo 14-bit only with the maximum volume */ ch3 = ch0+sq.block_size_quarter; ch2 = ch1+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); @@ -2210,7 +2300,7 @@ static void AmiPlay(void) } -static void ami_sq_interrupt(int irq, struct pt_regs *fp, void *dummy) +static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp) { int minframes = 1; @@ -2256,20 +2346,34 @@ static void ami_sq_interrupt(int irq, struct pt_regs *fp, void *dummy) #ifdef CONFIG_ATARI static MACHINE machTT = { - DMASND_TT, AtaAlloc, AtaFree, AtaIrqInit, TTInit, TTSilence, TTSetFormat, - TTSetVolume, AtaSetBass, AtaSetTreble, AtaPlay + DMASND_TT, AtaAlloc, AtaFree, AtaIrqInit, +#ifdef MODULE + AtaIrqCleanUp, +#endif /* MODULE */ + TTInit, TTSilence, TTSetFormat, TTSetVolume, AtaSetBass, AtaSetTreble, + TTSetGain, + AtaPlay }; static MACHINE machFalcon = { - DMASND_FALCON, AtaAlloc, AtaFree, AtaIrqInit, FalconInit, FalconSilence, - FalconSetFormat, FalconSetVolume, AtaSetBass, AtaSetTreble, AtaPlay + DMASND_FALCON, AtaAlloc, AtaFree, AtaIrqInit, +#ifdef MODULE + AtaIrqCleanUp, +#endif /* MODULE */ + FalconInit, FalconSilence, FalconSetFormat, FalconSetVolume, AtaSetBass, + AtaSetTreble, NULL, AtaPlay }; #endif /* CONFIG_ATARI */ #ifdef CONFIG_AMIGA static MACHINE machAmiga = { - DMASND_AMIGA, AmiAlloc, AmiFree, AmiIrqInit, AmiInit, AmiSilence, - AmiSetFormat, AmiSetVolume, NULL, AmiSetTreble, AmiPlay + DMASND_AMIGA, AmiAlloc, AmiFree, AmiIrqInit, +#ifdef MODULE + AmiIrqCleanUp, +#endif /* MODULE */ + AmiInit, AmiSilence, AmiSetFormat, AmiSetVolume, NULL, AmiSetTreble, + NULL, + AmiPlay }; #endif /* CONFIG_AMIGA */ @@ -2339,6 +2443,11 @@ static int sound_set_bass(int bass) { return(sound.mach.setBass ? (*sound.mach.setBass)(bass) : 50); } + +static int sound_set_gain(int gain) +{ + return sound.mach.setGain ? sound.mach.setGain(gain) : 100; +} #endif /* CONFIG_ATARI */ @@ -2348,11 +2457,12 @@ static int sound_set_treble(int treble) } -static long sound_copy_translate(const u_char *userPtr, long userCount, +static long sound_copy_translate(const u_char *userPtr, + unsigned long userCount, u_char frame[], long *frameUsed, long frameLeft) { - long (*ct_func)(const u_char *, long, u_char *, long *, long) = NULL; + long (*ct_func)(const u_char *, unsigned long, u_char *, long *, long) = NULL; switch (sound.soft.format) { case AFMT_MU_LAW: @@ -2452,6 +2562,7 @@ static int mixer_release(void) static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { + int data; switch (sound.mach.type) { #ifdef CONFIG_ATARI case DMASND_FALCON: @@ -2469,9 +2580,10 @@ static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, VOLUME_ATT_TO_VOXWARE(sound.volume_left) | VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8)); case SOUND_MIXER_WRITE_MIC: + IOCTL_IN(arg, data); tt_dmasnd.input_gain = - RECLEVEL_VOXWARE_TO_GAIN(IOCTL_IN(arg) & 0xff) << 4 | - RECLEVEL_VOXWARE_TO_GAIN(IOCTL_IN(arg) >> 8 & 0xff); + RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 | + RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff); /* fall thru, return set value */ case SOUND_MIXER_READ_MIC: return(IOCTL_OUT(arg, @@ -2487,14 +2599,16 @@ static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100)); } case SOUND_MIXER_WRITE_VOLUME: - return(IOCTL_OUT(arg, sound_set_volume(IOCTL_IN(arg)))); + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_volume(data))); case SOUND_MIXER_WRITE_SPEAKER: { int porta; + IOCTL_IN(arg, data); cli(); sound_ym.rd_data_reg_sel = 14; porta = (sound_ym.rd_data_reg_sel & ~0x40) | - (IOCTL_IN(arg) < 50 ? 0x40 : 0); + (data < 50 ? 0x40 : 0); sound_ym.wd_data = porta; sti(); return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100)); @@ -2507,8 +2621,7 @@ static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, case SOUND_MIXER_READ_DEVMASK: return(IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS | - ((boot_info.bi_atari.mch_cookie >> 16) == ATARI_MCH_TT ? - SOUND_MASK_SPEAKER : 0))); + (MACH_IS_TT ? SOUND_MASK_SPEAKER : 0))); case SOUND_MIXER_READ_RECMASK: return(IOCTL_OUT(arg, 0)); case SOUND_MIXER_READ_STEREODEVS: @@ -2521,10 +2634,12 @@ static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, return(IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.bass))); case SOUND_MIXER_READ_TREBLE: return(IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.treble))); + case SOUND_MIXER_READ_OGAIN: + return(IOCTL_OUT(arg, GAIN_DB_TO_VOXWARE(sound.gain))); case SOUND_MIXER_READ_SPEAKER: { int porta; - if ((boot_info.bi_atari.mch_cookie >> 16) == ATARI_MCH_TT) { + if (MACH_IS_TT) { cli(); sound_ym.rd_data_reg_sel = 14; porta = sound_ym.rd_data_reg_sel; @@ -2534,18 +2649,25 @@ static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, return(-EINVAL); } case SOUND_MIXER_WRITE_VOLUME: - return(IOCTL_OUT(arg, sound_set_volume(IOCTL_IN(arg)))); + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_volume(data))); case SOUND_MIXER_WRITE_BASS: - return(IOCTL_OUT(arg, sound_set_bass(IOCTL_IN(arg)))); + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_bass(data))); case SOUND_MIXER_WRITE_TREBLE: - return(IOCTL_OUT(arg, sound_set_treble(IOCTL_IN(arg)))); + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_treble(data))); + case SOUND_MIXER_WRITE_OGAIN: + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_gain(data))); case SOUND_MIXER_WRITE_SPEAKER: - if ((boot_info.bi_atari.mch_cookie >> 16) == ATARI_MCH_TT) { + if (MACH_IS_TT) { int porta; + IOCTL_IN(arg, data); cli(); sound_ym.rd_data_reg_sel = 14; porta = (sound_ym.rd_data_reg_sel & ~0x40) | - (IOCTL_IN(arg) < 50 ? 0x40 : 0); + (data < 50 ? 0x40 : 0); sound_ym.wd_data = porta; sti(); return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100)); @@ -2569,11 +2691,13 @@ static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, VOLUME_AMI_TO_VOXWARE(sound.volume_left) | VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8)); case SOUND_MIXER_WRITE_VOLUME: - return(IOCTL_OUT(arg, sound_set_volume(IOCTL_IN(arg)))); + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_volume(data))); case SOUND_MIXER_READ_TREBLE: return(IOCTL_OUT(arg, sound.treble)); case SOUND_MIXER_WRITE_TREBLE: - return(IOCTL_OUT(arg, sound_set_treble(IOCTL_IN(arg)))); + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_treble(data))); } break; #endif /* CONFIG_AMIGA */ @@ -2640,6 +2764,7 @@ static void sq_init(int numBufs, int bufSize, char **buffers) /* before the first open to /dev/dsp this wouldn't be set */ sound.soft = sound.dsp; + sound.hard = sound.dsp; } @@ -2651,9 +2776,9 @@ static void sq_play(void) /* ++TeSche: radically changed this one too */ -static int sq_write(const char *src, int uLeft) +static long sq_write(const char *src, unsigned long uLeft) { - int uWritten = 0; + long uWritten = 0; u_char *dest; long uUsed, bUsed, bLeft; @@ -2661,7 +2786,7 @@ static int sq_write(const char *src, int uLeft) * Hey, that's an honest question! Or does any other part of the * filesystem already checks this situation? I really don't know. */ - if (uLeft < 1) + if (uLeft == 0) return(0); /* The interrupt doesn't start to play the last, incomplete frame. @@ -2680,7 +2805,7 @@ static int sq_write(const char *src, int uLeft) } do { - if (sq.count == sq.max_count) { + while (sq.count == sq.max_count) { sq_play(); if (NON_BLOCKING(sq.open_mode)) return(uWritten > 0 ? uWritten : -EAGAIN); @@ -2901,14 +3026,14 @@ static int state_release(void) } -static int state_read(char *dest, int count) +static long state_read(char *dest, unsigned long count) { int n = state.len-state.ptr; if (n > count) n = count; if (n <= 0) return(0); - memcpy_tofs(dest, &state.buf[state.ptr], n); + copy_to_user(dest, &state.buf[state.ptr], n); state.ptr += n; return(n); } @@ -2921,37 +3046,44 @@ static int state_read(char *dest, int count) static int sound_open(struct inode *inode, struct file *file) { int dev = MINOR(inode->i_rdev) & 0x0f; + int rc = 0; switch (dev) { case SND_DEV_STATUS: - return(state_open(file->f_flags)); + rc = state_open(file->f_flags); + break; case SND_DEV_CTL: - return(mixer_open(file->f_flags)); + rc = mixer_open(file->f_flags); + break; case SND_DEV_DSP: case SND_DEV_AUDIO: - { - int rc = sq_open(file->f_flags); - if (rc == 0) { - sound.minDev = dev; - sound.soft = sound.dsp; - sound_init(); - if (dev == SND_DEV_AUDIO) { - sound_set_speed(8000); - sound_set_stereo(0); - sound_set_format(AFMT_MU_LAW); - } + rc = sq_open(file->f_flags); + if (rc == 0) { + sound.minDev = dev; + sound.soft = sound.dsp; + sound.hard = sound.dsp; + sound_init(); + if (dev == SND_DEV_AUDIO) { + sound_set_speed(8000); + sound_set_stereo(0); + sound_set_format(AFMT_MU_LAW); } - return(rc); } + break; default: - return(-ENXIO); + rc = -ENXIO; } +#ifdef MODULE + if (rc >= 0) + MOD_INC_USE_COUNT; +#endif + return(rc); } -static int sound_fsync(struct inode *inode, struct file *filp) +static int sound_fsync(struct file *filp, struct dentry *dentry) { - int dev = MINOR(inode->i_rdev) & 0x0f; + int dev = MINOR(dentry->d_inode->i_rdev) & 0x0f; switch (dev) { case SND_DEV_STATUS: @@ -2966,33 +3098,44 @@ static int sound_fsync(struct inode *inode, struct file *filp) } -static void sound_release(struct inode *inode, struct file *file) +static int sound_release(struct inode *inode, struct file *file) { int dev = MINOR(inode->i_rdev); switch (dev & 0x0f) { - case SND_DEV_STATUS: state_release(); return; - case SND_DEV_CTL: mixer_release(); return; + case SND_DEV_STATUS: + state_release(); + break; + case SND_DEV_CTL: + mixer_release(); + break; case SND_DEV_DSP: case SND_DEV_AUDIO: - sq_release(); sound.soft = sound.dsp; sound_silence(); - return; + sq_release(); + sound.soft = sound.dsp; + sound.hard = sound.dsp; + sound_silence(); + break; default: - unknown_minor_dev("sound_release", dev); + return unknown_minor_dev("sound_release", dev); } +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; } -static int sound_lseek(struct inode *inode, struct file *file, off_t offset, - int orig) +static long long sound_lseek(struct file *file, long long offset, int orig) { - return(-EPERM); + return -ESPIPE; } -static int sound_read(struct inode *inode, struct file *file, char *buf, - int count) +static ssize_t sound_read(struct file *file, char *buf, size_t count, + loff_t *ppos) { + struct inode *inode = file->f_dentry->d_inode; int dev = MINOR(inode->i_rdev); switch (dev & 0x0f) { @@ -3008,9 +3151,10 @@ static int sound_read(struct inode *inode, struct file *file, char *buf, } -static int sound_write(struct inode *inode, struct file *file, const char *buf, - int count) +static ssize_t sound_write(struct file *file, const char *buf, size_t count, + loff_t *ppos) { + struct inode *inode = file->f_dentry->d_inode; int dev = MINOR(inode->i_rdev); switch (dev & 0x0f) { @@ -3026,22 +3170,6 @@ static int sound_write(struct inode *inode, struct file *file, const char *buf, } -static int ioctl_return(int *addr, int value) -{ - int error; - - if (value < 0) - return(value); - - error = verify_area(VERIFY_WRITE, addr, sizeof(int)); - if (error) - return(error); - - put_user(value, addr); - return(0); -} - - static int unknown_minor_dev(char *fname, int dev) { /* printk("%s: Unknown minor device %d\n", fname, dev); */ @@ -3054,6 +3182,7 @@ static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd, { int dev = MINOR(inode->i_rdev); u_long fmt; + int data; switch (dev & 0x0f) { case SND_DEV_STATUS: @@ -3068,23 +3197,27 @@ static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd, return(0); case SNDCTL_DSP_POST: case SNDCTL_DSP_SYNC: - return(sound_fsync(inode, file)); + return(sound_fsync(file, file->f_dentry)); /* ++TeSche: before changing any of these it's probably wise to * wait until sound playing has settled down */ case SNDCTL_DSP_SPEED: - sound_fsync(inode, file); - return(IOCTL_OUT(arg, sound_set_speed(IOCTL_IN(arg)))); + sound_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_speed(data))); case SNDCTL_DSP_STEREO: - sound_fsync(inode, file); - return(IOCTL_OUT(arg, sound_set_stereo(IOCTL_IN(arg)))); + sound_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_stereo(data))); case SOUND_PCM_WRITE_CHANNELS: - sound_fsync(inode, file); - return(IOCTL_OUT(arg, sound_set_stereo(IOCTL_IN(arg)-1)+1)); + sound_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_stereo(data-1)+1)); case SNDCTL_DSP_SETFMT: - sound_fsync(inode, file); - return(IOCTL_OUT(arg, sound_set_format(IOCTL_IN(arg)))); + sound_fsync(file, file->f_dentry); + IOCTL_IN(arg, data); + return(IOCTL_OUT(arg, sound_set_format(data))); case SNDCTL_DSP_GETFMTS: fmt = 0; if (sound.trans) { @@ -3146,10 +3279,9 @@ static struct file_operations sound_fops = void soundcard_init(void) { int has_sound = 0; - char **buffers; int i; - switch (boot_info.machtype) { + switch (m68k_machtype) { #ifdef CONFIG_ATARI case MACH_ATARI: if (ATARIHW_PRESENT(PCM_8BIT)) { @@ -3180,26 +3312,29 @@ void soundcard_init(void) return; /* Set up sound queue, /dev/audio and /dev/dsp. */ - buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL); - if (!buffers) { - out_of_memory: + sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL); + if (!sound_buffers) { +out_of_memory: printk("DMA sound driver: Not enough buffer memory, driver disabled!\n"); return; } for (i = 0; i < numBufs; i++) { - buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL); - if (!buffers[i]) { + sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL); + if (!sound_buffers[i]) { while (i--) - sound.mach.dma_free (buffers[i], bufSize << 10); - kfree (buffers); + sound.mach.dma_free (sound_buffers[i], bufSize << 10); + kfree (sound_buffers); + sound_buffers = 0; goto out_of_memory; } } +#ifndef MODULE /* Register driver with the VFS. */ register_chrdev(SOUND_MAJOR, "sound", &sound_fops); +#endif - sq_init(numBufs, bufSize << 10, buffers); + sq_init(numBufs, bufSize << 10, sound_buffers); /* Set up /dev/sndstat. */ state_init(); @@ -3211,6 +3346,9 @@ void soundcard_init(void) printk("DMA sound driver: Interrupt initialization failed\n"); return; } +#ifdef MODULE + irq_installed = 1; +#endif printk("DMA sound driver installed, using %d buffers of %dk.\n", numBufs, bufSize); @@ -3223,6 +3361,9 @@ void sound_setup(char *str, int *ints) /* ++Martin: stub, could possibly be merged with soundcard.c et al later */ } + +#define MAXARGS 8 /* Should be sufficient for now */ + void dmasound_setup(char *str, int *ints) { /* check the bootstrap parameter for "dmasound=" */ @@ -3250,3 +3391,56 @@ void dmasound_setup(char *str, int *ints) printk("dmasound_setup: illegal number of arguments\n"); } } + + +#ifdef MODULE + +static int dmasound[MAXARGS] = { 0 }; + +int init_module(void) +{ + int err, i = 0; + int ints[MAXARGS+1]; + + while (i < MAXARGS && dmasound[i]) + ints[i + 1] = dmasound[i++]; + ints[0] = i; + + if (i) + dmasound_setup("dmasound=", ints); + + err = register_chrdev(SOUND_MAJOR, "sound", &sound_fops); + if (err) { + printk("dmasound: driver already loaded/included in kernel\n"); + return err; + } + chrdev_registered = 1; + soundcard_init(); + + return 0; +} + + +void cleanup_module(void) +{ + int i; + + if (MOD_IN_USE) + return; + + if (chrdev_registered) + unregister_chrdev(SOUND_MAJOR, "sound"); + + if (irq_installed) { + sound_silence(); + sound.mach.irqcleanup(); + } + + if (sound_buffers) { + for (i = 0; i < numBufs; i++) + sound.mach.dma_free(sound_buffers[i], bufSize << 10); + kfree(sound_buffers); + } +} + +#endif /* MODULE */ diff --git a/drivers/sound/es1370.c b/drivers/sound/es1370.c new file mode 100644 index 000000000000..27ac1112abe5 --- /dev/null +++ b/drivers/sound/es1370.c @@ -0,0 +1,2776 @@ +/*****************************************************************************/ + +/* + * es1370.c -- Ensoniq ES1370/Ashai Kasei AK4531 audio driver. + * + * Copyright (C) 1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * 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. + * + * Special thanks to David C. Niemi + * + * + * Module command line parameters: + * joystick if 1 enables the joystick interface on the card; but it still + * needs a separate joystick driver (presumably PC standard, although + * the chip doc doesn't say anything and it looks slightly fishy from + * the PCI standpoint...) + * lineout if 1 the LINE jack is used as an output instead of an input. + * LINE then contains the unmixed dsp output. This can be used + * to make the card a four channel one: use dsp to output two + * channels to LINE and dac to output the other two channels to + * SPKR. Set the mixer to only output synth to SPKR. + * micz it looks like this changes the MIC input impedance. I don't know + * any detail though. + * + * Note: sync mode is not yet supported (i.e. running dsp and dac from the same + * clock source) + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/dsp1 additional DAC, like /dev/dsp, but output only, + * only 5512, 11025, 22050 and 44100 samples/s, + * outputs to mixer "SYNTH" setting + * /dev/midi simple MIDI UART interface, no ioctl + * + * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed + * to be done in software. That is what /dev/dac is for. By now (Q2 1998) + * there are several MIDI to PCM (WAV) packages, one of them is timidity. + * + * Revision history + * 26.03.98 0.1 Initial release + * 31.03.98 0.2 Fix bug in GETOSPACE + * 04.04.98 0.3 Make it work (again) under 2.0.33 + * Fix mixer write operation not returning the actual + * settings + * 05.04.98 0.4 First attempt at using the new PCI stuff + * 29.04.98 0.5 Fix hang when ^C is pressed on amp + * 07.05.98 0.6 Don't double lock around stop_*() in *_release() + * 10.05.98 0.7 First stab at a simple midi interface (no bells&whistles) + * 14.05.98 0.8 Don't allow excessive interrupt rates + * 08.06.98 0.9 First release using Alan Cox' soundcore instead of + * miscdevice + * 05.07.98 0.10 Fixed the driver to correctly maintin OSS style volume + * settings (not sure if this should be standard) + * Fixed many references: f_flags should be f_mode + * -- Gerald Britton + * 06.07.98 0.11 Reversed the mixer changes by Gerald Britton, as they + * are bogus. According to OSS API docs, MIXER_WRITE ioctls + * return the actual audio level of the hardware, i.e. rounded + * to the next possible hardware volume step. Gerald's patch + * effectively just stored and returned the requested value by + * the application. + * + * some important things missing in Ensoniq documentation: + * + * Experimental PCLKDIV results: play the same waveforms on both DAC1 and DAC2 + * and vary PCLKDIV to obtain zero beat. + * 5512sps: 254 + * 44100sps: 30 + * seems to be fs = 1411200/(PCLKDIV+2) + * + * should find out when curr_sample_ct is cleared and + * where exactly the CCB fetches data + * + * The card uses a 22.5792 MHz crystal. + * The LINEIN jack may be converted to an AOUT jack by + * setting pin 47 (XCTL0) of the ES1370 to high. + * Pin 48 (XCTL1) of the ES1370 presumably changes the input impedance of the + * MIC jack. + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < 131421 +#include +#endif +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ENSONIQ +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#endif +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1370 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 +#endif + +#define ES1370_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1370) + +#define ES1370_EXTENT 0x40 +#define JOY_EXTENT 8 + +#define ES1370_REG_CONTROL 0x00 +#define ES1370_REG_STATUS 0x04 +#define ES1370_REG_UART_DATA 0x08 +#define ES1370_REG_UART_STATUS 0x09 +#define ES1370_REG_UART_CONTROL 0x09 +#define ES1370_REG_UART_TEST 0x0a +#define ES1370_REG_MEMPAGE 0x0c +#define ES1370_REG_CODEC 0x10 +#define ES1370_REG_SERIAL_CONTROL 0x20 +#define ES1370_REG_DAC1_SCOUNT 0x24 +#define ES1370_REG_DAC2_SCOUNT 0x28 +#define ES1370_REG_ADC_SCOUNT 0x2c + +#define ES1370_REG_DAC1_FRAMEADR 0xc30 +#define ES1370_REG_DAC1_FRAMECNT 0xc34 +#define ES1370_REG_DAC2_FRAMEADR 0xc38 +#define ES1370_REG_DAC2_FRAMECNT 0xc3c +#define ES1370_REG_ADC_FRAMEADR 0xd30 +#define ES1370_REG_ADC_FRAMECNT 0xd34 + +#define ES1370_FMT_U8_MONO 0 +#define ES1370_FMT_U8_STEREO 1 +#define ES1370_FMT_S16_MONO 2 +#define ES1370_FMT_S16_STEREO 3 +#define ES1370_FMT_STEREO 1 +#define ES1370_FMT_S16 2 +#define ES1370_FMT_MASK 3 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; + +#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2) +#define DAC2_DIVTOSR(x) (1411200/((x)+2)) + +#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ +#define CTRL_XCTL1 0x40000000 /* ? mic impedance */ +#define CTRL_OPEN 0x20000000 /* no function, can be read and written */ +#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ +#define CTRL_SH_PCLKDIV 16 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ +#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */ +#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */ +#define CTRL_SH_WTSRSEL 12 +#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */ +#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */ +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */ +#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */ +#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */ + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */ +#define STAT_CBUSY 0x00000200 /* 1 = codec busy */ +#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */ +#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ +#define STAT_SH_VC 5 +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define USTAT_RXINT 0x80 /* UART rx int pending */ +#define USTAT_TXINT 0x04 /* UART tx int pending */ +#define USTAT_TXRDY 0x02 /* UART tx ready */ +#define USTAT_RXRDY 0x01 /* UART rx ready */ + +#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ +#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ +#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ +#define UCTRL_CNTRL 0x03 /* control field */ +#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ + +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + +/* misc stuff */ + +#define FMODE_DAC 4 /* slight misuse of mode_t */ + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define SND_DEV_DSP16 5 + +/* --------------------------------------------------------------------- */ + +/* Linux 2.0 compatibility stuff */ + +#ifndef SNDCTL_DSP_GETODELAY +#define SNDCTL_DSP_GETODELAY _IOR ('P', 23, int) +#endif + +#if LINUX_VERSION_CODE < 131328 + +#define __init +#define __initdata +#define __initfunc(x) x + +typedef unsigned long mm_segment_t; + +#include +#include + +#undef put_user +#undef get_user + +#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) +#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) + +#define put_user_ret(x,ptr,ret) ({ if (put_user(x,ptr)) return ret; }) +#define get_user_ret(x,ptr,ret) ({ if (get_user(x,ptr)) return ret; }) + +extern inline int copy_to_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_WRITE, to, n); + if (i) + return i; + memcpy_tofs(to, from, n); + return 0; +} + +extern inline int copy_from_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_READ, from, n); + if (i) + return i; + memcpy_fromfs(to, from, n); + return 0; +} + +#define access_ok(x,y,z) (!verify_area(x,y,z)) + +typedef struct { } spinlock_t; +#define SPIN_LOCK_UNLOCKED { } + +#define spin_lock_init(lock) do { } while(0) +#define spin_lock(lock) do { } while(0) +#define spin_trylock(lock) do { } while(0) +#define spin_unlock_wait(lock) do { } while(0) +#define spin_unlock(lock) do { } while(0) +#define spin_lock_irq(lock) cli() +#define spin_unlock_irq(lock) sti() + +#define spin_lock_irqsave(lock, flags) \ + do { save_flags(flags); cli(); } while (0) +#define spin_unlock_irqrestore(lock, flags) \ + restore_flags(flags) + +#define signal_pending(x) ((x)->signal & ~(x)->blocked) + +#define synchronize_irq() + +#else + +#include +#include +#include +#include +#include + +#endif + +#ifndef OSS_GETVERSION +#define OSS_GETVERSION _IOR ('M', 118, int) +#endif + +/* --------------------------------------------------------------------- */ + +struct es1370_state { + /* magic */ + unsigned int magic; + + /* we keep sb cards in a linked list */ + struct es1370_state *next; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_dac; + int dev_midi; + + /* hardware resources */ + unsigned int io, irq; + + /* mixer registers; there is no HW readback */ + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + /* wave stuff */ + unsigned ctrl; + unsigned sctrl; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + struct wait_queue *open_wait; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + struct wait_queue *wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac1, dma_dac2, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + struct wait_queue *iwait; + struct wait_queue *owait; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; +}; + +/* --------------------------------------------------------------------- */ + +struct es1370_state *devs = NULL; + +/* --------------------------------------------------------------------- */ + +extern inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static void wrcodec(struct es1370_state *s, unsigned char idx, unsigned char data) +{ + unsigned long tmo = jiffies + HZ/10; + + do { + if (!(inl(s->io+ES1370_REG_STATUS) & STAT_CSTAT)) { + outw((((unsigned short)idx)<<8)|data, s->io+ES1370_REG_CODEC); + return; + } + schedule(); + } while ((signed)(tmo-jiffies) > 0); + printk(KERN_ERR "es1370: write to codec register timeout\n"); +} + +/* --------------------------------------------------------------------- */ + +extern inline void stop_adc(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac1(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac2(struct es1370_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) + && s->dma_dac1.ready) { + s->ctrl |= CTRL_DAC1_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac1.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac2(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) + && s->dma_dac2.ready) { + s->ctrl |= CTRL_DAC2_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | + SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | + (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | + (0 << SCTRL_SH_P2STINC); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac2.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct es1370_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize) + && s->dma_adc.ready) { + s->ctrl |= CTRL_ADC_EN; + s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_adc.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER 8 +#define DMABUF_MINORDER 1 + + +extern inline void dealloc_dmabuf(struct dmabuf *db) +{ + unsigned long map, mapend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + free_pages((unsigned long)db->rawbuf, db->buforder); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct es1370_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + unsigned long map, mapend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; +#if LINUX_VERSION_CODE < 131328 + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) + db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order, 0); +#else + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) + db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order); +#endif + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &mem_map[map].flags); + } + fmt &= ES1370_FMT_MASK; + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & ES1370_FMT_S16) ? 0 : 0x80, db->dmasize); + outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + outl(virt_to_bus(db->rawbuf), s->io+(reg & 0xff)); + outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); + db->ready = 1; + return 0; +} + +extern inline int prog_dmabuf_adc(struct es1370_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + (s->sctrl >> SCTRL_SH_R1FMT) & ES1370_FMT_MASK, ES1370_REG_ADC_FRAMEADR); +} + +extern inline int prog_dmabuf_dac2(struct es1370_state *s) +{ + stop_dac2(s); + return prog_dmabuf(s, &s->dma_dac2, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + (s->sctrl >> SCTRL_SH_P2FMT) & ES1370_FMT_MASK, ES1370_REG_DAC2_FRAMEADR); +} + +extern inline int prog_dmabuf_dac1(struct es1370_state *s) +{ + stop_dac1(s); + return prog_dmabuf(s, &s->dma_dac1, dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], + (s->sctrl >> SCTRL_SH_P1FMT) & ES1370_FMT_MASK, ES1370_REG_DAC1_FRAMEADR); +} + +extern inline unsigned get_hwptr(struct es1370_state *s, struct dmabuf *db, unsigned reg) +{ + unsigned hwptr, diff; + + outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; + diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; + db->hwptr = hwptr; + return diff; +} + +extern inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ +static void es1370_update_ptr(struct es1370_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->ctrl & CTRL_ADC_EN) { + diff = get_hwptr(s, &s->dma_adc, ES1370_REG_ADC_FRAMECNT); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) { + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + /* update DAC1 pointer */ + if (s->ctrl & CTRL_DAC1_EN) { + diff = get_hwptr(s, &s->dma_dac1, ES1370_REG_DAC1_FRAMECNT); + s->dma_dac1.total_bytes += diff; + if (s->dma_dac1.mapped) { + s->dma_dac1.count += diff; + if (s->dma_dac1.count >= s->dma_dac1.fragsize) + wake_up(&s->dma_dac1.wait); + } else { + s->dma_dac1.count -= diff; + if (s->dma_dac1.count <= 0) { + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_dac1.error++; + } else if (s->dma_dac1.count <= s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { + clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, + s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); + s->dma_dac1.endcleared = 1; + } + if (s->dma_dac1.count < s->dma_dac1.dmasize) + wake_up(&s->dma_dac1.wait); + } + } + /* update DAC2 pointer */ + if (s->ctrl & CTRL_DAC2_EN) { + diff = get_hwptr(s, &s->dma_dac2, ES1370_REG_DAC2_FRAMECNT); + s->dma_dac2.total_bytes += diff; + if (s->dma_dac2.mapped) { + s->dma_dac2.count += diff; + if (s->dma_dac2.count >= s->dma_dac2.fragsize) + wake_up(&s->dma_dac2.wait); + } else { + s->dma_dac2.count -= diff; + if (s->dma_dac2.count <= 0) { + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + s->dma_dac2.error++; + } else if (s->dma_dac2.count <= s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { + clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, + s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); + s->dma_dac2.endcleared = 1; + } + if (s->dma_dac2.count < s->dma_dac2.dmasize) + wake_up(&s->dma_dac2.wait); + } + } +} + +/* hold spinlock for the following! */ +static void es1370_handle_midi(struct es1370_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->ctrl & CTRL_UART_EN)) + return; + wake = 0; + while (inb(s->io+ES1370_REG_UART_STATUS) & USTAT_RXRDY) { + ch = inb(s->io+ES1370_REG_UART_DATA); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while ((inb(s->io+ES1370_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->io+ES1370_REG_UART_DATA); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); + outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1370_REG_UART_CONTROL); +} + +static void es1370_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct es1370_state *s = (struct es1370_state *)dev_id; + unsigned int intsrc, sctl; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->io+ES1370_REG_STATUS); + if (!(intsrc & 0x80000000)) + return; + spin_lock(&s->lock); + /* clear audio interrupts first */ + sctl = s->sctrl; + if (intsrc & STAT_ADC) + sctl &= ~SCTRL_R1INTEN; + if (intsrc & STAT_DAC1) + sctl &= ~SCTRL_P1INTEN; + if (intsrc & STAT_DAC2) + sctl &= ~SCTRL_P2INTEN; + outl(sctl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + es1370_update_ptr(s); + es1370_handle_midi(s); + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "es1370: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != ES1370_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +static const struct { + unsigned volidx:4; + unsigned left:4; + unsigned right:4; + unsigned stereo:1; + unsigned recmask:13; + unsigned avail:1; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 }, /* master */ + [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 }, /* voice */ + [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 }, /* FM */ + [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 }, /* CD */ + [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 }, /* Line */ + [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 }, /* AUX */ + [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 1, 0x0100, 1 }, /* Mono1 */ + [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 1, 0x0200, 1 }, /* Mono2 */ + [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 1, 0x0001, 1 }, /* Mic */ + [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 1, 0x0000, 1 } /* mono out */ +}; + +static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg) +{ + int i, val, j; + unsigned char l, r, rl, rr; + + VALIDATE_STATE(s); + +#if LINUX_VERSION_CODE < 131328 + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } +#else + if (cmd == SOUND_MIXER_PRIVATE1) { + get_user_ret(val, (int *)arg, -EFAULT); + if (val != -1) { + s->mix.micpreamp = !!val; + wrcodec(s, 0x19, s->mix.micpreamp); + } + return put_user(s->mix.micpreamp, (int *)arg); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "ES1370", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1370", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } +#endif + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(s->mix.recsrc, (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].avail) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].recmask) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].stereo) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(0, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg); + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + get_user_ret(val, (int *)arg, -EFAULT); + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].recmask) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].recmask; + } + s->mix.recsrc = val; + wrcodec(s, 0x12, j & 0xd5); + wrcodec(s, 0x13, j & 0xaa); + wrcodec(s, 0x14, (j >> 8) & 0x17); + wrcodec(s, 0x15, (j >> 8) & 0x0f); + i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc30; + wrcodec(s, 0x10, i); + wrcodec(s, 0x11, i >> 8); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + l = val & 0xff; + if (l > 100) + l = 100; + if (mixtable[i].stereo) { + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (l < 10) { + rl = 0x80; + l = 0; + } else { + rl = 15 - ((l - 10) / 6); + l = (15 - rl) * 6 + 10; + } + if (r < 10) { + rr = 0x80; + r = 0; + } else { + rr = 15 - ((r - 10) / 6); + r = (15 - rr) * 6 + 10; + } + wrcodec(s, mixtable[i].right, rr); + } else { + if (mixtable[i].left == 15) { + if (l < 2) { + rr = rl = 0x80; + r = l = 0; + } else { + rl = 7 - ((l - 2) / 14); + r = l = (7 - rl) * 14 + 2; + } + } else { + if (l < 10) { + rl = 0x80; + r = l = 0; + } else { + rl = 15 - ((l - 10) / 6); + r = l = (15 - rl) * 6 + 10; + } + } + } + wrcodec(s, mixtable[i].left, rl); + s->mix.vol[mixtable[i].volidx] = ((unsigned int)r << 8) | l; + return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int es1370_llseek(struct inode *ino, struct file *file, off_t offset, int origin) +{ + return -ESPIPE; +} +#else +static loff_t es1370_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} +#endif + +/* --------------------------------------------------------------------- */ + +static int es1370_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1370_state *s = devs; + + while (s && s->dev_mixer != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + MOD_INC_USE_COUNT; + return 0; +} + +static int es1370_release_mixdev(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + +static int es1370_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct es1370_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations es1370_mixer_fops = { + &es1370_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &es1370_ioctl_mixdev, + NULL, /* mmap */ + &es1370_open_mixdev, + &es1370_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac1(struct es1370_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac1.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac1.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac1.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; + tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + current->timeout = jiffies + (tmo ? tmo : 1); + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1370: dma timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac2(struct es1370_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac2.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac2.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac2.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + current->timeout = jiffies + (tmo ? tmo : 1); + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1370: dma timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->dma_dac2.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int es1370_read(struct inode *ino, struct file *file, char *buffer, int count) +#else +static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 +static int es1370_write(struct inode *ino, struct file *file, const char *buffer, int count) +#else +static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (s->dma_dac2.mapped) + return -ENXIO; + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac2.count < 0) { + s->dma_dac2.count = 0; + s->dma_dac2.swptr = s->dma_dac2.hwptr; + } + swptr = s->dma_dac2.swptr; + cnt = s->dma_dac2.dmasize-swptr; + if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) + cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac2(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_dac2.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac2.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac2.swptr = swptr; + s->dma_dac2.count += cnt; + s->dma_dac2.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac2(s); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 + +/* well select really */ +static int es1370_poll(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + int ret = 0; + + VALIDATE_STATE(s); + if (sel_type == SEL_IN && file->f_mode & FMODE_READ) { + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + ret = 1; + } else { + if (s->dma_adc.count > 0) + ret = 1; + } + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->dma_adc.wait, wait); + } + if (sel_type == SEL_OUT && file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (s->dma_dac2.mapped) { + if (s->dma_dac2.count >= s->dma_dac2.fragsize) + ret = 1; + } else { + if (s->dma_dac2.dmasize > s->dma_dac2.count) + ret = 1; + } + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->dma_dac2.wait, wait); + } + return 0; +} + +#else + +static unsigned int es1370_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac2.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac2.mapped) { + if (s->dma_dac2.count >= s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if (s->dma_dac2.dmasize > s->dma_dac2.count) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +#endif + +#if LINUX_VERSION_CODE < 131328 +static int es1370_mmap(struct inode *ino, struct file *file, struct vm_area_struct *vma) +#else +static int es1370_mmap(struct file *file, struct vm_area_struct *vma) +#endif +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac2(s)) != 0) + return ret; + db = &s->dma_dac2; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; +#if LINUX_VERSION_CODE < 131328 + vma->vm_inode = ino; + ino->i_count++; +#else + vma->vm_file = file; + file->f_count++; +#endif + return 0; +} + +static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(); + s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (s->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) + return -EINVAL; + if (val < 4000) + val = 4000; + if (val > 50000) + val = 50000; + stop_adc(s); + stop_dac2(s); + s->dma_adc.ready = s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_R1SEB; + else + s->sctrl &= ~SCTRL_R1SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P2SEB; + else + s->sctrl &= ~SCTRL_P2SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + start_dac2(s); + } else + stop_dac2(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_dac2.fragsize; + abinfo.bytes = s->dma_dac2.dmasize - s->dma_dac2.count; + abinfo.fragstotal = s->dma_dac2.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(s->ctrl & CTRL_ADC_EN) && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + val = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_dac2.total_bytes; + cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift; + cinfo.ptr = s->dma_dac2.hwptr; + if (s->dma_dac2.mapped) + s->dma_dac2.count &= s->dma_dac2.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac2(s))) + return val; + return put_user(s->dma_dac2.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = val & 0xffff; + s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac2.ossfragshift < 4) + s->dma_dac2.ossfragshift = 4; + if (s->dma_dac2.ossfragshift > 15) + s->dma_dac2.ossfragshift = 15; + if (s->dma_dac2.ossmaxfrags < 4) + s->dma_dac2.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1370_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1370_state *s = devs; + unsigned long flags; + + while (s && ((s->dev_audio ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_READ|FMODE_WRITE))) + s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->sctrl &= ~SCTRL_R1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_R1FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_R1FMT; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; + s->sctrl &= ~SCTRL_P2FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P2FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P2FMT; + } + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1370_release(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac2(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + dealloc_dmabuf(&s->dma_dac2); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(&s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1370_audio_fops = { + &es1370_llseek, + &es1370_read, + &es1370_write, + NULL, /* readdir */ + &es1370_poll, + &es1370_ioctl, + &es1370_mmap, + &es1370_open, + &es1370_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int es1370_write_dac(struct inode *ino, struct file *file, const char *buffer, int count) +#else +static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (s->dma_dac1.mapped) + return -ENXIO; + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac1.count < 0) { + s->dma_dac1.count = 0; + s->dma_dac1.swptr = s->dma_dac1.hwptr; + } + swptr = s->dma_dac1.swptr; + cnt = s->dma_dac1.dmasize-swptr; + if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) + cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac1(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_dac1.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac1.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac1.swptr = swptr; + s->dma_dac1.count += cnt; + s->dma_dac1.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac1(s); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 + +/* well select really */ +static int es1370_poll_dac(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + int ret = 0; + + VALIDATE_STATE(s); + if (sel_type == SEL_OUT) { + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= s->dma_dac1.fragsize) + ret = 1; + } else { + if (s->dma_dac1.dmasize > s->dma_dac1.count) + ret = 1; + } + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->dma_dac1.wait, wait); + } + return 0; +} + +#else + +static unsigned int es1370_poll_dac(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + poll_wait(file, &s->dma_dac1.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if (s->dma_dac1.dmasize > s->dma_dac1.count) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} +#endif + +#if LINUX_VERSION_CODE < 131328 +static int es1370_mmap_dac(struct inode *ino, struct file *file, struct vm_area_struct *vma) +#else +static int es1370_mmap_dac(struct file *file, struct vm_area_struct *vma) +#endif +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (!(vma->vm_flags & VM_WRITE)) + return -EINVAL; + if ((ret = prog_dmabuf_dac1(s)) != 0) + return ret; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << s->dma_dac1.buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + s->dma_dac1.mapped = 1; +#if LINUX_VERSION_CODE < 131328 + vma->vm_inode = ino; + ino->i_count++; +#else + vma->vm_file = file; + file->f_count++; +#endif + return 0; +} + +static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + unsigned ctrl; + int val, ret; + + VALIDATE_STATE(s); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + stop_dac1(s); + synchronize_irq(); + s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + for (ctrl = 0; ctrl <= 2; ctrl++) + if (val < (dac1_samplerate[ctrl] + dac1_samplerate[ctrl+1]) / 2) + break; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (ctrl << CTRL_SH_WTSRSEL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + if (s->dma_dac1.mapped) + return -EINVAL; + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P1SEB; + else + s->sctrl &= ~SCTRL_P1SEB; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + start_dac1(s); + } else + stop_dac1(s); + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + abinfo.fragsize = s->dma_dac1.fragsize; + abinfo.bytes = s->dma_dac1.dmasize - s->dma_dac1.count; + abinfo.fragstotal = s->dma_dac1.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + val = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1370_update_ptr(s); + cinfo.bytes = s->dma_dac1.total_bytes; + cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift; + cinfo.ptr = s->dma_dac1.hwptr; + if (s->dma_dac1.mapped) + s->dma_dac1.count &= s->dma_dac1.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if ((val = prog_dmabuf_dac1(s))) + return val; + return put_user(s->dma_dac1.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + s->dma_dac1.ossfragshift = val & 0xffff; + s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac1.ossfragshift < 4) + s->dma_dac1.ossfragshift = 4; + if (s->dma_dac1.ossfragshift > 15) + s->dma_dac1.ossfragshift = 15; + if (s->dma_dac1.ossmaxfrags < 4) + s->dma_dac1.ossmaxfrags = 4; + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (s->dma_dac1.subdivision) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + s->dma_dac1.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1370_open_dac(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1370_state *s = devs; + unsigned long flags; + + while (s && ((s->dev_dac ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + /* we allow opening with O_RDWR, most programs do it although they will only write */ +#if 0 + if (file->f_mode & FMODE_READ) + return -EPERM; +#endif + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DAC) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; + spin_lock_irqsave(&s->lock, flags); + s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (1 << CTRL_SH_WTSRSEL); + s->sctrl &= ~SCTRL_P1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P1FMT; + else + s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P1FMT; + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= FMODE_DAC; + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1370_release_dac(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + + VALIDATE_STATE(s); + drain_dac1(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + stop_dac1(s); + dealloc_dmabuf(&s->dma_dac1); + s->open_mode &= ~FMODE_DAC; + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1370_dac_fops = { + &es1370_llseek, + NULL, /* read */ + &es1370_write_dac, + NULL, /* readdir */ + &es1370_poll_dac, + &es1370_ioctl_dac, + &es1370_mmap_dac, + &es1370_open_dac, + &es1370_release_dac, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int es1370_midi_read(struct inode *ino, struct file *file, char *buffer, int count) +#else +static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 +static int es1370_midi_write(struct inode *ino, struct file *file, const char *buffer, int count) +#else +static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 + +/* well select really */ +static int es1370_midi_poll(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + int ret = 0; + + VALIDATE_STATE(s); + if (sel_type == SEL_IN && file->f_mode & FMODE_READ) { + spin_lock_irqsave(&s->lock, flags); + if (s->midi.icnt > 0) + ret = 1; + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->midi.iwait, wait); + } + if (sel_type == SEL_OUT && file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&s->lock, flags); + if (s->midi.ocnt < MIDIOUTBUF) + ret = 1; + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->midi.owait, wait); + } + return 0; +} + +#else + +static unsigned int es1370_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +#endif + +static int es1370_midi_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1370_state *s = devs; + unsigned long flags; + + while (s && s->dev_midi != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(UCTRL_CNTRL_SWR, s->io+ES1370_REG_UART_CONTROL); + outb(0, s->io+ES1370_REG_UART_CONTROL); + outb(0, s->io+ES1370_REG_UART_TEST); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + s->ctrl |= CTRL_UART_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + es1370_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1370_midi_release(struct inode *inode, struct file *file) +{ + struct es1370_state *s = (struct es1370_state *)file->private_data; + struct wait_queue wait = { current, NULL }; + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + current->timeout = tmo ? jiffies + tmo : 0; + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1370: midi timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->ctrl &= ~CTRL_UART_EN; + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1370_midi_fops = { + &es1370_llseek, + &es1370_midi_read, + &es1370_midi_write, + NULL, /* readdir */ + &es1370_midi_poll, + NULL, /* ioctl */ + NULL, /* mmap */ + &es1370_midi_open, + &es1370_midi_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices */ +#define NR_DEVICE 5 + +static int joystick[NR_DEVICE] = { 0, }; +static int lineout[NR_DEVICE] = { 0, }; +static int micz[NR_DEVICE] = { 0, }; + +/* --------------------------------------------------------------------- */ + +static const struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_LINE3, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_OGAIN, 0x4040 } +}; + +#if LINUX_VERSION_CODE < 131421 + +#ifdef MODULE +__initfunc(int init_module(void)) +#else +__initfunc(int init_es1370(void)) +#endif +{ + struct es1370_state *s; + unsigned char bus, dev_fn, irq; + unsigned short index, vendid, devid; + unsigned int ioaddr; + mm_segment_t fs; + int i, val; + + if (!pcibios_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "es1370: version v0.11 time " __TIME__ " " __DATE__ "\n"); + for (index = 0; index < NR_DEVICE; index++) { + if (pcibios_find_class((PCI_CLASS_MULTIMEDIA_AUDIO << 8), index, &bus, &dev_fn) != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_word(bus, dev_fn, PCI_VENDOR_ID, &vendid); + pcibios_read_config_word(bus, dev_fn, PCI_DEVICE_ID, &devid); + if (vendid != PCI_VENDOR_ID_ENSONIQ || devid != PCI_DEVICE_ID_ENSONIQ_ES1370) + continue; + pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_0, &ioaddr); + if (ioaddr == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + pcibios_read_config_byte(bus, dev_fn, PCI_INTERRUPT_LINE, &irq); + if (irq == 0 || irq == 0xff) + continue; + if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) { + printk(KERN_WARNING "es1370: out of memory\n"); + continue; + } + memset(s, 0, sizeof(struct es1370_state)); + init_waitqueue(&s->dma_adc.wait); + init_waitqueue(&s->dma_dac1.wait); + init_waitqueue(&s->dma_dac2.wait); + init_waitqueue(&s->open_wait); + init_waitqueue(&s->midi.iwait); + init_waitqueue(&s->midi.owait); + s->open_sem = MUTEX; + s->magic = ES1370_MAGIC; + s->io = ioaddr & PCI_BASE_ADDRESS_IO_MASK; + s->irq = irq; + if (check_region(s->io, ES1370_EXTENT)) { + printk(KERN_ERR "es1370: io ports %#x-%#x in use\n", s->io, s->io+ES1370_EXTENT-1); + goto err_region; + } + request_region(s->io, ES1370_EXTENT, "es1370"); + if (request_irq(s->irq, es1370_interrupt, SA_INTERRUPT|SA_SHIRQ, "es1370", s)) { + printk(KERN_ERR "es1370: irq %u in use\n", s->irq); + goto err_irq; + } + /* initialize codec registers */ + s->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV); + if (joystick[index]) { + if (check_region(0x200, JOY_EXTENT)) + printk(KERN_ERR "es1370: io port 0x200 in use\n"); + else + s->ctrl |= CTRL_JYSTK_EN; + } + if (lineout[index]) + s->ctrl |= CTRL_XCTL0; + if (micz[index]) + s->ctrl |= CTRL_XCTL1; + s->sctrl = 0; + printk(KERN_INFO "es1370: found adapter at io %#06x irq %u\n" + KERN_INFO "es1370: features: joystick %s, line %s, mic impedance %s\n", + s->io, s->irq, (s->ctrl & CTRL_JYSTK_EN) ? "on" : "off", + (s->ctrl & CTRL_XCTL0) ? "out" : "in", + (s->ctrl & CTRL_XCTL1) ? "1" : "0"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops)) < 0) + goto err_dev2; + if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops)) < 0) + goto err_dev3; + if ((s->dev_midi = register_sound_midi(&es1370_midi_fops)) < 0) + goto err_dev4; + if (s->ctrl & CTRL_JYSTK_EN) + request_region(0x200, JOY_EXTENT, "es1370"); + /* initialize the chips */ + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + wrcodec(s, 0x16, 3); /* no RST, PD */ + wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ + wrcodec(s, 0x18, 0); /* recording source is mixer */ + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* queue it for later freeing */ + s->next = devs; + devs = s; + continue; + + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "es1370: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, ES1370_EXTENT); + err_region: + kfree_s(s, sizeof(struct es1370_state)); + } + if (!devs) + return -ENODEV; + return 0; +} + +#else /* Linux Version >= 2.1.93 */ + +#ifdef MODULE +__initfunc(int init_module(void)) +#else +__initfunc(int init_es1370(void)) +#endif +{ + struct es1370_state *s; + struct pci_dev *pcidev = NULL; + mm_segment_t fs; + int i, val, index = 0; + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "es1370: version v0.11 time " __TIME__ " " __DATE__ "\n"); + while (index < NR_DEVICE && + (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) { + if (pcidev->base_address[0] == 0 || + (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->irq == 0) + continue; + if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) { + printk(KERN_WARNING "es1370: out of memory\n"); + continue; + } + memset(s, 0, sizeof(struct es1370_state)); + init_waitqueue(&s->dma_adc.wait); + init_waitqueue(&s->dma_dac1.wait); + init_waitqueue(&s->dma_dac2.wait); + init_waitqueue(&s->open_wait); + init_waitqueue(&s->midi.iwait); + init_waitqueue(&s->midi.owait); + s->open_sem = MUTEX; + s->magic = ES1370_MAGIC; + s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + s->irq = pcidev->irq; + if (check_region(s->io, ES1370_EXTENT)) { + printk(KERN_ERR "es1370: io ports %#x-%#x in use\n", s->io, s->io+ES1370_EXTENT-1); + goto err_region; + } + request_region(s->io, ES1370_EXTENT, "es1370"); + if (request_irq(s->irq, es1370_interrupt, SA_INTERRUPT|SA_SHIRQ, "es1370", s)) { + printk(KERN_ERR "es1370: irq %u in use\n", s->irq); + goto err_irq; + } + /* initialize codec registers */ + s->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV); + if (joystick[index]) { + if (check_region(0x200, JOY_EXTENT)) + printk(KERN_ERR "es1370: io port 0x200 in use\n"); + else + s->ctrl |= CTRL_JYSTK_EN; + } + if (lineout[index]) + s->ctrl |= CTRL_XCTL0; + if (micz[index]) + s->ctrl |= CTRL_XCTL1; + s->sctrl = 0; + printk(KERN_INFO "es1370: found adapter at io %#06x irq %u\n" + KERN_INFO "es1370: features: joystick %s, line %s, mic impedance %s\n", + s->io, s->irq, (s->ctrl & CTRL_JYSTK_EN) ? "on" : "off", + (s->ctrl & CTRL_XCTL0) ? "out" : "in", + (s->ctrl & CTRL_XCTL1) ? "1" : "0"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops)) < 0) + goto err_dev2; + if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops)) < 0) + goto err_dev3; + if ((s->dev_midi = register_sound_midi(&es1370_midi_fops)) < 0) + goto err_dev4; + if (s->ctrl & CTRL_JYSTK_EN) + request_region(0x200, JOY_EXTENT, "es1370"); + /* initialize the chips */ + outl(s->ctrl, s->io+ES1370_REG_CONTROL); + outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + wrcodec(s, 0x16, 3); /* no RST, PD */ + wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ + wrcodec(s, 0x18, 0); /* recording source is mixer */ + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* queue it for later freeing */ + s->next = devs; + devs = s; + index++; + continue; + + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "es1370: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, ES1370_EXTENT); + err_region: + kfree_s(s, sizeof(struct es1370_state)); + } + if (!devs) + return -ENODEV; + return 0; +} + +#endif + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +#if LINUX_VERSION_CODE >= 131328 +MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)"); +MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); +MODULE_PARM(micz, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(micz, "changes (??) the microphone impedance"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1370 AudioPCI Driver"); +#endif + +void cleanup_module(void) +{ + struct es1370_state *s; + + while ((s = devs)) { + devs = devs->next; + outl(CTRL_SERR_DIS, s->io+ES1370_REG_CONTROL); /* switch everything off */ + outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */ + synchronize_irq(); + free_irq(s->irq, s); + release_region(s->io, ES1370_EXTENT); + if (s->ctrl & CTRL_JYSTK_EN) + release_region(0x200, JOY_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_dsp(s->dev_dac); + unregister_sound_midi(s->dev_midi); + kfree_s(s, sizeof(struct es1370_state)); + } + printk(KERN_INFO "es1370: unloading\n"); +} + +#endif /* MODULE */ diff --git a/drivers/sound/es1371.c b/drivers/sound/es1371.c new file mode 100644 index 000000000000..c7fdf83f85a9 --- /dev/null +++ b/drivers/sound/es1371.c @@ -0,0 +1,3302 @@ +/*****************************************************************************/ + +/* + * es1371.c -- Creative Ensoniq ES1371. + * + * Copyright (C) 1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * 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. + * + * Special thanks to Ensoniq + * + * + * Module command line parameters: + * joystick if 1 enables the joystick interface on the card; but it still + * needs a separate joystick driver (presumably PC standard, although + * the chip doc doesn't say anything and it looks slightly fishy from + * the PCI standpoint...) + * + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/dsp1 additional DAC, like /dev/dsp, but outputs to mixer "SYNTH" setting + * /dev/midi simple MIDI UART interface, no ioctl + * + * NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed + * to be done in software. That is what /dev/dac is for. By now (Q2 1998) + * there are several MIDI to PCM (WAV) packages, one of them is timidity. + * + * Revision history + * 04.06.98 0.1 Initial release + * Mixer stuff should be overhauled; especially optional AC97 mixer bits + * should be detected. This results in strange behaviour of some mixer + * settings, like master volume and mic. + * 08.06.98 0.2 First release using Alan Cox' soundcore instead of miscdevice + * + * + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < 131421 +#include +#endif +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_ENSONIQ +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#endif +#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 +#endif + +#define ES1371_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371) + +#define ES1371_EXTENT 0x40 +#define JOY_EXTENT 8 + +#define ES1371_REG_CONTROL 0x00 +#define ES1371_REG_STATUS 0x04 +#define ES1371_REG_UART_DATA 0x08 +#define ES1371_REG_UART_STATUS 0x09 +#define ES1371_REG_UART_CONTROL 0x09 +#define ES1371_REG_UART_TEST 0x0a +#define ES1371_REG_MEMPAGE 0x0c +#define ES1371_REG_SRCONV 0x10 +#define ES1371_REG_CODEC 0x14 +#define ES1371_REG_LEGACY 0x18 +#define ES1371_REG_SERIAL_CONTROL 0x20 +#define ES1371_REG_DAC1_SCOUNT 0x24 +#define ES1371_REG_DAC2_SCOUNT 0x28 +#define ES1371_REG_ADC_SCOUNT 0x2c + +#define ES1371_REG_DAC1_FRAMEADR 0xc30 +#define ES1371_REG_DAC1_FRAMECNT 0xc34 +#define ES1371_REG_DAC2_FRAMEADR 0xc38 +#define ES1371_REG_DAC2_FRAMECNT 0xc3c +#define ES1371_REG_ADC_FRAMEADR 0xd30 +#define ES1371_REG_ADC_FRAMECNT 0xd34 + +#define ES1371_FMT_U8_MONO 0 +#define ES1371_FMT_U8_STEREO 1 +#define ES1371_FMT_S16_MONO 2 +#define ES1371_FMT_S16_STEREO 3 +#define ES1371_FMT_STEREO 1 +#define ES1371_FMT_S16 2 +#define ES1371_FMT_MASK 3 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define CTRL_JOY_SHIFT 24 +#define CTRL_JOY_MASK 3 +#define CTRL_JOY_200 0x00000000 /* joystick base address */ +#define CTRL_JOY_208 0x01000000 +#define CTRL_JOY_210 0x02000000 +#define CTRL_JOY_218 0x03000000 +#define CTRL_GPIO_IN0 0x00100000 /* general purpose inputs/outputs */ +#define CTRL_GPIO_IN1 0x00200000 +#define CTRL_GPIO_IN2 0x00400000 +#define CTRL_GPIO_IN3 0x00800000 +#define CTRL_GPIO_OUT0 0x00010000 +#define CTRL_GPIO_OUT1 0x00020000 +#define CTRL_GPIO_OUT2 0x00040000 +#define CTRL_GPIO_OUT3 0x00080000 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ +#define CTRL_SYNCRES 0x00004000 /* AC97 warm reset */ +#define CTRL_ADCSTOP 0x00002000 /* stop ADC transfers */ +#define CTRL_PWR_INTRM 0x00001000 /* 1 = power level ints enabled */ +#define CTRL_M_CB 0x00000800 /* recording source: 0 = ADC, 1 = MPEG */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_PDLEV0 0x00000000 /* power down level */ +#define CTRL_PDLEV1 0x00000100 +#define CTRL_PDLEV2 0x00000200 +#define CTRL_PDLEV3 0x00000300 +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port */ +#define CTRL_XTALCLKDIS 0x00000002 /* 1 = disable crystal clock input */ +#define CTRL_PCICLKDIS 0x00000001 /* 1 = disable PCI clock distribution */ + + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define STAT_SYNC_ERR 0x00000100 /* 1 = codec sync error */ +#define STAT_VC 0x000000c0 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ +#define STAT_SH_VC 6 +#define STAT_MPWR 0x00000020 /* power level interrupt */ +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define USTAT_RXINT 0x80 /* UART rx int pending */ +#define USTAT_TXINT 0x04 /* UART tx int pending */ +#define USTAT_TXRDY 0x02 /* UART tx ready */ +#define USTAT_RXRDY 0x01 /* UART rx ready */ + +#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ +#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ +#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ +#define UCTRL_CNTRL 0x03 /* control field */ +#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ + +/* sample rate converter */ +#define SRC_RAMADDR_MASK 0xfe000000 +#define SRC_RAMADDR_SHIFT 25 +#define SRC_WE 0x01000000 /* read/write control for SRC RAM */ +#define SRC_BUSY 0x00800000 /* SRC busy */ +#define SRC_DIS 0x00400000 /* 1 = disable SRC */ +#define SRC_DDAC1 0x00200000 /* 1 = disable accum update for DAC1 */ +#define SRC_DDAC2 0x00100000 /* 1 = disable accum update for DAC2 */ +#define SRC_DADC 0x00080000 /* 1 = disable accum update for ADC2 */ +#define SRC_RAMDATA_MASK 0x0000ffff +#define SRC_RAMDATA_SHIFT 0 + +#define SRCREG_ADC 0x78 +#define SRCREG_DAC1 0x70 +#define SRCREG_DAC2 0x74 +#define SRCREG_VOL_ADC 0x6c +#define SRCREG_VOL_DAC1 0x7c +#define SRCREG_VOL_DAC2 0x7e + +#define SRCREG_TRUNC_N 0x00 +#define SRCREG_INT_REGS 0x01 +#define SRCREG_ACCUM_FRAC 0x02 +#define SRCREG_VFREQ_FRAC 0x03 + +#define CODEC_PIRD 0x00800000 /* 0 = write AC97 register */ +#define CODEC_PIADD_MASK 0x007f0000 +#define CODEC_PIADD_SHIFT 16 +#define CODEC_PIDAT_MASK 0x0000ffff +#define CODEC_PIDAT_SHIFT 0 + +#define CODEC_RDY 0x80000000 /* AC97 read data valid */ +#define CODEC_WIP 0x40000000 /* AC97 write in progress */ +#define CODEC_PORD 0x00800000 /* 0 = write AC97 register */ +#define CODEC_POADD_MASK 0x007f0000 +#define CODEC_POADD_SHIFT 16 +#define CODEC_PODAT_MASK 0x0000ffff +#define CODEC_PODAT_SHIFT 0 + + +#define LEGACY_JFAST 0x80000000 /* fast joystick timing */ +#define LEGACY_FIRQ 0x01000000 /* force IRQ */ + +#define SCTRL_DACTEST 0x00400000 /* 1 = DAC test, test vector generation purposes */ +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + +/* codec constants */ + +#define CODEC_ID_DEDICATEDMIC 0x001 +#define CODEC_ID_MODEMCODEC 0x002 +#define CODEC_ID_BASSTREBLE 0x004 +#define CODEC_ID_SIMULATEDSTEREO 0x008 +#define CODEC_ID_HEADPHONEOUT 0x010 +#define CODEC_ID_LOUDNESS 0x020 +#define CODEC_ID_18BITDAC 0x040 +#define CODEC_ID_20BITDAC 0x080 +#define CODEC_ID_18BITADC 0x100 +#define CODEC_ID_20BITADC 0x200 + +#define CODEC_ID_SESHIFT 10 +#define CODEC_ID_SEMASK 0x1f + + +/* misc stuff */ + +#define FMODE_DAC 4 /* slight misuse of mode_t */ + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define SND_DEV_DSP16 5 + +/* --------------------------------------------------------------------- */ + +/* Linux 2.0 compatibility stuff */ + +#ifndef SNDCTL_DSP_GETODELAY +#define SNDCTL_DSP_GETODELAY _IOR ('P', 23, int) +#endif + +#if LINUX_VERSION_CODE < 131328 + +#define __init +#define __initdata +#define __initfunc(x) x + +typedef unsigned long mm_segment_t; + +#include +#include + +#undef put_user +#undef get_user + +#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) +#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) + +#define put_user_ret(x,ptr,ret) ({ if (put_user(x,ptr)) return ret; }) +#define get_user_ret(x,ptr,ret) ({ if (get_user(x,ptr)) return ret; }) + +extern inline int copy_to_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_WRITE, to, n); + if (i) + return i; + memcpy_tofs(to, from, n); + return 0; +} + +extern inline int copy_from_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_READ, from, n); + if (i) + return i; + memcpy_fromfs(to, from, n); + return 0; +} + +#define access_ok(x,y,z) (!verify_area(x,y,z)) + +typedef struct { } spinlock_t; +#define SPIN_LOCK_UNLOCKED { } + +#define spin_lock_init(lock) do { } while(0) +#define spin_lock(lock) do { } while(0) +#define spin_trylock(lock) do { } while(0) +#define spin_unlock_wait(lock) do { } while(0) +#define spin_unlock(lock) do { } while(0) +#define spin_lock_irq(lock) cli() +#define spin_unlock_irq(lock) sti() + +#define spin_lock_irqsave(lock, flags) \ + do { save_flags(flags); cli(); } while (0) +#define spin_unlock_irqrestore(lock, flags) \ + restore_flags(flags) + +#define signal_pending(x) ((x)->signal & ~(x)->blocked) + +#define synchronize_irq() + +#else + +#include +#include +#include +#include +#include + +#endif + +#ifndef OSS_GETVERSION +#define OSS_GETVERSION _IOR ('M', 118, int) +#endif + +/* --------------------------------------------------------------------- */ + +static const char *stereo_enhancement[] __initdata = +{ + "no 3D stereo enhancement", + "Analog Devices Phat Stereo", + "Creative Stereo Enhancement", + "National Semiconductor 3D Stereo Enhancement", + "YAMAHA Ymersion", + "BBE 3D Stereo Enhancement", + "Crystal Semiconductor 3D Stereo Enhancement", + "Qsound QXpander", + "Spatializer 3D Stereo Enhancement", + "SRS 3D Stereo Enhancement", + "Platform Technologies 3D Stereo Enhancement", + "AKM 3D Audio", + "Aureal Stereo Enhancement", + "AZTECH 3D Enhancement", + "Binaura 3D Audio Enhancement", + "ESS Technology Stereo Enhancement", + "Harman International VMAx", + "NVidea 3D Stereo Enhancement", + "Philips Incredible Sound", + "Texas Instruments 3D Stereo Enhancement", + "VLSI Technology 3D Stereo Enhancement" +}; + +/* --------------------------------------------------------------------- */ + +struct es1371_state { + /* magic */ + unsigned int magic; + + /* we keep sb cards in a linked list */ + struct es1371_state *next; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_dac; + int dev_midi; + + /* hardware resources */ + unsigned int io, irq; + + /* mixer registers; there is no HW readback */ + struct { + unsigned short codec_id; + unsigned int modcnt; + } mix; + + /* wave stuff */ + unsigned ctrl; + unsigned sctrl; + unsigned dac1rate, dac2rate, adcrate; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + struct wait_queue *open_wait; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + struct wait_queue *wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac1, dma_dac2, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + struct wait_queue *iwait; + struct wait_queue *owait; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; +}; + +/* --------------------------------------------------------------------- */ + +struct es1371_state *devs = NULL; + +/* --------------------------------------------------------------------- */ + +extern inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ + +#ifdef hweight32 +#undef hweight32 +#endif + +extern __inline__ unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +/* --------------------------------------------------------------------- */ + +static unsigned wait_src_ready(struct es1371_state *s) +{ + unsigned int t, r; + + for (t = 0; t < 1000; t++) { + if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY)) + return r; + udelay(1); + } + printk(KERN_DEBUG "es1371: sample rate converter timeout r = 0x%08x\n", r); + return r; +} + +static unsigned src_read(struct es1371_state *s, unsigned reg) +{ + unsigned int r; + + r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); + r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; + outl(r, s->io + ES1371_REG_SRCONV); + return (wait_src_ready(s) & SRC_RAMDATA_MASK) >> SRC_RAMDATA_SHIFT; +} + + +static void src_write(struct es1371_state *s, unsigned reg, unsigned data) +{ + unsigned int r; + + r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); + r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; + r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK; + outl(r | SRC_WE, s->io + ES1371_REG_SRCONV); +} + +/* --------------------------------------------------------------------- */ + +/* most of the following here is black magic */ + +static void set_adc_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int n, truncm, freq; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + n = rate / 3000; + if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) + n--; + truncm = (21 * n - 1) | 1; + freq = ((48000UL << 15) / rate) * n; + s->adcrate = (48000UL << 15) / (freq / n); + spin_lock_irqsave(&s->lock, flags); + if (rate >= 24000) { + if (truncm > 239) + truncm = 239; + src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, + (((239 - truncm) >> 1) << 9) | (n << 4)); + } else { + if (truncm > 119) + truncm = 119; + src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, + 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); + } + src_write(s, SRCREG_ADC+SRCREG_INT_REGS, + (src_read(s, SRCREG_ADC+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_ADC+SRCREG_VFREQ_FRAC, freq & 0x7fff); + src_write(s, SRCREG_VOL_ADC, n << 8); + src_write(s, SRCREG_VOL_ADC+1, n << 8); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void set_dac1_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int freq, r; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + freq = (rate << 15) / 3000; + s->dac1rate = (freq * 3000) >> 15; + spin_lock_irqsave(&s->lock, flags); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1; + outl(r, s->io + ES1371_REG_SRCONV); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, + (src_read(s, SRCREG_DAC1+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_DAC1+SRCREG_VFREQ_FRAC, freq & 0x7fff); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)); + outl(r, s->io + ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void set_dac2_rate(struct es1371_state *s, unsigned rate) +{ + unsigned long flags; + unsigned int freq, r; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + freq = (rate << 15) / 3000; + s->dac2rate = (freq * 3000) >> 15; + spin_lock_irqsave(&s->lock, flags); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2; + outl(r, s->io + ES1371_REG_SRCONV); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, + (src_read(s, SRCREG_DAC2+SRCREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + src_write(s, SRCREG_DAC2+SRCREG_VFREQ_FRAC, freq & 0x7fff); + r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)); + outl(r, s->io + ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +static void wrcodec(struct es1371_state *s, unsigned addr, unsigned data) +{ + unsigned long flags; + unsigned t, x; + + for (t = 0; t < 0x1000; t++) + if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) + break; + spin_lock_irqsave(&s->lock, flags); + /* save the current state for later */ + x = inl(s->io+ES1371_REG_SRCONV); + /* enable SRC state data in SRC mux */ + outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, + s->io+ES1371_REG_SRCONV); + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < 0x1000; t++) + if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000) + break; + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | + ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC); + /* restore SRC reg */ + wait_src_ready(s); + outl(x, s->io+ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); +} + +static unsigned rdcodec(struct es1371_state *s, unsigned addr) +{ + unsigned long flags; + unsigned t, x; + + for (t = 0; t < 0x1000; t++) + if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) + break; + spin_lock_irqsave(&s->lock, flags); + /* save the current state for later */ + x = inl(s->io+ES1371_REG_SRCONV); + /* enable SRC state data in SRC mux */ + outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, + s->io+ES1371_REG_SRCONV); + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < 0x1000; t++) + if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000) + break; + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC); + /* restore SRC reg */ + wait_src_ready(s); + outl(x, s->io+ES1371_REG_SRCONV); + spin_unlock_irqrestore(&s->lock, flags); + /* now wait for the stinkin' data (RDY) */ + for (t = 0; t < 0x1000; t++) + if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY) + break; + return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); +} + +/* --------------------------------------------------------------------- */ + +extern inline void stop_adc(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac1(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac2(struct es1371_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac1(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0) + && s->dma_dac1.ready) { + s->ctrl |= CTRL_DAC1_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac1.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac2(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0) + && s->dma_dac2.ready) { + s->ctrl |= CTRL_DAC2_EN; + s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | + SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN | + (((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | + (0 << SCTRL_SH_P2STINC); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_dac2.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct es1371_state *s) +{ + unsigned long flags; + unsigned fragremain, fshift; + + spin_lock_irqsave(&s->lock, flags); + if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize) + && s->dma_adc.ready) { + s->ctrl |= CTRL_ADC_EN; + s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1)); + fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT]; + if (fragremain < 2*fshift) + fragremain = s->dma_adc.fragsize; + outl((fragremain >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER 8 +#define DMABUF_MINORDER 1 + + +extern inline void dealloc_dmabuf(struct dmabuf *db) +{ + unsigned long map, mapend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + free_pages((unsigned long)db->rawbuf, db->buforder); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct es1371_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + unsigned long map, mapend; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; +#if LINUX_VERSION_CODE < 131328 + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) + db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order, 0); +#else + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) + db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order); +#endif + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &mem_map[map].flags); + } + fmt &= ES1371_FMT_MASK; + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & ES1371_FMT_S16) ? 0 : 0x80, db->dmasize); + outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE); + outl(virt_to_bus(db->rawbuf), s->io+(reg & 0xff)); + outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff)); + db->ready = 1; + return 0; +} + +extern inline int prog_dmabuf_adc(struct es1371_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, s->adcrate, (s->sctrl >> SCTRL_SH_R1FMT) & ES1371_FMT_MASK, + ES1371_REG_ADC_FRAMEADR); +} + +extern inline int prog_dmabuf_dac2(struct es1371_state *s) +{ + stop_dac2(s); + return prog_dmabuf(s, &s->dma_dac2, s->dac2rate, (s->sctrl >> SCTRL_SH_P2FMT) & ES1371_FMT_MASK, + ES1371_REG_DAC2_FRAMEADR); +} + +extern inline int prog_dmabuf_dac1(struct es1371_state *s) +{ + stop_dac1(s); + return prog_dmabuf(s, &s->dma_dac1, s->dac1rate, (s->sctrl >> SCTRL_SH_P1FMT) & ES1371_FMT_MASK, + ES1371_REG_DAC1_FRAMEADR); +} + +extern inline unsigned get_hwptr(struct es1371_state *s, struct dmabuf *db, unsigned reg) +{ + unsigned hwptr, diff; + + outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE); + hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc; + diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize; + db->hwptr = hwptr; + return diff; +} + +extern inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +/* call with spinlock held! */ +static void es1371_update_ptr(struct es1371_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->ctrl & CTRL_ADC_EN) { + diff = get_hwptr(s, &s->dma_adc, ES1371_REG_ADC_FRAMECNT); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) { + s->ctrl &= ~CTRL_ADC_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + /* update DAC1 pointer */ + if (s->ctrl & CTRL_DAC1_EN) { + diff = get_hwptr(s, &s->dma_dac1, ES1371_REG_DAC1_FRAMECNT); + s->dma_dac1.total_bytes += diff; + if (s->dma_dac1.mapped) { + s->dma_dac1.count += diff; + if (s->dma_dac1.count >= s->dma_dac1.fragsize) + wake_up(&s->dma_dac1.wait); + } else { + s->dma_dac1.count -= diff; + if (s->dma_dac1.count <= 0) { + s->ctrl &= ~CTRL_DAC1_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_dac1.error++; + } else if (s->dma_dac1.count <= s->dma_dac1.fragsize && !s->dma_dac1.endcleared) { + clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, + s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80); + s->dma_dac1.endcleared = 1; + } + if (s->dma_dac1.count < s->dma_dac1.dmasize) + wake_up(&s->dma_dac1.wait); + } + } + /* update DAC2 pointer */ + if (s->ctrl & CTRL_DAC2_EN) { + diff = get_hwptr(s, &s->dma_dac2, ES1371_REG_DAC2_FRAMECNT); + s->dma_dac2.total_bytes += diff; + if (s->dma_dac2.mapped) { + s->dma_dac2.count += diff; + if (s->dma_dac2.count >= s->dma_dac2.fragsize) + wake_up(&s->dma_dac2.wait); + } else { + s->dma_dac2.count -= diff; + if (s->dma_dac2.count <= 0) { + s->ctrl &= ~CTRL_DAC2_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + s->dma_dac2.error++; + } else if (s->dma_dac2.count <= s->dma_dac2.fragsize && !s->dma_dac2.endcleared) { + clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, + s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80); + s->dma_dac2.endcleared = 1; + } + if (s->dma_dac2.count < s->dma_dac2.dmasize) + wake_up(&s->dma_dac2.wait); + } + } +} + +/* hold spinlock for the following! */ +static void es1371_handle_midi(struct es1371_state *s) +{ + unsigned char ch; + int wake; + + if (!(s->ctrl & CTRL_UART_EN)) + return; + wake = 0; + while (inb(s->io+ES1371_REG_UART_STATUS) & USTAT_RXRDY) { + ch = inb(s->io+ES1371_REG_UART_DATA); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while ((inb(s->io+ES1371_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->io+ES1371_REG_UART_DATA); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); + outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1371_REG_UART_CONTROL); +} + +static void es1371_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct es1371_state *s = (struct es1371_state *)dev_id; + unsigned int intsrc, sctl; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inl(s->io+ES1371_REG_STATUS); + if (!(intsrc & 0x80000000)) + return; + spin_lock(&s->lock); + /* clear audio interrupts first */ + sctl = s->sctrl; + if (intsrc & STAT_ADC) + sctl &= ~SCTRL_R1INTEN; + if (intsrc & STAT_DAC1) + sctl &= ~SCTRL_P1INTEN; + if (intsrc & STAT_DAC2) + sctl &= ~SCTRL_P2INTEN; + outl(sctl, s->io+ES1371_REG_SERIAL_CONTROL); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + es1371_update_ptr(s); + es1371_handle_midi(s); + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "es1371: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != ES1371_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +#define AC97_PESSIMISTIC + +/* + * this define causes the driver to assume that all optional + * AC97 bits are missing. This is what Ensoniq does too in their + * Windows driver. Maybe we should one day autoprobe for these + * bits. But anyway I have to see an AC97 codec that implements + * one of those optional (volume) bits. + */ + +static const unsigned int recsrc[8] = +{ + SOUND_MASK_MIC, + SOUND_MASK_CD, + SOUND_MASK_VIDEO, + SOUND_MASK_LINE1, + SOUND_MASK_LINE, + SOUND_MASK_VOLUME, + SOUND_MASK_PHONEOUT, + SOUND_MASK_PHONEIN +}; + +static const unsigned char volreg[] = +{ + /* 5 bit stereo */ + [SOUND_MIXER_LINE] = 0x10, + [SOUND_MIXER_CD] = 0x12, + [SOUND_MIXER_VIDEO] = 0x14, + [SOUND_MIXER_LINE1] = 0x16, + [SOUND_MIXER_PCM] = 0x18, + /* 6 bit stereo */ + [SOUND_MIXER_VOLUME] = 0x02, + [SOUND_MIXER_PHONEOUT] = 0x04, + /* 6 bit mono */ + [SOUND_MIXER_OGAIN] = 0x06, + [SOUND_MIXER_PHONEIN] = 0x0c, + /* 4 bit mono but shifted by 1 */ + [SOUND_MIXER_SPEAKER] = 0x08, + /* 6 bit mono + preamp */ + [SOUND_MIXER_MIC] = 0x0e, + /* 4 bit stereo */ + [SOUND_MIXER_RECLEV] = 0x1c, + /* 4 bit mono */ + [SOUND_MIXER_IGAIN] = 0x1e +}; + +#define swab(x) ((((x) >> 8) & 0xff) | (((x) << 8) & 0xff00)) + +static int mixer_rdch(struct es1371_state *s, unsigned int ch, int *arg) +{ + int j; + + switch (ch) { + case SOUND_MIXER_MIC: + j = rdcodec(s, 0x0e); + if (j & 0x8000) + return put_user(0, (int *)arg); +#ifdef AC97_PESSIMISTIC + return put_user(0x4949 - 0x202 * (j & 0x1f) + ((j & 0x40) ? 0x1b1b : 0), (int *)arg); +#else /* AC97_PESSIMISTIC */ + return put_user(0x5757 - 0x101 * ((j & 0x3f) * 5 / 4) + ((j & 0x40) ? 0x0d0d : 0), (int *)arg); +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_OGAIN: + case SOUND_MIXER_PHONEIN: + j = rdcodec(s, volreg[ch]); + if (j & 0x8000) + return put_user(0, (int *)arg); +#ifdef AC97_PESSIMISTIC + return put_user(0x6464 - 0x303 * (j & 0x1f), (int *)arg); +#else /* AC97_PESSIMISTIC */ + return put_user((0x6464 - 0x303 * (j & 0x3f) / 2) & 0x7f7f, (int *)arg); +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_PHONEOUT: + if (!(s->mix.codec_id & CODEC_ID_HEADPHONEOUT)) + return -EINVAL; + /* fall through */ + case SOUND_MIXER_VOLUME: + j = rdcodec(s, volreg[ch]); + if (j & 0x8000) + return put_user(0, (int *)arg); +#ifdef AC97_PESSIMISTIC + return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg); +#else /* AC97_PESSIMISTIC */ + return put_user((0x6464 - (swab(j) & 0x3f3f) * 3 / 2) & 0x7f7f, (int *)arg); +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_SPEAKER: + j = rdcodec(s, 0x0a); + if (j & 0x8000) + return put_user(0, (int *)arg); + return put_user(0x6464 - ((j >> 1) & 0xf) * 0x606, (int *)arg); + + case SOUND_MIXER_LINE: + case SOUND_MIXER_CD: + case SOUND_MIXER_VIDEO: + case SOUND_MIXER_LINE1: + case SOUND_MIXER_PCM: + j = rdcodec(s, volreg[ch]); + if (j & 0x8000) + return put_user(0, (int *)arg); + return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg); + + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + if (!(s->mix.codec_id & CODEC_ID_BASSTREBLE)) + return -EINVAL; + j = rdcodec(s, 0x08); + if (ch == SOUND_MIXER_BASS) + j >>= 8; + return put_user((((j & 15) * 100) / 15) * 0x101, (int *)arg); + + /* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */ + case SOUND_MIXER_RECLEV: + j = rdcodec(s, 0x1c); + if (j & 0x8000) + return put_user(0, (int *)arg); + return put_user((swab(j) & 0xf0f) * 6 + 0xa0a, (int *)arg); + + case SOUND_MIXER_IGAIN: + if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC)) + return -EINVAL; + j = rdcodec(s, 0x1e); + if (j & 0x8000) + return put_user(0, (int *)arg); + return put_user((j & 0xf) * 0x606 + 0xa0a, (int *)arg); + + default: + return -EINVAL; + } +} + +static int mixer_wrch(struct es1371_state *s, unsigned int ch, int val) +{ + int i; + unsigned l1, r1; + + l1 = val & 0xff; + r1 = (val >> 8) & 0xff; + if (l1 > 100) + l1 = 100; + if (r1 > 100) + r1 = 100; + switch (ch) { + case SOUND_MIXER_LINE: + case SOUND_MIXER_CD: + case SOUND_MIXER_VIDEO: + case SOUND_MIXER_LINE1: + case SOUND_MIXER_PCM: + if (l1 < 7 && r1 < 7) { + wrcodec(s, volreg[ch], 0x8000); + return 0; + } + if (l1 < 7) + l1 = 7; + if (r1 < 7) + r1 = 7; + wrcodec(s, volreg[ch], (((100 - l1) / 3) << 8) | ((100 - r1) / 3)); + return 0; + + case SOUND_MIXER_PHONEOUT: + if (!(s->mix.codec_id & CODEC_ID_HEADPHONEOUT)) + return -EINVAL; + /* fall through */ + case SOUND_MIXER_VOLUME: +#ifdef AC97_PESSIMISTIC + if (l1 < 7 && r1 < 7) { + wrcodec(s, volreg[ch], 0x8000); + return 0; + } + if (l1 < 7) + l1 = 7; + if (r1 < 7) + r1 = 7; + wrcodec(s, volreg[ch], (((100 - l1) / 3) << 8) | ((100 - r1) / 3)); + return 0; +#else /* AC97_PESSIMISTIC */ + if (l1 < 4 && r1 < 4) { + wrcodec(s, volreg[ch], 0x8000); + return 0; + } + if (l1 < 4) + l1 = 4; + if (r1 < 4) + r1 = 4; + wrcodec(s, volreg[ch], ((2 * (100 - l1) / 3) << 8) | (2 * (100 - r1) / 3)); + return 0; +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_OGAIN: + case SOUND_MIXER_PHONEIN: +#ifdef AC97_PESSIMISTIC + wrcodec(s, volreg[ch], (l1 < 7) ? 0x8000 : (100 - l1) / 3); + return 0; +#else /* AC97_PESSIMISTIC */ + wrcodec(s, volreg[ch], (l1 < 4) ? 0x8000 : (2 * (100 - l1) / 3)); + return 0; +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_SPEAKER: + wrcodec(s, 0x0a, (l1 < 10) ? 0x8000 : ((100 - l1) / 6) << 1); + return 0; + + case SOUND_MIXER_MIC: +#ifdef AC97_PESSIMISTIC + if (l1 < 11) { + wrcodec(s, 0x0e, 0x8000); + return 0; + } + i = 0; + if (l1 >= 27) { + l1 -= 27; + i = 0x40; + } + if (l1 < 11) + l1 = 11; + wrcodec(s, 0x0e, ((73 - l1) / 2) | i); + return 0; +#else /* AC97_PESSIMISTIC */ + if (l1 < 9) { + wrcodec(s, 0x0e, 0x8000); + return 0; + } + i = 0; + if (l1 >= 13) { + l1 -= 13; + i = 0x40; + } + if (l1 < 9) + l1 = 9; + wrcodec(s, 0x0e, (((87 - l1) * 4) / 5) | i); + return 0; +#endif /* AC97_PESSIMISTIC */ + + case SOUND_MIXER_BASS: + val = ((l1 * 15) / 100) & 0xf; + wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0x00ff) | (val << 8)); + return 0; + + case SOUND_MIXER_TREBLE: + val = ((l1 * 15) / 100) & 0xf; + wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0xff00) | val); + return 0; + + /* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */ + case SOUND_MIXER_RECLEV: + if (l1 < 10 || r1 < 10) { + wrcodec(s, 0x1c, 0x8000); + return 0; + } + if (l1 < 10) + l1 = 10; + if (r1 < 10) + r1 = 10; + wrcodec(s, 0x1c, (((l1 - 10) / 6) << 8) | ((r1 - 10) / 6)); + return 0; + + case SOUND_MIXER_IGAIN: + if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC)) + return -EINVAL; + wrcodec(s, 0x1e, (l1 < 10) ? 0x8000 : ((l1 - 10) / 6) & 0xf); + return 0; + + default: + return -EINVAL; + } +} + +static int mixer_ioctl(struct es1371_state *s, unsigned int cmd, unsigned long arg) +{ + int i, val; + + VALIDATE_STATE(s); + if (cmd == SOUND_MIXER_PRIVATE1) { + if (!(s->mix.codec_id & (CODEC_ID_SEMASK << CODEC_ID_SESHIFT))) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val & 1) + wrcodec(s, 0x22, ((val << 3) & 0xf00) | ((val >> 1) & 0xf)); + val = rdcodec(s, 0x22); + return put_user(((val & 0xf) << 1) | ((val & 0xf00) >> 3), (int *)arg); + } +#if LINUX_VERSION_CODE < 131328 + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "ES1371", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1371", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } +#else + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "ES1371", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1371", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "ES1371", sizeof(info.id)); + strncpy(info.name, "Ensoniq ES1371", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } +#endif + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(recsrc[rdcodec(s, 0x1a) & 7], (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_VIDEO | + SOUND_MASK_LINE1 | SOUND_MASK_PCM | SOUND_MASK_VOLUME | + SOUND_MASK_OGAIN | SOUND_MASK_PHONEIN | SOUND_MASK_SPEAKER | + SOUND_MASK_MIC | SOUND_MASK_RECLEV | + ((s->mix.codec_id & CODEC_ID_BASSTREBLE) ? (SOUND_MASK_BASS | SOUND_MASK_TREBLE) : 0) | + ((s->mix.codec_id & CODEC_ID_HEADPHONEOUT) ? SOUND_MASK_PHONEOUT : 0) | + ((s->mix.codec_id & CODEC_ID_DEDICATEDMIC) ? SOUND_MASK_IGAIN : 0), (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + return put_user(SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VIDEO | SOUND_MASK_LINE1 | + SOUND_MASK_LINE | SOUND_MASK_VOLUME | SOUND_MASK_PHONEOUT | + SOUND_MASK_PHONEIN, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_VIDEO | + SOUND_MASK_LINE1 | SOUND_MASK_PCM | SOUND_MASK_VOLUME | + SOUND_MASK_PHONEOUT | SOUND_MASK_RECLEV, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + return mixer_rdch(s, i, (int *)arg); + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + get_user_ret(val, (int *)arg, -EFAULT); + i = hweight32(val); + if (i == 0) + return 0; /*val = mixer_recmask(s);*/ + else if (i > 1) + val &= ~recsrc[rdcodec(s, 0x1a) & 7]; + for (i = 0; i < 8; i++) { + if (val & recsrc[i]) { + wrcodec(s, 0x1a, 0x101 * i); + return 0; + } + } + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (mixer_wrch(s, i, val)) + return -EINVAL; + return mixer_rdch(s, i, (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int es1371_llseek(struct inode *ino, struct file *file, off_t offset, int origin) +{ + return -ESPIPE; +} +#else +static loff_t es1371_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} +#endif + +/* --------------------------------------------------------------------- */ + +static int es1371_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1371_state *s = devs; + + while (s && s->dev_mixer != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + MOD_INC_USE_COUNT; + return 0; +} + +static int es1371_release_mixdev(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + +static int es1371_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct es1371_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations es1371_mixer_fops = { + &es1371_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &es1371_ioctl_mixdev, + NULL, /* mmap */ + &es1371_open_mixdev, + &es1371_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac1(struct es1371_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac1.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac1.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac1.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->dac1rate; + tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; + current->timeout = jiffies + (tmo ? tmo : 1); + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1371: dma timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->dma_dac1.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac2(struct es1371_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac2.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac2.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac2.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->dac2rate; + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + current->timeout = jiffies + (tmo ? tmo : 1); + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1371: dma timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->dma_dac2.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int es1371_read(struct inode *ino, struct file *file, char *buffer, int count) +#else +static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 +static int es1371_write(struct inode *ino, struct file *file, const char *buffer, int count) +#else +static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (s->dma_dac2.mapped) + return -ENXIO; + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac2.count < 0) { + s->dma_dac2.count = 0; + s->dma_dac2.swptr = s->dma_dac2.hwptr; + } + swptr = s->dma_dac2.swptr; + cnt = s->dma_dac2.dmasize-swptr; + if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize) + cnt = s->dma_dac2.dmasize - s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac2(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_dac2.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac2.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac2.swptr = swptr; + s->dma_dac2.count += cnt; + s->dma_dac2.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac2(s); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 + +/* well select really */ +static int es1371_poll(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + int ret = 0; + + VALIDATE_STATE(s); + if (sel_type == SEL_IN && file->f_mode & FMODE_READ) { + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + ret = 1; + } else { + if (s->dma_adc.count > 0) + ret = 1; + } + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->dma_adc.wait, wait); + } + if (sel_type == SEL_OUT && file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + if (s->dma_dac2.mapped) { + if (s->dma_dac2.count >= s->dma_dac2.fragsize) + ret = 1; + } else { + if (s->dma_dac2.dmasize > s->dma_dac2.count) + ret = 1; + } + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->dma_dac2.wait, wait); + } + return 0; +} + +#else + +static unsigned int es1371_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->dma_dac2.wait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + if (file->f_flags & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_flags & FMODE_WRITE) { + if (s->dma_dac2.mapped) { + if (s->dma_dac2.count >= s->dma_dac2.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if (s->dma_dac2.dmasize > s->dma_dac2.count) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +#endif + +#if LINUX_VERSION_CODE < 131328 +static int es1371_mmap(struct inode *ino, struct file *file, struct vm_area_struct *vma) +#else +static int es1371_mmap(struct file *file, struct vm_area_struct *vma) +#endif +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac2(s)) != 0) + return ret; + db = &s->dma_dac2; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; +#if LINUX_VERSION_CODE < 131328 + vma->vm_inode = ino; + ino->i_count++; +#else + vma->vm_file = file; + file->f_count++; +#endif + return 0; +} + +static int es1371_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + synchronize_irq(); + s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + set_dac2_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_R1SMB; + else + s->sctrl &= ~SCTRL_R1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P2SMB; + else + s->sctrl &= ~SCTRL_P2SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_R1SEB; + else + s->sctrl &= ~SCTRL_R1SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac2(s); + s->dma_dac2.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P2SEB; + else + s->sctrl &= ~SCTRL_P2SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + } + return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s))) + return ret; + start_dac2(s); + } else + stop_dac2(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac2(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_dac2.fragsize; + abinfo.bytes = s->dma_dac2.dmasize - s->dma_dac2.count; + abinfo.fragstotal = s->dma_dac2.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(s->ctrl & CTRL_ADC_EN) && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + val = s->dma_dac2.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_dac2.total_bytes; + cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift; + cinfo.ptr = s->dma_dac2.hwptr; + if (s->dma_dac2.mapped) + s->dma_dac2.count &= s->dma_dac2.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac2(s))) + return val; + return put_user(s->dma_dac2.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = val & 0xffff; + s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac2.ossfragshift < 4) + s->dma_dac2.ossfragshift = 4; + if (s->dma_dac2.ossfragshift > 15) + s->dma_dac2.ossfragshift = 15; + if (s->dma_dac2.ossmaxfrags < 4) + s->dma_dac2.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac2.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1371_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1371_state *s = devs; + unsigned long flags; + + while (s && ((s->dev_audio ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0; + set_dac2_rate(s, 8000); + } + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + s->sctrl &= ~SCTRL_R1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_R1FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_R1FMT; + } + if (file->f_mode & FMODE_WRITE) { + s->sctrl &= ~SCTRL_P2FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P2FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P2FMT; + } + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1371_release(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac2(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_flags & FMODE_WRITE) { + stop_dac2(s); + dealloc_dmabuf(&s->dma_dac2); + } + if (file->f_flags & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(&s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1371_audio_fops = { + &es1371_llseek, + &es1371_read, + &es1371_write, + NULL, /* readdir */ + &es1371_poll, + &es1371_ioctl, + &es1371_mmap, + &es1371_open, + &es1371_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int es1371_write_dac(struct inode *ino, struct file *file, const char *buffer, int count) +#else +static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (s->dma_dac1.mapped) + return -ENXIO; + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac1.count < 0) { + s->dma_dac1.count = 0; + s->dma_dac1.swptr = s->dma_dac1.hwptr; + } + swptr = s->dma_dac1.swptr; + cnt = s->dma_dac1.dmasize-swptr; + if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize) + cnt = s->dma_dac1.dmasize - s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac1(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_dac1.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac1.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac1.swptr = swptr; + s->dma_dac1.count += cnt; + s->dma_dac1.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac1(s); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 + +/* well select really */ +static int es1371_poll_dac(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + int ret = 0; + + VALIDATE_STATE(s); + if (sel_type == SEL_OUT) { + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= s->dma_dac1.fragsize) + ret = 1; + } else { + if (s->dma_dac1.dmasize > s->dma_dac1.count) + ret = 1; + } + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->dma_dac1.wait, wait); + } + return 0; +} + +#else + +static unsigned int es1371_poll_dac(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + poll_wait(file, &s->dma_dac1.wait, wait); + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + if (s->dma_dac1.mapped) { + if (s->dma_dac1.count >= s->dma_dac1.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if (s->dma_dac1.dmasize > s->dma_dac1.count) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} +#endif + +#if LINUX_VERSION_CODE < 131328 +static int es1371_mmap_dac(struct inode *ino, struct file *file, struct vm_area_struct *vma) +#else +static int es1371_mmap_dac(struct file *file, struct vm_area_struct *vma) +#endif +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (!(vma->vm_flags & VM_WRITE)) + return -EINVAL; + if ((ret = prog_dmabuf_dac1(s)) != 0) + return ret; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << s->dma_dac1.buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + s->dma_dac1.mapped = 1; +#if LINUX_VERSION_CODE < 131328 + vma->vm_inode = ino; + ino->i_count++; +#else + vma->vm_file = file; + file->f_count++; +#endif + return 0; +} + +static int es1371_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, ret; + + VALIDATE_STATE(s); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + stop_dac1(s); + synchronize_irq(); + s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0; + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + set_dac1_rate(s, val); + } + return put_user(s->dac1rate, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val >= 2) + s->sctrl |= SCTRL_P1SMB; + else + s->sctrl &= ~SCTRL_P1SMB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + stop_dac1(s); + s->dma_dac1.ready = 0; + spin_lock_irqsave(&s->lock, flags); + if (val == AFMT_S16_LE) + s->sctrl |= SCTRL_P1SEB; + else + s->sctrl &= ~SCTRL_P1SEB; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + } + return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s))) + return ret; + start_dac1(s); + } else + stop_dac1(s); + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac1(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + abinfo.fragsize = s->dma_dac1.fragsize; + abinfo.bytes = s->dma_dac1.dmasize - s->dma_dac1.count; + abinfo.fragstotal = s->dma_dac1.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + val = s->dma_dac1.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + es1371_update_ptr(s); + cinfo.bytes = s->dma_dac1.total_bytes; + cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift; + cinfo.ptr = s->dma_dac1.hwptr; + if (s->dma_dac1.mapped) + s->dma_dac1.count &= s->dma_dac1.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if ((val = prog_dmabuf_dac1(s))) + return val; + return put_user(s->dma_dac1.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + s->dma_dac1.ossfragshift = val & 0xffff; + s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac1.ossfragshift < 4) + s->dma_dac1.ossfragshift = 4; + if (s->dma_dac1.ossfragshift > 15) + s->dma_dac1.ossfragshift = 15; + if (s->dma_dac1.ossmaxfrags < 4) + s->dma_dac1.ossmaxfrags = 4; + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (s->dma_dac1.subdivision) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + s->dma_dac1.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int es1371_open_dac(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1371_state *s = devs; + unsigned long flags; + + while (s && ((s->dev_dac ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + /* we allow opening with O_RDWR, most programs do it although they will only write */ +#if 0 + if (file->f_mode & FMODE_READ) + return -EPERM; +#endif + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DAC) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0; + set_dac1_rate(s, 8000); + spin_lock_irqsave(&s->lock, flags); + s->sctrl &= ~SCTRL_P1FMT; + if ((minor & 0xf) == SND_DEV_DSP16) + s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P1FMT; + else + s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P1FMT; + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= FMODE_DAC; + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1371_release_dac(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + + VALIDATE_STATE(s); + drain_dac1(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + stop_dac1(s); + dealloc_dmabuf(&s->dma_dac1); + s->open_mode &= ~FMODE_DAC; + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1371_dac_fops = { + &es1371_llseek, + NULL, /* read */ + &es1371_write_dac, + NULL, /* readdir */ + &es1371_poll_dac, + &es1371_ioctl_dac, + &es1371_mmap_dac, + &es1371_open_dac, + &es1371_release_dac, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int es1371_midi_read(struct inode *ino, struct file *file, char *buffer, int count) +#else +static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 +static int es1371_midi_write(struct inode *ino, struct file *file, const char *buffer, int count) +#else +static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + es1371_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + es1371_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 + +/* well select really */ +static int es1371_midi_poll(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + int ret = 0; + + VALIDATE_STATE(s); + if (sel_type == SEL_IN && file->f_mode & FMODE_READ) { + spin_lock_irqsave(&s->lock, flags); + if (s->midi.icnt > 0) + ret = 1; + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->midi.iwait, wait); + } + if (sel_type == SEL_OUT && file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&s->lock, flags); + if (s->midi.ocnt < MIDIOUTBUF) + ret = 1; + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->midi.owait, wait); + } + return 0; +} + +#else + +static unsigned int es1371_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +#endif + +static int es1371_midi_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct es1371_state *s = devs; + unsigned long flags; + + while (s && s->dev_midi != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + outb(UCTRL_CNTRL_SWR, s->io+ES1371_REG_UART_CONTROL); + outb(0, s->io+ES1371_REG_UART_CONTROL); + outb(0, s->io+ES1371_REG_UART_TEST); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + s->ctrl |= CTRL_UART_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + es1371_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int es1371_midi_release(struct inode *inode, struct file *file) +{ + struct es1371_state *s = (struct es1371_state *)file->private_data; + struct wait_queue wait = { current, NULL }; + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + current->timeout = tmo ? jiffies + tmo : 0; + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "es1371: midi timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->ctrl &= ~CTRL_UART_EN; + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations es1371_midi_fops = { + &es1371_llseek, + &es1371_midi_read, + &es1371_midi_write, + NULL, /* readdir */ + &es1371_midi_poll, + NULL, /* ioctl */ + NULL, /* mmap */ + &es1371_midi_open, + &es1371_midi_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices */ +#define NR_DEVICE 5 + +static int joystick[NR_DEVICE] = { 0, }; + +/* --------------------------------------------------------------------- */ + +static const struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_VIDEO), 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 }, + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_PHONEOUT), 0x4040 }, + { SOUND_MIXER_WRITE_OGAIN, 0x4040 }, + { MIXER_WRITE(SOUND_MIXER_PHONEIN), 0x4040 }, + { SOUND_MIXER_WRITE_SPEAKER, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_IGAIN, 0x4040 } +}; + +#if LINUX_VERSION_CODE < 131421 + +#ifdef MODULE +__initfunc(int init_module(void)) +#else +__initfunc(int init_es1371(void)) +#endif +{ + struct es1371_state *s; + unsigned char bus, dev_fn, irq; + unsigned short index, vendid, devid; + unsigned int ioaddr; + mm_segment_t fs; + int i, val, val2; + + if (!pcibios_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "es1371: version v0.2 time " __TIME__ " " __DATE__ "\n"); + for (index = 0; index < NR_DEVICE; index++) { + if (pcibios_find_class((PCI_CLASS_MULTIMEDIA_AUDIO << 8), index, &bus, &dev_fn) != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_word(bus, dev_fn, PCI_VENDOR_ID, &vendid); + pcibios_read_config_word(bus, dev_fn, PCI_DEVICE_ID, &devid); + if (vendid != PCI_VENDOR_ID_ENSONIQ || devid != PCI_DEVICE_ID_ENSONIQ_ES1371) + continue; + pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_0, &ioaddr); + if (ioaddr == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + pcibios_read_config_byte(bus, dev_fn, PCI_INTERRUPT_LINE, &irq); + if (irq == 0 || irq == 0xff) + continue; + if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) { + printk(KERN_WARNING "es1371: out of memory\n"); + continue; + } + memset(s, 0, sizeof(struct es1371_state)); + init_waitqueue(&s->dma_adc.wait); + init_waitqueue(&s->dma_dac1.wait); + init_waitqueue(&s->dma_dac2.wait); + init_waitqueue(&s->open_wait); + init_waitqueue(&s->midi.iwait); + init_waitqueue(&s->midi.owait); + s->open_sem = MUTEX; + s->magic = ES1371_MAGIC; + s->io = ioaddr & PCI_BASE_ADDRESS_IO_MASK; + s->irq = irq; + if (check_region(s->io, ES1371_EXTENT)) { + printk(KERN_ERR "es1371: io ports %#x-%#x in use\n", s->io, s->io+ES1371_EXTENT-1); + goto err_region; + } + request_region(s->io, ES1371_EXTENT, "es1371"); + if (request_irq(s->irq, es1371_interrupt, SA_INTERRUPT|SA_SHIRQ, "es1371", s)) { + printk(KERN_ERR "es1371: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "es1371: found adapter at io %#06x irq %u\n" + KERN_INFO "es1371: features: joystick 0x%x\n", s->io, s->irq, joystick[index]); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&es1371_audio_fops)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&es1371_mixer_fops)) < 0) + goto err_dev2; + if ((s->dev_dac = register_sound_dsp(&es1371_dac_fops)) < 0) + goto err_dev3; + if ((s->dev_midi = register_sound_midi(&es1371_midi_fops)) < 0) + goto err_dev4; + /* initialize codec registers */ + s->ctrl = 0; + if ((joystick[index] & ~0x18) == 0x200) { + if (check_region(joystick[index], JOY_EXTENT)) + printk(KERN_ERR "es1371: joystick address 0x%x already in use\n", joystick[index]); + else { + s->ctrl |= CTRL_JYSTK_EN | (((joystick[index] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); + request_region(joystick[index], JOY_EXTENT, "es1371"); + } + } + s->sctrl = 0; + /* initialize the chips */ + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + outl(0, s->io+ES1371_REG_LEGACY); + /* AC97 warm reset to start the bitclk */ + outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL); + udelay(2); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + /* init the sample rate converter */ + outl(SRC_DIS, s->io + ES1371_REG_SRCONV); + for (val = 0; val < 0x80; val++) + src_write(s, val, 0); + src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_VOL_ADC, 1 << 12); + src_write(s, SRCREG_VOL_ADC+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC2, 1 << 12); + src_write(s, SRCREG_VOL_DAC2+1, 1 << 12); + set_adc_rate(s, 22050); + set_dac1_rate(s, 22050); + set_dac2_rate(s, 22050); + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) + */ + outl(0, s->io+ES1371_REG_SRCONV); + /* codec init */ + wrcodec(s, 0x00, 0); /* reset codec */ + s->mix.codec_id = rdcodec(s, 0x00); /* get codec ID */ + val = rdcodec(s, 0x7c); + val2 = rdcodec(s, 0x7e); + printk(KERN_INFO "es1371: codec vendor %c%c%c revision %d\n", + (val >> 8) & 0xff, val & 0xff, (val2 >> 8) & 0xff, val2 & 0xff); + printk(KERN_INFO "es1371: codec features"); + if (s->mix.codec_id & CODEC_ID_DEDICATEDMIC) + printk(" dedicated MIC PCM in"); + if (s->mix.codec_id & CODEC_ID_MODEMCODEC) + printk(" Modem Line Codec"); + if (s->mix.codec_id & CODEC_ID_BASSTREBLE) + printk(" Bass & Treble"); + if (s->mix.codec_id & CODEC_ID_SIMULATEDSTEREO) + printk(" Simulated Stereo"); + if (s->mix.codec_id & CODEC_ID_HEADPHONEOUT) + printk(" Headphone out"); + if (s->mix.codec_id & CODEC_ID_LOUDNESS) + printk(" Loudness"); + if (s->mix.codec_id & CODEC_ID_18BITDAC) + printk(" 18bit DAC"); + if (s->mix.codec_id & CODEC_ID_20BITDAC) + printk(" 20bit DAC"); + if (s->mix.codec_id & CODEC_ID_18BITADC) + printk(" 18bit ADC"); + if (s->mix.codec_id & CODEC_ID_20BITADC) + printk(" 20bit ADC"); + printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none"); + val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK; + printk(KERN_INFO "es1371: stereo enhancement: %s\n", (val <= 20) ? stereo_enhancement[val] : "unknown"); + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* queue it for later freeing */ + s->next = devs; + devs = s; + continue; + + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "es1371: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, ES1371_EXTENT); + err_region: + kfree_s(s, sizeof(struct es1371_state)); + } + if (!devs) + return -ENODEV; + return 0; +} + + +#else /* Linux Version >= 2.1.93 */ + +#ifdef MODULE +__initfunc(int init_module(void)) +#else +__initfunc(int init_es1371(void)) +#endif +{ + struct es1371_state *s; + struct pci_dev *pcidev = NULL; + mm_segment_t fs; + int i, val, val2, index = 0; + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "es1371: version v0.2 time " __TIME__ " " __DATE__ "\n"); + while (index < NR_DEVICE && + (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) { + if (pcidev->base_address[0] == 0 || + (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->irq == 0) + continue; + if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) { + printk(KERN_WARNING "es1371: out of memory\n"); + continue; + } + memset(s, 0, sizeof(struct es1371_state)); + init_waitqueue(&s->dma_adc.wait); + init_waitqueue(&s->dma_dac1.wait); + init_waitqueue(&s->dma_dac2.wait); + init_waitqueue(&s->open_wait); + init_waitqueue(&s->midi.iwait); + init_waitqueue(&s->midi.owait); + s->open_sem = MUTEX; + s->magic = ES1371_MAGIC; + s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + s->irq = pcidev->irq; + if (check_region(s->io, ES1371_EXTENT)) { + printk(KERN_ERR "es1371: io ports %#x-%#x in use\n", s->io, s->io+ES1371_EXTENT-1); + goto err_region; + } + request_region(s->io, ES1371_EXTENT, "es1371"); + if (request_irq(s->irq, es1371_interrupt, SA_INTERRUPT|SA_SHIRQ, "es1371", s)) { + printk(KERN_ERR "es1371: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "es1371: found adapter at io %#06x irq %u\n" + KERN_INFO "es1371: features: joystick 0x%x\n", s->io, s->irq, joystick[index]); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&es1371_audio_fops)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&es1371_mixer_fops)) < 0) + goto err_dev2; + if ((s->dev_dac = register_sound_dsp(&es1371_dac_fops)) < 0) + goto err_dev3; + if ((s->dev_midi = register_sound_midi(&es1371_midi_fops)) < 0) + goto err_dev4; + /* initialize codec registers */ + s->ctrl = 0; + if ((joystick[index] & ~0x18) == 0x200) { + if (check_region(joystick[index], JOY_EXTENT)) + printk(KERN_ERR "es1371: joystick address 0x%x already in use\n", joystick[index]); + else { + s->ctrl |= CTRL_JYSTK_EN | (((joystick[index] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT); + request_region(joystick[index], JOY_EXTENT, "es1371"); + } + } + s->sctrl = 0; + /* initialize the chips */ + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL); + outl(0, s->io+ES1371_REG_LEGACY); + /* AC97 warm reset to start the bitclk */ + outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL); + udelay(2); + outl(s->ctrl, s->io+ES1371_REG_CONTROL); + /* init the sample rate converter */ + outl(SRC_DIS, s->io + ES1371_REG_SRCONV); + for (val = 0; val < 0x80; val++) + src_write(s, val, 0); + src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_VOL_ADC, 1 << 12); + src_write(s, SRCREG_VOL_ADC+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC2, 1 << 12); + src_write(s, SRCREG_VOL_DAC2+1, 1 << 12); + set_adc_rate(s, 22050); + set_dac1_rate(s, 22050); + set_dac2_rate(s, 22050); + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) + */ + outl(0, s->io+ES1371_REG_SRCONV); + /* codec init */ + wrcodec(s, 0x00, 0); /* reset codec */ + s->mix.codec_id = rdcodec(s, 0x00); /* get codec ID */ + val = rdcodec(s, 0x7c); + val2 = rdcodec(s, 0x7e); + printk(KERN_INFO "es1371: codec vendor %c%c%c revision %d\n", + (val >> 8) & 0xff, val & 0xff, (val2 >> 8) & 0xff, val2 & 0xff); + printk(KERN_INFO "es1371: codec features"); + if (s->mix.codec_id & CODEC_ID_DEDICATEDMIC) + printk(" dedicated MIC PCM in"); + if (s->mix.codec_id & CODEC_ID_MODEMCODEC) + printk(" Modem Line Codec"); + if (s->mix.codec_id & CODEC_ID_BASSTREBLE) + printk(" Bass & Treble"); + if (s->mix.codec_id & CODEC_ID_SIMULATEDSTEREO) + printk(" Simulated Stereo"); + if (s->mix.codec_id & CODEC_ID_HEADPHONEOUT) + printk(" Headphone out"); + if (s->mix.codec_id & CODEC_ID_LOUDNESS) + printk(" Loudness"); + if (s->mix.codec_id & CODEC_ID_18BITDAC) + printk(" 18bit DAC"); + if (s->mix.codec_id & CODEC_ID_20BITDAC) + printk(" 20bit DAC"); + if (s->mix.codec_id & CODEC_ID_18BITADC) + printk(" 18bit ADC"); + if (s->mix.codec_id & CODEC_ID_20BITADC) + printk(" 20bit ADC"); + printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none"); + val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK; + printk(KERN_INFO "es1371: stereo enhancement: %s\n", (val <= 20) ? stereo_enhancement[val] : "unknown"); + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* queue it for later freeing */ + s->next = devs; + devs = s; + index++; + continue; + + err_dev4: + unregister_sound_dsp(s->dev_dac); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "es1371: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, ES1371_EXTENT); + err_region: + kfree_s(s, sizeof(struct es1371_state)); + } + if (!devs) + return -ENODEV; + return 0; +} + +#endif +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +#if LINUX_VERSION_CODE >= 131328 +MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)"); +MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out"); +MODULE_PARM(micz, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(micz, "changes (??) the microphone impedance"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("ES1371 AudioPCI97 Driver"); +#endif + +void cleanup_module(void) +{ + struct es1371_state *s; + + while ((s = devs)) { + devs = devs->next; + outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */ + outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */ + synchronize_irq(); + free_irq(s->irq, s); + release_region(s->io, ES1371_EXTENT); + if (s->ctrl & CTRL_JYSTK_EN) + release_region(((((s->ctrl >> CTRL_JOY_SHIFT) & CTRL_JOY_MASK) << 3) | 0x200), JOY_EXTENT); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_dsp(s->dev_dac); + unregister_sound_midi(s->dev_midi); + kfree_s(s, sizeof(struct es1371_state)); + } + printk(KERN_INFO "es1371: unloading\n"); +} + +#endif /* MODULE */ diff --git a/drivers/sound/finetune.h b/drivers/sound/finetune.h index e967015fd292..f3253cab97d6 100644 --- a/drivers/sound/finetune.h +++ b/drivers/sound/finetune.h @@ -1,8 +1,8 @@ #ifdef SEQUENCER_C /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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. */ diff --git a/drivers/sound/gus_card.c b/drivers/sound/gus_card.c index 29805b7d5ec0..3c46d7adad2f 100644 --- a/drivers/sound/gus_card.c +++ b/drivers/sound/gus_card.c @@ -3,248 +3,289 @@ * * Detection routine for the Gravis Ultrasound. */ + /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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. */ +/* + * Frank van de Pol : Fixed GUS MAX interrupt handling, enabled simultanious + * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. + * + * Status: + * Tested... + */ + + #include - +#include #include "sound_config.h" +#include "soundmodule.h" -#if defined(CONFIG_GUS) +#ifdef CONFIG_GUS #include "gus_hw.h" -void gusintr (int irq, void *dev_id, struct pt_regs *dummy); +void gusintr(int irq, void *dev_id, struct pt_regs *dummy); -int gus_base, gus_irq, gus_dma; +int gus_base = 0, gus_irq = 0, gus_dma = 0; +int gus_no_wave_dma = 0; extern int gus_wave_volume; extern int gus_pcm_volume; extern int have_gus_max; int gus_pnp_flag = 0; +#ifdef CONFIG_GUS16 +static int db16 = 0; /* Has a Gus16 AD1848 on it */ +#endif -int *gus_osp; - -void -attach_gus_card (struct address_info *hw_config) +void attach_gus_card(struct address_info *hw_config) { - int io_addr; - - gus_osp = hw_config->osp; - snd_set_irq_handler (hw_config->irq, gusintr, "Gravis Ultrasound", hw_config->osp); - - if (gus_wave_detect (hw_config->io_base)) /* - * Try first the default - */ - { - gus_wave_init (hw_config); - - request_region (hw_config->io_base, 16, "GUS"); - request_region (hw_config->io_base + 0x100, 12, "GUS"); /* 0x10c-> is MAX */ + if(request_irq(hw_config->irq, gusintr, 0, "Gravis Ultrasound", hw_config)<0) + printk(KERN_ERR "gus_card.c: Unable to allocate IRQ %d\n", hw_config->irq); - if (sound_alloc_dma (hw_config->dma, "GUS")) - printk ("gus_card.c: Can't allocate DMA channel\n"); - if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) - if (sound_alloc_dma (hw_config->dma2, "GUS(2)")) - printk ("gus_card.c: Can't allocate DMA channel2\n"); -#ifdef CONFIG_MIDI - gus_midi_init (); -#endif - return; - } + gus_wave_init(hw_config); -#ifndef EXCLUDE_GUS_IODETECT + request_region(hw_config->io_base, 16, "GUS"); + request_region(hw_config->io_base + 0x100, 12, "GUS"); /* 0x10c-> is MAX */ - /* - * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) - */ - - for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) - if (io_addr != hw_config->io_base) /* - * Already tested - */ - if (gus_wave_detect (io_addr)) - { - hw_config->io_base = io_addr; - - printk (" WARNING! GUS found at %x, config was %x ", io_addr, hw_config->io_base); - gus_wave_init (hw_config); - request_region (io_addr, 16, "GUS"); - request_region (io_addr + 0x100, 12, "GUS"); /* 0x10c-> is MAX */ - if (sound_alloc_dma (hw_config->dma, "GUS")) - printk ("gus_card.c: Can't allocate DMA channel\n"); - if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) - if (sound_alloc_dma (hw_config->dma2, "GUS")) - printk ("gus_card.c: Can't allocate DMA channel2\n"); + if (sound_alloc_dma(hw_config->dma, "GUS")) + printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma); + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + if (sound_alloc_dma(hw_config->dma2, "GUS(2)")) + printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma2); #ifdef CONFIG_MIDI - gus_midi_init (); + gus_midi_init(hw_config); #endif - return; - } - -#endif - } -int -probe_gus (struct address_info *hw_config) +int probe_gus(struct address_info *hw_config) { - int io_addr, irq; - - gus_osp = hw_config->osp; - - if (hw_config->card_subtype == 1) - gus_pnp_flag = 1; - - irq = hw_config->irq; - - if (hw_config->card_subtype == 0) /* GUS/MAX/ACE */ - if (irq != 3 && irq != 5 && irq != 7 && irq != 9 && - irq != 11 && irq != 12 && irq != 15) - { - printk ("GUS: Unsupported IRQ %d\n", irq); - return 0; - } - - if (check_region (hw_config->io_base, 16)) - printk ("GUS: I/O range conflict (1)\n"); - else if (check_region (hw_config->io_base + 0x100, 16)) - printk ("GUS: I/O range conflict (2)\n"); - else if (gus_wave_detect (hw_config->io_base)) - return 1; + int irq; + int io_addr; + + if (hw_config->card_subtype == 1) + gus_pnp_flag = 1; + + irq = hw_config->irq; + + if (hw_config->card_subtype == 0) /* GUS/MAX/ACE */ + if (irq != 3 && irq != 5 && irq != 7 && irq != 9 && + irq != 11 && irq != 12 && irq != 15) + { + printk(KERN_ERR "GUS: Unsupported IRQ %d\n", irq); + return 0; + } + if (check_region(hw_config->io_base, 16)) + printk(KERN_ERR "GUS: I/O range conflict (1)\n"); + else if (check_region(hw_config->io_base + 0x100, 16)) + printk(KERN_ERR "GUS: I/O range conflict (2)\n"); + else if (gus_wave_detect(hw_config->io_base)) + return 1; #ifndef EXCLUDE_GUS_IODETECT - /* - * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) - */ - - for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) - if (io_addr != hw_config->io_base) /* - * Already tested - */ - if (!check_region (io_addr, 16)) - if (!check_region (io_addr + 0x100, 16)) - if (gus_wave_detect (io_addr)) - { - hw_config->io_base = io_addr; - return 1; - } - + /* + * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6) + */ + + for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) + if (io_addr != hw_config->io_base) /* + * Already tested + */ + if (!check_region(io_addr, 16)) + if (!check_region(io_addr + 0x100, 16)) + if (gus_wave_detect(io_addr)) + { + hw_config->io_base = io_addr; + return 1; + } #endif - return 0; + return 0; } -void -unload_gus (struct address_info *hw_config) +void unload_gus(struct address_info *hw_config) { - DDB (printk ("unload_gus(%x)\n", hw_config->io_base)); + DDB(printk("unload_gus(%x)\n", hw_config->io_base)); - gus_wave_unload (); + gus_wave_unload(hw_config); - release_region (hw_config->io_base, 16); - release_region (hw_config->io_base + 0x100, 12); /* 0x10c-> is MAX */ - snd_release_irq (hw_config->irq); + release_region(hw_config->io_base, 16); + release_region(hw_config->io_base + 0x100, 12); /* 0x10c-> is MAX */ + free_irq(hw_config->irq, hw_config); - sound_free_dma (hw_config->dma); + sound_free_dma(hw_config->dma); - if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) - sound_free_dma (hw_config->dma2); + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + sound_free_dma(hw_config->dma2); } -void -gusintr (int irq, void *dev_id, struct pt_regs *dummy) +void gusintr(int irq, void *dev_id, struct pt_regs *dummy) { - unsigned char src; - extern int gus_timer_enabled; + unsigned char src; + extern int gus_timer_enabled; + struct address_info *hw_config=dev_id; - sti (); + sti(); #ifdef CONFIG_GUSMAX - if (have_gus_max) - ad1848_interrupt (irq, NULL, NULL); + if (have_gus_max) + adintr(irq, (void *)hw_config->slots[1], NULL); +#endif +#ifdef CONFIG_GUS16 + if (db16) + adintr(irq, (void *)hw_config->slots[3], NULL); #endif - while (1) - { - if (!(src = inb (u_IrqStatus))) - return; - - if (src & DMA_TC_IRQ) - { - guswave_dma_irq (); - } - - if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) + while (1) { + if (!(src = inb(u_IrqStatus))) + return; + + if (src & DMA_TC_IRQ) + { + guswave_dma_irq(); + } + if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) + { #ifdef CONFIG_MIDI - gus_midi_interrupt (0); + gus_midi_interrupt(0); #endif - } - - if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) - { + } + if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ)) + { #ifdef CONFIG_SEQUENCER - if (gus_timer_enabled) - { - sound_timer_interrupt (); - } - - gus_write8 (0x45, 0); /* Ack IRQ */ - gus_timer_command (4, 0x80); /* Reset IRQ flags */ - + if (gus_timer_enabled) + sound_timer_interrupt(); + gus_write8(0x45, 0); /* Ack IRQ */ + gus_timer_command(4, 0x80); /* Reset IRQ flags */ #else - gus_write8 (0x45, 0); /* Stop timers */ + gus_write8(0x45, 0); /* Stop timers */ #endif + } + if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) + gus_voice_irq(); } - - if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) - { - gus_voice_irq (); - } - } } #endif /* - * Some extra code for the 16 bit sampling option + * Some extra code for the 16 bit sampling option */ -#if defined(CONFIG_GUS16) -int -probe_gus_db16 (struct address_info *hw_config) +#ifdef CONFIG_GUS16 + +int probe_gus_db16(struct address_info *hw_config) { - return ad1848_detect (hw_config->io_base, NULL, hw_config->osp); + return ad1848_detect(hw_config->io_base, NULL, hw_config->osp); } -void -attach_gus_db16 (struct address_info *hw_config) +void attach_gus_db16(struct address_info *hw_config) { #ifdef CONFIG_GUS - gus_pcm_volume = 100; - gus_wave_volume = 90; + gus_pcm_volume = 100; + gus_wave_volume = 90; #endif - ad1848_init ("GUS 16 bit sampling", hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma, 0, - hw_config->osp); + hw_config->slots[3] = ad1848_init("GUS 16 bit sampling", hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0, + hw_config->osp); +} + +void unload_gus_db16(struct address_info *hw_config) +{ + + ad1848_unload(hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0); + sound_unload_audiodev(hw_config->slots[3]); } +#endif + + + +#ifdef MODULE + +static struct address_info config; + +/* + * Note DMA2 of -1 has the right meaning in the GUS driver as well + * as here. + */ + +int io = -1; +int irq = -1; +int dma = -1; +int dma16 = -1; /* Set this for modules that need it */ +int type = 0; /* 1 for PnP */ +int gus16 = 0; +#ifdef CONFIG_GUSMAX +static int no_wave_dma = 0;/* Set if no dma is to be used for the + wave table (GF1 chip) */ +#endif -void -unload_gus_db16 (struct address_info *hw_config) +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(dma, "i"); +MODULE_PARM(dma16, "i"); +MODULE_PARM(type, "i"); +MODULE_PARM(gus16, "i"); +#ifdef CONFIG_GUSMAX +MODULE_PARM(no_wave_dma, "i"); +#endif +#ifdef CONFIG_GUS16 +MODULE_PARM(db16, "i"); +#endif + +int init_module(void) { + printk(KERN_INFO "Gravis Ultrasound audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + if (io == -1 || dma == -1 || irq == -1) + { + printk(KERN_ERR "I/O, IRQ, and DMA are mandatory\n"); + return -EINVAL; + } + config.io_base = io; + config.irq = irq; + config.dma = dma; + config.dma2 = dma16; + config.card_subtype = type; + +#ifdef CONFIG_GUSMAX + gus_no_wave_dma = no_wave_dma; +#endif - ad1848_unload (hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma, 0); +#if defined(CONFIG_GUS16) + if (probe_gus_db16(&config) && gus16) + { + attach_gus_db16(&config); + db16 = 1; + } +#endif + if (probe_gus(&config) == 0) + return -ENODEV; + attach_gus_card(&config); + SOUND_LOCK; + return 0; } + +void cleanup_module(void) +{ +#if defined(CONFIG_GUS16) + if (db16) + unload_gus_db16(&config); +#endif + unload_gus(&config); + SOUND_LOCK_END; +} + #endif diff --git a/drivers/sound/gus_midi.c b/drivers/sound/gus_midi.c index 5bea9c00f87f..7f82d3997cca 100644 --- a/drivers/sound/gus_midi.c +++ b/drivers/sound/gus_midi.c @@ -4,9 +4,9 @@ * The low level driver for the GUS Midi Interface. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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. */ @@ -17,7 +17,8 @@ #include "gus_hw.h" -#if defined(CONFIG_GUS) && defined(CONFIG_MIDI) +#ifdef CONFIG_GUS +#ifdef CONFIG_MIDI static int midi_busy = 0, input_opened = 0; static int my_dev; @@ -27,187 +28,161 @@ static volatile unsigned char gus_midi_control; static void (*midi_input_intr) (int dev, unsigned char data); static unsigned char tmp_queue[256]; +extern int gus_pnp_flag; static volatile int qlen; static volatile unsigned char qhead, qtail; extern int gus_base, gus_irq, gus_dma; extern int *gus_osp; -static int -GUS_MIDI_STATUS (void) +static int GUS_MIDI_STATUS(void) { - return inb (u_MidiStatus); + return inb(u_MidiStatus); } -static int -gus_midi_open (int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) +static int gus_midi_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev)) { + if (midi_busy) + { +/* printk("GUS: Midi busy\n");*/ + return -EBUSY; + } + outb((MIDI_RESET), u_MidiControl); + gus_delay(); - if (midi_busy) - { - printk ("GUS: Midi busy\n"); - return -(EBUSY); - } + gus_midi_control = 0; + input_opened = 0; - outb (MIDI_RESET, u_MidiControl); - gus_delay (); + if (mode == OPEN_READ || mode == OPEN_READWRITE) + if (!gus_pnp_flag) + { + gus_midi_control |= MIDI_ENABLE_RCV; + input_opened = 1; + } + outb((gus_midi_control), u_MidiControl); /* Enable */ - gus_midi_control = 0; - input_opened = 0; + midi_busy = 1; + qlen = qhead = qtail = output_used = 0; + midi_input_intr = input; - if (mode == OPEN_READ || mode == OPEN_READWRITE) - { - gus_midi_control |= MIDI_ENABLE_RCV; - input_opened = 1; - } + return 0; +} +static int dump_to_midi(unsigned char midi_byte) +{ + unsigned long flags; + int ok = 0; - outb (gus_midi_control, u_MidiControl); /* Enable */ + output_used = 1; - midi_busy = 1; - qlen = qhead = qtail = output_used = 0; - midi_input_intr = input; + save_flags(flags); + cli(); - return 0; -} + if (GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY) + { + ok = 1; + outb((midi_byte), u_MidiData); + } + else + { + /* + * Enable Midi xmit interrupts (again) + */ + gus_midi_control |= MIDI_ENABLE_XMIT; + outb((gus_midi_control), u_MidiControl); + } -static int -dump_to_midi (unsigned char midi_byte) -{ - unsigned long flags; - int ok = 0; - - output_used = 1; - - save_flags (flags); - cli (); - - if (GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY) - { - ok = 1; - outb (midi_byte, u_MidiData); - } - else - { - /* - * Enable Midi xmit interrupts (again) - */ - gus_midi_control |= MIDI_ENABLE_XMIT; - outb (gus_midi_control, u_MidiControl); - } - - restore_flags (flags); - return ok; + restore_flags(flags); + return ok; } -static void -gus_midi_close (int dev) +static void gus_midi_close(int dev) { - /* - * Reset FIFO pointers, disable intrs - */ + /* + * Reset FIFO pointers, disable intrs + */ - outb (MIDI_RESET, u_MidiControl); - midi_busy = 0; + outb((MIDI_RESET), u_MidiControl); + midi_busy = 0; } -static int -gus_midi_out (int dev, unsigned char midi_byte) +static int gus_midi_out(int dev, unsigned char midi_byte) { + unsigned long flags; - unsigned long flags; - - /* - * Drain the local queue first - */ + /* + * Drain the local queue first + */ - save_flags (flags); - cli (); + save_flags(flags); + cli(); - while (qlen && dump_to_midi (tmp_queue[qhead])) - { - qlen--; - qhead++; - } - - restore_flags (flags); + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + restore_flags(flags); - /* - * Output the byte if the local queue is empty. - */ + /* + * Output the byte if the local queue is empty. + */ - if (!qlen) - if (dump_to_midi (midi_byte)) - return 1; /* - * OK - */ + if (!qlen) + if (dump_to_midi(midi_byte)) + return 1; /* + * OK + */ - /* - * Put to the local queue - */ + /* + * Put to the local queue + */ - if (qlen >= 256) - return 0; /* + if (qlen >= 256) + return 0; /* * Local queue full */ + save_flags(flags); + cli(); - save_flags (flags); - cli (); - - tmp_queue[qtail] = midi_byte; - qlen++; - qtail++; - - restore_flags (flags); + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; - return 1; + restore_flags(flags); + return 1; } -static int -gus_midi_start_read (int dev) +static int gus_midi_start_read(int dev) { - return 0; + return 0; } -static int -gus_midi_end_read (int dev) +static int gus_midi_end_read(int dev) { - return 0; + return 0; } -static int -gus_midi_ioctl (int dev, unsigned cmd, caddr_t arg) +static void gus_midi_kick(int dev) { - return -(EINVAL); } -static void -gus_midi_kick (int dev) +static int gus_midi_buffer_status(int dev) { -} - -static int -gus_midi_buffer_status (int dev) -{ - unsigned long flags; - - if (!output_used) - return 0; + unsigned long flags; - save_flags (flags); - cli (); + if (!output_used) + return 0; - if (qlen && dump_to_midi (tmp_queue[qhead])) - { - qlen--; - qhead++; - } + save_flags(flags); + cli(); - restore_flags (flags); - - return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY); + if (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + restore_flags(flags); + return (qlen > 0) | !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY); } #define MIDI_SYNTH_NAME "Gravis Ultrasound Midi" @@ -216,80 +191,80 @@ gus_midi_buffer_status (int dev) static struct midi_operations gus_midi_operations = { - {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS}, - &std_midi_synth, - {0}, - gus_midi_open, - gus_midi_close, - gus_midi_ioctl, - gus_midi_out, - gus_midi_start_read, - gus_midi_end_read, - gus_midi_kick, - NULL, /* + { + "Gravis UltraSound Midi", 0, 0, SNDCARD_GUS + }, + &std_midi_synth, + {0}, + gus_midi_open, + gus_midi_close, + NULL, /* ioctl */ + gus_midi_out, + gus_midi_start_read, + gus_midi_end_read, + gus_midi_kick, + NULL, /* * command */ - gus_midi_buffer_status, - NULL + gus_midi_buffer_status, + NULL }; -void -gus_midi_init (void) +void gus_midi_init(struct address_info *hw_config) { - if (num_midis >= MAX_MIDI_DEV) - { - printk ("Sound: Too many midi devices detected\n"); - return; - } + int dev = sound_alloc_mididev(); - outb (MIDI_RESET, u_MidiControl); + if (dev == -1) + { + printk(KERN_INFO "gus_midi: Too many midi devices detected\n"); + return; + } + outb((MIDI_RESET), u_MidiControl); - std_midi_synth.midi_dev = my_dev = num_midis; - midi_devs[num_midis++] = &gus_midi_operations; - return; + std_midi_synth.midi_dev = my_dev = dev; + hw_config->slots[2] = dev; + midi_devs[dev] = &gus_midi_operations; + sequencer_init(); + return; } -void -gus_midi_interrupt (int dummy) +void gus_midi_interrupt(int dummy) { - volatile unsigned char stat, data; - unsigned long flags; - int timeout = 10; + volatile unsigned char stat, data; + unsigned long flags; + int timeout = 10; - save_flags (flags); - cli (); - - while (timeout-- > 0 && (stat = GUS_MIDI_STATUS ()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY)) - { - if (stat & MIDI_RCV_FULL) - { - data = inb (u_MidiData); - if (input_opened) - midi_input_intr (my_dev, data); - } + save_flags(flags); + cli(); - if (stat & MIDI_XMIT_EMPTY) + while (timeout-- > 0 && (stat = GUS_MIDI_STATUS()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY)) { - while (qlen && dump_to_midi (tmp_queue[qhead])) - { - qlen--; - qhead++; - } - - if (!qlen) - { - /* - * Disable Midi output interrupts, since no data in the buffer - */ - gus_midi_control &= ~MIDI_ENABLE_XMIT; - outb (gus_midi_control, u_MidiControl); - outb (gus_midi_control, u_MidiControl); - } + if (stat & MIDI_RCV_FULL) + { + data = inb(u_MidiData); + if (input_opened) + midi_input_intr(my_dev, data); + } + if (stat & MIDI_XMIT_EMPTY) + { + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + if (!qlen) + { + /* + * Disable Midi output interrupts, since no data in the buffer + */ + gus_midi_control &= ~MIDI_ENABLE_XMIT; + outb((gus_midi_control), u_MidiControl); + outb((gus_midi_control), u_MidiControl); + } + } } - - } - - restore_flags (flags); + restore_flags(flags); } #endif +#endif diff --git a/drivers/sound/gus_vol.c b/drivers/sound/gus_vol.c index e9e352812e2a..3e5752d8f818 100644 --- a/drivers/sound/gus_vol.c +++ b/drivers/sound/gus_vol.c @@ -1,10 +1,11 @@ + /* * gus_vol.c - Compute volume for GUS. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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. */ @@ -33,88 +34,91 @@ extern int gus_wave_volume; * we can give a big boost to very weak voices like nylon guitar and the * basses. The normal value is 64. Strings are assigned lower values. */ -unsigned short -gus_adagio_vol (int vel, int mainv, int xpn, int voicev) + +unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev) { - int i, m, n, x; + int i, m, n, x; - /* - * A voice volume of 64 is considered neutral, so adjust the main volume if - * something other than this neutral value was assigned in the patch - * library. - */ - x = 256 + 6 * (voicev - 64); + /* + * A voice volume of 64 is considered neutral, so adjust the main volume if + * something other than this neutral value was assigned in the patch + * library. + */ + x = 256 + 6 * (voicev - 64); - /* - * Boost expression by voice volume above neutral. - */ - if (voicev > 65) - xpn += voicev - 64; - xpn += (voicev - 64) / 2; + /* + * Boost expression by voice volume above neutral. + */ + + if (voicev > 65) + xpn += voicev - 64; + xpn += (voicev - 64) / 2; - /* - * Combine multiplicative and level components. - */ - x = vel * xpn * 6 + (voicev / 4) * x; + /* + * Combine multiplicative and level components. + */ + x = vel * xpn * 6 + (voicev / 4) * x; #ifdef GUS_VOLUME - /* - * Further adjustment by installation-specific master volume control - * (default 60). - */ - x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; + /* + * Further adjustment by installation-specific master volume control + * (default 60). + */ + x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; #endif #ifdef GUS_USE_CHN_MAIN_VOLUME - /* - * Experimental support for the channel main volume - */ + /* + * Experimental support for the channel main volume + */ - mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */ - x = (x * mainv * mainv) / 16384; + mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */ + x = (x * mainv * mainv) / 16384; #endif - if (x < 2) - return (0); - else if (x >= 65535) - return ((15 << 8) | 255); - - /* - * Convert to GUS's logarithmic form with 4 bit exponent i and 8 bit - * mantissa m. - */ - n = x; - i = 7; - if (n < 128) - { - while (i > 0 && n < (1 << i)) - i--; - } - else - while (n > 255) - { - n >>= 1; - i++; - } - /* - * Mantissa is part of linear volume not expressed in exponent. (This is - * not quite like real logs -- I wonder if it's right.) - */ - m = x - (1 << i); - - /* - * Adjust mantissa to 8 bits. - */ - if (m > 0) - { - if (i > 8) - m >>= i - 8; - else if (i < 8) - m <<= 8 - i; - } - - return ((i << 8) + m); + if (x < 2) + return (0); + else if (x >= 65535) + return ((15 << 8) | 255); + + /* + * Convert to GUS's logarithmic form with 4 bit exponent i and 8 bit + * mantissa m. + */ + + n = x; + i = 7; + if (n < 128) + { + while (i > 0 && n < (1 << i)) + i--; + } + else + { + while (n > 255) + { + n >>= 1; + i++; + } + } + /* + * Mantissa is part of linear volume not expressed in exponent. (This is + * not quite like real logs -- I wonder if it's right.) + */ + m = x - (1 << i); + + /* + * Adjust mantissa to 8 bits. + */ + if (m > 0) + { + if (i > 8) + m >>= i - 8; + else if (i < 8) + m <<= 8 - i; + } + return ((i << 8) + m); } /* @@ -123,32 +127,30 @@ gus_adagio_vol (int vel, int mainv, int xpn, int voicev) * and the volume set by the mixer-device (default 60%). */ -unsigned short -gus_linear_vol (int vol, int mainvol) +unsigned short gus_linear_vol(int vol, int mainvol) { - int mixer_mainvol; + int mixer_mainvol; - if (vol <= 0) - vol = 0; - else if (vol >= 127) - vol = 127; + if (vol <= 0) + vol = 0; + else if (vol >= 127) + vol = 127; #ifdef GUS_VOLUME - mixer_mainvol = GUS_VOLUME; + mixer_mainvol = GUS_VOLUME; #else - mixer_mainvol = 100; + mixer_mainvol = 100; #endif #ifdef GUS_USE_CHN_MAIN_VOLUME - if (mainvol <= 0) - mainvol = 0; - else if (mainvol >= 127) - mainvol = 127; + if (mainvol <= 0) + mainvol = 0; + else if (mainvol >= 127) + mainvol = 127; #else - mainvol = 127; + mainvol = 127; #endif - - return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100]; + return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100]; } #endif diff --git a/drivers/sound/gus_wave.c b/drivers/sound/gus_wave.c index e21b6b6c6fb4..0144752998c7 100644 --- a/drivers/sound/gus_wave.c +++ b/drivers/sound/gus_wave.c @@ -4,22 +4,30 @@ * Driver for the Gravis UltraSound wave table synth. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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) + * Frank van de Pol : Fixed GUS MAX interrupt handling. Enabled simultanious + * usage of CS4231A codec, GUS wave and MIDI for GUS MAX. + */ + + #include - #define GUSPNP_AUTODETECT #include "sound_config.h" #include #include "gus_hw.h" -#if defined(CONFIG_GUS) +#ifdef CONFIG_GUS + +#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024)) #define MAX_SAMPLE 150 #define MAX_PATCH 256 @@ -27,46 +35,49 @@ #define NOT_SAMPLE 0xffff struct voice_info - { - unsigned long orig_freq; - unsigned long current_freq; - unsigned long mode; - int bender; - int bender_range; - int panning; - int midi_volume; - unsigned int initial_volume; - unsigned int current_volume; - int loop_irq_mode, loop_irq_parm; +{ + unsigned long orig_freq; + unsigned long current_freq; + unsigned long mode; + int fixed_pitch; + int bender; + int bender_range; + int panning; + int midi_volume; + unsigned int initial_volume; + unsigned int current_volume; + int loop_irq_mode, loop_irq_parm; #define LMODE_FINISH 1 #define LMODE_PCM 2 #define LMODE_PCM_STOP 3 - int volume_irq_mode, volume_irq_parm; + int volume_irq_mode, volume_irq_parm; #define VMODE_HALT 1 #define VMODE_ENVELOPE 2 #define VMODE_START_NOTE 3 - int env_phase; - unsigned char env_rate[6]; - unsigned char env_offset[6]; + int env_phase; + unsigned char env_rate[6]; + unsigned char env_offset[6]; - /* - * Volume computation parameters for gus_adagio_vol() - */ - int main_vol, expression_vol, patch_vol; + /* + * Volume computation parameters for gus_adagio_vol() + */ + int main_vol, expression_vol, patch_vol; - /* Variables for "Ultraclick" removal */ - int dev_pending, note_pending, volume_pending, sample_pending; - char kill_pending; - long offset_pending; + /* Variables for "Ultraclick" removal */ + int dev_pending, note_pending, volume_pending, + sample_pending; + char kill_pending; + long offset_pending; - }; +}; static struct voice_alloc_info *voice_alloc; - +static struct address_info *gus_hw_config; extern int gus_base; extern int gus_irq, gus_dma; extern int gus_pnp_flag; +extern int gus_no_wave_dma; static int gus_dma2 = -1; static int dual_dma_mode = 0; static long gus_mem_size = 0; @@ -81,6 +92,7 @@ static int recording_active = 0; static int only_read_access = 0; static int only_8_bits = 0; +int iw_mode = 0; int gus_wave_volume = 60; int gus_pcm_volume = 80; int have_gus_max = 0; @@ -93,6 +105,7 @@ int gus_timer_enabled = 0; * Current version of this driver doesn't allow synth and PCM functions * at the same time. The active_device specifies the active driver */ + static int active_device = 0; #define GUS_DEV_WAVE 1 /* Wave table synth */ @@ -102,15 +115,16 @@ static int active_device = 0; static int gus_audio_speed; static int gus_audio_channels; static int gus_audio_bits; +static int gus_audio_bsize; +static char bounce_buf[8 * 1024]; /* Must match value set to max_fragment */ -static wait_handle *dram_sleeper = NULL; -static volatile struct snd_wait dram_sleep_flag = -{0}; +static struct wait_queue *dram_sleeper = NULL; /* * Variables and buffers for PCM output */ -#define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /* Don't change */ + +#define MAX_PCM_BUFFERS (128*MAX_REALTIME_FACTOR) /* Don't change */ static int pcm_bsize, pcm_nblk, pcm_banksize; static int pcm_datasize[MAX_PCM_BUFFERS]; @@ -126,32 +140,33 @@ static int pcm_current_intrflag; extern int *gus_osp; -struct voice_info voices[32]; +static struct voice_info voices[32]; static int freq_div_table[] = { - 44100, /* 14 */ - 41160, /* 15 */ - 38587, /* 16 */ - 36317, /* 17 */ - 34300, /* 18 */ - 32494, /* 19 */ - 30870, /* 20 */ - 29400, /* 21 */ - 28063, /* 22 */ - 26843, /* 23 */ - 25725, /* 24 */ - 24696, /* 25 */ - 23746, /* 26 */ - 22866, /* 27 */ - 22050, /* 28 */ - 21289, /* 29 */ - 20580, /* 30 */ - 19916, /* 31 */ - 19293 /* 32 */ + 44100, + 44100, /* 14 */ + 41160, /* 15 */ + 38587, /* 16 */ + 36317, /* 17 */ + 34300, /* 18 */ + 32494, /* 19 */ + 30870, /* 20 */ + 29400, /* 21 */ + 28063, /* 22 */ + 26843, /* 23 */ + 25725, /* 24 */ + 24696, /* 25 */ + 23746, /* 26 */ + 22866, /* 27 */ + 22050, /* 28 */ + 21289, /* 29 */ + 20580, /* 30 */ + 19916, /* 31 */ + 19293 /* 32 */ }; -static struct patch_info *samples; +static struct patch_info *samples = NULL; static long sample_ptrs[MAX_SAMPLE + 1]; static int sample_map[32]; static int free_sample; @@ -161,3535 +176,3270 @@ static int mixer_type = 0; static int patch_table[MAX_PATCH]; static int patch_map[32]; -static struct synth_info gus_info = -{"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH}; +static struct synth_info gus_info = { + "Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, + 0, 16, 0, MAX_PATCH +}; -static void gus_poke (long addr, unsigned char data); -static void compute_and_set_volume (int voice, int volume, int ramp_time); -extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev); -extern unsigned short gus_linear_vol (int vol, int mainvol); -static void compute_volume (int voice, int volume); -static void do_volume_irq (int voice); -static void set_input_volumes (void); -static void gus_tmr_install (int io_base); +static void gus_poke(long addr, unsigned char data); +static void compute_and_set_volume(int voice, int volume, int ramp_time); +extern unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev); +extern unsigned short gus_linear_vol(int vol, int mainvol); +static void compute_volume(int voice, int volume); +static void do_volume_irq(int voice); +static void set_input_volumes(void); +static void gus_tmr_install(int io_base); #define INSTANT_RAMP -1 /* Instant change. No ramping */ #define FAST_RAMP 0 /* Fastest possible ramp */ -static void -reset_sample_memory (void) +static void reset_sample_memory(void) { - int i; + int i; - for (i = 0; i <= MAX_SAMPLE; i++) - sample_ptrs[i] = -1; - for (i = 0; i < 32; i++) - sample_map[i] = -1; - for (i = 0; i < 32; i++) - patch_map[i] = -1; + for (i = 0; i <= MAX_SAMPLE; i++) + sample_ptrs[i] = -1; + for (i = 0; i < 32; i++) + sample_map[i] = -1; + for (i = 0; i < 32; i++) + patch_map[i] = -1; - gus_poke (0, 0); /* Put a silent sample to the beginning */ - gus_poke (1, 0); - free_mem_ptr = 2; + gus_poke(0, 0); /* Put a silent sample to the beginning */ + gus_poke(1, 0); + free_mem_ptr = 2; - free_sample = 0; + free_sample = 0; - for (i = 0; i < MAX_PATCH; i++) - patch_table[i] = NOT_SAMPLE; + for (i = 0; i < MAX_PATCH; i++) + patch_table[i] = NOT_SAMPLE; } -void -gus_delay (void) +void gus_delay(void) { - int i; + int i; - for (i = 0; i < 7; i++) - inb (u_DRAMIO); + for (i = 0; i < 7; i++) + inb(u_DRAMIO); } -static void -gus_poke (long addr, unsigned char data) +static void gus_poke(long addr, unsigned char data) { /* Writes a byte to the DRAM */ - unsigned long flags; + unsigned long flags; - save_flags (flags); - cli (); - outb (0x43, u_Command); - outb (addr & 0xff, u_DataLo); - outb ((addr >> 8) & 0xff, u_DataHi); + save_flags(flags); + cli(); + outb((0x43), u_Command); + outb((addr & 0xff), u_DataLo); + outb(((addr >> 8) & 0xff), u_DataHi); - outb (0x44, u_Command); - outb ((addr >> 16) & 0xff, u_DataHi); - outb (data, u_DRAMIO); - restore_flags (flags); + outb((0x44), u_Command); + outb(((addr >> 16) & 0xff), u_DataHi); + outb((data), u_DRAMIO); + restore_flags(flags); } -static unsigned char -gus_peek (long addr) +static unsigned char gus_peek(long addr) { /* Reads a byte from the DRAM */ - unsigned long flags; - unsigned char tmp; + unsigned long flags; + unsigned char tmp; - save_flags (flags); - cli (); - outb (0x43, u_Command); - outb (addr & 0xff, u_DataLo); - outb ((addr >> 8) & 0xff, u_DataHi); + save_flags(flags); + cli(); + outb((0x43), u_Command); + outb((addr & 0xff), u_DataLo); + outb(((addr >> 8) & 0xff), u_DataHi); - outb (0x44, u_Command); - outb ((addr >> 16) & 0xff, u_DataHi); - tmp = inb (u_DRAMIO); - restore_flags (flags); + outb((0x44), u_Command); + outb(((addr >> 16) & 0xff), u_DataHi); + tmp = inb(u_DRAMIO); + restore_flags(flags); - return tmp; + return tmp; } -void -gus_write8 (int reg, unsigned int data) +void gus_write8(int reg, unsigned int data) { /* Writes to an indirect register (8 bit) */ - unsigned long flags; + unsigned long flags; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - outb (reg, u_Command); - outb ((unsigned char) (data & 0xff), u_DataHi); + outb((reg), u_Command); + outb(((unsigned char) (data & 0xff)), u_DataHi); - restore_flags (flags); + restore_flags(flags); } -unsigned char -gus_read8 (int reg) -{ /* Reads from an indirect register (8 bit). Offset 0x80. */ - unsigned long flags; - unsigned char val; +static unsigned char gus_read8(int reg) +{ + /* Reads from an indirect register (8 bit). Offset 0x80. */ + unsigned long flags; + unsigned char val; - save_flags (flags); - cli (); - outb (reg | 0x80, u_Command); - val = inb (u_DataHi); - restore_flags (flags); + save_flags(flags); + cli(); + outb((reg | 0x80), u_Command); + val = inb(u_DataHi); + restore_flags(flags); - return val; + return val; } -unsigned char -gus_look8 (int reg) -{ /* Reads from an indirect register (8 bit). No additional offset. */ - unsigned long flags; - unsigned char val; +static unsigned char gus_look8(int reg) +{ + /* Reads from an indirect register (8 bit). No additional offset. */ + unsigned long flags; + unsigned char val; - save_flags (flags); - cli (); - outb (reg, u_Command); - val = inb (u_DataHi); - restore_flags (flags); + save_flags(flags); + cli(); + outb((reg), u_Command); + val = inb(u_DataHi); + restore_flags(flags); - return val; + return val; } -void -gus_write16 (int reg, unsigned int data) -{ /* Writes to an indirect register (16 bit) */ - unsigned long flags; +static void gus_write16(int reg, unsigned int data) +{ + /* Writes to an indirect register (16 bit) */ + unsigned long flags; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - outb (reg, u_Command); + outb((reg), u_Command); - outb ((unsigned char) (data & 0xff), u_DataLo); - outb ((unsigned char) ((data >> 8) & 0xff), u_DataHi); + outb(((unsigned char) (data & 0xff)), u_DataLo); + outb(((unsigned char) ((data >> 8) & 0xff)), u_DataHi); - restore_flags (flags); + restore_flags(flags); } -unsigned short -gus_read16 (int reg) -{ /* Reads from an indirect register (16 bit). Offset 0x80. */ - unsigned long flags; - unsigned char hi, lo; +static unsigned short gus_read16(int reg) +{ + /* Reads from an indirect register (16 bit). Offset 0x80. */ + unsigned long flags; + unsigned char hi, lo; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - outb (reg | 0x80, u_Command); + outb((reg | 0x80), u_Command); - lo = inb (u_DataLo); - hi = inb (u_DataHi); + lo = inb(u_DataLo); + hi = inb(u_DataHi); - restore_flags (flags); + restore_flags(flags); - return ((hi << 8) & 0xff00) | lo; + return ((hi << 8) & 0xff00) | lo; } -unsigned short -gus_look16 (int reg) -{ /* Reads from an indirect register (16 bit). No additional offset. */ - unsigned long flags; - unsigned char hi, lo; +static unsigned short gus_look16(int reg) +{ + /* Reads from an indirect register (16 bit). No additional offset. */ + unsigned long flags; + unsigned char hi, lo; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - outb (reg, u_Command); + outb((reg), u_Command); - lo = inb (u_DataLo); - hi = inb (u_DataHi); + lo = inb(u_DataLo); + hi = inb(u_DataHi); - restore_flags (flags); + restore_flags(flags); - return ((hi << 8) & 0xff00) | lo; + return ((hi << 8) & 0xff00) | lo; } -void -gus_write_addr (int reg, unsigned long address, int frac, int is16bit) -{ /* Writes an 24 bit memory address */ - unsigned long hold_address; - unsigned long flags; - - save_flags (flags); - cli (); - if (is16bit) - { - /* - * Special processing required for 16 bit patches - */ - - hold_address = address; - address = address >> 1; - address &= 0x0001ffffL; - address |= (hold_address & 0x000c0000L); - } +static void gus_write_addr(int reg, unsigned long address, int frac, int is16bit) +{ + /* Writes an 24 bit memory address */ + unsigned long hold_address; + unsigned long flags; - gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); - gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff) - + (frac << 5)); - /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */ - gus_delay (); - gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff)); - gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff) - + (frac << 5)); - restore_flags (flags); + save_flags(flags); + cli(); + if (is16bit) + { + if (iw_mode) + { + /* Interwave spesific address translations */ + address >>= 1; + } + else + { + /* + * Special processing required for 16 bit patches + */ + + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + } + gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) + + (frac << 5)); + /* Could writing twice fix problems with GUS_VOICE_POS()? Let's try. */ + gus_delay(); + gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff)); + gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff) + + (frac << 5)); + restore_flags(flags); } -static void -gus_select_voice (int voice) +static void gus_select_voice(int voice) { - if (voice < 0 || voice > 31) - return; - - outb (voice, u_Voice); + if (voice < 0 || voice > 31) + return; + outb((voice), u_Voice); } -static void -gus_select_max_voices (int nvoices) +static void gus_select_max_voices(int nvoices) { - if (nvoices < 14) - nvoices = 14; - if (nvoices > 32) - nvoices = 32; - - voice_alloc->max_voice = nr_voices = nvoices; + if (iw_mode) + nvoices = 32; + if (nvoices < 14) + nvoices = 14; + if (nvoices > 32) + nvoices = 32; - gus_write8 (0x0e, (nvoices - 1) | 0xc0); + voice_alloc->max_voice = nr_voices = nvoices; + gus_write8(0x0e, (nvoices - 1) | 0xc0); } -static void -gus_voice_on (unsigned int mode) +static void gus_voice_on(unsigned int mode) { - gus_write8 (0x00, (unsigned char) (mode & 0xfc)); - gus_delay (); - gus_write8 (0x00, (unsigned char) (mode & 0xfc)); + gus_write8(0x00, (unsigned char) (mode & 0xfc)); + gus_delay(); + gus_write8(0x00, (unsigned char) (mode & 0xfc)); } -static void -gus_voice_off (void) +static void gus_voice_off(void) { - gus_write8 (0x00, gus_read8 (0x00) | 0x03); + gus_write8(0x00, gus_read8(0x00) | 0x03); } -static void -gus_voice_mode (unsigned int m) +static void gus_voice_mode(unsigned int m) { - unsigned char mode = (unsigned char) (m & 0xff); + unsigned char mode = (unsigned char) (m & 0xff); - gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | - (mode & 0xfc)); /* Don't touch last two bits */ - gus_delay (); - gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); + gus_write8(0x00, (gus_read8(0x00) & 0x03) | + (mode & 0xfc)); /* Don't touch last two bits */ + gus_delay(); + gus_write8(0x00, (gus_read8(0x00) & 0x03) | (mode & 0xfc)); } -static void -gus_voice_freq (unsigned long freq) +static void gus_voice_freq(unsigned long freq) { - unsigned long divisor = freq_div_table[nr_voices - 14]; - unsigned short fc; + unsigned long divisor = freq_div_table[nr_voices - 14]; + unsigned short fc; - fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); - fc = fc << 1; + /* Interwave plays at 44100 Hz with any number of voices */ + if (iw_mode) + fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100); + else + fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor); + fc = fc << 1; - gus_write16 (0x01, fc); + gus_write16(0x01, fc); } -static void -gus_voice_volume (unsigned int vol) +static void gus_voice_volume(unsigned int vol) { - gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */ - gus_write16 (0x09, (unsigned short) (vol << 4)); + gus_write8(0x0d, 0x03); /* Stop ramp before setting volume */ + gus_write16(0x09, (unsigned short) (vol << 4)); } -static void -gus_voice_balance (unsigned int balance) +static void gus_voice_balance(unsigned int balance) { - gus_write8 (0x0c, (unsigned char) (balance & 0xff)); + gus_write8(0x0c, (unsigned char) (balance & 0xff)); } -static void -gus_ramp_range (unsigned int low, unsigned int high) +static void gus_ramp_range(unsigned int low, unsigned int high) { - gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff)); - gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff)); + gus_write8(0x07, (unsigned char) ((low >> 4) & 0xff)); + gus_write8(0x08, (unsigned char) ((high >> 4) & 0xff)); } -static void -gus_ramp_rate (unsigned int scale, unsigned int rate) +static void gus_ramp_rate(unsigned int scale, unsigned int rate) { - gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); + gus_write8(0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f))); } -static void -gus_rampon (unsigned int m) +static void gus_rampon(unsigned int m) { - unsigned char mode = (unsigned char) (m & 0xff); + unsigned char mode = (unsigned char) (m & 0xff); - gus_write8 (0x0d, mode & 0xfc); - gus_delay (); - gus_write8 (0x0d, mode & 0xfc); + gus_write8(0x0d, mode & 0xfc); + gus_delay(); + gus_write8(0x0d, mode & 0xfc); } -static void -gus_ramp_mode (unsigned int m) +static void gus_ramp_mode(unsigned int m) { - unsigned char mode = (unsigned char) (m & 0xff); + unsigned char mode = (unsigned char) (m & 0xff); - gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | - (mode & 0xfc)); /* Leave the last 2 bits alone */ - gus_delay (); - gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); + gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | + (mode & 0xfc)); /* Leave the last 2 bits alone */ + gus_delay(); + gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | (mode & 0xfc)); } -static void -gus_rampoff (void) +static void gus_rampoff(void) { - gus_write8 (0x0d, 0x03); + gus_write8(0x0d, 0x03); } -static void -gus_set_voice_pos (int voice, long position) +static void gus_set_voice_pos(int voice, long position) { - int sample_no; + int sample_no; - if ((sample_no = sample_map[voice]) != -1) - if (position < samples[sample_no].len) - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - voices[voice].offset_pending = position; - else - gus_write_addr (0x0a, sample_ptrs[sample_no] + position, 0, - samples[sample_no].mode & WAVE_16_BITS); + if ((sample_no = sample_map[voice]) != -1) { + if (position < samples[sample_no].len) { + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + voices[voice].offset_pending = position; + else + gus_write_addr(0x0a, sample_ptrs[sample_no] + position, 0, + samples[sample_no].mode & WAVE_16_BITS); + } + } } -static void -gus_voice_init (int voice) +static void gus_voice_init(int voice) { - unsigned long flags; + unsigned long flags; - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_voice_volume (0); - gus_voice_off (); - gus_write_addr (0x0a, 0, 0, 0); /* Set current position to 0 */ - gus_write8 (0x00, 0x03); /* Voice off */ - gus_write8 (0x0d, 0x03); /* Ramping off */ - voice_alloc->map[voice] = 0; - voice_alloc->alloc_times[voice] = 0; - restore_flags (flags); + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_volume(0); + gus_voice_off(); + gus_write_addr(0x0a, 0, 0, 0); /* Set current position to 0 */ + gus_write8(0x00, 0x03); /* Voice off */ + gus_write8(0x0d, 0x03); /* Ramping off */ + voice_alloc->map[voice] = 0; + voice_alloc->alloc_times[voice] = 0; + restore_flags(flags); } -static void -gus_voice_init2 (int voice) +static void gus_voice_init2(int voice) { - voices[voice].panning = 0; - voices[voice].mode = 0; - voices[voice].orig_freq = 20000; - voices[voice].current_freq = 20000; - voices[voice].bender = 0; - voices[voice].bender_range = 200; - voices[voice].initial_volume = 0; - voices[voice].current_volume = 0; - voices[voice].loop_irq_mode = 0; - voices[voice].loop_irq_parm = 0; - voices[voice].volume_irq_mode = 0; - voices[voice].volume_irq_parm = 0; - voices[voice].env_phase = 0; - voices[voice].main_vol = 127; - voices[voice].patch_vol = 127; - voices[voice].expression_vol = 127; - voices[voice].sample_pending = -1; + voices[voice].panning = 0; + voices[voice].mode = 0; + voices[voice].orig_freq = 20000; + voices[voice].current_freq = 20000; + voices[voice].bender = 0; + voices[voice].bender_range = 200; + voices[voice].initial_volume = 0; + voices[voice].current_volume = 0; + voices[voice].loop_irq_mode = 0; + voices[voice].loop_irq_parm = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].volume_irq_parm = 0; + voices[voice].env_phase = 0; + voices[voice].main_vol = 127; + voices[voice].patch_vol = 127; + voices[voice].expression_vol = 127; + voices[voice].sample_pending = -1; + voices[voice].fixed_pitch = 0; } -static void -step_envelope (int voice) +static void step_envelope(int voice) { - unsigned vol, prev_vol, phase; - unsigned char rate; - long int flags; + unsigned vol, prev_vol, phase; + unsigned char rate; + long int flags; + + if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) + { + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_rampoff(); + restore_flags(flags); + return; + /* + * Sustain phase begins. Continue envelope after receiving note off. + */ + } + if (voices[voice].env_phase >= 5) + { + /* Envelope finished. Shoot the voice down */ + gus_voice_init(voice); + return; + } + prev_vol = voices[voice].current_volume; + phase = ++voices[voice].env_phase; + compute_volume(voice, voices[voice].midi_volume); + vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; + rate = voices[voice].env_rate[phase]; + + save_flags(flags); + cli(); + gus_select_voice(voice); + + gus_voice_volume(prev_vol); + - if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) - { - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_rampoff (); - restore_flags (flags); - return; - /* - * Sustain phase begins. Continue envelope after receiving note off. - */ - } + gus_write8(0x06, rate); /* Ramping rate */ - if (voices[voice].env_phase >= 5) - { /* Envelope finished. Shoot the voice down */ - gus_voice_init (voice); - return; - } + voices[voice].volume_irq_mode = VMODE_ENVELOPE; - prev_vol = voices[voice].current_volume; - phase = ++voices[voice].env_phase; - compute_volume (voice, voices[voice].midi_volume); - vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255; - rate = voices[voice].env_rate[phase]; - - save_flags (flags); - cli (); - gus_select_voice (voice); - - gus_voice_volume (prev_vol); - - - gus_write8 (0x06, rate); /* Ramping rate */ - - voices[voice].volume_irq_mode = VMODE_ENVELOPE; - - if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ - { - restore_flags (flags); - step_envelope (voice); /* Continue the envelope on the next step */ - return; - } - - if (vol > prev_vol) - { - if (vol >= (4096 - 64)) - vol = 4096 - 65; - gus_ramp_range (0, vol); - gus_rampon (0x20); /* Increasing volume, with IRQ */ - } - else - { - if (vol <= 64) - vol = 65; - gus_ramp_range (vol, 4030); - gus_rampon (0x60); /* Decreasing volume, with IRQ */ - } - voices[voice].current_volume = vol; - restore_flags (flags); -} - -static void -init_envelope (int voice) -{ - voices[voice].env_phase = -1; - voices[voice].current_volume = 64; - - step_envelope (voice); -} - -static void -start_release (int voice, long int flags) -{ - if (gus_read8 (0x00) & 0x03) - return; /* Voice already stopped */ - - voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ - - voices[voice].current_volume = - voices[voice].initial_volume = - gus_read16 (0x09) >> 4; /* Get current volume */ - - voices[voice].mode &= ~WAVE_SUSTAIN_ON; - gus_rampoff (); - restore_flags (flags); - step_envelope (voice); + if (((vol - prev_vol) / 64) == 0) /* No significant volume change */ + { + restore_flags(flags); + step_envelope(voice); /* Continue the envelope on the next step */ + return; + } + if (vol > prev_vol) + { + if (vol >= (4096 - 64)) + vol = 4096 - 65; + gus_ramp_range(0, vol); + gus_rampon(0x20); /* Increasing volume, with IRQ */ + } + else + { + if (vol <= 64) + vol = 65; + gus_ramp_range(vol, 4030); + gus_rampon(0x60); /* Decreasing volume, with IRQ */ + } + voices[voice].current_volume = vol; + restore_flags(flags); } -static void -gus_voice_fade (int voice) +static void init_envelope(int voice) { - int instr_no = sample_map[voice], is16bits; - long int flags; - - save_flags (flags); - cli (); - gus_select_voice (voice); + voices[voice].env_phase = -1; + voices[voice].current_volume = 64; - if (instr_no < 0 || instr_no > MAX_SAMPLE) - { - gus_write8 (0x00, 0x03); /* Hard stop */ - voice_alloc->map[voice] = 0; - restore_flags (flags); - return; - } + step_envelope(voice); +} - is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */ +static void start_release(int voice, long int flags) +{ + if (gus_read8(0x00) & 0x03) + return; /* Voice already stopped */ - if (voices[voice].mode & WAVE_ENVELOPES) - { - start_release (voice, flags); - return; - } + voices[voice].env_phase = 2; /* Will be incremented by step_envelope */ - /* - * Ramp the volume down but not too quickly. - */ - if ((int) (gus_read16 (0x09) >> 4) < 100) /* Get current volume */ - { - gus_voice_off (); - gus_rampoff (); - gus_voice_init (voice); - return; - } + voices[voice].current_volume = voices[voice].initial_volume = + gus_read16(0x09) >> 4; /* Get current volume */ - gus_ramp_range (65, 4030); - gus_ramp_rate (2, 4); - gus_rampon (0x40 | 0x20); /* Down, once, with IRQ */ - voices[voice].volume_irq_mode = VMODE_HALT; - restore_flags (flags); + voices[voice].mode &= ~WAVE_SUSTAIN_ON; + gus_rampoff(); + restore_flags(flags); + step_envelope(voice); } -static void -gus_reset (void) +static void gus_voice_fade(int voice) { - int i; + int instr_no = sample_map[voice], is16bits; + long int flags; - gus_select_max_voices (24); - volume_base = 3071; - volume_scale = 4; - volume_method = VOL_METHOD_ADAGIO; + save_flags(flags); + cli(); + gus_select_voice(voice); + + if (instr_no < 0 || instr_no > MAX_SAMPLE) + { + gus_write8(0x00, 0x03); /* Hard stop */ + voice_alloc->map[voice] = 0; + restore_flags(flags); + return; + } + is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */ - for (i = 0; i < 32; i++) - { - gus_voice_init (i); /* Turn voice off */ - gus_voice_init2 (i); - } + if (voices[voice].mode & WAVE_ENVELOPES) + { + start_release(voice, flags); + restore_flags(flags); + return; + } + /* + * Ramp the volume down but not too quickly. + */ + if ((int) (gus_read16(0x09) >> 4) < 100) /* Get current volume */ + { + gus_voice_off(); + gus_rampoff(); + gus_voice_init(voice); + restore_flags(flags); + return; + } + gus_ramp_range(65, 4030); + gus_ramp_rate(2, 4); + gus_rampon(0x40 | 0x20); /* Down, once, with IRQ */ + voices[voice].volume_irq_mode = VMODE_HALT; + restore_flags(flags); } -static void -gus_initialize (void) +static void gus_reset(void) { - unsigned long flags; - unsigned char dma_image, irq_image, tmp; + int i; - static unsigned char gus_irq_map[16] = - {0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7}; + gus_select_max_voices(24); + volume_base = 3071; + volume_scale = 4; + volume_method = VOL_METHOD_ADAGIO; - static unsigned char gus_dma_map[8] = - {0, 1, 0, 2, 0, 3, 4, 5}; + for (i = 0; i < 32; i++) + { + gus_voice_init(i); /* Turn voice off */ + gus_voice_init2(i); + } +} - save_flags (flags); - cli (); - gus_write8 (0x4c, 0); /* Reset GF1 */ - gus_delay (); - gus_delay (); +static void gus_initialize(void) +{ + unsigned long flags; + unsigned char dma_image, irq_image, tmp; - gus_write8 (0x4c, 1); /* Release Reset */ - gus_delay (); - gus_delay (); + static unsigned char gus_irq_map[16] = { + 0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7 + }; - /* - * Clear all interrupts - */ + static unsigned char gus_dma_map[8] = { + 0, 1, 0, 2, 0, 3, 4, 5 + }; - gus_write8 (0x41, 0); /* DMA control */ - gus_write8 (0x45, 0); /* Timer control */ - gus_write8 (0x49, 0); /* Sample control */ + save_flags(flags); + cli(); + gus_write8(0x4c, 0); /* Reset GF1 */ + gus_delay(); + gus_delay(); - gus_select_max_voices (24); + gus_write8(0x4c, 1); /* Release Reset */ + gus_delay(); + gus_delay(); - inb (u_Status); /* Touch the status register */ + /* + * Clear all interrupts + */ - gus_look8 (0x41); /* Clear any pending DMA IRQs */ - gus_look8 (0x49); /* Clear any pending sample IRQs */ - gus_read8 (0x0f); /* Clear pending IRQs */ + gus_write8(0x41, 0); /* DMA control */ + gus_write8(0x45, 0); /* Timer control */ + gus_write8(0x49, 0); /* Sample control */ - gus_reset (); /* Resets all voices */ + gus_select_max_voices(24); - gus_look8 (0x41); /* Clear any pending DMA IRQs */ - gus_look8 (0x49); /* Clear any pending sample IRQs */ - gus_read8 (0x0f); /* Clear pending IRQs */ + inb(u_Status); /* Touch the status register */ - gus_write8 (0x4c, 7); /* Master reset | DAC enable | IRQ enable */ + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ - /* - * Set up for Digital ASIC - */ + gus_reset(); /* Resets all voices */ - outb (0x05, gus_base + 0x0f); + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ - mix_image |= 0x02; /* Disable line out (for a moment) */ - outb (mix_image, u_Mixer); + gus_write8(0x4c, 7); /* Master reset | DAC enable | IRQ enable */ - outb (0x00, u_IRQDMAControl); + /* + * Set up for Digital ASIC + */ - outb (0x00, gus_base + 0x0f); + outb((0x05), gus_base + 0x0f); - /* - * Now set up the DMA and IRQ interface - * - * The GUS supports two IRQs and two DMAs. - * - * Just one DMA channel is used. This prevents simultaneous ADC and DAC. - * Adding this support requires significant changes to the dmabuf.c, dsp.c - * and audio.c also. - */ + mix_image |= 0x02; /* Disable line out (for a moment) */ + outb((mix_image), u_Mixer); - irq_image = 0; - tmp = gus_irq_map[gus_irq]; - if (!gus_pnp_flag && !tmp) - printk ("Warning! GUS IRQ not selected\n"); - irq_image |= tmp; - irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ + outb((0x00), u_IRQDMAControl); - dual_dma_mode = 1; - if (gus_dma2 == gus_dma || gus_dma2 == -1) - { - dual_dma_mode = 0; - dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */ + outb((0x00), gus_base + 0x0f); - tmp = gus_dma_map[gus_dma]; - if (!tmp) - printk ("Warning! GUS DMA not selected\n"); + /* + * Now set up the DMA and IRQ interface + * + * The GUS supports two IRQs and two DMAs. + * + * Just one DMA channel is used. This prevents simultaneous ADC and DAC. + * Adding this support requires significant changes to the dmabuf.c, dsp.c + * and audio.c also. + */ - dma_image |= tmp; - } - else - /* Setup dual DMA channel mode for GUS MAX */ - { - dma_image = gus_dma_map[gus_dma]; - if (!dma_image) - printk ("Warning! GUS DMA not selected\n"); + irq_image = 0; + tmp = gus_irq_map[gus_irq]; + if (!gus_pnp_flag && !tmp) + printk(KERN_WARNING "Warning! GUS IRQ not selected\n"); + irq_image |= tmp; + irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ - tmp = gus_dma_map[gus_dma2] << 3; - if (!tmp) + dual_dma_mode = 1; + if (gus_dma2 == gus_dma || gus_dma2 == -1) { - printk ("Warning! Invalid GUS MAX DMA\n"); - tmp = 0x40; /* Combine DMA channels */ - dual_dma_mode = 0; - } + dual_dma_mode = 0; + dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */ - dma_image |= tmp; - } + tmp = gus_dma_map[gus_dma]; + if (!tmp) + printk(KERN_WARNING "Warning! GUS DMA not selected\n"); + + dma_image |= tmp; + } + else + { + /* Setup dual DMA channel mode for GUS MAX */ + + dma_image = gus_dma_map[gus_dma]; + if (!dma_image) + printk(KERN_WARNING "Warning! GUS DMA not selected\n"); + + tmp = gus_dma_map[gus_dma2] << 3; + if (!tmp) + { + printk(KERN_WARNING "Warning! Invalid GUS MAX DMA\n"); + tmp = 0x40; /* Combine DMA channels */ + dual_dma_mode = 0; + } + dma_image |= tmp; + } - /* - * For some reason the IRQ and DMA addresses must be written twice - */ + /* + * For some reason the IRQ and DMA addresses must be written twice + */ - /* - * Doing it first time - */ + /* + * Doing it first time + */ - outb (mix_image, u_Mixer); /* Select DMA control */ - outb (dma_image | 0x80, u_IRQDMAControl); /* Set DMA address */ + outb((mix_image), u_Mixer); /* Select DMA control */ + outb((dma_image | 0x80), u_IRQDMAControl); /* Set DMA address */ - outb (mix_image | 0x40, u_Mixer); /* Select IRQ control */ - outb (irq_image, u_IRQDMAControl); /* Set IRQ address */ + outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ + outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ - /* - * Doing it second time - */ + /* + * Doing it second time + */ - outb (mix_image, u_Mixer); /* Select DMA control */ - outb (dma_image, u_IRQDMAControl); /* Set DMA address */ + outb((mix_image), u_Mixer); /* Select DMA control */ + outb((dma_image), u_IRQDMAControl); /* Set DMA address */ - outb (mix_image | 0x40, u_Mixer); /* Select IRQ control */ - outb (irq_image, u_IRQDMAControl); /* Set IRQ address */ + outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */ + outb((irq_image), u_IRQDMAControl); /* Set IRQ address */ - gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ + gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ - mix_image &= ~0x02; /* Enable line out */ - mix_image |= 0x08; /* Enable IRQ */ - outb (mix_image, u_Mixer); /* - * Turn mixer channels on - * Note! Mic in is left off. - */ + mix_image &= ~0x02; /* Enable line out */ + mix_image |= 0x08; /* Enable IRQ */ + outb((mix_image), u_Mixer); /* + * Turn mixer channels on + * Note! Mic in is left off. + */ - gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ + gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ - gusintr (gus_irq, NULL, NULL); /* Serve pending interrupts */ + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ - inb (u_Status); /* Touch the status register */ + inb(u_Status); /* Touch the status register */ - gus_look8 (0x41); /* Clear any pending DMA IRQs */ - gus_look8 (0x49); /* Clear any pending sample IRQs */ + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ - gus_read8 (0x0f); /* Clear pending IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ - restore_flags (flags); + if (iw_mode) + gus_write8(0x19, gus_read8(0x19) | 0x01); + restore_flags(flags); } -static void -pnp_mem_init (void) + +static void pnp_mem_init(void) { #include "iwmem.h" #define CHUNK_SIZE (256*1024) #define BANK_SIZE (4*1024*1024) #define CHUNKS_PER_BANK (BANK_SIZE/CHUNK_SIZE) - int bank, chunk, addr, total = 0; - int bank_sizes[4]; - int i, j, bits = -1, nbanks = 0; - -/* - * This routine determines what kind of RAM is installed in each of the four - * SIMM banks and configures the DRAM address decode logic accordingly. - */ - -/* - * Place the chip into enhanced mode - */ - gus_write8 (0x19, gus_read8 (0x19) | 0x01); - gus_write8 (0x53, gus_look8 (0x53) & ~0x02); /* Select DRAM I/O access */ + int bank, chunk, addr, total = 0; + int bank_sizes[4]; + int i, j, bits = -1, nbanks = 0; -/* - * Set memory configuration to 4 DRAM banks of 4M in each (16M total). - */ + /* + * This routine determines what kind of RAM is installed in each of the four + * SIMM banks and configures the DRAM address decode logic accordingly. + */ - gus_write16 (0x52, (gus_look16 (0x52) & 0xfff0) | 0x000c); + /* + * Place the chip into enhanced mode + */ + gus_write8(0x19, gus_read8(0x19) | 0x01); + gus_write8(0x53, gus_look8(0x53) & ~0x02); /* Select DRAM I/O access */ -/* - * Perform the DRAM size detection for each bank individually. - */ - for (bank = 0; bank < 4; bank++) - { - int size = 0; + /* + * Set memory configuration to 4 DRAM banks of 4M in each (16M total). + */ - addr = bank * BANK_SIZE; + gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | 0x000c); - /* Clean check points of each chunk */ - for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) + /* + * Perform the DRAM size detection for each bank individually. + */ + for (bank = 0; bank < 4; bank++) { - gus_poke (addr + chunk * CHUNK_SIZE + 0L, 0x00); - gus_poke (addr + chunk * CHUNK_SIZE + 1L, 0x00); + int size = 0; + + addr = bank * BANK_SIZE; + + /* Clean check points of each chunk */ + for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) + { + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); + } + + /* Write a value to each chunk point and verify the result */ + for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) + { + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x55); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0xAA); + + if (gus_peek(addr + chunk * CHUNK_SIZE + 0L) == 0x55 && + gus_peek(addr + chunk * CHUNK_SIZE + 1L) == 0xAA) + { + /* OK. There is RAM. Now check for possible shadows */ + int ok = 1, chunk2; + + for (chunk2 = 0; ok && chunk2 < chunk; chunk2++) + if (gus_peek(addr + chunk2 * CHUNK_SIZE + 0L) || + gus_peek(addr + chunk2 * CHUNK_SIZE + 1L)) + ok = 0; /* Addressing wraps */ + + if (ok) + size = (chunk + 1) * CHUNK_SIZE; + } + gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00); + gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00); + } + bank_sizes[bank] = size; + if (size) + nbanks = bank + 1; + DDB(printk("Interwave: Bank %d, size=%dk\n", bank, size / 1024)); } - /* Write a value to each chunk point and verify the result */ - for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++) + if (nbanks == 0) /* No RAM - Give up */ { - gus_poke (addr + chunk * CHUNK_SIZE + 0L, 0x55); - gus_poke (addr + chunk * CHUNK_SIZE + 1L, 0xAA); - - if (gus_peek (addr + chunk * CHUNK_SIZE + 0L) == 0x55 && - gus_peek (addr + chunk * CHUNK_SIZE + 1L) == 0xAA) - { /* OK. There is RAM. Now check for possible shadows */ - int ok = 1, chunk2; - - for (chunk2 = 0; ok && chunk2 < chunk; chunk2++) - if (gus_peek (addr + chunk2 * CHUNK_SIZE + 0L) || - gus_peek (addr + chunk2 * CHUNK_SIZE + 1L)) - ok = 0; /* Addressing wraps */ - - if (ok) - size = (chunk + 1) * CHUNK_SIZE; - } - gus_poke (addr + chunk * CHUNK_SIZE + 0L, 0x00); - gus_poke (addr + chunk * CHUNK_SIZE + 1L, 0x00); + printk(KERN_ERR "Sound: An Interwave audio chip detected but no DRAM\n"); + printk(KERN_ERR "Sound: Unable to work with this card.\n"); + gus_write8(0x19, gus_read8(0x19) & ~0x01); + gus_mem_size = 0; + return; } - bank_sizes[bank] = size; - if (size) - nbanks = bank + 1; - DDB (printk ("Interwave: Bank %d, size=%dk\n", bank, size / 1024)); - } - - if (nbanks == 0) /* No RAM - Give up */ - { - printk ("Sound: An Interwave audio chip detected but no DRAM\n"); - printk ("Sound: Unable to work with this card.\n"); - gus_write8 (0x19, gus_read8 (0x19) & ~0x01); - return; - } -/* - * Now we know how much DRAM there is in each bank. The next step is - * to find a DRAM size encoding (0 to 12) which is best for the combination - * we have. - * - * First try if any of the possible alternatives matches exactly the amount - * of memory we have. - */ + /* + * Now we know how much DRAM there is in each bank. The next step is + * to find a DRAM size encoding (0 to 12) which is best for the combination + * we have. + * + * First try if any of the possible alternatives matches exactly the amount + * of memory we have. + */ - for (i = 0; bits == -1 && i < 13; i++) - { - bits = i; + for (i = 0; bits == -1 && i < 13; i++) + { + bits = i; - for (j = 0; bits != -1 && j < 4; j++) - if (mem_decode[i][j] != bank_sizes[j]) - bits = -1; /* No hit */ - } + for (j = 0; bits != -1 && j < 4; j++) + if (mem_decode[i][j] != bank_sizes[j]) + bits = -1; /* No hit */ + } -/* - * If necessary, try to find a combination where other than the last - * bank matches our configuration and the last bank is left oversized. - * In this way we don't leave holes in the middle of memory. - */ - if (bits == -1) /* No luck yet */ - for (i = 0; bits == -1 && i < 13; i++) - { - bits = i; - - for (j = 0; bits != -1 && j < nbanks - 1; j++) - if (mem_decode[i][j] != bank_sizes[j]) - bits = -1; /* No hit */ - if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1]) - bits = -1; /* The last bank is too small */ - } + /* + * If necessary, try to find a combination where other than the last + * bank matches our configuration and the last bank is left oversized. + * In this way we don't leave holes in the middle of memory. + */ -/* - * The last resort is to search for a combination where the last bank is - * smaller than the actual SIMM. This leaves some memory in the last bank - * unused but doesn't leave holes in the DRAM address space. - */ - if (bits == -1) /* No luck yet */ - { - for (i = 0; bits == -1 && i < 13; i++) + if (bits == -1) /* No luck yet */ { - bits = i; - - for (j = 0; bits != -1 && j < nbanks - 1; j++) - if (mem_decode[i][j] != bank_sizes[j]) - bits = -1; /* No hit */ + for (i = 0; bits == -1 && i < 13; i++) + { + bits = i; + + for (j = 0; bits != -1 && j < nbanks - 1; j++) + if (mem_decode[i][j] != bank_sizes[j]) + bits = -1; /* No hit */ + if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1]) + bits = -1; /* The last bank is too small */ + } } - - if (bits != -1) + /* + * The last resort is to search for a combination where the last bank is + * smaller than the actual SIMM. This leaves some memory in the last bank + * unused but doesn't leave holes in the DRAM address space. + */ + if (bits == -1) /* No luck yet */ { - printk ("Interwave: Can't use all installed RAM.\n"); - printk ("Interwave: Try reordering SIMMS.\n"); + for (i = 0; bits == -1 && i < 13; i++) + { + bits = i; + + for (j = 0; bits != -1 && j < nbanks - 1; j++) + if (mem_decode[i][j] != bank_sizes[j]) + bits = -1; /* No hit */ + } + if (bits != -1) + { + printk(KERN_INFO "Interwave: Can't use all installed RAM.\n"); + printk(KERN_INFO "Interwave: Try reordering SIMMS.\n"); + } } - } - - if (bits == -1) - { - printk ("Interwave: Can't find working DRAM encoding.\n"); - printk ("Interwave: Defaulting to 256k. Try reordering SIMMS.\n"); - bits = 0; - } + if (bits == -1) + { + printk(KERN_INFO "Interwave: Can't find working DRAM encoding.\n"); + printk(KERN_INFO "Interwave: Defaulting to 256k. Try reordering SIMMS.\n"); + bits = 0; + } + DDB(printk("Interwave: Selecting DRAM addressing mode %d\n", bits)); - DDB (printk ("Interwave: Selecting DRAM addressing mode %d\n", bits)); + for (bank = 0; bank < 4; bank++) + { + DDB(printk(" Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024)); - for (bank = 0; bank < 4; bank++) - { - DDB (printk (" Bank %d, mem=%dk (limit %dk)\n", - bank, bank_sizes[bank] / 1024, - mem_decode[bits][bank] / 1024)); + if (bank_sizes[bank] > mem_decode[bits][bank]) + total += mem_decode[bits][bank]; + else + total += bank_sizes[bank]; + } - if (bank_sizes[bank] > mem_decode[bits][bank]) - total += mem_decode[bits][bank]; - else - total += bank_sizes[bank]; - } + DDB(printk("Total %dk of DRAM (enhanced mode)\n", total / 1024)); - DDB (printk ("Total %dk of DRAM (enhanced mode)\n", total / 1024)); -/* - * Set the memory addressing mode. - */ - gus_write16 (0x52, (gus_look16 (0x52) & 0xfff0) | bits); + /* + * Set the memory addressing mode. + */ + gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | bits); -/* - * Return the chip back to GUS compatible mode. - */ - gus_write8 (0x19, gus_read8 (0x19) & ~0x01); +/* Leave the chip into enhanced mode. Disable LFO */ + gus_mem_size = total; + iw_mode = 1; + gus_write8(0x19, (gus_read8(0x19) | 0x01) & ~0x02); } -int -gus_wave_detect (int baseaddr) +int gus_wave_detect(int baseaddr) { - unsigned long i, max_mem = 1024L; - unsigned long loc; + unsigned long i, max_mem = 1024L; + unsigned long loc; + unsigned char val; - gus_base = baseaddr; + gus_base = baseaddr; - gus_write8 (0x4c, 0); /* Reset GF1 */ - gus_delay (); - gus_delay (); + gus_write8(0x4c, 0); /* Reset GF1 */ + gus_delay(); + gus_delay(); - gus_write8 (0x4c, 1); /* Release Reset */ - gus_delay (); - gus_delay (); + gus_write8(0x4c, 1); /* Release Reset */ + gus_delay(); + gus_delay(); - if (gus_pnp_flag) - pnp_mem_init (); +#ifdef GUSPNP_AUTODETECT + val = gus_look8(0x5b); /* Version number register */ + gus_write8(0x5b, ~val); /* Invert all bits */ - /* See if there is first block there.... */ - gus_poke (0L, 0xaa); - if (gus_peek (0L) != 0xaa) - return (0); + if ((gus_look8(0x5b) & 0xf0) == (val & 0xf0)) /* No change */ + { + if ((gus_look8(0x5b) & 0x0f) == ((~val) & 0x0f)) /* Change */ + { + DDB(printk("Interwave chip version %d detected\n", (val & 0xf0) >> 4)); + gus_pnp_flag = 1; + } + else + { + DDB(printk("Not an Interwave chip (%x)\n", gus_look8(0x5b))); + gus_pnp_flag = 0; + } + } + gus_write8(0x5b, val); /* Restore all bits */ +#endif - /* Now zero it out so that I can check for mirroring .. */ - gus_poke (0L, 0x00); - for (i = 1L; i < max_mem; i++) - { - int n, failed; + if (gus_pnp_flag) + pnp_mem_init(); + if (iw_mode) + return 1; - /* check for mirroring ... */ - if (gus_peek (0L) != 0) - break; - loc = i << 10; + /* See if there is first block there.... */ + gus_poke(0L, 0xaa); + if (gus_peek(0L) != 0xaa) + return (0); - for (n = loc - 1, failed = 0; n <= loc; n++) + /* Now zero it out so that I can check for mirroring .. */ + gus_poke(0L, 0x00); + for (i = 1L; i < max_mem; i++) { - gus_poke (loc, 0xaa); - if (gus_peek (loc) != 0xaa) - failed = 1; - - gus_poke (loc, 0x55); - if (gus_peek (loc) != 0x55) - failed = 1; + int n, failed; + + /* check for mirroring ... */ + if (gus_peek(0L) != 0) + break; + loc = i << 10; + + for (n = loc - 1, failed = 0; n <= loc; n++) + { + gus_poke(loc, 0xaa); + if (gus_peek(loc) != 0xaa) + failed = 1; + gus_poke(loc, 0x55); + if (gus_peek(loc) != 0x55) + failed = 1; + } + if (failed) + break; } - - if (failed) - break; - } - gus_mem_size = i << 10; - return 1; + gus_mem_size = i << 10; + return 1; } -static int -guswave_ioctl (int dev, - unsigned int cmd, caddr_t arg) +static int guswave_ioctl(int dev, unsigned int cmd, caddr_t arg) { - switch (cmd) - { - case SNDCTL_SYNTH_INFO: - gus_info.nr_voices = nr_voices; - memcpy_tofs (&((char *) arg)[0], &gus_info, sizeof (gus_info)); - return 0; - break; + switch (cmd) + { + case SNDCTL_SYNTH_INFO: + gus_info.nr_voices = nr_voices; + if (copy_to_user(arg, &gus_info, sizeof(gus_info))) + return -EFAULT; + return 0; - case SNDCTL_SEQ_RESETSAMPLES: - reset_sample_memory (); - return 0; - break; + case SNDCTL_SEQ_RESETSAMPLES: + reset_sample_memory(); + return 0; - case SNDCTL_SEQ_PERCMODE: - return 0; - break; + case SNDCTL_SEQ_PERCMODE: + return 0; - case SNDCTL_SYNTH_MEMAVL: - return gus_mem_size - free_mem_ptr - 32; + case SNDCTL_SYNTH_MEMAVL: + return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32; - default: - return -(EINVAL); - } + default: + return -EINVAL; + } } -static int -guswave_set_instr (int dev, int voice, int instr_no) +static int guswave_set_instr(int dev, int voice, int instr_no) { - int sample_no; - - if (instr_no < 0 || instr_no > MAX_PATCH) - return -(EINVAL); - - if (voice < 0 || voice > 31) - return -(EINVAL); + int sample_no; - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - { - voices[voice].sample_pending = instr_no; - return 0; - } + if (instr_no < 0 || instr_no > MAX_PATCH) + instr_no = 0; /* Default to acoustic piano */ - sample_no = patch_table[instr_no]; - patch_map[voice] = -1; + if (voice < 0 || voice > 31) + return -EINVAL; - if (sample_no == NOT_SAMPLE) - { - printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice); - return -(EINVAL); /* Patch not defined */ - } - - if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ - { - printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", - sample_no, instr_no, voice); - return -(EINVAL); - } + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].sample_pending = instr_no; + return 0; + } + sample_no = patch_table[instr_no]; + patch_map[voice] = -1; - sample_map[voice] = sample_no; - patch_map[voice] = instr_no; - return 0; + if (sample_no == NOT_SAMPLE) + { +/* printk("GUS: Undefined patch %d for voice %d\n", instr_no, voice);*/ + return -EINVAL; /* Patch not defined */ + } + if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ + { +/* printk("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);*/ + return -EINVAL; + } + sample_map[voice] = sample_no; + patch_map[voice] = instr_no; + return 0; } -static int -guswave_kill_note (int dev, int voice, int note, int velocity) +static int guswave_kill_note(int dev, int voice, int note, int velocity) { - unsigned long flags; + unsigned long flags; - save_flags (flags); - cli (); - /* voice_alloc->map[voice] = 0xffff; */ - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) - { - voices[voice].kill_pending = 1; - restore_flags (flags); - } - else - { - restore_flags (flags); - gus_voice_fade (voice); - } + save_flags(flags); + cli(); + /* voice_alloc->map[voice] = 0xffff; */ + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].kill_pending = 1; + restore_flags(flags); + } + else + { + restore_flags(flags); + gus_voice_fade(voice); + } - restore_flags (flags); - return 0; + restore_flags(flags); + return 0; } -static void -guswave_aftertouch (int dev, int voice, int pressure) +static void guswave_aftertouch(int dev, int voice, int pressure) { } -static void -guswave_panning (int dev, int voice, int value) +static void guswave_panning(int dev, int voice, int value) { - if (voice >= 0 || voice < 32) - voices[voice].panning = value; + if (voice >= 0 || voice < 32) + voices[voice].panning = value; } -static void -guswave_volume_method (int dev, int mode) +static void guswave_volume_method(int dev, int mode) { - if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) - volume_method = mode; + if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) + volume_method = mode; } -static void -compute_volume (int voice, int volume) +static void compute_volume(int voice, int volume) { - if (volume < 128) - voices[voice].midi_volume = volume; + if (volume < 128) + voices[voice].midi_volume = volume; - switch (volume_method) - { - case VOL_METHOD_ADAGIO: - voices[voice].initial_volume = - gus_adagio_vol (voices[voice].midi_volume, voices[voice].main_vol, - voices[voice].expression_vol, - voices[voice].patch_vol); - break; + switch (volume_method) + { + case VOL_METHOD_ADAGIO: + voices[voice].initial_volume = + gus_adagio_vol(voices[voice].midi_volume, voices[voice].main_vol, + voices[voice].expression_vol, + voices[voice].patch_vol); + break; + + case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ + voices[voice].initial_volume = gus_linear_vol(volume, voices[voice].main_vol); + break; + + default: + voices[voice].initial_volume = volume_base + + (voices[voice].midi_volume * volume_scale); + } - case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */ - voices[voice].initial_volume = - gus_linear_vol (volume, voices[voice].main_vol); - break; + if (voices[voice].initial_volume > 4030) + voices[voice].initial_volume = 4030; +} - default: - voices[voice].initial_volume = volume_base + - (voices[voice].midi_volume * volume_scale); - } - - if (voices[voice].initial_volume > 4030) - voices[voice].initial_volume = 4030; -} - -static void -compute_and_set_volume (int voice, int volume, int ramp_time) +static void compute_and_set_volume(int voice, int volume, int ramp_time) { - int curr, target, rate; - unsigned long flags; - - compute_volume (voice, volume); - voices[voice].current_volume = voices[voice].initial_volume; + int curr, target, rate; + unsigned long flags; - save_flags (flags); - cli (); - /* - * CAUTION! Interrupts disabled. Enable them before returning - */ + compute_volume(voice, volume); + voices[voice].current_volume = voices[voice].initial_volume; - gus_select_voice (voice); - - curr = gus_read16 (0x09) >> 4; - target = voices[voice].initial_volume; + save_flags(flags); + cli(); + /* + * CAUTION! Interrupts disabled. Enable them before returning + */ - if (ramp_time == INSTANT_RAMP) - { - gus_rampoff (); - gus_voice_volume (target); - restore_flags (flags); - return; - } + gus_select_voice(voice); - if (ramp_time == FAST_RAMP) - rate = 63; - else - rate = 16; - gus_ramp_rate (0, rate); + curr = gus_read16(0x09) >> 4; + target = voices[voice].initial_volume; - if ((target - curr) / 64 == 0) /* Close enough to target. */ - { - gus_rampoff (); - gus_voice_volume (target); - restore_flags (flags); - return; - } + if (ramp_time == INSTANT_RAMP) + { + gus_rampoff(); + gus_voice_volume(target); + restore_flags(flags); + return; + } + if (ramp_time == FAST_RAMP) + rate = 63; + else + rate = 16; + gus_ramp_rate(0, rate); - if (target > curr) - { - if (target > (4095 - 65)) - target = 4095 - 65; - gus_ramp_range (curr, target); - gus_rampon (0x00); /* Ramp up, once, no IRQ */ - } - else - { - if (target < 65) - target = 65; + if ((target - curr) / 64 == 0) /* Close enough to target. */ + { + gus_rampoff(); + gus_voice_volume(target); + restore_flags(flags); + return; + } + if (target > curr) + { + if (target > (4095 - 65)) + target = 4095 - 65; + gus_ramp_range(curr, target); + gus_rampon(0x00); /* Ramp up, once, no IRQ */ + } + else + { + if (target < 65) + target = 65; - gus_ramp_range (target, curr); - gus_rampon (0x40); /* Ramp down, once, no irq */ - } - restore_flags (flags); + gus_ramp_range(target, curr); + gus_rampon(0x40); /* Ramp down, once, no irq */ + } + restore_flags(flags); } -static void -dynamic_volume_change (int voice) +static void dynamic_volume_change(int voice) { - unsigned char status; - unsigned long flags; + unsigned char status; + unsigned long flags; - save_flags (flags); - cli (); - gus_select_voice (voice); - status = gus_read8 (0x00); /* Get voice status */ - restore_flags (flags); - - if (status & 0x03) - return; /* Voice was not running */ - - if (!(voices[voice].mode & WAVE_ENVELOPES)) - { - compute_and_set_volume (voice, voices[voice].midi_volume, 1); - return; - } - - /* - * Voice is running and has envelopes. - */ - - save_flags (flags); - cli (); - gus_select_voice (voice); - status = gus_read8 (0x0d); /* Ramping status */ - restore_flags (flags); + save_flags(flags); + cli(); + gus_select_voice(voice); + status = gus_read8(0x00); /* Get voice status */ + restore_flags(flags); - if (status & 0x03) /* Sustain phase? */ - { - compute_and_set_volume (voice, voices[voice].midi_volume, 1); - return; - } + if (status & 0x03) + return; /* Voice was not running */ - if (voices[voice].env_phase < 0) - return; - - compute_volume (voice, voices[voice].midi_volume); - -} - -static void -guswave_controller (int dev, int voice, int ctrl_num, int value) -{ - unsigned long flags; - unsigned long freq; - - if (voice < 0 || voice > 31) - return; - - switch (ctrl_num) - { - case CTRL_PITCH_BENDER: - voices[voice].bender = value; - - if (voices[voice].volume_irq_mode != VMODE_START_NOTE) - { - freq = compute_finetune (voices[voice].orig_freq, value, - voices[voice].bender_range); - voices[voice].current_freq = freq; - - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_voice_freq (freq); - restore_flags (flags); - } - break; - - case CTRL_PITCH_BENDER_RANGE: - voices[voice].bender_range = value; - break; - case CTL_EXPRESSION: - value /= 128; - case CTRL_EXPRESSION: - if (volume_method == VOL_METHOD_ADAGIO) - { - voices[voice].expression_vol = value; - if (voices[voice].volume_irq_mode != VMODE_START_NOTE) - dynamic_volume_change (voice); - } - break; - - case CTL_PAN: - voices[voice].panning = (value * 2) - 128; - break; + if (!(voices[voice].mode & WAVE_ENVELOPES)) + { + compute_and_set_volume(voice, voices[voice].midi_volume, 1); + return; + } + + /* + * Voice is running and has envelopes. + */ - case CTL_MAIN_VOLUME: - value = (value * 100) / 16383; + save_flags(flags); + cli(); + gus_select_voice(voice); + status = gus_read8(0x0d); /* Ramping status */ + restore_flags(flags); + + if (status & 0x03) /* Sustain phase? */ + { + compute_and_set_volume(voice, voices[voice].midi_volume, 1); + return; + } + if (voices[voice].env_phase < 0) + return; - case CTRL_MAIN_VOLUME: - voices[voice].main_vol = value; - if (voices[voice].volume_irq_mode != VMODE_START_NOTE) - dynamic_volume_change (voice); - break; + compute_volume(voice, voices[voice].midi_volume); - default: - break; - } } -static int -guswave_start_note2 (int dev, int voice, int note_num, int volume) +static void guswave_controller(int dev, int voice, int ctrl_num, int value) { - int sample, best_sample, best_delta, delta_freq; - int is16bits, samplep, patch, pan; - unsigned long note_freq, base_note, freq, flags; - unsigned char mode = 0; + unsigned long flags; + unsigned long freq; - if (voice < 0 || voice > 31) - { - printk ("GUS: Invalid voice\n"); - return -(EINVAL); - } + if (voice < 0 || voice > 31) + return; - if (note_num == 255) - { - if (voices[voice].mode & WAVE_ENVELOPES) + switch (ctrl_num) { - voices[voice].midi_volume = volume; - dynamic_volume_change (voice); - return 0; + case CTRL_PITCH_BENDER: + voices[voice].bender = value; + + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + { + freq = compute_finetune(voices[voice].orig_freq, value, voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_freq(freq); + restore_flags(flags); + } + break; + + case CTRL_PITCH_BENDER_RANGE: + voices[voice].bender_range = value; + break; + case CTL_EXPRESSION: + value /= 128; + case CTRL_EXPRESSION: + if (volume_method == VOL_METHOD_ADAGIO) + { + voices[voice].expression_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change(voice); + } + break; + + case CTL_PAN: + voices[voice].panning = (value * 2) - 128; + break; + + case CTL_MAIN_VOLUME: + value = (value * 100) / 16383; + + case CTRL_MAIN_VOLUME: + voices[voice].main_vol = value; + if (voices[voice].volume_irq_mode != VMODE_START_NOTE) + dynamic_volume_change(voice); + break; + + default: + break; } - - compute_and_set_volume (voice, volume, 1); - return 0; - } - - if ((patch = patch_map[voice]) == -1) - { - return -(EINVAL); - } - - if ((samplep = patch_table[patch]) == NOT_SAMPLE) - { - return -(EINVAL); - } - - note_freq = note_to_freq (note_num); - - /* - * Find a sample within a patch so that the note_freq is between low_note - * and high_note. - */ - sample = -1; - - best_sample = samplep; - best_delta = 1000000; - while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1) - { - delta_freq = note_freq - samples[samplep].base_note; - if (delta_freq < 0) - delta_freq = -delta_freq; - if (delta_freq < best_delta) - { - best_sample = samplep; - best_delta = delta_freq; - } - if (samples[samplep].low_note <= note_freq && - note_freq <= samples[samplep].high_note) - sample = samplep; - else - samplep = samples[samplep].key; /* Link to next sample */ - } - if (sample == -1) - sample = best_sample; - - if (sample == -1) - { - printk ("GUS: Patch %d not defined for note %d\n", patch, note_num); - return 0; /* Should play default patch ??? */ - } - - is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; - voices[voice].mode = samples[sample].mode; - voices[voice].patch_vol = samples[sample].volume; - - if (voices[voice].mode & WAVE_ENVELOPES) - { - int i; - - for (i = 0; i < 6; i++) - { - voices[voice].env_rate[i] = samples[sample].env_rate[i]; - voices[voice].env_offset[i] = samples[sample].env_offset[i]; - } - } - - sample_map[voice] = sample; - - base_note = samples[sample].base_note / 100; /* Try to avoid overflows */ - note_freq /= 100; - - freq = samples[sample].base_freq * note_freq / base_note; - - voices[voice].orig_freq = freq; - - /* - * Since the pitch bender may have been set before playing the note, we - * have to calculate the bending now. - */ - - freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, - voices[voice].bender_range); - voices[voice].current_freq = freq; - - pan = (samples[sample].panning + voices[voice].panning) / 32; - pan += 7; - if (pan < 0) - pan = 0; - if (pan > 15) - pan = 15; - - if (samples[sample].mode & WAVE_16_BITS) - { - mode |= 0x04; /* 16 bits */ - if ((sample_ptrs[sample] >> 18) != - ((sample_ptrs[sample] + samples[sample].len) >> 18)) - printk ("GUS: Sample address error\n"); - } - - /************************************************************************* - * CAUTION! Interrupts disabled. Don't return before enabling - *************************************************************************/ - - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_voice_off (); - gus_rampoff (); - - restore_flags (flags); - - if (voices[voice].mode & WAVE_ENVELOPES) - { - compute_volume (voice, volume); - init_envelope (voice); - } - else - { - compute_and_set_volume (voice, volume, 0); - } - - save_flags (flags); - cli (); - gus_select_voice (voice); - - if (samples[sample].mode & WAVE_LOOP_BACK) - gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len - - voices[voice].offset_pending, 0, is16bits); /* start=end */ - else - gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending, - 0, is16bits); /* Sample start=begin */ - - if (samples[sample].mode & WAVE_LOOPING) - { - mode |= 0x08; - - if (samples[sample].mode & WAVE_BIDIR_LOOP) - mode |= 0x10; - - if (samples[sample].mode & WAVE_LOOP_BACK) - { - gus_write_addr (0x0a, - sample_ptrs[sample] + samples[sample].loop_end - - voices[voice].offset_pending, - (samples[sample].fractions >> 4) & 0x0f, is16bits); - mode |= 0x40; - } - - gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start, - samples[sample].fractions & 0x0f, - is16bits); /* Loop start location */ - gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end, - (samples[sample].fractions >> 4) & 0x0f, - is16bits); /* Loop end location */ - } - else - { - mode |= 0x20; /* Loop IRQ at the end */ - voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */ - voices[voice].loop_irq_parm = 1; - gus_write_addr (0x02, sample_ptrs[sample], - 0, is16bits); /* Loop start location */ - gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len - 1, - (samples[sample].fractions >> 4) & 0x0f, - is16bits); /* Loop end location */ - } - gus_voice_freq (freq); - gus_voice_balance (pan); - gus_voice_on (mode); - restore_flags (flags); - - return 0; } -/* - * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking - * when the note playing on the voice is changed. It uses volume - * ramping. - */ - -static int -guswave_start_note (int dev, int voice, int note_num, int volume) +static int guswave_start_note2(int dev, int voice, int note_num, int volume) { - long int flags; - int mode; - int ret_val = 0; + int sample, best_sample, best_delta, delta_freq; + int is16bits, samplep, patch, pan; + unsigned long note_freq, base_note, freq, flags; + unsigned char mode = 0; - save_flags (flags); - cli (); - if (note_num == 255) - { - if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + if (voice < 0 || voice > 31) + { +/* printk("GUS: Invalid voice\n");*/ + return -EINVAL; + } + if (note_num == 255) + { + if (voices[voice].mode & WAVE_ENVELOPES) + { + voices[voice].midi_volume = volume; + dynamic_volume_change(voice); + return 0; + } + compute_and_set_volume(voice, volume, 1); + return 0; + } + if ((patch = patch_map[voice]) == -1) + return -EINVAL; + if ((samplep = patch_table[patch]) == NOT_SAMPLE) { - voices[voice].volume_pending = volume; + return -EINVAL; } - else + note_freq = note_to_freq(note_num); + + /* + * Find a sample within a patch so that the note_freq is between low_note + * and high_note. + */ + sample = -1; + + best_sample = samplep; + best_delta = 1000000; + while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1) + { + delta_freq = note_freq - samples[samplep].base_note; + if (delta_freq < 0) + delta_freq = -delta_freq; + if (delta_freq < best_delta) + { + best_sample = samplep; + best_delta = delta_freq; + } + if (samples[samplep].low_note <= note_freq && + note_freq <= samples[samplep].high_note) + { + sample = samplep; + } + else + samplep = samples[samplep].key; /* Link to next sample */ + } + if (sample == -1) + sample = best_sample; + + if (sample == -1) { - ret_val = guswave_start_note2 (dev, voice, note_num, volume); +/* printk("GUS: Patch %d not defined for note %d\n", patch, note_num);*/ + return 0; /* Should play default patch ??? */ } - } - else - { - gus_select_voice (voice); - mode = gus_read8 (0x00); - if (mode & 0x20) - gus_write8 (0x00, mode & 0xdf); /* No interrupt! */ + is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0; + voices[voice].mode = samples[sample].mode; + voices[voice].patch_vol = samples[sample].volume; - voices[voice].offset_pending = 0; - voices[voice].kill_pending = 0; - voices[voice].volume_irq_mode = 0; - voices[voice].loop_irq_mode = 0; + if (iw_mode) + gus_write8(0x15, 0x00); /* RAM, Reset voice deactivate bit of SMSI */ - if (voices[voice].sample_pending >= 0) + if (voices[voice].mode & WAVE_ENVELOPES) { - restore_flags (flags); /* Run temporarily with interrupts enabled */ - guswave_set_instr (voices[voice].dev_pending, voice, - voices[voice].sample_pending); - voices[voice].sample_pending = -1; - save_flags (flags); - cli (); - gus_select_voice (voice); /* Reselect the voice (just to be sure) */ + int i; + + for (i = 0; i < 6; i++) + { + voices[voice].env_rate[i] = samples[sample].env_rate[i]; + voices[voice].env_offset[i] = samples[sample].env_offset[i]; + } } + sample_map[voice] = sample; - if ((mode & 0x01) || (int) ((gus_read16 (0x09) >> 4) < (unsigned) 2065)) + if (voices[voice].fixed_pitch) /* Fixed pitch */ { - ret_val = guswave_start_note2 (dev, voice, note_num, volume); + freq = samples[sample].base_freq; } - else + else { - voices[voice].dev_pending = dev; - voices[voice].note_pending = note_num; - voices[voice].volume_pending = volume; - voices[voice].volume_irq_mode = VMODE_START_NOTE; + base_note = samples[sample].base_note / 100; + note_freq /= 100; - gus_rampoff (); - gus_ramp_range (2000, 4065); - gus_ramp_rate (0, 63); /* Fastest possible rate */ - gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */ + freq = samples[sample].base_freq * note_freq / base_note; } - } - restore_flags (flags); - return ret_val; -} -static void -guswave_reset (int dev) -{ - int i; + voices[voice].orig_freq = freq; - for (i = 0; i < 32; i++) - { - gus_voice_init (i); - gus_voice_init2 (i); - } -} + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ -static int -guswave_open (int dev, int mode) -{ - int err; + freq = compute_finetune(voices[voice].orig_freq, voices[voice].bender, + voices[voice].bender_range, 0); + voices[voice].current_freq = freq; + + pan = (samples[sample].panning + voices[voice].panning) / 32; + pan += 7; + if (pan < 0) + pan = 0; + if (pan > 15) + pan = 15; + + if (samples[sample].mode & WAVE_16_BITS) + { + mode |= 0x04; /* 16 bits */ + if ((sample_ptrs[sample] / GUS_BANK_SIZE) != + ((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE)) + printk(KERN_ERR "GUS: Sample address error\n"); + } + /************************************************************************* + * CAUTION! Interrupts disabled. Don't return before enabling + *************************************************************************/ - if (gus_busy) - return -(EBUSY); + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_off(); + gus_rampoff(); - voice_alloc->timestamp = 0; + restore_flags(flags); + + if (voices[voice].mode & WAVE_ENVELOPES) + { + compute_volume(voice, volume); + init_envelope(voice); + } + else + { + compute_and_set_volume(voice, volume, 0); + } - if ((err = DMAbuf_open_dma (gus_devnum)) < 0) - { - /* printk ("GUS: Loading samples without DMA\n"); */ - gus_no_dma = 1; /* Upload samples using PIO */ - } - else - gus_no_dma = 0; + save_flags(flags); + cli(); + gus_select_voice(voice); - dram_sleep_flag.flags = WK_NONE; - gus_busy = 1; - active_device = GUS_DEV_WAVE; + if (samples[sample].mode & WAVE_LOOP_BACK) + gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].len - + voices[voice].offset_pending, 0, is16bits); /* start=end */ + else + gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending, 0, is16bits); /* Sample start=begin */ - gusintr (gus_irq, NULL, NULL); /* Serve pending interrupts */ - gus_initialize (); - gus_reset (); - gusintr (gus_irq, NULL, NULL); /* Serve pending interrupts */ + if (samples[sample].mode & WAVE_LOOPING) + { + mode |= 0x08; + + if (samples[sample].mode & WAVE_BIDIR_LOOP) + mode |= 0x10; + + if (samples[sample].mode & WAVE_LOOP_BACK) + { + gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].loop_end - + voices[voice].offset_pending, + (samples[sample].fractions >> 4) & 0x0f, is16bits); + mode |= 0x40; + } + gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start, + samples[sample].fractions & 0x0f, is16bits); /* Loop start location */ + gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end, + (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ + } + else + { + mode |= 0x20; /* Loop IRQ at the end */ + voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */ + voices[voice].loop_irq_parm = 1; + gus_write_addr(0x02, sample_ptrs[sample], 0, is16bits); /* Loop start location */ + gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1, + (samples[sample].fractions >> 4) & 0x0f, is16bits); /* Loop end location */ + } + gus_voice_freq(freq); + gus_voice_balance(pan); + gus_voice_on(mode); + restore_flags(flags); - return 0; + return 0; } -static void -guswave_close (int dev) +/* + * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking + * when the note playing on the voice is changed. It uses volume + * ramping. + */ + +static int guswave_start_note(int dev, int voice, int note_num, int volume) { - gus_busy = 0; - active_device = 0; - gus_reset (); + long int flags; + int mode; + int ret_val = 0; - if (!gus_no_dma) - DMAbuf_close_dma (gus_devnum); + save_flags(flags); + cli(); + if (note_num == 255) + { + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) + { + voices[voice].volume_pending = volume; + } + else + { + ret_val = guswave_start_note2(dev, voice, note_num, volume); + } + } + else + { + gus_select_voice(voice); + mode = gus_read8(0x00); + if (mode & 0x20) + gus_write8(0x00, mode & 0xdf); /* No interrupt! */ + + voices[voice].offset_pending = 0; + voices[voice].kill_pending = 0; + voices[voice].volume_irq_mode = 0; + voices[voice].loop_irq_mode = 0; + + if (voices[voice].sample_pending >= 0) + { + restore_flags(flags); /* Run temporarily with interrupts enabled */ + guswave_set_instr(voices[voice].dev_pending, voice, voices[voice].sample_pending); + voices[voice].sample_pending = -1; + save_flags(flags); + cli(); + gus_select_voice(voice); /* Reselect the voice (just to be sure) */ + } + if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < (unsigned) 2065)) + { + ret_val = guswave_start_note2(dev, voice, note_num, volume); + } + else + { + voices[voice].dev_pending = dev; + voices[voice].note_pending = note_num; + voices[voice].volume_pending = volume; + voices[voice].volume_irq_mode = VMODE_START_NOTE; + + gus_rampoff(); + gus_ramp_range(2000, 4065); + gus_ramp_rate(0, 63); /* Fastest possible rate */ + gus_rampon(0x20 | 0x40); /* Ramp down, once, irq */ + } + } + restore_flags(flags); + return ret_val; } -static int -guswave_load_patch (int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) +static void guswave_reset(int dev) { - struct patch_info patch; - int instr; - long sizeof_patch; + int i; + + for (i = 0; i < 32; i++) + { + gus_voice_init(i); + gus_voice_init2(i); + } +} - unsigned long blk_sz, blk_end, left, src_offs, target; +static int guswave_open(int dev, int mode) +{ + int err; - sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */ + if (gus_busy) + return -EBUSY; - if (format != GUS_PATCH) - { - printk ("GUS Error: Invalid patch format (key) 0x%x\n", format); - return -(EINVAL); - } + voice_alloc->timestamp = 0; - if (count < sizeof_patch) - { - printk ("GUS Error: Patch header too short\n"); - return -(EINVAL); - } + if (gus_no_wave_dma) { + gus_no_dma = 1; + } else { + if ((err = DMAbuf_open_dma(gus_devnum)) < 0) + { + /* printk( "GUS: Loading samples without DMA\n"); */ + gus_no_dma = 1; /* Upload samples using PIO */ + } + else + gus_no_dma = 0; + } - count -= sizeof_patch; + init_waitqueue(&dram_sleeper); + gus_busy = 1; + active_device = GUS_DEV_WAVE; - if (free_sample >= MAX_SAMPLE) - { - printk ("GUS: Sample table full\n"); - return -(ENOSPC); - } + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ + gus_initialize(); + gus_reset(); + gusintr(gus_irq, (void *)gus_hw_config, NULL); /* Serve pending interrupts */ - /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. - */ + return 0; +} - memcpy_fromfs (&((char *) &patch)[offs], &(addr)[offs], sizeof_patch - offs); +static void guswave_close(int dev) +{ + gus_busy = 0; + active_device = 0; + gus_reset(); - instr = patch.instr_no; + if (!gus_no_dma) + DMAbuf_close_dma(gus_devnum); +} - if (instr < 0 || instr > MAX_PATCH) - { - printk ("GUS: Invalid patch number %d\n", instr); - return -(EINVAL); - } +static int guswave_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info patch; + int instr; + long sizeof_patch; - if (count < patch.len) - { - printk ("GUS Warning: Patch record too short (%d<%d)\n", - count, (int) patch.len); - patch.len = count; - } + unsigned long blk_sz, blk_end, left, src_offs, target; - if (patch.len <= 0 || patch.len > gus_mem_size) - { - printk ("GUS: Invalid sample length %d\n", (int) patch.len); - return -(EINVAL); - } + sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */ - if (patch.mode & WAVE_LOOPING) - { - if (patch.loop_start < 0 || patch.loop_start >= patch.len) + if (format != GUS_PATCH) + { +/* printk("GUS Error: Invalid patch format (key) 0x%x\n", format);*/ + return -EINVAL; + } + if (count < sizeof_patch) { - printk ("GUS: Invalid loop start\n"); - return -(EINVAL); +/* printk("GUS Error: Patch header too short\n");*/ + return -EINVAL; } + count -= sizeof_patch; - if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) + if (free_sample >= MAX_SAMPLE) { - printk ("GUS: Invalid loop end\n"); - return -(EINVAL); +/* printk("GUS: Sample table full\n");*/ + return -ENOSPC; } - } + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + copy_from_user(&((char *) &patch)[offs], &(addr)[offs], sizeof_patch - offs); - free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */ + if (patch.mode & WAVE_ROM) + return -EINVAL; + if (gus_mem_size == 0) + return -ENOSPC; -#define GUS_BANK_SIZE (256*1024) + instr = patch.instr_no; - if (patch.mode & WAVE_16_BITS) - { - /* - * 16 bit samples must fit one 256k bank. - */ - if (patch.len >= GUS_BANK_SIZE) + if (instr < 0 || instr > MAX_PATCH) { - printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len); - return -(ENOSPC); +/* printk(KERN_ERR "GUS: Invalid patch number %d\n", instr);*/ + return -EINVAL; } - - if ((free_mem_ptr / GUS_BANK_SIZE) != - ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) + if (count < patch.len) { - unsigned long tmp_mem = /* Align to 256K */ - ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; - - if ((tmp_mem + patch.len) > gus_mem_size) - return -(ENOSPC); +/* printk(KERN_ERR "GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len);*/ + patch.len = count; + } + if (patch.len <= 0 || patch.len > gus_mem_size) + { +/* printk(KERN_ERR "GUS: Invalid sample length %d\n", (int) patch.len);*/ + return -EINVAL; + } + if (patch.mode & WAVE_LOOPING) + { + if (patch.loop_start < 0 || patch.loop_start >= patch.len) + { +/* printk(KERN_ERR "GUS: Invalid loop start\n");*/ + return -EINVAL; + } + if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) + { +/* printk(KERN_ERR "GUS: Invalid loop end\n");*/ + return -EINVAL; + } + } + free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */ - free_mem_ptr = tmp_mem; /* This leaves unusable memory */ + if (patch.mode & WAVE_16_BITS) + { + /* + * 16 bit samples must fit one 256k bank. + */ + if (patch.len >= GUS_BANK_SIZE) + { +/* printk("GUS: Sample (16 bit) too long %d\n", (int) patch.len);*/ + return -ENOSPC; + } + if ((free_mem_ptr / GUS_BANK_SIZE) != + ((free_mem_ptr + patch.len) / GUS_BANK_SIZE)) + { + unsigned long tmp_mem = + /* Align to 256K */ + ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE; + + if ((tmp_mem + patch.len) > gus_mem_size) + return -ENOSPC; + + free_mem_ptr = tmp_mem; /* This leaves unusable memory */ + } } - } + if ((free_mem_ptr + patch.len) > gus_mem_size) + return -ENOSPC; - if ((free_mem_ptr + patch.len) > gus_mem_size) - return -(ENOSPC); + sample_ptrs[free_sample] = free_mem_ptr; - sample_ptrs[free_sample] = free_mem_ptr; + /* + * Tremolo is not possible with envelopes + */ - /* - * Tremolo is not possible with envelopes - */ + if (patch.mode & WAVE_ENVELOPES) + patch.mode &= ~WAVE_TREMOLO; - if (patch.mode & WAVE_ENVELOPES) - patch.mode &= ~WAVE_TREMOLO; + if (!(patch.mode & WAVE_FRACTIONS)) + { + patch.fractions = 0; + } + memcpy((char *) &samples[free_sample], &patch, sizeof_patch); - if (!(patch.mode & WAVE_FRACTIONS)) - { - patch.fractions = 0; - } - - memcpy ((char *) &samples[free_sample], &patch, sizeof_patch); - - /* - * Link this_one sample to the list of samples for patch 'instr'. - */ - - samples[free_sample].key = patch_table[instr]; - patch_table[instr] = free_sample; - - /* - * Use DMA to transfer the wave data to the DRAM - */ - - left = patch.len; - src_offs = 0; - target = free_mem_ptr; - - while (left) /* Not completely transferred yet */ - { - blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use; - if (blk_sz > left) - blk_sz = left; + /* + * Link this_one sample to the list of samples for patch 'instr'. + */ - /* - * DMA cannot cross 256k bank boundaries. Check for that. - */ - blk_end = target + blk_sz; + samples[free_sample].key = patch_table[instr]; + patch_table[instr] = free_sample; - if ((target >> 18) != (blk_end >> 18)) - { /* Split the block */ + /* + * Use DMA to transfer the wave data to the DRAM + */ - blk_end &= ~(256 * 1024 - 1); - blk_sz = blk_end - target; - } + left = patch.len; + src_offs = 0; + target = free_mem_ptr; - if (gus_no_dma) + while (left) /* Not completely transferred yet */ { - /* - * For some reason the DMA is not possible. We have to use PIO. - */ - long i; - unsigned char data; - - - for (i = 0; i < blk_sz; i++) - { - data = get_fs_byte (&((addr)[sizeof_patch + i])); - if (patch.mode & WAVE_UNSIGNED) - - if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) - data ^= 0x80; /* Convert to signed */ - gus_poke (target + i, data); - } + blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use; + if (blk_sz > left) + blk_sz = left; + + /* + * DMA cannot cross bank (256k) boundaries. Check for that. + */ + + blk_end = target + blk_sz; + + if ((target / GUS_BANK_SIZE) != (blk_end / GUS_BANK_SIZE)) + { + /* Split the block */ + blk_end &= ~(GUS_BANK_SIZE - 1); + blk_sz = blk_end - target; + } + if (gus_no_dma) + { + /* + * For some reason the DMA is not possible. We have to use PIO. + */ + long i; + unsigned char data; + + for (i = 0; i < blk_sz; i++) + { + get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[sizeof_patch + i])); + if (patch.mode & WAVE_UNSIGNED) + if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) + data ^= 0x80; /* Convert to signed */ + gus_poke(target + i, data); + } + } + else + { + unsigned long address, hold_address; + unsigned char dma_command; + unsigned long flags; + + if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL) + { + printk(KERN_ERR "GUS: DMA buffer == NULL\n"); + return -ENOSPC; + } + /* + * OK, move now. First in and then out. + */ + + copy_from_user(audio_devs[gus_devnum]->dmap_out->raw_buf, &(addr)[sizeof_patch + src_offs], blk_sz); + + save_flags(flags); + cli(); + /******** INTERRUPTS DISABLED NOW ********/ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma(gus_devnum, audio_devs[gus_devnum]->dmap_out->raw_buf_phys, + blk_sz, DMA_MODE_WRITE); + + /* + * Set the DRAM address for the wave data + */ + + if (iw_mode) + { + /* Different address translation in enhanced mode */ + + unsigned char hi; + + if (gus_dma > 4) + address = target >> 1; /* Convert to 16 bit word address */ + else + address = target; + + hi = (unsigned char) ((address >> 16) & 0xf0); + hi += (unsigned char) (address & 0x0f); + + gus_write16(0x42, (address >> 4) & 0xffff); /* DMA address (low) */ + gus_write8(0x50, hi); + } + else + { + address = target; + if (audio_devs[gus_devnum]->dmap_out->dma > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ + } + + /* + * Start the DMA transfer + */ + + dma_command = 0x21; /* IRQ enable, DMA start */ + if (patch.mode & WAVE_UNSIGNED) + dma_command |= 0x80; /* Invert MSB */ + if (patch.mode & WAVE_16_BITS) + dma_command |= 0x40; /* 16 bit _DATA_ */ + if (audio_devs[gus_devnum]->dmap_out->dma > 3) + dma_command |= 0x04; /* 16 bit DMA _channel_ */ + + gus_write8(0x41, dma_command); /* Lets go luteet (=bugs) */ + + /* + * Sleep here until the DRAM DMA done interrupt is served + */ + active_device = GUS_DEV_WAVE; + + current->timeout = jiffies + HZ; + interruptible_sleep_on(&dram_sleeper); + if (!current->timeout) + printk("GUS: DMA Transfer timed out\n"); + current->timeout = 0; + restore_flags(flags); + } + + /* + * Now the next part + */ + + left -= blk_sz; + src_offs += blk_sz; + target += blk_sz; + + gus_write8(0x41, 0); /* Stop DMA */ } - else - { - unsigned long address, hold_address; - unsigned char dma_command; - unsigned long flags; - - if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL) - { - printk ("GUS: DMA buffer == NULL\n"); - return -(EINVAL); - } - /* - * OK, move now. First in and then out. - */ + free_mem_ptr += patch.len; + free_sample++; + return 0; +} - memcpy_fromfs (audio_devs[gus_devnum]->dmap_out->raw_buf, &(addr)[sizeof_patch + src_offs], blk_sz); +static void guswave_hw_control(int dev, unsigned char *event_rec) +{ + int voice, cmd; + unsigned short p1, p2; + unsigned int plong; + unsigned flags; - save_flags (flags); - cli (); -/******** INTERRUPTS DISABLED NOW ********/ - gus_write8 (0x41, 0); /* Disable GF1 DMA */ - DMAbuf_start_dma (gus_devnum, - audio_devs[gus_devnum]->dmap_out->raw_buf_phys, - blk_sz, DMA_MODE_WRITE); + cmd = event_rec[2]; + voice = event_rec[3]; + p1 = *(unsigned short *) &event_rec[4]; + p2 = *(unsigned short *) &event_rec[6]; + plong = *(unsigned int *) &event_rec[4]; - /* - * Set the DRAM address for the wave data - */ - - address = target; + if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && + (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) + do_volume_irq(voice); - if (audio_devs[gus_devnum]->dmachan1 > 3) - { - hold_address = address; - address = address >> 1; - address &= 0x0001ffffL; - address |= (hold_address & 0x000c0000L); - } - - gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ - - /* - * Start the DMA transfer - */ - - dma_command = 0x21; /* IRQ enable, DMA start */ - if (patch.mode & WAVE_UNSIGNED) - dma_command |= 0x80; /* Invert MSB */ - if (patch.mode & WAVE_16_BITS) - dma_command |= 0x40; /* 16 bit _DATA_ */ - if (audio_devs[gus_devnum]->dmachan1 > 3) - dma_command |= 0x04; /* 16 bit DMA _channel_ */ - - gus_write8 (0x41, dma_command); /* Lets bo luteet (=bugs) */ - - /* - * Sleep here until the DRAM DMA done interrupt is served - */ - active_device = GUS_DEV_WAVE; - - - { - unsigned long tlimit; - - if (HZ) - current_set_timeout (tlimit = jiffies + (HZ)); - else - tlimit = (unsigned long) -1; - dram_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&dram_sleeper); - if (!(dram_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - dram_sleep_flag.flags |= WK_TIMEOUT; - } - dram_sleep_flag.flags &= ~WK_SLEEP; - }; - if ((dram_sleep_flag.flags & WK_TIMEOUT)) - printk ("GUS: DMA Transfer timed out\n"); - restore_flags (flags); - } - - /* - * Now the next part - */ - - left -= blk_sz; - src_offs += blk_sz; - target += blk_sz; - - gus_write8 (0x41, 0); /* Stop DMA */ - } - - free_mem_ptr += patch.len; - - if (!pmgr_flag) - pmgr_inform (dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0); - free_sample++; - return 0; -} - -static void -guswave_hw_control (int dev, unsigned char *event_rec) -{ - int voice, cmd; - unsigned short p1, p2; - unsigned int plong; - unsigned flags; - - cmd = event_rec[2]; - voice = event_rec[3]; - p1 = *(unsigned short *) &event_rec[4]; - p2 = *(unsigned short *) &event_rec[6]; - plong = *(unsigned int *) &event_rec[4]; - - if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) && - (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS)) - do_volume_irq (voice); - - switch (cmd) - { - - case _GUS_NUMVOICES: - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_select_max_voices (p1); - restore_flags (flags); - break; - - case _GUS_VOICESAMPLE: - guswave_set_instr (dev, voice, p1); - break; - - case _GUS_VOICEON: - save_flags (flags); - cli (); - gus_select_voice (voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_voice_on (p1); - restore_flags (flags); - break; - - case _GUS_VOICEOFF: - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_voice_off (); - restore_flags (flags); - break; - - case _GUS_VOICEFADE: - gus_voice_fade (voice); - break; - - case _GUS_VOICEMODE: - save_flags (flags); - cli (); - gus_select_voice (voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_voice_mode (p1); - restore_flags (flags); - break; - - case _GUS_VOICEBALA: - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_voice_balance (p1); - restore_flags (flags); - break; - - case _GUS_VOICEFREQ: - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_voice_freq (plong); - restore_flags (flags); - break; - - case _GUS_VOICEVOL: - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_voice_volume (p1); - restore_flags (flags); - break; - - case _GUS_VOICEVOL2: /* Just update the software voice level */ - voices[voice].initial_volume = - voices[voice].current_volume = p1; - break; - - case _GUS_RAMPRANGE: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_ramp_range (p1, p2); - restore_flags (flags); - break; - - case _GUS_RAMPRATE: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NJET-NJET */ - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_ramp_rate (p1, p2); - restore_flags (flags); - break; - - case _GUS_RAMPMODE: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NO-NO */ - save_flags (flags); - cli (); - gus_select_voice (voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_ramp_mode (p1); - restore_flags (flags); - break; - - case _GUS_RAMPON: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* EI-EI */ - save_flags (flags); - cli (); - gus_select_voice (voice); - p1 &= ~0x20; /* Don't allow interrupts */ - gus_rampon (p1); - restore_flags (flags); - break; - - case _GUS_RAMPOFF: - if (voices[voice].mode & WAVE_ENVELOPES) - break; /* NEJ-NEJ */ - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_rampoff (); - restore_flags (flags); - break; - - case _GUS_VOLUME_SCALE: - volume_base = p1; - volume_scale = p2; - break; - - case _GUS_VOICE_POS: - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_set_voice_pos (voice, plong); - restore_flags (flags); - break; - - default:; - } -} - -static int -gus_audio_set_speed (int speed) -{ - - if (speed <= 0) - speed = gus_audio_speed; - - if (speed < 4000) - speed = 4000; - - if (speed > 44100) - speed = 44100; - - gus_audio_speed = speed; - - if (only_read_access) - { - /* Compute nearest valid recording speed and return it */ - - /* speed = (9878400 / (gus_audio_speed + 2)) / 16; */ - speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; - speed = (9878400 / (speed * 16)) - 2; - } - return speed; -} - -static int -gus_audio_set_channels (int channels) -{ - if (!channels) - return gus_audio_channels; - if (channels > 2) - channels = 2; - if (channels < 1) - channels = 1; - gus_audio_channels = channels; - return channels; -} - -static int -gus_audio_set_bits (int bits) -{ - if (!bits) - return gus_audio_bits; - - if (bits != 8 && bits != 16) - bits = 8; - - if (only_8_bits) - bits = 8; - - gus_audio_bits = bits; - return bits; -} - -static int -gus_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local) -{ - switch (cmd) - { - case SOUND_PCM_WRITE_RATE: - if (local) - return gus_audio_set_speed ((int) arg); - return snd_ioctl_return ((int *) arg, gus_audio_set_speed (get_user ((int *) arg))); - break; - - case SOUND_PCM_READ_RATE: - if (local) - return gus_audio_speed; - return snd_ioctl_return ((int *) arg, gus_audio_speed); - break; - - case SNDCTL_DSP_STEREO: - if (local) - return gus_audio_set_channels ((int) arg + 1) - 1; - return snd_ioctl_return ((int *) arg, gus_audio_set_channels (get_user ((int *) arg) + 1) - 1); - break; + switch (cmd) + { + case _GUS_NUMVOICES: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_select_max_voices(p1); + restore_flags(flags); + break; + + case _GUS_VOICESAMPLE: + guswave_set_instr(dev, voice, p1); + break; + + case _GUS_VOICEON: + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_on(p1); + restore_flags(flags); + break; + + case _GUS_VOICEOFF: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_off(); + restore_flags(flags); + break; + + case _GUS_VOICEFADE: + gus_voice_fade(voice); + break; + + case _GUS_VOICEMODE: + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_mode(p1); + restore_flags(flags); + break; + + case _GUS_VOICEBALA: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_balance(p1); + restore_flags(flags); + break; + + case _GUS_VOICEFREQ: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_freq(plong); + restore_flags(flags); + break; + + case _GUS_VOICEVOL: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_volume(p1); + restore_flags(flags); + break; + + case _GUS_VOICEVOL2: /* Just update the software voice level */ + voices[voice].initial_volume = voices[voice].current_volume = p1; + break; + + case _GUS_RAMPRANGE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_ramp_range(p1, p2); + restore_flags(flags); + break; + + case _GUS_RAMPRATE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NJET-NJET */ + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_ramp_rate(p1, p2); + restore_flags(flags); + break; + + case _GUS_RAMPMODE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_ramp_mode(p1); + restore_flags(flags); + break; + + case _GUS_RAMPON: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* EI-EI */ + save_flags(flags); + cli(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_rampon(p1); + restore_flags(flags); + break; + + case _GUS_RAMPOFF: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NEJ-NEJ */ + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_rampoff(); + restore_flags(flags); + break; + + case _GUS_VOLUME_SCALE: + volume_base = p1; + volume_scale = p2; + break; + + case _GUS_VOICE_POS: + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_set_voice_pos(voice, plong); + restore_flags(flags); + break; + + default: + } +} - case SOUND_PCM_WRITE_CHANNELS: - if (local) - return gus_audio_set_channels ((int) arg); - return snd_ioctl_return ((int *) arg, gus_audio_set_channels (get_user ((int *) arg))); - break; +static int gus_audio_set_speed(int speed) +{ + if (speed <= 0) + speed = gus_audio_speed; - case SOUND_PCM_READ_CHANNELS: - if (local) - return gus_audio_channels; - return snd_ioctl_return ((int *) arg, gus_audio_channels); - break; + if (speed < 4000) + speed = 4000; - case SNDCTL_DSP_SETFMT: - if (local) - return gus_audio_set_bits ((int) arg); - return snd_ioctl_return ((int *) arg, gus_audio_set_bits (get_user ((int *) arg))); - break; + if (speed > 44100) + speed = 44100; - case SOUND_PCM_READ_BITS: - if (local) - return gus_audio_bits; - return snd_ioctl_return ((int *) arg, gus_audio_bits); + gus_audio_speed = speed; - case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */ - return snd_ioctl_return ((int *) arg, -(EINVAL)); - break; - - case SOUND_PCM_READ_FILTER: - return snd_ioctl_return ((int *) arg, -(EINVAL)); - break; + if (only_read_access) + { + /* Compute nearest valid recording speed and return it */ - } - return -(EINVAL); + /* speed = (9878400 / (gus_audio_speed + 2)) / 16; */ + speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; + speed = (9878400 / (speed * 16)) - 2; + } + return speed; } -static void -gus_audio_reset (int dev) -{ - if (recording_active) - { - gus_write8 (0x49, 0x00); /* Halt recording */ - set_input_volumes (); - } +static int gus_audio_set_channels(int channels) +{ + if (!channels) + return gus_audio_channels; + if (channels > 2) + channels = 2; + if (channels < 1) + channels = 1; + gus_audio_channels = channels; + return channels; } -static int -gus_audio_open (int dev, int mode) +static int gus_audio_set_bits(int bits) { - if (gus_busy) - return -(EBUSY); - - if (gus_pnp_flag && mode & OPEN_READ) - { - printk ("Sound: This audio device doesn't have recording capability\n"); - return -(EIO); - } - gus_initialize (); - - gus_busy = 1; - active_device = 0; + if (!bits) + return gus_audio_bits; - gus_reset (); - reset_sample_memory (); - gus_select_max_voices (14); + if (bits != 8 && bits != 16) + bits = 8; - pcm_active = 0; - dma_active = 0; - pcm_opened = 1; - if (mode & OPEN_READ) - { - recording_active = 1; - set_input_volumes (); - } - only_read_access = !(mode & OPEN_WRITE); - only_8_bits = mode & OPEN_READ; - if (only_8_bits) - audio_devs[dev]->format_mask = AFMT_U8; - else - audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE; + if (only_8_bits) + bits = 8; - return 0; + gus_audio_bits = bits; + return bits; } -static void -gus_audio_close (int dev) +static int gus_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) { - gus_reset (); - gus_busy = 0; - pcm_opened = 0; - active_device = 0; + int val; - if (recording_active) - { - gus_write8 (0x49, 0x00); /* Halt recording */ - set_input_volumes (); - } - - recording_active = 0; + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_speed(val); + break; + + case SOUND_PCM_READ_RATE: + val = gus_audio_speed; + break; + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_channels(val + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_channels(val); + break; + + case SOUND_PCM_READ_CHANNELS: + val = gus_audio_channels; + break; + + case SNDCTL_DSP_SETFMT: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = gus_audio_set_bits(val); + break; + + case SOUND_PCM_READ_BITS: + val = gus_audio_bits; + break; + + case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */ + case SOUND_PCM_READ_FILTER: + val = -EINVAL; + break; + default: + return -EINVAL; + } + return put_user(val, (int *)arg); } -static void -gus_audio_update_volume (void) +static void gus_audio_reset(int dev) { - unsigned long flags; - int voice; - - if (pcm_active && pcm_opened) - for (voice = 0; voice < gus_audio_channels; voice++) - { - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_rampoff (); - gus_voice_volume (1530 + (25 * gus_pcm_volume)); - gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); - restore_flags (flags); - } + if (recording_active) + { + gus_write8(0x49, 0x00); /* Halt recording */ + set_input_volumes(); + } } -static void -play_next_pcm_block (void) -{ - unsigned long flags; - int speed = gus_audio_speed; - int this_one, is16bits, chn; - unsigned long dram_loc; - unsigned char mode[2], ramp_mode[2]; - - if (!pcm_qlen) - return; - - this_one = pcm_head; +static int saved_iw_mode; /* A hack hack hack */ - for (chn = 0; chn < gus_audio_channels; chn++) - { - mode[chn] = 0x00; - ramp_mode[chn] = 0x03; /* Ramping and rollover off */ +static int gus_audio_open(int dev, int mode) +{ + if (gus_busy) + return -EBUSY; - if (chn == 0) + if (gus_pnp_flag && mode & OPEN_READ) { - mode[chn] |= 0x20; /* Loop IRQ */ - voices[chn].loop_irq_mode = LMODE_PCM; +/* printk(KERN_ERR "GUS: Audio device #%d is playback only.\n", dev);*/ + return -EIO; } + gus_initialize(); - if (gus_audio_bits != 8) - { - is16bits = 1; - mode[chn] |= 0x04; /* 16 bit data */ - } - else - is16bits = 0; - - dram_loc = this_one * pcm_bsize; - dram_loc += chn * pcm_banksize; + gus_busy = 1; + active_device = 0; - if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */ + gus_reset(); + reset_sample_memory(); + gus_select_max_voices(14); + saved_iw_mode = iw_mode; + if (iw_mode) { - mode[chn] |= 0x08; /* Enable loop */ - ramp_mode[chn] = 0x03; /* Disable rollover bit */ + /* There are some problems with audio in enhanced mode so disable it */ + gus_write8(0x19, gus_read8(0x19) & ~0x01); /* Disable enhanced mode */ + iw_mode = 0; } - else - { - if (chn == 0) - ramp_mode[chn] = 0x04; /* Enable rollover bit */ - } - - save_flags (flags); - cli (); - gus_select_voice (chn); - gus_voice_freq (speed); - - if (gus_audio_channels == 1) - gus_voice_balance (7); /* mono */ - else if (chn == 0) - gus_voice_balance (0); /* left */ - else - gus_voice_balance (15); /* right */ - - if (!pcm_active) /* Playback not already active */ + pcm_active = 0; + dma_active = 0; + pcm_opened = 1; + if (mode & OPEN_READ) { - /* - * The playback was not started yet (or there has been a pause). - * Start the voice (again) and ask for a rollover irq at the end of - * this_one block. If this_one one is last of the buffers, use just - * the normal loop with irq. - */ - - gus_voice_off (); - gus_rampoff (); - gus_voice_volume (1530 + (25 * gus_pcm_volume)); - gus_ramp_range (65, 1530 + (25 * gus_pcm_volume)); - - gus_write_addr (0x0a, dram_loc, 0, is16bits); /* Starting position */ - gus_write_addr (0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */ - - if (chn != 0) - gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1, - 0, is16bits); /* Loop end location */ + recording_active = 1; + set_input_volumes(); } + only_read_access = !(mode & OPEN_WRITE); + only_8_bits = mode & OPEN_READ; + if (only_8_bits) + audio_devs[dev]->format_mask = AFMT_U8; + else + audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE; - if (chn == 0) - gus_write_addr (0x04, dram_loc + pcm_datasize[this_one] - 1, - 0, is16bits); /* Loop end location */ - else - mode[chn] |= 0x08; /* Enable looping */ - - if (pcm_datasize[this_one] != pcm_bsize) - { - /* - * Incompletely filled block. Possibly the last one. - */ - if (chn == 0) - { - mode[chn] &= ~0x08; /* Disable looping */ - mode[chn] |= 0x20; /* Enable IRQ at the end */ - voices[0].loop_irq_mode = LMODE_PCM_STOP; - ramp_mode[chn] = 0x03; /* No rollover bit */ - } - else - { - gus_write_addr (0x04, dram_loc + pcm_datasize[this_one], - 0, is16bits); /* Loop end location */ - mode[chn] &= ~0x08; /* Disable looping */ - } - } - - restore_flags (flags); - } - - for (chn = 0; chn < gus_audio_channels; chn++) - { - save_flags (flags); - cli (); - gus_select_voice (chn); - gus_write8 (0x0d, ramp_mode[chn]); - gus_voice_on (mode[chn]); - restore_flags (flags); - } - - pcm_active = 1; + return 0; } -static void -gus_transfer_output_block (int dev, unsigned long buf, - int total_count, int intrflag, int chn) -{ - /* - * This routine transfers one block of audio data to the DRAM. In mono mode - * it's called just once. When in stereo mode, this_one routine is called - * once for both channels. - * - * The left/mono channel data is transferred to the beginning of dram and the - * right data to the area pointed by gus_page_size. - */ - - int this_one, count; - unsigned long flags; - unsigned char dma_command; - unsigned long address, hold_address; - - save_flags (flags); - cli (); - - count = total_count / gus_audio_channels; - - if (chn == 0) - { - if (pcm_qlen >= pcm_nblk) - printk ("GUS Warning: PCM buffers out of sync\n"); - - this_one = pcm_current_block = pcm_tail; - pcm_qlen++; - pcm_tail = (pcm_tail + 1) % pcm_nblk; - pcm_datasize[this_one] = count; - } - else - this_one = pcm_current_block; - - gus_write8 (0x41, 0); /* Disable GF1 DMA */ - DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE); - - address = this_one * pcm_bsize; - address += chn * pcm_banksize; - - if (audio_devs[dev]->dmachan1 > 3) - { - hold_address = address; - address = address >> 1; - address &= 0x0001ffffL; - address |= (hold_address & 0x000c0000L); - } - - gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ - - dma_command = 0x21; /* IRQ enable, DMA start */ - - if (gus_audio_bits != 8) - dma_command |= 0x40; /* 16 bit _DATA_ */ - else - dma_command |= 0x80; /* Invert MSB */ - - if (audio_devs[dev]->dmachan1 > 3) - dma_command |= 0x04; /* 16 bit DMA channel */ - - gus_write8 (0x41, dma_command); /* Kick start */ +static void gus_audio_close(int dev) +{ + iw_mode = saved_iw_mode; + gus_reset(); + gus_busy = 0; + pcm_opened = 0; + active_device = 0; - if (chn == (gus_audio_channels - 1)) /* Last channel */ - { - /* - * Last (right or mono) channel data - */ - dma_active = 1; /* DMA started. There is a unacknowledged buffer */ - active_device = GUS_DEV_PCM_DONE; - if (!pcm_active && (pcm_qlen > 0 || count < pcm_bsize)) + if (recording_active) { - play_next_pcm_block (); + gus_write8(0x49, 0x00); /* Halt recording */ + set_input_volumes(); } - } - else - { - /* - * Left channel data. The right channel - * is transferred after DMA interrupt - */ - active_device = GUS_DEV_PCM_CONTINUE; - } - - restore_flags (flags); + recording_active = 0; } -static void -gus_audio_output_block (int dev, unsigned long buf, int total_count, - int intrflag, int restart_dma) +static void gus_audio_update_volume(void) { - pcm_current_buf = buf; - pcm_current_count = total_count; - pcm_current_intrflag = intrflag; - pcm_current_dev = dev; - gus_transfer_output_block (dev, buf, total_count, intrflag, 0); -} + unsigned long flags; + int voice; -static void -gus_audio_start_input (int dev, unsigned long buf, int count, - int intrflag, int restart_dma) -{ - unsigned long flags; - unsigned char mode; - - save_flags (flags); - cli (); - - DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); - - mode = 0xa0; /* DMA IRQ enabled, invert MSB */ - - if (audio_devs[dev]->dmachan2 > 3) - mode |= 0x04; /* 16 bit DMA channel */ - if (gus_audio_channels > 1) - mode |= 0x02; /* Stereo */ - mode |= 0x01; /* DMA enable */ - - gus_write8 (0x49, mode); - - restore_flags (flags); + if (pcm_active && pcm_opened) + for (voice = 0; voice < gus_audio_channels; voice++) + { + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_rampoff(); + gus_voice_volume(1530 + (25 * gus_pcm_volume)); + gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); + restore_flags(flags); + } } -static int -gus_audio_prepare_for_input (int dev, int bsize, int bcount) +static void play_next_pcm_block(void) { - unsigned int rate; + unsigned long flags; + int speed = gus_audio_speed; + int this_one, is16bits, chn; + unsigned long dram_loc; + unsigned char mode[2], ramp_mode[2]; - rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; + if (!pcm_qlen) + return; - gus_write8 (0x48, rate & 0xff); /* Set sampling rate */ + this_one = pcm_head; - if (gus_audio_bits != 8) - { - printk ("GUS Error: 16 bit recording not supported\n"); - return -(EINVAL); - } - - return 0; + for (chn = 0; chn < gus_audio_channels; chn++) + { + mode[chn] = 0x00; + ramp_mode[chn] = 0x03; /* Ramping and rollover off */ + + if (chn == 0) + { + mode[chn] |= 0x20; /* Loop IRQ */ + voices[chn].loop_irq_mode = LMODE_PCM; + } + if (gus_audio_bits != 8) + { + is16bits = 1; + mode[chn] |= 0x04; /* 16 bit data */ + } + else + is16bits = 0; + + dram_loc = this_one * pcm_bsize; + dram_loc += chn * pcm_banksize; + + if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */ + { + mode[chn] |= 0x08; /* Enable loop */ + ramp_mode[chn] = 0x03; /* Disable rollover bit */ + } + else + { + if (chn == 0) + ramp_mode[chn] = 0x04; /* Enable rollover bit */ + } + save_flags(flags); + cli(); + gus_select_voice(chn); + gus_voice_freq(speed); + + if (gus_audio_channels == 1) + gus_voice_balance(7); /* mono */ + else if (chn == 0) + gus_voice_balance(0); /* left */ + else + gus_voice_balance(15); /* right */ + + if (!pcm_active) /* Playback not already active */ + { + /* + * The playback was not started yet (or there has been a pause). + * Start the voice (again) and ask for a rollover irq at the end of + * this_one block. If this_one one is last of the buffers, use just + * the normal loop with irq. + */ + + gus_voice_off(); + gus_rampoff(); + gus_voice_volume(1530 + (25 * gus_pcm_volume)); + gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); + + gus_write_addr(0x0a, chn * pcm_banksize, 0, is16bits); /* Starting position */ + gus_write_addr(0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */ + + if (chn != 0) + gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1, + 0, is16bits); /* Loop end location */ + } + if (chn == 0) + gus_write_addr(0x04, dram_loc + pcm_bsize - 1, + 0, is16bits); /* Loop end location */ + else + mode[chn] |= 0x08; /* Enable looping */ + restore_flags(flags); + } + for (chn = 0; chn < gus_audio_channels; chn++) + { + save_flags(flags); + cli(); + gus_select_voice(chn); + gus_write8(0x0d, ramp_mode[chn]); + if (iw_mode) + gus_write8(0x15, 0x00); /* Reset voice deactivate bit of SMSI */ + gus_voice_on(mode[chn]); + restore_flags(flags); + } + pcm_active = 1; } -static int -gus_audio_prepare_for_output (int dev, int bsize, int bcount) +static void gus_transfer_output_block(int dev, unsigned long buf, + int total_count, int intrflag, int chn) { - int i; + /* + * This routine transfers one block of audio data to the DRAM. In mono mode + * it's called just once. When in stereo mode, this_one routine is called + * once for both channels. + * + * The left/mono channel data is transferred to the beginning of dram and the + * right data to the area pointed by gus_page_size. + */ - long mem_ptr, mem_size; + int this_one, count; + unsigned long flags; + unsigned char dma_command; + unsigned long address, hold_address; - mem_ptr = 0; - mem_size = gus_mem_size / gus_audio_channels; + save_flags(flags); + cli(); - if (mem_size > (256 * 1024)) - mem_size = 256 * 1024; + count = total_count / gus_audio_channels; - pcm_bsize = bsize / gus_audio_channels; - pcm_head = pcm_tail = pcm_qlen = 0; + if (chn == 0) + { + if (pcm_qlen >= pcm_nblk) + printk(KERN_WARNING "GUS Warning: PCM buffers out of sync\n"); - pcm_nblk = MAX_PCM_BUFFERS; - if ((pcm_bsize * pcm_nblk) > mem_size) - pcm_nblk = mem_size / pcm_bsize; + this_one = pcm_current_block = pcm_tail; + pcm_qlen++; + pcm_tail = (pcm_tail + 1) % pcm_nblk; + pcm_datasize[this_one] = count; + } + else + this_one = pcm_current_block; - for (i = 0; i < pcm_nblk; i++) - pcm_datasize[i] = 0; + gus_write8(0x41, 0); /* Disable GF1 DMA */ + DMAbuf_start_dma(dev, buf + (chn * count), count, DMA_MODE_WRITE); - pcm_banksize = pcm_nblk * pcm_bsize; + address = this_one * pcm_bsize; + address += chn * pcm_banksize; - if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024)) - pcm_nblk--; + if (audio_devs[dev]->dmap_out->dma > 3) + { + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */ - return 0; -} + dma_command = 0x21; /* IRQ enable, DMA start */ -static int -gus_local_qlen (int dev) -{ - return pcm_qlen; -} + if (gus_audio_bits != 8) + dma_command |= 0x40; /* 16 bit _DATA_ */ + else + dma_command |= 0x80; /* Invert MSB */ -static void -gus_copy_from_user (int dev, char *localbuf, int localoffs, - const char *userbuf, int useroffs, int len) -{ - if (gus_audio_channels == 1) - { - memcpy_fromfs (&localbuf[localoffs], &(userbuf)[useroffs], len); - } - else if (gus_audio_bits == 8) - { - int in_left = useroffs; - int in_right = useroffs + 1; - char *out_left, *out_right; - int i; + if (audio_devs[dev]->dmap_out->dma > 3) + dma_command |= 0x04; /* 16 bit DMA channel */ - len /= 2; - localoffs /= 2; - out_left = localbuf + localoffs; - out_right = out_left + pcm_bsize; + gus_write8(0x41, dma_command); /* Kick start */ - for (i = 0; i < len; i++) + if (chn == (gus_audio_channels - 1)) /* Last channel */ { - *out_left++ = get_fs_byte (&((userbuf)[in_left])); - in_left += 2; - *out_right++ = get_fs_byte (&((userbuf)[in_right])); - in_right += 2; + /* + * Last (right or mono) channel data + */ + dma_active = 1; /* DMA started. There is a unacknowledged buffer */ + active_device = GUS_DEV_PCM_DONE; + if (!pcm_active && (pcm_qlen > 1 || count < pcm_bsize)) + { + play_next_pcm_block(); + } } - } - else - { - int in_left = useroffs; - int in_right = useroffs + 2; - short *out_left, *out_right; - int i; - - len /= 4; - localoffs /= 4; - - out_left = ((short *) localbuf) + localoffs; - out_right = out_left + (pcm_bsize / 2); - - for (i = 0; i < len; i++) + else { - *out_left++ = get_fs_word (&((userbuf)[in_left])); - in_left += 4; - *out_right++ = get_fs_word (&((userbuf)[in_right])); - in_right += 4; + /* + * Left channel data. The right channel + * is transferred after DMA interrupt + */ + active_device = GUS_DEV_PCM_CONTINUE; } - } -} -static struct audio_driver gus_audio_driver = -{ - gus_audio_open, - gus_audio_close, - gus_audio_output_block, - gus_audio_start_input, - gus_audio_ioctl, - gus_audio_prepare_for_input, - gus_audio_prepare_for_output, - gus_audio_reset, - gus_audio_reset, - gus_local_qlen, - gus_copy_from_user -}; - -static struct audio_operations gus_audio_operations = -{ - "Gravis UltraSound", - NEEDS_RESTART, - AFMT_U8 | AFMT_S16_LE, - NULL, - &gus_audio_driver -}; + restore_flags(flags); +} -static void -guswave_setup_voice (int dev, int voice, int chn) +static void gus_uninterleave8(char *buf, int l) { - struct channel_info *info = - &synth_devs[dev]->chn_info[chn]; +/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */ + int i, p = 0, halfsize = l / 2; + char *buf2 = buf + halfsize, *src = bounce_buf; - guswave_set_instr (dev, voice, info->pgm_num); + memcpy(bounce_buf, buf, l); - voices[voice].expression_vol = - info->controllers[CTL_EXPRESSION]; /* Just MSB */ - voices[voice].main_vol = - (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128; - voices[voice].panning = - (info->controllers[CTL_PAN] * 2) - 128; - voices[voice].bender = info->bender_value; + for (i = 0; i < halfsize; i++) + { + buf[i] = src[p++]; /* Left channel */ + buf2[i] = src[p++]; /* Right channel */ + } } -static void -guswave_bender (int dev, int voice, int value) +static void gus_uninterleave16(short *buf, int l) { - int freq; - unsigned long flags; +/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */ + int i, p = 0, halfsize = l / 2; + short *buf2 = buf + halfsize, *src = (short *) bounce_buf; - voices[voice].bender = value - 8192; - freq = compute_finetune (voices[voice].orig_freq, value - 8192, - voices[voice].bender_range); - voices[voice].current_freq = freq; + memcpy(bounce_buf, (char *) buf, l * 2); - save_flags (flags); - cli (); - gus_select_voice (voice); - gus_voice_freq (freq); - restore_flags (flags); + for (i = 0; i < halfsize; i++) + { + buf[i] = src[p++]; /* Left channel */ + buf2[i] = src[p++]; /* Right channel */ + } } -static int -guswave_patchmgr (int dev, struct patmgr_info *rec) +static void gus_audio_output_block(int dev, unsigned long buf, int total_count, + int intrflag) { - int i, n; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - switch (rec->command) - { - case PM_GET_DEVTYPE: - rec->parm1 = PMTYPE_WAVE; - return 0; - break; + dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT; - case PM_GET_NRPGM: - rec->parm1 = MAX_PATCH; - return 0; - break; - - case PM_GET_PGMMAP: - rec->parm1 = MAX_PATCH; - - for (i = 0; i < MAX_PATCH; i++) + pcm_current_buf = buf; + pcm_current_count = total_count; + pcm_current_intrflag = intrflag; + pcm_current_dev = dev; + if (gus_audio_channels == 2) { - int ptr = patch_table[i]; + char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys); - rec->data.data8[i] = 0; - - while (ptr >= 0 && ptr < free_sample && ptr != NOT_SAMPLE) - { - rec->data.data8[i]++; - ptr = samples[ptr].key; /* Follow link */ - } + if (gus_audio_bits == 8) + gus_uninterleave8(b, total_count); + else + gus_uninterleave16((short *) b, total_count / 2); } - return 0; - break; - - case PM_GET_PGM_PATCHES: - { - int ptr = patch_table[rec->parm1]; - - n = 0; - - while (ptr >= 0 && ptr < free_sample && ptr != NOT_SAMPLE) - { - rec->data.data32[n++] = ptr; - ptr = samples[ptr].key; /* Follow link */ - } - } - rec->parm1 = n; - return 0; - break; - - case PM_GET_PATCH: - { - int ptr = rec->parm1; - struct patch_info *pat; - - if (ptr < 0 || ptr >= free_sample) - return -(EINVAL); + gus_transfer_output_block(dev, buf, total_count, intrflag, 0); +} - memcpy (rec->data.data8, (char *) &samples[ptr], - sizeof (struct patch_info)); +static void gus_audio_start_input(int dev, unsigned long buf, int count, + int intrflag) +{ + unsigned long flags; + unsigned char mode; - pat = (struct patch_info *) rec->data.data8; + save_flags(flags); + cli(); - pat->key = GUS_PATCH; /* Restore patch type */ - rec->parm1 = sample_ptrs[ptr]; /* DRAM location */ - rec->parm2 = sizeof (struct patch_info); - } - return 0; - break; + DMAbuf_start_dma(dev, buf, count, DMA_MODE_READ); + mode = 0xa0; /* DMA IRQ enabled, invert MSB */ - case PM_SET_PATCH: - { - int ptr = rec->parm1; - struct patch_info *pat; + if (audio_devs[dev]->dmap_in->dma > 3) + mode |= 0x04; /* 16 bit DMA channel */ + if (gus_audio_channels > 1) + mode |= 0x02; /* Stereo */ + mode |= 0x01; /* DMA enable */ - if (ptr < 0 || ptr >= free_sample) - return -(EINVAL); + gus_write8(0x49, mode); + restore_flags(flags); +} - pat = (struct patch_info *) rec->data.data8; +static int gus_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + unsigned int rate; - if (pat->len > samples[ptr].len) /* Cannot expand sample */ - return -(EINVAL); + gus_audio_bsize = bsize; + audio_devs[dev]->dmap_in->flags |= DMA_NODMA; + rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16; - pat->key = samples[ptr].key; /* Ensure the link is correct */ + gus_write8(0x48, rate & 0xff); /* Set sampling rate */ - memcpy ((char *) &samples[ptr], rec->data.data8, - sizeof (struct patch_info)); + if (gus_audio_bits != 8) + { +/* printk("GUS Error: 16 bit recording not supported\n");*/ + return -EINVAL; + } + return 0; +} - pat->key = GUS_PATCH; - } - return 0; - break; +static int gus_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + int i; - case PM_READ_PATCH: /* Returns a block of wave data from the DRAM */ - { - int sample = rec->parm1; - int n; - long offs = rec->parm2; - int l = rec->parm3; + long mem_ptr, mem_size; - if (sample < 0 || sample >= free_sample) - return -(EINVAL); + audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT; + mem_ptr = 0; + mem_size = gus_mem_size / gus_audio_channels; - if (offs < 0 || offs >= samples[sample].len) - return -(EINVAL); /* Invalid offset */ + if (mem_size > (256 * 1024)) + mem_size = 256 * 1024; - n = samples[sample].len - offs; /* Num of bytes left */ + pcm_bsize = bsize / gus_audio_channels; + pcm_head = pcm_tail = pcm_qlen = 0; - if (l > n) - l = n; + pcm_nblk = 2; /* MAX_PCM_BUFFERS; */ + if ((pcm_bsize * pcm_nblk) > mem_size) + pcm_nblk = mem_size / pcm_bsize; - if (l > sizeof (rec->data.data8)) - l = sizeof (rec->data.data8); + for (i = 0; i < pcm_nblk; i++) + pcm_datasize[i] = 0; - if (l <= 0) - return -(EINVAL); /* - * Was there a bug? - */ + pcm_banksize = pcm_nblk * pcm_bsize; - offs += sample_ptrs[sample]; /* - * Begin offsets + offset to DRAM - */ + if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024)) + pcm_nblk--; + gus_write8(0x41, 0); /* Disable GF1 DMA */ + return 0; +} - for (n = 0; n < l; n++) - rec->data.data8[n] = gus_peek (offs++); - rec->parm1 = n; /* - * Nr of bytes copied - */ - } - return 0; - break; +static int gus_local_qlen(int dev) +{ + return pcm_qlen; +} - case PM_WRITE_PATCH: /* - * Writes a block of wave data to the DRAM - */ - { - int sample = rec->parm1; - int n; - long offs = rec->parm2; - int l = rec->parm3; - - if (sample < 0 || sample >= free_sample) - return -(EINVAL); - - if (offs < 0 || offs >= samples[sample].len) - return -(EINVAL); /* - * Invalid offset - */ - n = samples[sample].len - offs; /* - * Nr of bytes left - */ +static struct audio_driver gus_audio_driver = +{ + gus_audio_open, + gus_audio_close, + gus_audio_output_block, + gus_audio_start_input, + gus_audio_ioctl, + gus_audio_prepare_for_input, + gus_audio_prepare_for_output, + gus_audio_reset, + gus_local_qlen, + NULL +}; - if (l > n) - l = n; +static void guswave_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info = &synth_devs[dev]->chn_info[chn]; - if (l > sizeof (rec->data.data8)) - l = sizeof (rec->data.data8); + guswave_set_instr(dev, voice, info->pgm_num); + voices[voice].expression_vol = info->controllers[CTL_EXPRESSION]; /* Just MSB */ + voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128; + voices[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; + voices[voice].bender = 0; + voices[voice].bender_range = info->bender_range; - if (l <= 0) - return -(EINVAL); /* - * Was there a bug? - */ + if (chn == 9) + voices[voice].fixed_pitch = 1; +} - offs += sample_ptrs[sample]; /* - * Begin offsets + offset to DRAM - */ +static void guswave_bender(int dev, int voice, int value) +{ + int freq; + unsigned long flags; - for (n = 0; n < l; n++) - gus_poke (offs++, rec->data.data8[n]); - rec->parm1 = n; /* - * Nr of bytes copied - */ - } - return 0; - break; + voices[voice].bender = value - 8192; + freq = compute_finetune(voices[voice].orig_freq, value - 8192, voices[voice].bender_range, 0); + voices[voice].current_freq = freq; - default: - return -(EINVAL); - } + save_flags(flags); + cli(); + gus_select_voice(voice); + gus_voice_freq(freq); + restore_flags(flags); } -static int -guswave_alloc (int dev, int chn, int note, struct voice_alloc_info *alloc) +static int guswave_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) { - int i, p, best = -1, best_time = 0x7fffffff; + int i, p, best = -1, best_time = 0x7fffffff; - p = alloc->ptr; - /* - * First look for a completely stopped voice - */ + p = alloc->ptr; + /* + * First look for a completely stopped voice + */ - for (i = 0; i < alloc->max_voice; i++) - { - if (alloc->map[p] == 0) - { - alloc->ptr = p; - return p; - } - if (alloc->alloc_times[p] < best_time) + for (i = 0; i < alloc->max_voice; i++) { - best = p; - best_time = alloc->alloc_times[p]; + if (alloc->map[p] == 0) + { + alloc->ptr = p; + return p; + } + if (alloc->alloc_times[p] < best_time) + { + best = p; + best_time = alloc->alloc_times[p]; + } + p = (p + 1) % alloc->max_voice; } - p = (p + 1) % alloc->max_voice; - } - /* - * Then look for a releasing voice - */ + /* + * Then look for a releasing voice + */ - for (i = 0; i < alloc->max_voice; i++) - { - if (alloc->map[p] == 0xffff) + for (i = 0; i < alloc->max_voice; i++) { - alloc->ptr = p; - return p; + if (alloc->map[p] == 0xffff) + { + alloc->ptr = p; + return p; + } + p = (p + 1) % alloc->max_voice; } - p = (p + 1) % alloc->max_voice; - } - - if (best >= 0) - p = best; + if (best >= 0) + p = best; - alloc->ptr = p; - return p; + alloc->ptr = p; + return p; } static struct synth_operations guswave_operations = { - &gus_info, - 0, - SYNTH_TYPE_SAMPLE, - SAMPLE_TYPE_GUS, - guswave_open, - guswave_close, - guswave_ioctl, - guswave_kill_note, - guswave_start_note, - guswave_set_instr, - guswave_reset, - guswave_hw_control, - guswave_load_patch, - guswave_aftertouch, - guswave_controller, - guswave_panning, - guswave_volume_method, - guswave_patchmgr, - guswave_bender, - guswave_alloc, - guswave_setup_voice + "GUS", + &gus_info, + 0, + SYNTH_TYPE_SAMPLE, + SAMPLE_TYPE_GUS, + guswave_open, + guswave_close, + guswave_ioctl, + guswave_kill_note, + guswave_start_note, + guswave_set_instr, + guswave_reset, + guswave_hw_control, + guswave_load_patch, + guswave_aftertouch, + guswave_controller, + guswave_panning, + guswave_volume_method, + guswave_bender, + guswave_alloc, + guswave_setup_voice }; -static void -set_input_volumes (void) +static void set_input_volumes(void) { - unsigned long flags; - unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ - - if (have_gus_max) /* Don't disturb GUS MAX */ - return; + unsigned long flags; + unsigned char mask = 0xff & ~0x06; /* Just line out enabled */ - save_flags (flags); - cli (); + if (have_gus_max) /* Don't disturb GUS MAX */ + return; - /* - * Enable channels having vol > 10% - * Note! bit 0x01 means the line in DISABLED while 0x04 means - * the mic in ENABLED. - */ - if (gus_line_vol > 10) - mask &= ~0x01; - if (gus_mic_vol > 10) - mask |= 0x04; + save_flags(flags); + cli(); - if (recording_active) - { - /* - * Disable channel, if not selected for recording - */ - if (!(gus_recmask & SOUND_MASK_LINE)) - mask |= 0x01; - if (!(gus_recmask & SOUND_MASK_MIC)) - mask &= ~0x04; - } + /* + * Enable channels having vol > 10% + * Note! bit 0x01 means the line in DISABLED while 0x04 means + * the mic in ENABLED. + */ + if (gus_line_vol > 10) + mask &= ~0x01; + if (gus_mic_vol > 10) + mask |= 0x04; - mix_image &= ~0x07; - mix_image |= mask & 0x07; - outb (mix_image, u_Mixer); + if (recording_active) + { + /* + * Disable channel, if not selected for recording + */ + if (!(gus_recmask & SOUND_MASK_LINE)) + mask |= 0x01; + if (!(gus_recmask & SOUND_MASK_MIC)) + mask &= ~0x04; + } + mix_image &= ~0x07; + mix_image |= mask & 0x07; + outb((mix_image), u_Mixer); - restore_flags (flags); + restore_flags(flags); } -int -gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) -{ #define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ SOUND_MASK_SYNTH|SOUND_MASK_PCM) - if (((cmd >> 8) & 0xff) == 'M') - { - if (_IOC_DIR (cmd) & _IOC_WRITE) - switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - gus_recmask = get_user ((int *) arg) & MIX_DEVS; - if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) - gus_recmask = SOUND_MASK_MIC; - /* Note! Input volumes are updated during next open for recording */ - return snd_ioctl_return ((int *) arg, gus_recmask); - break; - - case SOUND_MIXER_MIC: - { - int vol = get_user ((int *) arg) & 0xff; - - if (vol < 0) - vol = 0; - if (vol > 100) - vol = 100; - gus_mic_vol = vol; - set_input_volumes (); - return snd_ioctl_return ((int *) arg, vol | (vol << 8)); - } - break; - - case SOUND_MIXER_LINE: - { - int vol = get_user ((int *) arg) & 0xff; - - if (vol < 0) - vol = 0; - if (vol > 100) - vol = 100; - gus_line_vol = vol; - set_input_volumes (); - return snd_ioctl_return ((int *) arg, vol | (vol << 8)); - } - break; - - case SOUND_MIXER_PCM: - gus_pcm_volume = get_user ((int *) arg) & 0xff; - if (gus_pcm_volume < 0) - gus_pcm_volume = 0; - if (gus_pcm_volume > 100) - gus_pcm_volume = 100; - gus_audio_update_volume (); - return snd_ioctl_return ((int *) arg, gus_pcm_volume | (gus_pcm_volume << 8)); - break; - - case SOUND_MIXER_SYNTH: - { - int voice; - - gus_wave_volume = get_user ((int *) arg) & 0xff; - - if (gus_wave_volume < 0) - gus_wave_volume = 0; - if (gus_wave_volume > 100) - gus_wave_volume = 100; - - if (active_device == GUS_DEV_WAVE) - for (voice = 0; voice < nr_voices; voice++) - dynamic_volume_change (voice); /* Apply the new vol */ - - return snd_ioctl_return ((int *) arg, gus_wave_volume | (gus_wave_volume << 8)); - } - break; - - default: - return -(EINVAL); - } - else - switch (cmd & 0xff) /* - * Return parameters - */ - { - - case SOUND_MIXER_RECSRC: - return snd_ioctl_return ((int *) arg, gus_recmask); - break; - - case SOUND_MIXER_DEVMASK: - return snd_ioctl_return ((int *) arg, MIX_DEVS); - break; - - case SOUND_MIXER_STEREODEVS: - return snd_ioctl_return ((int *) arg, 0); - break; - - case SOUND_MIXER_RECMASK: - return snd_ioctl_return ((int *) arg, SOUND_MASK_MIC | SOUND_MASK_LINE); - break; - case SOUND_MIXER_CAPS: - return snd_ioctl_return ((int *) arg, 0); - break; - - case SOUND_MIXER_MIC: - return snd_ioctl_return ((int *) arg, gus_mic_vol | (gus_mic_vol << 8)); - break; - - case SOUND_MIXER_LINE: - return snd_ioctl_return ((int *) arg, gus_line_vol | (gus_line_vol << 8)); - break; +int gus_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int vol, val; - case SOUND_MIXER_PCM: - return snd_ioctl_return ((int *) arg, gus_pcm_volume | (gus_pcm_volume << 8)); - break; + if (((cmd >> 8) & 0xff) != 'M') + return -EINVAL; - case SOUND_MIXER_SYNTH: - return snd_ioctl_return ((int *) arg, gus_wave_volume | (gus_wave_volume << 8)); - break; + if (!access_ok(VERIFY_WRITE, (int *)arg, sizeof(int))) + return -EFAULT; - default: - return -(EINVAL); - } - } - else - return -(EINVAL); + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + { + if (__get_user(val, (int *) arg)) + return -EFAULT; + + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + gus_recmask = val & MIX_DEVS; + if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE))) + gus_recmask = SOUND_MASK_MIC; + /* Note! Input volumes are updated during next open for recording */ + val = gus_recmask; + break; + + case SOUND_MIXER_MIC: + vol = val & 0xff; + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_mic_vol = vol; + set_input_volumes(); + val = vol | (vol << 8); + break; + + case SOUND_MIXER_LINE: + vol = val & 0xff; + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_line_vol = vol; + set_input_volumes(); + val = vol | (vol << 8); + break; + + case SOUND_MIXER_PCM: + gus_pcm_volume = val & 0xff; + if (gus_pcm_volume < 0) + gus_pcm_volume = 0; + if (gus_pcm_volume > 100) + gus_pcm_volume = 100; + gus_audio_update_volume(); + val = gus_pcm_volume | (gus_pcm_volume << 8); + break; + + case SOUND_MIXER_SYNTH: + gus_wave_volume = val & 0xff; + if (gus_wave_volume < 0) + gus_wave_volume = 0; + if (gus_wave_volume > 100) + gus_wave_volume = 100; + if (active_device == GUS_DEV_WAVE) + { + int voice; + for (voice = 0; voice < nr_voices; voice++) + dynamic_volume_change(voice); /* Apply the new vol */ + } + val = gus_wave_volume | (gus_wave_volume << 8); + break; + + default: + return -EINVAL; + } + } + else + { + switch (cmd & 0xff) + { + /* + * Return parameters + */ + case SOUND_MIXER_RECSRC: + val = gus_recmask; + break; + + case SOUND_MIXER_DEVMASK: + val = MIX_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + val = 0; + break; + + case SOUND_MIXER_RECMASK: + val = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + + case SOUND_MIXER_CAPS: + val = 0; + break; + + case SOUND_MIXER_MIC: + val = gus_mic_vol | (gus_mic_vol << 8); + break; + + case SOUND_MIXER_LINE: + val = gus_line_vol | (gus_line_vol << 8); + break; + + case SOUND_MIXER_PCM: + val = gus_pcm_volume | (gus_pcm_volume << 8); + break; + + case SOUND_MIXER_SYNTH: + val = gus_wave_volume | (gus_wave_volume << 8); + break; + + default: + return -EINVAL; + } + } + return __put_user(val, (int *)arg); } static struct mixer_operations gus_mixer_operations = { - "GUS", - "Gravis Ultrasound", - gus_default_mixer_ioctl + "GUS", + "Gravis Ultrasound", + gus_default_mixer_ioctl }; -static void -gus_default_mixer_init (void) +static int gus_default_mixer_init(void) { - if (num_mixers < MAX_MIXER_DEV) /* - * Don't install if there is another - * mixer - */ - mixer_devs[num_mixers++] = &gus_mixer_operations; + int n; - if (have_gus_max) - { -/* - * Enable all mixer channels on the GF1 side. Otherwise recording will - * not be possible using GUS MAX. - */ - mix_image &= ~0x07; - mix_image |= 0x04; /* All channels enabled */ - outb (mix_image, u_Mixer); - } + if ((n = sound_alloc_mixerdev()) != -1) + { + /* + * Don't install if there is another + * mixer + */ + mixer_devs[n] = &gus_mixer_operations; + } + if (have_gus_max) + { + /* + * Enable all mixer channels on the GF1 side. Otherwise recording will + * not be possible using GUS MAX. + */ + mix_image &= ~0x07; + mix_image |= 0x04; /* All channels enabled */ + outb((mix_image), u_Mixer); + } + return n; } -void -gus_wave_init (struct address_info *hw_config) +void gus_wave_init(struct address_info *hw_config) { - unsigned long flags; - unsigned char val; - char *model_num = "2.4"; - int gus_type = 0x24; /* 2.4 */ + unsigned long flags; + unsigned char val; + char *model_num = "2.4"; + char tmp[64], tmp2[64]; + int gus_type = 0x24; /* 2.4 */ - int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2; + int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2; + int sdev; - if (!gus_pnp_flag) - if (irq < 0 || irq > 15) - { - printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq); - return; - } + hw_config->slots[0] = -1; /* No wave */ + hw_config->slots[1] = -1; /* No ad1848 */ + hw_config->slots[4] = -1; /* No audio */ + hw_config->slots[5] = -1; /* No mixer */ - if (dma < 0 || dma > 7 || dma == 4) - { - printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma); - return; - } + if (!gus_pnp_flag) + { + if (irq < 0 || irq > 15) + { + printk(KERN_ERR "ERROR! Invalid IRQ#%d. GUS Disabled", irq); + return; + } + } + + if (dma < 0 || dma > 7 || dma == 4) + { + printk(KERN_ERR "ERROR! Invalid DMA#%d. GUS Disabled", dma); + return; + } + gus_irq = irq; + gus_dma = dma; + gus_dma2 = dma2; + gus_hw_config = hw_config; - gus_irq = irq; - gus_dma = dma; - gus_dma2 = dma2; + if (gus_dma2 == -1) + gus_dma2 = dma; - if (gus_dma2 == -1) - gus_dma2 = dma; + /* + * Try to identify the GUS model. + * + * Versions < 3.6 don't have the digital ASIC. Try to probe it first. + */ - /* - * Try to identify the GUS model. - * - * Versions < 3.6 don't have the digital ASIC. Try to probe it first. - */ + save_flags(flags); + cli(); + outb((0x20), gus_base + 0x0f); + val = inb(gus_base + 0x0f); + restore_flags(flags); -#ifdef GUSPNP_AUTODETECT - val = gus_look8 (0x5b); /* Version number register */ - gus_write8 (0x5b, ~val); /* Invert all bits */ - - if ((gus_look8 (0x5b) & 0xf0) == (val & 0xf0)) /* No change */ - if ((gus_look8 (0x5b) & 0x0f) == ((~val) & 0x0f)) /* Change */ - { - DDB (printk ("Interwave chip version %d detected\n", (val & 0xf0) >> 4)); - gus_pnp_flag = 1; - } - else - { - DDB (printk ("Not an Interwave chip\n")); - gus_pnp_flag = 0; - } - gus_write8 (0x5b, val); /* Restore all bits */ + if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */ + { + int ad_flags = 0; + + if (gus_pnp_flag) + ad_flags = 0x12345678; /* Interwave "magic" */ + /* + * It has the digital ASIC so the card is at least v3.4. + * Next try to detect the true model. + */ + + if (gus_pnp_flag) /* Hack hack hack */ + val = 10; + else + val = inb(u_MixSelect); + + /* + * Value 255 means pre-3.7 which don't have mixer. + * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. + * 10 and above is GUS MAX which has the CS4231 codec/mixer. + * + */ + + if (val == 255 || val < 5) + { + model_num = "3.4"; + gus_type = 0x34; + } + else if (val < 10) + { + model_num = "3.7"; + gus_type = 0x37; + mixer_type = ICS2101; + request_region(u_MixSelect, 1, "GUS mixer"); + } + else + { + model_num = "MAX"; + gus_type = 0x40; + mixer_type = CS4231; +#ifdef CONFIG_GUSMAX + { + unsigned char max_config = 0x40; /* Codec enable */ + + if (gus_dma2 == -1) + gus_dma2 = gus_dma; + + if (gus_dma > 3) + max_config |= 0x10; /* 16 bit capture DMA */ + + if (gus_dma2 > 3) + max_config |= 0x20; /* 16 bit playback DMA */ + + max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ + + outb((max_config), gus_base + 0x106); /* UltraMax control */ + } + + if (ad1848_detect(gus_base + 0x10c, &ad_flags, hw_config->osp)) + { + char *name = "GUS MAX"; + int old_num_mixers = num_mixers; + + if (gus_pnp_flag) + name = "GUS PnP"; + + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + have_gus_max = 1; + if (hw_config->name) + name = hw_config->name; + + hw_config->slots[1] = ad1848_init(name, gus_base + 0x10c, + -irq, gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1, /* Share DMA channels with GF1 */ + hw_config->osp); + + if (num_mixers > old_num_mixers) + { + /* GUS has it's own mixer map */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } + } + else + printk(KERN_WARNING "GUS: No CS4231 ??"); +#else + printk(KERN_ERR "GUS MAX found, but not compiled in\n"); #endif - - save_flags (flags); - cli (); - outb (0x20, gus_base + 0x0f); - val = inb (gus_base + 0x0f); - restore_flags (flags); - - if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */ - { - int ad_flags = 0; - - if (gus_pnp_flag) - ad_flags = 0x12345678; /* Interwave "magic" */ - /* - * It has the digital ASIC so the card is at least v3.4. - * Next try to detect the true model. - */ - - if (gus_pnp_flag) /* Hack hack hack */ - val = 10; - else - val = inb (u_MixSelect); - - /* - * Value 255 means pre-3.7 which don't have mixer. - * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. - * 10 and above is GUS MAX which has the CS4231 codec/mixer. - * - */ - - if (val == 255 || val < 5) + } + } + else { - model_num = "3.4"; - gus_type = 0x34; + /* + * ASIC not detected so the card must be 2.2 or 2.4. + * There could still be the 16-bit/mixer daughter card. + */ } - else if (val < 10) + + if (hw_config->name) { - model_num = "3.7"; - gus_type = 0x37; - mixer_type = ICS2101; - request_region (u_MixSelect, 1, "GUS mixer"); + strncpy(tmp, hw_config->name, 45); + tmp[45] = 0; + sprintf(tmp2, "%s (%dk)", tmp, (int) gus_mem_size / 1024); + tmp2[sizeof(tmp2) - 1] = 0; } - else + else if (gus_pnp_flag) { - model_num = "MAX"; - gus_type = 0x40; - mixer_type = CS4231; -#ifdef CONFIG_GUSMAX - { - unsigned char max_config = 0x40; /* Codec enable */ - - if (gus_dma2 == -1) - gus_dma2 = gus_dma; + sprintf(tmp2, "Gravis UltraSound PnP (%dk)", + (int) gus_mem_size / 1024); + } + else + sprintf(tmp2, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); - if (gus_dma > 3) - max_config |= 0x10; /* 16 bit capture DMA */ - if (gus_dma2 > 3) - max_config |= 0x20; /* 16 bit playback DMA */ + samples = (struct patch_info *)vmalloc((MAX_SAMPLE + 1) * sizeof(*samples)); + if (samples == NULL) + { + printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n"); + return; + } + conf_printf(tmp2, hw_config); + tmp2[sizeof(gus_info.name) - 1] = 0; + strcpy(gus_info.name, tmp2); - max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ + if ((sdev = sound_alloc_synthdev()) == -1) + printk(KERN_WARNING "gus_init: Too many synthesizers\n"); + else + { + voice_alloc = &guswave_operations.alloc; + if (iw_mode) + guswave_operations.id = "IWAVE"; + hw_config->slots[0] = sdev; + synth_devs[sdev] = &guswave_operations; + sequencer_init(); +#ifdef CONFIG_SEQUENCER + gus_tmr_install(gus_base + 8); +#endif + } - outb (max_config, gus_base + 0x106); /* UltraMax control */ - } + reset_sample_memory(); - if (ad1848_detect (gus_base + 0x10c, &ad_flags, hw_config->osp)) - { - char *name = "GUS MAX"; - - gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; - gus_wave_volume = 90; - have_gus_max = 1; - if (hw_config->name) - name = hw_config->name; - - ad1848_init (name, gus_base + 0x10c, - -irq, - gus_dma2, /* Playback DMA */ - gus_dma, /* Capture DMA */ - 1, /* Share DMA channels with GF1 */ - hw_config->osp); - } - else - printk ("[Where's the CS4231?]"); -#else - printk ("\n\n\nGUS MAX support was not compiled in!!!\n\n\n\n"); -#endif + gus_initialize(); + + if ((gus_mem_size > 0) & !gus_no_wave_dma) + { + hw_config->slots[4] = -1; + if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + "Ultrasound", + &gus_audio_driver, + sizeof(struct audio_driver), + NEEDS_RESTART | + ((!iw_mode && dma2 != dma && dma2 != -1) ? + DMA_DUPLEX : 0), + AFMT_U8 | AFMT_S16_LE, + NULL, dma, dma2)) < 0) + { + return; + } + + hw_config->slots[4] = gus_devnum; + audio_devs[gus_devnum]->min_fragment = 9; /* 512k */ + audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */ + audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */ + audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; } - } - else - { - /* - * ASIC not detected so the card must be 2.2 or 2.4. - * There could still be the 16-bit/mixer daughter card. - */ - } - - if (hw_config->name) - { - char tmp[20]; - - strncpy (tmp, hw_config->name, 20); - tmp[19] = 0; - sprintf (gus_info.name, "%s (%dk)", tmp, (int) gus_mem_size / 1024); - gus_info.name[sizeof (gus_info.name) - 1] = 0; - } - else - sprintf (gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); - - - samples = (struct patch_info *) (sound_mem_blocks[sound_nblocks] = vmalloc ((MAX_SAMPLE + 1) * sizeof (*samples))); - if (sound_nblocks < 1024) - sound_nblocks++;; - if (samples == NULL) - { - printk ("GUS Error: Cant allocate memory for instrument tables\n"); - return; - } - - conf_printf (gus_info.name, hw_config); - - if (num_synths >= MAX_SYNTH_DEV) - printk ("GUS Error: Too many synthesizers\n"); - else - { - voice_alloc = &guswave_operations.alloc; - synth_devs[num_synths++] = &guswave_operations; -#ifdef CONFIG_SEQUENCER - gus_tmr_install (gus_base + 8); -#endif - } - - reset_sample_memory (); - - gus_initialize (); - - if (num_audiodevs < MAX_AUDIO_DEV) - { - audio_devs[gus_devnum = num_audiodevs++] = &gus_audio_operations; - audio_devs[gus_devnum]->dmachan1 = dma; - audio_devs[gus_devnum]->dmachan2 = dma2; - audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE; - audio_devs[gus_devnum]->min_fragment = 9; - audio_devs[gus_devnum]->mixer_dev = num_mixers; /* Next mixer# */ - audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; - if (dma2 != dma && dma2 != -1) - audio_devs[gus_devnum]->flags |= DMA_DUPLEX; - } - else - printk ("GUS: Too many PCM devices available\n"); - - /* - * Mixer dependent initialization. - */ - - switch (mixer_type) - { - case ICS2101: - gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; - gus_wave_volume = 90; - request_region (u_MixSelect, 1, "GUS mixer"); - ics2101_mixer_init (); - return; - - case CS4231: - /* Initialized elsewhere (ad1848.c) */ - default: - gus_default_mixer_init (); - return; - } -} - -void -gus_wave_unload (void) -{ -#ifdef CONFIG_GUSMAX - if (have_gus_max) - { - ad1848_unload (gus_base + 0x10c, - -gus_irq, - gus_dma2, /* Playback DMA */ - gus_dma, /* Capture DMA */ - 1); /* Share DMA channels with GF1 */ - } -#endif + + /* + * Mixer dependent initialization. + */ - if (mixer_type == ICS2101) - { - release_region (u_MixSelect, 1); - } + switch (mixer_type) + { + case ICS2101: + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + request_region(u_MixSelect, 1, "GUS mixer"); + hw_config->slots[5] = ics2101_mixer_init(); + audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ + return; + + case CS4231: + /* Initialized elsewhere (ad1848.c) */ + default: + hw_config->slots[5] = gus_default_mixer_init(); + audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ + return; + } } -static void -do_loop_irq (int voice) +void gus_wave_unload(struct address_info *hw_config) { - unsigned char tmp; - int mode, parm; - unsigned long flags; - - save_flags (flags); - cli (); - gus_select_voice (voice); +#ifdef CONFIG_GUSMAX + if (have_gus_max) + { + ad1848_unload(gus_base + 0x10c, + -gus_irq, + gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1); /* Share DMA channels with GF1 */ + } +#endif - tmp = gus_read8 (0x00); - tmp &= ~0x20; /* + if (mixer_type == ICS2101) + { + release_region(u_MixSelect, 1); + } + if (hw_config->slots[0] != -1) + sound_unload_synthdev(hw_config->slots[0]); + if (hw_config->slots[1] != -1) + sound_unload_audiodev(hw_config->slots[1]); + if (hw_config->slots[2] != -1) + sound_unload_mididev(hw_config->slots[2]); + if (hw_config->slots[4] != -1) + sound_unload_audiodev(hw_config->slots[4]); + if (hw_config->slots[5] != -1) + sound_unload_mixerdev(hw_config->slots[5]); + + if(samples) + vfree(samples); + samples=NULL; +} + +static void do_loop_irq(int voice) +{ + unsigned char tmp; + int mode, parm; + unsigned long flags; + + save_flags(flags); + cli(); + gus_select_voice(voice); + + tmp = gus_read8(0x00); + tmp &= ~0x20; /* * Disable wave IRQ for this_one voice */ - gus_write8 (0x00, tmp); + gus_write8(0x00, tmp); - if (tmp & 0x03) /* Voice stopped */ - voice_alloc->map[voice] = 0; + if (tmp & 0x03) /* Voice stopped */ + voice_alloc->map[voice] = 0; - mode = voices[voice].loop_irq_mode; - voices[voice].loop_irq_mode = 0; - parm = voices[voice].loop_irq_parm; + mode = voices[voice].loop_irq_mode; + voices[voice].loop_irq_mode = 0; + parm = voices[voice].loop_irq_parm; - switch (mode) - { - - case LMODE_FINISH: /* - * Final loop finished, shoot volume down - */ + switch (mode) + { + case LMODE_FINISH: /* + * Final loop finished, shoot volume down + */ - if ((int) (gus_read16 (0x09) >> 4) < 100) /* - * Get current volume + if ((int) (gus_read16(0x09) >> 4) < 100) /* + * Get current volume + */ + { + gus_voice_off(); + gus_rampoff(); + gus_voice_init(voice); + break; + } + gus_ramp_range(65, 4065); + gus_ramp_rate(0, 63); /* + * Fastest possible rate */ - { - gus_voice_off (); - gus_rampoff (); - gus_voice_init (voice); - break; + gus_rampon(0x20 | 0x40); /* + * Ramp down, once, irq + */ + voices[voice].volume_irq_mode = VMODE_HALT; + break; + + case LMODE_PCM_STOP: + pcm_active = 0; /* Signal to the play_next_pcm_block routine */ + case LMODE_PCM: + { + pcm_qlen--; + pcm_head = (pcm_head + 1) % pcm_nblk; + if (pcm_qlen && pcm_active) + { + play_next_pcm_block(); + } + else + { + /* Underrun. Just stop the voice */ + gus_select_voice(0); /* Left channel */ + gus_voice_off(); + gus_rampoff(); + gus_select_voice(1); /* Right channel */ + gus_voice_off(); + gus_rampoff(); + pcm_active = 0; + } + + /* + * If the queue was full before this interrupt, the DMA transfer was + * suspended. Let it continue now. + */ + + if (audio_devs[gus_devnum]->dmap_out->qlen > 0) + DMAbuf_outputintr(gus_devnum, 0); + } + break; + + default: } - gus_ramp_range (65, 4065); - gus_ramp_rate (0, 63); /* - * Fastest possible rate - */ - gus_rampon (0x20 | 0x40); /* - * Ramp down, once, irq - */ - voices[voice].volume_irq_mode = VMODE_HALT; - break; - - case LMODE_PCM_STOP: - pcm_active = 0; /* Signal to the play_next_pcm_block routine */ - case LMODE_PCM: - { - int flag; /* 0 or 2 */ - - pcm_qlen--; - pcm_head = (pcm_head + 1) % pcm_nblk; - if (pcm_qlen && pcm_active) - { - play_next_pcm_block (); - } - else - { /* Underrun. Just stop the voice */ - gus_select_voice (0); /* Left channel */ - gus_voice_off (); - gus_rampoff (); - gus_select_voice (1); /* Right channel */ - gus_voice_off (); - gus_rampoff (); - pcm_active = 0; - } - - /* - * If the queue was full before this interrupt, the DMA transfer was - * suspended. Let it continue now. - */ - if (dma_active) - { - if (pcm_qlen == 0) - flag = 1; /* Underflow */ - else - flag = 0; - dma_active = 0; - } - else - flag = 2; /* Just notify the dmabuf.c */ - DMAbuf_outputintr (gus_devnum, flag); - } - break; - - default:; - } - restore_flags (flags); + restore_flags(flags); } -static void -do_volume_irq (int voice) +static void do_volume_irq(int voice) { - unsigned char tmp; - int mode, parm; - unsigned long flags; + unsigned char tmp; + int mode, parm; + unsigned long flags; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - gus_select_voice (voice); - - tmp = gus_read8 (0x0d); - tmp &= ~0x20; /* + gus_select_voice(voice); + tmp = gus_read8(0x0d); + tmp &= ~0x20; /* * Disable volume ramp IRQ */ - gus_write8 (0x0d, tmp); - - mode = voices[voice].volume_irq_mode; - voices[voice].volume_irq_mode = 0; - parm = voices[voice].volume_irq_parm; - - switch (mode) - { - case VMODE_HALT: /* - * Decay phase finished - */ - restore_flags (flags); - gus_voice_init (voice); - break; + gus_write8(0x0d, tmp); - case VMODE_ENVELOPE: - gus_rampoff (); - restore_flags (flags); - step_envelope (voice); - break; + mode = voices[voice].volume_irq_mode; + voices[voice].volume_irq_mode = 0; + parm = voices[voice].volume_irq_parm; - case VMODE_START_NOTE: - restore_flags (flags); - guswave_start_note2 (voices[voice].dev_pending, voice, - voices[voice].note_pending, voices[voice].volume_pending); - if (voices[voice].kill_pending) - guswave_kill_note (voices[voice].dev_pending, voice, - voices[voice].note_pending, 0); - - if (voices[voice].sample_pending >= 0) + switch (mode) { - guswave_set_instr (voices[voice].dev_pending, voice, - voices[voice].sample_pending); - voices[voice].sample_pending = -1; + case VMODE_HALT: /* Decay phase finished */ + if (iw_mode) + gus_write8(0x15, 0x02); /* Set voice deactivate bit of SMSI */ + restore_flags(flags); + gus_voice_init(voice); + break; + + case VMODE_ENVELOPE: + gus_rampoff(); + restore_flags(flags); + step_envelope(voice); + break; + + case VMODE_START_NOTE: + restore_flags(flags); + guswave_start_note2(voices[voice].dev_pending, voice, + voices[voice].note_pending, voices[voice].volume_pending); + if (voices[voice].kill_pending) + guswave_kill_note(voices[voice].dev_pending, voice, + voices[voice].note_pending, 0); + + if (voices[voice].sample_pending >= 0) + { + guswave_set_instr(voices[voice].dev_pending, voice, + voices[voice].sample_pending); + voices[voice].sample_pending = -1; + } + break; + + default: + restore_flags(flags); } - break; - - default:; - } + restore_flags(flags); } -void -gus_voice_irq (void) +void gus_voice_irq(void) { - unsigned long wave_ignore = 0, volume_ignore = 0; - unsigned long voice_bit; + unsigned long wave_ignore = 0, volume_ignore = 0; + unsigned long voice_bit; - unsigned char src, voice; + unsigned char src, voice; - while (1) - { - src = gus_read8 (0x0f); /* - * Get source info - */ - voice = src & 0x1f; - src &= 0xc0; + while (1) + { + src = gus_read8(0x0f); /* + * Get source info + */ + voice = src & 0x1f; + src &= 0xc0; - if (src == (0x80 | 0x40)) - return; /* + if (src == (0x80 | 0x40)) + return; /* * No interrupt */ - voice_bit = 1 << voice; - - if (!(src & 0x80)) /* - * Wave IRQ pending - */ - if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /* - * Not done - * yet - */ - { - wave_ignore |= voice_bit; - do_loop_irq (voice); - } - - if (!(src & 0x40)) /* - * Volume IRQ pending - */ - if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /* - * Not done - * yet - */ - { - volume_ignore |= voice_bit; - do_volume_irq (voice); - } - } -} - -void -guswave_dma_irq (void) -{ - unsigned char status; - - status = gus_look8 (0x41); /* Get DMA IRQ Status */ - if (status & 0x40) /* DMA interrupt pending */ - switch (active_device) - { - case GUS_DEV_WAVE: - if ((dram_sleep_flag.flags & WK_SLEEP)) - { - dram_sleep_flag.flags = WK_WAKEUP; - module_wake_up (&dram_sleeper); - }; - break; - - case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */ - gus_transfer_output_block (pcm_current_dev, pcm_current_buf, - pcm_current_count, - pcm_current_intrflag, 1); - break; - - case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */ - if (pcm_qlen < pcm_nblk) - { - int flag = (1 - dma_active) * 2; /* 0 or 2 */ - - if (pcm_qlen == 0) - flag = 1; /* Underrun */ - dma_active = 0; - if (gus_busy) - DMAbuf_outputintr (gus_devnum, flag); - } - break; + voice_bit = 1 << voice; - default:; - } + if (!(src & 0x80)) /* + * Wave IRQ pending + */ + if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /* + * Not done + * yet + */ + { + wave_ignore |= voice_bit; + do_loop_irq(voice); + } + if (!(src & 0x40)) /* + * Volume IRQ pending + */ + if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /* + * Not done + * yet + */ + { + volume_ignore |= voice_bit; + do_volume_irq(voice); + } + } +} - status = gus_look8 (0x49); /* - * Get Sampling IRQ Status - */ - if (status & 0x40) /* +void guswave_dma_irq(void) +{ + unsigned char status; + + status = gus_look8(0x41); /* Get DMA IRQ Status */ + if (status & 0x40) /* DMA interrupt pending */ + switch (active_device) + { + case GUS_DEV_WAVE: + wake_up(&dram_sleeper); + break; + + case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + gus_transfer_output_block(pcm_current_dev, pcm_current_buf, + pcm_current_count, + pcm_current_intrflag, 1); + break; + + case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */ + gus_write8(0x41, 0); /* Disable GF1 DMA */ + if (pcm_qlen < pcm_nblk) + { + dma_active = 0; + if (gus_busy) + { + if (audio_devs[gus_devnum]->dmap_out->qlen > 0) + DMAbuf_outputintr(gus_devnum, 0); + } + } + break; + + default: + } + status = gus_look8(0x49); /* + * Get Sampling IRQ Status + */ + if (status & 0x40) /* * Sampling Irq pending */ - { - DMAbuf_inputintr (gus_devnum); - } - + { + DMAbuf_inputintr(gus_devnum); + } } #ifdef CONFIG_SEQUENCER + /* * Timer stuff */ @@ -3697,105 +3447,97 @@ guswave_dma_irq (void) static volatile int select_addr, data_addr; static volatile int curr_timer = 0; -void -gus_timer_command (unsigned int addr, unsigned int val) +void gus_timer_command(unsigned int addr, unsigned int val) { - int i; + int i; - outb ((unsigned char) (addr & 0xff), select_addr); + outb(((unsigned char) (addr & 0xff)), select_addr); - for (i = 0; i < 2; i++) - inb (select_addr); + for (i = 0; i < 2; i++) + inb(select_addr); - outb ((unsigned char) (val & 0xff), data_addr); + outb(((unsigned char) (val & 0xff)), data_addr); - for (i = 0; i < 2; i++) - inb (select_addr); + for (i = 0; i < 2; i++) + inb(select_addr); } -static void -arm_timer (int timer, unsigned int interval) +static void arm_timer(int timer, unsigned int interval) { + curr_timer = timer; - curr_timer = timer; - - if (timer == 1) - { - gus_write8 (0x46, 256 - interval); /* Set counter for timer 1 */ - gus_write8 (0x45, 0x04); /* Enable timer 1 IRQ */ - gus_timer_command (0x04, 0x01); /* Start timer 1 */ - } - else - { - gus_write8 (0x47, 256 - interval); /* Set counter for timer 2 */ - gus_write8 (0x45, 0x08); /* Enable timer 2 IRQ */ - gus_timer_command (0x04, 0x02); /* Start timer 2 */ - } + if (timer == 1) + { + gus_write8(0x46, 256 - interval); /* Set counter for timer 1 */ + gus_write8(0x45, 0x04); /* Enable timer 1 IRQ */ + gus_timer_command(0x04, 0x01); /* Start timer 1 */ + } + else + { + gus_write8(0x47, 256 - interval); /* Set counter for timer 2 */ + gus_write8(0x45, 0x08); /* Enable timer 2 IRQ */ + gus_timer_command(0x04, 0x02); /* Start timer 2 */ + } - gus_timer_enabled = 1; + gus_timer_enabled = 1; } -static unsigned int -gus_tmr_start (int dev, unsigned int usecs_per_tick) +static unsigned int gus_tmr_start(int dev, unsigned int usecs_per_tick) { - int timer_no, resolution; - int divisor; + int timer_no, resolution; + int divisor; - if (usecs_per_tick > (256 * 80)) - { - timer_no = 2; - resolution = 320; /* usec */ - } - else - { - timer_no = 1; - resolution = 80; /* usec */ - } - - divisor = (usecs_per_tick + (resolution / 2)) / resolution; - - arm_timer (timer_no, divisor); + if (usecs_per_tick > (256 * 80)) + { + timer_no = 2; + resolution = 320; /* usec */ + } + else + { + timer_no = 1; + resolution = 80; /* usec */ + } + divisor = (usecs_per_tick + (resolution / 2)) / resolution; + arm_timer(timer_no, divisor); - return divisor * resolution; + return divisor * resolution; } -static void -gus_tmr_disable (int dev) +static void gus_tmr_disable(int dev) { - gus_write8 (0x45, 0); /* Disable both timers */ - gus_timer_enabled = 0; + gus_write8(0x45, 0); /* Disable both timers */ + gus_timer_enabled = 0; } -static void -gus_tmr_restart (int dev) +static void gus_tmr_restart(int dev) { - if (curr_timer == 1) - gus_write8 (0x45, 0x04); /* Start timer 1 again */ - else - gus_write8 (0x45, 0x08); /* Start timer 2 again */ - gus_timer_enabled = 1; + if (curr_timer == 1) + gus_write8(0x45, 0x04); /* Start timer 1 again */ + else + gus_write8(0x45, 0x08); /* Start timer 2 again */ + gus_timer_enabled = 1; } static struct sound_lowlev_timer gus_tmr = { - 0, - gus_tmr_start, - gus_tmr_disable, - gus_tmr_restart + 0, + 1, + gus_tmr_start, + gus_tmr_disable, + gus_tmr_restart }; -static void -gus_tmr_install (int io_base) +static void gus_tmr_install(int io_base) { - struct sound_lowlev_timer *tmr; + struct sound_lowlev_timer *tmr; - select_addr = io_base; - data_addr = io_base + 1; + select_addr = io_base; + data_addr = io_base + 1; - tmr = &gus_tmr; + tmr = &gus_tmr; #ifdef THIS_GETS_FIXED - sound_timer_init (&gus_tmr, "GUS"); + sound_timer_init(&gus_tmr, "GUS"); #endif } #endif diff --git a/drivers/sound/hex2hex.c b/drivers/sound/hex2hex.c new file mode 100644 index 000000000000..8f6cd11b17b7 --- /dev/null +++ b/drivers/sound/hex2hex.c @@ -0,0 +1,100 @@ +/* + * 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 +#include + +#define ABANDON(why) { fprintf(stderr, "%s\n", why); exit(1); } +#define MAX_SIZE (256*1024) +unsigned char buf[MAX_SIZE]; + +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= 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= 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 hex2hex(char *source, char *target, char *varline) -{ - FILE *inf, *outf; - - int i,l; - unsigned char buf[MAX_SIZE]; - - if ((inf=fopen(source, "r"))==NULL) - { - perror(source); - return 0; - } - - if ((outf=fopen(target, "w"))==NULL) - { - perror(target); - fclose(inf); - return 0; - } - - l=loadhex(inf, buf, source); - if (l<=0) - { - fclose(inf); - fclose(outf); - return l; - } - - - fprintf(outf, "/*\n *\t Computer generated file. Do not edit.\n */\n"); - fprintf(outf, "static int %s_len = %d;\n", varline, l); - fprintf(outf, "static unsigned char %s[] = {\n", varline); - - for (i=0;i #include "sound_config.h" -#if defined(CONFIG_GUS) +#ifdef CONFIG_GUS #include #include "gus_hw.h" @@ -31,216 +34,215 @@ static int left_fix[ICS_MIXDEVS] = static int right_fix[ICS_MIXDEVS] = {2, 2, 2, 1, 2, 1}; -static int -scale_vol (int vol) +static int scale_vol(int vol) { - /* - * Experimental volume scaling by Risto Kankkunen. - * This should give smoother volume response than just - * a plain multiplication. - */ - int e; - - if (vol < 0) - vol = 0; - if (vol > 100) - vol = 100; - vol = (31 * vol + 50) / 100; - e = 0; - if (vol) - { - while (vol < 16) + /* + * Experimental volume scaling by Risto Kankkunen. + * This should give smoother volume response than just + * a plain multiplication. + */ + + int e; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + vol = (31 * vol + 50) / 100; + e = 0; + if (vol) { - vol <<= 1; - e--; + while (vol < 16) + { + vol <<= 1; + e--; + } + vol -= 16; + e += 7; } - vol -= 16; - e += 7; - } - return ((e << 4) + vol); + return ((e << 4) + vol); } -static void -write_mix (int dev, int chn, int vol) +static void write_mix(int dev, int chn, int vol) { - int *selector; - unsigned long flags; - int ctrl_addr = dev << 3; - int attn_addr = dev << 3; - - vol = scale_vol (vol); - - if (chn == CHN_LEFT) - { - selector = left_fix; - ctrl_addr |= 0x00; - attn_addr |= 0x02; - } - else - { - selector = right_fix; - ctrl_addr |= 0x01; - attn_addr |= 0x03; - } - - save_flags (flags); - cli (); - outb (ctrl_addr, u_MixSelect); - outb (selector[dev], u_MixData); - outb (attn_addr, u_MixSelect); - outb ((unsigned char) vol, u_MixData); - restore_flags (flags); + int *selector; + unsigned long flags; + int ctrl_addr = dev << 3; + int attn_addr = dev << 3; + + vol = scale_vol(vol); + + if (chn == CHN_LEFT) + { + selector = left_fix; + ctrl_addr |= 0x00; + attn_addr |= 0x02; + } + else + { + selector = right_fix; + ctrl_addr |= 0x01; + attn_addr |= 0x03; + } + + save_flags(flags); + cli(); + outb((ctrl_addr), u_MixSelect); + outb((selector[dev]), u_MixData); + outb((attn_addr), u_MixSelect); + outb(((unsigned char) vol), u_MixData); + restore_flags(flags); } -static int -set_volumes (int dev, int vol) +static int set_volumes(int dev, int vol) { - int left = vol & 0x00ff; - int right = (vol >> 8) & 0x00ff; - - if (left < 0) - left = 0; - if (left > 100) - left = 100; - if (right < 0) - right = 0; - if (right > 100) - right = 100; - - write_mix (dev, CHN_LEFT, left); - write_mix (dev, CHN_RIGHT, right); - - vol = left + (right << 8); - volumes[dev] = vol; - return vol; + int left = vol & 0x00ff; + int right = (vol >> 8) & 0x00ff; + + if (left < 0) + left = 0; + if (left > 100) + left = 100; + if (right < 0) + right = 0; + if (right > 100) + right = 100; + + write_mix(dev, CHN_LEFT, left); + write_mix(dev, CHN_RIGHT, right); + + vol = left + (right << 8); + volumes[dev] = vol; + return vol; } -static int -ics2101_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +static int ics2101_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) { - if (((cmd >> 8) & 0xff) == 'M') - { - if (_IOC_DIR (cmd) & _IOC_WRITE) - switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - return gus_default_mixer_ioctl (dev, cmd, arg); - break; - - case SOUND_MIXER_MIC: - return snd_ioctl_return ((int *) arg, set_volumes (DEV_MIC, get_user ((int *) arg))); - break; - - case SOUND_MIXER_CD: - return snd_ioctl_return ((int *) arg, set_volumes (DEV_CD, get_user ((int *) arg))); - break; - - case SOUND_MIXER_LINE: - return snd_ioctl_return ((int *) arg, set_volumes (DEV_LINE, get_user ((int *) arg))); - break; - - case SOUND_MIXER_SYNTH: - return snd_ioctl_return ((int *) arg, set_volumes (DEV_GF1, get_user ((int *) arg))); - break; - - case SOUND_MIXER_VOLUME: - return snd_ioctl_return ((int *) arg, set_volumes (DEV_VOL, get_user ((int *) arg))); - break; - - default: - return -(EINVAL); - } - else - switch (cmd & 0xff) /* + int val; + + if (((cmd >> 8) & 0xff) == 'M') { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + + if (get_user(val, (int *)arg)) + return -EFAULT; + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl(dev, cmd, arg); + + case SOUND_MIXER_MIC: + val = set_volumes(DEV_MIC, val); + break; + + case SOUND_MIXER_CD: + val = set_volumes(DEV_CD, val); + break; + + case SOUND_MIXER_LINE: + val = set_volumes(DEV_LINE, val); + break; + + case SOUND_MIXER_SYNTH: + val = set_volumes(DEV_GF1, val); + break; + + case SOUND_MIXER_VOLUME: + val = set_volumes(DEV_VOL, val); + break; + + default: + return -EINVAL; + } + return put_user(val, (int *)arg); + } else { + switch (cmd & 0xff) { + /* * Return parameters */ - { - - case SOUND_MIXER_RECSRC: - return gus_default_mixer_ioctl (dev, cmd, arg); - break; - - case SOUND_MIXER_DEVMASK: - return snd_ioctl_return ((int *) arg, MIX_DEVS); - break; - - case SOUND_MIXER_STEREODEVS: - return snd_ioctl_return ((int *) arg, SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC); - break; - - case SOUND_MIXER_RECMASK: - return snd_ioctl_return ((int *) arg, SOUND_MASK_MIC | SOUND_MASK_LINE); - break; - - case SOUND_MIXER_CAPS: - return snd_ioctl_return ((int *) arg, 0); - break; - - case SOUND_MIXER_MIC: - return snd_ioctl_return ((int *) arg, volumes[DEV_MIC]); - break; - - case SOUND_MIXER_LINE: - return snd_ioctl_return ((int *) arg, volumes[DEV_LINE]); - break; - - case SOUND_MIXER_CD: - return snd_ioctl_return ((int *) arg, volumes[DEV_CD]); - break; - - case SOUND_MIXER_VOLUME: - return snd_ioctl_return ((int *) arg, volumes[DEV_VOL]); - break; - - case SOUND_MIXER_SYNTH: - return snd_ioctl_return ((int *) arg, volumes[DEV_GF1]); - break; - - default: - return -(EINVAL); - } - } - - return -(EINVAL); + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl(dev, cmd, arg); + + case SOUND_MIXER_DEVMASK: + val = MIX_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; + break; + + case SOUND_MIXER_RECMASK: + val = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + + case SOUND_MIXER_CAPS: + val = 0; + break; + + case SOUND_MIXER_MIC: + val = volumes[DEV_MIC]; + break; + + case SOUND_MIXER_LINE: + val = volumes[DEV_LINE]; + break; + + case SOUND_MIXER_CD: + val = volumes[DEV_CD]; + break; + + case SOUND_MIXER_VOLUME: + val = volumes[DEV_VOL]; + break; + + case SOUND_MIXER_SYNTH: + val = volumes[DEV_GF1]; + break; + + default: + return -EINVAL; + } + return put_user(val, (int *)arg); + } + } + return -EINVAL; } static struct mixer_operations ics2101_mixer_operations = { - "ICS2101", - "ICS2101 Multimedia Mixer", - ics2101_mixer_ioctl + "ICS2101", + "ICS2101 Multimedia Mixer", + ics2101_mixer_ioctl }; -void -ics2101_mixer_init (void) +int +ics2101_mixer_init(void) { - int i; - - if (num_mixers < MAX_MIXER_DEV) - { - mixer_devs[num_mixers++] = &ics2101_mixer_operations; + int i; + int n; - /* - * Some GUS v3.7 cards had some channels flipped. Disable - * the flipping feature if the model id is other than 5. - */ - - if (inb (u_MixSelect) != 5) + if ((n = sound_alloc_mixerdev()) != -1) { - for (i = 0; i < ICS_MIXDEVS; i++) - left_fix[i] = 1; - for (i = 0; i < ICS_MIXDEVS; i++) - right_fix[i] = 2; + mixer_devs[n] = &ics2101_mixer_operations; + + /* + * Some GUS v3.7 cards had some channels flipped. Disable + * the flipping feature if the model id is other than 5. + */ + + if (inb(u_MixSelect) != 5) + { + for (i = 0; i < ICS_MIXDEVS; i++) + left_fix[i] = 1; + for (i = 0; i < ICS_MIXDEVS; i++) + right_fix[i] = 2; + } + set_volumes(DEV_GF1, 0x5a5a); + set_volumes(DEV_CD, 0x5a5a); + set_volumes(DEV_MIC, 0x0000); + set_volumes(DEV_LINE, 0x5a5a); + set_volumes(DEV_VOL, 0x5a5a); + set_volumes(DEV_UNUSED, 0x0000); } - - set_volumes (DEV_GF1, 0x5a5a); - set_volumes (DEV_CD, 0x5a5a); - set_volumes (DEV_MIC, 0x0000); - set_volumes (DEV_LINE, 0x5a5a); - set_volumes (DEV_VOL, 0x5a5a); - set_volumes (DEV_UNUSED, 0x0000); - } - + return n; } #endif diff --git a/drivers/sound/iwmem.h b/drivers/sound/iwmem.h index c3d29dceb2bf..b96d5ed1b22e 100644 --- a/drivers/sound/iwmem.h +++ b/drivers/sound/iwmem.h @@ -4,9 +4,9 @@ * DRAM size encoding table for AMD Interwave chip. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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. */ diff --git a/drivers/sound/legacy.h b/drivers/sound/legacy.h new file mode 100644 index 000000000000..c9b39b1c9a89 --- /dev/null +++ b/drivers/sound/legacy.h @@ -0,0 +1,48 @@ +#ifndef _SOUND_LEGACY_H_ +#define _SOUND_LEGACY_H_ + +/* + * Force on additional support + */ + +#define __SGNXPRO__ +#define DESKPROXL +/* #define SM_GAMES */ +#define SM_WAVE + +/* + * Define legacy options. + */ + +#define SELECTED_SOUND_OPTIONS 0x0 + +#define HAVE_MAUI_BOOT +#define PSS_HAVE_LD +#define INCLUDE_TRIX_BOOT + +#define CONFIG_CS4232 +#define CONFIG_GUS +#define CONFIG_MAD16 +#define CONFIG_MAUI +#define CONFIG_MPU401 +#define CONFIG_MSS +#define CONFIG_OPL3SA1 +#define CONFIG_PAS +#define CONFIG_PSS +#define CONFIG_SB +#define CONFIG_SOFTOSS +#define CONFIG_SSCAPE +#define CONFIG_TRIX +#define CONFIG_VMIDI +#define CONFIG_YM3812 + +#define CONFIG_AUDIO +#define CONFIG_MIDI +#define CONFIG_SEQUENCER + +#define CONFIG_AD1848 +#define CONFIG_MPU_EMU +#define CONFIG_SBDSP +#define CONFIG_UART401 + +#endif /* _SOUND_LEGACY_H */ diff --git a/drivers/sound/lowlevel/Config.in b/drivers/sound/lowlevel/Config.in new file mode 100644 index 000000000000..36928af081c8 --- /dev/null +++ b/drivers/sound/lowlevel/Config.in @@ -0,0 +1,56 @@ +dep_tristate 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER $CONFIG_SOUND_OSS + +dep_tristate 'AWE32 synth' CONFIG_AWE32_SYNTH $CONFIG_SOUND_OSS + +if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND" = "m" ]; then + dep_tristate 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16 $CONFIG_SOUND_OSS + if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then + hex ' I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 + fi + + if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then + comment 'SC-6600 Audio Cards have no jumper switches at all' + bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600 + if [ "$CONFIG_SC6600" = "y" ]; then + comment 'SC-6600 specific configuration' + bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY + int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' CONFIG_SC6600_CDROM 4 + hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0 + fi + + if [ "$CONFIG_SOUND_SB" = "y" -o "$CONFIG_SOUND_SB" = "m" ]; then + if [ "$CONFIG_AEDSP16_MSS" != "y" ]; then + bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO + if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then + comment 'Audio Excel DSP 16 [Sound Blaster Pro]' + hex 'I/O base for Audio Excel DSP 16 220, 240' CONFIG_AEDSP16_BASE $CONFIG_SB_BASE + int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' CONFIG_AEDSP16_SB_IRQ $CONFIG_SB_IRQ + int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_SB_DMA $CONFIG_SB_DMA + fi + fi + fi + + if [ "$CONFIG_SOUND_MSS" = "y" -o "$CONFIG_SOUND_MSS" = "m" ]; then + if [ "$CONFIG_AEDSP16_SBPRO" != "y" ]; then + bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS + if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then + comment 'Audio Excel DSP 16 [Microsoft Sound System]' + hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 + int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' CONFIG_AEDSP16_MSS_IRQ $CONFIG_MSS_IRQ + int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_MSS_DMA $CONFIG_MSS_DMA + fi + fi + fi + + if [ "$CONFIG_SOUND_MPU401" = "y" -o "$CONFIG_SOUND_MPU401" = "m" ]; then + bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401 + if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then + comment 'Audio Excel DSP 16 [MPU-401]' + if [ "$CONFIG_AEDSP16_SBPRO" != "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then + hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220 + fi + int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' CONFIG_AEDSP16_MPU_IRQ $CONFIG_MPU_IRQ + fi + fi + fi +fi diff --git a/drivers/sound/lowlevel/Config.tmpl b/drivers/sound/lowlevel/Config.tmpl deleted file mode 100644 index f5428e6408ce..000000000000 --- a/drivers/sound/lowlevel/Config.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND - -if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then - bool 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER -fi diff --git a/drivers/sound/lowlevel/Makefile b/drivers/sound/lowlevel/Makefile index 13a2bf1fd79d..921f10362cb7 100644 --- a/drivers/sound/lowlevel/Makefile +++ b/drivers/sound/lowlevel/Makefile @@ -1,19 +1,24 @@ -all: lowlevel.o +# Makefile for the Linux low-level sound card drivers. +# +# 11 Feb 1998, Michael Elizabeth Chastain, +# Rewritten to use lists instead of if statements. -OBJS = init.o +export-objs := soundlow.o -ifdef CONFIG_LOWLEVEL_SOUND -ifdef CONFIG_ACI_MIXER -OBJS := $(OBJS) aci.o -endif -endif +list-y := +list-m := +list-n := +list- := -lowlevel.o: $(OBJS) - $(LD) -r -o lowlevel.o $(OBJS) +obj-$(CONFIG_SOUND_OSS) += soundlow.o +obj-$(CONFIG_ACI_MIXER) += aci.o +obj-$(CONFIG_AEDSP16) += aedsp16.o +obj-$(CONFIG_AWE32_SYNTH) += awe_wave.o -clean: - rm -f core x y z *~ *.o +O_TARGET := lowlevel.o +O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +OX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) -ifdef HOSTCC include $(TOPDIR)/Rules.make -endif diff --git a/drivers/sound/lowlevel/README b/drivers/sound/lowlevel/README deleted file mode 100644 index de1eb5720514..000000000000 --- a/drivers/sound/lowlevel/README +++ /dev/null @@ -1,13 +0,0 @@ -Additional low level sound drivers for Linux --------------------------------------------- - -This directory contains additional low level sound drivers which -are not part of USS/Lite (UNIX Sound System). These drivers are -maintained by their authors. - -If you like to write a low level sound driver, please contact -Hannu Savolainen (hannu@voxware.pp.fi) for more info. - -The following low level drivers are included: - -- ACI MIXER for miroPCM12 by Markus Kuhn. See aci.readme for more info. diff --git a/drivers/sound/lowlevel/aci.c b/drivers/sound/lowlevel/aci.c index abcffcd80e3e..d46088b8eee6 100644 --- a/drivers/sound/lowlevel/aci.c +++ b/drivers/sound/lowlevel/aci.c @@ -13,7 +13,7 @@ * software. * * This Voxware ACI driver currently only supports the ACI functions - * on the miroSOUND PCM12 card. Support for miro soundcards with + * on the miroSOUND PCM12 card. Support for miro sound cards with * additional ACI functions can easily be added later. * * Revision history: @@ -35,7 +35,7 @@ * This mixer driver identifies itself to applications as "ACI" in * mixer_info.id as retrieved by ioctl(fd, SOUND_MIXER_INFO, &mixer_info). * - * Proprietary mixer features that go beyond the standard USS mixer + * Proprietary mixer features that go beyond the standard OSS mixer * interface are: * * Full duplex solo configuration: @@ -58,8 +58,10 @@ * */ -#include /* CONFIG_ACI_MIXER */ +#include /* for CONFIG_ACI_MIXER */ +#include "lowlevel.h" #include "../sound_config.h" +#include "lowlevel.h" #ifdef CONFIG_ACI_MIXER #undef DEBUG /* if defined, produce a verbose report via syslog */ @@ -72,6 +74,14 @@ int aci_solo; /* status bit of the card that can't be * static int aci_present = 0; +#ifdef MODULE /* Whether the aci mixer is to be reset. */ +int aci_reset = 0; /* Default: don't reset if the driver is a */ +MODULE_PARM(aci_reset,"i"); +#else /* module; use "insmod sound.o aci_reset=1" */ +int aci_reset = 1; /* to override. */ +#endif + + #define COMMAND_REGISTER (aci_port) #define STATUS_REGISTER (aci_port + 1) #define BUSY_REGISTER (aci_port + 2) @@ -79,7 +89,7 @@ static int aci_present = 0; /* * Wait until the ACI microcontroller has set the READYFLAG in the * Busy/IRQ Source Register to 0. This is required to avoid - * overrunning the soundcard microcontroller. We do a busy wait here, + * overrunning the sound card microcontroller. We do a busy wait here, * because the microcontroller is not supposed to signal a busy * condition for more than a few clock cycles. In case of a time-out, * this function returns -1. @@ -281,7 +291,7 @@ static int getvolume(caddr_t arg, if (indexed_cmd(0xf0, right_index, &buf)) return -EIO; vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8; - return snd_ioctl_return((int *) arg, vol); + return (*(int *) arg = vol); } @@ -289,23 +299,21 @@ static int setvolume(caddr_t arg, unsigned char left_index, unsigned char right_index) { int vol, ret; - unsigned param; - param = get_user((int *) arg); /* left channel */ - vol = param & 0xff; + vol = *(int *)arg & 0xff; if (vol > 100) vol = 100; vol = SCALE(100, 0x20, vol); if (write_cmd(left_index, 0x20 - vol)) return -EIO; ret = SCALE(0x20, 100, vol); /* right channel */ - vol = (param >> 8) & 0xff; + vol = (*(int *)arg >> 8) & 0xff; if (vol > 100) vol = 100; vol = SCALE(100, 0x20, vol); if (write_cmd(right_index, 0x20 - vol)) return -EIO; ret |= SCALE(0x20, 100, vol) << 8; - return snd_ioctl_return((int *) arg, ret); + return (*(int *) arg = ret); } @@ -317,14 +325,14 @@ aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) /* handle solo mode control */ if (cmd == SOUND_MIXER_PRIVATE1) { - if (get_user((int *) arg) >= 0) { - aci_solo = !!get_user((int *) arg); + if (*(int *) arg >= 0) { + aci_solo = !!*(int *) arg; if (write_cmd(0xd2, aci_solo)) return -EIO; } else if (aci_version >= 0xb0) { if ((status = read_general_status()) < 0) return -EIO; - return snd_ioctl_return ((int *) arg, (status & 0x20) == 0); + return (*(int *) arg = (status & 0x20) == 0); } - return snd_ioctl_return((int *) arg, aci_solo); + return (*(int *) arg = aci_solo); } if (((cmd >> 8) & 0xff) == 'M') { @@ -348,14 +356,14 @@ aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) case SOUND_MIXER_LINE2: /* AUX2 */ return setvolume(arg, 0x3e, 0x36); case SOUND_MIXER_IGAIN: /* MIC pre-amp */ - vol = get_user((int *) arg) & 0xff; + vol = *(int *) arg & 0xff; if (vol > 100) vol = 100; vol = SCALE(100, 3, vol); if (write_cmd(0x03, vol)) return -EIO; vol = SCALE(3, 100, vol); - return snd_ioctl_return((int *) arg, vol | (vol << 8)); + return (*(int *) arg = vol | (vol << 8)); case SOUND_MIXER_RECSRC: - return snd_ioctl_return ((int *) arg, 0); + return (*(int *) arg = 0); break; default: return -EINVAL; @@ -364,7 +372,7 @@ aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) /* only read */ switch (cmd & 0xff) { case SOUND_MIXER_DEVMASK: - return snd_ioctl_return ((int *) arg, + return (*(int *) arg = SOUND_MASK_VOLUME | SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_SYNTH | SOUND_MASK_PCM | @@ -374,20 +382,20 @@ aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) SOUND_MASK_LINE1 | SOUND_MASK_LINE2); break; case SOUND_MIXER_STEREODEVS: - return snd_ioctl_return ((int *) arg, + return (*(int *) arg = SOUND_MASK_VOLUME | SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE1 | SOUND_MASK_LINE2); break; case SOUND_MIXER_RECMASK: - return snd_ioctl_return ((int *) arg, 0); + return (*(int *) arg = 0); break; case SOUND_MIXER_RECSRC: - return snd_ioctl_return ((int *) arg, 0); + return (*(int *) arg = 0); break; case SOUND_MIXER_CAPS: - return snd_ioctl_return ((int *) arg, 0); + return (*(int *) arg = 0); break; case SOUND_MIXER_VOLUME: return getvolume(arg, 0x04, 0x03); @@ -409,7 +417,7 @@ aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) if (indexed_cmd(0xf0, 0x21, &buf)) return -EIO; vol = SCALE(3, 100, buf <= 3 ? buf : 3); vol |= vol << 8; - return snd_ioctl_return((int *) arg, vol); + return (*(int *) arg = vol); default: return -EINVAL; } @@ -484,7 +492,7 @@ int attach_aci(void) } if (aci_idcode[0] == 0x6d) { - /* it looks like a miro soundcard */ + /* It looks like a miro sound card. */ switch (aci_idcode[1]) { case 0x41: boardname = "PCM1 pro / early PCM12"; @@ -506,15 +514,17 @@ int attach_aci(void) printk(" at 0x%03x\n", aci_version, aci_idcode[0], aci_idcode[1], boardname, aci_port); - /* initialize ACI mixer */ - implied_cmd(0xff); - aci_solo = 0; + if (aci_reset) { + /* initialize ACI mixer */ + implied_cmd(0xff); + aci_solo = 0; + } /* attach the mixer */ request_region(aci_port, 3, "sound mixer (ACI)"); if (num_mixers < MAX_MIXER_DEV) { if (num_mixers > 0 && - !strcmp("MAD16 WSS (CS4231A)", mixer_devs[num_mixers-1]->name)) { + !strncmp("MAD16 WSS", mixer_devs[num_mixers-1]->name, 9)) { /* * The previously registered mixer device is the CS4231A which * has no function on an ACI card. Make the ACI mixer the first @@ -559,23 +569,30 @@ int attach_aci(void) mixer_devs[num_mixers++] = &aci_mixer_operations; } - /* Initialize ACI mixer with reasonable power-up values */ - volume = 0x3232; - aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_VOLUME, (caddr_t) &volume); - volume = 0x3232; - aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_SYNTH, (caddr_t) &volume); - volume = 0x3232; - aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_PCM, (caddr_t) &volume); - volume = 0x3232; - aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE, (caddr_t) &volume); - volume = 0x3232; - aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_MIC, (caddr_t) &volume); - volume = 0x3232; - aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_CD, (caddr_t) &volume); - volume = 0x3232; - aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE1, (caddr_t) &volume); - volume = 0x3232; - aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE2, (caddr_t) &volume); + /* Just do something; otherwise the first write command fails, at + * least with my PCM20. + */ + aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_READ_VOLUME, (caddr_t) &volume); + + if (aci_reset) { + /* Initialize ACI mixer with reasonable power-up values */ + volume = 0x3232; + aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_VOLUME, (caddr_t) &volume); + volume = 0x3232; + aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_SYNTH, (caddr_t) &volume); + volume = 0x3232; + aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_PCM, (caddr_t) &volume); + volume = 0x3232; + aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE, (caddr_t) &volume); + volume = 0x3232; + aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_MIC, (caddr_t) &volume); + volume = 0x3232; + aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_CD, (caddr_t) &volume); + volume = 0x3232; + aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE1, (caddr_t) &volume); + volume = 0x3232; + aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE2, (caddr_t) &volume); + } aci_present = 1; diff --git a/drivers/sound/lowlevel/aedsp16.c b/drivers/sound/lowlevel/aedsp16.c new file mode 100644 index 000000000000..cb919507ebc8 --- /dev/null +++ b/drivers/sound/lowlevel/aedsp16.c @@ -0,0 +1,1387 @@ +/* + drivers/sound/lowlevel/aedsp16.c + + Audio Excel DSP 16 software configuration routines + Copyright (C) 1995,1996,1997,1998 Riccardo Facchetti (fizban@tin.it) + + 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 the main OSS Lite header file. It include all the os, OSS Lite, etc + * headers needed by this source. + */ +#include +#include +#include +#include "../sound_config.h" +#include "../soundmodule.h" + +/* + * Sanity checks + */ + +#if !defined(CONFIG_AEDSP16_BASE) +# undef CONFIG_AEDSP16 +#else +# if defined(MODULE) && defined(CONFIG_AEDSP16_MODULE) +# define CONFIG_AEDSP16 1 +# endif +#endif + + +#if defined(CONFIG_AEDSP16) + +#if defined(CONFIG_AEDSP16_SBPRO) && defined(CONFIG_AEDSP16_MSS) +#error You have to enable only one of the MSS and SBPRO emulations. +#endif + +/* + + READ THIS + + This module started to configure the Audio Excel DSP 16 Sound Card. + Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards. + + NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this + audio card and want to see the kernel support for it, please contact me. + + Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401 + compatible card. + It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq), + so before this module, the only way to configure the DSP under linux was + boot the MS-DOS loading the sound.sys device driver (this driver soft- + configure the sound board hardware by massaging someone of its registers), + and then ctrl-alt-del to boot linux with the DSP configured by the DOS + driver. + + This module works configuring your Audio Excel DSP 16's irq, dma and + mpu-401-irq. The OSS Lite routines rely on the fact that if the + hardware is there, they can detect it. The problem with AEDSP16 is + that no hardware can be found by the probe routines if the sound card + is not configured properly. Sometimes the kernel probe routines can find + an SBPRO even when the card is not configured (this is the standard setup + of the card), but the SBPRO emulation don't work well if the card is not + properly initialized. For this reason + + aedsp16_init_board() + + routine is called before the OSS Lite probe routines try to detect the + hardware. + + NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS) + + NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards + have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They + have to be configured by software. + + NOTE: The driver is merged with the new OSS Lite sound driver. It works + as a lowlevel driver. + + The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS; + the OSS Lite sound driver can be configured for SBPRO and MSS cards + at the same time, but the aedsp16 can't be two cards!! + When we configure it, we have to choose the SBPRO or the MSS emulation + for AEDSP16. We also can install a *REAL* card of the other type (see [1]). + + NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO + please let me know if it works. + + The MPU-401 support can be compiled in together with one of the other + two operating modes. + + NOTE: This is something like plug-and-play: we have only to plug + the AEDSP16 board in the socket, and then configure and compile + a kernel that uses the AEDSP16 software configuration capability. + No jumper setting is needed! + + For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3 + you have just to make config the OSS Lite package, configuring + the AEDSP16 sound card, then activating the SBPro emulation mode + and at last configuring IRQ and DMA. + Compile the kernel and run it. + + NOTE: This means for SC-6000 cards that you can choose irq and dma, + but not the I/O addresses. To change I/O addresses you have to set + them with jumpers. For SC-6600 cards you have no jumpers so you have + to set up your full card configuration in the make config. + + You can change the irq/dma/mirq settings WITHOUT THE NEED to open + your computer and massage the jumpers (there are no irq/dma/mirq + jumpers to be configured anyway, only I/O BASE values have to be + configured with jumpers) + + For some ununderstandable reason, the card default of irq 7, dma 1, + don't work for me. Seems to be an IRQ or DMA conflict. Under heavy + HDD work, the kernel start to erupt out a lot of messages like: + + 'Sound: DMA timed out - IRQ/DRQ config error?' + + For what I can say, I have NOT any conflict at irq 7 (under linux I'm + using the lp polling driver), and dma line 1 is unused as stated by + /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so + I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows! + Anyway a setting of irq 10, dma 3 works really fine. + + NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know + the emulation mode, all the installed hardware and the hardware + configuration (irq and dma settings of all the hardware). + + This init module should work with SBPRO+MSS, when one of the two is + the AEDSP16 emulation and the other the real card. (see [1]) + For example: + + AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other + AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other + + MPU401 should work. (see [2]) + + [1] + --- + Date: Mon, 29 Jul 1997 08:35:40 +0100 + From: Mr S J Greenaway + + [...] + Just to let you know got my Audio Excel (emulating a MSS) working + with my original SB16, thanks for the driver! + [...] + --- + + [2] Not tested by me for lack of hardware. + + TODO, WISHES AND TECH + + - About I/O ports allocation - + + Request the 2x0h region (port base) in any case if we are using this card. + + NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16 + port base region (see code) does not mean necessarily that we are emulating + sbpro. Even if this region is the sbpro I/O ports region, we use this + region to access the control registers of the card, and if emulating + sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro + registers are not used, in no way, to emulate an sbpro: they are + used only for configuration purposes. + + Started Fri Mar 17 16:13:18 MET 1995 + + v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c) + - Initial code. + v0.2 (ALPHA) + - Cleanups. + - Integrated with Linux voxware v 2.90-2 kernel sound driver. + - SoundBlaster Pro mode configuration. + - Microsoft Sound System mode configuration. + - MPU-401 mode configuration. + v0.3 (ALPHA) + - Cleanups. + - Rearranged the code to let aedsp16_init_board be more general. + - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h + inclusion too. We rely on os.h + - Used the to get a variable + len string (we are not sure about the len of Copyright string). + This works with any SB and compatible. + - Added the code to request_region at device init (should go in + the main body of voxware). + v0.4 (BETA) + - Better configure.c patch for aedsp16 configuration (better + logic of inclusion of AEDSP16 support) + - Modified the conditional compilation to better support more than + one sound card of the emulated type (read the NOTES above) + - Moved the sb init routine from the attach to the very first + probe in sb_card.c + - Rearrangements and cleanups + - Wiped out some unnecessary code and variables: this is kernel + code so it is better save some TEXT and DATA + - Fixed the request_region code. We must allocate the aedsp16 (sbpro) + I/O ports in any case because they are used to access the DSP + configuration registers and we can not allow anyone to get them. + v0.5 + - cleanups on comments + - prep for diffs against v3.0-proto-950402 + v0.6 + - removed the request_region()s when compiling the MODULE sound.o + because we are not allowed (by the actual voxware structure) to + release_region() + v0.7 (pre ALPHA, not distributed) + - started porting this module to kernel 1.3.84. Dummy probe/attach + routines. + v0.8 (ALPHA) + - attached all the init routines. + v0.9 (BETA) + - Integrated with linux-pre2.0.7 + - Integrated with configuration scripts. + - Cleaned up and beautyfied the code. + v0.9.9 (BETA) + - Thanks to Piercarlo Grandi: corrected the conditonal compilation code. + Now only the code configured is compiled in, with some memory saving. + v0.9.10 + - Integration into the sound/lowlevel/ section of the sound driver. + - Re-organized the code. + v0.9.11 (not distributed) + - Rewritten the init interface-routines to initialize the AEDSP16 in + one shot. + - More cosmetics. + - SC-6600 support. + - More soft/hard configuration. + v0.9.12 + - Refined the v0.9.11 code with conditional compilation to distinguish + between SC-6000 and SC-6600 code. + v1.0.0 + - Prep for merging with OSS Lite and Linux kernel 2.1.13 + - Corrected a bug in request/check/release region calls (thanks to the + new kernel exception handling). + v1.1 + - Revamped for integration with new modularized sound drivers: to enhance + the flexibility of modular version, I have removed all the conditional + compilation for SBPRO, MPU and MSS code. Now it is all managed with + the ae_config structure. + + Known Problems: + - Audio Excel DSP 16 III don't work with this driver. + + Credits: + Many thanks to Gerald Britton . He helped me a + lot in testing the 0.9.11 and 0.9.12 versions of this driver. + + */ + + +#define VERSION "1.1" /* Version of Audio Excel DSP 16 driver */ + +#undef AEDSP16_DEBUG 1 /* Define this to enable debug code */ +#undef AEDSP16_DEBUG_MORE 1 /* Define this to enable more debug */ +#undef AEDSP16_INFO 1 /* Define this to enable info code */ + +#if defined(AEDSP16_DEBUG) +# define DBG(x) printk x +# if defined(AEDSP16_DEBUG_MORE) +# define DBG1(x) printk x +# else +# define DBG1(x) +# endif +#else +# define DBG(x) +# define DBG1(x) +#endif + +/* + * Misc definitions + */ +#define TRUE 1 +#define FALSE 0 + +/* + * Region Size for request/check/release region. + */ +#define IOBASE_REGION_SIZE 0x10 + +/* + * Hardware related defaults + */ +#define DEF_AEDSP16_IOB 0x220 /* 0x220(default) 0x240 */ +#define DEF_AEDSP16_IRQ 7 /* 5 7(default) 9 10 11 */ +#define DEF_AEDSP16_MRQ 0 /* 5 7 9 10 0(default), 0 means disable */ +#define DEF_AEDSP16_DMA 1 /* 0 1(default) 3 */ + +/* + * Commands of AEDSP16's DSP (SBPRO+special). + * Some of them are COMMAND_xx, in the future they may change. + */ +#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */ +#define COMMAND_52 0x52 /* */ +#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */ +#define COMMAND_5C 0x5c /* */ +#define COMMAND_60 0x60 /* */ +#define COMMAND_66 0x66 /* */ +#define COMMAND_6C 0x6c /* */ +#define COMMAND_6E 0x6e /* */ +#define COMMAND_88 0x88 /* */ +#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */ +#define COMMAND_C5 0xc5 /* */ +#define GET_DSP_VERSION 0xe1 /* Get DSP Version */ +#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */ + +/* + * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port + * to have the actual I/O port. + * Register permissions are: + * (wo) == Write Only + * (ro) == Read Only + * (w-) == Write + * (r-) == Read + */ +#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */ +#define DSP_READ 0x0a /* offset of DSP READ (ro) */ +#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */ +#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */ +#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */ +#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */ + + +#define RETRY 10 /* Various retry values on I/O opera- */ +#define STATUSRETRY 1000 /* tions. Sometimes we have to */ +#define HARDRETRY 500000 /* wait for previous cmd to complete */ + +/* + * Size of character arrays that store name and version of sound card + */ +#define CARDNAMELEN 15 /* Size of the card's name in chars */ +#define CARDVERLEN 2 /* Size of the card's version in chars */ + +#if defined(CONFIG_SC6600) +/* + * Bitmapped flags of hard configuration + */ +/* + * Decode macros (xl == low byte, xh = high byte) + */ +#define IOBASE(xl) ((xl & 0x01)?0x240:0x220) +#define JOY(xl) (xl & 0x02) +#define MPUADDR(xl) ( \ + (xl & 0x0C)?0x330: \ + (xl & 0x08)?0x320: \ + (xl & 0x04)?0x310: \ + 0x300) +#define WSSADDR(xl) ((xl & 0x10)?0xE80:0x530) +#define CDROM(xh) (xh & 0x20) +#define CDROMADDR(xh) (((xh & 0x1F) << 4) + 0x200) +/* + * Encode macros + */ +#define BLDIOBASE(xl, val) { \ + xl &= ~0x01; \ + if (val == 0x240) \ + xl |= 0x01; \ + } +#define BLDJOY(xl, val) { \ + xl &= ~0x02; \ + if (val == 1) \ + xl |= 0x02; \ + } +#define BLDMPUADDR(xl, val) { \ + xl &= ~0x0C; \ + switch (val) { \ + case 0x330: \ + xl |= 0x0C; \ + break; \ + case 0x320: \ + xl |= 0x08; \ + break; \ + case 0x310: \ + xl |= 0x04; \ + break; \ + case 0x300: \ + xl |= 0x00; \ + break; \ + default: \ + xl |= 0x00; \ + break; \ + } \ + } +#define BLDWSSADDR(xl, val) { \ + xl &= ~0x10; \ + if (val == 0xE80) \ + xl |= 0x10; \ + } +#define BLDCDROM(xh, val) { \ + xh &= ~0x20; \ + if (val == 1) \ + xh |= 0x20; \ + } +#define BLDCDROMADDR(xh, val) { \ + int tmp = val; \ + tmp -= 0x200; \ + tmp >>= 4; \ + tmp &= 0x1F; \ + xh |= tmp; \ + xh &= 0x7F; \ + xh |= 0x40; \ + } +#endif /* CONFIG_SC6600 */ + +/* + * Bit mapped flags for calling aedsp16_init_board(), and saving the current + * emulation mode. + */ +#define INIT_NONE (0 ) +#define INIT_SBPRO (1<<0) +#define INIT_MSS (1<<1) +#define INIT_MPU401 (1<<2) + +static int soft_cfg = 0; /* Will contain or'ed values of soft cf */ +static int soft_cfg_1 = 0; /* Will contain or'ed values of some cf */ +static int gc = 0; /* generic counter (utility counter) */ +static int ver[3]; /* DSP Version, hi<-ver[0], lo<-ver[1] */ + +#if defined(CONFIG_SC6600) +static int hard_cfg[2] /* lo<-hard_cfg[0] hi<-hard_cfg[1] */ + = { 0, 0}; +#endif /* CONFIG_SC6600 */ + +#if defined(CONFIG_SC6600) +/* Decoded hard configuration */ +struct d_hcfg { + int iobase; + int joystick; + int mpubase; + int wssbase; + int cdrom; + int cdrombase; +} decoded_hcfg; +#endif /* CONFIG_SC6600 */ + +/* orVals contain the values to be or'ed */ +struct orVals { + int val; /* irq|mirq|dma */ + int or; /* soft_cfg |= TheStruct.or */ +}; + +/* aedsp16_info contain the audio card configuration */ +struct aedsp16_info { + int base_io; /* base I/O address for accessing card */ + int irq; /* irq value for DSP I/O */ + int mpu_irq; /* irq for mpu401 interface I/O */ + int dma; /* dma value for DSP I/O */ + int mss_base; /* base I/O for Microsoft Sound System */ + int mpu_base; /* base I/O for MPU-401 emulation */ + int init; /* Initialization status of the card */ +}; + +/* + * Magic values that the DSP will eat when configuring irq/mirq/dma + */ +/* DSP IRQ conversion array */ +static struct orVals orIRQ[] = { + {0x05, 0x28}, + {0x07, 0x08}, + {0x09, 0x10}, + {0x0a, 0x18}, + {0x0b, 0x20}, + {0x00, 0x00} +}; + +/* MPU-401 IRQ conversion array */ +static struct orVals orMIRQ[] = { + {0x05, 0x04}, + {0x07, 0x44}, + {0x09, 0x84}, + {0x0a, 0xc4}, + {0x00, 0x00} +}; + +/* DMA Channels conversion array */ +static struct orVals orDMA[] = { + {0x00, 0x01}, + {0x01, 0x02}, + {0x03, 0x03}, + {0x00, 0x00} +}; + +static struct aedsp16_info ae_config = { + DEF_AEDSP16_IOB, + DEF_AEDSP16_IRQ, + DEF_AEDSP16_MRQ, + DEF_AEDSP16_DMA, + -1, + -1, + INIT_NONE +}; + +/* + * Buffers to store audio card informations + */ +static char DSPCopyright[CARDNAMELEN + 1]; +static char DSPVersion[CARDVERLEN + 1]; + +static void aedsp16_delay_10msec(void) +{ + for (gc = 0; gc < 1000; gc++) + udelay(10); +} + +static int aedsp16_wait_data(int port) +{ + int loop = STATUSRETRY; + unsigned char ret = 0; + + DBG1(("aedsp16_wait_data (0x%x): ", port)); + + do { + ret = inb(port + DSP_DATAVAIL); + /* + * Wait for data available (bit 7 of ret == 1) + */ + } while (!(ret & 0x80) && loop--); + + if (ret & 0x80) { + DBG1(("success.\n")); + return TRUE; + } + + DBG1(("failure.\n")); + return FALSE; +} + +static int aedsp16_read(int port) +{ + int inbyte; + + DBG((" Read DSP Byte (0x%x): ", port)); + + if (aedsp16_wait_data(port) == FALSE) { + DBG(("failure.\n")); + return -1; + } + + inbyte = inb(port + DSP_READ); + + DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte)); + + return inbyte; +} + +static int aedsp16_test_dsp(int port) +{ + return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE); +} + +static int aedsp16_dsp_reset(int port) +{ + /* + * Reset DSP + */ + + DBG(("Reset DSP:\n")); + + outb(1, (port + DSP_RESET)); + udelay(10); + outb(0, (port + DSP_RESET)); + udelay(10); + udelay(10); + if (aedsp16_test_dsp(port) == TRUE) { + DBG(("success.\n")); + return TRUE; + } else + DBG(("failure.\n")); + return FALSE; +} + +static int aedsp16_write(int port, int cmd) +{ + unsigned char ret; + int loop = HARDRETRY; + + DBG((" Write DSP Byte (0x%x) [0x%x]: ", port, cmd)); + + do { + ret = inb(port + DSP_STATUS); + /* + * DSP ready to receive data if bit 7 of ret == 0 + */ + if (!(ret & 0x80)) { + outb(cmd, port + DSP_COMMAND); + DBG(("success.\n")); + return 0; + } + } while (loop--); + + DBG(("timeout.\n")); + printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd); + + return -1; +} + +#if defined(CONFIG_SC6600) + +#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) +void aedsp16_pinfo(void) { + DBG(("\n Base address: %x\n", decoded_hcfg.iobase)); + DBG((" Joystick : %s present\n", decoded_hcfg.joystick?"":" not")); + DBG((" WSS addr : %x\n", decoded_hcfg.wssbase)); + DBG((" MPU-401 addr: %x\n", decoded_hcfg.mpubase)); + DBG((" CDROM : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not")); + DBG((" CDROMADDR : %x\n\n", decoded_hcfg.cdrombase)); +} +#endif + +void aedsp16_hard_decode(void) { + + DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); + +/* + * Decode Cfg Bytes. + */ + decoded_hcfg.iobase = IOBASE(hard_cfg[0]); + decoded_hcfg.joystick = JOY(hard_cfg[0]); + decoded_hcfg.wssbase = WSSADDR(hard_cfg[0]); + decoded_hcfg.mpubase = MPUADDR(hard_cfg[0]); + decoded_hcfg.cdrom = CDROM(hard_cfg[1]); + decoded_hcfg.cdrombase = CDROMADDR(hard_cfg[1]); + +#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) + printk(" Original sound card configuration:\n"); + aedsp16_pinfo(); +#endif + +/* + * Now set up the real kernel configuration. + */ + decoded_hcfg.iobase = ae_config.base_io; + decoded_hcfg.wssbase = ae_config.mss_base; + decoded_hcfg.mpubase = ae_config.mpu_base; + +#if defined(CONFIG_SC6600_JOY) + decoded_hcfg.joystick = CONFIG_SC6600_JOY; /* Enable */ +#endif +#if defined(CONFIG_SC6600_CDROM) + decoded_hcfg.cdrom = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */ +#endif +#if defined(CONFIG_SC6600_CDROMBASE) + decoded_hcfg.cdrombase = CONFIG_SC6600_CDROMBASE; /* 0 Disable */ +#endif + +#if defined(AEDSP16_DEBUG) + DBG((" New Values:\n")); + aedsp16_pinfo(); +#endif + + DBG(("success.\n")); +} + +void aedsp16_hard_encode(void) { + + DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); + + hard_cfg[0] = 0; + hard_cfg[1] = 0; + + hard_cfg[0] |= 0x20; + + BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase); + BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase); + BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase); + BLDJOY(hard_cfg[0], decoded_hcfg.joystick); + BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom); + BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase); + +#if defined(AEDSP16_DEBUG) + aedsp16_pinfo(); +#endif + + DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); + DBG(("success.\n")); + +} + +static int aedsp16_hard_write(int port) { + + DBG(("aedsp16_hard_write:\n")); + + if (aedsp16_write(port, COMMAND_6C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, COMMAND_5C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, hard_cfg[0])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, hard_cfg[1])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_write(port, COMMAND_C5)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5); + DBG(("failure.\n")); + return FALSE; + } + + DBG(("success.\n")); + + return TRUE; +} + +static int aedsp16_hard_read(int port) { + + DBG(("aedsp16_hard_read:\n")); + + if (aedsp16_write(port, READ_HARD_CFG)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + + if ((hard_cfg[0] = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + if ((hard_cfg[1] = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + if (aedsp16_read(port) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + READ_HARD_CFG); + DBG(("failure.\n")); + return FALSE; + } + + DBG(("success.\n")); + + return TRUE; +} + +static int aedsp16_ext_cfg_write(int port) { + + int extcfg, val; + + if (aedsp16_write(port, COMMAND_66)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66); + return FALSE; + } + + extcfg = 7; + if (decoded_hcfg.cdrom != 2) + extcfg = 0x0F; + if ((decoded_hcfg.cdrom == 4) || + (decoded_hcfg.cdrom == 3)) + extcfg &= ~2; + if (decoded_hcfg.cdrombase == 0) + extcfg &= ~2; + if (decoded_hcfg.mpubase == 0) + extcfg &= ~1; + + if (aedsp16_write(port, extcfg)) { + printk("[AEDSP16] Write extcfg: failed!\n"); + return FALSE; + } + if (aedsp16_write(port, 0)) { + printk("[AEDSP16] Write extcfg: failed!\n"); + return FALSE; + } + if (decoded_hcfg.cdrom == 3) { + if (aedsp16_write(port, COMMAND_52)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52); + return FALSE; + } + if ((val = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n" + , COMMAND_52); + return FALSE; + } + val &= 0x7F; + if (aedsp16_write(port, COMMAND_60)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60); + return FALSE; + } + if (aedsp16_write(port, val)) { + printk("[AEDSP16] Write val: failed!\n"); + return FALSE; + } + } + + return TRUE; +} + +#endif /* CONFIG_SC6600 */ + +static int aedsp16_cfg_write(int port) { + if (aedsp16_write(port, WRITE_MDIRQ_CFG)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); + return FALSE; + } + if (aedsp16_write(port, soft_cfg)) { + printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n"); + return FALSE; + } + return TRUE; +} + +static int aedsp16_init_mss(int port) +{ + DBG(("aedsp16_init_mss:\n")); + + aedsp16_delay_10msec(); + + if (aedsp16_write(port, DSP_INIT_MSS)) { + printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n", + DSP_INIT_MSS); + DBG(("failure.\n")); + return FALSE; + } + aedsp16_delay_10msec(); + + if (aedsp16_cfg_write(port) == FALSE) + return FALSE; + + outb(soft_cfg_1, ae_config.mss_base); + + DBG(("success.\n")); + + return TRUE; +} + +static int aedsp16_setup_board(int port) { + int loop = RETRY; + +#if defined(CONFIG_SC6600) + int val = 0; + + if (aedsp16_hard_read(port) == FALSE) { + printk("[AEDSP16] aedsp16_hard_read: failed!\n"); + return FALSE; + } + + if (aedsp16_write(port, COMMAND_52)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52); + return FALSE; + } + + if ((val = aedsp16_read(port)) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + COMMAND_52); + return FALSE; + } +#endif + + do { + if (aedsp16_write(port, COMMAND_88)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88); + return FALSE; + } + aedsp16_delay_10msec(); + } while ((aedsp16_wait_data(port) == FALSE) && loop--); + + if (aedsp16_read(port) == -1) { + printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", + COMMAND_88); + return FALSE; + } + +#if !defined(CONFIG_SC6600) + if (aedsp16_write(port, COMMAND_5C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); + return FALSE; + } +#endif + + if (aedsp16_cfg_write(port) == FALSE) + return FALSE; + +#if defined(CONFIG_SC6600) + if (aedsp16_write(port, COMMAND_60)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60); + return FALSE; + } + if (aedsp16_write(port, val)) { + printk("[AEDSP16] DATA 0x%x: failed!\n", val); + return FALSE; + } + if (aedsp16_write(port, COMMAND_6E)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E); + return FALSE; + } + if (aedsp16_write(port, ver[0])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]); + return FALSE; + } + if (aedsp16_write(port, ver[1])) { + printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]); + return FALSE; + } + + if (aedsp16_hard_write(port) == FALSE) { + printk("[AEDSP16] aedsp16_hard_write: failed!\n"); + return FALSE; + } + + if (aedsp16_write(port, COMMAND_5C)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); + return FALSE; + } + +#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET) + if (aedsp16_cfg_write(port) == FALSE) + return FALSE; +#endif + +#endif + + return TRUE; +} + +static int aedsp16_stdcfg(int port) { + if (aedsp16_write(port, WRITE_MDIRQ_CFG)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); + return FALSE; + } + /* + * 0x0A == (IRQ 7, DMA 1, MIRQ 0) + */ + if (aedsp16_write(port, 0x0A)) { + printk("[AEDSP16] aedsp16_stdcfg: failed!\n"); + return FALSE; + } + return TRUE; +} + +static int aedsp16_dsp_version(int port) +{ + int len = 0; + int ret; + + DBG(("Get DSP Version:\n")); + + if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION); + DBG(("failed.\n")); + return FALSE; + } + + do { + if ((ret = aedsp16_read(port)) == -1) { + DBG(("failed.\n")); + return FALSE; + } + /* + * We already know how many int are stored (2), so we know when the + * string is finished. + */ + ver[len++] = ret; + } while (len < CARDVERLEN); + sprintf(DSPVersion, "%d.%d", ver[0], ver[1]); + + DBG(("success.\n")); + + return TRUE; +} + +static int aedsp16_dsp_copyright(int port) +{ + int len = 0; + int ret; + + DBG(("Get DSP Copyright:\n")); + + if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) { + printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT); + DBG(("failed.\n")); + return FALSE; + } + + do { + if ((ret = aedsp16_read(port)) == -1) { + /* + * If no more data available, return to the caller, no error if len>0. + * We have no other way to know when the string is finished. + */ + if (len) + break; + else { + DBG(("failed.\n")); + return FALSE; + } + } + + DSPCopyright[len++] = ret; + + } while (len < CARDNAMELEN); + + DBG(("success.\n")); + + return TRUE; +} + +static void aedsp16_init_tables(void) +{ + memset(DSPCopyright, 0, CARDNAMELEN + 1); + memset(DSPVersion, 0, CARDVERLEN + 1); + + for (gc = 0; orIRQ[gc].or; gc++) + if (orIRQ[gc].val == ae_config.irq) { + soft_cfg |= orIRQ[gc].or; + soft_cfg_1 |= orIRQ[gc].or; + } + + for (gc = 0; orMIRQ[gc].or; gc++) + if (orMIRQ[gc].or == ae_config.mpu_irq) + soft_cfg |= orMIRQ[gc].or; + + for (gc = 0; orDMA[gc].or; gc++) + if (orDMA[gc].val == ae_config.dma) { + soft_cfg |= orDMA[gc].or; + soft_cfg_1 |= orDMA[gc].or; + } +} + +static int aedsp16_init_board(void) +{ + aedsp16_init_tables(); + + if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_dsp_reset: failed!\n"); + return FALSE; + } + if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n"); + return FALSE; + } + + /* + * My AEDSP16 card return SC-6000 in DSPCopyright, so + * if we have something different, we have to be warned. + */ + if (strcmp("SC-6000", DSPCopyright)) + printk("[AEDSP16] Warning: non SC-6000 audio card!\n"); + + if (aedsp16_dsp_version(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_dsp_version: failed!\n"); + return FALSE; + } + + if (aedsp16_stdcfg(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_stdcfg: failed!\n"); + return FALSE; + } + +#if defined(CONFIG_SC6600) + if (aedsp16_hard_read(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_hard_read: failed!\n"); + return FALSE; + } + + aedsp16_hard_decode(); + + aedsp16_hard_encode(); + + if (aedsp16_hard_write(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_hard_write: failed!\n"); + return FALSE; + } + + if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n"); + return FALSE; + } +#endif /* CONFIG_SC6600 */ + + if (aedsp16_setup_board(ae_config.base_io) == FALSE) { + printk("[AEDSP16] aedsp16_setup_board: failed!\n"); + return FALSE; + } + + if (ae_config.mss_base != -1) { + if (ae_config.init & INIT_MSS) { + if (aedsp16_init_mss(ae_config.base_io) == FALSE) { + printk("[AEDSP16] Can not initialize" + "Microsoft Sound System mode.\n"); + return FALSE; + } + } + } + +#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) + + printk("Audio Excel DSP 16 init v%s (%s %s) [", + VERSION, DSPCopyright, + DSPVersion); + + if (ae_config.mpu_base != -1) { + if (ae_config.init & INIT_MPU401) { + printk("MPU401"); + if ((ae_config.init & INIT_MSS) || + (ae_config.init & INIT_SBPRO)) + printk(" "); + } + } + + if (ae_config.mss_base == -1) { + if (ae_config.init & INIT_SBPRO) { + printk("SBPro"); + if (ae_config.init & INIT_MSS) + printk(" "); + } + } + + if (ae_config.mss_base != -1) + if (ae_config.init & INIT_MSS) + printk("MSS"); + + printk("]\n"); +#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */ + + aedsp16_delay_10msec(); + + return TRUE; +} + +static int init_aedsp16_sb(void) +{ + DBG(("init_aedsp16_sb: ")); + +/* + * If the card is already init'ed MSS, we can not init it to SBPRO too + * because the board can not emulate simultaneously MSS and SBPRO. + */ + if (ae_config.init & INIT_MSS) + return FALSE; + if (ae_config.init & INIT_SBPRO) + return FALSE; + + ae_config.init |= INIT_SBPRO; + + DBG(("done.\n")); + + return TRUE; +} + +static void uninit_aedsp16_sb(void) +{ + DBG(("uninit_aedsp16_sb: ")); + + ae_config.init &= ~INIT_SBPRO; + + DBG(("done.\n")); +} + +static int init_aedsp16_mss(void) +{ + DBG(("init_aedsp16_mss: ")); + +/* + * If the card is already init'ed SBPRO, we can not init it to MSS too + * because the board can not emulate simultaneously MSS and SBPRO. + */ + if (ae_config.init & INIT_SBPRO) + return FALSE; + if (ae_config.init & INIT_MSS) + return FALSE; +/* + * We must check the CONFIG_AEDSP16_BASE region too because these are the I/O + * ports to access card's control registers. + */ + if (!(ae_config.init & INIT_MPU401)) { + if (check_region(ae_config.base_io, IOBASE_REGION_SIZE)) { + printk( + "AEDSP16 BASE I/O port region is already in use.\n"); + return FALSE; + } + } + +/* + * We must allocate the CONFIG_AEDSP16_BASE region too because these are the + * I/O ports to access card's control registers. + */ + if (!(ae_config.init & INIT_MPU401)) + request_region(ae_config.base_io, IOBASE_REGION_SIZE, + "aedsp16 (base)"); + + ae_config.init |= INIT_MSS; + + DBG(("done.\n")); + + return TRUE; +} + +static void uninit_aedsp16_mss(void) +{ + DBG(("uninit_aedsp16_mss: ")); + + if ((!(ae_config.init & INIT_MPU401)) && + (ae_config.init & INIT_MSS)) { + release_region(ae_config.base_io, IOBASE_REGION_SIZE); + DBG(("AEDSP16 base region released.\n")); + } + + ae_config.init &= ~INIT_MSS; + DBG(("done.\n")); +} + +static int init_aedsp16_mpu(void) +{ + DBG(("init_aedsp16_mpu: ")); + + if (ae_config.init & INIT_MPU401) + return FALSE; + +/* + * We must check the CONFIG_AEDSP16_BASE region too because these are the I/O + * ports to access card's control registers. + */ + if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) { + if (check_region(ae_config.base_io, IOBASE_REGION_SIZE)) { + printk( + "AEDSP16 BASE I/O port region is already in use.\n"); + return FALSE; + } + } + + if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) + request_region(ae_config.base_io, IOBASE_REGION_SIZE, + "aedsp16 (base)"); + + ae_config.init |= INIT_MPU401; + + DBG(("done.\n")); + + return TRUE; +} + +static void uninit_aedsp16_mpu(void) +{ + DBG(("uninit_aedsp16_mpu: ")); + + if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) && + (ae_config.init & INIT_MPU401)) { + release_region(ae_config.base_io, IOBASE_REGION_SIZE); + DBG(("AEDSP16 base region released.\n")); + } + + ae_config.init &= ~INIT_MPU401; + + DBG(("done.\n")); +} + +int init_aedsp16(void) +{ + int initialized = FALSE; + +#if !defined(MODULE) + ae_config.base_io = CONFIG_AEDSP16_BASE; +#if defined(CONFIG_AEDSP16_SBPRO) + ae_config.irq = CONFIG_AEDSP16_SB_IRQ; + ae_config.dma = CONFIG_AEDSP16_SB_DMA; +#endif +#if defined(CONFIG_AEDSP16_MSS) + ae_config.mss_base = CONFIG_MSS_BASE; + ae_config.irq = CONFIG_AEDSP16_MSS_IRQ; + ae_config.dma = CONFIG_AEDSP16_MSS_DMA; +#endif +#if defined(CONFIG_AEDSP16_MPU401) + ae_config.mpu_base = CONFIG_MPU_BASE; + ae_config.mpu_irq = CONFIG_AEDSP16_MPU_IRQ; +#endif +#endif /* !MODULE */ + DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n", + ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq)); + + if (ae_config.mss_base == -1) { + if (init_aedsp16_sb() == FALSE) { + uninit_aedsp16_sb(); + } else { + initialized = TRUE; + } + } + + if (ae_config.mpu_base != -1) { + if (init_aedsp16_mpu() == FALSE) { + uninit_aedsp16_mpu(); + } else { + initialized = TRUE; + } + } + +/* + * In the sequence of init routines, the MSS init MUST be the last! + * This because of the special register programming the MSS mode needs. + * A board reset would disable the MSS mode restoring the default SBPRO + * mode. + */ + if (ae_config.mss_base != -1) { + if (init_aedsp16_mss() == FALSE) { + uninit_aedsp16_mss(); + } else { + initialized = TRUE; + } + } + + if (initialized) + initialized = aedsp16_init_board(); + return initialized; +} + +void uninit_aedsp16(void) +{ + if (ae_config.mss_base != -1) + uninit_aedsp16_mss(); + else + uninit_aedsp16_sb(); + if (ae_config.mpu_base != -1) + uninit_aedsp16_mpu(); +} + +#if defined(MODULE) + +int io = -1; +int irq = -1; +int dma = -1; +int mpu_irq = -1; +int mss_base = -1; +int mpu_base = -1; + + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(mpu_irq,"i"); +MODULE_PARM(mss_base,"i"); +MODULE_PARM(mpu_base,"i"); + +int init_module(void) { + printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n"); + if (io == -1 || dma == -1 || irq == -1) { + printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n"); + return -EINVAL; + } + + ae_config.base_io = io; + ae_config.irq = irq; + ae_config.dma = dma; + + ae_config.mss_base = mss_base; + ae_config.mpu_base = mpu_base; + ae_config.mpu_irq = mpu_irq; + + if (init_aedsp16() == FALSE) { + printk(KERN_ERR "aedsp16: initialization failed\n"); + /* + * XXX + * What error should we return here ? + */ + return -EINVAL; + } + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) { + uninit_aedsp16(); + SOUND_LOCK_END; +} +#endif /* MODULE */ + +#endif /* CONFIG_AEDSP16 */ diff --git a/drivers/sound/lowlevel/awe_compat.h b/drivers/sound/lowlevel/awe_compat.h new file mode 100644 index 000000000000..d0c34a1fa8ed --- /dev/null +++ b/drivers/sound/lowlevel/awe_compat.h @@ -0,0 +1,190 @@ +/*---------------------------------------------------------------- + * compatibility macros for AWE32 driver + *----------------------------------------------------------------*/ + +/* redefine following macros */ +#undef IOCTL_IN +#undef IOCTL_OUT +#undef OUTW +#undef COPY_FROM_USER +#undef COPY_TO_USER +#undef GET_BYTE_FROM_USER +#undef GET_SHORT_FROM_USER +#undef IOCTL_TO_USER + +#ifdef linux + +/*================================================================ + * Linux macros + *================================================================*/ + +/* use inline prefix */ +#define INLINE inline + +/*---------------------------------------------------------------- + * memory management for linux + *----------------------------------------------------------------*/ + +#ifdef AWE_OBSOLETE_VOXWARE +/* old type linux system */ + +/* i/o requests; nothing */ +#define awe_check_port() 0 /* always false */ +#define awe_request_region() /* nothing */ +#define awe_release_region() /* nothing */ + +static int _mem_start; /* memory pointer for permanent buffers */ + +#define my_malloc_init(memptr) _mem_start = (memptr) +#define my_malloc_memptr() _mem_start +#define my_free(ptr) /* do nothing */ +#define my_realloc(buf,oldsize,size) NULL /* no realloc */ + +static void *my_malloc(int size) +{ + char *ptr; + PERMANENT_MALLOC(ptr, char*, size, _mem_start); + return (void*)ptr; +} + +/* allocate buffer only once */ +#define INIT_TABLE(buffer,index,nums,type) {\ +buffer = my_malloc(sizeof(type) * (nums)); index = (nums);\ +} + +#else + +#define AWE_DYNAMIC_BUFFER + +#define my_malloc_init(ptr) /* nothing */ +#define my_malloc_memptr() 0 +#define my_malloc(size) vmalloc(size) +#define my_free(ptr) if (ptr) {vfree(ptr);} + +static void *my_realloc(void *buf, int oldsize, int size) +{ + void *ptr; + if ((ptr = vmalloc(size)) == NULL) + return NULL; + memcpy(ptr, buf, ((oldsize < size) ? oldsize : size) ); + vfree(buf); + return ptr; +} + +/* do not allocate buffer at beginning */ +#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;} + +/* old type macro */ +#define RET_ERROR(err) -err + +#endif + +/*---------------------------------------------------------------- + * i/o interfaces for linux + *----------------------------------------------------------------*/ + +#define OUTW(data,addr) outw(data, addr) + +#ifdef AWE_NEW_KERNEL_INTERFACE +#define COPY_FROM_USER(target,source,offs,count) \ + copy_from_user(target, (source)+(offs), count) +#define GET_BYTE_FROM_USER(target,addr,offs) \ + get_user(target, (unsigned char*)&((addr)[offs])) +#define GET_SHORT_FROM_USER(target,addr,offs) \ + get_user(target, (unsigned short*)&((addr)[offs])) +#ifdef AWE_OSS38 +#define IOCTL_TO_USER(target,offs,source,count) \ + memcpy(target, (source)+(offs), count) +#define IO_WRITE_CHECK(cmd) (_SIOC_DIR(cmd) & _IOC_WRITE) +#else +#define IOCTL_TO_USER(target,offs,source,count) \ + copy_to_user(target, (source)+(offs), count) +#define IO_WRITE_CHECK(cmd) (_IOC_DIR(cmd) & _IOC_WRITE) +#endif /* AWE_OSS38 */ +#define COPY_TO_USER IOCTL_TO_USER +#define IOCTL_IN(arg) (*(int*)(arg)) +#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val)) + +#else /* old type i/o */ +#define COPY_FROM_USER(target,source,offs,count) \ + memcpy_fromfs(target, (source)+(offs), (count)) +#define GET_BYTE_FROM_USER(target,addr,offs) \ + *((char *)&(target)) = get_fs_byte((addr)+(offs)) +#define GET_SHORT_FROM_USER(target,addr,offs) \ + *((short *)&(target)) = get_fs_word((addr)+(offs)) +#define IOCTL_TO_USER(target,offs,source,count) \ + memcpy_tofs(target, (source)+(offs), (count)) +#define COPY_TO_USER IOCTL_TO_USER +#define IO_WRITE_CHECK(cmd) (cmd & IOC_IN) +#define IOCTL_IN(arg) get_fs_long((long *)(arg)) +#define IOCTL_OUT(arg,ret) snd_ioctl_return((int *)arg, ret) + +#endif /* AWE_NEW_KERNEL_INTERFACE */ + +#define BZERO(target,len) memset(target, 0, len) +#define MEMCPY(dst,src,len) memcpy(dst, src, len) + + +#elif defined(__FreeBSD__) + +/*================================================================ + * FreeBSD macros + *================================================================*/ + +/* inline is not checked yet.. maybe it'll work */ +#define INLINE /*inline*/ + +/*---------------------------------------------------------------- + * memory management for freebsd + *----------------------------------------------------------------*/ + +/* i/o requests; nothing */ +#define awe_check_port() 0 /* always false */ +#define awe_request_region() /* nothing */ +#define awe_release_region() /* nothing */ + +#define AWE_DYNAMIC_BUFFER + +#define my_malloc_init(ptr) /* nothing */ +#define my_malloc_memptr() 0 +#define my_malloc(size) malloc(size, M_TEMP, M_WAITOK) +#define my_free(ptr) if (ptr) {free(ptr, M_TEMP);} + +#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;} + +/* it should be realloc? */ +static void *my_realloc(void *buf, int oldsize, int size) +{ + void *ptr; + if ((ptr = my_malloc(size)) == NULL) + return NULL; + memcpy(ptr, buf, ((oldsize < size) ? oldsize : size) ); + my_free(buf); + return ptr; +} + +/*---------------------------------------------------------------- + * i/o interfaces for freebsd + *----------------------------------------------------------------*/ + +/* according to linux rule; the arguments are swapped */ +#define OUTW(data,addr) outw(addr, data) + +#define COPY_FROM_USER(target,source,offs,count) \ + uiomove(((caddr_t)(target)),(count),((struct uio *)(source))) +#define COPY_TO_USER(target,source,offs,count) \ + uiomove(((caddr_t)(source)),(count),((struct uio *)(target))) +#define GET_BYTE_FROM_USER(target,addr,offs) \ + uiomove(((char*)&(target)), 1, ((struct uio *)(addr))) +#define GET_SHORT_FROM_USER(target,addr,offs) \ + uiomove(((char*)&(target)), 2, ((struct uio *)(addr))) +#define IOCTL_TO_USER(target,offs,source,count) \ + memcpy(&((target)[offs]), (source), (count)) +#define IO_WRITE_CHECK(cmd) (cmd & IOC_IN) +#define IOCTL_IN(arg) (*(int*)(arg)) +#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val)) +#define BZERO(target,len) bzero((caddr_t)target, len) +#define MEMCPY(dst,src,len) bcopy((caddr_t)src, (caddr_t)dst, len) + +#endif + diff --git a/drivers/sound/lowlevel/awe_config.h b/drivers/sound/lowlevel/awe_config.h new file mode 100644 index 000000000000..c6b0dae6de6e --- /dev/null +++ b/drivers/sound/lowlevel/awe_config.h @@ -0,0 +1,145 @@ +/* + * sound/awe_config.h + * + * Configuration of AWE32 sound driver + * version 0.4.2; Sep. 15, 1997 + * + * Copyright (C) 1996 Takashi Iwai + * + * 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 AWE_CONFIG_H_DEF +#define AWE_CONFIG_H_DEF + +/*---------------------------------------------------------------- + * system configuration + *----------------------------------------------------------------*/ + +/* if you're using obsolete VoxWare 3.0.x on Linux 1.2.x (or FreeBSD), + * define the following line. + */ +#undef AWE_OBSOLETE_VOXWARE + +#ifdef __FreeBSD__ +# define AWE_OBSOLETE_VOXWARE +#endif + +/* if you're using OSS-Lite on Linux 2.1.6 or later, define the + * following line. + */ +#define AWE_NEW_KERNEL_INTERFACE + +/* if you have lowlevel.h in the lowlevel directory (OSS-Lite), define + * the following line. + */ +#define HAS_LOWLEVEL_H + +/* if your system doesn't support patch manager (OSS 3.7 or newer), + * define the following line. + */ +#define AWE_NO_PATCHMGR + +/* if your system has an additional parameter (OSS 3.8b5 or newer), + * define this. + */ +#define AWE_OSS38 + +/*---------------------------------------------------------------- + * AWE32 card configuration: + * uncomment the following lines only when auto detection doesn't + * work properly on your machine. + *----------------------------------------------------------------*/ + +/*#define AWE_DEFAULT_BASE_ADDR 0x620*/ /* base port address */ +/*#define AWE_DEFAULT_MEM_SIZE 512*/ /* kbytes */ + + +/*---------------------------------------------------------------- + * maximum size of soundfont list table: + * you usually don't need to touch this value. + *----------------------------------------------------------------*/ + +#define AWE_MAX_SF_LISTS 16 + + +/*---------------------------------------------------------------- + * chunk size of sample and voice tables: + * you usually don't need to touch these values. + *----------------------------------------------------------------*/ + +#define AWE_MAX_SAMPLES 400 +#define AWE_MAX_INFOS 800 + + +/*---------------------------------------------------------------- + * chorus & reverb effects send for FM chip: from 0 to 0xff + * larger numbers often cause weird sounds. + *----------------------------------------------------------------*/ + +#define DEF_FM_CHORUS_DEPTH 0x10 +#define DEF_FM_REVERB_DEPTH 0x10 + + +/*----------------------------------------------------------------* + * other compile conditions + *----------------------------------------------------------------*/ + +/* initialize FM passthrough even without extended RAM */ +#undef AWE_ALWAYS_INIT_FM + +/* debug on */ +#define AWE_DEBUG_ON + +/* GUS compatible mode */ +#define AWE_HAS_GUS_COMPATIBILITY + +/* accept all notes/sounds off controls */ +#define AWE_ACCEPT_ALL_SOUNDS_CONTROL + +/* add mixer control of emu8000 equalizer */ +#define CONFIG_AWE32_MIXER + +/* look up voices according to MIDI channel priority */ +#define AWE_LOOKUP_MIDI_PRIORITY + +/*----------------------------------------------------------------*/ + +/* reading configuration of sound driver */ + +#ifdef AWE_OBSOLETE_VOXWARE + +#ifdef __FreeBSD__ +# include +#else +# include "sound_config.h" +#endif + +#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AWE32) +#define CONFIG_AWE32_SYNTH +#endif + +#else /* AWE_OBSOLETE_VOXWARE */ + +#ifdef HAS_LOWLEVEL_H +#include "lowlevel.h" +#endif + +#include "../sound_config.h" + +#endif /* AWE_OBSOLETE_VOXWARE */ + + +#endif /* AWE_CONFIG_H_DEF */ diff --git a/drivers/sound/lowlevel/awe_hw.h b/drivers/sound/lowlevel/awe_hw.h new file mode 100644 index 000000000000..7d0d88e77d5c --- /dev/null +++ b/drivers/sound/lowlevel/awe_hw.h @@ -0,0 +1,100 @@ +/* + * sound/awe_hw.h + * + * Access routines and definitions for the low level driver for the + * AWE32/Sound Blaster 32 wave table synth. + * version 0.4.2; Sep. 15, 1997 + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * 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 AWE_HW_H_DEF +#define AWE_HW_H_DEF + +/* + * Emu-8000 control registers + * name(channel) reg, port + */ + +#define awe_cmd_idx(reg,ch) (((reg)<< 5) | (ch)) + +#define Data0 0x620 /* doubleword r/w */ +#define Data1 0xA20 /* doubleword r/w */ +#define Data2 0xA22 /* word r/w */ +#define Data3 0xE20 /* word r/w */ +#define Pointer 0xE22 /* register pointer r/w */ + +#define AWE_CPF(ch) awe_cmd_idx(0,ch), Data0 /* DW: current pitch and fractional address */ +#define AWE_PTRX(ch) awe_cmd_idx(1,ch), Data0 /* DW: pitch target and reverb send */ +#define AWE_CVCF(ch) awe_cmd_idx(2,ch), Data0 /* DW: current volume and filter cutoff */ +#define AWE_VTFT(ch) awe_cmd_idx(3,ch), Data0 /* DW: volume and filter cutoff targets */ +#define AWE_PSST(ch) awe_cmd_idx(6,ch), Data0 /* DW: pan send and loop start address */ +#define AWE_CSL(ch) awe_cmd_idx(7,ch), Data0 /* DW: chorus send and loop end address */ +#define AWE_CCCA(ch) awe_cmd_idx(0,ch), Data1 /* DW: Q, control bits, and current address */ +#define AWE_HWCF4 awe_cmd_idx(1,9), Data1 /* DW: config dw 4 */ +#define AWE_HWCF5 awe_cmd_idx(1,10), Data1 /* DW: config dw 5 */ +#define AWE_HWCF6 awe_cmd_idx(1,13), Data1 /* DW: config dw 6 */ +#define AWE_HWCF7 awe_cmd_idx(1,14), Data1 /* DW: config dw 7? (not documented) */ +#define AWE_SMALR awe_cmd_idx(1,20), Data1 /* DW: sound memory address for left read */ +#define AWE_SMARR awe_cmd_idx(1,21), Data1 /* DW: for right read */ +#define AWE_SMALW awe_cmd_idx(1,22), Data1 /* DW: sound memory address for left write */ +#define AWE_SMARW awe_cmd_idx(1,23), Data1 /* DW: for right write */ +#define AWE_SMLD awe_cmd_idx(1,26), Data1 /* W: sound memory left data */ +#define AWE_SMRD awe_cmd_idx(1,26), Data2 /* W: right data */ +#define AWE_WC awe_cmd_idx(1,27), Data2 /* W: sample counter */ +#define AWE_WC_Cmd awe_cmd_idx(1,27) +#define AWE_WC_Port Data2 +#define AWE_HWCF1 awe_cmd_idx(1,29), Data1 /* W: config w 1 */ +#define AWE_HWCF2 awe_cmd_idx(1,30), Data1 /* W: config w 2 */ +#define AWE_HWCF3 awe_cmd_idx(1,31), Data1 /* W: config w 3 */ +#define AWE_INIT1(ch) awe_cmd_idx(2,ch), Data1 /* W: init array 1 */ +#define AWE_INIT2(ch) awe_cmd_idx(2,ch), Data2 /* W: init array 2 */ +#define AWE_INIT3(ch) awe_cmd_idx(3,ch), Data1 /* W: init array 3 */ +#define AWE_INIT4(ch) awe_cmd_idx(3,ch), Data2 /* W: init array 4 */ +#define AWE_ENVVOL(ch) awe_cmd_idx(4,ch), Data1 /* W: volume envelope delay */ +#define AWE_DCYSUSV(ch) awe_cmd_idx(5,ch), Data1 /* W: volume envelope sustain and decay */ +#define AWE_ENVVAL(ch) awe_cmd_idx(6,ch), Data1 /* W: modulation envelope delay */ +#define AWE_DCYSUS(ch) awe_cmd_idx(7,ch), Data1 /* W: modulation envelope sustain and decay */ +#define AWE_ATKHLDV(ch) awe_cmd_idx(4,ch), Data2 /* W: volume envelope attack and hold */ +#define AWE_LFO1VAL(ch) awe_cmd_idx(5,ch), Data2 /* W: LFO#1 Delay */ +#define AWE_ATKHLD(ch) awe_cmd_idx(6,ch), Data2 /* W: modulation envelope attack and hold */ +#define AWE_LFO2VAL(ch) awe_cmd_idx(7,ch), Data2 /* W: LFO#2 Delay */ +#define AWE_IP(ch) awe_cmd_idx(0,ch), Data3 /* W: initial pitch */ +#define AWE_IFATN(ch) awe_cmd_idx(1,ch), Data3 /* W: initial filter cutoff and attenuation */ +#define AWE_PEFE(ch) awe_cmd_idx(2,ch), Data3 /* W: pitch and filter envelope heights */ +#define AWE_FMMOD(ch) awe_cmd_idx(3,ch), Data3 /* W: vibrato and filter modulation freq */ +#define AWE_TREMFRQ(ch) awe_cmd_idx(4,ch), Data3 /* W: LFO#1 tremolo amount and freq */ +#define AWE_FM2FRQ2(ch) awe_cmd_idx(5,ch), Data3 /* W: LFO#2 vibrato amount and freq */ + +/* used during detection (returns ROM version?; not documented in ADIP) */ +#define AWE_U1 0xE0, Data3 /* (R)(W) used in initialization */ +#define AWE_U2(ch) 0xC0+(ch), Data3 /* (W)(W) used in init envelope */ + + +#define AWE_MAX_VOICES 32 +#define AWE_NORMAL_VOICES 30 /*30&31 are reserved for DRAM refresh*/ + +#define AWE_MAX_CHANNELS 32 /* max midi channels (must >= voices) */ +#define AWE_MAX_LAYERS AWE_MAX_VOICES /* maximum number of multiple layers */ + +#define AWE_DRAM_OFFSET 0x200000 +#define AWE_MAX_DRAM_SIZE (28 * 1024) /* 28 MB is max onboard memory */ + +#define AWE_DEFAULT_ATTENUATION 32 /* 12dB below */ +#define AWE_DEFAULT_MOD_SENSE 18 + +#endif diff --git a/drivers/sound/lowlevel/awe_version.h b/drivers/sound/lowlevel/awe_version.h new file mode 100644 index 000000000000..c1de31715806 --- /dev/null +++ b/drivers/sound/lowlevel/awe_version.h @@ -0,0 +1,13 @@ +/* AWE32 driver version number */ + +#ifndef AWE_VERSION_H_DEF +#define AWE_VERSION_H_DEF + +#define AWE_VERSION_NUMBER 0x00040203 +#define AWEDRV_VERSION "0.4.2c" +#define AWE_MAJOR_VERSION(id) (((id) >> 16) & 0xff) +#define AWE_MINOR_VERSION(id) (((id) >> 8) & 0xff) +#define AWE_TINY_VERSION(id) ((id) & 0xff) + +#endif + diff --git a/drivers/sound/lowlevel/awe_voice.h b/drivers/sound/lowlevel/awe_voice.h new file mode 100644 index 000000000000..aa131313141b --- /dev/null +++ b/drivers/sound/lowlevel/awe_voice.h @@ -0,0 +1,490 @@ +/* + * sound/awe_voice.h + * + * Voice information definitions for the low level driver for the + * AWE32/Sound Blaster 32 wave table synth. + * version 0.4.2c; Oct. 7, 1997 + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * 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 AWE_VOICE_H +#define AWE_VOICE_H + +#ifndef SAMPLE_TYPE_AWE32 +#define SAMPLE_TYPE_AWE32 0x20 +#endif + +#ifndef _PATCHKEY +#define _PATCHKEY(id) ((id<<8)|0xfd) +#endif + +/*---------------------------------------------------------------- + * patch information record + *----------------------------------------------------------------*/ + +/* patch interface header: 16 bytes */ +typedef struct awe_patch_info { + short key; /* use AWE_PATCH here */ +#define AWE_PATCH _PATCHKEY(0x07) + + short device_no; /* synthesizer number */ + unsigned short sf_id; /* file id (should be zero) */ + short optarg; /* optional argument */ + int len; /* data length (without this header) */ + + short type; /* patch operation type */ +#define AWE_LOAD_INFO 0 /* awe_voice_rec */ +#define AWE_LOAD_DATA 1 /* awe_sample_info */ +#define AWE_OPEN_PATCH 2 /* awe_open_parm */ +#define AWE_CLOSE_PATCH 3 /* none */ +#define AWE_UNLOAD_PATCH 4 /* none */ +#define AWE_REPLACE_DATA 5 /* awe_sample_info (optarg=#channels)*/ +#define AWE_MAP_PRESET 6 /* awe_voice_map */ +#define AWE_LOAD_CHORUS_FX 0x10 /* awe_chorus_fx_rec (optarg=mode) */ +#define AWE_LOAD_REVERB_FX 0x11 /* awe_reverb_fx_rec (optarg=mode) */ + + short reserved; /* word alignment data */ + + /* the actual patch data begins after this */ +#if defined(AWE_COMPAT_030) && AWE_COMPAT_030 + char data[0]; +#endif +} awe_patch_info; + +/*#define AWE_PATCH_INFO_SIZE 16*/ +#define AWE_PATCH_INFO_SIZE sizeof(awe_patch_info) + + +/*---------------------------------------------------------------- + * open patch + *----------------------------------------------------------------*/ + +#define AWE_PATCH_NAME_LEN 32 + +typedef struct _awe_open_parm { + unsigned short type; /* sample type */ +#define AWE_PAT_TYPE_MISC 0 +#define AWE_PAT_TYPE_GM 1 +#define AWE_PAT_TYPE_GS 2 +#define AWE_PAT_TYPE_MT32 3 +#define AWE_PAT_TYPE_XG 4 +#define AWE_PAT_TYPE_SFX 5 +#define AWE_PAT_TYPE_GUS 6 +#define AWE_PAT_TYPE_MAP 7 + +#define AWE_PAT_LOCKED 0x100 /* lock the samples */ + + short reserved; + char name[AWE_PATCH_NAME_LEN]; +} awe_open_parm; + +/*#define AWE_OPEN_PARM_SIZE 28*/ +#define AWE_OPEN_PARM_SIZE sizeof(awe_open_parm) + + +/*---------------------------------------------------------------- + * raw voice information record + *----------------------------------------------------------------*/ + +/* wave table envelope & effect parameters to control EMU8000 */ +typedef struct _awe_voice_parm { + unsigned short moddelay; /* modulation delay (0x8000) */ + unsigned short modatkhld; /* modulation attack & hold time (0x7f7f) */ + unsigned short moddcysus; /* modulation decay & sustain (0x7f7f) */ + unsigned short modrelease; /* modulation release time (0x807f) */ + short modkeyhold, modkeydecay; /* envelope change per key (not used) */ + unsigned short voldelay; /* volume delay (0x8000) */ + unsigned short volatkhld; /* volume attack & hold time (0x7f7f) */ + unsigned short voldcysus; /* volume decay & sustain (0x7f7f) */ + unsigned short volrelease; /* volume release time (0x807f) */ + short volkeyhold, volkeydecay; /* envelope change per key (not used) */ + unsigned short lfo1delay; /* LFO1 delay (0x8000) */ + unsigned short lfo2delay; /* LFO2 delay (0x8000) */ + unsigned short pefe; /* modulation pitch & cutoff (0x0000) */ + unsigned short fmmod; /* LFO1 pitch & cutoff (0x0000) */ + unsigned short tremfrq; /* LFO1 volume & freq (0x0000) */ + unsigned short fm2frq2; /* LFO2 pitch & freq (0x0000) */ + unsigned char cutoff; /* initial cutoff (0xff) */ + unsigned char filterQ; /* initial filter Q [0-15] (0x0) */ + unsigned char chorus; /* chorus send (0x00) */ + unsigned char reverb; /* reverb send (0x00) */ + unsigned short reserved[4]; /* not used */ +} awe_voice_parm; + +#define AWE_VOICE_PARM_SIZE 48 + + +/* wave table parameters: 92 bytes */ +typedef struct _awe_voice_info { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* sample offset correction */ + int loopstart, loopend; /* loop offset correction */ + short rate_offset; /* sample rate pitch offset */ + unsigned short mode; /* sample mode */ +#define AWE_MODE_ROMSOUND 0x8000 +#define AWE_MODE_STEREO 1 +#define AWE_MODE_LOOPING 2 +#define AWE_MODE_NORELEASE 4 /* obsolete */ +#define AWE_MODE_INIT_PARM 8 + + short root; /* midi root key */ + short tune; /* pitch tuning (in cents) */ + char low, high; /* key note range */ + char vellow, velhigh; /* velocity range */ + char fixkey, fixvel; /* fixed key, velocity */ + char pan, fixpan; /* panning, fixed panning */ + short exclusiveClass; /* exclusive class (0 = none) */ + unsigned char amplitude; /* sample volume (127 max) */ + unsigned char attenuation; /* attenuation (0.375dB) */ + short scaleTuning; /* pitch scale tuning(%), normally 100 */ + awe_voice_parm parm; /* voice envelope parameters */ + short index; /* internal index (set by driver) */ +} awe_voice_info; + +/*#define AWE_VOICE_INFO_SIZE 92*/ +#define AWE_VOICE_INFO_SIZE sizeof(awe_voice_info) + +/*----------------------------------------------------------------*/ + +/* The info entry of awe_voice_rec is changed from 0 to 1 + * for some compilers refusing zero size array. + * Due to this change, sizeof(awe_voice_rec) becomes different + * from older versions. + * Use AWE_VOICE_REC_SIZE instead. + */ + +/* instrument info header: 4 bytes */ +typedef struct _awe_voice_rec_hdr { + unsigned char bank; /* midi bank number */ + unsigned char instr; /* midi preset number */ + char nvoices; /* number of voices */ + char write_mode; /* write mode; normally 0 */ +#define AWE_WR_APPEND 0 /* append anyway */ +#define AWE_WR_EXCLUSIVE 1 /* skip if already exists */ +#define AWE_WR_REPLACE 2 /* replace if already exists */ +} awe_voice_rec_hdr; + +/*#define AWE_VOICE_REC_SIZE 4*/ +#define AWE_VOICE_REC_SIZE sizeof(awe_voice_rec_hdr) + +/* the standard patch structure for one sample */ +typedef struct _awe_voice_rec_patch { + awe_patch_info patch; + awe_voice_rec_hdr hdr; + awe_voice_info info; +} awe_voice_rec_patch; + + +/* obsolete data type */ +#if defined(AWE_COMPAT_030) && AWE_COMPAT_030 +#define AWE_INFOARRAY_SIZE 0 +#else +#define AWE_INFOARRAY_SIZE 1 +#endif + +typedef struct _awe_voice_rec { + unsigned char bank; /* midi bank number */ + unsigned char instr; /* midi preset number */ + short nvoices; /* number of voices */ + /* voice information follows here */ + awe_voice_info info[AWE_INFOARRAY_SIZE]; +} awe_voice_rec; + + +/*---------------------------------------------------------------- + * sample wave information + *----------------------------------------------------------------*/ + +/* wave table sample header: 32 bytes */ +typedef struct awe_sample_info { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* start & end offset */ + int loopstart, loopend; /* loop start & end offset */ + int size; /* size (0 = ROM) */ + short checksum_flag; /* use check sum = 1 */ + unsigned short mode_flags; /* mode flags */ +#define AWE_SAMPLE_8BITS 1 /* wave data is 8bits */ +#define AWE_SAMPLE_UNSIGNED 2 /* wave data is unsigned */ +#define AWE_SAMPLE_NO_BLANK 4 /* no blank loop is attached */ +#define AWE_SAMPLE_SINGLESHOT 8 /* single-shot w/o loop */ +#define AWE_SAMPLE_BIDIR_LOOP 16 /* bidirectional looping */ +#define AWE_SAMPLE_STEREO_LEFT 32 /* stereo left sound */ +#define AWE_SAMPLE_STEREO_RIGHT 64 /* stereo right sound */ +#define AWE_SAMPLE_REVERSE_LOOP 128 /* reverse looping */ + unsigned int checksum; /* check sum */ +#if defined(AWE_COMPAT_030) && AWE_COMPAT_030 + unsigned short data[0]; /* sample data follows here */ +#endif +} awe_sample_info; + +/*#define AWE_SAMPLE_INFO_SIZE 32*/ +#define AWE_SAMPLE_INFO_SIZE sizeof(awe_sample_info) + + +/*---------------------------------------------------------------- + * voice preset mapping + *----------------------------------------------------------------*/ + +typedef struct awe_voice_map { + int map_bank, map_instr, map_key; /* key = -1 means all keys */ + int src_bank, src_instr, src_key; +} awe_voice_map; + +#define AWE_VOICE_MAP_SIZE sizeof(awe_voice_map) + + +/*---------------------------------------------------------------- + * awe hardware controls + *----------------------------------------------------------------*/ + +#define _AWE_DEBUG_MODE 0x00 +#define _AWE_REVERB_MODE 0x01 +#define _AWE_CHORUS_MODE 0x02 +#define _AWE_REMOVE_LAST_SAMPLES 0x03 +#define _AWE_INITIALIZE_CHIP 0x04 +#define _AWE_SEND_EFFECT 0x05 +#define _AWE_TERMINATE_CHANNEL 0x06 +#define _AWE_TERMINATE_ALL 0x07 +#define _AWE_INITIAL_VOLUME 0x08 +#define _AWE_INITIAL_ATTEN _AWE_INITIAL_VOLUME +#define _AWE_RESET_CHANNEL 0x09 +#define _AWE_CHANNEL_MODE 0x0a +#define _AWE_DRUM_CHANNELS 0x0b +#define _AWE_MISC_MODE 0x0c +#define _AWE_RELEASE_ALL 0x0d +#define _AWE_NOTEOFF_ALL 0x0e +#define _AWE_CHN_PRESSURE 0x0f +/*#define _AWE_GET_CURRENT_MODE 0x10*/ +#define _AWE_EQUALIZER 0x11 +/*#define _AWE_GET_MISC_MODE 0x12*/ +/*#define _AWE_GET_FONTINFO 0x13*/ + +#define _AWE_MODE_FLAG 0x80 +#define _AWE_COOKED_FLAG 0x40 /* not supported */ +#define _AWE_MODE_VALUE_MASK 0x3F + +/*----------------------------------------------------------------*/ + +#define _AWE_SET_CMD(p,dev,voice,cmd,p1,p2) \ +{((char*)(p))[0] = SEQ_PRIVATE;\ + ((char*)(p))[1] = dev;\ + ((char*)(p))[2] = _AWE_MODE_FLAG|(cmd);\ + ((char*)(p))[3] = voice;\ + ((unsigned short*)(p))[2] = p1;\ + ((unsigned short*)(p))[3] = p2;} + +/* buffered access */ +#define _AWE_CMD(dev, voice, cmd, p1, p2) \ +{_SEQ_NEEDBUF(8);\ + _AWE_SET_CMD(_seqbuf + _seqbufptr, dev, voice, cmd, p1, p2);\ + _SEQ_ADVBUF(8);} + +/* direct access */ +#define _AWE_CMD_NOW(seqfd,dev,voice,cmd,p1,p2) \ +{struct seq_event_rec tmp;\ + _AWE_SET_CMD(&tmp, dev, voice, cmd, p1, p2);\ + ioctl(seqfd, SNDCTL_SEQ_OUTOFBAND, &tmp);} + +/*----------------------------------------------------------------*/ + +/* set debugging mode */ +#define AWE_DEBUG_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0) +/* set reverb mode; from 0 to 7 */ +#define AWE_REVERB_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0) +/* set chorus mode; from 0 to 7 */ +#define AWE_CHORUS_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_CHORUS_MODE, p1, 0) + +/* reset channel */ +#define AWE_RESET_CHANNEL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 0, 0) +#define AWE_RESET_CONTROL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 1, 0) + +/* send an effect to all layers */ +#define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value) +#define AWE_ADD_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x80),value) +#define AWE_UNSET_EFFECT(dev,voice,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x40),0) +/* send an effect to a layer */ +#define AWE_SEND_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)),value) +#define AWE_ADD_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x80),value) +#define AWE_UNSET_LAYER_EFFECT(dev,voice,layer,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x40),0) + +/* terminate sound on the channel/voice */ +#define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0) +/* terminate all sounds */ +#define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0) +/* release all sounds (w/o sustain effect) */ +#define AWE_RELEASE_ALL(dev) _AWE_CMD(dev, 0, _AWE_RELEASE_ALL, 0, 0) +/* note off all sounds (w sustain effect) */ +#define AWE_NOTEOFF_ALL(dev) _AWE_CMD(dev, 0, _AWE_NOTEOFF_ALL, 0, 0) + +/* set initial attenuation */ +#define AWE_INITIAL_VOLUME(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 0) +#define AWE_INITIAL_ATTEN AWE_INITIAL_VOLUME +/* relative attenuation */ +#define AWE_SET_ATTEN(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 1) + +/* set channel playing mode; mode=0/1/2 */ +#define AWE_SET_CHANNEL_MODE(dev,mode) _AWE_CMD(dev, 0, _AWE_CHANNEL_MODE, mode, 0) +#define AWE_PLAY_INDIRECT 0 /* indirect voice mode (default) */ +#define AWE_PLAY_MULTI 1 /* multi note voice mode */ +#define AWE_PLAY_DIRECT 2 /* direct single voice mode */ +#define AWE_PLAY_MULTI2 3 /* sequencer2 mode; used internally */ + +/* set drum channel mask; channels is 32bit long value */ +#define AWE_DRUM_CHANNELS(dev,channels) _AWE_CMD(dev, 0, _AWE_DRUM_CHANNELS, ((channels) & 0xffff), ((channels) >> 16)) + +/* set bass and treble control; values are from 0 to 11 */ +#define AWE_EQUALIZER(dev,bass,treble) _AWE_CMD(dev, 0, _AWE_EQUALIZER, bass, treble) + +/* remove last loaded samples */ +#define AWE_REMOVE_LAST_SAMPLES(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0) +/* initialize emu8000 chip */ +#define AWE_INITIALIZE_CHIP(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_INITIALIZE_CHIP, 0, 0) + +/* set miscellaneous modes; meta command */ +#define AWE_MISC_MODE(dev,mode,value) _AWE_CMD(dev, 0, _AWE_MISC_MODE, mode, value) +/* exclusive sound off; 1=off */ +#define AWE_EXCLUSIVE_SOUND(dev,mode) AWE_MISC_MODE(dev,AWE_MD_EXCLUSIVE_SOUND,mode) +/* default GUS bank number */ +#define AWE_SET_GUS_BANK(dev,bank) AWE_MISC_MODE(dev,AWE_MD_GUS_BANK,bank) +/* change panning position in realtime; 0=don't 1=do */ +#define AWE_REALTIME_PAN(dev,mode) AWE_MISC_MODE(dev,AWE_MD_REALTIME_PAN,mode) + +/* extended pressure controls; not portable with other sound drivers */ +#define AWE_KEY_PRESSURE(dev,ch,note,vel) SEQ_START_NOTE(dev,ch,(note)+128,vel) +#define AWE_CHN_PRESSURE(dev,ch,vel) _AWE_CMD(dev,ch,_AWE_CHN_PRESSURE,vel,0) + +/*----------------------------------------------------------------*/ + +/* reverb mode parameters */ +#define AWE_REVERB_ROOM1 0 +#define AWE_REVERB_ROOM2 1 +#define AWE_REVERB_ROOM3 2 +#define AWE_REVERB_HALL1 3 +#define AWE_REVERB_HALL2 4 +#define AWE_REVERB_PLATE 5 +#define AWE_REVERB_DELAY 6 +#define AWE_REVERB_PANNINGDELAY 7 +#define AWE_REVERB_PREDEFINED 8 +/* user can define reverb modes up to 32 */ +#define AWE_REVERB_NUMBERS 32 + +typedef struct awe_reverb_fx_rec { + unsigned short parms[28]; +} awe_reverb_fx_rec; + +/*----------------------------------------------------------------*/ + +/* chorus mode parameters */ +#define AWE_CHORUS_1 0 +#define AWE_CHORUS_2 1 +#define AWE_CHORUS_3 2 +#define AWE_CHORUS_4 3 +#define AWE_CHORUS_FEEDBACK 4 +#define AWE_CHORUS_FLANGER 5 +#define AWE_CHORUS_SHORTDELAY 6 +#define AWE_CHORUS_SHORTDELAY2 7 +#define AWE_CHORUS_PREDEFINED 8 +/* user can define chorus modes up to 32 */ +#define AWE_CHORUS_NUMBERS 32 + +typedef struct awe_chorus_fx_rec { + unsigned short feedback; /* feedback level (0xE600-0xE6FF) */ + unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */ + unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */ + unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */ + unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */ +} awe_chorus_fx_rec; + +/*----------------------------------------------------------------*/ + +/* misc mode types */ +enum { +/* 0*/ AWE_MD_EXCLUSIVE_OFF, /* obsolete */ +/* 1*/ AWE_MD_EXCLUSIVE_ON, /* obsolete */ +/* 2*/ AWE_MD_VERSION, /* read only */ +/* 3*/ AWE_MD_EXCLUSIVE_SOUND, /* ignored */ +/* 4*/ AWE_MD_REALTIME_PAN, /* 0/1: do realtime pan change (default=1) */ +/* 5*/ AWE_MD_GUS_BANK, /* bank number for GUS patches (default=0) */ +/* 6*/ AWE_MD_KEEP_EFFECT, /* 0/1: keep effect values, (default=0) */ +/* 7*/ AWE_MD_ZERO_ATTEN, /* attenuation of max volume (default=32) */ +/* 8*/ AWE_MD_CHN_PRIOR, /* 0/1: set MIDI channel priority mode (default=1) */ +/* 9*/ AWE_MD_MOD_SENSE, /* integer: modwheel sensitivity (def=18) */ +/*10*/ AWE_MD_DEF_PRESET, /* integer: default preset number (def=0) */ +/*11*/ AWE_MD_DEF_BANK, /* integer: default bank number (def=0) */ +/*12*/ AWE_MD_DEF_DRUM, /* integer: default drumset number (def=0) */ +/*13*/ AWE_MD_TOGGLE_DRUM_BANK, /* 0/1: toggle drum flag with bank# (def=0) */ + AWE_MD_END, +}; + +/*----------------------------------------------------------------*/ + +/* effect parameters */ +enum { + +/* modulation envelope parameters */ +/* 0*/ AWE_FX_ENV1_DELAY, /* WORD: ENVVAL */ +/* 1*/ AWE_FX_ENV1_ATTACK, /* BYTE: up ATKHLD */ +/* 2*/ AWE_FX_ENV1_HOLD, /* BYTE: lw ATKHLD */ +/* 3*/ AWE_FX_ENV1_DECAY, /* BYTE: lw DCYSUS */ +/* 4*/ AWE_FX_ENV1_RELEASE, /* BYTE: lw DCYSUS */ +/* 5*/ AWE_FX_ENV1_SUSTAIN, /* BYTE: up DCYSUS */ +/* 6*/ AWE_FX_ENV1_PITCH, /* BYTE: up PEFE */ +/* 7*/ AWE_FX_ENV1_CUTOFF, /* BYTE: lw PEFE */ + +/* volume envelope parameters */ +/* 8*/ AWE_FX_ENV2_DELAY, /* WORD: ENVVOL */ +/* 9*/ AWE_FX_ENV2_ATTACK, /* BYTE: up ATKHLDV */ +/*10*/ AWE_FX_ENV2_HOLD, /* BYTE: lw ATKHLDV */ +/*11*/ AWE_FX_ENV2_DECAY, /* BYTE: lw DCYSUSV */ +/*12*/ AWE_FX_ENV2_RELEASE, /* BYTE: lw DCYSUSV */ +/*13*/ AWE_FX_ENV2_SUSTAIN, /* BYTE: up DCYSUSV */ + +/* LFO1 (tremolo & vibrato) parameters */ +/*14*/ AWE_FX_LFO1_DELAY, /* WORD: LFO1VAL */ +/*15*/ AWE_FX_LFO1_FREQ, /* BYTE: lo TREMFRQ */ +/*16*/ AWE_FX_LFO1_VOLUME, /* BYTE: up TREMFRQ */ +/*17*/ AWE_FX_LFO1_PITCH, /* BYTE: up FMMOD */ +/*18*/ AWE_FX_LFO1_CUTOFF, /* BYTE: lo FMMOD */ + +/* LFO2 (vibrato) parameters */ +/*19*/ AWE_FX_LFO2_DELAY, /* WORD: LFO2VAL */ +/*20*/ AWE_FX_LFO2_FREQ, /* BYTE: lo FM2FRQ2 */ +/*21*/ AWE_FX_LFO2_PITCH, /* BYTE: up FM2FRQ2 */ + +/* Other overall effect parameters */ +/*22*/ AWE_FX_INIT_PITCH, /* SHORT: pitch offset */ +/*23*/ AWE_FX_CHORUS, /* BYTE: chorus effects send (0-255) */ +/*24*/ AWE_FX_REVERB, /* BYTE: reverb effects send (0-255) */ +/*25*/ AWE_FX_CUTOFF, /* BYTE: up IFATN */ +/*26*/ AWE_FX_FILTERQ, /* BYTE: up CCCA */ + +/* Sample / loop offset changes */ +/*27*/ AWE_FX_SAMPLE_START, /* SHORT: offset */ +/*28*/ AWE_FX_LOOP_START, /* SHORT: offset */ +/*29*/ AWE_FX_LOOP_END, /* SHORT: offset */ +/*30*/ AWE_FX_COARSE_SAMPLE_START, /* SHORT: upper word offset */ +/*31*/ AWE_FX_COARSE_LOOP_START, /* SHORT: upper word offset */ +/*32*/ AWE_FX_COARSE_LOOP_END, /* SHORT: upper word offset */ +/*33*/ AWE_FX_ATTEN, /* BYTE: lo IFATN */ + + AWE_FX_END, +}; + +#endif /* AWE_VOICE_H */ diff --git a/drivers/sound/lowlevel/awe_wave.c b/drivers/sound/lowlevel/awe_wave.c new file mode 100644 index 000000000000..111b0c5bec20 --- /dev/null +++ b/drivers/sound/lowlevel/awe_wave.c @@ -0,0 +1,4581 @@ +/* + * sound/awe_wave.c + * + * The low level driver for the AWE32/Sound Blaster 32 wave table synth. + * version 0.4.2c; Oct. 7, 1997 + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * 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. + */ + +#ifdef __FreeBSD__ +# include +#else +#ifdef MODULE +#include +#include +#include +# include "../soundmodule.h" +#endif +# include "awe_config.h" +#endif + +/*----------------------------------------------------------------*/ + +#if defined(CONFIG_AWE32_SYNTH) || defined(CONFIG_AWE32_SYNTH_MODULE) + +#ifdef __FreeBSD__ +# include +# include +# include +#else +# include "awe_hw.h" +# include "awe_version.h" +# include +#endif + +#ifdef AWE_HAS_GUS_COMPATIBILITY +/* include fine tuning table */ +#ifdef AWE_OBSOLETE_VOXWARE +# ifdef __FreeBSD__ +# define SEQUENCER_C +# include +# else +# include "tuning.h" +# endif +#else +# include "../tuning.h" +#endif + +#ifdef linux +# include +#elif defined(__FreeBSD__) +# include +#endif + +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + + +/*---------------------------------------------------------------- + * debug message + *----------------------------------------------------------------*/ + +static int debug_mode = 0; +#ifdef AWE_DEBUG_ON +#define DEBUG(LVL,XXX) {if (debug_mode > LVL) { XXX; }} +#define ERRMSG(XXX) {if (debug_mode) { XXX; }} +#define FATALERR(XXX) XXX +#else +#define DEBUG(LVL,XXX) /**/ +#define ERRMSG(XXX) XXX +#define FATALERR(XXX) XXX +#endif + +/*---------------------------------------------------------------- + * bank and voice record + *----------------------------------------------------------------*/ + +/* soundfont record */ +typedef struct _sf_list { + unsigned short sf_id; + unsigned short type; + int num_info; /* current info table index */ + int num_sample; /* current sample table index */ + int mem_ptr; /* current word byte pointer */ + int infos; + int samples; + /*char name[AWE_PATCH_NAME_LEN];*/ +} sf_list; + +/* bank record */ +typedef struct _awe_voice_list { + int next; /* linked list with same sf_id */ + unsigned char bank, instr; /* preset number information */ + char type, disabled; /* type=normal/mapped, disabled=boolean */ + awe_voice_info v; /* voice information */ + int next_instr; /* preset table list */ + int next_bank; /* preset table list */ +} awe_voice_list; + +/* voice list type */ +#define V_ST_NORMAL 0 +#define V_ST_MAPPED 1 + +typedef struct _awe_sample_list { + int next; /* linked list with same sf_id */ + awe_sample_info v; /* sample information */ +} awe_sample_list; + +/* sample and information table */ +static int current_sf_id = 0; +static int locked_sf_id = 0; +static int max_sfs; +static sf_list *sflists = NULL; + +#define awe_free_mem_ptr() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].mem_ptr) +#define awe_free_info() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_info) +#define awe_free_sample() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_sample) + +static int max_samples; +static awe_sample_list *samples = NULL; + +static int max_infos; +static awe_voice_list *infos = NULL; + + +#define AWE_MAX_PRESETS 256 +#define AWE_DEFAULT_PRESET 0 +#define AWE_DEFAULT_BANK 0 +#define AWE_DEFAULT_DRUM 0 +#define AWE_DRUM_BANK 128 + +#define MAX_LAYERS AWE_MAX_VOICES + +/* preset table index */ +static int preset_table[AWE_MAX_PRESETS]; + +/*---------------------------------------------------------------- + * voice table + *----------------------------------------------------------------*/ + +/* effects table */ +typedef struct FX_Rec { /* channel effects */ + unsigned char flags[AWE_FX_END]; + short val[AWE_FX_END]; +} FX_Rec; + + +/* channel parameters */ +typedef struct _awe_chan_info { + int channel; /* channel number */ + int bank; /* current tone bank */ + int instr; /* current program */ + int bender; /* midi pitchbend (-8192 - 8192) */ + int bender_range; /* midi bender range (x100) */ + int panning; /* panning (0-127) */ + int main_vol; /* channel volume (0-127) */ + int expression_vol; /* midi expression (0-127) */ + int chan_press; /* channel pressure */ + int vrec; /* instrument list */ + int def_vrec; /* default instrument list */ + int sustained; /* sustain status in MIDI */ + FX_Rec fx; /* effects */ + FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */ +} awe_chan_info; + +/* voice parameters */ +typedef struct _voice_info { + int state; +#define AWE_ST_OFF (1<<0) /* no sound */ +#define AWE_ST_ON (1<<1) /* playing */ +#define AWE_ST_STANDBY (1<<2) /* stand by for playing */ +#define AWE_ST_SUSTAINED (1<<3) /* sustained */ +#define AWE_ST_MARK (1<<4) /* marked for allocation */ +#define AWE_ST_DRAM (1<<5) /* DRAM read/write */ +#define AWE_ST_FM (1<<6) /* reserved for FM */ +#define AWE_ST_RELEASED (1<<7) /* released */ + + int ch; /* midi channel */ + int key; /* internal key for search */ + int layer; /* layer number (for channel mode only) */ + int time; /* allocated time */ + awe_chan_info *cinfo; /* channel info */ + + int note; /* midi key (0-127) */ + int velocity; /* midi velocity (0-127) */ + int sostenuto; /* sostenuto on/off */ + awe_voice_info *sample; /* assigned voice */ + + /* EMU8000 parameters */ + int apitch; /* pitch parameter */ + int avol; /* volume parameter */ + int apan; /* panning parameter */ +} voice_info; + +/* voice information */ +static voice_info *voices; + +#define IS_NO_SOUND(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED)) +#define IS_NO_EFFECT(v) (voices[v].state != AWE_ST_ON) +#define IS_PLAYING(v) (voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED)) +#define IS_EMPTY(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_MARK|AWE_ST_DRAM|AWE_ST_FM)) + + +/* MIDI channel effects information (for hw control) */ +static awe_chan_info *channels; + + +/*---------------------------------------------------------------- + * global variables + *----------------------------------------------------------------*/ + +#ifndef AWE_DEFAULT_BASE_ADDR +#define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */ +#endif + +#ifndef AWE_DEFAULT_MEM_SIZE +#define AWE_DEFAULT_MEM_SIZE 0 /* autodetect */ +#endif + +/* awe32 base address (overwritten at initialization) */ +static int awe_base = AWE_DEFAULT_BASE_ADDR; +/* memory byte size */ +static int awe_mem_size = AWE_DEFAULT_MEM_SIZE; +/* DRAM start offset */ +static int awe_mem_start = AWE_DRAM_OFFSET; + +/* maximum channels for playing */ +static int awe_max_voices = AWE_MAX_VOICES; + +static int patch_opened = 0; /* sample already loaded? */ + +static int reverb_mode = 4; /* reverb mode */ +static int chorus_mode = 2; /* chorus mode */ +static short init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */ + +static int awe_present = FALSE; /* awe device present? */ +static int awe_busy = FALSE; /* awe device opened? */ + +static int my_dev = -1; +static int my_mixerdev = -1 ; + +#define DEFAULT_DRUM_FLAGS ((1 << 9) | (1 << 25)) +#define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c))) +#define DRUM_CHANNEL_ON(c) (drum_flags |= (1 << (c))) +#define DRUM_CHANNEL_OFF(c) (drum_flags &= ~(1 << (c))) +static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */ + +static int playing_mode = AWE_PLAY_INDIRECT; +#define SINGLE_LAYER_MODE() (playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT) +#define MULTI_LAYER_MODE() (playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2) + +static int current_alloc_time = 0; /* voice allocation index for channel mode */ + +static struct MiscModeDef { + int value; + int init_each_time; +} misc_modes_default[AWE_MD_END] = { + {0,0}, {0,0}, /* <-- not used */ + {AWE_VERSION_NUMBER, FALSE}, + {TRUE, TRUE}, /* exclusive */ + {TRUE, TRUE}, /* realpan */ + {AWE_DEFAULT_BANK, TRUE}, /* gusbank */ + {FALSE, TRUE}, /* keep effect */ + {AWE_DEFAULT_ATTENUATION, FALSE}, /* zero_atten */ + {FALSE, TRUE}, /* chn_prior */ + {AWE_DEFAULT_MOD_SENSE, TRUE}, /* modwheel sense */ + {AWE_DEFAULT_PRESET, TRUE}, /* def_preset */ + {AWE_DEFAULT_BANK, TRUE}, /* def_bank */ + {AWE_DEFAULT_DRUM, TRUE}, /* def_drum */ + {FALSE, TRUE}, /* toggle_drum_bank */ +}; + +static int misc_modes[AWE_MD_END]; + +static int awe_bass_level = 5; +static int awe_treble_level = 9; + + +static struct synth_info awe_info = { + "AWE32 Synth", /* name */ + 0, /* device */ + SYNTH_TYPE_SAMPLE, /* synth_type */ + SAMPLE_TYPE_AWE32, /* synth_subtype */ + 0, /* perc_mode (obsolete) */ + AWE_MAX_VOICES, /* nr_voices */ + 0, /* nr_drums (obsolete) */ + AWE_MAX_INFOS /* instr_bank_size */ +}; + + +static struct voice_alloc_info *voice_alloc; /* set at initialization */ + + +/*---------------------------------------------------------------- + * function prototypes + *----------------------------------------------------------------*/ + +#ifndef AWE_OBSOLETE_VOXWARE +static int awe_check_port(void); +static void awe_request_region(void); +static void awe_release_region(void); +#endif + +static void awe_reset_samples(void); +/* emu8000 chip i/o access */ +static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data); +static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data); +static unsigned short awe_peek(unsigned short cmd, unsigned short port); +static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port); +static void awe_wait(unsigned short delay); + +/* initialize emu8000 chip */ +static void awe_initialize(void); + +/* set voice parameters */ +static void awe_init_misc_modes(int init_all); +static void awe_init_voice_info(awe_voice_info *vp); +static void awe_init_voice_parm(awe_voice_parm *pp); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static int freq_to_note(int freq); +static int calc_rate_offset(int Hz); +/*static int calc_parm_delay(int msec);*/ +static int calc_parm_hold(int msec); +static int calc_parm_attack(int msec); +static int calc_parm_decay(int msec); +static int calc_parm_search(int msec, short *table); +#endif + +/* turn on/off note */ +static void awe_note_on(int voice); +static void awe_note_off(int voice); +static void awe_terminate(int voice); +static void awe_exclusive_off(int voice); +static void awe_note_off_all(int do_sustain); + +/* calculate voice parameters */ +typedef void (*fx_affect_func)(int voice, int forced); +static void awe_set_pitch(int voice, int forced); +static void awe_set_voice_pitch(int voice, int forced); +static void awe_set_volume(int voice, int forced); +static void awe_set_voice_vol(int voice, int forced); +static void awe_set_pan(int voice, int forced); +static void awe_fx_fmmod(int voice, int forced); +static void awe_fx_tremfrq(int voice, int forced); +static void awe_fx_fm2frq2(int voice, int forced); +static void awe_fx_filterQ(int voice, int forced); +static void awe_calc_pitch(int voice); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static void awe_calc_pitch_from_freq(int voice, int freq); +#endif +static void awe_calc_volume(int voice); +static void awe_voice_init(int voice, int init_all); +static void awe_channel_init(int ch, int init_all); +static void awe_fx_init(int ch); + +/* sequencer interface */ +static int awe_open(int dev, int mode); +static void awe_close(int dev); +static int awe_ioctl(int dev, unsigned int cmd, caddr_t arg); +static int awe_kill_note(int dev, int voice, int note, int velocity); +static int awe_start_note(int dev, int v, int note_num, int volume); +static int awe_set_instr(int dev, int voice, int instr_no); +static int awe_set_instr_2(int dev, int voice, int instr_no); +static void awe_reset(int dev); +static void awe_hw_control(int dev, unsigned char *event); +static int awe_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag); +static void awe_aftertouch(int dev, int voice, int pressure); +static void awe_controller(int dev, int voice, int ctrl_num, int value); +static void awe_panning(int dev, int voice, int value); +static void awe_volume_method(int dev, int mode); +#ifndef AWE_NO_PATCHMGR +static int awe_patchmgr(int dev, struct patmgr_info *rec); +#endif +static void awe_bender(int dev, int voice, int value); +static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc); +static void awe_setup_voice(int dev, int voice, int chn); + +/* hardware controls */ +#ifdef AWE_HAS_GUS_COMPATIBILITY +static void awe_hw_gus_control(int dev, int cmd, unsigned char *event); +#endif +static void awe_hw_awe_control(int dev, int cmd, unsigned char *event); +static void awe_voice_change(int voice, fx_affect_func func); +static void awe_sostenuto_on(int voice, int forced); +static void awe_sustain_off(int voice, int forced); +static void awe_terminate_and_init(int voice, int forced); + +/* voice search */ +static int awe_search_instr(int bank, int preset); +static int awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist); +static void awe_alloc_multi_voices(int ch, int note, int velocity, int key); +static void awe_alloc_one_voice(int voice, int note, int velocity); +static int awe_clear_voice(void); + +/* load / remove patches */ +static int awe_open_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_close_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_load_info(awe_patch_info *patch, const char *addr, int count); +static int awe_load_data(awe_patch_info *patch, const char *addr, int count); +static int awe_replace_data(awe_patch_info *patch, const char *addr, int count); +static int awe_load_map(awe_patch_info *patch, const char *addr, int count); +#ifdef AWE_HAS_GUS_COMPATIBILITY +static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag); +#endif +static int check_patch_opened(int type, char *name); +static int awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels); +static void add_sf_info(int rec); +static void add_sf_sample(int rec); +static void purge_old_list(int rec, int next); +static void add_info_list(int rec); +static void awe_remove_samples(int sf_id); +static void rebuild_preset_list(void); +static short awe_set_sample(awe_voice_info *vp); + +/* lowlevel functions */ +static void awe_init_audio(void); +static void awe_init_dma(void); +static void awe_init_array(void); +static void awe_send_array(unsigned short *data); +static void awe_tweak_voice(int voice); +static void awe_tweak(void); +static void awe_init_fm(void); +static int awe_open_dram_for_write(int offset, int channels); +static void awe_open_dram_for_check(void); +static void awe_close_dram(void); +static void awe_write_dram(unsigned short c); +static int awe_detect_base(int addr); +static int awe_detect(void); +static int awe_check_dram(void); +static int awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count); +static void awe_set_chorus_mode(int mode); +static int awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count); +static void awe_set_reverb_mode(int mode); +static void awe_equalizer(int bass, int treble); +#ifdef CONFIG_AWE32_MIXER +static int awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg); +#endif + +/* define macros for compatibility */ +#ifdef __FreeBSD__ +# include +#else +# include "awe_compat.h" +#endif + +/*---------------------------------------------------------------- + * synth operation table + *----------------------------------------------------------------*/ + +static struct synth_operations awe_operations = +{ +#ifdef AWE_OSS38 + "EMU8K", +#endif + &awe_info, + 0, + SYNTH_TYPE_SAMPLE, + SAMPLE_TYPE_AWE32, + awe_open, + awe_close, + awe_ioctl, + awe_kill_note, + awe_start_note, + awe_set_instr_2, + awe_reset, + awe_hw_control, + awe_load_patch, + awe_aftertouch, + awe_controller, + awe_panning, + awe_volume_method, +#ifndef AWE_NO_PATCHMGR + awe_patchmgr, +#endif + awe_bender, + awe_alloc, + awe_setup_voice +}; + +#ifdef CONFIG_AWE32_MIXER +static struct mixer_operations awe_mixer_operations = { +#ifndef __FreeBSD__ + "AWE32", +#endif + "AWE32 Equalizer", + awe_mixer_ioctl, +}; +#endif + + +/*================================================================ + * attach / unload interface + *================================================================*/ + +#ifdef AWE_OBSOLETE_VOXWARE +#define ATTACH_DECL static +#else +#define ATTACH_DECL /**/ +#endif + +ATTACH_DECL +int attach_awe(void) +{ + /* check presence of AWE32 card */ + if (! awe_detect()) { + printk("AWE32: not detected\n"); + return 0; + } + + /* check AWE32 ports are available */ + if (awe_check_port()) { + printk("AWE32: I/O area already used.\n"); + return 0; + } + + /* set buffers to NULL */ + voices = NULL; + channels = NULL; + sflists = NULL; + samples = NULL; + infos = NULL; + + /* voice & channel info */ + voices = (voice_info*)my_malloc(AWE_MAX_VOICES * sizeof(voice_info)); + channels = (awe_chan_info*)my_malloc(AWE_MAX_CHANNELS * sizeof(awe_chan_info)); + + if (voices == NULL || channels == NULL) { + my_free(voices); + my_free(channels); + printk("AWE32: can't allocate sample tables\n"); + return 0; + } + + /* allocate sample tables */ + INIT_TABLE(sflists, max_sfs, AWE_MAX_SF_LISTS, sf_list); + INIT_TABLE(samples, max_samples, AWE_MAX_SAMPLES, awe_sample_list); + INIT_TABLE(infos, max_infos, AWE_MAX_INFOS, awe_voice_list); + + my_dev = sound_alloc_synthdev(); + if (my_dev == -1) { + my_free(voices); + my_free(channels); + printk(KERN_WARNING "AWE32 Error: too many synthesizers\n"); + return 0; + } + + voice_alloc = &awe_operations.alloc; + voice_alloc->max_voice = awe_max_voices; + synth_devs[my_dev] = &awe_operations; + +#ifdef CONFIG_AWE32_MIXER + if ((my_mixerdev=sound_alloc_mixerdev())!=-1) { + mixer_devs[my_mixerdev] = &awe_mixer_operations; + } +#endif + + /* reserve I/O ports for awedrv */ + awe_request_region(); + + /* clear all samples */ + awe_reset_samples(); + + /* intialize AWE32 hardware */ + awe_initialize(); + + sprintf(awe_info.name, "AWE32-%s (RAM%dk)", + AWEDRV_VERSION, awe_mem_size/1024); +#ifdef __FreeBSD__ + printk("awe0: ", awe_mem_size/1024); +#elif defined(AWE_DEBUG_ON) + printk("%s\n", awe_info.name); +#endif + + /* set default values */ + awe_init_misc_modes(TRUE); + + /* set reverb & chorus modes */ + awe_set_reverb_mode(reverb_mode); + awe_set_chorus_mode(chorus_mode); + + awe_present = TRUE; + + return 1; +} + + +#ifdef AWE_DYNAMIC_BUFFER +static void free_tables(void) +{ + my_free(sflists); + sflists = NULL; max_sfs = 0; + my_free(samples); + samples = NULL; max_samples = 0; + my_free(infos); + infos = NULL; max_infos = 0; +} +#else +#define free_buffers() /**/ +#endif + + +ATTACH_DECL +void unload_awe(void) +{ + if (awe_present) { + awe_reset_samples(); + awe_release_region(); + my_free(voices); + my_free(channels); + free_tables(); + sound_unload_mixerdev(my_mixerdev); + sound_unload_synthdev(my_dev); + awe_present = FALSE; + } +} + + +/*---------------------------------------------------------------- + * old type interface + *----------------------------------------------------------------*/ + +#ifdef AWE_OBSOLETE_VOXWARE + +#ifdef __FreeBSD__ +long attach_awe_obsolete(long mem_start, struct address_info *hw_config) +#else +int attach_awe_obsolete(int mem_start, struct address_info *hw_config) +#endif +{ + my_malloc_init(mem_start); + if (! attach_awe()) + return 0; + return my_malloc_memptr(); +} + +int probe_awe_obsolete(struct address_info *hw_config) +{ + return 1; + /*return awe_detect();*/ +} + +#endif + + +/*================================================================ + * clear sample tables + *================================================================*/ + +static void +awe_reset_samples(void) +{ + int i; + + /* free all bank tables */ + for (i = 0; i < AWE_MAX_PRESETS; i++) + preset_table[i] = -1; + + free_tables(); + + current_sf_id = 0; + locked_sf_id = 0; + patch_opened = 0; +} + + +/*================================================================ + * EMU register access + *================================================================*/ + +/* select a given AWE32 pointer */ +static int awe_cur_cmd = -1; +#define awe_set_cmd(cmd) \ +if (awe_cur_cmd != cmd) { OUTW(cmd, awe_base + 0x802); awe_cur_cmd = cmd; } +#define awe_port(port) (awe_base - 0x620 + port) + +/* write 16bit data */ +INLINE static void +awe_poke(unsigned short cmd, unsigned short port, unsigned short data) +{ + awe_set_cmd(cmd); + OUTW(data, awe_port(port)); +} + +/* write 32bit data */ +INLINE static void +awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data) +{ + awe_set_cmd(cmd); + OUTW(data, awe_port(port)); /* write lower 16 bits */ + OUTW(data >> 16, awe_port(port)+2); /* write higher 16 bits */ +} + +/* read 16bit data */ +INLINE static unsigned short +awe_peek(unsigned short cmd, unsigned short port) +{ + unsigned short k; + awe_set_cmd(cmd); + k = inw(awe_port(port)); + return k; +} + +/* read 32bit data */ +INLINE static unsigned int +awe_peek_dw(unsigned short cmd, unsigned short port) +{ + unsigned int k1, k2; + awe_set_cmd(cmd); + k1 = inw(awe_port(port)); + k2 = inw(awe_port(port)+2); + k1 |= k2 << 16; + return k1; +} + +/* wait delay number of AWE32 44100Hz clocks */ +static void +awe_wait(unsigned short delay) +{ + unsigned short clock, target; + unsigned short port = awe_port(AWE_WC_Port); + int counter; + + /* sample counter */ + awe_set_cmd(AWE_WC_Cmd); + clock = (unsigned short)inw(port); + target = clock + delay; + counter = 0; + if (target < clock) { + for (; (unsigned short)inw(port) > target; counter++) + if (counter > 65536) + break; + } + for (; (unsigned short)inw(port) < target; counter++) + if (counter > 65536) + break; +} + +/* write a word data */ +INLINE static void +awe_write_dram(unsigned short c) +{ + awe_poke(AWE_SMLD, c); +} + + +#ifndef AWE_OBSOLETE_VOXWARE + +/*================================================================ + * port check / request + * 0x620-622, 0xA20-A22, 0xE20-E22 + *================================================================*/ + +static int +awe_check_port(void) +{ + return (check_region(awe_port(Data0), 4) || + check_region(awe_port(Data1), 4) || + check_region(awe_port(Data3), 4)); +} + +static void +awe_request_region(void) +{ + request_region(awe_port(Data0), 4, "sound driver (AWE32)"); + request_region(awe_port(Data1), 4, "sound driver (AWE32)"); + request_region(awe_port(Data3), 4, "sound driver (AWE32)"); +} + +static void +awe_release_region(void) +{ + release_region(awe_port(Data0), 4); + release_region(awe_port(Data1), 4); + release_region(awe_port(Data3), 4); +} + +#endif /* !AWE_OBSOLETE_VOXWARE */ + + +/*================================================================ + * AWE32 initialization + *================================================================*/ +static void +awe_initialize(void) +{ + DEBUG(0,printk("AWE32: initializing..\n")); + + /* initialize hardware configuration */ + awe_poke(AWE_HWCF1, 0x0059); + awe_poke(AWE_HWCF2, 0x0020); + + /* disable audio; this seems to reduce a clicking noise a bit.. */ + awe_poke(AWE_HWCF3, 0); + + /* initialize audio channels */ + awe_init_audio(); + + /* initialize DMA */ + awe_init_dma(); + + /* initialize init array */ + awe_init_array(); + + /* check DRAM memory size */ + awe_mem_size = awe_check_dram(); + + /* initialize the FM section of the AWE32 */ + awe_init_fm(); + + /* set up voice envelopes */ + awe_tweak(); + + /* enable audio */ + awe_poke(AWE_HWCF3, 0x0004); + + /* set equalizer */ + awe_equalizer(5, 9); +} + + +/*================================================================ + * AWE32 voice parameters + *================================================================*/ + +/* initialize voice_info record */ +static void +awe_init_voice_info(awe_voice_info *vp) +{ + vp->sf_id = 0; /* normal mode */ + vp->sample = 0; + vp->rate_offset = 0; + + vp->start = 0; + vp->end = 0; + vp->loopstart = 0; + vp->loopend = 0; + vp->mode = 0; + vp->root = 60; + vp->tune = 0; + vp->low = 0; + vp->high = 127; + vp->vellow = 0; + vp->velhigh = 127; + + vp->fixkey = -1; + vp->fixvel = -1; + vp->fixpan = -1; + vp->pan = -1; + + vp->exclusiveClass = 0; + vp->amplitude = 127; + vp->attenuation = 0; + vp->scaleTuning = 100; + + awe_init_voice_parm(&vp->parm); +} + +/* initialize voice_parm record: + * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. + * Vibrato and Tremolo effects are zero. + * Cutoff is maximum. + * Chorus and Reverb effects are zero. + */ +static void +awe_init_voice_parm(awe_voice_parm *pp) +{ + pp->moddelay = 0x8000; + pp->modatkhld = 0x7f7f; + pp->moddcysus = 0x7f7f; + pp->modrelease = 0x807f; + pp->modkeyhold = 0; + pp->modkeydecay = 0; + + pp->voldelay = 0x8000; + pp->volatkhld = 0x7f7f; + pp->voldcysus = 0x7f7f; + pp->volrelease = 0x807f; + pp->volkeyhold = 0; + pp->volkeydecay = 0; + + pp->lfo1delay = 0x8000; + pp->lfo2delay = 0x8000; + pp->pefe = 0; + + pp->fmmod = 0; + pp->tremfrq = 0; + pp->fm2frq2 = 0; + + pp->cutoff = 0xff; + pp->filterQ = 0; + + pp->chorus = 0; + pp->reverb = 0; +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* convert frequency mHz to abstract cents (= midi key * 100) */ +static int +freq_to_note(int mHz) +{ + /* abscents = log(mHz/8176) / log(2) * 1200 */ + unsigned int max_val = (unsigned int)0xffffffff / 10000; + int i, times; + unsigned int base; + unsigned int freq; + int note, tune; + + if (mHz == 0) + return 0; + if (mHz < 0) + return 12799; /* maximum */ + + freq = mHz; + note = 0; + for (base = 8176 * 2; freq >= base; base *= 2) { + note += 12; + if (note >= 128) /* over maximum */ + return 12799; + } + base /= 2; + + /* to avoid overflow... */ + times = 10000; + while (freq > max_val) { + max_val *= 10; + times /= 10; + base /= 10; + } + + freq = freq * times / base; + for (i = 0; i < 12; i++) { + if (freq < semitone_tuning[i+1]) + break; + note++; + } + + tune = 0; + freq = freq * 10000 / semitone_tuning[i]; + for (i = 0; i < 100; i++) { + if (freq < cent_tuning[i+1]) + break; + tune++; + } + + return note * 100 + tune; +} + + +/* convert Hz to AWE32 rate offset: + * sample pitch offset for the specified sample rate + * rate=44100 is no offset, each 4096 is 1 octave (twice). + * eg, when rate is 22050, this offset becomes -4096. + */ +static int +calc_rate_offset(int Hz) +{ + /* offset = log(Hz / 44100) / log(2) * 4096 */ + int freq, base, i; + + /* maybe smaller than max (44100Hz) */ + if (Hz <= 0 || Hz >= 44100) return 0; + + base = 0; + for (freq = Hz * 2; freq < 44100; freq *= 2) + base++; + base *= 1200; + + freq = 44100 * 10000 / (freq/2); + for (i = 0; i < 12; i++) { + if (freq < semitone_tuning[i+1]) + break; + base += 100; + } + freq = freq * 10000 / semitone_tuning[i]; + for (i = 0; i < 100; i++) { + if (freq < cent_tuning[i+1]) + break; + base++; + } + return -base * 4096 / 1200; +} + + +/*---------------------------------------------------------------- + * convert envelope time parameter to AWE32 raw parameter + *----------------------------------------------------------------*/ + +/* attack & decay/release time table (msec) */ +static short attack_time_tbl[128] = { +32767, 11878, 5939, 3959, 2969, 2375, 1979, 1696, 1484, 1319, 1187, 1079, 989, 913, 848, 791, 742, + 698, 659, 625, 593, 565, 539, 516, 494, 475, 456, 439, 424, 409, 395, 383, 371, + 359, 344, 330, 316, 302, 290, 277, 266, 255, 244, 233, 224, 214, 205, 196, 188, + 180, 173, 165, 158, 152, 145, 139, 133, 127, 122, 117, 112, 107, 103, 98, 94, + 90, 86, 83, 79, 76, 73, 69, 67, 64, 61, 58, 56, 54, 51, 49, 47, + 45, 43, 41, 39, 38, 36, 35, 33, 32, 30, 29, 28, 27, 25, 24, 23, + 22, 21, 20, 20, 19, 18, 17, 16, 16, 15, 14, 14, 13, 13, 12, 11, + 11, 10, 10, 10, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 0, +}; + +static short decay_time_tbl[128] = { +32767, 32766, 4589, 4400, 4219, 4045, 3879, 3719, 3566, 3419, 3279, 3144, 3014, 2890, 2771, 2657, + 2548, 2443, 2343, 2246, 2154, 2065, 1980, 1899, 1820, 1746, 1674, 1605, 1539, 1475, 1415, 1356, + 1301, 1247, 1196, 1146, 1099, 1054, 1011, 969, 929, 891, 854, 819, 785, 753, 722, 692, + 664, 636, 610, 585, 561, 538, 516, 494, 474, 455, 436, 418, 401, 384, 368, 353, + 339, 325, 311, 298, 286, 274, 263, 252, 242, 232, 222, 213, 204, 196, 188, 180, + 173, 166, 159, 152, 146, 140, 134, 129, 123, 118, 113, 109, 104, 100, 96, 92, + 88, 84, 81, 77, 74, 71, 68, 65, 63, 60, 58, 55, 53, 51, 49, 47, + 45, 43, 41, 39, 38, 36, 35, 33, 32, 30, 29, 28, 27, 26, 25, 24, +}; + +/* +static int +calc_parm_delay(int msec) +{ + return (0x8000 - msec * 1000 / 725); +} +*/ + +/* delay time = 0x8000 - msec/92 */ +static int +calc_parm_hold(int msec) +{ + int val = (0x7f * 92 - msec) / 92; + if (val < 1) val = 1; + if (val > 127) val = 127; + return val; +} + +/* attack time: search from time table */ +static int +calc_parm_attack(int msec) +{ + return calc_parm_search(msec, attack_time_tbl); +} + +/* decay/release time: search from time table */ +static int +calc_parm_decay(int msec) +{ + return calc_parm_search(msec, decay_time_tbl); +} + +/* search an index for specified time from given time table */ +static int +calc_parm_search(int msec, short *table) +{ + int left = 1, right = 127, mid; + while (left < right) { + mid = (left + right) / 2; + if (msec < (int)table[mid]) + left = mid + 1; + else + right = mid; + } + return left; +} +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + + +/*================================================================ + * effects table + *================================================================*/ + +/* set an effect value */ +#define FX_FLAG_OFF 0 +#define FX_FLAG_SET 1 +#define FX_FLAG_ADD 2 + +#define FX_SET(rec,type,value) \ + ((rec)->flags[type] = FX_FLAG_SET, (rec)->val[type] = (value)) +#define FX_ADD(rec,type,value) \ + ((rec)->flags[type] = FX_FLAG_ADD, (rec)->val[type] = (value)) +#define FX_UNSET(rec,type) \ + ((rec)->flags[type] = FX_FLAG_OFF, (rec)->val[type] = 0) + +/* check the effect value is set */ +#define FX_ON(rec,type) ((rec)->flags[type]) + +#define PARM_BYTE 0 +#define PARM_WORD 1 + +static struct PARM_DEFS { + int type; /* byte or word */ + int low, high; /* value range */ + fx_affect_func realtime; /* realtime paramater change */ +} parm_defs[] = { + {PARM_WORD, 0, 0x8000, NULL}, /* env1 delay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 attack */ + {PARM_BYTE, 0, 0x7e, NULL}, /* env1 hold */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 decay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env1 release */ + {PARM_BYTE, 0, 0x7f, NULL}, /* env1 sustain */ + {PARM_BYTE, 0, 0xff, NULL}, /* env1 pitch */ + {PARM_BYTE, 0, 0xff, NULL}, /* env1 cutoff */ + + {PARM_WORD, 0, 0x8000, NULL}, /* env2 delay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 attack */ + {PARM_BYTE, 0, 0x7e, NULL}, /* env2 hold */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 decay */ + {PARM_BYTE, 1, 0x7f, NULL}, /* env2 release */ + {PARM_BYTE, 0, 0x7f, NULL}, /* env2 sustain */ + + {PARM_WORD, 0, 0x8000, NULL}, /* lfo1 delay */ + {PARM_BYTE, 0, 0xff, awe_fx_tremfrq}, /* lfo1 freq */ + {PARM_BYTE, 0, 0x7f, awe_fx_tremfrq}, /* lfo1 volume (positive only)*/ + {PARM_BYTE, 0, 0x7f, awe_fx_fmmod}, /* lfo1 pitch (positive only)*/ + {PARM_BYTE, 0, 0xff, awe_fx_fmmod}, /* lfo1 cutoff (positive only)*/ + + {PARM_WORD, 0, 0x8000, NULL}, /* lfo2 delay */ + {PARM_BYTE, 0, 0xff, awe_fx_fm2frq2}, /* lfo2 freq */ + {PARM_BYTE, 0, 0x7f, awe_fx_fm2frq2}, /* lfo2 pitch (positive only)*/ + + {PARM_WORD, 0, 0xffff, awe_set_voice_pitch}, /* initial pitch */ + {PARM_BYTE, 0, 0xff, NULL}, /* chorus */ + {PARM_BYTE, 0, 0xff, NULL}, /* reverb */ + {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial cutoff */ + {PARM_BYTE, 0, 15, awe_fx_filterQ}, /* initial resonance */ + + {PARM_WORD, 0, 0xffff, NULL}, /* sample start */ + {PARM_WORD, 0, 0xffff, NULL}, /* loop start */ + {PARM_WORD, 0, 0xffff, NULL}, /* loop end */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse sample start */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop start */ + {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop end */ + {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial attenuation */ +}; + + +static unsigned char +FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value) +{ + int effect = 0; + int on = 0; + if (lay && (on = FX_ON(lay, type)) != 0) + effect = lay->val[type]; + if (!on && (on = FX_ON(rec, type)) != 0) + effect = rec->val[type]; + if (on == FX_FLAG_ADD) + effect += (int)value; + if (on) { + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + return (unsigned char)effect; + } + return value; +} + +/* get word effect value */ +static unsigned short +FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value) +{ + int effect = 0; + int on = 0; + if (lay && (on = FX_ON(lay, type)) != 0) + effect = lay->val[type]; + if (!on && (on = FX_ON(rec, type)) != 0) + effect = rec->val[type]; + if (on == FX_FLAG_ADD) + effect += (int)value; + if (on) { + if (effect < parm_defs[type].low) + effect = parm_defs[type].low; + else if (effect > parm_defs[type].high) + effect = parm_defs[type].high; + return (unsigned short)effect; + } + return value; +} + +/* get word (upper=type1/lower=type2) effect value */ +static unsigned short +FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value) +{ + unsigned short tmp; + tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8)); + tmp <<= 8; + tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff)); + return tmp; +} + +/* address offset */ +static int +FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode) +{ + int addr = 0; + if (lay && FX_ON(lay, hi)) + addr = (short)lay->val[hi]; + else if (FX_ON(rec, hi)) + addr = (short)rec->val[hi]; + addr = addr << 15; + if (lay && FX_ON(lay, lo)) + addr += (short)lay->val[lo]; + else if (FX_ON(rec, lo)) + addr += (short)rec->val[lo]; + if (!(mode & AWE_SAMPLE_8BITS)) + addr /= 2; + return addr; +} + + +/*================================================================ + * turn on/off sample + *================================================================*/ + +static void +awe_note_on(int voice) +{ + unsigned int temp; + int addr; + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + /* A voice sample must assigned before calling */ + if ((vp = voices[voice].sample) == NULL || vp->index < 0) + return; + + /* channel to be silent and idle */ + awe_poke(AWE_DCYSUSV(voice), 0x0080); + awe_poke(AWE_VTFT(voice), 0); + awe_poke(AWE_CVCF(voice), 0); + awe_poke(AWE_PTRX(voice), 0); + awe_poke(AWE_CPF(voice), 0); + + /* modulation & volume envelope */ + awe_poke(AWE_ENVVAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, vp->parm.moddelay)); + awe_poke(AWE_ATKHLD(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK, + vp->parm.modatkhld)); + awe_poke(AWE_DCYSUS(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY, + vp->parm.moddcysus)); + awe_poke(AWE_ENVVOL(voice), + FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay)); + awe_poke(AWE_ATKHLDV(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK, + vp->parm.volatkhld)); + /* decay/sustain parameter for volume envelope must be set at last */ + + /* pitch offset */ + awe_set_pitch(voice, TRUE); + + /* cutoff and volume */ + awe_set_volume(voice, TRUE); + + /* modulation envelope heights */ + awe_poke(AWE_PEFE(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF, + vp->parm.pefe)); + + /* lfo1/2 delay */ + awe_poke(AWE_LFO1VAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay)); + awe_poke(AWE_LFO2VAL(voice), + FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay)); + + /* lfo1 pitch & cutoff shift */ + awe_fx_fmmod(voice, TRUE); + /* lfo1 volume & freq */ + awe_fx_tremfrq(voice, TRUE); + /* lfo2 pitch & freq */ + awe_fx_fm2frq2(voice, TRUE); + /* pan & loop start */ + awe_set_pan(voice, TRUE); + + /* chorus & loop end (chorus 8bit, MSB) */ + addr = vp->loopend - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END, + AWE_FX_COARSE_LOOP_END, vp->mode); + temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus); + temp = (temp <<24) | (unsigned int)addr; + awe_poke_dw(AWE_CSL(voice), temp); + DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr)); + + /* Q & current address (Q 4bit value, MSB) */ + addr = vp->start - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START, + AWE_FX_COARSE_SAMPLE_START, vp->mode); + temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ); + temp = (temp<<28) | (unsigned int)addr; + awe_poke_dw(AWE_CCCA(voice), temp); + DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr)); + + /* reset volume */ + awe_poke_dw(AWE_VTFT(voice), 0x0000FFFF); + awe_poke_dw(AWE_CVCF(voice), 0x0000FFFF); + + /* turn on envelope */ + awe_poke(AWE_DCYSUSV(voice), + FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY, + vp->parm.voldcysus)); + /* set reverb */ + temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb); + temp = (awe_peek_dw(AWE_PTRX(voice)) & 0xffff0000) | (temp<<8); + awe_poke_dw(AWE_PTRX(voice), temp); + awe_poke_dw(AWE_CPF(voice), 0x40000000); + + voices[voice].state = AWE_ST_ON; + + /* clear voice position for the next note on this channel */ + if (SINGLE_LAYER_MODE()) { + FX_UNSET(fx, AWE_FX_SAMPLE_START); + FX_UNSET(fx, AWE_FX_COARSE_SAMPLE_START); + } +} + + +/* turn off the voice */ +static void +awe_note_off(int voice) +{ + awe_voice_info *vp; + unsigned short tmp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if ((vp = voices[voice].sample) == NULL) { + voices[voice].state = AWE_ST_OFF; + return; + } + + tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE, + (unsigned char)vp->parm.modrelease); + awe_poke(AWE_DCYSUS(voice), tmp); + tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE, + (unsigned char)vp->parm.volrelease); + awe_poke(AWE_DCYSUSV(voice), tmp); + voices[voice].state = AWE_ST_RELEASED; +} + +/* force to terminate the voice (no releasing echo) */ +static void +awe_terminate(int voice) +{ + awe_poke(AWE_DCYSUSV(voice), 0x807F); + awe_tweak_voice(voice); + voices[voice].state = AWE_ST_OFF; +} + +/* turn off other voices with the same exclusive class (for drums) */ +static void +awe_exclusive_off(int voice) +{ + int i, exclass; + + if (voices[voice].sample == NULL) + return; + if ((exclass = voices[voice].sample->exclusiveClass) == 0) + return; /* not exclusive */ + + /* turn off voices with the same class */ + for (i = 0; i < awe_max_voices; i++) { + if (i != voice && IS_PLAYING(i) && + voices[i].sample && voices[i].ch == voices[voice].ch && + voices[i].sample->exclusiveClass == exclass) { + DEBUG(4,printk("AWE32: [exoff(%d)]\n", i)); + awe_terminate(i); + awe_voice_init(i, TRUE); + } + } +} + + +/*================================================================ + * change the parameters of an audible voice + *================================================================*/ + +/* change pitch */ +static void +awe_set_pitch(int voice, int forced) +{ + if (IS_NO_EFFECT(voice) && !forced) return; + awe_poke(AWE_IP(voice), voices[voice].apitch); + DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch)); +} + +/* calculate & change pitch */ +static void +awe_set_voice_pitch(int voice, int forced) +{ + awe_calc_pitch(voice); + awe_set_pitch(voice, forced); +} + +/* change volume & cutoff */ +static void +awe_set_volume(int voice, int forced) +{ + awe_voice_info *vp; + unsigned short tmp2; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (!IS_PLAYING(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index < 0) + return; + + tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF, vp->parm.cutoff); + tmp2 = (tmp2 << 8); + tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN, + (unsigned char)voices[voice].avol); + awe_poke(AWE_IFATN(voice), tmp2); +} + +/* calculate & change volume */ +static void +awe_set_voice_vol(int voice, int forced) +{ + if (IS_EMPTY(voice)) + return; + awe_calc_volume(voice); + awe_set_volume(voice, forced); +} + + +/* change pan; this could make a click noise.. */ +static void +awe_set_pan(int voice, int forced) +{ + unsigned int temp; + int addr; + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index < 0) + return; + + /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */ + if (vp->fixpan > 0) /* 0-127 */ + temp = 255 - (int)vp->fixpan * 2; + else { + int pos = 0; + if (vp->pan >= 0) /* 0-127 */ + pos = (int)vp->pan * 2 - 128; + pos += voices[voice].cinfo->panning; /* -128 - 127 */ + pos = 127 - pos; + if (pos < 0) + temp = 0; + else if (pos > 255) + temp = 255; + else + temp = pos; + } + if (forced || temp != voices[voice].apan) { + addr = vp->loopstart - 1; + addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START, + AWE_FX_COARSE_LOOP_START, vp->mode); + temp = (temp<<24) | (unsigned int)addr; + awe_poke_dw(AWE_PSST(voice), temp); + voices[voice].apan = temp; + DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr)); + } +} + +/* effects change during playing */ +static void +awe_fx_fmmod(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index < 0) + return; + awe_poke(AWE_FMMOD(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF, + vp->parm.fmmod)); +} + +/* set tremolo (lfo1) volume & frequency */ +static void +awe_fx_tremfrq(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index < 0) + return; + awe_poke(AWE_TREMFRQ(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ, + vp->parm.tremfrq)); +} + +/* set lfo2 pitch & frequency */ +static void +awe_fx_fm2frq2(int voice, int forced) +{ + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index < 0) + return; + awe_poke(AWE_FM2FRQ2(voice), + FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ, + vp->parm.fm2frq2)); +} + + +/* Q & current address (Q 4bit value, MSB) */ +static void +awe_fx_filterQ(int voice, int forced) +{ + unsigned int addr; + awe_voice_info *vp; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + if (IS_NO_EFFECT(voice) && !forced) return; + if ((vp = voices[voice].sample) == NULL || vp->index < 0) + return; + + addr = awe_peek_dw(AWE_CCCA(voice)) & 0xffffff; + addr |= (FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ) << 28); + awe_poke_dw(AWE_CCCA(voice), addr); +} + +/*================================================================ + * calculate pitch offset + *---------------------------------------------------------------- + * 0xE000 is no pitch offset at 44100Hz sample. + * Every 4096 is one octave. + *================================================================*/ + +static void +awe_calc_pitch(int voice) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + awe_chan_info *cp = voices[voice].cinfo; + int offset; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + if (ap->index < 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample(ap) < 0) + return; + } + + /* calculate offset */ + if (ap->fixkey >= 0) { + DEBUG(3,printk("AWE32: p-> fixkey(%d) tune(%d)\n", ap->fixkey, ap->tune)); + offset = (ap->fixkey - ap->root) * 4096 / 12; + } else { + DEBUG(3,printk("AWE32: p(%d)-> root(%d) tune(%d)\n", vp->note, ap->root, ap->tune)); + offset = (vp->note - ap->root) * 4096 / 12; + DEBUG(4,printk("AWE32: p-> ofs=%d\n", offset)); + } + offset = (offset * ap->scaleTuning) / 100; + DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset)); + offset += ap->tune * 4096 / 1200; + DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset)); + if (cp->bender != 0) { + DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender)); + /* (819200: 1 semitone) ==> (4096: 12 semitones) */ + offset += cp->bender * cp->bender_range / 2400; + } + + /* add initial pitch correction */ + if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH)) + offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH]; + else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) + offset += cp->fx.val[AWE_FX_INIT_PITCH]; + + /* 0xe000: root pitch */ + vp->apitch = 0xe000 + ap->rate_offset + offset; + DEBUG(4,printk("AWE32: p-> sum aofs=%x, rate_ofs=%d\n", vp->apitch, ap->rate_offset)); + if (vp->apitch > 0xffff) + vp->apitch = 0xffff; + if (vp->apitch < 0) + vp->apitch = 0; +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY +/* calculate MIDI key and semitone from the specified frequency */ +static void +awe_calc_pitch_from_freq(int voice, int freq) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + int offset; + int note; + + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + if (ap->index < 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample(ap) < 0) + return; + } + note = freq_to_note(freq); + offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200; + offset = (offset * ap->scaleTuning) / 100; + if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH)) + offset += fx_lay->val[AWE_FX_INIT_PITCH]; + else if (FX_ON(fx, AWE_FX_INIT_PITCH)) + offset += fx->val[AWE_FX_INIT_PITCH]; + vp->apitch = 0xe000 + ap->rate_offset + offset; + if (vp->apitch > 0xffff) + vp->apitch = 0xffff; + if (vp->apitch < 0) + vp->apitch = 0; +} +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + + +/*================================================================ + * calculate volume attenuation + *---------------------------------------------------------------- + * Voice volume is controlled by volume attenuation parameter. + * So volume becomes maximum when avol is 0 (no attenuation), and + * minimum when 255 (-96dB or silence). + *================================================================*/ + +static int vol_table[128] = { + 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, + 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, + 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, + 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, + 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, + 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, + 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, + 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, +}; + +static void +awe_calc_volume(int voice) +{ + voice_info *vp = &voices[voice]; + awe_voice_info *ap; + awe_chan_info *cp = voices[voice].cinfo; + int vol; + + /* search voice information */ + if ((ap = vp->sample) == NULL) + return; + + ap = vp->sample; + if (ap->index < 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); + if (awe_set_sample(ap) < 0) + return; + } + + /* 0 - 127 */ + vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127); + vol = vol * ap->amplitude / 127; + + if (vol < 0) vol = 0; + if (vol > 127) vol = 127; + + /* calc to attenuation */ + vol = vol_table[vol]; + vol = vol + (int)ap->attenuation + init_atten; + if (vol > 255) vol = 255; + + vp->avol = vol; + DEBUG(3,printk("AWE32: [-- voice(%d) vol=%x]\n", voice, vol)); +} + + +/* set sostenuto on */ +static void awe_sostenuto_on(int voice, int forced) +{ + if (IS_NO_EFFECT(voice) && !forced) return; + voices[voice].sostenuto = 127; +} + + +/* drop sustain */ +static void awe_sustain_off(int voice, int forced) +{ + if (voices[voice].state == AWE_ST_SUSTAINED) { + awe_note_off(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, FALSE); + } +} + + +/* terminate and initialize voice */ +static void awe_terminate_and_init(int voice, int forced) +{ + awe_terminate(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, TRUE); +} + + +/*================================================================ + * synth operation routines + *================================================================*/ + +#define AWE_VOICE_KEY(v) (0x8000 | (v)) +#define AWE_CHAN_KEY(c,n) (((c) << 8) | ((n) + 1)) +#define KEY_CHAN_MATCH(key,c) (((key) >> 8) == (c)) + +/* initialize the voice */ +static void +awe_voice_init(int voice, int init_all) +{ + voice_info *vp = &voices[voice]; + + /* reset voice search key */ + if (playing_mode == AWE_PLAY_DIRECT) + vp->key = AWE_VOICE_KEY(voice); + else + vp->key = 0; + + /* clear voice mapping */ + voice_alloc->map[voice] = 0; + + /* touch the timing flag */ + vp->time = current_alloc_time; + + /* initialize other parameters if necessary */ + if (init_all) { + vp->note = -1; + vp->velocity = 0; + vp->sostenuto = 0; + + vp->sample = NULL; + vp->cinfo = &channels[voice]; + vp->ch = voice; + vp->state = AWE_ST_OFF; + + /* emu8000 parameters */ + vp->apitch = 0; + vp->avol = 255; + vp->apan = -1; + } +} + +/* clear effects */ +static void awe_fx_init(int ch) +{ + if (SINGLE_LAYER_MODE() && !misc_modes[AWE_MD_KEEP_EFFECT]) { + BZERO(&channels[ch].fx, sizeof(channels[ch].fx)); + BZERO(&channels[ch].fx_layer, sizeof(&channels[ch].fx_layer)); + } +} + +/* initialize channel info */ +static void awe_channel_init(int ch, int init_all) +{ + awe_chan_info *cp = &channels[ch]; + cp->channel = ch; + if (init_all) { + cp->panning = 0; /* zero center */ + cp->bender_range = 200; /* sense * 100 */ + cp->main_vol = 127; + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) { + cp->instr = misc_modes[AWE_MD_DEF_DRUM]; + cp->bank = AWE_DRUM_BANK; + } else { + cp->instr = misc_modes[AWE_MD_DEF_PRESET]; + cp->bank = misc_modes[AWE_MD_DEF_BANK]; + } + cp->vrec = -1; + cp->def_vrec = -1; + } + + cp->bender = 0; /* zero tune skew */ + cp->expression_vol = 127; + cp->chan_press = 0; + cp->sustained = 0; + + if (! misc_modes[AWE_MD_KEEP_EFFECT]) { + BZERO(&cp->fx, sizeof(cp->fx)); + BZERO(&cp->fx_layer, sizeof(cp->fx_layer)); + } +} + + +/* change the voice parameters; voice = channel */ +static void awe_voice_change(int voice, fx_affect_func func) +{ + int i; + switch (playing_mode) { + case AWE_PLAY_DIRECT: + func(voice, FALSE); + break; + case AWE_PLAY_INDIRECT: + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == AWE_VOICE_KEY(voice)) + func(i, FALSE); + break; + default: + for (i = 0; i < awe_max_voices; i++) + if (KEY_CHAN_MATCH(voices[i].key, voice)) + func(i, FALSE); + break; + } +} + + +/*---------------------------------------------------------------- + * device open / close + *----------------------------------------------------------------*/ + +/* open device: + * reset status of all voices, and clear sample position flag + */ +static int +awe_open(int dev, int mode) +{ + if (awe_busy) + return RET_ERROR(EBUSY); + + awe_busy = TRUE; + + /* set default mode */ + awe_init_misc_modes(FALSE); + init_atten = misc_modes[AWE_MD_ZERO_ATTEN]; + drum_flags = DEFAULT_DRUM_FLAGS; + playing_mode = AWE_PLAY_INDIRECT; + + /* reset voices & channels */ + awe_reset(dev); + + patch_opened = 0; + + return 0; +} + + +/* close device: + * reset all voices again (terminate sounds) + */ +static void +awe_close(int dev) +{ + awe_reset(dev); + awe_busy = FALSE; +} + + +/* set miscellaneous mode parameters + */ +static void +awe_init_misc_modes(int init_all) +{ + int i; + for (i = 0; i < AWE_MD_END; i++) { + if (init_all || misc_modes_default[i].init_each_time) + misc_modes[i] = misc_modes_default[i].value; + } +} + + +/* sequencer I/O control: + */ +static int +awe_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + switch (cmd) { + case SNDCTL_SYNTH_INFO: + if (playing_mode == AWE_PLAY_DIRECT) + awe_info.nr_voices = awe_max_voices; + else + awe_info.nr_voices = AWE_MAX_CHANNELS; + IOCTL_TO_USER((char*)arg, 0, &awe_info, sizeof(awe_info)); + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + awe_reset_samples(); + awe_reset(dev); + return 0; + break; + + case SNDCTL_SEQ_PERCMODE: + /* what's this? */ + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return awe_mem_size - awe_free_mem_ptr() * 2; + + default: + printk("AWE32: unsupported ioctl %d\n", cmd); + return RET_ERROR(EINVAL); + } +} + + +static int voice_in_range(int voice) +{ + if (playing_mode == AWE_PLAY_DIRECT) { + if (voice < 0 || voice >= awe_max_voices) + return FALSE; + } else { + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return FALSE; + } + return TRUE; +} + +static void release_voice(int voice, int do_sustain) +{ + if (IS_NO_SOUND(voice)) + return; + if (do_sustain && (voices[voice].cinfo->sustained == 127 || + voices[voice].sostenuto == 127)) + voices[voice].state = AWE_ST_SUSTAINED; + else { + awe_note_off(voice); + awe_fx_init(voices[voice].ch); + awe_voice_init(voice, FALSE); + } +} + +/* release all notes */ +static void awe_note_off_all(int do_sustain) +{ + int i; + for (i = 0; i < awe_max_voices; i++) + release_voice(i, do_sustain); +} + +/* kill a voice: + * not terminate, just release the voice. + */ +static int +awe_kill_note(int dev, int voice, int note, int velocity) +{ + int i, v2, key; + + DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity)); + if (! voice_in_range(voice)) + return RET_ERROR(EINVAL); + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + key = AWE_VOICE_KEY(voice); + break; + + case AWE_PLAY_MULTI2: + v2 = voice_alloc->map[voice] >> 8; + voice_alloc->map[voice] = 0; + voice = v2; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return RET_ERROR(EINVAL); + /* continue to below */ + default: + key = AWE_CHAN_KEY(voice, note); + break; + } + + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) + release_voice(i, TRUE); + } + return 0; +} + + +static void start_or_volume_change(int voice, int velocity) +{ + voices[voice].velocity = velocity; + awe_calc_volume(voice); + if (voices[voice].state == AWE_ST_STANDBY) + awe_note_on(voice); + else if (voices[voice].state == AWE_ST_ON) + awe_set_volume(voice, FALSE); +} + +static void set_and_start_voice(int voice, int state) +{ + /* calculate pitch & volume parameters */ + voices[voice].state = state; + awe_calc_pitch(voice); + awe_calc_volume(voice); + if (state == AWE_ST_ON) + awe_note_on(voice); +} + +/* start a voice: + * if note is 255, identical with aftertouch function. + * Otherwise, start a voice with specified not and volume. + */ +static int +awe_start_note(int dev, int voice, int note, int velocity) +{ + int i, key, state, volonly; + + DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity)); + if (! voice_in_range(voice)) + return RET_ERROR(EINVAL); + + if (velocity == 0) + state = AWE_ST_STANDBY; /* stand by for playing */ + else + state = AWE_ST_ON; /* really play */ + volonly = FALSE; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + key = AWE_VOICE_KEY(voice); + if (note == 255) + volonly = TRUE; + break; + + case AWE_PLAY_MULTI2: + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return RET_ERROR(EINVAL); + /* continue to below */ + default: + if (note >= 128) { /* key volume mode */ + note -= 128; + volonly = TRUE; + } + key = AWE_CHAN_KEY(voice, note); + break; + } + + /* dynamic volume change */ + if (volonly) { + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) + start_or_volume_change(i, velocity); + } + return 0; + } + + /* if the same note still playing, stop it */ + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == key) { + if (voices[i].state == AWE_ST_ON) { + awe_note_off(i); + awe_voice_init(i, FALSE); + } else if (voices[i].state == AWE_ST_STANDBY) + awe_voice_init(i, TRUE); + } + + /* allocate voices */ + if (playing_mode == AWE_PLAY_DIRECT) + awe_alloc_one_voice(voice, note, velocity); + else + awe_alloc_multi_voices(voice, note, velocity, key); + + /* turn off other voices exlusively (for drums) */ + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == key) + awe_exclusive_off(i); + + /* set up pitch and volume parameters */ + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key && voices[i].state == AWE_ST_OFF) + set_and_start_voice(i, state); + } + + return 0; +} + + +/* search instrument from preset table with the specified bank */ +static int +awe_search_instr(int bank, int preset) +{ + int i; + + for (i = preset_table[preset]; i >= 0; i = infos[i].next_bank) { + if (infos[i].bank == bank) + return i; + } + return -1; +} + + +/* assign the instrument to a voice */ +static int +awe_set_instr_2(int dev, int voice, int instr_no) +{ + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return RET_ERROR(EINVAL); + } + return awe_set_instr(dev, voice, instr_no); +} + +/* assign the instrument to a channel; voice is the channel number */ +static int +awe_set_instr(int dev, int voice, int instr_no) +{ + awe_chan_info *cinfo; + int def_bank; + + if (! voice_in_range(voice)) + return RET_ERROR(EINVAL); + + if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS) + return RET_ERROR(EINVAL); + + cinfo = &channels[voice]; + + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice)) + def_bank = AWE_DRUM_BANK; /* always search drumset */ + else + def_bank = cinfo->bank; + + cinfo->vrec = -1; + cinfo->def_vrec = -1; + cinfo->vrec = awe_search_instr(def_bank, instr_no); + if (def_bank == AWE_DRUM_BANK) /* search default drumset */ + cinfo->def_vrec = awe_search_instr(def_bank, misc_modes[AWE_MD_DEF_DRUM]); + else /* search default preset */ + cinfo->def_vrec = awe_search_instr(misc_modes[AWE_MD_DEF_BANK], instr_no); + + if (cinfo->vrec < 0 && cinfo->def_vrec < 0) { + DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no)); + } + + cinfo->instr = instr_no; + + return 0; +} + + +/* reset all voices; terminate sounds and initialize parameters */ +static void +awe_reset(int dev) +{ + int i; + current_alloc_time = 0; + /* don't turn off voice 31 and 32. they are used also for FM voices */ + for (i = 0; i < awe_max_voices; i++) { + awe_terminate(i); + awe_voice_init(i, TRUE); + } + for (i = 0; i < AWE_MAX_CHANNELS; i++) + awe_channel_init(i, TRUE); + for (i = 0; i < 16; i++) { + awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127; + awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127; + } + awe_init_fm(); + awe_tweak(); +} + + +/* hardware specific control: + * GUS specific and AWE32 specific controls are available. + */ +static void +awe_hw_control(int dev, unsigned char *event) +{ + int cmd = event[2]; + if (cmd & _AWE_MODE_FLAG) + awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event); +#ifdef AWE_HAS_GUS_COMPATIBILITY + else + awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event); +#endif +} + + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* GUS compatible controls */ +static void +awe_hw_gus_control(int dev, int cmd, unsigned char *event) +{ + int voice, i, key; + unsigned short p1; + short p2; + int plong; + + if (MULTI_LAYER_MODE()) + return; + if (cmd == _GUS_NUMVOICES) + return; + + voice = event[3]; + if (! voice_in_range(voice)) + return; + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + plong = *(int*) &event[4]; + + switch (cmd) { + case _GUS_VOICESAMPLE: + awe_set_instr(dev, voice, p1); + return; + + case _GUS_VOICEBALA: + /* 0 to 15 --> -128 to 127 */ + awe_panning(dev, voice, ((int)p1 << 4) - 128); + return; + + case _GUS_VOICEVOL: + case _GUS_VOICEVOL2: + /* not supported yet */ + return; + + case _GUS_RAMPRANGE: + case _GUS_RAMPRATE: + case _GUS_RAMPMODE: + case _GUS_RAMPON: + case _GUS_RAMPOFF: + /* volume ramping not supported */ + return; + + case _GUS_VOLUME_SCALE: + return; + + case _GUS_VOICE_POS: + FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START, + (short)(plong & 0x7fff)); + FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START, + (plong >> 15) & 0xffff); + return; + } + + key = AWE_VOICE_KEY(voice); + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) { + switch (cmd) { + case _GUS_VOICEON: + awe_note_on(i); + break; + + case _GUS_VOICEOFF: + awe_terminate(i); + awe_fx_init(voices[i].ch); + awe_voice_init(i, TRUE); + break; + + case _GUS_VOICEFADE: + awe_note_off(i); + awe_fx_init(voices[i].ch); + awe_voice_init(i, FALSE); + break; + + case _GUS_VOICEFREQ: + awe_calc_pitch_from_freq(i, plong); + break; + } + } + } +} + +#endif + + +/* AWE32 specific controls */ +static void +awe_hw_awe_control(int dev, int cmd, unsigned char *event) +{ + int voice; + unsigned short p1; + short p2; + awe_chan_info *cinfo; + FX_Rec *fx; + int i; + + voice = event[3]; + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + cinfo = &channels[voice]; + + switch (cmd) { + case _AWE_DEBUG_MODE: + debug_mode = p1; + printk("AWE32: debug mode = %d\n", debug_mode); + break; + case _AWE_REVERB_MODE: + awe_set_reverb_mode(p1); + break; + + case _AWE_CHORUS_MODE: + awe_set_chorus_mode(p1); + break; + + case _AWE_REMOVE_LAST_SAMPLES: + DEBUG(0,printk("AWE32: remove last samples\n")); + if (locked_sf_id > 0) + awe_remove_samples(locked_sf_id); + break; + + case _AWE_INITIALIZE_CHIP: + awe_initialize(); + break; + + case _AWE_SEND_EFFECT: + fx = &cinfo->fx; + i = FX_FLAG_SET; + if (p1 >= 0x100) { + int layer = (p1 >> 8); + if (layer >= 0 && layer < MAX_LAYERS) + fx = &cinfo->fx_layer[layer]; + p1 &= 0xff; + } + if (p1 & 0x40) i = FX_FLAG_OFF; + if (p1 & 0x80) i = FX_FLAG_ADD; + p1 &= 0x3f; + if (p1 < AWE_FX_END) { + DEBUG(0,printk("AWE32: effects (%d) %d %d\n", voice, p1, p2)); + if (i == FX_FLAG_SET) + FX_SET(fx, p1, p2); + else if (i == FX_FLAG_ADD) + FX_ADD(fx, p1, p2); + else + FX_UNSET(fx, p1); + if (i != FX_FLAG_OFF && parm_defs[p1].realtime) { + DEBUG(0,printk("AWE32: fx_realtime (%d)\n", voice)); + awe_voice_change(voice, parm_defs[p1].realtime); + } + } + break; + + case _AWE_RESET_CHANNEL: + awe_channel_init(voice, !p1); + break; + + case _AWE_TERMINATE_ALL: + awe_reset(0); + break; + + case _AWE_TERMINATE_CHANNEL: + awe_voice_change(voice, awe_terminate_and_init); + break; + + case _AWE_RELEASE_ALL: + awe_note_off_all(FALSE); + break; + case _AWE_NOTEOFF_ALL: + awe_note_off_all(TRUE); + break; + + case _AWE_INITIAL_VOLUME: + DEBUG(0,printk("AWE32: init attenuation %d\n", p1)); + if (p2 == 0) /* absolute value */ + init_atten = (short)p1; + else /* relative value */ + init_atten = misc_modes[AWE_MD_ZERO_ATTEN] + (short)p1; + if (init_atten < 0) init_atten = 0; + for (i = 0; i < awe_max_voices; i++) + awe_set_voice_vol(i, TRUE); + break; + + case _AWE_CHN_PRESSURE: + cinfo->chan_press = p1; + p1 = p1 * misc_modes[AWE_MD_MOD_SENSE] / 1200; + FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, p1); + awe_voice_change(voice, awe_fx_fmmod); + FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, p1); + awe_voice_change(voice, awe_fx_fm2frq2); + break; + + case _AWE_CHANNEL_MODE: + DEBUG(0,printk("AWE32: channel mode = %d\n", p1)); + playing_mode = p1; + awe_reset(0); + break; + + case _AWE_DRUM_CHANNELS: + DEBUG(0,printk("AWE32: drum flags = %x\n", p1)); + drum_flags = *(unsigned int*)&event[4]; + break; + + case _AWE_MISC_MODE: + DEBUG(0,printk("AWE32: misc mode = %d %d\n", p1, p2)); + if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END) + misc_modes[p1] = p2; + break; + + case _AWE_EQUALIZER: + awe_equalizer((int)p1, (int)p2); + break; + + default: + DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice)); + break; + } +} + + +/* voice pressure change */ +static void +awe_aftertouch(int dev, int voice, int pressure) +{ + int note; + + DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure)); + if (! voice_in_range(voice)) + return; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + awe_start_note(dev, voice, 255, pressure); + break; + case AWE_PLAY_MULTI2: + note = (voice_alloc->map[voice] & 0xff) - 1; + awe_start_note(dev, voice, note + 0x80, pressure); + break; + } +} + + +/* voice control change */ +static void +awe_controller(int dev, int voice, int ctrl_num, int value) +{ + int i; + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + cinfo = &channels[voice]; + + switch (ctrl_num) { + case CTL_BANK_SELECT: /* MIDI control #0 */ + DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value)); + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice) && + !misc_modes[AWE_MD_TOGGLE_DRUM_BANK]) + break; + cinfo->bank = value; + if (cinfo->bank == AWE_DRUM_BANK) + DRUM_CHANNEL_ON(cinfo->channel); + else + DRUM_CHANNEL_OFF(cinfo->channel); + awe_set_instr(dev, voice, cinfo->instr); + break; + + case CTL_MODWHEEL: /* MIDI control #1 */ + DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value)); + i = value * misc_modes[AWE_MD_MOD_SENSE] / 1200; + FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i); + awe_voice_change(voice, awe_fx_fmmod); + FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i); + awe_voice_change(voice, awe_fx_fm2frq2); + break; + + case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */ + DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value)); + /* zero centered */ + cinfo->bender = value; + awe_voice_change(voice, awe_set_voice_pitch); + break; + + case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value)); + /* value = sense x 100 */ + cinfo->bender_range = value; + /* no audible pitch change yet.. */ + break; + + case CTL_EXPRESSION: /* MIDI control #11 */ + if (SINGLE_LAYER_MODE()) + value /= 128; + case CTRL_EXPRESSION: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value)); + /* 0 - 127 */ + cinfo->expression_vol = value; + awe_voice_change(voice, awe_set_voice_vol); + break; + + case CTL_PAN: /* MIDI control #10 */ + DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value)); + /* (0-127) -> signed 8bit */ + cinfo->panning = value * 2 - 128; + if (misc_modes[AWE_MD_REALTIME_PAN]) + awe_voice_change(voice, awe_set_pan); + break; + + case CTL_MAIN_VOLUME: /* MIDI control #7 */ + if (SINGLE_LAYER_MODE()) + value = (value * 100) / 16383; + case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value)); + /* 0 - 127 */ + cinfo->main_vol = value; + awe_voice_change(voice, awe_set_voice_vol); + break; + + case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */ + DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value)); + FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2); + break; + + case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */ + DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value)); + FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2); + break; + +#ifdef AWE_ACCEPT_ALL_SOUNDS_CONTROLL + case 120: /* all sounds off */ + awe_note_off_all(FALSE); + break; + case 123: /* all notes off */ + awe_note_off_all(TRUE); + break; +#endif + + case CTL_SUSTAIN: /* MIDI control #64 */ + cinfo->sustained = value; + if (value != 127) + awe_voice_change(voice, awe_sustain_off); + break; + + case CTL_SOSTENUTO: /* MIDI control #66 */ + if (value == 127) + awe_voice_change(voice, awe_sostenuto_on); + else + awe_voice_change(voice, awe_sustain_off); + break; + + default: + DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n", + voice, ctrl_num, value)); + break; + } +} + + +/* voice pan change (value = -128 - 127) */ +static void +awe_panning(int dev, int voice, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + cinfo = &channels[voice]; + cinfo->panning = value; + DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning)); + if (misc_modes[AWE_MD_REALTIME_PAN]) + awe_voice_change(voice, awe_set_pan); +} + + +/* volume mode change */ +static void +awe_volume_method(int dev, int mode) +{ + /* not impremented */ + DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode)); +} + + +#ifndef AWE_NO_PATCHMGR +/* patch manager */ +static int +awe_patchmgr(int dev, struct patmgr_info *rec) +{ + printk("AWE32 Warning: patch manager control not supported\n"); + return 0; +} +#endif + + +/* pitch wheel change: 0-16384 */ +static void +awe_bender(int dev, int voice, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + /* convert to zero centered value */ + cinfo = &channels[voice]; + cinfo->bender = value - 8192; + DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender)); + awe_voice_change(voice, awe_set_voice_pitch); +} + + +/*---------------------------------------------------------------- + * load a sound patch: + * three types of patches are accepted: AWE, GUS, and SYSEX. + *----------------------------------------------------------------*/ + +static int +awe_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + awe_patch_info patch; + int rc = 0; + +#ifdef AWE_HAS_GUS_COMPATIBILITY + if (format == GUS_PATCH) { + return awe_load_guspatch(addr, offs, count, pmgr_flag); + } else +#endif + if (format == SYSEX_PATCH) { + /* no system exclusive message supported yet */ + return 0; + } else if (format != AWE_PATCH) { + printk("AWE32 Error: Invalid patch format (key) 0x%x\n", format); + return RET_ERROR(EINVAL); + } + + if (count < AWE_PATCH_INFO_SIZE) { + printk("AWE32 Error: Patch header too short\n"); + return RET_ERROR(EINVAL); + } + COPY_FROM_USER(((char*)&patch) + offs, addr, offs, + AWE_PATCH_INFO_SIZE - offs); + + count -= AWE_PATCH_INFO_SIZE; + if (count < patch.len) { + printk("AWE32: sample: Patch record too short (%d<%d)\n", + count, patch.len); + return RET_ERROR(EINVAL); + } + + switch (patch.type) { + case AWE_LOAD_INFO: + rc = awe_load_info(&patch, addr, count); + break; + case AWE_LOAD_DATA: + rc = awe_load_data(&patch, addr, count); + break; + case AWE_OPEN_PATCH: + rc = awe_open_patch(&patch, addr, count); + break; + case AWE_CLOSE_PATCH: + rc = awe_close_patch(&patch, addr, count); + break; + case AWE_UNLOAD_PATCH: + rc = awe_unload_patch(&patch, addr, count); + break; + case AWE_REPLACE_DATA: + rc = awe_replace_data(&patch, addr, count); + break; + case AWE_MAP_PRESET: + rc = awe_load_map(&patch, addr, count); + break; + case AWE_LOAD_CHORUS_FX: + rc = awe_load_chorus_fx(&patch, addr, count); + break; + case AWE_LOAD_REVERB_FX: + rc = awe_load_reverb_fx(&patch, addr, count); + break; + + default: + printk("AWE32 Error: unknown patch format type %d\n", + patch.type); + rc = RET_ERROR(EINVAL); + } + + return rc; +} + + +/* create an sflist record */ +static int +awe_create_sf(int type, char *name) +{ + sf_list *rec; + + /* terminate sounds */ + awe_reset(0); + if (current_sf_id >= max_sfs) { + int newsize = max_sfs + AWE_MAX_SF_LISTS; + sf_list *newlist = my_realloc(sflists, sizeof(sf_list)*max_sfs, + sizeof(sf_list)*newsize); + if (newlist == NULL) + return 1; + sflists = newlist; + max_sfs = newsize; + } + rec = &sflists[current_sf_id]; + rec->sf_id = current_sf_id + 1; + rec->type = type; + if (current_sf_id == 0 || (type & AWE_PAT_LOCKED) != 0) + locked_sf_id = current_sf_id + 1; + /* + if (name) + MEMCPY(rec->name, name, AWE_PATCH_NAME_LEN); + else + BZERO(rec->name, AWE_PATCH_NAME_LEN); + */ + rec->num_info = awe_free_info(); + rec->num_sample = awe_free_sample(); + rec->mem_ptr = awe_free_mem_ptr(); + rec->infos = -1; + rec->samples = -1; + + current_sf_id++; + return 0; +} + + +/* open patch; create sf list and set opened flag */ +static int +awe_open_patch(awe_patch_info *patch, const char *addr, int count) +{ + awe_open_parm parm; + COPY_FROM_USER(&parm, addr, AWE_PATCH_INFO_SIZE, sizeof(parm)); + if (awe_create_sf(parm.type, parm.name)) { + printk("AWE32: can't open: failed to alloc new list\n"); + return RET_ERROR(ENOSPC); + } + patch_opened = TRUE; + return current_sf_id; +} + +/* check if the patch is already opened */ +static int +check_patch_opened(int type, char *name) +{ + if (! patch_opened) { + if (awe_create_sf(type, name)) { + printk("AWE32: failed to alloc new list\n"); + return RET_ERROR(ENOSPC); + } + patch_opened = TRUE; + return current_sf_id; + } + return current_sf_id; +} + +/* close the patch; if no voice is loaded, remove the patch */ +static int +awe_close_patch(awe_patch_info *patch, const char *addr, int count) +{ + if (patch_opened && current_sf_id > 0) { + /* if no voice is loaded, release the current patch */ + if (sflists[current_sf_id-1].infos == -1) + awe_remove_samples(current_sf_id - 1); + } + patch_opened = 0; + return 0; +} + + +/* remove the latest patch */ +static int +awe_unload_patch(awe_patch_info *patch, const char *addr, int count) +{ + if (current_sf_id > 0) + awe_remove_samples(current_sf_id - 1); + return 0; +} + +/* allocate voice info list records */ +static int alloc_new_info(int nvoices) +{ + int newsize, free_info; + awe_voice_list *newlist; + free_info = awe_free_info(); + if (free_info + nvoices >= max_infos) { + do { + newsize = max_infos + AWE_MAX_INFOS; + } while (free_info + nvoices >= newsize); + newlist = my_realloc(infos, sizeof(awe_voice_list)*max_infos, + sizeof(awe_voice_list)*newsize); + if (newlist == NULL) { + printk("AWE32: can't alloc info table\n"); + return RET_ERROR(ENOSPC); + } + infos = newlist; + max_infos = newsize; + } + return 0; +} + +/* allocate sample info list records */ +static int alloc_new_sample(void) +{ + int newsize, free_sample; + awe_sample_list *newlist; + free_sample = awe_free_sample(); + if (free_sample >= max_samples) { + newsize = max_samples + AWE_MAX_SAMPLES; + newlist = my_realloc(samples, + sizeof(awe_sample_list)*max_samples, + sizeof(awe_sample_list)*newsize); + if (newlist == NULL) { + printk("AWE32: can't alloc sample table\n"); + return RET_ERROR(ENOSPC); + } + samples = newlist; + max_samples = newsize; + } + return 0; +} + +/* load voice map */ +static int +awe_load_map(awe_patch_info *patch, const char *addr, int count) +{ + awe_voice_map map; + awe_voice_list *rec; + int free_info; + + if (check_patch_opened(AWE_PAT_TYPE_MAP, NULL) < 0) + return RET_ERROR(ENOSPC); + if (alloc_new_info(1) < 0) + return RET_ERROR(ENOSPC); + + COPY_FROM_USER(&map, addr, AWE_PATCH_INFO_SIZE, sizeof(map)); + + free_info = awe_free_info(); + rec = &infos[free_info]; + rec->bank = map.map_bank; + rec->instr = map.map_instr; + rec->type = V_ST_MAPPED; + rec->disabled = FALSE; + awe_init_voice_info(&rec->v); + if (map.map_key >= 0) { + rec->v.low = map.map_key; + rec->v.high = map.map_key; + } + rec->v.start = map.src_instr; + rec->v.end = map.src_bank; + rec->v.fixkey = map.src_key; + rec->v.sf_id = current_sf_id; + add_info_list(free_info); + add_sf_info(free_info); + + return 0; +} + +/* load voice information data */ +static int +awe_load_info(awe_patch_info *patch, const char *addr, int count) +{ + int offset; + awe_voice_rec_hdr hdr; + int i; + int total_size; + + if (count < AWE_VOICE_REC_SIZE) { + printk("AWE32 Error: invalid patch info length\n"); + return RET_ERROR(EINVAL); + } + + offset = AWE_PATCH_INFO_SIZE; + COPY_FROM_USER((char*)&hdr, addr, offset, AWE_VOICE_REC_SIZE); + offset += AWE_VOICE_REC_SIZE; + + if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { + printk("AWE32 Error: Illegal voice number %d\n", hdr.nvoices); + return RET_ERROR(EINVAL); + } + total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices; + if (count < total_size) { + printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n", + count, hdr.nvoices); + return RET_ERROR(EINVAL); + } + + if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0) + return RET_ERROR(ENOSPC); + +#if 0 /* it looks like not so useful.. */ + /* check if the same preset already exists in the info list */ + for (i = sflists[current_sf_id-1].infos; i >= 0; i = infos[i].next) { + if (infos[i].disabled) continue; + if (infos[i].bank == hdr.bank && infos[i].instr == hdr.instr) { + /* in exclusive mode, do skip loading this */ + if (hdr.write_mode == AWE_WR_EXCLUSIVE) + return 0; + /* in replace mode, disable the old data */ + else if (hdr.write_mode == AWE_WR_REPLACE) + infos[i].disabled = TRUE; + } + } + if (hdr.write_mode == AWE_WR_REPLACE) + rebuild_preset_list(); +#endif + + if (alloc_new_info(hdr.nvoices) < 0) + return RET_ERROR(ENOSPC); + + for (i = 0; i < hdr.nvoices; i++) { + int rec = awe_free_info(); + + infos[rec].bank = hdr.bank; + infos[rec].instr = hdr.instr; + infos[rec].type = V_ST_NORMAL; + infos[rec].disabled = FALSE; + + /* copy awe_voice_info parameters */ + COPY_FROM_USER(&infos[rec].v, addr, offset, AWE_VOICE_INFO_SIZE); + offset += AWE_VOICE_INFO_SIZE; + infos[rec].v.sf_id = current_sf_id; + if (infos[rec].v.mode & AWE_MODE_INIT_PARM) + awe_init_voice_parm(&infos[rec].v.parm); + awe_set_sample(&infos[rec].v); + add_info_list(rec); + add_sf_info(rec); + } + + return 0; +} + +/* load wave sample data */ +static int +awe_load_data(awe_patch_info *patch, const char *addr, int count) +{ + int offset, size; + int rc, free_sample; + awe_sample_info *rec; + + if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0) + return RET_ERROR(ENOSPC); + + if (alloc_new_sample() < 0) + return RET_ERROR(ENOSPC); + + free_sample = awe_free_sample(); + rec = &samples[free_sample].v; + + size = (count - AWE_SAMPLE_INFO_SIZE) / 2; + offset = AWE_PATCH_INFO_SIZE; + COPY_FROM_USER(rec, addr, offset, AWE_SAMPLE_INFO_SIZE); + offset += AWE_SAMPLE_INFO_SIZE; + if (size != rec->size) { + printk("AWE32: load: sample size differed (%d != %d)\n", + rec->size, size); + return RET_ERROR(EINVAL); + } + if (rec->size > 0) + if ((rc = awe_write_wave_data(addr, offset, rec, -1)) != 0) + return rc; + + rec->sf_id = current_sf_id; + + add_sf_sample(free_sample); + + return 0; +} + + +/* replace wave sample data */ +static int +awe_replace_data(awe_patch_info *patch, const char *addr, int count) +{ + int offset; + int size; + int rc, i; + int channels; + awe_sample_info cursmp; + int save_mem_ptr; + + if (! patch_opened) { + printk("AWE32: replace: patch not opened\n"); + return RET_ERROR(EINVAL); + } + + size = (count - AWE_SAMPLE_INFO_SIZE) / 2; + offset = AWE_PATCH_INFO_SIZE; + COPY_FROM_USER(&cursmp, addr, offset, AWE_SAMPLE_INFO_SIZE); + offset += AWE_SAMPLE_INFO_SIZE; + if (cursmp.size == 0 || size != cursmp.size) { + printk("AWE32: replace: illegal sample size (%d!=%d)\n", + cursmp.size, size); + return RET_ERROR(EINVAL); + } + channels = patch->optarg; + if (channels <= 0 || channels > AWE_NORMAL_VOICES) { + printk("AWE32: replace: illegal channels %d\n", channels); + return RET_ERROR(EINVAL); + } + + for (i = sflists[current_sf_id-1].samples; + i >= 0; i = samples[i].next) { + if (samples[i].v.sample == cursmp.sample) + break; + } + if (i < 0) { + printk("AWE32: replace: cannot find existing sample data %d\n", + cursmp.sample); + return RET_ERROR(EINVAL); + } + + if (samples[i].v.size != cursmp.size) { + printk("AWE32: replace: exiting size differed (%d!=%d)\n", + samples[i].v.size, cursmp.size); + return RET_ERROR(EINVAL); + } + + save_mem_ptr = awe_free_mem_ptr(); + sflists[current_sf_id-1].mem_ptr = samples[i].v.start - awe_mem_start; + MEMCPY(&samples[i].v, &cursmp, sizeof(cursmp)); + if ((rc = awe_write_wave_data(addr, offset, &samples[i].v, channels)) != 0) + return rc; + sflists[current_sf_id-1].mem_ptr = save_mem_ptr; + samples[i].v.sf_id = current_sf_id; + + return 0; +} + + +/*----------------------------------------------------------------*/ + +static const char *readbuf_addr; +static int readbuf_offs; +static int readbuf_flags; +#ifdef __FreeBSD__ +static unsigned short *readbuf_loop; +static int readbuf_loopstart, readbuf_loopend; +#endif + +/* initialize read buffer */ +static int +readbuf_init(const char *addr, int offset, awe_sample_info *sp) +{ +#ifdef __FreeBSD__ + readbuf_loop = NULL; + readbuf_loopstart = sp->loopstart; + readbuf_loopend = sp->loopend; + if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) { + int looplen = sp->loopend - sp->loopstart; + readbuf_loop = my_malloc(looplen * 2); + if (readbuf_loop == NULL) { + printk("AWE32: can't malloc temp buffer\n"); + return RET_ERROR(ENOSPC); + } + } +#endif + readbuf_addr = addr; + readbuf_offs = offset; + readbuf_flags = sp->mode_flags; + return 0; +} + +/* read directly from user buffer */ +static unsigned short +readbuf_word(int pos) +{ + unsigned short c; + /* read from user buffer */ + if (readbuf_flags & AWE_SAMPLE_8BITS) { + unsigned char cc; + GET_BYTE_FROM_USER(cc, readbuf_addr, readbuf_offs + pos); + c = cc << 8; /* convert 8bit -> 16bit */ + } else { + GET_SHORT_FROM_USER(c, readbuf_addr, readbuf_offs + pos * 2); + } + if (readbuf_flags & AWE_SAMPLE_UNSIGNED) + c ^= 0x8000; /* unsigned -> signed */ +#ifdef __FreeBSD__ + /* write on cache for reverse loop */ + if (readbuf_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) { + if (pos >= readbuf_loopstart && pos < readbuf_loopend) + readbuf_loop[pos - readbuf_loopstart] = c; + } +#endif + return c; +} + +#ifdef __FreeBSD__ +/* read from cache */ +static unsigned short +readbuf_word_cache(int pos) +{ + if (pos >= readbuf_loopstart && pos < readbuf_loopend) + return readbuf_loop[pos - readbuf_loopstart]; + return 0; +} + +static void +readbuf_end(void) +{ + if (readbuf_loop) { + my_free(readbuf_loop); + } + readbuf_loop = NULL; +} + +#else + +#define readbuf_word_cache readbuf_word +#define readbuf_end() /**/ + +#endif + +/*----------------------------------------------------------------*/ + +#define BLANK_LOOP_START 8 +#define BLANK_LOOP_END 40 +#define BLANK_LOOP_SIZE 48 + +/* loading onto memory */ +static int +awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels) +{ + int i, truesize, dram_offset; + int rc; + + /* be sure loop points start < end */ + if (sp->loopstart > sp->loopend) { + int tmp = sp->loopstart; + sp->loopstart = sp->loopend; + sp->loopend = tmp; + } + + /* compute true data size to be loaded */ + truesize = sp->size; + if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) + truesize += sp->loopend - sp->loopstart; + if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) + truesize += BLANK_LOOP_SIZE; + if (awe_free_mem_ptr() + truesize >= awe_mem_size/2) { + printk("AWE32 Error: Sample memory full\n"); + return RET_ERROR(ENOSPC); + } + + /* recalculate address offset */ + sp->end -= sp->start; + sp->loopstart -= sp->start; + sp->loopend -= sp->start; + + dram_offset = awe_free_mem_ptr() + awe_mem_start; + sp->start = dram_offset; + sp->end += dram_offset; + sp->loopstart += dram_offset; + sp->loopend += dram_offset; + + /* set the total size (store onto obsolete checksum value) */ + if (sp->size == 0) + sp->checksum = 0; + else + sp->checksum = truesize; + + if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0) + return rc; + + if (readbuf_init(addr, offset, sp) < 0) + return RET_ERROR(ENOSPC); + + for (i = 0; i < sp->size; i++) { + unsigned short c; + c = readbuf_word(i); + awe_write_dram(c); + if (i == sp->loopend && + (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) { + int looplen = sp->loopend - sp->loopstart; + /* copy reverse loop */ + int k; + for (k = 1; k <= looplen; k++) { + c = readbuf_word_cache(i - k); + awe_write_dram(c); + } + if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) { + sp->end += looplen; + } else { + sp->start += looplen; + sp->end += looplen; + } + } + } + readbuf_end(); + + /* if no blank loop is attached in the sample, add it */ + if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) { + for (i = 0; i < BLANK_LOOP_SIZE; i++) + awe_write_dram(0); + if (sp->mode_flags & AWE_SAMPLE_SINGLESHOT) { + sp->loopstart = sp->end + BLANK_LOOP_START; + sp->loopend = sp->end + BLANK_LOOP_END; + } + } + + sflists[current_sf_id-1].mem_ptr += truesize; + awe_close_dram(); + + /* initialize FM */ + awe_init_fm(); + + return 0; +} + + +/*----------------------------------------------------------------*/ + +#ifdef AWE_HAS_GUS_COMPATIBILITY + +/* calculate GUS envelope time: + * is this correct? i have no idea.. + */ +static int +calc_gus_envelope_time(int rate, int start, int end) +{ + int r, p, t; + r = (3 - ((rate >> 6) & 3)) * 3; + p = rate & 0x3f; + t = end - start; + if (t < 0) t = -t; + if (13 > r) + t = t << (13 - r); + else + t = t >> (r - 13); + return (t * 10) / (p * 441); +} + +#define calc_gus_sustain(val) (0x7f - vol_table[(val)/2]) +#define calc_gus_attenuation(val) vol_table[(val)/2] + +/* load GUS patch */ +static int +awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag) +{ + struct patch_info patch; + awe_voice_info *rec; + awe_sample_info *smp; + int sizeof_patch; + int note, free_sample, free_info; + int rc; + + sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */ + if (size < sizeof_patch) { + printk("AWE32 Error: Patch header too short\n"); + return RET_ERROR(EINVAL); + } + COPY_FROM_USER(((char*)&patch) + offs, addr, offs, sizeof_patch - offs); + size -= sizeof_patch; + if (size < patch.len) { + printk("AWE32 Warning: Patch record too short (%d<%d)\n", + size, patch.len); + return RET_ERROR(EINVAL); + } + if (check_patch_opened(AWE_PAT_TYPE_GUS, NULL) < 0) + return RET_ERROR(ENOSPC); + if (alloc_new_sample() < 0) + return RET_ERROR(ENOSPC); + if (alloc_new_info(1)) + return RET_ERROR(ENOSPC); + + free_sample = awe_free_sample(); + smp = &samples[free_sample].v; + + smp->sample = free_sample; + smp->start = 0; + smp->end = patch.len; + smp->loopstart = patch.loop_start; + smp->loopend = patch.loop_end; + smp->size = patch.len; + + /* set up mode flags */ + smp->mode_flags = 0; + if (!(patch.mode & WAVE_16_BITS)) + smp->mode_flags |= AWE_SAMPLE_8BITS; + if (patch.mode & WAVE_UNSIGNED) + smp->mode_flags |= AWE_SAMPLE_UNSIGNED; + smp->mode_flags |= AWE_SAMPLE_NO_BLANK; + if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) + smp->mode_flags |= AWE_SAMPLE_SINGLESHOT; + if (patch.mode & WAVE_BIDIR_LOOP) + smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP; + if (patch.mode & WAVE_LOOP_BACK) + smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP; + + DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags)); + if (patch.mode & WAVE_16_BITS) { + /* convert to word offsets */ + smp->size /= 2; + smp->end /= 2; + smp->loopstart /= 2; + smp->loopend /= 2; + } + smp->checksum_flag = 0; + smp->checksum = 0; + + if ((rc = awe_write_wave_data(addr, sizeof_patch, smp, -1)) != 0) + return rc; + + smp->sf_id = current_sf_id; + add_sf_sample(free_sample); + + /* set up voice info */ + free_info = awe_free_info(); + rec = &infos[free_info].v; + awe_init_voice_info(rec); + rec->sample = free_sample; /* the last sample */ + rec->rate_offset = calc_rate_offset(patch.base_freq); + note = freq_to_note(patch.base_note); + rec->root = note / 100; + rec->tune = -(note % 100); + rec->low = freq_to_note(patch.low_note) / 100; + rec->high = freq_to_note(patch.high_note) / 100; + DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n", + rec->rate_offset, note, + rec->low, rec->high, + patch.low_note, patch.high_note)); + /* panning position; -128 - 127 => 0-127 */ + rec->pan = (patch.panning + 128) / 2; + + /* detuning is ignored */ + /* 6points volume envelope */ + if (patch.mode & WAVE_ENVELOPES) { + int attack, hold, decay, release; + attack = calc_gus_envelope_time + (patch.env_rate[0], 0, patch.env_offset[0]); + hold = calc_gus_envelope_time + (patch.env_rate[1], patch.env_offset[0], + patch.env_offset[1]); + decay = calc_gus_envelope_time + (patch.env_rate[2], patch.env_offset[1], + patch.env_offset[2]); + release = calc_gus_envelope_time + (patch.env_rate[3], patch.env_offset[1], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[4], patch.env_offset[3], + patch.env_offset[4]); + release += calc_gus_envelope_time + (patch.env_rate[5], patch.env_offset[4], + patch.env_offset[5]); + rec->parm.volatkhld = (calc_parm_attack(attack) << 8) | + calc_parm_hold(hold); + rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | + calc_parm_decay(decay); + rec->parm.volrelease = 0x8000 | calc_parm_decay(release); + DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release)); + rec->attenuation = calc_gus_attenuation(patch.env_offset[0]); + } + + /* tremolo effect */ + if (patch.mode & WAVE_TREMOLO) { + int rate = (patch.tremolo_rate * 1000 / 38) / 42; + rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; + DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n", + patch.tremolo_rate, patch.tremolo_depth, + rec->parm.tremfrq)); + } + /* vibrato effect */ + if (patch.mode & WAVE_VIBRATO) { + int rate = (patch.vibrato_rate * 1000 / 38) / 42; + rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; + DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n", + patch.tremolo_rate, patch.tremolo_depth, + rec->parm.tremfrq)); + } + + /* scale_freq, scale_factor, volume, and fractions not implemented */ + + /* append to the tail of the list */ + infos[free_info].bank = misc_modes[AWE_MD_GUS_BANK]; + infos[free_info].instr = patch.instr_no; + infos[free_info].disabled = FALSE; + infos[free_info].type = V_ST_NORMAL; + infos[free_info].v.sf_id = current_sf_id; + + add_info_list(free_info); + add_sf_info(free_info); + + /* set the voice index */ + awe_set_sample(rec); + + return 0; +} + +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + +/*---------------------------------------------------------------- + * sample and voice list handlers + *----------------------------------------------------------------*/ + +/* append this to the sf list */ +static void add_sf_info(int rec) +{ + int sf_id = infos[rec].v.sf_id; + if (sf_id == 0) return; + sf_id--; + if (sflists[sf_id].infos < 0) + sflists[sf_id].infos = rec; + else { + int i, prev; + prev = sflists[sf_id].infos; + while ((i = infos[prev].next) >= 0) + prev = i; + infos[prev].next = rec; + } + infos[rec].next = -1; + sflists[sf_id].num_info++; +} + +/* prepend this sample to sf list */ +static void add_sf_sample(int rec) +{ + int sf_id = samples[rec].v.sf_id; + if (sf_id == 0) return; + sf_id--; + samples[rec].next = sflists[sf_id].samples; + sflists[sf_id].samples = rec; + sflists[sf_id].num_sample++; +} + +/* purge the old records which don't belong with the same file id */ +static void purge_old_list(int rec, int next) +{ + infos[rec].next_instr = next; + if (infos[rec].bank == AWE_DRUM_BANK) { + /* remove samples with the same note range */ + int cur, *prevp = &infos[rec].next_instr; + int low = infos[rec].v.low; + int high = infos[rec].v.high; + for (cur = next; cur >= 0; cur = infos[cur].next_instr) { + if (infos[cur].v.low == low && + infos[cur].v.high == high && + infos[cur].v.sf_id != infos[rec].v.sf_id) + *prevp = infos[cur].next_instr; + prevp = &infos[cur].next_instr; + } + } else { + if (infos[next].v.sf_id != infos[rec].v.sf_id) + infos[rec].next_instr = -1; + } +} + +/* prepend to top of the preset table */ +static void add_info_list(int rec) +{ + int *prevp, cur; + int instr = infos[rec].instr; + int bank = infos[rec].bank; + + if (infos[rec].disabled) + return; + + prevp = &preset_table[instr]; + cur = *prevp; + while (cur >= 0) { + /* search the first record with the same bank number */ + if (infos[cur].bank == bank) { + /* replace the list with the new record */ + infos[rec].next_bank = infos[cur].next_bank; + *prevp = rec; + purge_old_list(rec, cur); + return; + } + prevp = &infos[cur].next_bank; + cur = infos[cur].next_bank; + } + + /* this is the first bank record.. just add this */ + infos[rec].next_instr = -1; + infos[rec].next_bank = preset_table[instr]; + preset_table[instr] = rec; +} + +/* remove samples later than the specified sf_id */ +static void +awe_remove_samples(int sf_id) +{ + if (sf_id <= 0) { + awe_reset_samples(); + return; + } + /* already removed? */ + if (current_sf_id <= sf_id) + return; + + current_sf_id = sf_id; + if (locked_sf_id > sf_id) + locked_sf_id = sf_id; + + rebuild_preset_list(); +} + +/* rebuild preset search list */ +static void rebuild_preset_list(void) +{ + int i, j; + + for (i = 0; i < AWE_MAX_PRESETS; i++) + preset_table[i] = -1; + + for (i = 0; i < current_sf_id; i++) { + for (j = sflists[i].infos; j >= 0; j = infos[j].next) + add_info_list(j); + } +} + +/* search the specified sample */ +static short +awe_set_sample(awe_voice_info *vp) +{ + int i; + vp->index = -1; + for (i = sflists[vp->sf_id-1].samples; i >= 0; i = samples[i].next) { + if (samples[i].v.sample == vp->sample) { + /* set the actual sample offsets */ + vp->start += samples[i].v.start; + vp->end += samples[i].v.end; + vp->loopstart += samples[i].v.loopstart; + vp->loopend += samples[i].v.loopend; + /* copy mode flags */ + vp->mode = samples[i].v.mode_flags; + /* set index */ + vp->index = i; + return i; + } + } + return -1; +} + + +/*---------------------------------------------------------------- + * voice allocation + *----------------------------------------------------------------*/ + +/* look for all voices associated with the specified note & velocity */ +static int +awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist) +{ + int nvoices; + + nvoices = 0; + for (; rec >= 0; rec = infos[rec].next_instr) { + if (note >= infos[rec].v.low && + note <= infos[rec].v.high && + velocity >= infos[rec].v.vellow && + velocity <= infos[rec].v.velhigh) { + vlist[nvoices] = &infos[rec].v; + if (infos[rec].type == V_ST_MAPPED) /* mapper */ + return -1; + nvoices++; + if (nvoices >= AWE_MAX_VOICES) + break; + } + } + return nvoices; +} + +/* store the voice list from the specified note and velocity. + if the preset is mapped, seek for the destination preset, and rewrite + the note number if necessary. + */ +static int +really_alloc_voices(int vrec, int def_vrec, int *note, int velocity, awe_voice_info **vlist, int level) +{ + int nvoices; + + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + if (nvoices == 0) + nvoices = awe_search_multi_voices(def_vrec, *note, velocity, vlist); + if (nvoices < 0) { /* mapping */ + int preset = vlist[0]->start; + int bank = vlist[0]->end; + int key = vlist[0]->fixkey; + if (level > 5) { + printk("AWE32: too deep mapping level\n"); + return 0; + } + vrec = awe_search_instr(bank, preset); + if (bank == AWE_DRUM_BANK) + def_vrec = awe_search_instr(bank, 0); + else + def_vrec = awe_search_instr(0, preset); + if (key >= 0) + *note = key; + return really_alloc_voices(vrec, def_vrec, note, velocity, vlist, level+1); + } + + return nvoices; +} + +/* allocate voices corresponding note and velocity; supports multiple insts. */ +static void +awe_alloc_multi_voices(int ch, int note, int velocity, int key) +{ + int i, v, nvoices; + awe_voice_info *vlist[AWE_MAX_VOICES]; + + if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0) + awe_set_instr(0, ch, channels[ch].instr); + + /* check the possible voices; note may be changeable if mapped */ + nvoices = really_alloc_voices(channels[ch].vrec, channels[ch].def_vrec, + ¬e, velocity, vlist, 0); + + /* set the voices */ + current_alloc_time++; + for (i = 0; i < nvoices; i++) { + v = awe_clear_voice(); + voices[v].key = key; + voices[v].ch = ch; + voices[v].note = note; + voices[v].velocity = velocity; + voices[v].time = current_alloc_time; + voices[v].cinfo = &channels[ch]; + voices[v].sample = vlist[i]; + voices[v].state = AWE_ST_MARK; + voices[v].layer = nvoices - i - 1; /* in reverse order */ + } + + /* clear the mark in allocated voices */ + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].state == AWE_ST_MARK) + voices[i].state = AWE_ST_OFF; + + } +} + + +/* search the best voice from the specified status condition */ +static int +search_best_voice(int condition) +{ + int i, time, best; + best = -1; + time = current_alloc_time + 1; + for (i = 0; i < awe_max_voices; i++) { + if ((voices[i].state & condition) && + (best < 0 || voices[i].time < time)) { + best = i; + time = voices[i].time; + } + } + /* clear voice */ + if (best >= 0) { + if (voices[best].state != AWE_ST_OFF) + awe_terminate(best); + awe_voice_init(best, TRUE); + } + + return best; +} + +/* search an empty voice. + if no empty voice is found, at least terminate a voice + */ +static int +awe_clear_voice(void) +{ + int best; + + /* looking for the oldest empty voice */ + if ((best = search_best_voice(AWE_ST_OFF)) >= 0) + return best; + if ((best = search_best_voice(AWE_ST_RELEASED)) >= 0) + return best; + /* looking for the oldest sustained voice */ + if ((best = search_best_voice(AWE_ST_SUSTAINED)) >= 0) + return best; + +#ifdef AWE_LOOKUP_MIDI_PRIORITY + if (MULTI_LAYER_MODE() && misc_modes[AWE_MD_CHN_PRIOR]) { + int ch = -1; + int time = current_alloc_time + 1; + int i; + /* looking for the voices from high channel (except drum ch) */ + for (i = 0; i < awe_max_voices; i++) { + if (IS_DRUM_CHANNEL(voices[i].ch)) continue; + if (voices[i].ch < ch) continue; + if (voices[i].state != AWE_ST_MARK && + (voices[i].ch > ch || voices[i].time < time)) { + best = i; + time = voices[i].time; + ch = voices[i].ch; + } + } + } +#endif + if (best < 0) + best = search_best_voice(~AWE_ST_MARK); + + if (best >= 0) + return best; + + return 0; +} + + +/* search sample for the specified note & velocity and set it on the voice; + * note that voice is the voice index (not channel index) + */ +static void +awe_alloc_one_voice(int voice, int note, int velocity) +{ + int ch, nvoices; + awe_voice_info *vlist[AWE_MAX_VOICES]; + + ch = voices[voice].ch; + if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0) + awe_set_instr(0, ch, channels[ch].instr); + + nvoices = really_alloc_voices(voices[voice].cinfo->vrec, + voices[voice].cinfo->def_vrec, + ¬e, velocity, vlist, 0); + if (nvoices > 0) { + voices[voice].time = ++current_alloc_time; + voices[voice].sample = vlist[0]; /* use the first one */ + voices[voice].layer = 0; + voices[voice].note = note; + voices[voice].velocity = velocity; + } +} + + +/*---------------------------------------------------------------- + * sequencer2 functions + *----------------------------------------------------------------*/ + +/* search an empty voice; used by sequencer2 */ +static int +awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + playing_mode = AWE_PLAY_MULTI2; + awe_info.nr_voices = AWE_MAX_CHANNELS; + return awe_clear_voice(); +} + + +/* set up voice; used by sequencer2 */ +static void +awe_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info; + if (synth_devs[dev] == NULL || + (info = &synth_devs[dev]->chn_info[chn]) == NULL) + return; + + if (voice < 0 || voice >= awe_max_voices) + return; + + DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn)); + channels[chn].expression_vol = info->controllers[CTL_EXPRESSION]; + channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME]; + channels[chn].panning = + info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */ + channels[chn].bender = info->bender_value; /* zero center */ + channels[chn].bank = info->controllers[CTL_BANK_SELECT]; + channels[chn].sustained = info->controllers[CTL_SUSTAIN]; + if (info->controllers[CTL_EXT_EFF_DEPTH]) { + FX_SET(&channels[chn].fx, AWE_FX_REVERB, + info->controllers[CTL_EXT_EFF_DEPTH] * 2); + } + if (info->controllers[CTL_CHORUS_DEPTH]) { + FX_SET(&channels[chn].fx, AWE_FX_CHORUS, + info->controllers[CTL_CHORUS_DEPTH] * 2); + } + awe_set_instr(dev, chn, info->pgm_num); +} + + +#ifdef CONFIG_AWE32_MIXER +/*================================================================ + * AWE32 mixer device control + *================================================================*/ + +static int +awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int i, level; + + if (((cmd >> 8) & 0xff) != 'M') + return RET_ERROR(EINVAL); + + level = (int)IOCTL_IN(arg); + level = ((level & 0xff) + (level >> 8)) / 2; + DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level)); + + if (IO_WRITE_CHECK(cmd)) { + switch (cmd & 0xff) { + case SOUND_MIXER_BASS: + awe_bass_level = level * 12 / 100; + if (awe_bass_level >= 12) + awe_bass_level = 11; + awe_equalizer(awe_bass_level, awe_treble_level); + break; + case SOUND_MIXER_TREBLE: + awe_treble_level = level * 12 / 100; + if (awe_treble_level >= 12) + awe_treble_level = 11; + awe_equalizer(awe_bass_level, awe_treble_level); + break; + case SOUND_MIXER_VOLUME: + level = level * 127 / 100; + if (level >= 128) level = 127; + init_atten = vol_table[level]; + for (i = 0; i < awe_max_voices; i++) + awe_set_voice_vol(i, TRUE); + break; + } + } + switch (cmd & 0xff) { + case SOUND_MIXER_BASS: + level = awe_bass_level * 100 / 24; + level = (level << 8) | level; + break; + case SOUND_MIXER_TREBLE: + level = awe_treble_level * 100 / 24; + level = (level << 8) | level; + break; + case SOUND_MIXER_VOLUME: + for (i = 127; i > 0; i--) { + if (init_atten <= vol_table[i]) + break; + } + level = i * 100 / 127; + level = (level << 8) | level; + break; + case SOUND_MIXER_DEVMASK: + level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME; + break; + default: + level = 0; + break; + } + return IOCTL_OUT(arg, level); +} +#endif /* CONFIG_AWE32_MIXER */ + + +/*================================================================ + * initialization of AWE32 + *================================================================*/ + +/* intiailize audio channels */ +static void +awe_init_audio(void) +{ + int ch; + + /* turn off envelope engines */ + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke(AWE_DCYSUSV(ch), 0x80); + } + + /* reset all other parameters to zero */ + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke(AWE_ENVVOL(ch), 0); + awe_poke(AWE_ENVVAL(ch), 0); + awe_poke(AWE_DCYSUS(ch), 0); + awe_poke(AWE_ATKHLDV(ch), 0); + awe_poke(AWE_LFO1VAL(ch), 0); + awe_poke(AWE_ATKHLD(ch), 0); + awe_poke(AWE_LFO2VAL(ch), 0); + awe_poke(AWE_IP(ch), 0); + awe_poke(AWE_IFATN(ch), 0); + awe_poke(AWE_PEFE(ch), 0); + awe_poke(AWE_FMMOD(ch), 0); + awe_poke(AWE_TREMFRQ(ch), 0); + awe_poke(AWE_FM2FRQ2(ch), 0); + awe_poke_dw(AWE_PTRX(ch), 0); + awe_poke_dw(AWE_VTFT(ch), 0); + awe_poke_dw(AWE_PSST(ch), 0); + awe_poke_dw(AWE_CSL(ch), 0); + awe_poke_dw(AWE_CCCA(ch), 0); + } + + for (ch = 0; ch < AWE_MAX_VOICES; ch++) { + awe_poke_dw(AWE_CPF(ch), 0); + awe_poke_dw(AWE_CVCF(ch), 0); + } +} + + +/* initialize DMA address */ +static void +awe_init_dma(void) +{ + awe_poke_dw(AWE_SMALR, 0); + awe_poke_dw(AWE_SMARR, 0); + awe_poke_dw(AWE_SMALW, 0); + awe_poke_dw(AWE_SMARW, 0); +} + + +/* initialization arrays; from ADIP */ + +static unsigned short init1[128] = { + 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, + 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, + 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, + 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, + + 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, + 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, + 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, + 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, + + 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, + 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, + 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, + 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, + + 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, + 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, + 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, + 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, +}; + +static unsigned short init2[128] = { + 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, + 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, + 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, + 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, + + 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, + 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, + 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, + 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, + + 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, + 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, + 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, + 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, + + 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, + 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, + 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, + 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, +}; + +static unsigned short init3[128] = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, + 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, + 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, + 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + +static unsigned short init4[128] = { + 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, + 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, + 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, + 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, + + 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, + 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, + 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, + 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, + + 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, + 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, + 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, + 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, + + 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, + 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, + 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, + 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, +}; + + +/* send initialization arrays to start up */ +static void +awe_init_array(void) +{ + awe_send_array(init1); + awe_wait(1024); + awe_send_array(init2); + awe_send_array(init3); + awe_poke_dw(AWE_HWCF4, 0); + awe_poke_dw(AWE_HWCF5, 0x83); + awe_poke_dw(AWE_HWCF6, 0x8000); + awe_send_array(init4); +} + +/* send an initialization array */ +static void +awe_send_array(unsigned short *data) +{ + int i; + unsigned short *p; + + p = data; + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT1(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT2(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT3(i), *p); + for (i = 0; i < AWE_MAX_VOICES; i++, p++) + awe_poke(AWE_INIT4(i), *p); +} + + +/* + * set up awe32 channels to some known state. + */ + +/* set the envelope & LFO parameters to the default values; see ADIP */ +static void +awe_tweak_voice(int i) +{ + /* set all mod/vol envelope shape to minimum */ + awe_poke(AWE_ENVVOL(i), 0x8000); + awe_poke(AWE_ENVVAL(i), 0x8000); + awe_poke(AWE_DCYSUS(i), 0x7F7F); + awe_poke(AWE_ATKHLDV(i), 0x7F7F); + awe_poke(AWE_ATKHLD(i), 0x7F7F); + awe_poke(AWE_PEFE(i), 0); /* mod envelope height to zero */ + awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */ + awe_poke(AWE_LFO2VAL(i), 0x8000); + awe_poke(AWE_IP(i), 0xE000); /* no pitch shift */ + awe_poke(AWE_IFATN(i), 0xFF00); /* volume to minimum */ + awe_poke(AWE_FMMOD(i), 0); + awe_poke(AWE_TREMFRQ(i), 0); + awe_poke(AWE_FM2FRQ2(i), 0); +} + +static void +awe_tweak(void) +{ + int i; + /* reset all channels */ + for (i = 0; i < awe_max_voices; i++) + awe_tweak_voice(i); +} + + +/* + * initializes the FM section of AWE32; + * see Vince Vu's unofficial AWE32 programming guide + */ + +static void +awe_init_fm(void) +{ +#ifndef AWE_ALWAYS_INIT_FM + /* if no extended memory is on board.. */ + if (awe_mem_size <= 0) + return; +#endif + DEBUG(3,printk("AWE32: initializing FM\n")); + + /* Initialize the last two channels for DRAM refresh and producing + the reverb and chorus effects for Yamaha OPL-3 synthesizer */ + + /* 31: FM left channel, 0xffffe0-0xffffe8 */ + awe_poke(AWE_DCYSUSV(30), 0x80); + awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */ + awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 | + (DEF_FM_CHORUS_DEPTH << 24)); + awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8)); + awe_poke_dw(AWE_CPF(30), 0); + awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3); + + /* 32: FM right channel, 0xfffff0-0xfffff8 */ + awe_poke(AWE_DCYSUSV(31), 0x80); + awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */ + awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 | + (DEF_FM_CHORUS_DEPTH << 24)); + awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8)); + awe_poke_dw(AWE_CPF(31), 0x8000); + awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3); + + /* skew volume & cutoff */ + awe_poke_dw(AWE_VTFT(30), 0x8000FFFF); + awe_poke_dw(AWE_VTFT(31), 0x8000FFFF); + + voices[30].state = AWE_ST_FM; + voices[31].state = AWE_ST_FM; + + /* change maximum channels to 30 */ + awe_max_voices = AWE_NORMAL_VOICES; + if (playing_mode == AWE_PLAY_DIRECT) + awe_info.nr_voices = awe_max_voices; + else + awe_info.nr_voices = AWE_MAX_CHANNELS; + voice_alloc->max_voice = awe_max_voices; +} + +/* + * AWE32 DRAM access routines + */ + +/* open DRAM write accessing mode */ +static int +awe_open_dram_for_write(int offset, int channels) +{ + int vidx[AWE_NORMAL_VOICES]; + int i; + + if (channels < 0 || channels >= AWE_NORMAL_VOICES) { + channels = AWE_NORMAL_VOICES; + for (i = 0; i < AWE_NORMAL_VOICES; i++) + vidx[i] = i; + } else { + for (i = 0; i < channels; i++) + vidx[i] = awe_clear_voice(); + } + + /* use all channels for DMA transfer */ + for (i = 0; i < channels; i++) { + if (vidx[i] < 0) continue; + awe_poke(AWE_DCYSUSV(vidx[i]), 0x80); + awe_poke_dw(AWE_VTFT(vidx[i]), 0); + awe_poke_dw(AWE_CVCF(vidx[i]), 0); + awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000); + awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000); + awe_poke_dw(AWE_PSST(vidx[i]), 0); + awe_poke_dw(AWE_CSL(vidx[i]), 0); + awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000); + voices[vidx[i]].state = AWE_ST_DRAM; + } + /* point channels 31 & 32 to ROM samples for DRAM refresh */ + awe_poke_dw(AWE_VTFT(30), 0); + awe_poke_dw(AWE_PSST(30), 0x1d8); + awe_poke_dw(AWE_CSL(30), 0x1e0); + awe_poke_dw(AWE_CCCA(30), 0x1d8); + awe_poke_dw(AWE_VTFT(31), 0); + awe_poke_dw(AWE_PSST(31), 0x1d8); + awe_poke_dw(AWE_CSL(31), 0x1e0); + awe_poke_dw(AWE_CCCA(31), 0x1d8); + voices[30].state = AWE_ST_FM; + voices[31].state = AWE_ST_FM; + + /* if full bit is on, not ready to write on */ + if (awe_peek_dw(AWE_SMALW) & 0x80000000) { + for (i = 0; i < channels; i++) { + awe_poke_dw(AWE_CCCA(vidx[i]), 0); + voices[i].state = AWE_ST_OFF; + } + return RET_ERROR(ENOSPC); + } + + /* set address to write */ + awe_poke_dw(AWE_SMALW, offset); + + return 0; +} + +/* open DRAM for RAM size detection */ +static void +awe_open_dram_for_check(void) +{ + int i; + for (i = 0; i < AWE_NORMAL_VOICES; i++) { + awe_poke(AWE_DCYSUSV(i), 0x80); + awe_poke_dw(AWE_VTFT(i), 0); + awe_poke_dw(AWE_CVCF(i), 0); + awe_poke_dw(AWE_PTRX(i), 0x40000000); + awe_poke_dw(AWE_CPF(i), 0x40000000); + awe_poke_dw(AWE_PSST(i), 0); + awe_poke_dw(AWE_CSL(i), 0); + if (i & 1) /* DMA write */ + awe_poke_dw(AWE_CCCA(i), 0x06000000); + else /* DMA read */ + awe_poke_dw(AWE_CCCA(i), 0x04000000); + voices[i].state = AWE_ST_DRAM; + } +} + + +/* close dram access */ +static void +awe_close_dram(void) +{ + int i; + /* wait until FULL bit in SMAxW register be false */ + for (i = 0; i < 10000; i++) { + if (!(awe_peek_dw(AWE_SMALW) & 0x80000000)) + break; + awe_wait(10); + } + + for (i = 0; i < AWE_NORMAL_VOICES; i++) { + if (voices[i].state == AWE_ST_DRAM) { + awe_poke_dw(AWE_CCCA(i), 0); + awe_poke(AWE_DCYSUSV(i), 0x807F); + voices[i].state = AWE_ST_OFF; + } + } +} + + +/*================================================================ + * detect presence of AWE32 and check memory size + *================================================================*/ + +/* detect emu8000 chip on the specified address; from VV's guide */ + +static int +awe_detect_base(int addr) +{ + awe_base = addr; + if ((awe_peek(AWE_U1) & 0x000F) != 0x000C) + return 0; + if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058) + return 0; + if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003) + return 0; + DEBUG(0,printk("AWE32 found at %x\n", awe_base)); + return 1; +} + +static int +awe_detect(void) +{ + int base; + if (awe_base == 0) { + for (base = 0x620; base <= 0x680; base += 0x20) + if (awe_detect_base(base)) + return 1; + DEBUG(0,printk("AWE32 not found\n")); + return 0; + } + return 1; +} + + +/*================================================================ + * check dram size on AWE board + *================================================================*/ + +/* any three numbers you like */ +#define UNIQUE_ID1 0x1234 +#define UNIQUE_ID2 0x4321 +#define UNIQUE_ID3 0xFFFF + +static int +awe_check_dram(void) +{ + if (awe_mem_size > 0) { + awe_mem_size *= 1024; /* convert to Kbytes */ + return awe_mem_size; + } + + awe_open_dram_for_check(); + + awe_mem_size = 0; + + /* set up unique two id numbers */ + awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET); + awe_poke(AWE_SMLD, UNIQUE_ID1); + awe_poke(AWE_SMLD, UNIQUE_ID2); + + while (awe_mem_size < AWE_MAX_DRAM_SIZE) { + awe_wait(2); + /* read a data on the DRAM start address */ + awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET); + awe_peek(AWE_SMLD); /* discard stale data */ + if (awe_peek(AWE_SMLD) != UNIQUE_ID1) + break; + if (awe_peek(AWE_SMLD) != UNIQUE_ID2) + break; + awe_mem_size += 32; /* increment 32 Kbytes */ + /* Write a unique data on the test address; + * if the address is out of range, the data is written on + * 0x200000(=AWE_DRAM_OFFSET). Then the two id words are + * broken by this data. + */ + awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L); + awe_poke(AWE_SMLD, UNIQUE_ID3); + awe_wait(2); + /* read a data on the just written DRAM address */ + awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + awe_mem_size*512L); + awe_peek(AWE_SMLD); /* discard stale data */ + if (awe_peek(AWE_SMLD) != UNIQUE_ID3) + break; + } + awe_close_dram(); + + DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", awe_mem_size)); + + /* convert to Kbytes */ + awe_mem_size *= 1024; + return awe_mem_size; +} + + +/*================================================================ + * chorus and reverb controls; from VV's guide + *================================================================*/ + +/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ +static char chorus_defined[AWE_CHORUS_NUMBERS]; +static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = { + {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ + {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ + {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ + {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ + {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ + {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ + {0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */ + {0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */ +}; + +static int +awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count) +{ + if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) { + printk("AWE32 Error: illegal chorus mode %d for uploading\n", patch->optarg); + return RET_ERROR(EINVAL); + } + if (count < sizeof(awe_chorus_fx_rec)) { + printk("AWE32 Error: too short chorus fx parameters\n"); + return RET_ERROR(EINVAL); + } + COPY_FROM_USER(&chorus_parm[patch->optarg], addr, AWE_PATCH_INFO_SIZE, + sizeof(awe_chorus_fx_rec)); + chorus_defined[patch->optarg] = TRUE; + return 0; +} + +static void +awe_set_chorus_mode(int effect) +{ + if (effect < 0 || effect >= AWE_CHORUS_NUMBERS || + (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect])) + return; + awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback); + awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset); + awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth); + awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay); + awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq); + awe_poke_dw(AWE_HWCF6, 0x8000); + awe_poke_dw(AWE_HWCF7, 0x0000); + chorus_mode = effect; +} + +/*----------------------------------------------------------------*/ + +/* reverb mode settings; write the following 28 data of 16 bit length + * on the corresponding ports in the reverb_cmds array + */ +static char reverb_defined[AWE_CHORUS_NUMBERS]; +static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = { +{{ /* room 1 */ + 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, + 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 2 */ + 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* room 3 */ + 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, + 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, + 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, +}}, +{{ /* hall 1 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, + 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, + 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, +}}, +{{ /* hall 2 */ + 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, + 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, + 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* plate */ + 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, + 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, + 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, + 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, +}}, +{{ /* delay */ + 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +{{ /* panning delay */ + 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, + 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, + 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, + 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, +}}, +}; + +static struct ReverbCmdPair { + unsigned short cmd, port; +} reverb_cmds[28] = { + {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)}, + {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)}, + {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)}, + {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)}, + {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)}, + {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)}, + {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, +}; + +static int +awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count) +{ + if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) { + printk("AWE32 Error: illegal reverb mode %d for uploading\n", patch->optarg); + return RET_ERROR(EINVAL); + } + if (count < sizeof(awe_reverb_fx_rec)) { + printk("AWE32 Error: too short reverb fx parameters\n"); + return RET_ERROR(EINVAL); + } + COPY_FROM_USER(&reverb_parm[patch->optarg], addr, AWE_PATCH_INFO_SIZE, + sizeof(awe_reverb_fx_rec)); + reverb_defined[patch->optarg] = TRUE; + return 0; +} + +static void +awe_set_reverb_mode(int effect) +{ + int i; + if (effect < 0 || effect >= AWE_REVERB_NUMBERS || + (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect])) + return; + for (i = 0; i < 28; i++) + awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port, + reverb_parm[effect].parms[i]); + reverb_mode = effect; +} + +/*================================================================ + * treble/bass equalizer control + *================================================================*/ + +static unsigned short bass_parm[12][3] = { + {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ + {0xD25B, 0xD35B, 0x0000}, /* -8 */ + {0xD24C, 0xD34C, 0x0000}, /* -6 */ + {0xD23D, 0xD33D, 0x0000}, /* -4 */ + {0xD21F, 0xD31F, 0x0000}, /* -2 */ + {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ + {0xC219, 0xC319, 0x0001}, /* +2 */ + {0xC22A, 0xC32A, 0x0001}, /* +4 */ + {0xC24C, 0xC34C, 0x0001}, /* +6 */ + {0xC26E, 0xC36E, 0x0001}, /* +8 */ + {0xC248, 0xC348, 0x0002}, /* +10 */ + {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ +}; + +static unsigned short treble_parm[12][9] = { + {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ + {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ + {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, + {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */ +}; + + +/* + * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] + */ +static void +awe_equalizer(int bass, int treble) +{ + unsigned short w; + + if (bass < 0 || bass > 11 || treble < 0 || treble > 11) + return; + awe_bass_level = bass; + awe_treble_level = treble; + awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]); + awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]); + awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]); + awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]); + awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]); + awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]); + awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]); + awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]); + awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]); + awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]); + w = bass_parm[bass][2] + treble_parm[treble][8]; + awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262)); + awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362)); +} + + +#endif /* CONFIG_AWE32_SYNTH */ + +#ifdef MODULE +int init_module(void) +{ + attach_awe(); + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + unload_awe(); + SOUND_LOCK_END; +} +#endif diff --git a/drivers/sound/lowlevel/init.c b/drivers/sound/lowlevel/init.c deleted file mode 100644 index 3adb52aa99c5..000000000000 --- a/drivers/sound/lowlevel/init.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * lowlevel/init.c - Calls initialization code for configured drivers. - */ - -#include - -#ifdef CONFIG_LOWLEVEL_SOUND -extern int attach_aci(void); -extern void unload_aci(void); - -void -sound_init_lowlevel_drivers(void) -{ -#ifdef CONFIG_ACI_MIXER - attach_aci(); -#endif -} - -void -sound_unload_lowlevel_drivers(void) -{ -#ifdef CONFIG_ACI_MIXER - unload_aci(); -#endif -} -#endif diff --git a/drivers/sound/lowlevel/lowlevel.h b/drivers/sound/lowlevel/lowlevel.h new file mode 100644 index 000000000000..bb0f6c7d19e7 --- /dev/null +++ b/drivers/sound/lowlevel/lowlevel.h @@ -0,0 +1,5 @@ +#ifdef LOWLEVEL_MODULE +#define MODVERSIONS +#include +#include "manual_config.h" +#endif diff --git a/drivers/sound/lowlevel/soundlow.c b/drivers/sound/lowlevel/soundlow.c new file mode 100644 index 000000000000..69530736736a --- /dev/null +++ b/drivers/sound/lowlevel/soundlow.c @@ -0,0 +1,80 @@ +/* + * lowlevel/init.c - Calls initialization code for configured drivers. + */ + +#include "lowlevel.h" +#include +#include +#include "../soundvers.h" + +#ifdef LOWLEVEL_MODULE +char *lowlevel_version = SOUND_VERSION_STRING; +#endif + +extern int attach_aci(void); +extern void unload_aci(void); +extern int attach_awe(void); +extern void unload_awe(void); +extern int init_aedsp16(void); +extern void uninit_aedsp16(void); + +/* + * There are two places where you can insert initialization calls of + * low level drivers. sound_init_lowlevel_drivers() is called after + * the sound driver has been initialized (the normal case) + * while sound_preinit_lowlevel_drivers() is called before that. + */ +void +sound_preinit_lowlevel_drivers(void) +{ +#if defined(CONFIG_AEDSP16) && !defined(MODULE) + init_aedsp16(); +#endif +} + +void +sound_init_lowlevel_drivers(void) +{ +#ifdef CONFIG_ACI_MIXER + attach_aci(); +#endif + +#ifdef CONFIG_AWE32_SYNTH + attach_awe(); +#endif +} + +void +sound_unload_lowlevel_drivers(void) +{ +#ifdef CONFIG_ACI_MIXER + unload_aci(); +#endif + +#ifdef CONFIG_AWE32_SYNTH + unload_awe(); +#endif + +#ifdef CONFIG_AEDSP16 + uninit_aedsp16(); +#endif + +} + +struct symbol_table lowlevel_syms= +{ +#include + X(sound_init_lowlevel_drivers), + X(sound_unload_lowlevel_drivers), + X(sound_preinit_lowlevel_drivers), +#include +}; + +int init_module(void) +{ + return 0; +} + +void cleanup_module(void) +{ +} diff --git a/drivers/sound/mad16.c b/drivers/sound/mad16.c index 2191c8fbd149..66b35cb558c5 100644 --- a/drivers/sound/mad16.c +++ b/drivers/sound/mad16.c @@ -1,12 +1,12 @@ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 - +#include /* * sound/mad16.c * @@ -16,6 +16,7 @@ * OAK OTI-601D Mozart * OPTi 82C929 MAD16 Pro * OPTi 82C930 + * OPTi 82C924 (in non PnP mode) * * These audio interface chips don't produce sound themselves. They just * connect some other components (OPL-[234] and a WSS compatible codec) @@ -47,7 +48,7 @@ * * CD-ROM port: 0x00=340, 0x40=330, 0x80=360 or 0xc0=320 * OPL4 select: 0x20=OPL4, 0x00=OPL3 - * CD-ROM irq: 0x00=disabled, 0x04=IRQ5, 0x08=IRQ7, 0x0a=IRQ3, 0x10=IRQ9, + * CD-ROM irq: 0x00=disabled, 0x04=IRQ5, 0x08=IRQ7, 0x0c=IRQ3, 0x10=IRQ9, * 0x14=IRQ10 and 0x18=IRQ11. * * CD-ROM DMA (Sony or Panasonic): 0x00=DMA3, 0x01=DMA2, 0x02=DMA1 or 0x03=disabled @@ -55,11 +56,25 @@ * CD-ROM DMA (Mitsumi or IDE): 0x00=DMA5, 0x01=DMA6, 0x02=DMA7 or 0x03=disabled * * For use with sbpcd, address 0x340, set MAD16_CDSEL to 0x03 or 0x23. + * + * Changes + * + * Alan Cox Clean up, added module selections. */ #include "sound_config.h" +#include "soundmodule.h" + +#ifdef MODULE +#define MAD16_CDSEL mad16_cdsel +#define MAD16_CONF mad16_conf + +static int mad16_conf; +static int mad16_cdsel; + +#endif -#if defined(CONFIG_MAD16) +#ifdef CONFIG_MAD16 #include "sb.h" @@ -69,6 +84,7 @@ static int already_initialized = 0; #define MOZART 2 #define C929 3 #define C930 4 +#define C924 5 /* * Registers @@ -100,676 +116,919 @@ static int already_initialized = 0; static int board_type = C928; static int *mad16_osp; +static int c931_detected; /* minor diferences from C930 */ #ifndef DDB #define DDB(x) #endif -static unsigned char -mad_read (int port) +static unsigned char mad_read(int port) { - unsigned long flags; - unsigned char tmp; - - save_flags (flags); - cli (); - - switch (board_type) /* Output password */ - { - case C928: - case MOZART: - outb (0xE2, PASSWD_REG); - break; - - case C929: - outb (0xE3, PASSWD_REG); - break; - - case C930: - /* outb( 0xE4, PASSWD_REG); */ - break; - } - - if (board_type == C930) - { - outb (port - MC0_PORT, 0xe0e); /* Write to index reg */ - tmp = inb (0xe0f); /* Read from data reg */ - } - else - tmp = inb (port); - restore_flags (flags); - - return tmp; -} + unsigned long flags; + unsigned char tmp; -static void -mad_write (int port, int value) -{ - unsigned long flags; - - save_flags (flags); - cli (); - - switch (board_type) /* Output password */ - { - case C928: - case MOZART: - outb (0xE2, PASSWD_REG); - break; - - case C929: - outb (0xE3, PASSWD_REG); - break; - - case C930: - /* outb( 0xE4, PASSWD_REG); */ - break; - } - - if (board_type == C930) - { - outb (port - MC0_PORT, 0xe0e); /* Write to index reg */ - outb ((unsigned char) (value & 0xff), 0xe0f); - } - else - outb ((unsigned char) (value & 0xff), port); - restore_flags (flags); -} + save_flags(flags); + cli(); -static int -detect_c930 (void) -{ - unsigned char tmp = mad_read (MC1_PORT); - - if ((tmp & 0x06) != 0x06) - { - DDB (printk ("Wrong C930 signature (%x)\n", tmp)); - /* return 0; */ - } - - mad_write (MC1_PORT, 0); - - if (mad_read (MC1_PORT) != 0x06) - { - DDB (printk ("Wrong C930 signature2 (%x)\n", tmp)); - /* return 0; */ - } - - mad_write (MC1_PORT, tmp); /* Restore bits */ - - mad_write (MC7_PORT, 0); - if ((tmp = mad_read (MC7_PORT)) != 0) - { - DDB (printk ("MC7 not writable (%x)\n", tmp)); - return 0; - } - - mad_write (MC7_PORT, 0xcb); - if ((tmp = mad_read (MC7_PORT)) != 0xcb) - { - DDB (printk ("MC7 not writable2 (%x)\n", tmp)); - return 0; - } - - return 1; + switch (board_type) /* Output password */ + { + case C928: + case MOZART: + outb((0xE2), PASSWD_REG); + break; + + case C929: + outb((0xE3), PASSWD_REG); + break; + + case C930: + /* outb(( 0xE4), PASSWD_REG); */ + break; + + case C924: + outb((0xE5), PASSWD_REG); + break; + } + + if (board_type == C930) + { + outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ + tmp = inb(0xe0f); /* Read from data reg */ + } + else + tmp = inb(port); + restore_flags(flags); + + return tmp; } -static int -detect_mad16 (void) +static void mad_write(int port, int value) { - unsigned char tmp, tmp2; - int i; + unsigned long flags; -/* - * Check that reading a register doesn't return bus float (0xff) - * when the card is accessed using password. This may fail in case - * the card is in low power mode. Normally at least the power saving mode - * bit should be 0. - */ - if ((tmp = mad_read (MC1_PORT)) == 0xff) - { - DDB (printk ("MC1_PORT returned 0xff\n")); - return 0; - } + save_flags(flags); + cli(); - for (i = 0xf8d; i <= 0xf98; i++) - DDB (printk ("Port %0x (init value) = %0x\n", i, mad_read (i))); + switch (board_type) /* Output password */ + { + case C928: + case MOZART: + outb((0xE2), PASSWD_REG); + break; + + case C929: + outb((0xE3), PASSWD_REG); + break; + + case C930: + /* outb(( 0xE4), PASSWD_REG); */ + break; + + case C924: + outb((0xE5), PASSWD_REG); + break; + } - if (board_type == C930) - return detect_c930 (); -/* - * Now check that the gate is closed on first I/O after writing - * the password. (This is how a MAD16 compatible card works). - */ + if (board_type == C930) + { + outb((port - MC0_PORT), 0xe0e); /* Write to index reg */ + outb(((unsigned char) (value & 0xff)), 0xe0f); + } + else + outb(((unsigned char) (value & 0xff)), port); + restore_flags(flags); +} - if ((tmp2 = inb (MC1_PORT)) == tmp) /* It didn't close */ - { - DDB (printk ("MC1_PORT didn't close after read (0x%02x)\n", tmp2)); - return 0; - } +static int detect_c930(void) +{ + unsigned char tmp = mad_read(MC1_PORT); - mad_write (MC1_PORT, tmp ^ 0x80); /* Toggle a bit */ - if ((tmp2 = mad_read (MC1_PORT)) != (tmp ^ 0x80)) /* Compare the bit */ - { - mad_write (MC1_PORT, tmp); /* Restore */ - DDB (printk ("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2)); - return 0; - } + if ((tmp & 0x06) != 0x06) + { + DDB(printk("Wrong C930 signature (%x)\n", tmp)); + /* return 0; */ + } + mad_write(MC1_PORT, 0); - mad_write (MC1_PORT, tmp); /* Restore */ - return 1; /* Bingo */ + if (mad_read(MC1_PORT) != 0x06) + { + DDB(printk("Wrong C930 signature2 (%x)\n", tmp)); + /* return 0; */ + } + mad_write(MC1_PORT, tmp); /* Restore bits */ + mad_write(MC7_PORT, 0); + if ((tmp = mad_read(MC7_PORT)) != 0) + { + DDB(printk("MC7 not writable (%x)\n", tmp)); + return 0; + } + mad_write(MC7_PORT, 0xcb); + if ((tmp = mad_read(MC7_PORT)) != 0xcb) + { + DDB(printk("MC7 not writable2 (%x)\n", tmp)); + return 0; + } + + tmp = mad_read(MC0_PORT+18); + if (tmp == 0xff || tmp == 0x00) + return 1; + /* We probably have a C931 */ + DDB(printk("Detected C931 config=0x%02x\n", tmp)); + c931_detected = 1; + + /* + * We cannot configure the chip if it is in PnP mode. + * If we have a CSN assigned (bit 8 in MC13) we first try + * a software reset, then a software power off, finally + * Clearing PnP mode. The last option is not + * Bit 8 in MC13 + */ + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Software reset */ + mad_write(MC9_PORT, 0x02); + mad_write(MC9_PORT, 0x00); + + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Power off, and on again */ + mad_write(MC9_PORT, 0xc2); + mad_write(MC9_PORT, 0xc0); + + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Force off PnP mode, This is not recommended because + * the PnP bios will not recognize the chip on the next + * warm boot and may assignd different resources to other + * PnP/PCI cards. + */ + mad_write(MC0_PORT+17, 0x04); + return 1; } -static int -wss_init (struct address_info *hw_config) +static int detect_mad16(void) { - int ad_flags = 0; + unsigned char tmp, tmp2; + int i; -/* - * Verify the WSS parameters - */ + /* + * Check that reading a register doesn't return bus float (0xff) + * when the card is accessed using password. This may fail in case + * the card is in low power mode. Normally at least the power saving mode + * bit should be 0. + */ + + if ((tmp = mad_read(MC1_PORT)) == 0xff) + { + DDB(printk("MC1_PORT returned 0xff\n")); + return 0; + } + for (i = 0xf8d; i <= 0xf98; i++) + DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i))); + + if (board_type == C930) + return detect_c930(); + + /* + * Now check that the gate is closed on first I/O after writing + * the password. (This is how a MAD16 compatible card works). + */ - if (check_region (hw_config->io_base, 8)) - { - printk ("MSS: I/O port conflict\n"); - return 0; - } - - if (!ad1848_detect (hw_config->io_base + 4, &ad_flags, mad16_osp)) - 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. - */ - - if ((inb (hw_config->io_base + 3) & 0x3f) != 0x04 && - (inb (hw_config->io_base + 3) & 0x3f) != 0x00) - { - DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n", - hw_config->io_base, inb (hw_config->io_base + 3))); - return 0; - } - - if (hw_config->irq > 11) - { - printk ("MSS: Bad IRQ %d\n", hw_config->irq); - return 0; - } - - if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) - { - printk ("MSS: Bad DMA %d\n", hw_config->dma); - return 0; - } - - /* - * Check that DMA0 is not in use with a 8 bit board. - */ - - if (hw_config->dma == 0 && inb (hw_config->io_base + 3) & 0x80) - { - printk ("MSS: Can't use DMA0 with a 8 bit card/slot\n"); - return 0; - } - - if (hw_config->irq > 7 && hw_config->irq != 9 && inb (hw_config->io_base + 3) & 0x80) - { - printk ("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); - } - - return 1; + if ((tmp2 = inb(MC1_PORT)) == tmp) /* It didn't close */ + { + DDB(printk("MC1_PORT didn't close after read (0x%02x)\n", tmp2)); + return 0; + } + mad_write(MC1_PORT, tmp ^ 0x80); /* Toggle a bit */ + if ((tmp2 = mad_read(MC1_PORT)) != (tmp ^ 0x80)) /* Compare the bit */ + { + mad_write(MC1_PORT, tmp); /* Restore */ + DDB(printk("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2)); + return 0; + } + mad_write(MC1_PORT, tmp); /* Restore */ + return 1; /* Bingo */ } -static int -init_c930 (struct address_info *hw_config) +static int wss_init(struct address_info *hw_config) { - unsigned char cfg; - - cfg = (mad_read (MC1_PORT) & ~0x30); - /* mad_write(MC1_PORT, 0); */ - - switch (hw_config->io_base) - { - case 0x530: - cfg |= 0x00; - break; - case 0xe80: - cfg |= 0x10; - break; - case 0xf40: - cfg |= 0x20; - break; - case 0x604: - cfg |= 0x30; - break; - - default: - printk ("MAD16: Invalid codec port %x\n", hw_config->io_base); - return 0; - } - mad_write (MC1_PORT, cfg); - - /* MC2 is CD configuration. Don't touch it. */ - - mad_write (MC3_PORT, 0); /* Disable SB mode IRQ and DMA */ - - mad_write (MC4_PORT, 0x52); /* ??? */ - mad_write (MC5_PORT, 0x3D); /* Init it into mode2 */ - mad_write (MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ - mad_write (MC7_PORT, 0xCB); - mad_write (MC10_PORT, 0x11); - - if (!wss_init (hw_config)) - return 0; + int ad_flags = 0; -/* - * A temporary kludge which drops the device back to mode1. - * This removes problems with interrupts but disables full duplex. - * A better solution should be introduced later. - */ - mad_write (MC5_PORT, 0x1D); /* Disable mode2 */ - return wss_init (hw_config); + /* + * Verify the WSS parameters + */ + + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "MSS: I/O port conflict\n"); + return 0; + } + if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) + 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. + */ + + if ((inb(hw_config->io_base + 3) & 0x3f) != 0x04 && + (inb(hw_config->io_base + 3) & 0x3f) != 0x00) + { + DDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb(hw_config->io_base + 3))); + return 0; + } + if (hw_config->irq > 11) + { + printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) + { + printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma); + return 0; + } + /* + * Check that DMA0 is not in use with a 8 bit board. + */ + + if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + { + printk("MSS: Can't use DMA0 with a 8 bit card/slot\n"); + return 0; + } + if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); + return 1; } -int -probe_mad16 (struct address_info *hw_config) +static int init_c930(struct address_info *hw_config) { - int i; - static int valid_ports[] = - {0x530, 0xe80, 0xf40, 0x604}; - unsigned char tmp; - unsigned char cs4231_mode = 0; + unsigned char cfg = 0; + +#ifdef MAD16_CONF + cfg |= (0x0f & MAD16_CONF); +#endif - int ad_flags = 0; + if(c931_detected) + { + /* Bit 0 has reversd meaning. Bits 1 and 2 sese + reversed on write. + Support only IDE cdrom. IDE port programmed + somewhere else. */ + cfg = (cfg & 0x09) ^ 0x07; + } - if (already_initialized) - return 0; + switch (hw_config->io_base) + { + case 0x530: + cfg |= 0x00; + break; + case 0xe80: + cfg |= 0x10; + break; + case 0xf40: + cfg |= 0x20; + break; + case 0x604: + cfg |= 0x30; + break; + default: + printk(KERN_ERR "MAD16: Invalid codec port %x\n", hw_config->io_base); + return 0; + } + mad_write(MC1_PORT, cfg); - mad16_osp = hw_config->osp; -/* - * Check that all ports return 0xff (bus float) when no password - * is written to the password register. - */ + /* MC2 is CD configuration. Don't touch it. */ - DDB (printk ("--- Detecting MAD16 / Mozart ---\n")); + mad_write(MC3_PORT, 0); /* Disable SB mode IRQ and DMA */ + /* bit 2 of MC4 reverses it's meaning between the C930 + and the C931. */ + cfg = c931_detected ? 0x04 : 0x00; +#ifdef MAD16_CDSEL + if(MAD16_CDSEL & 0x20) + mad_write(MC4_PORT, 0x62|cfg); /* opl4 */ + else + mad_write(MC4_PORT, 0x52|cfg); /* opl3 */ +#else + mad_write(MC4_PORT, 0x52|cfg); +#endif + mad_write(MC5_PORT, 0x3C); /* Init it into mode2 */ + mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ + mad_write(MC7_PORT, 0xCB); + mad_write(MC10_PORT, 0x11); -/* - * Then try to detect with the old password - */ - board_type = C928; + return wss_init(hw_config); +} - DDB (printk ("Detect using password = 0xE2\n")); +static int chip_detect(void) +{ + int i; - if (!detect_mad16 ()) /* No luck. Try different model */ - { - board_type = C929; + /* + * Then try to detect with the old password + */ + board_type = C924; - DDB (printk ("Detect using password = 0xE3\n")); + DDB(printk("Detect using password = 0xE5\n")); - if (!detect_mad16 ()) + if (!detect_mad16()) /* No luck. Try different model */ { - if (inb (PASSWD_REG) != 0xff) - return 0; + board_type = C928; + + DDB(printk("Detect using password = 0xE2\n")); + + if (!detect_mad16()) + { + board_type = C929; + + DDB(printk("Detect using password = 0xE3\n")); + + if (!detect_mad16()) + { + if (inb(PASSWD_REG) != 0xff) + return 0; + + /* + * First relocate MC# registers to 0xe0e/0xe0f, disable password + */ + + outb((0xE4), PASSWD_REG); + outb((0x80), PASSWD_REG); + + board_type = C930; + + DDB(printk("Detect using password = 0xE4\n")); + + for (i = 0xf8d; i <= 0xf93; i++) + DDB(printk("port %03x = %02x\n", i, mad_read(i))); + + if (!detect_mad16()) + return 0; + + DDB(printk("mad16.c: 82C930 detected\n")); + } + else + { + DDB(printk("mad16.c: 82C929 detected\n")); + } + } + else + { + unsigned char model; + + if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) + { + DDB(printk("mad16.c: Mozart detected\n")); + board_type = MOZART; + } + else + { + DDB(printk("mad16.c: 82C928 detected???\n")); + board_type = C928; + } + } + } + return 1; +} -/* - * First relocate MC# registers to 0xe0e/0xe0f, disable password - */ +int probe_mad16(struct address_info *hw_config) +{ + int i; + static int valid_ports[] = + { + 0x530, 0xe80, 0xf40, 0x604 + }; + unsigned char tmp; + unsigned char cs4231_mode = 0; - outb (0xE4, PASSWD_REG); - outb (0x80, PASSWD_REG); + int ad_flags = 0; - board_type = C930; + if (already_initialized) + return 0; - DDB (printk ("Detect using password = 0xE4\n")); + mad16_osp = hw_config->osp; - for (i = 0xf8d; i <= 0xf93; i++) - DDB (printk ("port %03x = %02x\n", i, mad_read (i))); + /* + * Check that all ports return 0xff (bus float) when no password + * is written to the password register. + */ - if (!detect_mad16 ()) - return 0; + DDB(printk("--- Detecting MAD16 / Mozart ---\n")); + if (!chip_detect()) + return 0; - DDB (printk ("mad16.c: 82C930 detected\n")); - return init_c930 (hw_config); - } - else - { - DDB (printk ("mad16.c: 82C929 detected\n")); - } - } - else - { - unsigned char model; + if (board_type == C930) + return init_c930(hw_config); - if (((model = mad_read (MC3_PORT)) & 0x03) == 0x03) - { - DDB (printk ("mad16.c: Mozart detected\n")); - board_type = MOZART; - } - else - { - DDB (printk ("mad16.c: 82C928 detected???\n")); - board_type = C928; - } - } - for (i = 0xf8d; i <= 0xf93; i++) - DDB (printk ("port %03x = %02x\n", i, mad_read (i))); + for (i = 0xf8d; i <= 0xf93; i++) + DDB(printk("port %03x = %02x\n", i, mad_read(i))); /* * Set the WSS address */ - tmp = 0x80; /* Enable WSS, Disable SB */ + tmp = (mad_read(MC1_PORT) & 0x0f) | 0x80; /* Enable WSS, Disable SB */ - for (i = 0; i < 5; i++) - { - if (i > 3) /* Not a valid port */ + for (i = 0; i < 5; i++) { - printk ("MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base); - return 0; + if (i > 3) /* Not a valid port */ + { + printk(KERN_ERR "MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base); + return 0; + } + if (valid_ports[i] == hw_config->io_base) + { + tmp |= i << 4; /* WSS port select bits */ + break; + } } - if (valid_ports[i] == hw_config->io_base) - { - tmp |= i << 4; /* WSS port select bits */ - break; - } - } + /* + * Set optional CD-ROM and joystick settings. + */ -/* - * Set optional CD-ROM and joystick settings. - */ - -#ifdef MAD16_CONF - tmp |= ((MAD16_CONF) & 0x0f); /* CD-ROM and joystick bits */ + tmp &= ~0x0f; +#if defined(MAD16_CONF) + tmp |= ((MAD16_CONF) & 0x0f); /* CD-ROM and joystick bits */ #endif - mad_write (MC1_PORT, tmp); + mad_write(MC1_PORT, tmp); #if defined(MAD16_CONF) && defined(MAD16_CDSEL) - tmp = MAD16_CDSEL; + tmp = MAD16_CDSEL; #else - tmp = 0x03; + tmp = mad_read(MC2_PORT); #endif #ifdef MAD16_OPL4 - tmp |= 0x20; /* Enable OPL4 access */ + tmp |= 0x20; /* Enable OPL4 access */ #endif - mad_write (MC2_PORT, tmp); - mad_write (MC3_PORT, 0xf0); /* Disable SB */ + mad_write(MC2_PORT, tmp); + mad_write(MC3_PORT, 0xf0); /* Disable SB */ - if (!ad1848_detect (hw_config->io_base + 4, &ad_flags, mad16_osp)) - return 0; + if (board_type == C924) /* Specific C924 init values */ + { + mad_write(MC4_PORT, 0xA0); + mad_write(MC5_PORT, 0x05); + mad_write(MC6_PORT, 0x03); + } + if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) + return 0; - if (ad_flags & (AD_F_CS4231 | AD_F_CS4248)) - cs4231_mode = 0x02; /* CS4248/CS4231 sync delay switch */ + if (ad_flags & (AD_F_CS4231 | AD_F_CS4248)) + cs4231_mode = 0x02; /* CS4248/CS4231 sync delay switch */ - if (board_type == C929) - { - mad_write (MC4_PORT, 0xa2); - mad_write (MC5_PORT, 0xA5 | cs4231_mode); - mad_write (MC6_PORT, 0x03); /* Disable MPU401 */ - } - else - { - mad_write (MC4_PORT, 0x02); - mad_write (MC5_PORT, 0x30 | cs4231_mode); - } + if (board_type == C929) + { + mad_write(MC4_PORT, 0xa2); + mad_write(MC5_PORT, 0xA5 | cs4231_mode); + mad_write(MC6_PORT, 0x03); /* Disable MPU401 */ + } + else + { + mad_write(MC4_PORT, 0x02); + mad_write(MC5_PORT, 0x30 | cs4231_mode); + } - for (i = 0xf8d; i <= 0xf93; i++) - DDB (printk ("port %03x after init = %02x\n", i, mad_read (i))); + for (i = 0xf8d; i <= 0xf93; i++) + DDB(printk("port %03x after init = %02x\n", i, mad_read(i))); - wss_init (hw_config); + wss_init(hw_config); - return 1; + return 1; } -void -attach_mad16 (struct address_info *hw_config) +void attach_mad16(struct address_info *hw_config) { - static char interrupt_bits[12] = - { - -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 - }; - char bits; - - static char dma_bits[4] = - { - 1, 2, 0, 3 - }; - - int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; - int ad_flags = 0, dma = hw_config->dma, dma2 = hw_config->dma2; - unsigned char dma2_bit = 0; + static char interrupt_bits[12] = { + -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 + }; + char bits; - already_initialized = 1; + static char dma_bits[4] = { + 1, 2, 0, 3 + }; - if (!ad1848_detect (hw_config->io_base + 4, &ad_flags, mad16_osp)) - return; + int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; + int ad_flags = 0, dma = hw_config->dma, dma2 = hw_config->dma2; + unsigned char dma2_bit = 0; - /* - * Set the IRQ and DMA addresses. - */ + already_initialized = 1; - bits = interrupt_bits[hw_config->irq]; - if (bits == -1) - return; + if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) + return; - outb (bits | 0x40, config_port); - if ((inb (version_port) & 0x40) == 0) - printk ("[IRQ Conflict?]"); + /* + * Set the IRQ and DMA addresses. + */ + + if (board_type == C930) + interrupt_bits[5] = 0x28; /* Also IRQ5 is possible on C930 */ -/* - * Handle the capture DMA channel - */ + bits = interrupt_bits[hw_config->irq]; + if (bits == -1) + return; - if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma) - { - if (!((dma == 0 && dma2 == 1) || - (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0))) - { /* Unsupported combination. Try to swap channels */ - int tmp = dma; + outb((bits | 0x40), config_port); + if ((inb(version_port) & 0x40) == 0) + printk(KERN_ERR "[IRQ Conflict?]\n"); - dma = dma2; - dma2 = tmp; - } + /* + * Handle the capture DMA channel + */ - if ((dma == 0 && dma2 == 1) || - (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0)) - { - dma2_bit = 0x04; /* Enable capture DMA */ - } - else + if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma) { - printk ("MAD16: Invalid capture DMA\n"); - dma2 = dma; + if (!((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0))) + { /* Unsupported combination. Try to swap channels */ + int tmp = dma; + + dma = dma2; + dma2 = tmp; + } + if ((dma == 0 && dma2 == 1) || (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0)) + { + dma2_bit = 0x04; /* Enable capture DMA */ + } + else + { + printk("MAD16: Invalid capture DMA\n"); + dma2 = dma; + } } - } - else - dma2 = dma; + else dma2 = dma; - outb (bits | dma_bits[dma] | dma2_bit, config_port); /* Write IRQ+DMA setup */ + outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ - ad1848_init ("MAD16 WSS", hw_config->io_base + 4, - hw_config->irq, - dma, - dma2, 0, - hw_config->osp); - request_region (hw_config->io_base, 4, "MAD16 WSS config"); + hw_config->slots[0] = ad1848_init("MAD16 WSS", hw_config->io_base + 4, + hw_config->irq, + dma, + dma2, 0, + hw_config->osp); + request_region(hw_config->io_base, 4, "MAD16 WSS config"); } -void -attach_mad16_mpu (struct address_info *hw_config) +void attach_mad16_mpu(struct address_info *hw_config) { - if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ - { -#ifdef CONFIG_MIDI + if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ + { +#if defined(CONFIG_MIDI) && defined(CONFIG_MAD16_OLDCARD) - if (mad_read (MC1_PORT) & 0x20) - hw_config->io_base = 0x240; - else - hw_config->io_base = 0x220; + if (mad_read(MC1_PORT) & 0x20) + hw_config->io_base = 0x240; + else + hw_config->io_base = 0x220; - hw_config->name = "Mad16/Mozart"; - sb_dsp_init (hw_config); + hw_config->name = "Mad16/Mozart"; + sb_dsp_init(hw_config); #endif - return; - } - + return; + } #if defined(CONFIG_UART401) && defined(CONFIG_MIDI) - if (!already_initialized) - return; + if (!already_initialized) + return; - hw_config->driver_use_1 = SB_MIDI_ONLY; - hw_config->name = "Mad16/Mozart"; - attach_uart401 (hw_config); + hw_config->driver_use_1 = SB_MIDI_ONLY; + hw_config->name = "Mad16/Mozart"; + attach_uart401(hw_config); #endif } -int -probe_mad16_mpu (struct address_info *hw_config) +int probe_mad16_mpu(struct address_info *hw_config) { #if defined(CONFIG_UART401) && defined(CONFIG_MIDI) - static int mpu_attached = 0; - static int valid_ports[] = - {0x330, 0x320, 0x310, 0x300}; - static short valid_irqs[] = - {9, 10, 5, 7}; - unsigned char tmp; - - int i; /* A variable with secret power */ - - if (!already_initialized) /* The MSS port must be initialized first */ - return 0; - - if (mpu_attached) /* Don't let them call this twice */ - return 0; - mpu_attached = 1; - - if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ - { - -#ifdef CONFIG_MIDI - unsigned char tmp; - - tmp = mad_read (MC3_PORT); - - /* - * MAD16 SB base is defined by the WSS base. It cannot be changed - * alone. - * Ignore configured I/O base. Use the active setting. - */ - - if (mad_read (MC1_PORT) & 0x20) - hw_config->io_base = 0x240; - else - hw_config->io_base = 0x220; - - switch (hw_config->irq) - { - case 5: - tmp = (tmp & 0x3f) | 0x80; - break; - case 7: - tmp = (tmp & 0x3f); - break; - case 11: - tmp = (tmp & 0x3f) | 0x40; - break; - default: - printk ("mad16/Mozart: Invalid MIDI IRQ\n"); - return 0; - } - - mad_write (MC3_PORT, tmp | 0x04); - hw_config->driver_use_1 = SB_MIDI_ONLY; - return sb_dsp_detect (hw_config); + static int mpu_attached = 0; + static int valid_ports[] = { + 0x330, 0x320, 0x310, 0x300 + }; + + static short valid_irqs[] = {9, 10, 5, 7}; + unsigned char tmp; + int i; /* A variable with secret power */ + + if (!already_initialized) /* The MSS port must be initialized first */ + return 0; + + if (mpu_attached) /* Don't let them call this twice */ + return 0; + mpu_attached = 1; + + if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ + { + +#if defined(CONFIG_MIDI) && defined(CONFIG_MAD16_OLDCARD) + unsigned char tmp; + + tmp = mad_read(MC3_PORT); + + /* + * MAD16 SB base is defined by the WSS base. It cannot be changed + * alone. + * Ignore configured I/O base. Use the active setting. + */ + + if (mad_read(MC1_PORT) & 0x20) + hw_config->io_base = 0x240; + else + hw_config->io_base = 0x220; + + switch (hw_config->irq) + { + case 5: + tmp = (tmp & 0x3f) | 0x80; + break; + case 7: + tmp = (tmp & 0x3f); + break; + case 11: + tmp = (tmp & 0x3f) | 0x40; + break; + default: + printk(KERN_ERR "mad16/Mozart: Invalid MIDI IRQ\n"); + return 0; + } + + mad_write(MC3_PORT, tmp | 0x04); + hw_config->driver_use_1 = SB_MIDI_ONLY; + return sb_dsp_detect(hw_config); #else - return 0; + return 0; #endif - } - - tmp = mad_read (MC6_PORT) & 0x83; - tmp |= 0x80; /* MPU-401 enable */ + } + tmp = mad_read(MC6_PORT) & 0x83; + tmp |= 0x80; /* MPU-401 enable */ /* * Set the MPU base bits */ - for (i = 0; i < 5; i++) - { - if (i > 3) /* Out of array bounds */ + for (i = 0; i < 5; i++) { - printk ("MAD16 / Mozart: Invalid MIDI port 0x%x\n", hw_config->io_base); - return 0; + if (i > 3) /* Out of array bounds */ + { + printk(KERN_ERR "MAD16 / Mozart: Invalid MIDI port 0x%x\n", hw_config->io_base); + return 0; + } + if (valid_ports[i] == hw_config->io_base) + { + tmp |= i << 5; + break; + } } - if (valid_ports[i] == hw_config->io_base) - { - tmp |= i << 5; - break; - } - } - /* * Set the MPU IRQ bits */ - for (i = 0; i < 5; i++) - { - if (i > 3) /* Out of array bounds */ - { - printk ("MAD16 / Mozart: Invalid MIDI IRQ %d\n", hw_config->irq); - return 0; - } - - if (valid_irqs[i] == hw_config->irq) + for (i = 0; i < 5; i++) { - tmp |= i << 3; - break; + if (i > 3) /* Out of array bounds */ + { + printk(KERN_ERR "MAD16 / Mozart: Invalid MIDI IRQ %d\n", hw_config->irq); + return 0; + } + if (valid_irqs[i] == hw_config->irq) + { + tmp |= i << 3; + break; + } } - } - mad_write (MC6_PORT, tmp); /* Write MPU401 config */ + mad_write(MC6_PORT, tmp); /* Write MPU401 config */ - return probe_uart401 (hw_config); + return probe_uart401(hw_config); #else - return 0; + return 0; #endif } -void -unload_mad16 (struct address_info *hw_config) +void unload_mad16(struct address_info *hw_config) { - ad1848_unload (hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - hw_config->dma2, 0); - release_region (hw_config->io_base, 4); - + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0); + release_region(hw_config->io_base, 4); + sound_unload_audiodev(hw_config->slots[0]); } void -unload_mad16_mpu (struct address_info *hw_config) +unload_mad16_mpu(struct address_info *hw_config) { -#ifdef CONFIG_MIDI - if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ - { - sb_dsp_unload (hw_config); - return; - } +#if defined(CONFIG_MIDI) && defined(CONFIG_MAD16_OLDCARD) + if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ + { + sb_dsp_unload(hw_config); + return; + } #endif -#if (defined(CONFIG_UART401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) - unload_uart401 (hw_config); +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + unload_uart401(hw_config); #endif } +#ifdef MODULE + +int mpu_io = 0; +int mpu_irq = 0; +int io = -1; +int dma = -1; +int dma16 = -1; /* Set this for modules that need it */ +int irq = -1; + +int cdtype = 0; +int cdirq = 0; +int cdport = 0x340; +int cddma = 3; +int opl4 = 0; +int joystick = 0; + +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM(io,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma16,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(cdtype,"i"); +MODULE_PARM(cdirq,"i"); +MODULE_PARM(cdport,"i"); +MODULE_PARM(cddma,"i"); +MODULE_PARM(opl4,"i"); +MODULE_PARM(joystick,"i"); + +static int found_mpu; + + +static int dma_map[2][8] = +{ + {0x03, -1, -1, -1, -1, 0x00, 0x01, 0x02}, + {0x03, -1, 0x01, 0x00, -1, -1, -1, -1} +}; + +static int irq_map[16] = +{ + 0x00, -1, -1, 0x0A, + -1, 0x04, -1, 0x08, + -1, 0x10, 0x14, 0x18, + -1, -1, -1, -1 +}; + +struct address_info config; +struct address_info config_mpu; + +int init_module(void) +{ + int dmatype = 0; + + printk(KERN_INFO "MAD16 audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + if (io == -1 || dma == -1 || irq == -1) + { + printk(KERN_ERR "I/O, DMA and irq are mandatory\n"); + return -EINVAL; + } + printk(KERN_INFO "CDROM "); + switch (cdtype) + { + case 0x00: + printk("Disabled"); + cdirq = 0; + break; + case 0x02: + printk("Sony CDU31A"); + dmatype = 2; + break; + case 0x04: + printk("Mitsumi"); + dmatype = 1; + break; + case 0x06: + printk("Panasonic Lasermate"); + dmatype = 2; + break; + case 0x08: + printk("Secondary IDE"); + dmatype = 1; + break; + case 0x0A: + printk("Primary IDE"); + dmatype = 1; + break; + default: + printk("\n"); + printk(KERN_ERR "Invalid CDROM type\n"); + return -EINVAL; + } + + if (dmatype) + { + if (cddma > 7 || cddma < 0 || dma_map[dmatype][cddma] == -1) + { + printk("\n"); + printk(KERN_ERR "Invalid CDROM DMA\n"); + return -EINVAL; + } + if (cddma) + printk(", DMA %d", cddma); + else + printk(", no DMA"); + } + if (cdtype && !cdirq) + printk(", no IRQ"); + else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1) + { + printk(", invalid IRQ (disabling)"); + cdirq = 0; + } + else printk(", IRQ %d", cdirq); + + printk(".\n"); + printk(KERN_INFO "Joystick port "); + if (joystick == 1) + printk("enabled.\n"); + else + { + joystick = 0; + printk("disabled.\n"); + } + + /* + * Build the config words + */ + + mad16_conf = (joystick ^ 1) | cdtype; + mad16_cdsel = 0; + if (opl4) + mad16_cdsel |= 0x20; + mad16_cdsel |= dma_map[dmatype][cddma]; + + if (cdtype < 0x08) + { + switch (cdport) + { + case 0x340: + mad16_cdsel |= 0x00; + break; + case 0x330: + mad16_cdsel |= 0x40; + break; + case 0x360: + mad16_cdsel |= 0x80; + break; + case 0x320: + mad16_cdsel |= 0xC0; + break; + default: + printk(KERN_ERR "Unknown CDROM I/O base %d\n", cdport); + return -EINVAL; + } + } + mad16_cdsel |= irq_map[cdirq]; + + config.io_base = io; + config.irq = irq; + config.dma = dma; + config.dma2 = dma16; + + if (!probe_mad16(&config)) + return -ENODEV; + + config_mpu.io_base = mpu_io; + config_mpu.irq = mpu_irq; + + attach_mad16(&config); + + found_mpu = probe_mad16_mpu(&config_mpu); + + if (found_mpu) + attach_mad16_mpu(&config_mpu); + + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + if (found_mpu) + unload_mad16_mpu(&config_mpu); + unload_mad16(&config); + SOUND_LOCK_END; +} + +#endif + + + /* That's all folks */ #endif diff --git a/drivers/sound/maui.c b/drivers/sound/maui.c index 1d2ac8214573..9d095811f89a 100644 --- a/drivers/sound/maui.c +++ b/drivers/sound/maui.c @@ -4,21 +4,31 @@ * The low level driver for Turtle Beach Maui and Tropez. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 General clean up, use kernel IRQ + * system + * + * Status: + * Untested */ + #include - +#include #define USE_SEQ_MACROS #define USE_SIMPLE_MACROS #include "sound_config.h" +#include "soundmodule.h" +#include "sound_firmware.h" -#if defined(CONFIG_MAUI) +#ifdef CONFIG_MAUI static int maui_base = 0x330; @@ -37,452 +47,454 @@ static int *maui_osp; #define STAT_RX_IENA 0x01 static int (*orig_load_patch) (int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) = NULL; + int offs, int count, int pmgr_flag) = NULL; #ifdef HAVE_MAUI_BOOT #include "maui_boot.h" #else static unsigned char *maui_os = NULL; -static int maui_osLen = 0; - +static int maui_osLen = 0; #endif -static wait_handle *maui_sleeper = NULL; -static volatile struct snd_wait maui_sleep_flag = -{0}; - -static int -maui_wait (int mask) +static int maui_wait(int mask) { - int i; + int i; -/* - * Perform a short initial wait without sleeping - */ + /* + * Perform a short initial wait without sleeping + */ - for (i = 0; i < 100; i++) - { - if (inb (HOST_STAT_PORT) & mask) + for (i = 0; i < 100; i++) { - return 1; + if (inb(HOST_STAT_PORT) & mask) + { + return 1; + } } - } -/* - * Wait up to 15 seconds with sleeping - */ + /* + * Wait up to 15 seconds with sleeping + */ - for (i = 0; i < 150; i++) - { - if (inb (HOST_STAT_PORT) & mask) + for (i = 0; i < 150; i++) { - return 1; + if (inb(HOST_STAT_PORT) & mask) + return 1; + current->timeout = jiffies + HZ / 10; + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->timeout = 0; + if (signal_pending(current)) + return 0; } - - - { - unsigned long tlimit; - - if (HZ / 10) - current_set_timeout (tlimit = jiffies + (HZ / 10)); - else - tlimit = (unsigned long) -1; - maui_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&maui_sleeper); - if (!(maui_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - maui_sleep_flag.flags |= WK_TIMEOUT; - } - maui_sleep_flag.flags &= ~WK_SLEEP; - }; - if (current_got_fatal_signal ()) return 0; - } - - return 0; } -static int -maui_read (void) +static int maui_read(void) { - if (maui_wait (STAT_RX_AVAIL)) - return inb (HOST_DATA_PORT); - - return -1; + if (maui_wait(STAT_RX_AVAIL)) + return inb(HOST_DATA_PORT); + return -1; } -static int -maui_write (unsigned char data) +static int maui_write(unsigned char data) { - if (maui_wait (STAT_TX_AVAIL)) - { - outb (data, HOST_DATA_PORT); - return 1; - } - printk ("Maui: Write timeout\n"); - - return 0; + if (maui_wait(STAT_TX_AVAIL)) + { + outb((data), HOST_DATA_PORT); + return 1; + } + printk(KERN_WARNING "Maui: Write timeout\n"); + return 0; } -void -mauiintr (int irq, void *dev_id, struct pt_regs *dummy) +static void mauiintr(int irq, void *dev_id, struct pt_regs *dummy) { - irq_ok = 1; + irq_ok = 1; } -static int -download_code (void) +static int download_code(void) { - int i, lines = 0; - int eol_seen = 0, done = 0; - int skip = 1; - - printk ("Code download (%d bytes): ", maui_osLen); - - for (i = 0; i < maui_osLen; i++) - { - if (maui_os[i] != '\r') - if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n'))) - { - skip = 0; - - if (maui_os[i] == '\n') - eol_seen = skip = 1; - else if (maui_os[i] == 'S') - { - if (maui_os[i + 1] == '8') - done = 1; - if (!maui_write (0xF1)) - goto failure; - if (!maui_write ('S')) - goto failure; - } - else - { - if (!maui_write (maui_os[i])) - goto failure; - } - - if (eol_seen) - { - int c = 0; - - int n; - - eol_seen = 0; - - for (n = 0; n < 2; n++) - if (maui_wait (STAT_RX_AVAIL)) - { - c = inb (HOST_DATA_PORT); - break; - } - - if (c != 0x80) - { - printk ("Download not acknowledged\n"); - return 0; - } - else if (!(lines++ % 10)) - printk ("."); - - if (done) - { - printk ("\nDownload complete\n"); - return 1; - } - } - } - } + int i, lines = 0; + int eol_seen = 0, done = 0; + int skip = 1; -failure: + printk(KERN_INFO "Code download (%d bytes): ", maui_osLen); - printk ("\nDownload failed!!!\n"); - return 0; + for (i = 0; i < maui_osLen; i++) + { + if (maui_os[i] != '\r') + { + if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n'))) + { + skip = 0; + + if (maui_os[i] == '\n') + eol_seen = skip = 1; + else if (maui_os[i] == 'S') + { + if (maui_os[i + 1] == '8') + done = 1; + if (!maui_write(0xF1)) + goto failure; + if (!maui_write('S')) + goto failure; + } + else + { + if (!maui_write(maui_os[i])) + goto failure; + } + + if (eol_seen) + { + int c = 0; + int n; + + eol_seen = 0; + + for (n = 0; n < 2; n++) + { + if (maui_wait(STAT_RX_AVAIL)) + { + c = inb(HOST_DATA_PORT); + break; + } + } + if (c != 0x80) + { + printk("Download not acknowledged\n"); + return 0; + } + else if (!(lines++ % 10)) + printk("."); + + if (done) + { + printk("\n"); + printk(KERN_INFO "Download complete\n"); + return 1; + } + } + } + } + } + +failure: + printk("\n"); + printk(KERN_ERR "Download failed!!!\n"); + return 0; } -static int -maui_init (int irq) +static int maui_init(int irq) { - int i; - unsigned char bits; - - switch (irq) - { - case 9: - bits = 0x00; - break; - case 5: - bits = 0x08; - break; - case 12: - bits = 0x10; - break; - case 15: - bits = 0x18; - break; - - default: - printk ("Maui: Invalid IRQ %d\n", irq); - return 0; - } +#ifdef __SMP__ + int i; +#endif + unsigned char bits; - outb (0x00, HOST_CTRL_PORT); /* Reset */ + switch (irq) + { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + printk(KERN_ERR "Maui: Invalid IRQ %d\n", irq); + return 0; + } + outb((0x00), HOST_CTRL_PORT); /* Reset */ + outb((bits), HOST_DATA_PORT); /* Set the IRQ bits */ + outb((bits | 0x80), HOST_DATA_PORT); /* Set the IRQ bits again? */ + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ + outb((0xD0), HOST_CTRL_PORT); /* Cause interrupt */ + +#ifdef __SMP__ + for (i = 0; i < 1000000 && !irq_ok; i++); + + if (!irq_ok) + return 0; +#endif + outb((0x80), HOST_CTRL_PORT); /* Leave reset */ - outb (bits, HOST_DATA_PORT); /* Set the IRQ bits */ - outb (bits | 0x80, HOST_DATA_PORT); /* Set the IRQ bits again? */ + printk(KERN_INFO "Turtle Beach Maui initialization\n"); - outb (0x80, HOST_CTRL_PORT); /* Leave reset */ - outb (0x80, HOST_CTRL_PORT); /* Leave reset */ + if (!download_code()) + return 0; - outb (0xD0, HOST_CTRL_PORT); /* Cause interrupt */ + outb((0xE0), HOST_CTRL_PORT); /* Normal operation */ - for (i = 0; i < 1000000 && !irq_ok; i++); + /* Select mpu401 mode */ - if (!irq_ok) - return 0; + maui_write(0xf0); + maui_write(1); + if (maui_read() != 0x80) + { + maui_write(0xf0); + maui_write(1); + if (maui_read() != 0x80) + printk(KERN_ERR "Maui didn't acknowledge set HW mode command\n"); + } + printk(KERN_INFO "Maui initialized OK\n"); + return 1; +} - outb (0x80, HOST_CTRL_PORT); /* Leave reset */ +static int maui_short_wait(int mask) +{ + int i; - printk ("Turtle Beach Maui initialization\n"); + for (i = 0; i < 1000; i++) + { + if (inb(HOST_STAT_PORT) & mask) + { + return 1; + } + } + return 0; +} - if (!download_code ()) - return 0; +static int maui_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ - outb (0xE0, HOST_CTRL_PORT); /* Normal operation */ + struct sysex_info header; + unsigned long left, src_offs; + int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header; + int i; - /* Select mpu401 mode */ + if (format == SYSEX_PATCH) /* Handled by midi_synth.c */ + return orig_load_patch(dev, format, addr, offs, count, pmgr_flag); - maui_write (0xf0); - maui_write (1); - if (maui_read () != 0x80) - { - maui_write (0xf0); - maui_write (1); - if (maui_read () != 0x80) - printk ("Maui didn't acknowledge set HW mode command\n"); - } + if (format != MAUI_PATCH) + { + printk(KERN_WARNING "Maui: Unknown patch format\n"); + } + if (count < hdr_size) + { +/* printk("Maui error: Patch header too short\n");*/ + return -EINVAL; + } + count -= hdr_size; - printk ("Maui initialized OK\n"); - return 1; -} + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ -static int -maui_short_wait (int mask) -{ - int i; + if(copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs)) + return -EFAULT; - for (i = 0; i < 1000; i++) - { - if (inb (HOST_STAT_PORT) & mask) + if (count < header.len) { - return 1; + printk(KERN_ERR "Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len); + header.len = count; } - } + left = header.len; + src_offs = 0; - return 0; -} + for (i = 0; i < left; i++) + { + unsigned char data; -int -maui_load_patch (int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) -{ + if(get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]))) + return -EFAULT; + if (i == 0 && !(data & 0x80)) + return -EINVAL; - struct sysex_info header; - unsigned long left, src_offs; - int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header; - int i; + if (maui_write(data) == -1) + return -EIO; + } - if (format == SYSEX_PATCH) /* Handled by midi_synth.c */ - return orig_load_patch (dev, format, addr, offs, count, pmgr_flag); + if ((i = maui_read()) != 0x80) + { + if (i != -1) + printk("Maui: Error status %02x\n", i); + return -EIO; + } + return 0; +} - if (format != MAUI_PATCH) - { - printk ("Maui: Unknown patch format\n"); - } +int probe_maui(struct address_info *hw_config) +{ + int i; + int tmp1, tmp2, ret; - if (count < hdr_size) - { - printk ("Maui error: Patch header too short\n"); - return -(EINVAL); - } + if (check_region(hw_config->io_base, 8)) + return 0; - count -= hdr_size; + maui_base = hw_config->io_base; + maui_osp = hw_config->osp; - /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. - */ + if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0) + return 0; - memcpy_fromfs (&((char *) &header)[offs], &(addr)[offs], hdr_size - offs); + /* + * Initialize the processor if necessary + */ - if (count < header.len) - { - printk ("Maui warning: Host command record too short (%d<%d)\n", - count, (int) header.len); - header.len = count; - } + if (maui_osLen > 0) + { + if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) || + !maui_write(0x9F) || /* Report firmware version */ + !maui_short_wait(STAT_RX_AVAIL) || + maui_read() == -1 || maui_read() == -1) + if (!maui_init(hw_config->irq)) + { + free_irq(hw_config->irq, NULL); + return 0; + } + } + if (!maui_write(0xCF)) /* Report hardware version */ + { + printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); + free_irq(hw_config->irq, NULL); + return 0; + } + if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) + { + printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); + free_irq(hw_config->irq, NULL); + return 0; + } + if (tmp1 == 0xff || tmp2 == 0xff) + { + free_irq(hw_config->irq, NULL); + return 0; + } + if (trace_init) + printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2); - left = header.len; - src_offs = 0; + if (!maui_write(0x9F)) /* Report firmware version */ + return 0; + if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) + return 0; - for (i = 0; i < left; i++) - { - unsigned char data; + if (trace_init) + printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2); - data = get_fs_byte (&((addr)[hdr_size + i])); - if (i == 0 && !(data & 0x80)) - return -(EINVAL); + if (!maui_write(0x85)) /* Report free DRAM */ + return 0; + tmp1 = 0; + for (i = 0; i < 4; i++) + { + tmp1 |= maui_read() << (7 * i); + } + if (trace_init) + printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024); - if (maui_write (data) == -1) - return -(EIO); - } + for (i = 0; i < 1000; i++) + if (probe_mpu401(hw_config)) + break; - if ((i = maui_read ()) != 0x80) - { - if (i != -1) - printk ("Maui: Error status %02x\n", i); + ret = probe_mpu401(hw_config); - return -(EIO); - } + if (ret) + request_region(hw_config->io_base + 2, 6, "Maui"); - return 0; + return ret; } -int -probe_maui (struct address_info *hw_config) +void attach_maui(struct address_info *hw_config) { - int i; - int tmp1, tmp2, ret; + int this_dev; - if (check_region (hw_config->io_base, 8)) - return 0; + conf_printf("Maui", hw_config); - maui_base = hw_config->io_base; - maui_osp = hw_config->osp; + hw_config->irq *= -1; + hw_config->name = "Maui"; + attach_mpu401(hw_config); - if (snd_set_irq_handler (hw_config->irq, mauiintr, "Maui", maui_osp) < 0) - return 0; + if (hw_config->slots[1] != -1) /* The MPU401 driver installed itself */ + { + struct synth_operations *synth; - maui_sleep_flag.flags = WK_NONE; -/* - * Initialize the processor if necessary - */ + this_dev = hw_config->slots[1]; + + /* + * Intercept patch loading calls so that they can be handled + * by the Maui driver. + */ + + synth = midi_devs[this_dev]->converter; + synth->id = "MAUI"; - if (maui_osLen > 0) - { - if (!(inb (HOST_STAT_PORT) & STAT_TX_AVAIL) || - !maui_write (0x9F) || /* Report firmware version */ - !maui_short_wait (STAT_RX_AVAIL) || - maui_read () == -1 || maui_read () == -1) - if (!maui_init (hw_config->irq)) - { - snd_release_irq (hw_config->irq); - return 0; - } - } - - if (!maui_write (0xCF)) /* Report hardware version */ - { - printk ("No WaveFront firmware detected (card uninitialized?)\n"); - snd_release_irq (hw_config->irq); - return 0; - } - - if ((tmp1 = maui_read ()) == -1 || (tmp2 = maui_read ()) == -1) - { - printk ("No WaveFront firmware detected (card uninitialized?)\n"); - snd_release_irq (hw_config->irq); - return 0; - } - - if (tmp1 == 0xff || tmp2 == 0xff) - { - snd_release_irq (hw_config->irq); - return 0; - } - - if (trace_init) - printk ("WaveFront hardware version %d.%d\n", tmp1, tmp2); - - if (!maui_write (0x9F)) /* Report firmware version */ - return 0; - if ((tmp1 = maui_read ()) == -1 || (tmp2 = maui_read ()) == -1) - return 0; - - if (trace_init) - printk ("WaveFront firmware version %d.%d\n", tmp1, tmp2); - - if (!maui_write (0x85)) /* Report free DRAM */ - return 0; - tmp1 = 0; - for (i = 0; i < 4; i++) - { - tmp1 |= maui_read () << (7 * i); - } - if (trace_init) - printk ("Available DRAM %dk\n", tmp1 / 1024); - - for (i = 0; i < 1000; i++) - if (probe_mpu401 (hw_config)) - break; - - ret = probe_mpu401 (hw_config); - - if (ret) - request_region (hw_config->io_base + 2, 6, "Maui"); - - return ret; + if (synth != NULL) + { + orig_load_patch = synth->load_patch; + synth->load_patch = &maui_load_patch; + } + else + printk(KERN_ERR "Maui: Can't install patch loader\n"); + } } -void -attach_maui (struct address_info *hw_config) +void unload_maui(struct address_info *hw_config) { - int this_dev = num_midis; + int irq = hw_config->irq; + release_region(hw_config->io_base + 2, 6); + unload_mpu401(hw_config); + + if (irq < 0) + irq = -irq; + if (irq > 0) + free_irq(irq, NULL); +} - conf_printf ("Maui", hw_config); +#ifdef MODULE - hw_config->irq *= -1; - hw_config->name = "Maui"; - attach_mpu401 (hw_config); +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); - if (num_midis > this_dev) /* The MPU401 driver installed itself */ - { - struct synth_operations *synth; +int io = -1; +int irq = -1; - /* - * Intercept patch loading calls so that they can be handled - * by the Maui driver. - */ +static int fw_load = 0; - synth = midi_devs[this_dev]->converter; +struct address_info cfg; + +/* + * Install a Maui card. Needs mpu401 loaded already. + */ + +int init_module(void) +{ + printk(KERN_INFO "Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n"); + if (io == -1 || irq == -1) + { + printk(KERN_INFO "maui: irq and io must be set.\n"); + return -EINVAL; + } + cfg.io_base = io; + cfg.irq = irq; - if (synth != NULL) + if (maui_os == NULL) { - orig_load_patch = synth->load_patch; - synth->load_patch = &maui_load_patch; + fw_load = 1; + maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os); } - else - printk ("Maui: Can't install patch loader\n"); - } + if (probe_maui(&cfg) == 0) + return -ENODEV; + attach_maui(&cfg); + SOUND_LOCK; + return 0; } -void -unload_maui (struct address_info *hw_config) +void cleanup_module(void) { - int irq = hw_config->irq; - - release_region (hw_config->io_base + 2, 6); - - unload_mpu401 (hw_config); - - if (irq < 0) - irq = -irq; - - if (irq > 0) - snd_release_irq (irq); + if (fw_load && maui_os) + vfree(maui_os); + unload_maui(&cfg); + SOUND_LOCK_END; } - - +#endif #endif diff --git a/drivers/sound/midi_ctrl.h b/drivers/sound/midi_ctrl.h index 8b68c7d91db3..3353e5a67c24 100644 --- a/drivers/sound/midi_ctrl.h +++ b/drivers/sound/midi_ctrl.h @@ -1,7 +1,7 @@ static unsigned char ctrl_def_values[128] = { - 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 0 to 7 */ - 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 8 to 15 */ + 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 */ @@ -15,7 +15,7 @@ static unsigned char ctrl_def_values[128] = 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 96 to 103 */ + 0x00,0x00,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 */ diff --git a/drivers/sound/midi_syms.c b/drivers/sound/midi_syms.c new file mode 100644 index 000000000000..eb1ef288fda5 --- /dev/null +++ b/drivers/sound/midi_syms.c @@ -0,0 +1,36 @@ +/* + * Exported symbols for midi driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include + +char midi_syms_symbol; + +#include "sound_config.h" +#define _MIDI_SYNTH_C_ +#include "midi_synth.h" + +struct symbol_table midi_syms= +{ +#include + X(do_midi_msg), + X(midi_synth_open), + X(midi_synth_close), + X(midi_synth_ioctl), + X(midi_synth_kill_note), + X(midi_synth_start_note), + X(midi_synth_set_instr), + X(midi_synth_reset), + X(midi_synth_hw_control), + X(midi_synth_aftertouch), + X(midi_synth_controller), + X(midi_synth_panning), + X(midi_synth_setup_voice), + X(midi_synth_send_sysex), + X(midi_synth_bender), + X(midi_synth_load_patch), + X(MIDIbuf_avail), +#include +}; \ No newline at end of file diff --git a/drivers/sound/midi_synth.c b/drivers/sound/midi_synth.c index ac25651a01cd..8848f410ec01 100644 --- a/drivers/sound/midi_synth.c +++ b/drivers/sound/midi_synth.c @@ -4,28 +4,26 @@ * High level midi sequencer manager for dumb MIDI interfaces. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 - #define USE_SEQ_MACROS #define USE_SIMPLE_MACROS #include "sound_config.h" -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI #define _MIDI_SYNTH_C_ -static wait_handle *sysex_sleeper = NULL; -static volatile struct snd_wait sysex_sleep_flag = -{0}; - #include "midi_synth.h" static int midi2synth[MAX_MIDI_DEV]; @@ -50,702 +48,660 @@ static unsigned char prev_out_status[MAX_MIDI_DEV]; #define _SEQ_ADVBUF(x) len=x void -do_midi_msg (int synthno, unsigned char *msg, int mlen) +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]); */ - ; - } + 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]); */ + ; + } } static void -midi_outc (int midi_dev, int data) +midi_outc(int midi_dev, int data) { - int timeout; + int timeout; - for (timeout = 0; timeout < 32000; timeout++) - if (midi_devs[midi_dev]->putc (midi_dev, (unsigned char) (data & 0xff))) - { - if (data & 0x80) /* - * Status byte - */ - prev_out_status[midi_dev] = - (unsigned char) (data & 0xff); /* - * Store for running status + for (timeout = 0; timeout < 3200; timeout++) + if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff))) + { + if (data & 0x80) /* + * Status byte */ - return; /* - * Mission complete - */ - } - - /* - * Sorry! No space on buffers. - */ - printk ("Midi send timed out\n"); + 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) +prefix_cmd(int midi_dev, unsigned char status) { - if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL) - return 1; + if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL) + return 1; - return midi_devs[midi_dev]->prefix_cmd (midi_dev, status); + return midi_devs[midi_dev]->prefix_cmd(midi_dev, status); } static void -midi_synth_input (int orig_dev, unsigned char data) +midi_synth_input(int orig_dev, unsigned char data) { - int dev; - struct midi_input_info *inc; + 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) - 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) /* Ignore if no previous status (yet) */ - { /* Data byte (use running status) */ - inc->m_state = MST_DATA; - inc->m_ptr = 2; - inc->m_left = len_tab[(data >> 4) - 8] - 1; - inc->m_buf[0] = inc->m_prev_status; - inc->m_buf[1] = data; - } - 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 */ + static unsigned char len_tab[] = /* # of data bytes following a status + */ { - 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; - } + 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) /* Ignore if no previous status (yet) */ + { /* Data byte (use running status) */ + inc->m_state = MST_DATA; + inc->m_ptr = 2; + inc->m_left = len_tab[(data >> 4) - 8] - 1; + inc->m_buf[0] = inc->m_prev_status; + inc->m_buf[1] = data; + } + 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) +leave_sysex(int dev) { - int orig_dev = synth_devs[dev]->midi_dev; - int timeout = 0; + int orig_dev = synth_devs[dev]->midi_dev; + int timeout = 0; - if (!sysex_state[dev]) - return; + if (!sysex_state[dev]) + return; - sysex_state[dev] = 0; + sysex_state[dev] = 0; - while (!midi_devs[orig_dev]->putc (orig_dev, 0xf7) && - timeout < 1000) - timeout++; + while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) && + timeout < 1000) + timeout++; - sysex_state[dev] = 0; + sysex_state[dev] = 0; } static void -midi_synth_output (int dev) +midi_synth_output(int dev) { - /* - * Currently NOP - */ + /* + * Currently NOP + */ } -int -midi_synth_ioctl (int dev, - unsigned int cmd, caddr_t arg) +int midi_synth_ioctl(int dev, unsigned int cmd, caddr_t arg) { - /* - * int orig_dev = synth_devs[dev]->midi_dev; - */ - - switch (cmd) - { - - case SNDCTL_SYNTH_INFO: - memcpy_tofs (&((char *) arg)[0], synth_devs[dev]->info, sizeof (struct synth_info)); + /* + * int orig_dev = synth_devs[dev]->midi_dev; + */ - return 0; - break; + switch (cmd) { - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - break; + 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); - } + default: + return -EINVAL; + } } int -midi_synth_kill_note (int dev, int channel, int note, int velocity) +midi_synth_kill_note(int dev, int channel, int note, int velocity) { - int orig_dev = synth_devs[dev]->midi_dev; - int msg, chn; + 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; + 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); + leave_sysex(dev); - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; - if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80)) - { /* + if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80)) + { /* * Use running status */ - if (!prefix_cmd (orig_dev, note)) - return 0; + if (!prefix_cmd(orig_dev, note)) + return 0; - midi_outc (orig_dev, note); + midi_outc(orig_dev, note); - if (msg == 0x90) /* - * Running status = Note on - */ - midi_outc (orig_dev, 0); /* - * Note on with velocity 0 == note - * off + if (msg == 0x90) /* + * Running status = Note on */ - 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); - } - } + 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; + return 0; } int -midi_synth_set_instr (int dev, int channel, int instr_no) +midi_synth_set_instr(int dev, int channel, int instr_no) { - int orig_dev = synth_devs[dev]->midi_dev; + int orig_dev = synth_devs[dev]->midi_dev; - if (instr_no < 0 || instr_no > 127) - return 0; - if (channel < 0 || channel > 15) - return 0; + if (instr_no < 0 || instr_no > 127) + instr_no = 0; + if (channel < 0 || channel > 15) + return 0; - leave_sysex (dev); + leave_sysex(dev); - if (!prefix_cmd (orig_dev, 0xc0 | (channel & 0x0f))) - return 0; - midi_outc (orig_dev, 0xc0 | (channel & 0x0f)); /* + 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); + midi_outc(orig_dev, instr_no); - return 0; + return 0; } int -midi_synth_start_note (int dev, int channel, int note, int velocity) +midi_synth_start_note(int dev, int channel, int note, int velocity) { - int orig_dev = synth_devs[dev]->midi_dev; - int msg, chn; + 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; + 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); + leave_sysex(dev); - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; - if (chn == channel && msg == 0x90) - { /* + 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))) + 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; - midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /* - * Note on - */ - midi_outc (orig_dev, note); - midi_outc (orig_dev, velocity); - } - return 0; } void -midi_synth_reset (int dev) +midi_synth_reset(int dev) { - leave_sysex (dev); + leave_sysex(dev); } int -midi_synth_open (int dev, int mode) +midi_synth_open(int dev, int mode) { - int orig_dev = synth_devs[dev]->midi_dev; - int err; - unsigned long flags; - struct midi_input_info *inc; - - if (orig_dev < 0 || orig_dev > num_midis) - 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 (); - 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); - - sysex_sleep_flag.flags = WK_NONE; - - return 1; + int orig_dev = synth_devs[dev]->midi_dev; + int err; + unsigned long flags; + 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; +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + inc = &midi_devs[orig_dev]->in_info; + + save_flags(flags); + cli(); + 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; } void -midi_synth_close (int dev) +midi_synth_close(int dev) { - int orig_dev = synth_devs[dev]->midi_dev; + int orig_dev = synth_devs[dev]->midi_dev; - leave_sysex (dev); + leave_sysex(dev); - /* - * Shut up the synths by sending just single active sensing message. - */ - midi_devs[orig_dev]->putc (orig_dev, 0xfe); + /* + * 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); + midi_devs[orig_dev]->close(orig_dev); +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif } void -midi_synth_hw_control (int dev, unsigned char *event) +midi_synth_hw_control(int dev, unsigned char *event) { } int -midi_synth_load_patch (int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) +midi_synth_load_patch(int dev, int format, const char *addr, + int offs, 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; - - if (format != SYSEX_PATCH) - { - printk ("MIDI Error: Invalid patch format (key) 0x%x\n", format); - return -(EINVAL); - } + int orig_dev = synth_devs[dev]->midi_dev; - if (count < hdr_size) - { - printk ("MIDI Error: Patch header too short\n"); - return -(EINVAL); - } + 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; - count -= hdr_size; + leave_sysex(dev); - /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. - */ + if (!prefix_cmd(orig_dev, 0xf0)) + return 0; - memcpy_fromfs (&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs); + if (format != SYSEX_PATCH) + { +/* printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);*/ + return -EINVAL; + } + if (count < hdr_size) + { +/* printk("MIDI Error: Patch header too short\n");*/ + return -EINVAL; + } + count -= hdr_size; - if (count < sysex.len) - { - printk ("MIDI Warning: Sysex record too short (%d<%d)\n", - count, (int) sysex.len); - sysex.len = count; - } + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ - left = sysex.len; - src_offs = 0; + if(copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs)) + return -EFAULT; + + if (count < sysex.len) + { +/* printk(KERN_WARNING "MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);*/ + sysex.len = count; + } + left = sysex.len; + src_offs = 0; - sysex_sleep_flag.flags = WK_NONE; + for (i = 0; i < left && !signal_pending(current); i++) + { + unsigned char data; - for (i = 0; i < left && !current_got_fatal_signal (); i++) - { - unsigned char data; + get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i])); - data = get_fs_byte (&((addr)[hdr_size + i])); + eox_seen = (i > 0 && data & 0x80); /* End of sysex */ - eox_seen = (i > 0 && data & 0x80); /* End of sysex */ + if (eox_seen && data != 0xf7) + data = 0xf7; - 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 (i == 0) - { - if (data != 0xf0) - { - printk ("Error: Sysex start missing\n"); - return -(EINVAL); - } + if (!first_byte && data & 0x80) + return 0; + first_byte = 0; } - while (!midi_devs[orig_dev]->putc (orig_dev, (unsigned char) (data & 0xff)) && - !current_got_fatal_signal ()) - - { - unsigned long tlimit; - - if (1) - current_set_timeout (tlimit = jiffies + (1)); - else - tlimit = (unsigned long) -1; - sysex_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&sysex_sleeper); - if (!(sysex_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - sysex_sleep_flag.flags |= WK_TIMEOUT; - } - sysex_sleep_flag.flags &= ~WK_SLEEP; - }; /* Wait for timeout */ - - if (!first_byte && data & 0x80) + if (!eox_seen) + midi_outc(orig_dev, 0xf7); return 0; - first_byte = 0; - } - - if (!eox_seen) - midi_outc (orig_dev, 0xf7); - return 0; } - -void -midi_synth_panning (int dev, int channel, int pressure) + +void midi_synth_panning(int dev, int channel, int pressure) { } - -void -midi_synth_aftertouch (int dev, int channel, int pressure) + +void midi_synth_aftertouch(int dev, int channel, int pressure) { - int orig_dev = synth_devs[dev]->midi_dev; - int msg, chn; + int orig_dev = synth_devs[dev]->midi_dev; + int msg, chn; - if (pressure < 0 || pressure > 127) - return; - if (channel < 0 || channel > 15) - return; + if (pressure < 0 || pressure > 127) + return; + if (channel < 0 || channel > 15) + return; - leave_sysex (dev); + leave_sysex(dev); - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; + 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); + 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); } void -midi_synth_controller (int dev, int channel, int ctrl_num, int value) +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); -} + int orig_dev = synth_devs[dev]->midi_dev; + int chn, msg; -int -midi_synth_patchmgr (int dev, struct patmgr_info *rec) -{ - return -(EINVAL); + 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); } void -midi_synth_bender (int dev, int channel, int value) +midi_synth_bender(int dev, int channel, int value) { - int orig_dev = synth_devs[dev]->midi_dev; - int msg, prev_chn; + int orig_dev = synth_devs[dev]->midi_dev; + int msg, prev_chn; - if (channel < 0 || channel > 15) - return; + if (channel < 0 || channel > 15) + return; - if (value < 0 || value > 16383) - return; + if (value < 0 || value > 16383) + return; - leave_sysex (dev); + leave_sysex(dev); - msg = prev_out_status[orig_dev] & 0xf0; - prev_chn = prev_out_status[orig_dev] & 0x0f; + 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); + 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); } void -midi_synth_setup_voice (int dev, int voice, int channel) +midi_synth_setup_voice(int dev, int voice, int channel) { } int -midi_synth_send_sysex (int dev, unsigned char *bytes, int len) +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]->putc (orig_dev, bytes[i])) - { + 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; + int timeout = 0; - bytes[i] = 0xf7; - sysex_state[dev] = 0; + bytes[i] = 0xf7; + sysex_state[dev] = 0; - while (!midi_devs[orig_dev]->putc (orig_dev, bytes[i]) && - timeout < 1000) - timeout++; - } + while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) && + timeout < 1000) + timeout++; + } + if (!sysex_state[dev]) + return 0; + } - if (!sysex_state[dev]) return 0; - } - - return 0; } + #endif diff --git a/drivers/sound/midi_synth.h b/drivers/sound/midi_synth.h index a2c812b30c22..1509b2255a34 100644 --- a/drivers/sound/midi_synth.h +++ b/drivers/sound/midi_synth.h @@ -12,7 +12,6 @@ int midi_synth_load_patch (int dev, int format, const char * addr, void midi_synth_panning (int dev, int channel, int pressure); void midi_synth_aftertouch (int dev, int channel, int pressure); void midi_synth_controller (int dev, int channel, int ctrl_num, int value); -int midi_synth_patchmgr (int dev, struct patmgr_info *rec); void midi_synth_bender (int dev, int chn, int value); void midi_synth_setup_voice (int dev, int voice, int chn); int midi_synth_send_sysex(int dev, unsigned char *bytes,int len); @@ -23,6 +22,7 @@ static struct synth_info std_synth_info = static struct synth_operations std_midi_synth = { + "MIDI", &std_synth_info, 0, SYNTH_TYPE_MIDI, @@ -40,7 +40,6 @@ static struct synth_operations std_midi_synth = midi_synth_controller, midi_synth_panning, NULL, - midi_synth_patchmgr, midi_synth_bender, NULL, /* alloc_voice */ midi_synth_setup_voice, diff --git a/drivers/sound/midibuf.c b/drivers/sound/midibuf.c index 34b9b008b861..7cd8d8ce90b3 100644 --- a/drivers/sound/midibuf.c +++ b/drivers/sound/midibuf.c @@ -4,18 +4,24 @@ * Device file manager for /dev/midi# */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 +#include +#include +#define MIDIBUF_C #include "sound_config.h" -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI /* * Don't make MAX_QUEUE_SIZE larger than 4000 @@ -23,41 +29,33 @@ #define MAX_QUEUE_SIZE 4000 -static wait_handle *midi_sleeper[MAX_MIDI_DEV] = -{NULL}; -static volatile struct snd_wait midi_sleep_flag[MAX_MIDI_DEV] = -{ - {0}}; -static wait_handle *input_sleeper[MAX_MIDI_DEV] = -{NULL}; -static volatile struct snd_wait input_sleep_flag[MAX_MIDI_DEV] = -{ - {0}}; +static struct wait_queue *midi_sleeper[MAX_MIDI_DEV] = {NULL}; +static struct wait_queue *input_sleeper[MAX_MIDI_DEV] = {NULL}; struct midi_buf - { - int len, head, tail; - unsigned char queue[MAX_QUEUE_SIZE]; - }; +{ + int len, head, tail; + unsigned char queue[MAX_QUEUE_SIZE]; +}; struct midi_parms - { - int prech_timeout; /* - * Timeout before the first ch - */ - }; +{ + int 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_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 void midi_poll(unsigned long dummy); -static struct timer_list poll_timer = -{NULL, NULL, 0, 0, midi_poll}; +static struct timer_list poll_timer = { + NULL, NULL, 0, 0, midi_poll +}; + static volatile int open_devs = 0; #define DATA_AVAIL(q) (q->len) @@ -67,7 +65,7 @@ static volatile int open_devs = 0; if (SPACE_AVAIL(q)) \ { \ unsigned long flags; \ - save_flags(flags);cli(); \ + save_flags( flags);cli(); \ q->queue[q->tail] = (data); \ q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \ restore_flags(flags); \ @@ -77,456 +75,365 @@ static volatile int open_devs = 0; if (DATA_AVAIL(q)) \ { \ unsigned long flags; \ - save_flags(flags);cli(); \ + save_flags( flags);cli(); \ data = q->queue[q->head]; \ q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \ restore_flags(flags); \ } -void -drain_midi_queue (int dev) +static void drain_midi_queue(int dev) { - /* - * Give the Midi driver time to drain its output queues - */ - - if (midi_devs[dev]->buffer_status != NULL) - while (!current_got_fatal_signal () && - midi_devs[dev]->buffer_status (dev)) - - { - unsigned long tlimit; - - if (HZ / 10) - current_set_timeout (tlimit = jiffies + (HZ / 10)); - else - tlimit = (unsigned long) -1; - midi_sleep_flag[dev].flags = WK_SLEEP; - module_interruptible_sleep_on (&midi_sleeper[dev]); - if (!(midi_sleep_flag[dev].flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - midi_sleep_flag[dev].flags |= WK_TIMEOUT; - } - midi_sleep_flag[dev].flags &= ~WK_SLEEP; - }; + /* + * Give the Midi driver time to drain its output queues + */ + + if (midi_devs[dev]->buffer_status != NULL) + while (!signal_pending(current) && midi_devs[dev]->buffer_status(dev)) + { + current->timeout = jiffies + HZ / 10; + interruptible_sleep_on(&midi_sleeper[dev]); + current->timeout = 0; + } } -static void -midi_input_intr (int dev, unsigned char data) +static void midi_input_intr(int dev, unsigned char data) { - if (midi_in_buf[dev] == NULL) - return; + if (midi_in_buf[dev] == NULL) + return; - if (data == 0xfe) /* + if (data == 0xfe) /* * Active sensing */ - return; /* + return; /* * Ignore */ - if (SPACE_AVAIL (midi_in_buf[dev])) - { - QUEUE_BYTE (midi_in_buf[dev], data); - if ((input_sleep_flag[dev].flags & WK_SLEEP)) - { - input_sleep_flag[dev].flags = WK_WAKEUP; - module_wake_up (&input_sleeper[dev]); - }; - } - + 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) +static void midi_output_intr(int dev) { - /* - * Currently NOP - */ + /* + * Currently NOP + */ } -static void -midi_poll (unsigned long dummy) +static void midi_poll(unsigned long dummy) { - unsigned long flags; - int dev; - - save_flags (flags); - cli (); - if (open_devs) - { - for (dev = 0; dev < num_midis; dev++) - if (midi_out_buf[dev] != NULL) - { - while (DATA_AVAIL (midi_out_buf[dev]) && - midi_devs[dev]->putc (dev, - midi_out_buf[dev]->queue[midi_out_buf[dev]->head])) - { - 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 && - (midi_sleep_flag[dev].flags & WK_SLEEP)) - { - midi_sleep_flag[dev].flags = WK_WAKEUP; - module_wake_up (&midi_sleeper[dev]); - }; - } - - { - poll_timer.expires = (1) + jiffies; - add_timer (&poll_timer); - }; /* - * Come back later - */ - } - restore_flags (flags); -} + unsigned long flags; + int dev; -int -MIDIbuf_open (int dev, struct fileinfo *file) -{ - int mode, err; - - dev = dev >> 4; - mode = file->mode & O_ACCMODE; - - if (num_midis > MAX_MIDI_DEV) - { - printk ("Sound: FATAL ERROR: Too many midi interfaces\n"); - num_midis = MAX_MIDI_DEV; - } - - if (dev < 0 || dev >= num_midis) - { - printk ("Sound: Nonexistent MIDI interface %d\n", dev); - return -(ENXIO); - } - - /* - * Interrupts disabled. Be careful - */ - - if ((err = midi_devs[dev]->open (dev, mode, - midi_input_intr, midi_output_intr)) < 0) - { - return err; - } - - parms[dev].prech_timeout = 0; - - midi_in_buf[dev] = (struct midi_buf *) vmalloc (sizeof (struct midi_buf)); - - if (midi_in_buf[dev] == NULL) - { - printk ("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] = (struct midi_buf *) vmalloc (sizeof (struct midi_buf)); - - if (midi_out_buf[dev] == NULL) - { - printk ("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++; - - midi_sleep_flag[dev].flags = WK_NONE; - input_sleep_flag[dev].flags = WK_NONE; - - if (open_devs < 2) /* This was first open */ - { - ; - - { - poll_timer.expires = (1) + jiffies; - add_timer (&poll_timer); - }; /* Start polling */ - } - - return err; + save_flags(flags); + cli(); + if (open_devs) + { + for (dev = 0; dev < num_midis; dev++) + if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL) + { + int ok = 1; + + while (DATA_AVAIL(midi_out_buf[dev]) && ok) + { + int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head]; + + restore_flags(flags); /* Give some time to others */ + ok = midi_devs[dev]->outputc(dev, c); + cli(); + 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 + */ + } + restore_flags(flags); } -void -MIDIbuf_release (int dev, struct fileinfo *file) +int MIDIbuf_open(int dev, struct file *file) { - int mode; - unsigned long flags; - - dev = dev >> 4; - mode = file->mode & O_ACCMODE; + int mode, err; - if (dev < 0 || dev >= num_midis) - return; + dev = dev >> 4; + mode = translate_mode(file); - save_flags (flags); - cli (); - - /* - * Wait until the queue is empty - */ + 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 + */ - if (mode != OPEN_READ) - { - midi_devs[dev]->putc (dev, 0xfe); /* - * Active sensing to shut the - * devices - */ + if ((err = midi_devs[dev]->open(dev, mode, + midi_input_intr, midi_output_intr)) < 0) + return err; - while (!current_got_fatal_signal () && - DATA_AVAIL (midi_out_buf[dev])) + parms[dev].prech_timeout = 0; + midi_in_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf)); + if (midi_in_buf[dev] == NULL) { - unsigned long tlimit; - - if (0) - current_set_timeout (tlimit = jiffies + (0)); - else - tlimit = (unsigned long) -1; - midi_sleep_flag[dev].flags = WK_SLEEP; - module_interruptible_sleep_on (&midi_sleeper[dev]); - if (!(midi_sleep_flag[dev].flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - midi_sleep_flag[dev].flags |= WK_TIMEOUT; - } - midi_sleep_flag[dev].flags &= ~WK_SLEEP; - }; /* - * Sync - */ + 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; - drain_midi_queue (dev); /* - * Ensure the output queues are empty - */ - } + midi_out_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf)); - restore_flags (flags); + 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++; - midi_devs[dev]->close (dev); + init_waitqueue(&midi_sleeper[dev]); + init_waitqueue(&input_sleeper[dev]); - vfree (midi_in_buf[dev]); - vfree (midi_out_buf[dev]); - midi_in_buf[dev] = NULL; - midi_out_buf[dev] = NULL; - if (open_devs < 2) - del_timer (&poll_timer);; - open_devs--; + if (open_devs < 2) /* This was first open */ + { + poll_timer.expires = 1 + jiffies; + add_timer(&poll_timer); /* Start polling */ + } + return err; } -int -MIDIbuf_write (int dev, struct fileinfo *file, const char *buf, int count) +void MIDIbuf_release(int dev, struct file *file) { - unsigned long flags; - int c, n, i; - unsigned char tmp_data; + int mode; + unsigned long flags; - dev = dev >> 4; + dev = dev >> 4; + mode = translate_mode(file); - if (!count) - return 0; + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + return; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - c = 0; + /* + * Wait until the queue is empty + */ - while (c < count) - { - n = SPACE_AVAIL (midi_out_buf[dev]); - - if (n == 0) /* - * No space just now. We have to sleep - */ + if (mode != OPEN_READ) { - - { - unsigned long tlimit; - - if (0) - current_set_timeout (tlimit = jiffies + (0)); - else - tlimit = (unsigned long) -1; - midi_sleep_flag[dev].flags = WK_SLEEP; - module_interruptible_sleep_on (&midi_sleeper[dev]); - if (!(midi_sleep_flag[dev].flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - midi_sleep_flag[dev].flags |= WK_TIMEOUT; - } - midi_sleep_flag[dev].flags &= ~WK_SLEEP; - }; - if (current_got_fatal_signal ()) - { - restore_flags (flags); - return -(EINTR); - } - - n = SPACE_AVAIL (midi_out_buf[dev]); - } - - if (n > (count - c)) - n = count - c; - - for (i = 0; i < n; i++) - { - memcpy_fromfs ((char *) &tmp_data, &(buf)[c], 1); - QUEUE_BYTE (midi_out_buf[dev], tmp_data); - c++; + midi_devs[dev]->outputc(dev, 0xfe); /* + * Active sensing to shut the + * devices + */ + + while (!signal_pending(current) && DATA_AVAIL(midi_out_buf[dev])) + interruptible_sleep_on(&midi_sleeper[dev]); + /* + * Sync + */ + + drain_midi_queue(dev); /* + * Ensure the output queues are empty + */ } - } + restore_flags(flags); - restore_flags (flags); + midi_devs[dev]->close(dev); - return c; + vfree(midi_in_buf[dev]); + vfree(midi_out_buf[dev]); + midi_in_buf[dev] = NULL; + midi_out_buf[dev] = NULL; + if (open_devs < 2) + del_timer(&poll_timer);; + open_devs--; } - -int -MIDIbuf_read (int dev, struct fileinfo *file, char *buf, int count) +int MIDIbuf_write(int dev, struct file *file, const char *buf, int count) { - int n, c = 0; - unsigned long flags; - unsigned char tmp_data; + unsigned long flags; + int c, n, i; + unsigned char tmp_data; - dev = dev >> 4; + dev = dev >> 4; - save_flags (flags); - cli (); + if (!count) + return 0; - if (!DATA_AVAIL (midi_in_buf[dev])) /* - * No data yet, wait - */ - { + save_flags(flags); + cli(); - { - unsigned long tlimit; + c = 0; - if (parms[dev].prech_timeout) - current_set_timeout (tlimit = jiffies + (parms[dev].prech_timeout)); - else - tlimit = (unsigned long) -1; - input_sleep_flag[dev].flags = WK_SLEEP; - module_interruptible_sleep_on (&input_sleeper[dev]); - if (!(input_sleep_flag[dev].flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - input_sleep_flag[dev].flags |= WK_TIMEOUT; - } - input_sleep_flag[dev].flags &= ~WK_SLEEP; - }; - if (current_got_fatal_signal ()) - c = -(EINTR); /* - * The user is getting restless + while (c < count) + { + n = SPACE_AVAIL(midi_out_buf[dev]); + + if (n == 0) { /* + * No space just now. We have to sleep */ - } + interruptible_sleep_on(&midi_sleeper[dev]); + if (signal_pending(current)) + { + restore_flags(flags); + return -EINTR; + } + n = SPACE_AVAIL(midi_out_buf[dev]); + } + if (n > (count - c)) + n = count - c; + + for (i = 0; i < n; i++) + { + /* BROKE BROKE BROKE - CANT DO THIS WITH CLI !! */ + copy_from_user((char *) &tmp_data, &(buf)[c], 1); + QUEUE_BYTE(midi_out_buf[dev], tmp_data); + c++; + } + } + restore_flags(flags); + return c; +} - 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) - { - REMOVE_BYTE (midi_in_buf[dev], tmp_data); - memcpy_tofs (&(buf)[c], (char *) &tmp_data, 1); - c++; - } - } +int MIDIbuf_read(int dev, struct file *file, char *buf, int count) +{ + int n, c = 0; + unsigned long flags; + unsigned char tmp_data; - restore_flags (flags); + dev = dev >> 4; - return c; -} + save_flags(flags); + cli(); -int -MIDIbuf_ioctl (int dev, struct fileinfo *file, - unsigned int cmd, caddr_t 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); - else - printk ("/dev/midi%d: No coprocessor for this device\n", dev); - - return -(ENXIO); - } - else - switch (cmd) - { - - case SNDCTL_MIDI_PRETIME: - val = (int) get_user ((int *) arg); - if (val < 0) - val = 0; - - val = (HZ * val) / 10; - parms[dev].prech_timeout = val; - return snd_ioctl_return ((int *) arg, val); - break; - - default: - return midi_devs[dev]->ioctl (dev, cmd, arg); - } + if (!DATA_AVAIL(midi_in_buf[dev])) { /* + * No data yet, wait + */ + current->timeout = parms[dev].prech_timeout ? jiffies + parms[dev].prech_timeout : 0; + interruptible_sleep_on(&input_sleeper[dev]); + current->timeout = 0; + 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 */ + copy_to_user(&(buf)[c], fixit, 1); + c++; + } + } + restore_flags(flags); + return c; } -int -MIDIbuf_select (int dev, struct fileinfo *file, int sel_type, select_table_handle * wait) +int MIDIbuf_ioctl(int dev, struct file *file, + unsigned int cmd, caddr_t arg) { - dev = dev >> 4; + int val; - switch (sel_type) - { - case SEL_IN: - if (!DATA_AVAIL (midi_in_buf[dev])) + dev = dev >> 4; + + if (((cmd >> 8) & 0xff) == 'C') { - - input_sleep_flag[dev].flags = WK_SLEEP; - module_select_wait (&input_sleeper[dev], wait); - return 0; + 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; } - return 1; - break; - - case SEL_OUT: - if (SPACE_AVAIL (midi_out_buf[dev])) + else { + switch (cmd) + { + case SNDCTL_MIDI_PRETIME: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 0) + val = 0; + val = (HZ * val) / 10; + parms[dev].prech_timeout = val; + return put_user(val, (int *)arg); + + default: + if (!midi_devs[dev]->ioctl) + return -EINVAL; + return midi_devs[dev]->ioctl(dev, cmd, arg); + } + } +} - midi_sleep_flag[dev].flags = WK_SLEEP; - module_select_wait (&midi_sleeper[dev], wait); - return 0; +unsigned int MIDIbuf_select(int dev, struct file *file, int sel_type, select_table * wait) +{ + dev = dev >> 4; + + switch(sel_type) + { + case SEL_IN: + /* input */ + if (!DATA_AVAIL(midi_in_buf[dev])) + { + select_wait(&midi_sleeper[dev], wait); + return 0; + } + return 1; + + case SEL_OUT: + /* output */ + if (!SPACE_AVAIL(midi_out_buf[dev])) + { + select_wait(&midi_sleeper[dev], wait); + return 0; + } + return 1; + case SEL_EX: + return 0; } - return 1; - break; + return 0; +} - case SEL_EX: - return 0; - } - return 0; +void MIDIbuf_init(void) +{ + /* drag in midi_syms.o */ + { + extern char midi_syms_symbol; + midi_syms_symbol = 0; + } } - -void -MIDIbuf_init (void) +int MIDIbuf_avail(int dev) { + if (midi_in_buf[dev]) + return DATA_AVAIL (midi_in_buf[dev]); + return 0; } + #endif diff --git a/drivers/sound/mpu401.c b/drivers/sound/mpu401.c index 71214e45e381..52f2cf9365eb 100644 --- a/drivers/sound/mpu401.c +++ b/drivers/sound/mpu401.c @@ -4,24 +4,29 @@ * The low level driver for Roland MPU-401 compatible Midi cards. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 modularisation, use normal request_irq, use dev_id */ -#include +#include +#include #define USE_SEQ_MACROS #define USE_SIMPLE_MACROS #include "sound_config.h" +#include "soundmodule.h" #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) #include "coproc.h" -static int init_sequence[20]; /* NOTE! pos 0 = len, start pos 1. */ #ifdef CONFIG_SEQUENCER static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL; @@ -29,71 +34,70 @@ static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL; #endif struct mpu_config - { - int base; /* +{ + int base; /* * I/O base */ - int irq; - int opened; /* + int irq; + int opened; /* * Open mode */ - int devno; - int synthno; - int uart_mode; - int initialized; - int mode; + int devno; + int synthno; + int uart_mode; + int initialized; + int mode; #define MODE_MIDI 1 #define MODE_SYNTH 2 - unsigned char version, revision; - unsigned int capabilities; + unsigned char version, revision; + unsigned int capabilities; #define MPU_CAP_INTLG 0x10000000 #define MPU_CAP_SYNC 0x00000010 #define MPU_CAP_FSK 0x00000020 #define MPU_CAP_CLS 0x00000040 #define MPU_CAP_SMPTE 0x00000080 #define MPU_CAP_2PORT 0x00000001 - int timer_flag; + int timer_flag; #define MBUF_MAX 10 #define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ - {printk("MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} - int m_busy; - unsigned char m_buf[MBUF_MAX]; - int m_ptr; - int m_state; - int m_left; - unsigned char last_status; - void (*inputintr) (int dev, unsigned char data); - int shared_irq; - int *osp; + {printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} + int m_busy; + unsigned char m_buf[MBUF_MAX]; + int m_ptr; + int m_state; + int m_left; + unsigned char last_status; + void (*inputintr) (int dev, unsigned char data); + int shared_irq; + int *osp; }; #define DATAPORT(base) (base) #define COMDPORT(base) (base+1) #define STATPORT(base) (base+1) -static int -mpu401_status (struct mpu_config *devc) +static int mpu401_status(struct mpu_config *devc) { - return inb (STATPORT (devc->base)); + return inb(STATPORT(devc->base)); } + #define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL)) #define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY)) -static void -write_command (struct mpu_config *devc, unsigned char cmd) + +static void write_command(struct mpu_config *devc, unsigned char cmd) { - outb (cmd, COMDPORT (devc->base)); + outb(cmd, COMDPORT(devc->base)); } -static int -read_data (struct mpu_config *devc) + +static int read_data(struct mpu_config *devc) { - return inb (DATAPORT (devc->base)); + return inb(DATAPORT(devc->base)); } -static void -write_data (struct mpu_config *devc, unsigned char byte) +static void write_data(struct mpu_config *devc, unsigned char byte) { - outb (byte, DATAPORT (devc->base)); + outb(byte, DATAPORT(devc->base)); } #define OUTPUT_READY 0x40 @@ -104,22 +108,27 @@ write_data (struct mpu_config *devc, unsigned char byte) static struct mpu_config dev_conf[MAX_MIDI_DEV] = { - {0}}; + {0} +}; -static int n_mpu_devs = 0; -static volatile int irq2dev[17] = -{-1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1}; +static int n_mpu_devs = 0; -static int reset_mpu401 (struct mpu_config *devc); -static void set_uart_mode (int dev, struct mpu_config *devc, int arg); +static int reset_mpu401(struct mpu_config *devc); +static void set_uart_mode(int dev, struct mpu_config *devc, int arg); -static void mpu_timer_init (int midi_dev); -static void mpu_timer_interrupt (void); -static void timer_ext_event (struct mpu_config *devc, int event, int parm); +static int mpu_timer_init(int midi_dev); +static void mpu_timer_interrupt(void); +static void timer_ext_event(struct mpu_config *devc, int event, int parm); -static struct synth_info mpu_synth_info_proto = -{"MPU-401 MIDI interface", 0, SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT}; +static struct synth_info mpu_synth_info_proto = { + "MPU-401 MIDI interface", + 0, + SYNTH_TYPE_MIDI, + MIDI_TYPE_MPU401, + 0, 128, + 0, 128, + SYNTH_CAP_INPUT +}; static struct synth_info mpu_synth_info[MAX_MIDI_DEV]; @@ -140,14 +149,14 @@ static struct synth_info mpu_synth_info[MAX_MIDI_DEV]; 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 */ + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ }; #ifndef CONFIG_SEQUENCER @@ -155,10 +164,10 @@ static unsigned char len_tab[] = /* # of data bytes following a status #else #define STORE(cmd) \ { \ - int len; \ - unsigned char obuf[8]; \ - cmd; \ - seq_input_event(obuf, len); \ + int len; \ + unsigned char obuf[8]; \ + cmd; \ + seq_input_event(obuf, len); \ } #endif @@ -166,783 +175,688 @@ static unsigned char len_tab[] = /* # of data bytes following a status #define _seqbufptr 0 #define _SEQ_ADVBUF(x) len=x -static int -mpu_input_scanner (struct mpu_config *devc, unsigned char midic) +static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic) { - switch (devc->m_state) - { - case ST_INIT: - switch (midic) - { - case 0xf8: - /* Timer overflow */ - break; - - case 0xfc: - printk (""); - break; - - case 0xfd: - if (devc->timer_flag) - mpu_timer_interrupt (); - break; - - case 0xfe: - return MPU_ACK; - break; - - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - printk ("", midic & 0x0f); - break; - - case 0xf9: - printk (""); - break; - - case 0xff: - devc->m_state = ST_SYSMSG; - break; - - default: - if (midic <= 0xef) - { - /* printk("mpu time: %d ", midic); */ - devc->m_state = ST_TIMED; - } - else - printk (" ", midic); - } - break; - - case ST_TIMED: - { - int msg = ((int) (midic & 0xf0) >> 4); - - devc->m_state = ST_DATABYTE; - - if (msg < 8) /* Data byte */ - { - /* printk("midi msg (running status) "); */ - msg = ((int) (devc->last_status & 0xf0) >> 4); - msg -= 8; - devc->m_left = len_tab[msg] - 1; - - devc->m_ptr = 2; - devc->m_buf[0] = devc->last_status; - devc->m_buf[1] = midic; - - if (devc->m_left <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - } - else if (msg == 0xf) /* MPU MARK */ - { - devc->m_state = ST_INIT; - - switch (midic) - { - case 0xf8: - /* printk("NOP "); */ - break; - - case 0xf9: - /* printk("meas end "); */ - break; - - case 0xfc: - /* printk("data end "); */ - break; - - default: - printk ("Unknown MPU mark %02x\n", midic); - } - } - else - { - devc->last_status = midic; - /* printk ("midi msg "); */ - msg -= 8; - devc->m_left = len_tab[msg]; - - devc->m_ptr = 1; - devc->m_buf[0] = midic; - - if (devc->m_left <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - } - } - break; - - case ST_SYSMSG: - switch (midic) - { - case 0xf0: - printk (""); - devc->m_state = ST_SYSEX; - break; - - case 0xf1: - devc->m_state = ST_MTC; - break; - - case 0xf2: - devc->m_state = ST_SONGPOS; - devc->m_ptr = 0; - break; - - case 0xf3: - devc->m_state = ST_SONGSEL; - break; - - case 0xf6: - /* printk("tune_request\n"); */ - devc->m_state = ST_INIT; - - /* - * Real time messages - */ - case 0xf8: - /* midi clock */ - devc->m_state = ST_INIT; - timer_ext_event (devc, TMR_CLOCK, 0); - break; - - case 0xfA: - devc->m_state = ST_INIT; - timer_ext_event (devc, TMR_START, 0); - break; - - case 0xFB: - devc->m_state = ST_INIT; - timer_ext_event (devc, TMR_CONTINUE, 0); - break; - - case 0xFC: - devc->m_state = ST_INIT; - timer_ext_event (devc, TMR_STOP, 0); - break; - - case 0xFE: - /* active sensing */ - devc->m_state = ST_INIT; - break; - - case 0xff: - /* printk("midi hard reset"); */ - devc->m_state = ST_INIT; - break; - - default: - printk ("unknown MIDI sysmsg %0x\n", midic); - devc->m_state = ST_INIT; - } - break; - - case ST_MTC: - devc->m_state = ST_INIT; - printk ("MTC frame %x02\n", midic); - break; - - case ST_SYSEX: - if (midic == 0xf7) - { - printk (""); - devc->m_state = ST_INIT; - } - else - printk ("%02x ", midic); - break; - - case ST_SONGPOS: - BUFTEST (devc); - devc->m_buf[devc->m_ptr++] = midic; - if (devc->m_ptr == 2) - { - devc->m_state = ST_INIT; - devc->m_ptr = 0; - timer_ext_event (devc, TMR_SPP, - ((devc->m_buf[1] & 0x7f) << 7) | - (devc->m_buf[0] & 0x7f)); - } - break; - - case ST_DATABYTE: - BUFTEST (devc); - devc->m_buf[devc->m_ptr++] = midic; - if ((--devc->m_left) <= 0) + switch (devc->m_state) { - devc->m_state = ST_INIT; - do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; + case ST_INIT: + switch (midic) + { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + printk(""); + break; + + case 0xfd: + if (devc->timer_flag) + mpu_timer_interrupt(); + break; + + case 0xfe: + return MPU_ACK; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + printk("", midic & 0x0f); + break; + + case 0xf9: + printk(""); + break; + + case 0xff: + devc->m_state = ST_SYSMSG; + break; + + default: + if (midic <= 0xef) + { + /* printk( "mpu time: %d ", midic); */ + devc->m_state = ST_TIMED; + } + else + printk(" ", midic); + } + break; + + case ST_TIMED: + { + int msg = ((int) (midic & 0xf0) >> 4); + + devc->m_state = ST_DATABYTE; + + if (msg < 8) /* Data byte */ + { + /* printk( "midi msg (running status) "); */ + msg = ((int) (devc->last_status & 0xf0) >> 4); + msg -= 8; + devc->m_left = len_tab[msg] - 1; + + devc->m_ptr = 2; + devc->m_buf[0] = devc->last_status; + devc->m_buf[1] = midic; + + if (devc->m_left <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + } + else if (msg == 0xf) /* MPU MARK */ + { + devc->m_state = ST_INIT; + + switch (midic) + { + case 0xf8: + /* printk( "NOP "); */ + break; + + case 0xf9: + /* printk( "meas end "); */ + break; + + case 0xfc: + /* printk( "data end "); */ + break; + + default: + printk("Unknown MPU mark %02x\n", midic); + } + } + else + { + devc->last_status = midic; + /* printk( "midi msg "); */ + msg -= 8; + devc->m_left = len_tab[msg]; + + devc->m_ptr = 1; + devc->m_buf[0] = midic; + + if (devc->m_left <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + } + } + break; + + case ST_SYSMSG: + switch (midic) + { + case 0xf0: + printk(""); + devc->m_state = ST_SYSEX; + break; + + case 0xf1: + devc->m_state = ST_MTC; + break; + + case 0xf2: + devc->m_state = ST_SONGPOS; + devc->m_ptr = 0; + break; + + case 0xf3: + devc->m_state = ST_SONGSEL; + break; + + case 0xf6: + /* printk( "tune_request\n"); */ + devc->m_state = ST_INIT; + + /* + * Real time messages + */ + case 0xf8: + /* midi clock */ + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_CLOCK, 0); + break; + + case 0xfA: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_START, 0); + break; + + case 0xFB: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_CONTINUE, 0); + break; + + case 0xFC: + devc->m_state = ST_INIT; + timer_ext_event(devc, TMR_STOP, 0); + break; + + case 0xFE: + /* active sensing */ + devc->m_state = ST_INIT; + break; + + case 0xff: + /* printk( "midi hard reset"); */ + devc->m_state = ST_INIT; + break; + + default: + printk("unknown MIDI sysmsg %0x\n", midic); + devc->m_state = ST_INIT; + } + break; + + case ST_MTC: + devc->m_state = ST_INIT; + printk("MTC frame %x02\n", midic); + break; + + case ST_SYSEX: + if (midic == 0xf7) + { + printk(""); + devc->m_state = ST_INIT; + } + else + printk("%02x ", midic); + break; + + case ST_SONGPOS: + BUFTEST(devc); + devc->m_buf[devc->m_ptr++] = midic; + if (devc->m_ptr == 2) + { + devc->m_state = ST_INIT; + devc->m_ptr = 0; + timer_ext_event(devc, TMR_SPP, + ((devc->m_buf[1] & 0x7f) << 7) | + (devc->m_buf[0] & 0x7f)); + } + break; + + case ST_DATABYTE: + BUFTEST(devc); + devc->m_buf[devc->m_ptr++] = midic; + if ((--devc->m_left) <= 0) + { + devc->m_state = ST_INIT; + do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); + devc->m_ptr = 0; + } + break; + + default: + printk("Bad state %d ", devc->m_state); + devc->m_state = ST_INIT; } - break; - - default: - printk ("Bad state %d ", devc->m_state); - devc->m_state = ST_INIT; - } - - return 1; + return 1; } -static void -mpu401_input_loop (struct mpu_config *devc) +static void mpu401_input_loop(struct mpu_config *devc) { - unsigned long flags; - int busy; - int n; + unsigned long flags; + int busy; + int n; - save_flags (flags); - cli (); - busy = devc->m_busy; - devc->m_busy = 1; - restore_flags (flags); + save_flags(flags); + cli(); + busy = devc->m_busy; + devc->m_busy = 1; + restore_flags(flags); - if (busy) /* Already inside the scanner */ - return; + if (busy) /* Already inside the scanner */ + return; - n = 50; + n = 50; - while (input_avail (devc) && n-- > 0) - { - unsigned char c = read_data (devc); - - if (devc->mode == MODE_SYNTH) + while (input_avail(devc) && n-- > 0) { - mpu_input_scanner (devc, c); + unsigned char c = read_data(devc); + + if (devc->mode == MODE_SYNTH) + { + mpu_input_scanner(devc, c); + } + else if (devc->opened & OPEN_READ && devc->inputintr != NULL) + devc->inputintr(devc->devno, c); } - else if (devc->opened & OPEN_READ && devc->inputintr != NULL) - devc->inputintr (devc->devno, c); - } - - devc->m_busy = 0; + devc->m_busy = 0; } -void -mpuintr (int irq, void *dev_id, struct pt_regs *dummy) +void mpuintr(int irq, void *dev_id, struct pt_regs *dummy) { - struct mpu_config *devc; - int dev; - - sti (); + struct mpu_config *devc; + int dev = (int) dev_id; -/* - * FreeBSD (and some others) pass unit number to the interrupt handler. - * In this case we have to scan the table for first handler. - */ + sti(); + devc = &dev_conf[dev]; - if (irq < 1 || irq > 15) - { - dev = -1; - } - else - dev = irq2dev[irq]; - - if (dev == -1) - { - int origirq = irq; - - for (irq = 0; irq <= 16; irq++) - if (irq2dev[irq] != -1) - break; - if (irq > 15) + if (input_avail(devc)) { - printk ("MPU-401: Bogus interrupt #%d?\n", origirq); - return; + if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH)) + mpu401_input_loop(devc); + else + { + /* Dummy read (just to acknowledge the interrupt) */ + read_data(devc); + } } - dev = irq2dev[irq]; - devc = &dev_conf[dev]; - } - else - devc = &dev_conf[dev]; - - if (input_avail (devc)) - if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH)) - mpu401_input_loop (devc); - else - { - /* Dummy read (just to acknowledge the interrupt) */ - read_data (devc); - } - } -static int -mpu401_open (int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) +static int mpu401_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) ) { - int err; - struct mpu_config *devc; - - if (dev < 0 || dev >= num_midis) - return -(ENXIO); - - devc = &dev_conf[dev]; - - if (devc->opened) - { - printk ("MPU-401: Midi busy\n"); - return -(EBUSY); - } - - /* - * Verify that the device is really running. - * Some devices (such as Ensoniq SoundScape don't - * work before the on board processor (OBP) is initialized - * by downloading its microcode. - */ - - if (!devc->initialized) - { - if (mpu401_status (devc) == 0xff) /* Bus float */ - { - printk ("MPU-401: Device not initialized properly\n"); - return -(EIO); - } - reset_mpu401 (devc); - } + int err; + struct mpu_config *devc; + + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + return -ENXIO; - irq2dev[devc->irq] = dev; + devc = &dev_conf[dev]; - if (midi_devs[dev]->coproc) - if ((err = midi_devs[dev]->coproc-> - open (midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0) - { - printk ("MPU-401: Can't access coprocessor device\n"); + if (devc->opened) + return -EBUSY; + /* + * Verify that the device is really running. + * Some devices (such as Ensoniq SoundScape don't + * work before the on board processor (OBP) is initialized + * by downloading its microcode. + */ - return err; - } + if (!devc->initialized) + { + if (mpu401_status(devc) == 0xff) /* Bus float */ + { + printk(KERN_ERR "mpu401: Device not initialized properly\n"); + return -EIO; + } + reset_mpu401(devc); + } - set_uart_mode (dev, devc, 1); - devc->mode = MODE_MIDI; - devc->synthno = 0; + if (midi_devs[dev]->coproc) + { + if ((err = midi_devs[dev]->coproc-> + open(midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0) + { + printk("MPU-401: Can't access coprocessor device\n"); + return err; + } + } + + set_uart_mode(dev, devc, 1); + devc->mode = MODE_MIDI; + devc->synthno = 0; - mpu401_input_loop (devc); + mpu401_input_loop(devc); - devc->inputintr = input; - devc->opened = mode; + devc->inputintr = input; + devc->opened = mode; - return 0; + return 0; } -static void -mpu401_close (int dev) +static void mpu401_close(int dev) { - struct mpu_config *devc; - - devc = &dev_conf[dev]; - - if (devc->uart_mode) - reset_mpu401 (devc); /* - * This disables the UART mode - */ - devc->mode = 0; + struct mpu_config *devc; - devc->inputintr = NULL; + devc = &dev_conf[dev]; + if (devc->uart_mode) + reset_mpu401(devc); /* + * This disables the UART mode + */ + devc->mode = 0; + devc->inputintr = NULL; - if (midi_devs[dev]->coproc) - midi_devs[dev]->coproc->close (midi_devs[dev]->coproc->devc, COPR_MIDI); - devc->opened = 0; + if (midi_devs[dev]->coproc) + midi_devs[dev]->coproc->close(midi_devs[dev]->coproc->devc, COPR_MIDI); + devc->opened = 0; } -static int -mpu401_out (int dev, unsigned char midi_byte) +static int mpu401_out(int dev, unsigned char midi_byte) { - int timeout; - unsigned long flags; - - struct mpu_config *devc; + int timeout; + unsigned long flags; - devc = &dev_conf[dev]; + struct mpu_config *devc; - /* - * Sometimes it takes about 30000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ + devc = &dev_conf[dev]; - for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); + /* + * Sometimes it takes about 30000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ - save_flags (flags); - cli (); - if (!output_ready (devc)) - { - printk ("MPU-401: Send data timeout\n"); - restore_flags (flags); - return 0; - } + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - write_data (devc, midi_byte); - restore_flags (flags); - return 1; + save_flags(flags); + cli(); + if (!output_ready(devc)) + { + printk(KERN_WARNING "mpu401: Send data timeout\n"); + restore_flags(flags); + return 0; + } + write_data(devc, midi_byte); + restore_flags(flags); + return 1; } -static int -mpu401_command (int dev, mpu_command_rec * cmd) +static int mpu401_command(int dev, mpu_command_rec * cmd) { - int i, timeout, ok; - int ret = 0; - unsigned long flags; - struct mpu_config *devc; + int i, timeout, ok; + int ret = 0; + unsigned long flags; + struct mpu_config *devc; - devc = &dev_conf[dev]; + devc = &dev_conf[dev]; - if (devc->uart_mode) /* + if (devc->uart_mode) /* * Not possible in UART mode */ - { - printk ("MPU-401 commands not possible in the UART mode\n"); - return -(EINVAL); - } - - /* - * Test for input since pending input seems to block the output. - */ - if (input_avail (devc)) - mpu401_input_loop (devc); - - /* - * Sometimes it takes about 50000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - timeout = 50000; + { + printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n"); + return -EINVAL; + } + /* + * Test for input since pending input seems to block the output. + */ + if (input_avail(devc)) + mpu401_input_loop(devc); + + /* + * Sometimes it takes about 50000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + timeout = 50000; retry: - if (timeout-- <= 0) - { - printk ("MPU-401: Command (0x%x) timeout\n", (int) cmd->cmd); - return -(EIO); - } - - save_flags (flags); - cli (); - - if (!output_ready (devc)) - { - restore_flags (flags); - goto retry; - } - - write_command (devc, cmd->cmd); - - ok = 0; - for (timeout = 50000; timeout > 0 && !ok; timeout--) - if (input_avail (devc)) - { - if (devc->opened && devc->mode == MODE_SYNTH) - { - if (mpu_input_scanner (devc, read_data (devc)) == MPU_ACK) - ok = 1; - } - else - { /* Device is not currently open. Use simpler method */ - if (read_data (devc) == MPU_ACK) - ok = 1; - } - } - - if (!ok) - { - restore_flags (flags); - /* printk ("MPU: No ACK to command (0x%x)\n", (int) cmd->cmd); */ - return -(EIO); - } - - if (cmd->nr_args) - for (i = 0; i < cmd->nr_args; i++) - { - for (timeout = 3000; timeout > 0 && !output_ready (devc); timeout--); - - if (!mpu401_out (dev, cmd->data[i])) - { - restore_flags (flags); - printk ("MPU: Command (0x%x), parm send failed.\n", (int) cmd->cmd); - return -(EIO); - } - } - - ret = 0; - cmd->data[0] = 0; - - if (cmd->nr_returns) - for (i = 0; i < cmd->nr_returns; i++) - { - ok = 0; - for (timeout = 5000; timeout > 0 && !ok; timeout--) - if (input_avail (devc)) - { - cmd->data[i] = read_data (devc); - ok = 1; - } + if (timeout-- <= 0) + { + printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd); + return -EIO; + } + save_flags(flags); + cli(); - if (!ok) - { - restore_flags (flags); - /* printk ("MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd); */ - return -(EIO); - } - } + if (!output_ready(devc)) + { + restore_flags(flags); + goto retry; + } + write_command(devc, cmd->cmd); - restore_flags (flags); + ok = 0; + for (timeout = 50000; timeout > 0 && !ok; timeout--) + { + if (input_avail(devc)) + { + if (devc->opened && devc->mode == MODE_SYNTH) + { + if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK) + ok = 1; + } + else + { + /* Device is not currently open. Use simpler method */ + if (read_data(devc) == MPU_ACK) + ok = 1; + } + } + } + if (!ok) + { + restore_flags(flags); + return -EIO; + } + if (cmd->nr_args) + { + for (i = 0; i < cmd->nr_args; i++) + { + for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--); + + if (!mpu401_out(dev, cmd->data[i])) + { + restore_flags(flags); + printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd); + return -EIO; + } + } + } + ret = 0; + cmd->data[0] = 0; - return ret; + if (cmd->nr_returns) + { + for (i = 0; i < cmd->nr_returns; i++) + { + ok = 0; + for (timeout = 5000; timeout > 0 && !ok; timeout--) + if (input_avail(devc)) + { + cmd->data[i] = read_data(devc); + ok = 1; + } + if (!ok) + { + restore_flags(flags); + return -EIO; + } + } + } + restore_flags(flags); + return ret; } -static int -mpu_cmd (int dev, int cmd, int data) +static int mpu_cmd(int dev, int cmd, int data) { - int ret; + int ret; - static mpu_command_rec rec; + static mpu_command_rec rec; - rec.cmd = cmd & 0xff; - rec.nr_args = ((cmd & 0xf0) == 0xE0); - rec.nr_returns = ((cmd & 0xf0) == 0xA0); - rec.data[0] = data & 0xff; + rec.cmd = cmd & 0xff; + rec.nr_args = ((cmd & 0xf0) == 0xE0); + rec.nr_returns = ((cmd & 0xf0) == 0xA0); + rec.data[0] = data & 0xff; - if ((ret = mpu401_command (dev, &rec)) < 0) - { - return ret; - } - return (unsigned char) rec.data[0]; + if ((ret = mpu401_command(dev, &rec)) < 0) + return ret; + return (unsigned char) rec.data[0]; } -static int -mpu401_prefix_cmd (int dev, unsigned char status) +static int mpu401_prefix_cmd(int dev, unsigned char status) { - struct mpu_config *devc = &dev_conf[dev]; + struct mpu_config *devc = &dev_conf[dev]; - if (devc->uart_mode) - return 1; + if (devc->uart_mode) + return 1; - if (status < 0xf0) - { - if (mpu_cmd (dev, 0xD0, 0) < 0) + if (status < 0xf0) { - return 0; + if (mpu_cmd(dev, 0xD0, 0) < 0) + return 0; + return 1; } - - return 1; - } - - switch (status) - { - case 0xF0: - if (mpu_cmd (dev, 0xDF, 0) < 0) + switch (status) { - return 0; - } - - return 1; - break; - - default: - return 0; - } + case 0xF0: + if (mpu_cmd(dev, 0xDF, 0) < 0) + return 0; + return 1; + default: + return 0; + } } -static int -mpu401_start_read (int dev) +static int mpu401_start_read(int dev) { - return 0; + return 0; } -static int -mpu401_end_read (int dev) +static int mpu401_end_read(int dev) { - return 0; + return 0; } -static int -mpu401_ioctl (int dev, unsigned cmd, caddr_t arg) +static int mpu401_ioctl(int dev, unsigned cmd, caddr_t arg) { - struct mpu_config *devc; - - devc = &dev_conf[dev]; - - switch (cmd) - { - case 1: - memcpy_fromfs ((char *) init_sequence, &((char *) arg)[0], sizeof (init_sequence)); - return 0; - break; + struct mpu_config *devc; + mpu_command_rec rec; + int val, ret; - case SNDCTL_MIDI_MPUMODE: - if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */ + devc = &dev_conf[dev]; + switch (cmd) { - printk ("MPU-401: Intelligent mode not supported by the HW\n"); - return -(EINVAL); + case SNDCTL_MIDI_MPUMODE: + if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */ + printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n"); + return -EINVAL; + } + if (get_user(val, (int *)arg)) + return -EFAULT; + set_uart_mode(dev, devc, !val); + return 0; + + case SNDCTL_MIDI_MPUCMD: + if (copy_from_user(&rec, arg, sizeof(rec))) + return -EFAULT; + if ((ret = mpu401_command(dev, &rec)) < 0) + return ret; + if (copy_to_user(arg, &rec, sizeof(rec))) + return -EFAULT; + return 0; + + default: + return -EINVAL; } - set_uart_mode (dev, devc, !get_user ((int *) arg)); - return 0; - break; - - case SNDCTL_MIDI_MPUCMD: - { - int ret; - mpu_command_rec rec; - - memcpy_fromfs ((char *) &rec, &((char *) arg)[0], sizeof (rec)); - - if ((ret = mpu401_command (dev, &rec)) < 0) - return ret; - - memcpy_tofs (&((char *) arg)[0], (char *) &rec, sizeof (rec)); - return 0; - } - break; - - default: - return -(EINVAL); - } } -static void -mpu401_kick (int dev) +static void mpu401_kick(int dev) { } -static int -mpu401_buffer_status (int dev) +static int mpu401_buffer_status(int dev) { - return 0; /* + return 0; /* * No data in buffers */ } -static int -mpu_synth_ioctl (int dev, - unsigned int cmd, caddr_t arg) +static int mpu_synth_ioctl(int dev, + unsigned int cmd, caddr_t arg) { - int midi_dev; - struct mpu_config *devc; + int midi_dev; + struct mpu_config *devc; - midi_dev = synth_devs[dev]->midi_dev; + midi_dev = synth_devs[dev]->midi_dev; - if (midi_dev < 0 || midi_dev > num_midis) - return -(ENXIO); + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL) + return -ENXIO; - devc = &dev_conf[midi_dev]; + devc = &dev_conf[midi_dev]; - switch (cmd) - { - - case SNDCTL_SYNTH_INFO: - memcpy_tofs (&((char *) arg)[0], &mpu_synth_info[midi_dev], sizeof (struct synth_info)); + switch (cmd) + { - return 0; - break; + case SNDCTL_SYNTH_INFO: + memcpy((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof(struct synth_info)); + return 0; - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - break; + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; - default: - return -(EINVAL); - } + default: + return -EINVAL; + } } -static int -mpu_synth_open (int dev, int mode) +static int mpu_synth_open(int dev, int mode) { - int midi_dev, err; - struct mpu_config *devc; + int midi_dev, err; + struct mpu_config *devc; - midi_dev = synth_devs[dev]->midi_dev; + midi_dev = synth_devs[dev]->midi_dev; - if (midi_dev < 0 || midi_dev > num_midis) - { - return -(ENXIO); - } + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL) + return -ENXIO; - devc = &dev_conf[midi_dev]; + devc = &dev_conf[midi_dev]; - /* - * Verify that the device is really running. - * Some devices (such as Ensoniq SoundScape don't - * work before the on board processor (OBP) is initialized - * by downloading its microcode. - */ + /* + * Verify that the device is really running. + * Some devices (such as Ensoniq SoundScape don't + * work before the on board processor (OBP) is initialized + * by downloading its microcode. + */ - if (!devc->initialized) - { - if (mpu401_status (devc) == 0xff) /* Bus float */ + if (!devc->initialized) { - printk ("MPU-401: Device not initialized properly\n"); - return -(EIO); + if (mpu401_status(devc) == 0xff) /* Bus float */ + { + printk(KERN_ERR "mpu401: Device not initialized properly\n"); + return -EIO; + } + reset_mpu401(devc); } - reset_mpu401 (devc); - } - - if (devc->opened) - { - printk ("MPU-401: Midi busy\n"); - return -(EBUSY); - } - - devc->mode = MODE_SYNTH; - devc->synthno = dev; - - devc->inputintr = NULL; - irq2dev[devc->irq] = midi_dev; - - if (midi_devs[midi_dev]->coproc) - if ((err = midi_devs[midi_dev]->coproc-> - open (midi_devs[midi_dev]->coproc->devc, COPR_MIDI)) < 0) - { - printk ("MPU-401: Can't access coprocessor device\n"); - - return err; - } - - devc->opened = mode; - reset_mpu401 (devc); - - if (mode & OPEN_READ) - { - mpu_cmd (midi_dev, 0x8B, 0); /* Enable data in stop mode */ - mpu_cmd (midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ - mpu_cmd (midi_dev, 0x87, 0); /* Enable pitch & controller */ - } - - return 0; + if (devc->opened) + return -EBUSY; + devc->mode = MODE_SYNTH; + devc->synthno = dev; + + devc->inputintr = NULL; + + if (midi_devs[midi_dev]->coproc) + if ((err = midi_devs[midi_dev]->coproc-> + open(midi_devs[midi_dev]->coproc->devc, COPR_MIDI)) < 0) + { + printk(KERN_WARNING "mpu401: Can't access coprocessor device\n"); + return err; + } + devc->opened = mode; + reset_mpu401(devc); + + if (mode & OPEN_READ) + { + mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */ + mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ + mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */ + } + return 0; } -static void -mpu_synth_close (int dev) -{ - int midi_dev; - struct mpu_config *devc; +static void mpu_synth_close(int dev) +{ + int midi_dev; + struct mpu_config *devc; - midi_dev = synth_devs[dev]->midi_dev; + midi_dev = synth_devs[dev]->midi_dev; - devc = &dev_conf[midi_dev]; - mpu_cmd (midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */ - mpu_cmd (midi_dev, 0x8a, 0); /* Disable data in stopped mode */ + devc = &dev_conf[midi_dev]; + mpu_cmd(midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */ + mpu_cmd(midi_dev, 0x8a, 0); /* Disable data in stopped mode */ - devc->inputintr = NULL; + devc->inputintr = NULL; - if (midi_devs[midi_dev]->coproc) - midi_devs[midi_dev]->coproc->close (midi_devs[midi_dev]->coproc->devc, COPR_MIDI); - devc->opened = 0; - devc->mode = 0; + if (midi_devs[midi_dev]->coproc) + midi_devs[midi_dev]->coproc->close(midi_devs[midi_dev]->coproc->devc, COPR_MIDI); + devc->opened = 0; + devc->mode = 0; } #define MIDI_SYNTH_NAME "MPU-401 UART Midi" @@ -951,367 +865,351 @@ mpu_synth_close (int dev) static struct synth_operations mpu401_synth_proto = { - NULL, - 0, - SYNTH_TYPE_MIDI, - 0, - mpu_synth_open, - mpu_synth_close, - mpu_synth_ioctl, - midi_synth_kill_note, - midi_synth_start_note, - midi_synth_set_instr, - midi_synth_reset, - midi_synth_hw_control, - midi_synth_load_patch, - midi_synth_aftertouch, - midi_synth_controller, - midi_synth_panning, - NULL, - midi_synth_patchmgr, - midi_synth_bender, - NULL, /* alloc */ - midi_synth_setup_voice, - midi_synth_send_sysex + "MPU401", + NULL, + 0, + SYNTH_TYPE_MIDI, + 0, + mpu_synth_open, + mpu_synth_close, + mpu_synth_ioctl, + midi_synth_kill_note, + midi_synth_start_note, + midi_synth_set_instr, + midi_synth_reset, + midi_synth_hw_control, + midi_synth_load_patch, + midi_synth_aftertouch, + midi_synth_controller, + midi_synth_panning, + NULL, + midi_synth_bender, + NULL, /* alloc */ + midi_synth_setup_voice, + midi_synth_send_sysex }; static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV]; static struct midi_operations mpu401_midi_proto = { - {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, - NULL, - {0}, - mpu401_open, - mpu401_close, - mpu401_ioctl, - mpu401_out, - mpu401_start_read, - mpu401_end_read, - mpu401_kick, - NULL, - mpu401_buffer_status, - mpu401_prefix_cmd + {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, + NULL, + {0}, + mpu401_open, + mpu401_close, + mpu401_ioctl, + mpu401_out, + mpu401_start_read, + mpu401_end_read, + mpu401_kick, + NULL, + mpu401_buffer_status, + mpu401_prefix_cmd }; static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV]; -static void -mpu401_chk_version (struct mpu_config *devc) +static void mpu401_chk_version(int n, struct mpu_config *devc) { - int tmp; - unsigned long flags; - - devc->version = devc->revision = 0; - - save_flags (flags); - cli (); - if ((tmp = mpu_cmd (num_midis, 0xAC, 0)) < 0) - { - restore_flags (flags); - return; - } - - if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */ - { - restore_flags (flags); - return; - } - - devc->version = tmp; - - if ((tmp = mpu_cmd (num_midis, 0xAD, 0)) < 0) - { - devc->version = 0; - restore_flags (flags); - return; - } - devc->revision = tmp; - - restore_flags (flags); -} + int tmp; + unsigned long flags; -void -attach_mpu401 (struct address_info *hw_config) -{ - unsigned long flags; - char revision_char; - - struct mpu_config *devc; - - if (num_midis >= MAX_MIDI_DEV) - { - printk ("MPU-401: Too many midi devices detected\n"); - return; - } - - devc = &dev_conf[num_midis]; - - devc->base = hw_config->io_base; - devc->osp = hw_config->osp; - devc->irq = hw_config->irq; - devc->opened = 0; - devc->uart_mode = 0; - devc->initialized = 0; - devc->version = 0; - devc->revision = 0; - devc->capabilities = 0; - devc->timer_flag = 0; - devc->m_busy = 0; - devc->m_state = ST_INIT; - devc->shared_irq = hw_config->always_detect; - devc->irq = hw_config->irq; - - if (devc->irq < 0) - { - devc->irq *= -1; - devc->shared_irq = 1; - } - irq2dev[devc->irq] = num_midis; - - if (!hw_config->always_detect) - { - /* Verify the hardware again */ - if (!reset_mpu401 (devc)) + devc->version = devc->revision = 0; + + save_flags(flags); + cli(); + if ((tmp = mpu_cmd(n, 0xAC, 0)) < 0) { - printk ("MPU401: Device didn't respond\n"); - return; + restore_flags(flags); + return; } + if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */ + { + restore_flags(flags); + return; + } + devc->version = tmp; - if (!devc->shared_irq) - if (snd_set_irq_handler (devc->irq, mpuintr, "mpu401", devc->osp) < 0) - { - printk ("MPU401: Failed to allocate IRQ%d\n", devc->irq); - return; - } - - save_flags (flags); - cli (); - mpu401_chk_version (devc); - if (devc->version == 0) - mpu401_chk_version (devc); - restore_flags (flags); - } - - request_region (hw_config->io_base, 2, "mpu401"); - - if (devc->version != 0) - if (mpu_cmd (num_midis, 0xC5, 0) >= 0) /* Set timebase OK */ - if (mpu_cmd (num_midis, 0xE0, 120) >= 0) /* Set tempo OK */ - devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */ - - - mpu401_synth_operations[num_midis] = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations))); - - if (sound_nblocks < 1024) - sound_nblocks++;; - - if (mpu401_synth_operations[num_midis] == NULL) - { - printk ("mpu401: Can't allocate memory\n"); - return; - } - - if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */ - { - memcpy ((char *) mpu401_synth_operations[num_midis], - (char *) &std_midi_synth, - sizeof (struct synth_operations)); - } - else - { - memcpy ((char *) mpu401_synth_operations[num_midis], - (char *) &mpu401_synth_proto, - sizeof (struct synth_operations)); - } - - memcpy ((char *) &mpu401_midi_operations[num_midis], - (char *) &mpu401_midi_proto, - sizeof (struct midi_operations)); - - mpu401_midi_operations[num_midis].converter = - mpu401_synth_operations[num_midis]; - - memcpy ((char *) &mpu_synth_info[num_midis], - (char *) &mpu_synth_info_proto, - sizeof (struct synth_info)); - - n_mpu_devs++; - - if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */ - { - int ports = (devc->revision & 0x08) ? 32 : 16; - - devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | - MPU_CAP_CLS | MPU_CAP_2PORT; - - revision_char = (devc->revision == 0x7f) ? 'M' : ' '; - sprintf (mpu_synth_info[num_midis].name, - "MQX-%d%c MIDI Interface #%d", - ports, - revision_char, - n_mpu_devs); - } - else - { - - revision_char = devc->revision ? devc->revision + '@' : ' '; - if ((int) devc->revision > ('Z' - '@')) - revision_char = '+'; - - devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; - - if (hw_config->name) - sprintf (mpu_synth_info[num_midis].name, "%s (MPU401)", hw_config->name); - else - sprintf (mpu_synth_info[num_midis].name, - "MPU-401 %d.%d%c Midi interface #%d", - (int) (devc->version & 0xf0) >> 4, - devc->version & 0x0f, - revision_char, - n_mpu_devs); - } - - strcpy (mpu401_midi_operations[num_midis].info.name, - mpu_synth_info[num_midis].name); - - conf_printf (mpu_synth_info[num_midis].name, hw_config); - - mpu401_synth_operations[num_midis]->midi_dev = devc->devno = num_midis; - mpu401_synth_operations[devc->devno]->info = - &mpu_synth_info[devc->devno]; - - if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */ - mpu_timer_init (num_midis); - - irq2dev[devc->irq] = num_midis; - midi_devs[num_midis++] = &mpu401_midi_operations[devc->devno]; + if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0) + { + devc->version = 0; + restore_flags(flags); + return; + } + devc->revision = tmp; + restore_flags(flags); } -static int -reset_mpu401 (struct mpu_config *devc) +void attach_mpu401(struct address_info *hw_config) { - unsigned long flags; - int ok, timeout, n; - int timeout_limit; + unsigned long flags; + char revision_char; - /* - * Send the RESET command. Try again if no success at the first time. - * (If the device is in the UART mode, it will not ack the reset cmd). - */ + int m; + struct mpu_config *devc; - ok = 0; + hw_config->slots[1] = -1; + m = sound_alloc_mididev(); + if (m == -1) + { + printk(KERN_WARNING "MPU-401: Too many midi devices detected\n"); + return; + } + devc = &dev_conf[m]; + devc->base = hw_config->io_base; + devc->osp = hw_config->osp; + devc->irq = hw_config->irq; + devc->opened = 0; + devc->uart_mode = 0; + devc->initialized = 0; + devc->version = 0; + devc->revision = 0; + devc->capabilities = 0; + devc->timer_flag = 0; + devc->m_busy = 0; + devc->m_state = ST_INIT; + devc->shared_irq = hw_config->always_detect; + devc->irq = hw_config->irq; + + if (devc->irq < 0) + { + devc->irq *= -1; + devc->shared_irq = 1; + } - timeout_limit = devc->initialized ? 30000 : 100000; - devc->initialized = 1; + if (!hw_config->always_detect) + { + /* Verify the hardware again */ + if (!reset_mpu401(devc)) + { + printk(KERN_WARNING "mpu401: Device didn't respond\n"); + sound_unload_mididev(m); + return; + } + if (!devc->shared_irq) + { + if (request_irq(devc->irq, mpuintr, 0, "mpu401", (void *)m) < 0) + { + printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq); + sound_unload_mididev(m); + return; + } + } + save_flags(flags); + cli(); + mpu401_chk_version(m, devc); + if (devc->version == 0) + mpu401_chk_version(m, devc); + restore_flags(flags); + } + request_region(hw_config->io_base, 2, "mpu401"); - for (n = 0; n < 2 && !ok; n++) - { - for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) - ok = output_ready (devc); + if (devc->version != 0) + if (mpu_cmd(m, 0xC5, 0) >= 0) /* Set timebase OK */ + if (mpu_cmd(m, 0xE0, 120) >= 0) /* Set tempo OK */ + devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */ - write_command (devc, MPU_RESET); /* - * Send MPU-401 RESET Command - */ - /* - * Wait at least 25 msec. This method is not accurate so let's make the - * loop bit longer. Cannot sleep since this is called during boot. - */ + mpu401_synth_operations[m] = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); + + if (mpu401_synth_operations[m] == NULL) + { + sound_unload_mididev(m); + printk(KERN_ERR "mpu401: Can't allocate memory\n"); + return; + } + if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */ + { + memcpy((char *) mpu401_synth_operations[m], + (char *) &std_midi_synth, + sizeof(struct synth_operations)); + } + else + { + memcpy((char *) mpu401_synth_operations[m], + (char *) &mpu401_synth_proto, + sizeof(struct synth_operations)); + } + + memcpy((char *) &mpu401_midi_operations[m], + (char *) &mpu401_midi_proto, + sizeof(struct midi_operations)); + + mpu401_midi_operations[m].converter = mpu401_synth_operations[m]; + + memcpy((char *) &mpu_synth_info[m], + (char *) &mpu_synth_info_proto, + sizeof(struct synth_info)); + + n_mpu_devs++; + + if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */ + { + int ports = (devc->revision & 0x08) ? 32 : 16; + + devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | + MPU_CAP_CLS | MPU_CAP_2PORT; - for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) + revision_char = (devc->revision == 0x7f) ? 'M' : ' '; + sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d", + ports, + revision_char, + n_mpu_devs); + } + else { - save_flags (flags); - cli (); - if (input_avail (devc)) - if (read_data (devc) == MPU_ACK) - ok = 1; - restore_flags (flags); + revision_char = devc->revision ? devc->revision + '@' : ' '; + if ((int) devc->revision > ('Z' - '@')) + revision_char = '+'; + + devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; + + if (hw_config->name) + sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name); + else + sprintf(mpu_synth_info[m].name, + "MPU-401 %d.%d%c Midi interface #%d", + (int) (devc->version & 0xf0) >> 4, + devc->version & 0x0f, + revision_char, + n_mpu_devs); } - } + strcpy(mpu401_midi_operations[m].info.name, + mpu_synth_info[m].name); - devc->m_state = ST_INIT; - devc->m_ptr = 0; - devc->m_left = 0; - devc->last_status = 0; - devc->uart_mode = 0; + conf_printf(mpu_synth_info[m].name, hw_config); - return ok; + mpu401_synth_operations[m]->midi_dev = devc->devno = m; + mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno]; + + if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */ + hw_config->slots[2] = mpu_timer_init(m); + + midi_devs[m] = &mpu401_midi_operations[devc->devno]; + hw_config->slots[1] = m; + sequencer_init(); } -static void -set_uart_mode (int dev, struct mpu_config *devc, int arg) +static int reset_mpu401(struct mpu_config *devc) { - if (!arg && (devc->capabilities & MPU_CAP_INTLG)) - { - return; - } + unsigned long flags; + int ok, timeout, n; + int timeout_limit; + + /* + * Send the RESET command. Try again if no success at the first time. + * (If the device is in the UART mode, it will not ack the reset cmd). + */ + + ok = 0; + + timeout_limit = devc->initialized ? 30000 : 100000; + devc->initialized = 1; + + for (n = 0; n < 2 && !ok; n++) + { + for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) + ok = output_ready(devc); + + write_command(devc, MPU_RESET); /* + * Send MPU-401 RESET Command + */ + + /* + * Wait at least 25 msec. This method is not accurate so let's make the + * loop bit longer. Cannot sleep since this is called during boot. + */ + + for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) + { + save_flags(flags); + cli(); + if (input_avail(devc)) + if (read_data(devc) == MPU_ACK) + ok = 1; + restore_flags(flags); + } + + } - if ((devc->uart_mode == 0) == (arg == 0)) - { - return; /* Already set */ - } + devc->m_state = ST_INIT; + devc->m_ptr = 0; + devc->m_left = 0; + devc->last_status = 0; + devc->uart_mode = 0; - reset_mpu401 (devc); /* This exits the uart mode */ + return ok; +} + +static void set_uart_mode(int dev, struct mpu_config *devc, int arg) +{ + if (!arg && (devc->capabilities & MPU_CAP_INTLG)) + return; + if ((devc->uart_mode == 0) == (arg == 0)) + return; /* Already set */ + reset_mpu401(devc); /* This exits the uart mode */ - if (arg) - { - if (mpu_cmd (dev, UART_MODE_ON, 0) < 0) + if (arg) { - printk ("MPU%d: Can't enter UART mode\n", devc->devno); - devc->uart_mode = 0; - return; + if (mpu_cmd(dev, UART_MODE_ON, 0) < 0) + { + printk(KERN_ERR "mpu401: Can't enter UART mode\n"); + devc->uart_mode = 0; + return; + } } - } - devc->uart_mode = arg; + devc->uart_mode = arg; } -int -probe_mpu401 (struct address_info *hw_config) +int probe_mpu401(struct address_info *hw_config) { - int ok = 0; - struct mpu_config tmp_devc; - - if (check_region (hw_config->io_base, 2)) - { - printk ("\n\nmpu401.c: I/O port %x already in use\n\n", - hw_config->io_base); - return 0; - } - - tmp_devc.base = hw_config->io_base; - tmp_devc.irq = hw_config->irq; - tmp_devc.initialized = 0; - tmp_devc.opened = 0; - tmp_devc.osp = hw_config->osp; - - if (hw_config->always_detect) - return 1; - - if (inb (hw_config->io_base + 1) == 0xff) - { - DDB (printk ("MPU401: Port %x looks dead.\n", hw_config->io_base)); - return 0; /* Just bus float? */ - } - - ok = reset_mpu401 (&tmp_devc); - - if (!ok) - { - DDB (printk ("MPU401: Reset failed on port %x\n", hw_config->io_base)); - } - - return ok; + int ok = 0; + struct mpu_config tmp_devc; + + if (check_region(hw_config->io_base, 2)) + { + printk(KERN_ERR "mpu401: I/O port %x already in use\n\n", hw_config->io_base); + return 0; + } + tmp_devc.base = hw_config->io_base; + tmp_devc.irq = hw_config->irq; + tmp_devc.initialized = 0; + tmp_devc.opened = 0; + tmp_devc.osp = hw_config->osp; + + if (hw_config->always_detect) + return 1; + + if (inb(hw_config->io_base + 1) == 0xff) + { + DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base)); + return 0; /* Just bus float? */ + } + ok = reset_mpu401(&tmp_devc); + + if (!ok) + { + DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base)); + } + return ok; } -void -unload_mpu401 (struct address_info *hw_config) +void unload_mpu401(struct address_info *hw_config) { - release_region (hw_config->io_base, 2); - if (hw_config->always_detect == 0 && hw_config->irq > 0) - snd_release_irq (hw_config->irq); + void *p; + int n=hw_config->slots[1]; + + release_region(hw_config->io_base, 2); + if (hw_config->always_detect == 0 && hw_config->irq > 0) + free_irq(hw_config->irq, (void *)n); + p=mpu401_synth_operations[n]; + sound_unload_mididev(n); + sound_unload_timerdev(hw_config->slots[2]); + if(p) + kfree(p); } /***************************************************** @@ -1328,511 +1226,536 @@ static volatile unsigned long curr_ticks, curr_clocks; static unsigned long prev_event_time; static int metronome_mode; -static unsigned long -clocks2ticks (unsigned long clocks) +static unsigned long clocks2ticks(unsigned long clocks) { - /* - * The MPU-401 supports just a limited set of possible timebase values. - * Since the applications require more choices, the driver has to - * program the HW to do its best and to convert between the HW and - * actual timebases. - */ - - return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase; + /* + * The MPU-401 supports just a limited set of possible timebase values. + * Since the applications require more choices, the driver has to + * program the HW to do its best and to convert between the HW and + * actual timebases. + */ + return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase; } -static void -set_timebase (int midi_dev, int val) +static void set_timebase(int midi_dev, int val) { - int hw_val; - - if (val < 48) - val = 48; - if (val > 1000) - val = 1000; - - hw_val = val; - hw_val = (hw_val + 12) / 24; - if (hw_val > max_timebase) - hw_val = max_timebase; - - if (mpu_cmd (midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0) - { - printk ("MPU: Can't set HW timebase to %d\n", hw_val * 24); - return; - } - hw_timebase = hw_val * 24; - curr_timebase = val; + int hw_val; + + if (val < 48) + val = 48; + if (val > 1000) + val = 1000; + + hw_val = val; + hw_val = (hw_val + 12) / 24; + if (hw_val > max_timebase) + hw_val = max_timebase; + + if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0) + { + printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24); + return; + } + hw_timebase = hw_val * 24; + curr_timebase = val; } -static void -tmr_reset (void) +static void tmr_reset(void) { - unsigned long flags; - - save_flags (flags); - cli (); - next_event_time = (unsigned long) -1; - prev_event_time = 0; - curr_ticks = curr_clocks = 0; - restore_flags (flags); + unsigned long flags; + + save_flags(flags); + cli(); + next_event_time = (unsigned long) -1; + prev_event_time = 0; + curr_ticks = curr_clocks = 0; + restore_flags(flags); } -static void -set_timer_mode (int midi_dev) +static void set_timer_mode(int midi_dev) { - if (timer_mode & TMR_MODE_CLS) - mpu_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */ - else if (timer_mode & TMR_MODE_SMPTE) - mpu_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */ - - if (timer_mode & TMR_INTERNAL) - { - mpu_cmd (midi_dev, 0x80, 0); /* Use MIDI sync */ - } - else - { - if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) + if (timer_mode & TMR_MODE_CLS) + mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ + else if (timer_mode & TMR_MODE_SMPTE) + mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ + + if (timer_mode & TMR_INTERNAL) + { + mpu_cmd(midi_dev, 0x80, 0); /* Use MIDI sync */ + } + else { - mpu_cmd (midi_dev, 0x82, 0); /* Use MIDI sync */ - mpu_cmd (midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ + if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) + { + mpu_cmd(midi_dev, 0x82, 0); /* Use MIDI sync */ + mpu_cmd(midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ + } + else if (timer_mode & TMR_MODE_FSK) + mpu_cmd(midi_dev, 0x81, 0); /* Use FSK sync */ } - else if (timer_mode & TMR_MODE_FSK) - mpu_cmd (midi_dev, 0x81, 0); /* Use FSK sync */ - } } -static void -stop_metronome (int midi_dev) +static void stop_metronome(int midi_dev) { - mpu_cmd (midi_dev, 0x84, 0); /* Disable metronome */ + mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ } -static void -setup_metronome (int midi_dev) +static void setup_metronome(int midi_dev) { - int numerator, denominator; - int clks_per_click, num_32nds_per_beat; - int beats_per_measure; - - numerator = ((unsigned) metronome_mode >> 24) & 0xff; - denominator = ((unsigned) metronome_mode >> 16) & 0xff; - clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff; - num_32nds_per_beat = (unsigned) metronome_mode & 0xff; - beats_per_measure = (numerator * 4) >> denominator; - - if (!metronome_mode) - mpu_cmd (midi_dev, 0x84, 0); /* Disable metronome */ - else - { - mpu_cmd (midi_dev, 0xE4, clks_per_click); - mpu_cmd (midi_dev, 0xE6, beats_per_measure); - mpu_cmd (midi_dev, 0x83, 0); /* Enable metronome without accents */ - } + int numerator, denominator; + int clks_per_click, num_32nds_per_beat; + int beats_per_measure; + + numerator = ((unsigned) metronome_mode >> 24) & 0xff; + denominator = ((unsigned) metronome_mode >> 16) & 0xff; + clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff; + num_32nds_per_beat = (unsigned) metronome_mode & 0xff; + beats_per_measure = (numerator * 4) >> denominator; + + if (!metronome_mode) + mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ + else + { + mpu_cmd(midi_dev, 0xE4, clks_per_click); + mpu_cmd(midi_dev, 0xE6, beats_per_measure); + mpu_cmd(midi_dev, 0x83, 0); /* Enable metronome without accents */ + } } -static int -mpu_start_timer (int midi_dev) +static int mpu_start_timer(int midi_dev) { - tmr_reset (); - set_timer_mode (midi_dev); - - if (tmr_running) - return TIMER_NOT_ARMED; /* Already running */ - - if (timer_mode & TMR_INTERNAL) - { - mpu_cmd (midi_dev, 0x02, 0); /* Send MIDI start */ - tmr_running = 1; - return TIMER_NOT_ARMED; - } - else - { - mpu_cmd (midi_dev, 0x35, 0); /* Enable mode messages to PC */ - mpu_cmd (midi_dev, 0x38, 0); /* Enable sys common messages to PC */ - mpu_cmd (midi_dev, 0x39, 0); /* Enable real time messages to PC */ - mpu_cmd (midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ - } - - return TIMER_ARMED; + tmr_reset(); + set_timer_mode(midi_dev); + + if (tmr_running) + return TIMER_NOT_ARMED; /* Already running */ + + if (timer_mode & TMR_INTERNAL) + { + mpu_cmd(midi_dev, 0x02, 0); /* Send MIDI start */ + tmr_running = 1; + return TIMER_NOT_ARMED; + } + else + { + mpu_cmd(midi_dev, 0x35, 0); /* Enable mode messages to PC */ + mpu_cmd(midi_dev, 0x38, 0); /* Enable sys common messages to PC */ + mpu_cmd(midi_dev, 0x39, 0); /* Enable real time messages to PC */ + mpu_cmd(midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ + } + return TIMER_ARMED; } -static int -mpu_timer_open (int dev, int mode) +static int mpu_timer_open(int dev, int mode) { - int midi_dev = sound_timer_devs[dev]->devlink; + int midi_dev = sound_timer_devs[dev]->devlink; - if (timer_open) - return -(EBUSY); + if (timer_open) + return -EBUSY; - tmr_reset (); - curr_tempo = 50; - mpu_cmd (midi_dev, 0xE0, 50); - curr_timebase = hw_timebase = 120; - set_timebase (midi_dev, 120); - timer_open = 1; - metronome_mode = 0; - set_timer_mode (midi_dev); + tmr_reset(); + curr_tempo = 50; + mpu_cmd(midi_dev, 0xE0, 50); + curr_timebase = hw_timebase = 120; + set_timebase(midi_dev, 120); + timer_open = 1; + metronome_mode = 0; + set_timer_mode(midi_dev); - mpu_cmd (midi_dev, 0xe7, 0x04); /* Send all clocks to host */ - mpu_cmd (midi_dev, 0x95, 0); /* Enable clock to host */ + mpu_cmd(midi_dev, 0xe7, 0x04); /* Send all clocks to host */ + mpu_cmd(midi_dev, 0x95, 0); /* Enable clock to host */ - return 0; + return 0; } -static void -mpu_timer_close (int dev) +static void mpu_timer_close(int dev) { - int midi_dev = sound_timer_devs[dev]->devlink; + int midi_dev = sound_timer_devs[dev]->devlink; - timer_open = tmr_running = 0; - mpu_cmd (midi_dev, 0x15, 0); /* Stop all */ - mpu_cmd (midi_dev, 0x94, 0); /* Disable clock to host */ - mpu_cmd (midi_dev, 0x8c, 0); /* Disable measure end messages to host */ - stop_metronome (midi_dev); + timer_open = tmr_running = 0; + mpu_cmd(midi_dev, 0x15, 0); /* Stop all */ + mpu_cmd(midi_dev, 0x94, 0); /* Disable clock to host */ + mpu_cmd(midi_dev, 0x8c, 0); /* Disable measure end messages to host */ + stop_metronome(midi_dev); } -static int -mpu_timer_event (int dev, unsigned char *event) +static int mpu_timer_event(int dev, unsigned char *event) { - unsigned char command = event[1]; - unsigned long parm = *(unsigned int *) &event[4]; - int midi_dev = sound_timer_devs[dev]->devlink; - - switch (command) - { - 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: - if (tmr_running) - break; - return mpu_start_timer (midi_dev); - break; - - case TMR_STOP: - mpu_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */ - stop_metronome (midi_dev); - tmr_running = 0; - break; - - case TMR_CONTINUE: - if (tmr_running) - break; - mpu_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */ - setup_metronome (midi_dev); - tmr_running = 1; - break; - - case TMR_TEMPO: - if (parm) - { - if (parm < 8) - parm = 8; - if (parm > 250) - parm = 250; - - if (mpu_cmd (midi_dev, 0xE0, parm) < 0) - printk ("MPU: Can't set tempo to %d\n", (int) parm); - curr_tempo = parm; - } - break; - - case TMR_ECHO: - seq_copy_to_input (event, 8); - break; + unsigned char command = event[1]; + unsigned long parm = *(unsigned int *) &event[4]; + int midi_dev = sound_timer_devs[dev]->devlink; - case TMR_TIMESIG: - if (metronome_mode) /* Metronome enabled */ + switch (command) { - metronome_mode = parm; - setup_metronome (midi_dev); + 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: + if (tmr_running) + break; + return mpu_start_timer(midi_dev); + + case TMR_STOP: + mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ + stop_metronome(midi_dev); + tmr_running = 0; + break; + + case TMR_CONTINUE: + if (tmr_running) + break; + mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ + setup_metronome(midi_dev); + tmr_running = 1; + break; + + case TMR_TEMPO: + if (parm) + { + if (parm < 8) + parm = 8; + if (parm > 250) + parm = 250; + if (mpu_cmd(midi_dev, 0xE0, parm) < 0) + printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm); + curr_tempo = parm; + } + break; + + case TMR_ECHO: + seq_copy_to_input(event, 8); + break; + + case TMR_TIMESIG: + if (metronome_mode) /* Metronome enabled */ + { + metronome_mode = parm; + setup_metronome(midi_dev); + } + break; + + default: } - break; - - default:; - } - - return TIMER_NOT_ARMED; + return TIMER_NOT_ARMED; } -static unsigned long -mpu_timer_get_time (int dev) +static unsigned long mpu_timer_get_time(int dev) { - if (!timer_open) - return 0; + if (!timer_open) + return 0; - return curr_ticks; + return curr_ticks; } -static int -mpu_timer_ioctl (int dev, - unsigned int command, caddr_t arg) +static int mpu_timer_ioctl(int dev, unsigned int command, caddr_t arg) { - int midi_dev = sound_timer_devs[dev]->devlink; - - switch (command) - { - case SNDCTL_TMR_SOURCE: - { - int parm = (int) get_user ((int *) arg) & timer_caps; - - if (parm != 0) - { - timer_mode = parm; - - if (timer_mode & TMR_MODE_CLS) - mpu_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */ - else if (timer_mode & TMR_MODE_SMPTE) - mpu_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */ - } - - return snd_ioctl_return ((int *) arg, timer_mode); - } - break; - - case SNDCTL_TMR_START: - mpu_start_timer (midi_dev); - return 0; - break; - - case SNDCTL_TMR_STOP: - tmr_running = 0; - mpu_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */ - stop_metronome (midi_dev); - return 0; - break; - - case SNDCTL_TMR_CONTINUE: - if (tmr_running) - return 0; - tmr_running = 1; - mpu_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */ - return 0; - break; - - case SNDCTL_TMR_TIMEBASE: - { - int val = (int) get_user ((int *) arg); - - if (val) - set_timebase (midi_dev, val); - - return snd_ioctl_return ((int *) arg, curr_timebase); - } - break; - - case SNDCTL_TMR_TEMPO: - { - int val = (int) get_user ((int *) arg); - int ret; - - if (val) - { - if (val < 8) - val = 8; - if (val > 250) - val = 250; - if ((ret = mpu_cmd (midi_dev, 0xE0, val)) < 0) - { - printk ("MPU: Can't set tempo to %d\n", (int) val); - return ret; - } - - curr_tempo = val; - } - - return snd_ioctl_return ((int *) arg, curr_tempo); - } - break; - - case SNDCTL_SEQ_CTRLRATE: - if (get_user ((int *) arg) != 0) /* Can't change */ - return -(EINVAL); + int midi_dev = sound_timer_devs[dev]->devlink; - return snd_ioctl_return ((int *) arg, ((curr_tempo * curr_timebase) + 30) / 60); - break; - - case SNDCTL_TMR_METRONOME: - metronome_mode = (int) get_user ((int *) arg); - setup_metronome (midi_dev); - return 0; - break; - - default:; - } - - return -(EINVAL); + switch (command) + { + case SNDCTL_TMR_SOURCE: + { + int parm; + + parm = *(int *) arg; + parm &= timer_caps; + + if (parm != 0) + { + timer_mode = parm; + + if (timer_mode & TMR_MODE_CLS) + mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ + else if (timer_mode & TMR_MODE_SMPTE) + mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ + } + return (*(int *) arg = timer_mode); + } + break; + + case SNDCTL_TMR_START: + mpu_start_timer(midi_dev); + return 0; + + case SNDCTL_TMR_STOP: + tmr_running = 0; + mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ + stop_metronome(midi_dev); + return 0; + + case SNDCTL_TMR_CONTINUE: + if (tmr_running) + return 0; + tmr_running = 1; + mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ + return 0; + + case SNDCTL_TMR_TIMEBASE: + { + int val; + + val = *(int *) arg; + if (val) + set_timebase(midi_dev, val); + return (*(int *) arg = curr_timebase); + } + break; + + case SNDCTL_TMR_TEMPO: + { + int val; + int ret; + + val = *(int *) arg; + + if (val) + { + if (val < 8) + val = 8; + if (val > 250) + val = 250; + if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0) + { + printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val); + return ret; + } + curr_tempo = val; + } + return (*(int *) arg = curr_tempo); + } + break; + + case SNDCTL_SEQ_CTRLRATE: + { + int val; + + val = *(int *) arg; + if (val != 0) /* Can't change */ + return -EINVAL; + return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60); + } + break; + + case SNDCTL_SEQ_GETTIME: + return (*(int *) arg = curr_ticks); + + case SNDCTL_TMR_METRONOME: + metronome_mode = *(int *) arg; + setup_metronome(midi_dev); + return 0; + + default: + } + return -EINVAL; } -static void -mpu_timer_arm (int dev, long time) +static void mpu_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; + 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 mpu_timer = { - {"MPU-401 Timer", 0}, - 10, /* Priority */ - 0, /* Local device link */ - mpu_timer_open, - mpu_timer_close, - mpu_timer_event, - mpu_timer_get_time, - mpu_timer_ioctl, - mpu_timer_arm + {"MPU-401 Timer", 0}, + 10, /* Priority */ + 0, /* Local device link */ + mpu_timer_open, + mpu_timer_close, + mpu_timer_event, + mpu_timer_get_time, + mpu_timer_ioctl, + mpu_timer_arm }; -static void -mpu_timer_interrupt (void) +static void mpu_timer_interrupt(void) { + if (!timer_open) + return; - if (!timer_open) - return; + if (!tmr_running) + return; - if (!tmr_running) - return; + curr_clocks++; + curr_ticks = clocks2ticks(curr_clocks); - curr_clocks++; - curr_ticks = clocks2ticks (curr_clocks); - - if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer (0); - } + if (curr_ticks >= next_event_time) + { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } } -static void -timer_ext_event (struct mpu_config *devc, int event, int parm) +static void timer_ext_event(struct mpu_config *devc, int event, int parm) { - int midi_dev = devc->devno; - - if (!devc->timer_flag) - return; - - switch (event) - { - case TMR_CLOCK: - printk (""); - break; - - case TMR_START: - printk ("Ext MIDI start\n"); - if (!tmr_running) - if (timer_mode & TMR_EXTERNAL) - { - tmr_running = 1; - setup_metronome (midi_dev); - next_event_time = 0; - STORE (SEQ_START_TIMER ()); - } - break; - - case TMR_STOP: - printk ("Ext MIDI stop\n"); - if (timer_mode & TMR_EXTERNAL) - { - tmr_running = 0; - stop_metronome (midi_dev); - STORE (SEQ_STOP_TIMER ()); - } - break; + int midi_dev = devc->devno; - case TMR_CONTINUE: - printk ("Ext MIDI continue\n"); - if (timer_mode & TMR_EXTERNAL) - { - tmr_running = 1; - setup_metronome (midi_dev); - STORE (SEQ_CONTINUE_TIMER ()); - } - break; + if (!devc->timer_flag) + return; - case TMR_SPP: - printk ("Songpos: %d\n", parm); - if (timer_mode & TMR_EXTERNAL) + switch (event) { - STORE (SEQ_SONGPOS (parm)); + case TMR_CLOCK: + printk(""); + break; + + case TMR_START: + printk("Ext MIDI start\n"); + if (!tmr_running) + { + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 1; + setup_metronome(midi_dev); + next_event_time = 0; + STORE(SEQ_START_TIMER()); + } + } + break; + + case TMR_STOP: + printk("Ext MIDI stop\n"); + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 0; + stop_metronome(midi_dev); + STORE(SEQ_STOP_TIMER()); + } + break; + + case TMR_CONTINUE: + printk("Ext MIDI continue\n"); + if (timer_mode & TMR_EXTERNAL) + { + tmr_running = 1; + setup_metronome(midi_dev); + STORE(SEQ_CONTINUE_TIMER()); + } + break; + + case TMR_SPP: + printk("Songpos: %d\n", parm); + if (timer_mode & TMR_EXTERNAL) + { + STORE(SEQ_SONGPOS(parm)); + } + break; } - break; - } } -static void -mpu_timer_init (int midi_dev) +static int mpu_timer_init(int midi_dev) { - struct mpu_config *devc; - int n; + struct mpu_config *devc; + int n; - devc = &dev_conf[midi_dev]; + devc = &dev_conf[midi_dev]; - if (timer_initialized) - return; /* There is already a similar timer */ + if (timer_initialized) + return -1; /* There is already a similar timer */ - timer_initialized = 1; + timer_initialized = 1; - mpu_timer.devlink = midi_dev; - dev_conf[midi_dev].timer_flag = 1; + mpu_timer.devlink = midi_dev; + dev_conf[midi_dev].timer_flag = 1; - if (num_sound_timers >= MAX_TIMER_DEV) - n = 0; /* Overwrite the system timer */ - else - n = num_sound_timers++; - sound_timer_devs[n] = &mpu_timer; + n = sound_alloc_timerdev(); + if (n == -1) + n = 0; + sound_timer_devs[n] = &mpu_timer; - if (devc->version < 0x20) /* Original MPU-401 */ - timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI; - else - { - /* - * The version number 2.0 is used (at least) by the - * MusicQuest cards and the Roland Super-MPU. - * - * MusicQuest has given a special meaning to the bits of the - * revision number. The Super-MPU returns 0. - */ + if (devc->version < 0x20) /* Original MPU-401 */ + timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI; + else + { + /* + * The version number 2.0 is used (at least) by the + * MusicQuest cards and the Roland Super-MPU. + * + * MusicQuest has given a special meaning to the bits of the + * revision number. The Super-MPU returns 0. + */ - if (devc->revision) - timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI; + if (devc->revision) + timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI; - if (devc->revision & 0x02) - timer_caps |= TMR_MODE_CLS; + if (devc->revision & 0x02) + timer_caps |= TMR_MODE_CLS; - if (devc->revision & 0x40) - max_timebase = 10; /* Has the 216 and 240 ppqn modes */ - } + if (devc->revision & 0x40) + max_timebase = 10; /* Has the 216 and 240 ppqn modes */ + } - timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps; + timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps; + return n; } #endif +struct symbol_table mpu401_symbols= +{ +#include + X(probe_mpu401), + X(attach_mpu401), + X(unload_mpu401), + X(mpuintr), +#include +}; + +#ifdef MODULE + +MODULE_PARM(irq, "i"); +MODULE_PARM(io, "i"); +int io = -1; +int irq = -1; +struct address_info hw; + +int init_module(void) +{ + /* Can be loaded either for module use or to provide functions + to others */ + if (io != -1 && irq != -1) + { + hw.irq = irq; + hw.io_base = io; + if (probe_mpu401(&hw) == 0) + return -ENODEV; + attach_mpu401(&hw); + } + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + if (io != -1 && irq != -1) + { + unload_mpu401(&hw); + } + /* FREE SYMTAB */ + SOUND_LOCK_END; +} + +#endif #endif diff --git a/drivers/sound/msnd.c b/drivers/sound/msnd.c new file mode 100644 index 000000000000..0bf731cb61a1 --- /dev/null +++ b/drivers/sound/msnd.c @@ -0,0 +1,359 @@ +/********************************************************************* + * + * msnd.c - Driver Base + * + * Turtle Beach MultiSound Soundcard Driver for Linux + * Linux 2.0 Version + * + * 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. + * + * $Id: msnd.c,v 1.2 1998/06/09 20:37:39 andrewtv Exp $ + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sound_config.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; + + MOD_INC_USE_COUNT; + + 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; + + MOD_DEC_USE_COUNT; +} + +int msnd_get_num_devs(void) +{ + return num_devs; +} + +multisound_dev_t *msnd_get_dev(int j) +{ + int i; + + for (i = 0; i < MSND_MAX_DEVS && j; ++i) + if (devs[i] != NULL) + --j; + + if (i == MSND_MAX_DEVS || j != 0) + return NULL; + + return devs[i]; +} + +void msnd_fifo_init(msnd_fifo *f) +{ + f->data = NULL; +} + +void msnd_fifo_free(msnd_fifo *f) +{ + if (f->data) { + vfree(f->data); + f->data = NULL; + } +} + +int msnd_fifo_alloc(msnd_fifo *f, size_t n) +{ + msnd_fifo_free(f); + f->data = (char *)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(msnd_fifo *f, const char *buf, size_t len, int user) +{ + int count = 0; + + if (f->len == f->n) + return 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; + } + + if (user) { + if (verify_area(VERIFY_READ, buf , nwritten)) + return nwritten; + + memcpy_fromfs(f->data + f->tail, buf, nwritten); + } else + 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(msnd_fifo *f, char *buf, size_t len, int user) +{ + int count = 0; + + if (f->len == 0) + return f->len; + + 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; + } + + if (user) { + if (verify_area(VERIFY_WRITE, buf, nread)) + return nread; + + memcpy_tofs(buf, f->data + f->head, nread); + } else + memcpy(buf, f->data + f->head, nread); + + count += nread; + buf += nread; + f->len -= nread; + f->head += nread; + f->head %= f->n; + } + + return count; +} + +int msnd_wait_TXDE(multisound_dev_t *dev) +{ + register unsigned int io = dev->io; + register int timeout = 5000; + + while(timeout-- > 0) + if (inb(io + HP_ISR) & HPISR_TXDE) + return 0; + + return -EIO; +} + +int msnd_wait_HC0(multisound_dev_t *dev) +{ + register unsigned int io = dev->io; + register int timeout = 25000; + + while(timeout-- > 0) + if (!(inb(io + HP_CVR) & HPCVR_HC)) + return 0; + + return -EIO; +} + +int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd) +{ + unsigned long flags; + + save_flags(flags); cli(); + if (msnd_wait_HC0(dev) == 0) { + + outb(cmd, dev->io + HP_CVR); + restore_flags(flags); + return 0; + } + restore_flags(flags); + + printk(KERN_WARNING 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) { + + outb(high, io + HP_TXH); + outb(mid, io + HP_TXM); + outb(low, io + HP_TXL); + return 0; + } + + printk(KERN_WARNING 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; + + inb(dev->io + HP_RXL); + inb(dev->io + HP_CVR); + + return 0; +} + +int msnd_enable_irq(multisound_dev_t *dev) +{ + if (dev->irq_ref++ != 0) + return 0; + + if (msnd_wait_TXDE(dev) == 0) { + + unsigned long flags; + + save_flags(flags); cli(); + + outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR); + + if (dev->type == msndClassic) + outb(dev->irqid, dev->io + HP_IRQM); + + outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR); + outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR); + + restore_flags(flags); + + return 0; + } + + 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) + dev->irq_ref = 0; + + save_flags(flags); cli(); + outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR); + + if (dev->type == msndClassic) + outb(HPIRQ_NONE, dev->io + HP_IRQM); + + restore_flags(flags); + + return 0; +} + +#ifdef MODULE +MODULE_AUTHOR ("Andrew Veliath "); +MODULE_DESCRIPTION ("Turtle Beach MultiSound Driver Base"); + +int init_module(void) +{ + return 0; +} + +void cleanup_module(void) +{ +} +#endif diff --git a/drivers/sound/msnd.h b/drivers/sound/msnd.h new file mode 100644 index 000000000000..1313446ed94d --- /dev/null +++ b/drivers/sound/msnd.h @@ -0,0 +1,260 @@ +/********************************************************************* + * + * msnd.h + * + * Turtle Beach MultiSound Soundcard 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. + * + * $Id: msnd.h,v 1.3 1998/06/09 20:39:34 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_H +#define __MSND_H + +#define VERSION "0.6.1" + +#define DEFSAMPLERATE DSP_DEFAULT_SPEED +#define DEFSAMPLESIZE 8 +#define DEFCHANNELS 1 + +#define DEFFIFOSIZE 64 + +#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 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) + +#ifdef SLOWIO +# undef outb +# undef inb +# define outb outb_p +# define inb inb_p +#endif + +typedef unsigned char BYTE; +typedef unsigned short USHORT; +typedef unsigned short WORD; +typedef unsigned int DWORD; +typedef +struct DAQueueDataStruct * LPDAQD; + +#define GCC_PACKED __attribute__ ((packed)) + +struct JobQueueStruct { + WORD wStart; + WORD wSize; + WORD wHead; + WORD wTail; +} GCC_PACKED; + +struct DAQueueDataStruct { + WORD wStart; + WORD wSize; + WORD wFormat; + WORD wSampleSize; + WORD wChannels; + WORD wSampleRate; + WORD wIntMsg; + WORD wFlags; +} GCC_PACKED; + +typedef struct { + size_t n, len; + char *data; + int head, tail; +} msnd_fifo; + +typedef struct multisound_dev { + + char *name; + int dsp_minor, mixer_minor; + + /* Hardware resources */ + unsigned int io, numio; + int memid, irqid; + int irq, irq_ref; + unsigned char info; + char *base; + + /* MultiSound DDK variables */ + enum { msndClassic, msndPinnacle } type; + struct SMA0_CommonData *SMA; /* diff. structure for classic vs. pinnacle */ + struct DAQueueDataStruct *CurDAQD; + struct DAQueueDataStruct *CurDARQD; + WORD *pwDSPQData , *pwMIDQData , *pwMODQData; + struct JobQueueStruct *DAPQ , *DARQ , *MODQ , *MIDQ , *DSPQ; + + /* State variables */ + mode_t mode; + unsigned long flags; +#define F_BANKONE 0 +#define F_INTERRUPT 1 +#define F_WRITING 2 +#define F_WRITEBLOCK 3 +#define F_READING 4 +#define F_READBLOCK 5 +#define F_AUDIO_INUSE 6 +#define F_EXT_MIDI_INUSE 7 +#define F_INT_MIDI_INUSE 8 + struct wait_queue *writeblock, *readblock; + unsigned long recsrc; + int left_levels[16]; + int right_levels[16]; + int calibrate_signal; + int sample_size; + int sample_rate; + int channels; + void (*inc_ref)(void); + void (*dec_ref)(void); + + /* Digital audio FIFOs */ + int fifosize; + msnd_fifo DAPF, DARF; + int lastbank; + + /* 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); +int msnd_get_num_devs(void); +multisound_dev_t * msnd_get_dev(int i); + +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(msnd_fifo *f, const char *buf, size_t len, int user); +int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user); + +int msnd_wait_TXDE(multisound_dev_t *dev); +int msnd_wait_HC0(multisound_dev_t *dev); +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 */ diff --git a/drivers/sound/msnd_classic.c b/drivers/sound/msnd_classic.c new file mode 100644 index 000000000000..a1beabb64fcb --- /dev/null +++ b/drivers/sound/msnd_classic.c @@ -0,0 +1,1314 @@ +/********************************************************************* + * + * msnd_classic.c - Support for Turtle Beach Classic/Monterey/Tahiti + * + * Turtle Beach MultiSound Soundcard Driver for Linux + * Linux 2.0 Version + * + * 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. + * + * $Id: msnd_classic.c,v 1.2 1998/06/09 20:37:39 andrewtv Exp $ + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include "sound_config.h" +#include "sound_firmware.h" +#define SLOWIO +#include "msnd.h" +#include "msnd_classic.h" + +#define LOGNAME "msnd_classic" +#define DEVNAME dev.name +#define MIXERMINOR dev.mixer_minor +#define DSPMINOR dev.dsp_minor + +multisound_dev_t dev; + +#ifndef HAVE_DSPCODEH +static char *dspini, *permini; +static int sizeof_dspini, sizeof_permini; +#endif + +static void reset_play_queue(void) +{ + int n; + LPDAQD lpDAQ; + + msnd_fifo_make_empty(&dev.DAPF); + dev.DAPQ->wHead = 0; + dev.DAPQ->wTail = PCTODSP_OFFSET(2 * DAPQ_STRUCT_SIZE); + dev.CurDAQD = (LPDAQD)(dev.base + 1 * DAPQ_DATA_BUFF); + outb(HPBLKSEL_0, dev.io + HP_BLKS); + memset_io(dev.base, 0, DAP_BUFF_SIZE * 3); + + for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), &lpDAQ->wStart); + writew(DAP_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_PLAY_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + + } + + dev.lastbank = -1; +} + +static void reset_record_queue(void) +{ + int n; + LPDAQD lpDAQ; + + msnd_fifo_make_empty(&dev.DARF); + dev.DARQ->wHead = 0; + dev.DARQ->wTail = PCTODSP_OFFSET(2 * DARQ_STRUCT_SIZE); + dev.CurDARQD = (LPDAQD)(dev.base + 1 * DARQ_DATA_BUFF); + outb(HPBLKSEL_1, dev.io + HP_BLKS); + memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, &lpDAQ->wStart); + writew(DAR_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_RECORD_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + + } +} + +static void reset_queues(void) +{ + dev.DSPQ->wHead = dev.DSPQ->wTail = 0; + reset_play_queue(); + reset_record_queue(); +} + +static int dsp_ioctl(unsigned int cmd, unsigned long arg) +{ + int val, i, data, tmp; + LPDAQD lpDAQ, lpDARQ; + + lpDAQ = (LPDAQD)(dev.base + DAPQ_DATA_BUFF); + lpDARQ = (LPDAQD)(dev.base + DARQ_DATA_BUFF); + + switch (cmd) { + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_SETFRAGMENT: + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + return -EINVAL; + + case SNDCTL_DSP_SYNC: + case SNDCTL_DSP_RESET: + + reset_play_queue(); + reset_record_queue(); + + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + + tmp = dev.fifosize / 4; + if (put_user(tmp, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_NONBLOCK: + + dev.mode |= O_NONBLOCK; + + return 0; + + case SNDCTL_DSP_GETCAPS: + + val = DSP_CAP_DUPLEX | DSP_CAP_BATCH; + if (put_user(val, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_SAMPLESIZE: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { + case 16: + case 8: + data = val; + break; + default: + data = DEFSAMPLESIZE; + break; + } + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wSampleSize = data; + lpDARQ->wSampleSize = data; + } + + dev.sample_size = data; + + if (put_user(data, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_SPEED: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (val < 8000) + val = 8000; + + if (val > 48000) + val = 48000; + + data = val; + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wSampleRate = data; + lpDARQ->wSampleRate = data; + } + + dev.sample_rate = data; + + if (put_user(data, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_CHANNELS: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { + case 1: + case 2: + data = val; + break; + default: + val = data = 2; + break; + } + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wChannels = data; + lpDARQ->wChannels = data; + } + + dev.channels = data; + + if (put_user(val, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_STEREO: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { + case 0: + data = 1; + break; + default: + val = 1; + case 1: + data = 2; + break; + } + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wChannels = data; + lpDARQ->wChannels = data; + } + + dev.channels = data; + + if (put_user(val, (int *)arg)) + return -EFAULT; + + return 0; + } + + return -EINVAL; +} + +static int mixer_get(int d) +{ + if (d > 31) + return -EINVAL; + + switch (d) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_SYNTH: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: + case SOUND_MIXER_MIC: + case SOUND_MIXER_IMIX: + case SOUND_MIXER_LINE1: + return (dev.left_levels[d] >> 8) * 100 / 0xff | + (((dev.right_levels[d] >> 8) * 100 / 0xff) << 8); + default: + return 0; + } +} + +#define update_vol(a,b,s) \ + writew(dev.left_levels[a] * readw(&dev.SMA->wCurrMastVolLeft) / 0xffff / s, \ + &dev.SMA->b##Left); \ + writew(dev.right_levels[a] * readw(&dev.SMA->wCurrMastVolRight) / 0xffff / s, \ + &dev.SMA->b##Right); + +static int mixer_set(int d, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int bLeft, bRight; + int wLeft, wRight; + + if (d > 31) + return -EINVAL; + + bLeft = left * 0xff / 100; + wLeft = left * 0xffff / 100; + + bRight = right * 0xff / 100; + wRight = right * 0xffff / 100; + + dev.left_levels[d] = wLeft; + dev.right_levels[d] = wRight; + + switch (d) { + case SOUND_MIXER_VOLUME: /* master volume */ + writew(wLeft / 2, &dev.SMA->wCurrMastVolLeft); + writew(wRight / 2, &dev.SMA->wCurrMastVolRight); + break; + + /* pot controls */ + case SOUND_MIXER_LINE: /* aux pot control */ + writeb(bLeft, &dev.SMA->bInPotPosLeft); + writeb(bRight, &dev.SMA->bInPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; + + case SOUND_MIXER_LINE1: /* line pot control */ + writeb(bLeft, &dev.SMA->bAuxPotPosLeft); + writeb(bRight, &dev.SMA->bAuxPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_AUX_SET_POTS) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; + + /* digital controls */ + case SOUND_MIXER_SYNTH: /* synth vol (dsp mix) */ + case SOUND_MIXER_PCM: /* pcm vol (dsp mix) */ + case SOUND_MIXER_IMIX: /* input monitor (dsp mix) */ + break; + + default: + return 0; + } + + /* update digital controls for master volume */ + update_vol(SOUND_MIXER_PCM, wCurrPlayVol, 1); + update_vol(SOUND_MIXER_IMIX, wCurrInVol, 1); + + return mixer_get(d); +} + +static unsigned long set_recsrc(unsigned long recsrc) +{ +#ifdef HAVE_NORECSRC + if (recsrc == 0) + dev.recsrc = 0; + else +#endif + dev.recsrc ^= recsrc; + + return dev.recsrc; +} + +static int mixer_ioctl(unsigned int cmd, unsigned long arg) +{ + int val = 0; + + if (((cmd >> 8) & 0xff) == 'M') { + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = set_recsrc(val); + break; + + default: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = mixer_set(cmd & 0xff, val); + break; + } + + return put_user(val, (int *)arg); + } + else { + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + val = dev.recsrc; + break; + + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_VOLUME | + SOUND_MASK_PCM | + SOUND_MASK_LINE | + SOUND_MASK_IMIX | + SOUND_MASK_LINE1; + break; + + case SOUND_MIXER_RECMASK: + val = 0; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: + if ((val = mixer_get(cmd & 0xff)) < 0) + return -EINVAL; + break; + } + } + + return put_user(val, (int *)arg); + } + + return -EINVAL; +} + +static int dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int minor = MINOR(inode->i_rdev); + + if (minor == DSPMINOR) + return dsp_ioctl(cmd, arg); + else if (minor == MIXERMINOR) + return mixer_ioctl(cmd, arg); + + return -EINVAL; +} + +static void dsp_halt(void) +{ + mdelay(1); + if (test_bit(F_READING, &dev.flags)) { + + clear_bit(F_READING, &dev.flags); + msnd_send_dsp_cmd(&dev, HDEX_RECORD_STOP); + msnd_disable_irq(&dev); + + } + mdelay(1); + if (test_bit(F_WRITING, &dev.flags)) { + + clear_bit(F_WRITING, &dev.flags); + msnd_send_dsp_cmd(&dev, HDEX_PLAY_STOP); + msnd_disable_irq(&dev); + + } + mdelay(1); + reset_queues(); +} + +static int dsp_open(struct file *file) +{ + dev.mode = file->f_mode; + set_bit(F_AUDIO_INUSE, &dev.flags); + reset_queues(); + return 0; +} + +static int dsp_close(void) +{ + dsp_halt(); + clear_bit(F_AUDIO_INUSE, &dev.flags); + return 0; +} + +static int dev_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + int err = 0; + + if (minor == DSPMINOR) { + + if (test_bit(F_AUDIO_INUSE, &dev.flags)) + return -EBUSY; + + err = dsp_open(file); + } + else if (minor == MIXERMINOR) { + /* nothing */ + } else + err = -EINVAL; + + if (err >= 0) + MOD_INC_USE_COUNT; + + return err; +} + +static void dev_close(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + + if (minor == DSPMINOR) { + dsp_close(); + } + else if (minor == MIXERMINOR) { + /* nothing */ + } + MOD_DEC_USE_COUNT; +} + +static int DAPF_to_bank(int bank) +{ + return msnd_fifo_read(&dev.DAPF, dev.base + bank * DAP_BUFF_SIZE, DAP_BUFF_SIZE, 0); +} + +static int bank_to_DARF(int bank) +{ + return msnd_fifo_write(&dev.DARF, dev.base + bank * DAR_BUFF_SIZE, DAR_BUFF_SIZE, 0); +} + +static int dsp_read(char *buf, size_t len) +{ + int err = 0; + int count = len; + + while (count > 0) { + + int n; + + if ((n = msnd_fifo_read(&dev.DARF, buf, count, 1)) < 0) { + + printk(KERN_WARNING LOGNAME ": FIFO read error\n"); + return n; + } + + buf += n; + count -= n; + + if (!test_bit(F_READING, &dev.flags) && (dev.mode & FMODE_READ)) { + + set_bit(F_READING, &dev.flags); + reset_record_queue(); + msnd_enable_irq(&dev); + msnd_send_dsp_cmd(&dev, HDEX_RECORD_START); + + } + + if (dev.mode & O_NONBLOCK) + return count == len ? -EAGAIN : len - count; + + if (count > 0) { + + set_bit(F_READBLOCK, &dev.flags); + interruptible_sleep_on(&dev.readblock); + clear_bit(F_READBLOCK, &dev.flags); + + if (signal_pending(current)) + err = -EINTR; + + } + + if (err != 0) + return err; + } + + return len - count; +} + +static int dsp_write(const char *buf, size_t len) +{ + int err = 0; + int count = len; + + while (count > 0) { + + int n; + + if ((n = msnd_fifo_write(&dev.DAPF, buf, count, 1)) < 0) { + + printk(KERN_WARNING LOGNAME ": FIFO write error\n"); + return n; + } + + buf += n; + count -= n; + + if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) { + + set_bit(F_WRITING, &dev.flags); + reset_play_queue(); + msnd_enable_irq(&dev); + msnd_send_dsp_cmd(&dev, HDEX_PLAY_START); + + } + + if (dev.mode & O_NONBLOCK) + return count == len ? -EAGAIN : len - count; + + if (count > 0) { + + set_bit(F_WRITEBLOCK, &dev.flags); + interruptible_sleep_on(&dev.writeblock); + clear_bit(F_WRITEBLOCK, &dev.flags); + + if (signal_pending(current)) + err = -EINTR; + + } + + if (err != 0) + return err; + } + + return len - count; +} + +static int dev_read(struct inode *inode, struct file *file, char *buf, int count) +{ + int minor = MINOR(inode->i_rdev); + + if (minor == DSPMINOR) { + + return dsp_read(buf, count); + + } else + return -EINVAL; +} + +static int dev_write(struct inode *inode, struct file *file, const char *buf, int count) +{ + int minor = MINOR(inode->i_rdev); + + if (minor == DSPMINOR) { + + return dsp_write(buf, count); + + } else + return -EINVAL; +} + +static void eval_dsp_msg(WORD wMessage) +{ + switch (HIBYTE(wMessage)) { + case HIMT_PLAY_DONE: + + if (dev.lastbank == LOBYTE(wMessage)) + break; + + dev.lastbank = LOBYTE(wMessage); + + dev.CurDAQD->wSize = DAP_BUFF_SIZE; + + if ((dev.DAPQ->wTail += PCTODSP_OFFSET(DAPQ_STRUCT_SIZE)) > dev.DAPQ->wSize) + dev.DAPQ->wTail = 0; + + if (++dev.CurDAQD > (LPDAQD)(dev.base + DAPQ_DATA_BUFF + 2 * DAPQ_STRUCT_SIZE)) + dev.CurDAQD = (LPDAQD)(dev.base + DAPQ_DATA_BUFF); + + if (dev.lastbank < 3) { + + if (DAPF_to_bank(dev.lastbank) > 0) { + + mdelay(1); + msnd_send_dsp_cmd(&dev, HDEX_PLAY_START); + + } + else if (!test_bit(F_WRITEBLOCK, &dev.flags)) { + + memset_io(dev.base, 0, DAP_BUFF_SIZE * 3); + clear_bit(F_WRITING, &dev.flags); + msnd_disable_irq(&dev); + + } + } + + if (test_bit(F_WRITEBLOCK, &dev.flags)) + wake_up_interruptible(&dev.writeblock); + + break; + + case HIMT_RECORD_DONE: { + + WORD wTemp; + + wTemp = dev.DARQ->wTail + (DARQ_STRUCT_SIZE / 2); + + if (wTemp > dev.DARQ->wSize) + wTemp = 0; + + while (wTemp == dev.DARQ->wHead); + + dev.DARQ->wTail = wTemp; + + outb(HPBLKSEL_1, dev.io + HP_BLKS); + if (bank_to_DARF(LOBYTE(wMessage)) == 0 && + !test_bit(F_READBLOCK, &dev.flags)) { + + memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); + clear_bit(F_READING, &dev.flags); + msnd_disable_irq(&dev); + + } + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + if (test_bit(F_READBLOCK, &dev.flags)) + wake_up_interruptible(&dev.readblock); + + } break; + + case HIMT_DSP: + switch (LOBYTE(wMessage)) { + case HIDSP_INT_PLAY_UNDER: + printk(KERN_INFO LOGNAME ": Write underflow\n"); + reset_play_queue(); + break; + + case HIDSP_INT_RECORD_OVER: + printk(KERN_INFO LOGNAME ": Read overflow\n"); + reset_record_queue(); + break; + + default: + printk(KERN_INFO LOGNAME ": DSP message %u\n", LOBYTE(wMessage)); + break; + } + break; + + case HIMT_MIDI_IN_UCHAR: + if (dev.midi_in_interrupt) + (*dev.midi_in_interrupt)(&dev); + break; + + default: + break; + } +} + +static void intr(int irq, void *dev_id, struct pt_regs *regs) +{ + if (test_bit(F_INTERRUPT, &dev.flags) || + ((multisound_dev_t *)dev_id != &dev)) + return; + + set_bit(F_INTERRUPT, &dev.flags); + + if (test_bit(F_BANKONE, &dev.flags)) + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + inb(dev.io + HP_RXL); + + while (dev.DSPQ->wTail != dev.DSPQ->wHead) { + + eval_dsp_msg(*(dev.pwDSPQData + dev.DSPQ->wHead)); + + if (++dev.DSPQ->wHead > dev.DSPQ->wSize) + dev.DSPQ->wHead = 0; + } + + if (test_bit(F_BANKONE, &dev.flags)) + outb(HPBLKSEL_1, dev.io + HP_BLKS); + + clear_bit(F_INTERRUPT, &dev.flags); +} + +static struct file_operations dev_fileops = { + NULL, + dev_read, + dev_write, + NULL, + NULL, + dev_ioctl, + NULL, + dev_open, + dev_close, +}; + +static int reset_dsp(void) +{ + int timeout = 20000; + + outb(HPDSPRESET_ON, dev.io + HP_DSPR); + + mdelay(1); + + dev.info = inb(dev.io + HP_INFO); + + outb(HPDSPRESET_OFF, dev.io + HP_DSPR); + + mdelay(1); + + while (timeout-- > 0) { + + if (inb(dev.io + HP_CVR) == HP_CVR_DEF) + return 0; + + mdelay(1); + } + + printk(KERN_ERR LOGNAME ": Cannot reset DSP\n"); + + return -EIO; +} + +static int probe_multisound(void) +{ + if (check_region(dev.io, dev.numio)) { + + printk(KERN_ERR LOGNAME ": I/O port conflict\n"); + return -ENODEV; + } + + request_region(dev.io, dev.numio, "probing"); + + if (reset_dsp() < 0) { + + release_region(dev.io, dev.numio); + return -ENODEV; + } + + printk(KERN_INFO LOGNAME ": DSP reset successful\n"); + + dev.name = "Classic/Tahiti/Monterey"; + + printk(KERN_INFO LOGNAME ": Turtle Beach %s, " + "I/O 0x%x-0x%x, IRQ %d, memory mapped to 0x%p-0x%p\n", + dev.name, + dev.io, dev.io + dev.numio - 1, + dev.irq, + dev.base, dev.base + 0x7fff); + + release_region(dev.io, dev.numio); + + return 0; +} + +static int init_sma(void) +{ + int n; + LPDAQD lpDAQ; + + outb(dev.memid, dev.io + HP_MEMM); + + outb(HPBLKSEL_0, dev.io + HP_BLKS); + memset_io(dev.base, 0, 0x8000); + + outb(HPBLKSEL_1, dev.io + HP_BLKS); + memset_io(dev.base, 0, 0x8000); + + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + dev.DAPQ = (struct JobQueueStruct *)(dev.base + DAPQ_OFFSET); + dev.DARQ = (struct JobQueueStruct *)(dev.base + DARQ_OFFSET); + dev.MODQ = (struct JobQueueStruct *)(dev.base + MODQ_OFFSET); + dev.MIDQ = (struct JobQueueStruct *)(dev.base + MIDQ_OFFSET); + dev.DSPQ = (struct JobQueueStruct *)(dev.base + DSPQ_OFFSET); + + dev.SMA = (struct SMA0_CommonData *)(dev.base + SMA_STRUCT_START); + + dev.CurDAQD = (LPDAQD)(dev.base + DAPQ_DATA_BUFF); + dev.CurDARQD = (LPDAQD)(dev.base + DARQ_DATA_BUFF); + + dev.sample_size = DEFSAMPLESIZE; + dev.sample_rate = DEFSAMPLERATE; + dev.channels = DEFCHANNELS; + + for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), &lpDAQ->wStart); + writew(DAP_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_PLAY_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + } + + for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, &lpDAQ->wStart); + writew(DAR_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_RECORD_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + + } + + dev.pwDSPQData = (WORD *)(dev.base + DSPQ_DATA_BUFF); + dev.pwMODQData = (WORD *)(dev.base + MODQ_DATA_BUFF); + dev.pwMIDQData = (WORD *)(dev.base + MIDQ_DATA_BUFF); + + writew(PCTODSP_BASED(MIDQ_DATA_BUFF), &dev.MIDQ->wStart); + writew(PCTODSP_OFFSET(MIDQ_BUFF_SIZE) - 1, &dev.MIDQ->wSize); + writew(0, &dev.MIDQ->wHead); + writew(0, &dev.MIDQ->wTail); + + writew(PCTODSP_BASED(MODQ_DATA_BUFF), &dev.MODQ->wStart); + writew(PCTODSP_OFFSET(MODQ_BUFF_SIZE) - 1, &dev.MODQ->wSize); + writew(0, &dev.MODQ->wHead); + writew(0, &dev.MODQ->wTail); + + writew(PCTODSP_BASED(DAPQ_DATA_BUFF), &dev.DAPQ->wStart); + writew(PCTODSP_OFFSET(DAPQ_BUFF_SIZE) - 1, &dev.DAPQ->wSize); + writew(0, &dev.DAPQ->wHead); + writew(0, &dev.DAPQ->wTail); + + writew(PCTODSP_BASED(DARQ_DATA_BUFF), &dev.DARQ->wStart); + writew(PCTODSP_OFFSET(DARQ_BUFF_SIZE) - 1, &dev.DARQ->wSize); + writew(0, &dev.DARQ->wHead); + writew(0, &dev.DARQ->wTail); + + writew(PCTODSP_BASED(DSPQ_DATA_BUFF), &dev.DSPQ->wStart); + writew(PCTODSP_OFFSET(DSPQ_BUFF_SIZE) - 1, &dev.DSPQ->wSize); + writew(0, &dev.DSPQ->wHead); + writew(0, &dev.DSPQ->wTail); + + writew(0, &dev.SMA->wCurrPlayBytes); + writew(0, &dev.SMA->wCurrRecordBytes); + + writew(0, &dev.SMA->wCurrPlayVolLeft); + writew(0, &dev.SMA->wCurrPlayVolRight); + + writew(0, &dev.SMA->wCurrInVolLeft); + writew(0, &dev.SMA->wCurrInVolRight); + + writew(0, &dev.SMA->wCurrMastVolLeft); + writew(0, &dev.SMA->wCurrMastVolRight); + + writew(0x0000, &dev.SMA->wCurrDSPStatusFlags); + writew(0x0000, &dev.SMA->wCurrHostStatusFlags); + + writew(0x303, &dev.SMA->wCurrInputTagBits); + writew(0, &dev.SMA->wCurrLeftPeak); + writew(0, &dev.SMA->wCurrRightPeak); + + writeb(0, &dev.SMA->bInPotPosRight); + writeb(0, &dev.SMA->bInPotPosLeft); + + writeb(0, &dev.SMA->bAuxPotPosRight); + writeb(0, &dev.SMA->bAuxPotPosLeft); + + writew(dev.sample_rate, &dev.SMA->wCalFreqAtoD); + + return 0; +} + +static int calibrate_adc(WORD srate) +{ + if (!dev.calibrate_signal) { + + printk(KERN_INFO LOGNAME ": ADC calibration to board ground "); + writew(readw(&dev.SMA->wCurrHostStatusFlags) + | 0x0001, &dev.SMA->wCurrHostStatusFlags); + } + else { + + printk(KERN_INFO LOGNAME ": ADC calibration to signal ground "); + writew(readw(&dev.SMA->wCurrHostStatusFlags) + & ~0x0001, &dev.SMA->wCurrHostStatusFlags); + } + + writew(srate, &dev.SMA->wCalFreqAtoD); + + if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 && + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) { + + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ; + schedule(); + current->timeout = 0; + printk("successful\n"); + return 0; + } + + printk("failed\n"); + + return -EIO; +} + +static int upload_dsp_code(void) +{ + outb(HPBLKSEL_0, dev.io + HP_BLKS); + +#ifdef HAVE_DSPCODEH + printk(KERN_INFO LOGNAME ": Using resident Turtle Beach DSP code\n"); +#else + printk(KERN_INFO LOGNAME ": Loading Turtle Beach DSP code\n"); + INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE); + if (!INITCODE) { + printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE); + return -EBUSY; + } + + PERMCODESIZE = mod_firmware_load(PERMCODEFILE, &PERMCODE); + if (!PERMCODE) { + printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE); + vfree(INITCODE); + return -EBUSY; + } +#endif + memcpy_toio(dev.base, PERMCODE, PERMCODESIZE); + + if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) { + + printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n"); + return -ENODEV; + } + +#ifndef HAVE_DSPCODEH + vfree(INITCODE); + vfree(PERMCODE); +#endif + + return 0; +} + +static void reset_proteus(void) +{ + outb(HPPRORESET_ON, dev.io + HP_PROR); + mdelay(TIME_PRO_RESET); + outb(HPPRORESET_OFF, dev.io + HP_PROR); + mdelay(TIME_PRO_RESET_DONE); +} + +static int initialize(void) +{ + int err, timeout; + + outb(HPWAITSTATE_0, dev.io + HP_WAIT); + outb(HPBITMODE_16, dev.io + HP_BITM); + + reset_proteus(); + + if ((err = init_sma()) < 0) { + + printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n"); + return err; + } + + if ((err = reset_dsp()) < 0) + return err; + + if ((err = upload_dsp_code()) < 0) { + + printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n"); + return err; + + } else + printk(KERN_INFO LOGNAME ": DSP upload successful\n"); + + timeout = 2000; + + while (readw(dev.base)) { + + mdelay(1); + if (--timeout < 0) + return -EIO; + } + + return 0; +} + +static int attach_multisound(void) +{ + int err; + + printk(KERN_DEBUG LOGNAME ": Intializing DSP\n"); + + if ((err = request_irq(dev.irq, intr, SA_SHIRQ, DEVNAME, &dev)) < 0) { + + printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq); + return err; + + } + + request_region(dev.io, dev.numio, DEVNAME); + + if ((err = initialize()) < 0) { + + printk(KERN_WARNING LOGNAME ": Initialization failure\n"); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return err; + + } + + if ((err = msnd_register(&dev)) < 0) { + + printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n"); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return err; + } + + if ((DSPMINOR = register_sound_dsp(&dev_fileops)) < 0) { + + printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n"); + msnd_unregister(&dev); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return DSPMINOR; + } + + if ((MIXERMINOR = register_sound_mixer(&dev_fileops)) < 0) { + + printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n"); + unregister_sound_mixer(MIXERMINOR); + msnd_unregister(&dev); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return MIXERMINOR; + } + printk(KERN_INFO LOGNAME ": Using DSP minor %d, mixer minor %d\n", MIXERMINOR, DSPMINOR); + + calibrate_adc(dev.sample_rate); + set_recsrc(0); + + return 0; +} + +static void unload_multisound(void) +{ + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + unregister_sound_mixer(MIXERMINOR); + unregister_sound_dsp(DSPMINOR); + msnd_unregister(&dev); +} + +static void mod_inc_ref(void) +{ + MOD_INC_USE_COUNT; +} + +static void mod_dec_ref(void) +{ + MOD_DEC_USE_COUNT; +} + +#ifdef MODULE +MODULE_AUTHOR ("Andrew Veliath "); +MODULE_DESCRIPTION ("Turtle Beach " LONGNAME " Linux Driver"); +MODULE_PARM (io, "i"); +MODULE_PARM (irq, "i"); +MODULE_PARM (mem, "i"); +MODULE_PARM (major, "i"); +MODULE_PARM (fifosize, "i"); +MODULE_PARM (calibrate_signal, "i"); + +static int io = -1; +static int irq = -1; +static int mem = -1; +static int fifosize = DEFFIFOSIZE; +static int calibrate_signal; + +int init_module(void) +#else +static int io = CONFIG_MSNDCLAS_IO; +static int irq = CONFIG_MSNDCLAS_IRQ; +static int mem = CONFIG_MSNDCLAS_MEM; +static int fifosize = DEFFIFOSIZE; +static int calibrate_signal; + +msnd_classic_init(void) +#endif +{ + int err; + + printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version " + VERSION ", Copyright (C) 1998 Andrew Veliath\n"); + + if (io == -1 || irq == -1 || mem == -1) { + + printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n"); + } + + if (io == -1 || + !(io == 0x290 || + io == 0x260 || + io == 0x250 || + io == 0x240 || + io == 0x230 || + io == 0x220 || + io == 0x210 || + io == 0x3e0)) { + + printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set\n"); + return -EINVAL; + } + + if (irq == -1 || + !(irq == 5 || + irq == 7 || + irq == 9 || + irq == 10 || + irq == 11 || + irq == 12)) { + + printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n"); + return -EINVAL; + } + + if (mem == -1 || + !(mem == 0xb0000 || + mem == 0xc8000 || + mem == 0xd0000 || + mem == 0xd8000 || + mem == 0xe0000 || + mem == 0xe8000)) { + + printk(KERN_ERR LOGNAME ": \"mem\" - must be set to " + "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); + return -EINVAL; + } + + switch (irq) { + case 5: dev.irqid = HPIRQ_5; break; + case 7: dev.irqid = HPIRQ_7; break; + case 9: dev.irqid = HPIRQ_9; break; + case 10: dev.irqid = HPIRQ_10; break; + case 11: dev.irqid = HPIRQ_11; break; + case 12: dev.irqid = HPIRQ_12; break; + } + + switch (mem) { + case 0xb0000: dev.memid = HPMEM_B000; break; + case 0xc8000: dev.memid = HPMEM_C800; break; + case 0xd0000: dev.memid = HPMEM_D000; break; + case 0xd8000: dev.memid = HPMEM_D800; break; + case 0xe0000: dev.memid = HPMEM_E000; break; + case 0xe8000: dev.memid = HPMEM_E800; break; + } + + if (fifosize < 16) + fifosize = 16; + + if (fifosize > 768) + fifosize = 768; + + dev.type = msndClassic; + dev.io = io; + dev.numio = DSP_NUMIO; + dev.irq = irq; + dev.base = phys_to_virt(mem); + dev.fifosize = fifosize * 1024; + dev.calibrate_signal = calibrate_signal ? 1 : 0; + dev.recsrc = 0; + dev.inc_ref = mod_inc_ref; + dev.dec_ref = mod_dec_ref; + + init_waitqueue(&dev.writeblock); + init_waitqueue(&dev.readblock); + msnd_fifo_init(&dev.DAPF); + msnd_fifo_init(&dev.DARF); + + printk(KERN_INFO LOGNAME ": Using %u byte digital audio FIFOs (x2)\n", dev.fifosize); + + if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) { + + printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n"); + return err; + } + + if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) { + + printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n"); + msnd_fifo_free(&dev.DAPF); + return err; + } + + if ((err = probe_multisound()) < 0) { + + printk(KERN_ERR LOGNAME ": Probe failed\n"); + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + return err; + + } + + if ((err = attach_multisound()) < 0) { + + printk(KERN_ERR LOGNAME ": Attach failed\n"); + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + return err; + + } + + return 0; +} + +#ifdef MODULE + +void cleanup_module(void) +{ + printk(KERN_INFO LOGNAME ": Unloading\n"); + + unload_multisound(); + + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + +} +#endif diff --git a/drivers/sound/msnd_classic.h b/drivers/sound/msnd_classic.h new file mode 100644 index 000000000000..cc485284be5b --- /dev/null +++ b/drivers/sound/msnd_classic.h @@ -0,0 +1,199 @@ +/********************************************************************* + * + * msnd_classic.h + * + * Turtle Beach MultiSound Soundcard 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. + * + * $Id: msnd_classic.h,v 1.3 1998/06/09 20:39:34 andrewtv Exp $ + * + ********************************************************************/ +#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 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 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_PROTEUS 0x10 +#define MOP_EXTOUT 0x32 +#define MOP_EXTTHRU 0x02 +#define MOP_OUTMASK 0x01 + +#define MIP_EXTIN 0x01 +#define MIP_PROTEUS 0x00 +#define MIP_INMASK 0x32 + +struct SMA0_CommonData { + WORD wCurrPlayBytes; + WORD wCurrRecordBytes; + WORD wCurrPlayVolLeft; + WORD wCurrPlayVolRight; + WORD wCurrInVolLeft; + WORD wCurrInVolRight; + WORD wUser_3; + WORD wUser_4; + DWORD dwUser_5; + DWORD dwUser_6; + WORD wUser_7; + WORD wReserved_A; + WORD wReserved_B; + WORD wReserved_C; + WORD wReserved_D; + WORD wReserved_E; + WORD wReserved_F; + WORD wReserved_G; + WORD wReserved_H; + WORD wCurrDSPStatusFlags; + WORD wCurrHostStatusFlags; + WORD wCurrInputTagBits; + WORD wCurrLeftPeak; + WORD wCurrRightPeak; + WORD wExtDSPbits; + BYTE bExtHostbits; + BYTE bBoardLevel; + BYTE bInPotPosRight; + BYTE bInPotPosLeft; + BYTE bAuxPotPosRight; + BYTE bAuxPotPosLeft; + WORD wCurrMastVolLeft; + WORD wCurrMastVolRight; + BYTE bUser_12; + BYTE bUser_13; + WORD wUser_14; + WORD wUser_15; + WORD wCalFreqAtoD; + WORD wUser_16; + WORD wUser_17; +} GCC_PACKED; + +#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 */ diff --git a/drivers/sound/msnd_pinnacle.c b/drivers/sound/msnd_pinnacle.c new file mode 100644 index 000000000000..8a182a5f56b3 --- /dev/null +++ b/drivers/sound/msnd_pinnacle.c @@ -0,0 +1,1346 @@ +/********************************************************************* + * + * msnd_pinnacle.c - Support for Turtle Beach Pinnacle and Fiji + * + * Turtle Beach MultiSound Soundcard Driver for Linux + * Linux 2.0 Version + * + * 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. + * + * $Id: msnd_pinnacle.c,v 1.2 1998/06/09 20:37:39 andrewtv Exp $ + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include "sound_config.h" +#include "sound_firmware.h" +#include "msnd.h" +#include "msnd_pinnacle.h" + +#define LOGNAME "msnd_pinnacle" +#define DEVNAME dev.name +#define MIXERMINOR dev.mixer_minor +#define DSPMINOR dev.dsp_minor + +multisound_dev_t dev; + +#ifndef HAVE_DSPCODEH +static char *dspini, *permini; +static int sizeof_dspini, sizeof_permini; +#endif + +static void reset_play_queue(void) +{ + int n; + LPDAQD lpDAQ; + + msnd_fifo_make_empty(&dev.DAPF); + dev.DAPQ->wHead = 0; + dev.DAPQ->wTail = PCTODSP_OFFSET(2 * DAPQ_STRUCT_SIZE); + dev.CurDAQD = (LPDAQD)(dev.base + 1 * DAPQ_DATA_BUFF); + outb(HPBLKSEL_0, dev.io + HP_BLKS); + memset_io(dev.base, 0, DAP_BUFF_SIZE * 3); + + for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), &lpDAQ->wStart); + writew(DAP_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_PLAY_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + + } + + dev.lastbank = -1; +} + +static void reset_record_queue(void) +{ + int n; + LPDAQD lpDAQ; + + msnd_fifo_make_empty(&dev.DARF); + dev.DARQ->wHead = 0; + dev.DARQ->wTail = PCTODSP_OFFSET(2 * DARQ_STRUCT_SIZE); + dev.CurDARQD = (LPDAQD)(dev.base + 1 * DARQ_DATA_BUFF); + outb(HPBLKSEL_1, dev.io + HP_BLKS); + memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, &lpDAQ->wStart); + writew(DAR_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_RECORD_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + + } +} + +static void reset_queues(void) +{ + dev.DSPQ->wHead = dev.DSPQ->wTail = 0; + reset_play_queue(); + reset_record_queue(); +} + +static int dsp_ioctl(unsigned int cmd, unsigned long arg) +{ + int val, i, data, tmp; + LPDAQD lpDAQ, lpDARQ; + + lpDAQ = (LPDAQD)(dev.base + DAPQ_DATA_BUFF); + lpDARQ = (LPDAQD)(dev.base + DARQ_DATA_BUFF); + + switch (cmd) { + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_SETFRAGMENT: + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETIPTR: + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + return -EINVAL; + + case SNDCTL_DSP_SYNC: + case SNDCTL_DSP_RESET: + + reset_play_queue(); + reset_record_queue(); + + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + + tmp = dev.fifosize / 4; + if (put_user(tmp, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_NONBLOCK: + + dev.mode |= O_NONBLOCK; + + return 0; + + case SNDCTL_DSP_GETCAPS: + + val = DSP_CAP_DUPLEX | DSP_CAP_BATCH; + if (put_user(val, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_SAMPLESIZE: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { + case 16: + case 8: + data = val; + break; + default: + data = DEFSAMPLESIZE; + break; + } + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wSampleSize = data; + lpDARQ->wSampleSize = data; + } + + dev.sample_size = data; + + if (put_user(data, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_SPEED: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + if (val < 8000) + val = 8000; + + if (val > 48000) + val = 48000; + + data = val; + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wSampleRate = data; + lpDARQ->wSampleRate = data; + } + + dev.sample_rate = data; + + if (put_user(data, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_CHANNELS: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { + case 1: + case 2: + data = val; + break; + default: + val = data = 2; + break; + } + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wChannels = data; + lpDARQ->wChannels = data; + } + + dev.channels = data; + + if (put_user(val, (int *)arg)) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_STEREO: + + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (val) { + case 0: + data = 1; + break; + default: + val = 1; + case 1: + data = 2; + break; + } + + for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) { + + lpDAQ->wChannels = data; + lpDARQ->wChannels = data; + } + + dev.channels = data; + + if (put_user(val, (int *)arg)) + return -EFAULT; + + return 0; + } + + return -EINVAL; +} + +static int mixer_get(int d) +{ + if (d > 31) + return -EINVAL; + + switch (d) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_SYNTH: + case SOUND_MIXER_PCM: + case SOUND_MIXER_LINE: + case SOUND_MIXER_MIC: + case SOUND_MIXER_IMIX: + case SOUND_MIXER_LINE1: + return (dev.left_levels[d] >> 8) * 100 / 0xff | + (((dev.right_levels[d] >> 8) * 100 / 0xff) << 8); + default: + return 0; + } +} + +#define update_vol(a,b,s) \ + writew(dev.left_levels[a] * readw(&dev.SMA->wCurrMastVolLeft) / 0xffff / s, \ + &dev.SMA->b##Left); \ + writew(dev.right_levels[a] * readw(&dev.SMA->wCurrMastVolRight) / 0xffff / s, \ + &dev.SMA->b##Right); + +static int mixer_set(int d, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int bLeft, bRight; + int wLeft, wRight; + + if (d > 31) + return -EINVAL; + + bLeft = left * 0xff / 100; + wLeft = left * 0xffff / 100; + + bRight = right * 0xff / 100; + wRight = right * 0xffff / 100; + + dev.left_levels[d] = wLeft; + dev.right_levels[d] = wRight; + + switch (d) { + case SOUND_MIXER_VOLUME: /* master volume */ + writew(wLeft / 2, &dev.SMA->wCurrMastVolLeft); + writew(wRight / 2, &dev.SMA->wCurrMastVolRight); + break; + + /* pot controls */ + case SOUND_MIXER_LINE: /* aux pot control */ + writeb(bLeft, &dev.SMA->bInPotPosLeft); + writeb(bRight, &dev.SMA->bInPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; + + case SOUND_MIXER_MIC: /* mic pot control */ + writeb(bLeft, &dev.SMA->bMicPotPosLeft); + writeb(bRight, &dev.SMA->bMicPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; + + case SOUND_MIXER_LINE1: /* line pot control */ + writeb(bLeft, &dev.SMA->bAuxPotPosLeft); + writeb(bRight, &dev.SMA->bAuxPotPosRight); + if (msnd_send_word(&dev, 0, 0, HDEXAR_AUX_SET_POTS) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + break; + + /* digital controls */ + case SOUND_MIXER_SYNTH: /* synth vol (dsp mix) */ + case SOUND_MIXER_PCM: /* pcm vol (dsp mix) */ + case SOUND_MIXER_IMIX: /* input monitor (dsp mix) */ + break; + + default: + return 0; + } + + /* update digital controls for master volume */ + update_vol(SOUND_MIXER_PCM, wCurrPlayVol, 1); + update_vol(SOUND_MIXER_IMIX, wCurrInVol, 1); + update_vol(SOUND_MIXER_SYNTH, wCurrMHdrVol, 1); + + return mixer_get(d); +} + +static unsigned long set_recsrc(unsigned long recsrc) +{ +#ifdef HAVE_NORECSRC + if (recsrc == 0) + dev.recsrc = 0; + else +#endif + dev.recsrc ^= recsrc; + + if (dev.recsrc & SOUND_MASK_LINE) { + + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + + } + else if (dev.recsrc & SOUND_MASK_SYNTH) { + + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); + + } + else { +#ifdef HAVE_NORECSRC + /* Select no input (?) */ + dev.recsrc = 0; +#else + dev.recsrc = SOUND_MASK_LINE; + if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ); +#endif + } + + return dev.recsrc; +} + +static int mixer_ioctl(unsigned int cmd, unsigned long arg) +{ + int val = 0; + + if (((cmd >> 8) & 0xff) == 'M') { + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) { + + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = set_recsrc(val); + break; + + default: + if (get_user(val, (int *)arg)) + return -EFAULT; + val = mixer_set(cmd & 0xff, val); + break; + } + + return put_user(val, (int *)arg); + } + else { + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + val = dev.recsrc; + break; + + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_VOLUME | + SOUND_MASK_SYNTH | + SOUND_MASK_PCM | + SOUND_MASK_LINE | + SOUND_MASK_IMIX | + SOUND_MASK_MIC; + break; + + case SOUND_MIXER_RECMASK: + val = SOUND_MASK_LINE | + SOUND_MASK_SYNTH; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: + if ((val = mixer_get(cmd & 0xff)) < 0) + return -EINVAL; + break; + } + } + + return put_user(val, (int *)arg); + } + + return -EINVAL; +} + +static int dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int minor = MINOR(inode->i_rdev); + + if (minor == DSPMINOR) + return dsp_ioctl(cmd, arg); + else if (minor == MIXERMINOR) + return mixer_ioctl(cmd, arg); + + return -EINVAL; +} + +static void dsp_halt(void) +{ + mdelay(1); + if (test_bit(F_READING, &dev.flags)) { + + clear_bit(F_READING, &dev.flags); + msnd_send_dsp_cmd(&dev, HDEX_RECORD_STOP); + msnd_disable_irq(&dev); + + } + mdelay(1); + if (test_bit(F_WRITING, &dev.flags)) { + + clear_bit(F_WRITING, &dev.flags); + msnd_send_dsp_cmd(&dev, HDEX_PLAY_STOP); + msnd_disable_irq(&dev); + + } + mdelay(1); + reset_queues(); +} + +static int dsp_open(struct file *file) +{ + dev.mode = file->f_mode; + set_bit(F_AUDIO_INUSE, &dev.flags); + reset_queues(); + return 0; +} + +static int dsp_close(void) +{ + dsp_halt(); + clear_bit(F_AUDIO_INUSE, &dev.flags); + return 0; +} + +static int dev_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + int err = 0; + + if (minor == DSPMINOR) { + + if (test_bit(F_AUDIO_INUSE, &dev.flags)) + return -EBUSY; + + err = dsp_open(file); + } + else if (minor == MIXERMINOR) { + /* nothing */ + } else + err = -EINVAL; + + if (err >= 0) + MOD_INC_USE_COUNT; + + return err; +} + +static void dev_close(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + + if (minor == DSPMINOR) { + dsp_close(); + } + else if (minor == MIXERMINOR) { + /* nothing */ + } + MOD_DEC_USE_COUNT; +} + +static int DAPF_to_bank(int bank) +{ + return msnd_fifo_read(&dev.DAPF, dev.base + bank * DAP_BUFF_SIZE, DAP_BUFF_SIZE, 0); +} + +static int bank_to_DARF(int bank) +{ + return msnd_fifo_write(&dev.DARF, dev.base + bank * DAR_BUFF_SIZE, DAR_BUFF_SIZE, 0); +} + +static int dsp_read(char *buf, size_t len) +{ + int err = 0; + int count = len; + + while (count > 0) { + + int n; + + if ((n = msnd_fifo_read(&dev.DARF, buf, count, 1)) < 0) { + + printk(KERN_WARNING LOGNAME ": FIFO read error\n"); + return n; + } + + buf += n; + count -= n; + + if (!test_bit(F_READING, &dev.flags) && (dev.mode & FMODE_READ)) { + + set_bit(F_READING, &dev.flags); + reset_record_queue(); + msnd_enable_irq(&dev); + msnd_send_dsp_cmd(&dev, HDEX_RECORD_START); + + } + + if (dev.mode & O_NONBLOCK) + return count == len ? -EAGAIN : len - count; + + if (count > 0) { + + set_bit(F_READBLOCK, &dev.flags); + interruptible_sleep_on(&dev.readblock); + clear_bit(F_READBLOCK, &dev.flags); + + if (signal_pending(current)) + err = -EINTR; + + } + + if (err != 0) + return err; + } + + return len - count; +} + +static int dsp_write(const char *buf, size_t len) +{ + int err = 0; + int count = len; + + while (count > 0) { + + int n; + + if ((n = msnd_fifo_write(&dev.DAPF, buf, count, 1)) < 0) { + + printk(KERN_WARNING LOGNAME ": FIFO write error\n"); + return n; + } + + buf += n; + count -= n; + + if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) { + + set_bit(F_WRITING, &dev.flags); + reset_play_queue(); + msnd_enable_irq(&dev); + msnd_send_dsp_cmd(&dev, HDEX_PLAY_START); + + } + + if (dev.mode & O_NONBLOCK) + return count == len ? -EAGAIN : len - count; + + if (count > 0) { + + set_bit(F_WRITEBLOCK, &dev.flags); + interruptible_sleep_on(&dev.writeblock); + clear_bit(F_WRITEBLOCK, &dev.flags); + + if (signal_pending(current)) + err = -EINTR; + + } + + if (err != 0) + return err; + } + + return len - count; +} + +static int dev_read(struct inode *inode, struct file *file, char *buf, int count) +{ + int minor = MINOR(inode->i_rdev); + + if (minor == DSPMINOR) { + + return dsp_read(buf, count); + + } else + return -EINVAL; +} + +static int dev_write(struct inode *inode, struct file *file, const char *buf, int count) +{ + int minor = MINOR(inode->i_rdev); + + if (minor == DSPMINOR) { + + return dsp_write(buf, count); + + } else + return -EINVAL; +} + +static void eval_dsp_msg(WORD wMessage) +{ + switch (HIBYTE(wMessage)) { + case HIMT_PLAY_DONE: + + if (dev.lastbank == LOBYTE(wMessage)) + break; + + dev.lastbank = LOBYTE(wMessage); + + dev.CurDAQD->wSize = DAP_BUFF_SIZE; + + if ((dev.DAPQ->wTail += PCTODSP_OFFSET(DAPQ_STRUCT_SIZE)) > dev.DAPQ->wSize) + dev.DAPQ->wTail = 0; + + if (++dev.CurDAQD > (LPDAQD)(dev.base + DAPQ_DATA_BUFF + 2 * DAPQ_STRUCT_SIZE)) + dev.CurDAQD = (LPDAQD)(dev.base + DAPQ_DATA_BUFF); + + if (dev.lastbank < 3) { + + if (DAPF_to_bank(dev.lastbank) > 0) { + + mdelay(1); + msnd_send_dsp_cmd(&dev, HDEX_PLAY_START); + + } + else if (!test_bit(F_WRITEBLOCK, &dev.flags)) { + + memset_io(dev.base, 0, DAP_BUFF_SIZE * 3); + clear_bit(F_WRITING, &dev.flags); + msnd_disable_irq(&dev); + + } + } + + if (test_bit(F_WRITEBLOCK, &dev.flags)) + wake_up_interruptible(&dev.writeblock); + + break; + + case HIMT_RECORD_DONE: { + + WORD wTemp; + + wTemp = dev.DARQ->wTail + (DARQ_STRUCT_SIZE / 2); + + if (wTemp > dev.DARQ->wSize) + wTemp = 0; + + while (wTemp == dev.DARQ->wHead); + + dev.DARQ->wTail = wTemp; + + outb(HPBLKSEL_1, dev.io + HP_BLKS); + if (bank_to_DARF(LOBYTE(wMessage)) == 0 && + !test_bit(F_READBLOCK, &dev.flags)) { + + memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); + clear_bit(F_READING, &dev.flags); + msnd_disable_irq(&dev); + + } + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + if (test_bit(F_READBLOCK, &dev.flags)) + wake_up_interruptible(&dev.readblock); + + } break; + + case HIMT_DSP: + switch (LOBYTE(wMessage)) { + case HIDSP_PLAY_UNDER: + case HIDSP_INT_PLAY_UNDER: + printk(KERN_INFO LOGNAME ": Write underflow\n"); + reset_play_queue(); + break; + + case HIDSP_INT_RECORD_OVER: + printk(KERN_INFO LOGNAME ": Read overflow\n"); + reset_record_queue(); + break; + + default: + printk(KERN_INFO LOGNAME ": DSP message %u\n", LOBYTE(wMessage)); + break; + } + break; + + case HIMT_MIDI_IN_UCHAR: + if (dev.midi_in_interrupt) + (*dev.midi_in_interrupt)(&dev); + break; + + default: + break; + } +} + +static void intr(int irq, void *dev_id, struct pt_regs *regs) +{ + if (test_bit(F_INTERRUPT, &dev.flags) || + ((multisound_dev_t *)dev_id != &dev)) + return; + + set_bit(F_INTERRUPT, &dev.flags); + + if (test_bit(F_BANKONE, &dev.flags)) + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + inb(dev.io + HP_RXL); + + while (dev.DSPQ->wTail != dev.DSPQ->wHead) { + + eval_dsp_msg(*(dev.pwDSPQData + dev.DSPQ->wHead)); + + if (++dev.DSPQ->wHead > dev.DSPQ->wSize) + dev.DSPQ->wHead = 0; + } + + if (test_bit(F_BANKONE, &dev.flags)) + outb(HPBLKSEL_1, dev.io + HP_BLKS); + + clear_bit(F_INTERRUPT, &dev.flags); +} + +static struct file_operations dev_fileops = { + NULL, + dev_read, + dev_write, + NULL, + NULL, + dev_ioctl, + NULL, + dev_open, + dev_close, +}; + +static int reset_dsp(void) +{ + int timeout = 20000; + + outb(HPDSPRESET_ON, dev.io + HP_DSPR); + + mdelay(1); + + dev.info = inb(dev.io + HP_INFO); + + outb(HPDSPRESET_OFF, dev.io + HP_DSPR); + + mdelay(1); + + while (timeout-- > 0) { + + if (inb(dev.io + HP_CVR) == HP_CVR_DEF) + return 0; + + mdelay(1); + } + + printk(KERN_ERR LOGNAME ": Cannot reset DSP\n"); + + return -EIO; +} + +static int probe_multisound(void) +{ + char *xv, *rev = NULL; + char *pin = "Pinnacle", *fiji = "Fiji"; + char *pinfiji = "Pinnacle/Fiji"; + + if (check_region(dev.io, dev.numio)) { + + printk(KERN_ERR LOGNAME ": I/O port conflict\n"); + return -ENODEV; + } + + request_region(dev.io, dev.numio, "probing"); + + if (reset_dsp() < 0) { + + release_region(dev.io, dev.numio); + return -ENODEV; + } + + printk(KERN_INFO LOGNAME ": DSP reset successful\n"); + + switch (dev.info >> 4) { + case 0xf: xv = "<= 1.15"; break; + case 0x1: xv = "1.18/1.2"; break; + case 0x2: xv = "1.3"; break; + case 0x3: xv = "1.4"; break; + default: xv = "unknown"; break; + } + + switch (dev.info & 0x7) { + case 0x0: rev = "I"; dev.name = pin; break; + case 0x1: rev = "F"; dev.name = pin; break; + case 0x2: rev = "G"; dev.name = pin; break; + case 0x3: rev = "H"; dev.name = pin; break; + case 0x4: rev = "E"; dev.name = fiji; break; + case 0x5: rev = "C"; dev.name = fiji; break; + case 0x6: rev = "D"; dev.name = fiji; break; + case 0x7: + rev = "A-B (Fiji) or A-E (Pinnacle)"; + dev.name = pinfiji; + break; + } + + printk(KERN_INFO LOGNAME ": Turtle Beach %s revision %s, Xilinx version %s, " + "I/O 0x%x-0x%x, IRQ %d, memory mapped to 0x%p-0x%p\n", + dev.name, + rev, xv, + dev.io, dev.io + dev.numio - 1, + dev.irq, + dev.base, dev.base + 0x7fff); + + release_region(dev.io, dev.numio); + + return 0; +} + +static int init_sma(void) +{ + int n; + LPDAQD lpDAQ; + + outb(HPBLKSEL_0, dev.io + HP_BLKS); + memset_io(dev.base, 0, 0x8000); + + outb(HPBLKSEL_1, dev.io + HP_BLKS); + memset_io(dev.base, 0, 0x8000); + + outb(HPBLKSEL_0, dev.io + HP_BLKS); + + dev.DAPQ = (struct JobQueueStruct *)(dev.base + DAPQ_OFFSET); + dev.DARQ = (struct JobQueueStruct *)(dev.base + DARQ_OFFSET); + dev.MODQ = (struct JobQueueStruct *)(dev.base + MODQ_OFFSET); + dev.MIDQ = (struct JobQueueStruct *)(dev.base + MIDQ_OFFSET); + dev.DSPQ = (struct JobQueueStruct *)(dev.base + DSPQ_OFFSET); + + dev.SMA = (struct SMA0_CommonData *)(dev.base + SMA_STRUCT_START); + + dev.CurDAQD = (LPDAQD)(dev.base + DAPQ_DATA_BUFF); + dev.CurDARQD = (LPDAQD)(dev.base + DARQ_DATA_BUFF); + + dev.sample_size = DEFSAMPLESIZE; + dev.sample_rate = DEFSAMPLERATE; + dev.channels = DEFCHANNELS; + + for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), &lpDAQ->wStart); + writew(DAP_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_PLAY_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + } + + for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, ++lpDAQ) { + + writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, &lpDAQ->wStart); + writew(DAR_BUFF_SIZE, &lpDAQ->wSize); + writew(1, &lpDAQ->wFormat); + writew(dev.sample_size, &lpDAQ->wSampleSize); + writew(dev.channels, &lpDAQ->wChannels); + writew(dev.sample_rate, &lpDAQ->wSampleRate); + writew(HIMT_RECORD_DONE * 0x100 + n, &lpDAQ->wIntMsg); + writew(n + 1, &lpDAQ->wFlags); + + } + + dev.pwDSPQData = (WORD *)(dev.base + DSPQ_DATA_BUFF); + dev.pwMODQData = (WORD *)(dev.base + MODQ_DATA_BUFF); + dev.pwMIDQData = (WORD *)(dev.base + MIDQ_DATA_BUFF); + + writew(PCTODSP_BASED(MIDQ_DATA_BUFF), &dev.MIDQ->wStart); + writew(PCTODSP_OFFSET(MIDQ_BUFF_SIZE) - 1, &dev.MIDQ->wSize); + writew(0, &dev.MIDQ->wHead); + writew(0, &dev.MIDQ->wTail); + + writew(PCTODSP_BASED(MODQ_DATA_BUFF), &dev.MODQ->wStart); + writew(PCTODSP_OFFSET(MODQ_BUFF_SIZE) - 1, &dev.MODQ->wSize); + writew(0, &dev.MODQ->wHead); + writew(0, &dev.MODQ->wTail); + + writew(PCTODSP_BASED(DAPQ_DATA_BUFF), &dev.DAPQ->wStart); + writew(PCTODSP_OFFSET(DAPQ_BUFF_SIZE) - 1, &dev.DAPQ->wSize); + writew(0, &dev.DAPQ->wHead); + writew(0, &dev.DAPQ->wTail); + + writew(PCTODSP_BASED(DARQ_DATA_BUFF), &dev.DARQ->wStart); + writew(PCTODSP_OFFSET(DARQ_BUFF_SIZE) - 1, &dev.DARQ->wSize); + writew(0, &dev.DARQ->wHead); + writew(0, &dev.DARQ->wTail); + + writew(PCTODSP_BASED(DSPQ_DATA_BUFF), &dev.DSPQ->wStart); + writew(PCTODSP_OFFSET(DSPQ_BUFF_SIZE) - 1, &dev.DSPQ->wSize); + writew(0, &dev.DSPQ->wHead); + writew(0, &dev.DSPQ->wTail); + + writew(0, &dev.SMA->wCurrPlayBytes); + writew(0, &dev.SMA->wCurrRecordBytes); + + writew(0, &dev.SMA->wCurrPlayVolLeft); + writew(0, &dev.SMA->wCurrPlayVolRight); + + writew(0, &dev.SMA->wCurrInVolLeft); + writew(0, &dev.SMA->wCurrInVolRight); + + writew(0, &dev.SMA->wCurrMastVolLeft); + writew(0, &dev.SMA->wCurrMastVolRight); + + writel(0x00010000, &dev.SMA->dwCurrPlayPitch); + writel(0x00000001, &dev.SMA->dwCurrPlayRate); + + writew(0x0000, &dev.SMA->wCurrDSPStatusFlags); + writew(0x0000, &dev.SMA->wCurrHostStatusFlags); + + writew(0x303, &dev.SMA->wCurrInputTagBits); + writew(0, &dev.SMA->wCurrLeftPeak); + writew(0, &dev.SMA->wCurrRightPeak); + + writeb(0, &dev.SMA->bInPotPosRight); + writeb(0, &dev.SMA->bInPotPosLeft); + + writeb(0, &dev.SMA->bAuxPotPosRight); + writeb(0, &dev.SMA->bAuxPotPosLeft); + + writew(1, &dev.SMA->wCurrPlayFormat); + writew(dev.sample_size, &dev.SMA->wCurrPlaySampleSize); + writew(dev.channels, &dev.SMA->wCurrPlayChannels); + writew(dev.sample_rate, &dev.SMA->wCurrPlaySampleRate); + writew(dev.sample_rate, &dev.SMA->wCalFreqAtoD); + + return 0; +} + +static int calibrate_adc(WORD srate) +{ + if (!dev.calibrate_signal) { + + printk(KERN_INFO LOGNAME ": ADC calibration to board ground "); + writew(readw(&dev.SMA->wCurrHostStatusFlags) + | 0x0001, &dev.SMA->wCurrHostStatusFlags); + } + else { + + printk(KERN_INFO LOGNAME ": ADC calibration to signal ground "); + writew(readw(&dev.SMA->wCurrHostStatusFlags) + & ~0x0001, &dev.SMA->wCurrHostStatusFlags); + } + + writew(srate, &dev.SMA->wCalFreqAtoD); + + if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 && + msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) { + + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ; + schedule(); + current->timeout = 0; + printk("successful\n"); + return 0; + } + + printk("failed\n"); + + return -EIO; +} + +static int upload_dsp_code(void) +{ + outb(HPBLKSEL_0, dev.io + HP_BLKS); + +#ifdef HAVE_DSPCODEH + printk(KERN_INFO LOGNAME ": Using resident Turtle Beach DSP code\n"); +#else + printk(KERN_INFO LOGNAME ": Loading Turtle Beach DSP code\n"); + INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE); + if (!INITCODE) { + printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE); + return -EBUSY; + } + + PERMCODESIZE = mod_firmware_load(PERMCODEFILE, &PERMCODE); + if (!PERMCODE) { + printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE); + vfree(INITCODE); + return -EBUSY; + } +#endif + memcpy_toio(dev.base, PERMCODE, PERMCODESIZE); + + if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) { + + printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n"); + return -ENODEV; + } + +#ifndef HAVE_DSPCODEH + vfree(INITCODE); + vfree(PERMCODE); +#endif + + return 0; +} + +static int initialize(void) +{ + int err, timeout; + + if ((err = init_sma()) < 0) { + + printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n"); + return err; + } + + if ((err = reset_dsp()) < 0) + return err; + + if ((err = upload_dsp_code()) < 0) { + + printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n"); + return err; + + } else + printk(KERN_INFO LOGNAME ": DSP upload successful\n"); + + timeout = 2000; + + while (readw(dev.base)) { + + mdelay(1); + if (--timeout < 0) + return -EIO; + } + + return 0; +} + +static int attach_multisound(void) +{ + int err; + + printk(KERN_DEBUG LOGNAME ": Intializing DSP\n"); + + if ((err = request_irq(dev.irq, intr, SA_SHIRQ, DEVNAME, &dev)) < 0) { + + printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq); + return err; + + } + + request_region(dev.io, dev.numio, DEVNAME); + + if ((err = initialize()) < 0) { + + printk(KERN_WARNING LOGNAME ": Initialization failure\n"); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return err; + + } + + if ((err = msnd_register(&dev)) < 0) { + + printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n"); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return err; + } + + if ((DSPMINOR = register_sound_dsp(&dev_fileops)) < 0) { + + printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n"); + msnd_unregister(&dev); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return DSPMINOR; + } + + if ((MIXERMINOR = register_sound_mixer(&dev_fileops)) < 0) { + + printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n"); + unregister_sound_mixer(MIXERMINOR); + msnd_unregister(&dev); + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + return MIXERMINOR; + } + printk(KERN_INFO LOGNAME ": Using DSP minor %d, mixer minor %d\n", MIXERMINOR, DSPMINOR); + + calibrate_adc(dev.sample_rate); + set_recsrc(0); + + return 0; +} + +static void unload_multisound(void) +{ + release_region(dev.io, dev.numio); + free_irq(dev.irq, &dev); + unregister_sound_mixer(MIXERMINOR); + unregister_sound_dsp(DSPMINOR); + msnd_unregister(&dev); +} + +static void mod_inc_ref(void) +{ + MOD_INC_USE_COUNT; +} + +static void mod_dec_ref(void) +{ + MOD_DEC_USE_COUNT; +} + +#ifdef MODULE +MODULE_AUTHOR ("Andrew Veliath "); +MODULE_DESCRIPTION ("Turtle Beach " LONGNAME " Linux Driver"); +MODULE_PARM (io, "i"); +MODULE_PARM (irq, "i"); +MODULE_PARM (mem, "i"); +MODULE_PARM (major, "i"); +MODULE_PARM (fifosize, "i"); +MODULE_PARM (calibrate_signal, "i"); + +static int io = -1; +static int irq = -1; +static int mem = -1; +static int fifosize = DEFFIFOSIZE; +static int calibrate_signal; + +int init_module(void) +#else +static int io = CONFIG_MSNDPIN_IO; +static int irq = CONFIG_MSNDPIN_IRQ; +static int mem = CONFIG_MSNDPIN_MEM; +static int fifosize = DEFFIFOSIZE; +static int calibrate_signal; + +int msnd_pinnacle_init(void) +#endif +{ + int err; + + printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version " + VERSION ", Copyright (C) 1998 Andrew Veliath\n"); + + if (io == -1 || irq == -1 || mem == -1) { + + printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n"); + } + + if (io == -1 || + !(io == 0x290 || + io == 0x260 || + io == 0x250 || + io == 0x240 || + io == 0x230 || + io == 0x220 || + io == 0x210 || + io == 0x3e0)) { + + printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set\n"); + return -EINVAL; + } + + if (irq == -1 || + !(irq == 5 || + irq == 7 || + irq == 9 || + irq == 10 || + irq == 11 || + irq == 12)) { + + printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n"); + return -EINVAL; + } + + if (mem == -1 || + !(mem == 0xb0000 || + mem == 0xc8000 || + mem == 0xd0000 || + mem == 0xd8000 || + mem == 0xe0000 || + mem == 0xe8000)) { + + printk(KERN_ERR LOGNAME ": \"mem\" - must be set to " + "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); + return -EINVAL; + } + + if (fifosize < 16) + fifosize = 16; + + if (fifosize > 768) + fifosize = 768; + + dev.type = msndPinnacle; + dev.io = io; + dev.numio = DSP_NUMIO; + dev.irq = irq; + dev.base = phys_to_virt(mem); + dev.fifosize = fifosize * 1024; + dev.calibrate_signal = calibrate_signal ? 1 : 0; + dev.recsrc = 0; + dev.inc_ref = mod_inc_ref; + dev.dec_ref = mod_dec_ref; + + init_waitqueue(&dev.writeblock); + init_waitqueue(&dev.readblock); + msnd_fifo_init(&dev.DAPF); + msnd_fifo_init(&dev.DARF); + + printk(KERN_INFO LOGNAME ": Using %u byte digital audio FIFOs (x2)\n", dev.fifosize); + + if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) { + + printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n"); + return err; + } + + if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) { + + printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n"); + msnd_fifo_free(&dev.DAPF); + return err; + } + + if ((err = probe_multisound()) < 0) { + + printk(KERN_ERR LOGNAME ": Probe failed\n"); + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + return err; + + } + + if ((err = attach_multisound()) < 0) { + + printk(KERN_ERR LOGNAME ": Attach failed\n"); + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + return err; + + } + + return 0; +} + +#ifdef MODULE + +void cleanup_module(void) +{ + printk(KERN_INFO LOGNAME ": Unloading\n"); + + unload_multisound(); + + msnd_fifo_free(&dev.DAPF); + msnd_fifo_free(&dev.DARF); + +} +#endif diff --git a/drivers/sound/msnd_pinnacle.h b/drivers/sound/msnd_pinnacle.h new file mode 100644 index 000000000000..9be03e1ce11f --- /dev/null +++ b/drivers/sound/msnd_pinnacle.h @@ -0,0 +1,255 @@ +/********************************************************************* + * + * msnd_pinnacle.h + * + * Turtle Beach MultiSound Soundcard 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. + * + * $Id: msnd_pinnacle.h,v 1.3 1998/06/09 20:39:34 andrewtv Exp $ + * + ********************************************************************/ +#ifndef __MSND_PINNACLE_H +#define __MSND_PINNACLE_H + +#define DSP_NUMIO 0x08 + +#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 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 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 WAVEHDR_MOP 0 +#define EXTOUT_MOP 1 +#define HWINIT_MOP 0xFE +#define NO_MOP 0xFF + +#define MAX_MOP 1 + +#define EXTIN_MIP 0 +#define WAVEHDR_MIP 1 +#define HWINIT_MIP 0xFE + +#define MAX_MIP 1 + +struct SMA0_CommonData { + WORD wCurrPlayBytes; + WORD wCurrRecordBytes; + WORD wCurrPlayVolLeft; + WORD wCurrPlayVolRight; + + WORD wCurrInVolLeft; + WORD wCurrInVolRight; + WORD wCurrMHdrVolLeft; + WORD wCurrMHdrVolRight; + + DWORD dwCurrPlayPitch; + DWORD dwCurrPlayRate; + + WORD wCurrMIDIIOPatch; + + WORD wCurrPlayFormat; + WORD wCurrPlaySampleSize; + WORD wCurrPlayChannels; + WORD wCurrPlaySampleRate; + + WORD wCurrRecordFormat; + WORD wCurrRecordSampleSize; + WORD wCurrRecordChannels; + WORD wCurrRecordSampleRate; + + WORD wCurrDSPStatusFlags; + WORD wCurrHostStatusFlags; + + WORD wCurrInputTagBits; + WORD wCurrLeftPeak; + WORD wCurrRightPeak; + + BYTE bMicPotPosLeft; + BYTE bMicPotPosRight; + + BYTE bMicPotMaxLeft; + BYTE bMicPotMaxRight; + + BYTE bInPotPosLeft; + BYTE bInPotPosRight; + + BYTE bAuxPotPosLeft; + BYTE bAuxPotPosRight; + + BYTE bInPotMaxLeft; + BYTE bInPotMaxRight; + BYTE bAuxPotMaxLeft; + BYTE bAuxPotMaxRight; + BYTE bInPotMaxMethod; + BYTE bAuxPotMaxMethod; + + WORD wCurrMastVolLeft; + WORD wCurrMastVolRight; + + WORD wCalFreqAtoD; + + WORD wCurrAuxVolLeft; + WORD wCurrAuxVolRight; + + WORD wCurrPlay1VolLeft; + WORD wCurrPlay1VolRight; + WORD wCurrPlay2VolLeft; + WORD wCurrPlay2VolRight; + WORD wCurrPlay3VolLeft; + WORD wCurrPlay3VolRight; + WORD wCurrPlay4VolLeft; + WORD wCurrPlay4VolRight; + WORD wCurrPlay1PeakLeft; + WORD wCurrPlay1PeakRight; + WORD wCurrPlay2PeakLeft; + WORD wCurrPlay2PeakRight; + WORD wCurrPlay3PeakLeft; + WORD wCurrPlay3PeakRight; + WORD wCurrPlay4PeakLeft; + WORD wCurrPlay4PeakRight; + WORD wCurrPlayPeakLeft; + WORD wCurrPlayPeakRight; + + WORD wCurrDATSR; + WORD wCurrDATRXCHNL; + WORD wCurrDATTXCHNL; + WORD wCurrDATRXRate; + + DWORD dwDSPPlayCount; +} GCC_PACKED; + +#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 */ diff --git a/drivers/sound/opl3.c b/drivers/sound/opl3.c index 54889ce3512b..3cc12ca7d8af 100644 --- a/drivers/sound/opl3.c +++ b/drivers/sound/opl3.c @@ -2,27 +2,37 @@ * sound/opl3.c * * A low level driver for Yamaha YM3812 and OPL-3 -chips - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1996 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) +* + * 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 + * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) + * Alan Cox modularisation, fixed sound_mem allocs. + * + * Status + * Believed to work. Badly needs rewriting a bit to support multiple + * OPL3 devices. */ -#include +#include +#include +#include /* * Major improvements to the FM handling 30AUG92 by Rob Hooft, - */ -/* * hooft@chem.ruu.nl */ #include "sound_config.h" +#include "soundmodule.h" -#if defined(CONFIG_YM3812) +#ifdef CONFIG_YM3812 #include "opl3.h" @@ -30,310 +40,240 @@ #define OFFS_4OP 11 struct voice_info - { - unsigned char keyon_byte; - long bender; - long bender_range; - unsigned long orig_freq; - unsigned long current_freq; - int volume; - int mode; - }; +{ + unsigned char keyon_byte; + long bender; + long bender_range; + unsigned long orig_freq; + unsigned long current_freq; + int volume; + int mode; + int panning; /* 0xffff means not set */ +}; typedef struct opl_devinfo - { - int base; - int left_io, right_io; - int nr_voice; - int lv_map[MAX_VOICE]; +{ + int base; + int left_io, right_io; + int nr_voice; + int lv_map[MAX_VOICE]; - struct voice_info voc[MAX_VOICE]; - struct voice_alloc_info *v_alloc; - struct channel_info *chn_info; + struct voice_info voc[MAX_VOICE]; + struct voice_alloc_info *v_alloc; + struct channel_info *chn_info; - struct sbi_instrument i_map[SBFM_MAXINSTR]; - struct sbi_instrument *act_i[MAX_VOICE]; + struct sbi_instrument i_map[SBFM_MAXINSTR]; + struct sbi_instrument *act_i[MAX_VOICE]; - struct synth_info fm_info; + struct synth_info fm_info; - int busy; - int model; - unsigned char cmask; + int busy; + int model; + unsigned char cmask; - int is_opl4; - int *osp; - } -opl_devinfo; + int is_opl4; + int *osp; +} opl_devinfo; static struct opl_devinfo *devc = NULL; -static int force_opl3_mode = 0; - static int detected_model; -static int store_instr (int instr_no, struct sbi_instrument *instr); -static void freq_to_fnum (int freq, int *block, int *fnum); -static void opl3_command (int io_addr, unsigned int addr, unsigned int val); -static int opl3_kill_note (int dev, int voice, int note, int velocity); - -void -enable_opl3_mode (int left, int right, int both) -{ - force_opl3_mode = 1; -} +static int store_instr(int instr_no, struct sbi_instrument *instr); +static void freq_to_fnum(int freq, int *block, int *fnum); +static void opl3_command(int io_addr, unsigned int addr, unsigned int val); +static int opl3_kill_note(int dev, int voice, int note, int velocity); -static void -enter_4op_mode (void) +static void enter_4op_mode(void) { - int i; - static int v4op[MAX_VOICE] = - {0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17}; - - devc->cmask = 0x3f; /* Connect all possible 4 OP voice operators */ - opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f); - - for (i = 0; i < 3; i++) - pv_map[i].voice_mode = 4; - for (i = 3; i < 6; i++) - pv_map[i].voice_mode = 0; - - for (i = 9; i < 12; i++) - pv_map[i].voice_mode = 4; - for (i = 12; i < 15; i++) - pv_map[i].voice_mode = 0; - - for (i = 0; i < 12; i++) - devc->lv_map[i] = v4op[i]; - devc->v_alloc->max_voice = devc->nr_voice = 12; + int i; + static int v4op[MAX_VOICE] = { + 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17 + }; + + devc->cmask = 0x3f; /* Connect all possible 4 OP voice operators */ + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f); + + for (i = 0; i < 3; i++) + pv_map[i].voice_mode = 4; + for (i = 3; i < 6; i++) + pv_map[i].voice_mode = 0; + + for (i = 9; i < 12; i++) + pv_map[i].voice_mode = 4; + for (i = 12; i < 15; i++) + pv_map[i].voice_mode = 0; + + for (i = 0; i < 12; i++) + devc->lv_map[i] = v4op[i]; + devc->v_alloc->max_voice = devc->nr_voice = 12; } -static int -opl3_ioctl (int dev, - unsigned int cmd, caddr_t arg) +static int opl3_ioctl(int dev, unsigned int cmd, caddr_t arg) { - switch (cmd) - { - - case SNDCTL_FM_LOAD_INSTR: - { struct sbi_instrument ins; - - memcpy_fromfs ((char *) &ins, &((char *) arg)[0], sizeof (ins)); - - if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) - { - printk ("FM Error: Invalid instrument number %d\n", ins.channel); - return -(EINVAL); - } - - pmgr_inform (dev, PM_E_PATCH_LOADED, ins.channel, 0, 0, 0); - return store_instr (ins.channel, &ins); - } - break; - - case SNDCTL_SYNTH_INFO: - devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice; - - memcpy_tofs (&((char *) arg)[0], &devc->fm_info, sizeof (devc->fm_info)); - return 0; - break; - - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - break; - - case SNDCTL_FM_4OP_ENABLE: - if (devc->model == 2) - enter_4op_mode (); - return 0; - break; - - default: - return -(EINVAL); - } - + + switch (cmd) { + case SNDCTL_FM_LOAD_INSTR: + printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); + if (copy_from_user(&ins, arg, sizeof(ins))) + return -EFAULT; + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { + printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); + return -EINVAL; + } + return store_instr(ins.channel, &ins); + + case SNDCTL_SYNTH_INFO: + devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice; + if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info))) + return -EFAULT; + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + + case SNDCTL_FM_4OP_ENABLE: + if (devc->model == 2) + enter_4op_mode(); + return 0; + + default: + return -EINVAL; + } } -int -opl3_detect (int ioaddr, int *osp) +int opl3_detect(int ioaddr, int *osp) { - /* - * This function returns 1 if the FM chip is present at the given I/O port - * The detection algorithm plays with the timer built in the FM chip and - * looks for a change in the status register. - * - * Note! The timers of the FM chip are not connected to AdLib (and compatible) - * boards. - * - * Note2! The chip is initialized if detected. - */ - - unsigned char stat1, stat2, signature; - int i; - - if (devc != NULL) - return 0; - - - devc = (struct opl_devinfo *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (*devc))); - if (sound_nblocks < 1024) - sound_nblocks++;; - - if (devc == NULL) - { - printk ("OPL3: Can't allocate memory for the device control structure\n"); - return 0; - } - - devc->osp = osp; - devc->base = ioaddr; - - /* Reset timers 1 and 2 */ - opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); - - /* Reset the IRQ of the FM chip */ - opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); - - signature = stat1 = inb (ioaddr); /* Status register */ - - if ((stat1 & 0xE0) != 0x00) - { - return 0; /* - * Should be 0x00 - */ - } - - opl3_command (ioaddr, TIMER1_REGISTER, 0xff); /* Set timer1 to 0xff */ - - opl3_command (ioaddr, TIMER_CONTROL_REGISTER, - TIMER2_MASK | TIMER1_START); /* - * Unmask and start timer 1 - */ - - /* - * Now we have to delay at least 80 usec - */ - - for (i = 0; i < 50; i++) - tenmicrosec (devc->osp); - - stat2 = inb (ioaddr); /* - * Read status after timers have expired - */ - - /* - * Stop the timers - */ - - /* Reset timers 1 and 2 */ - opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); - /* Reset the IRQ of the FM chip */ - opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); - - if ((stat2 & 0xE0) != 0xc0) - { - return 0; /* - * There is no YM3812 - */ - } + /* + * This function returns 1 if the FM chip is present at the given I/O port + * The detection algorithm plays with the timer built in the FM chip and + * looks for a change in the status register. + * + * Note! The timers of the FM chip are not connected to AdLib (and compatible) + * boards. + * + * Note2! The chip is initialized if detected. + */ + + unsigned char stat1, signature; + int i; + + if (devc != NULL) + { + printk(KERN_ERR "opl3: Only one OPL3 supported.\n"); + return 0; + } - /* - * There is a FM chip in this address. Detect the type (OPL2 to OPL4) - */ + devc = (struct opl_devinfo *)kmalloc(sizeof(*devc), GFP_KERNEL); - if (signature == 0x06 && !force_opl3_mode) /* OPL2 */ - { - detected_model = 2; - } - else if (signature == 0x00) /* OPL3 or OPL4 */ - { - unsigned char tmp; + if (devc == NULL) + { + printk(KERN_ERR "OPL3: Can't allocate memory for the device control " + "structure \n "); + return 0; + } + devc->osp = osp; + devc->base = ioaddr; - detected_model = 3; + /* Reset timers 1 and 2 */ + opl3_command(ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); - /* - * Detect availability of OPL4 (_experimental_). Works probably - * only after a cold boot. In addition the OPL4 port - * of the chip may not be connected to the PC bus at all. - */ + /* Reset the IRQ of the FM chip */ + opl3_command(ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); - opl3_command (ioaddr + 2, OPL3_MODE_REGISTER, 0x00); - opl3_command (ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE); + signature = stat1 = inb(ioaddr); /* Status register */ - if ((tmp = inb (ioaddr)) == 0x02) /* Have a OPL4 */ + if (signature != 0x00 && signature != 0x06 && signature != 0x02 && + signature != 0x0f) { - detected_model = 4; + MDB(printk(KERN_INFO "OPL3 not detected %x\n", signature)); + return 0; } - if (!check_region (ioaddr - 8, 2)) /* OPL4 port is free */ + if (signature == 0x06) /* OPL2 */ { - int tmp; - - outb (0x02, ioaddr - 8); /* Select OPL4 ID register */ - tenmicrosec (devc->osp); - tmp = inb (ioaddr - 7); /* Read it */ - tenmicrosec (devc->osp); - - if (tmp == 0x20) /* OPL4 should return 0x20 here */ - { - detected_model = 4; - - outb (0xF8, ioaddr - 8); /* Select OPL4 FM mixer control */ - tenmicrosec (devc->osp); - outb (0x1B, ioaddr - 7); /* Write value */ - tenmicrosec (devc->osp); - } - else - detected_model = 3; + detected_model = 2; } + else if (signature == 0x00 || signature == 0x0f) /* OPL3 or OPL4 */ + { + unsigned char tmp; + + detected_model = 3; + + /* + * Detect availability of OPL4 (_experimental_). Works probably + * only after a cold boot. In addition the OPL4 port + * of the chip may not be connected to the PC bus at all. + */ + + opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0x00); + opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE); + + if ((tmp = inb(ioaddr)) == 0x02) /* Have a OPL4 */ + { + detected_model = 4; + } + + if (!check_region(ioaddr - 8, 2)) /* OPL4 port is free */ + { + int tmp; + + outb((0x02), ioaddr - 8); /* Select OPL4 ID register */ + udelay(10); + tmp = inb(ioaddr - 7); /* Read it */ + udelay(10); + + if (tmp == 0x20) /* OPL4 should return 0x20 here */ + { + detected_model = 4; + outb((0xF8), ioaddr - 8); /* Select OPL4 FM mixer control */ + udelay(10); + outb((0x1B), ioaddr - 7); /* Write value */ + udelay(10); + } + else + detected_model = 3; + } + opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0); + } + for (i = 0; i < 9; i++) + opl3_command(ioaddr, KEYON_BLOCK + i, 0); /* + * Note off + */ - opl3_command (ioaddr + 2, OPL3_MODE_REGISTER, 0); - - } - - for (i = 0; i < 9; i++) - opl3_command (ioaddr, KEYON_BLOCK + i, 0); /* - * Note off - */ - - opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT); - opl3_command (ioaddr, PERCUSSION_REGISTER, 0x00); /* - * Melodic mode. - */ - - return 1; + opl3_command(ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT); + opl3_command(ioaddr, PERCOSSION_REGISTER, 0x00); /* + * Melodic mode. + */ + return 1; } -static int -opl3_kill_note (int devno, int voice, int note, int velocity) +static int opl3_kill_note (int devno, int voice, int note, int velocity) { - struct physical_voice_info *map; - - if (voice < 0 || voice >= devc->nr_voice) - return 0; + struct physical_voice_info *map; - devc->v_alloc->map[voice] = 0; + if (voice < 0 || voice >= devc->nr_voice) + return 0; - map = &pv_map[devc->lv_map[voice]]; + devc->v_alloc->map[voice] = 0; - DEB (printk ("Kill note %d\n", voice)); + map = &pv_map[devc->lv_map[voice]]; + DEB(printk("Kill note %d\n", voice)); - if (map->voice_mode == 0) - return 0; - - opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20); - - devc->voc[voice].keyon_byte = 0; - devc->voc[voice].bender = 0; - devc->voc[voice].volume = 64; - devc->voc[voice].bender_range = 200; /* - * 200 cents = 2 semitones - */ - devc->voc[voice].orig_freq = 0; - devc->voc[voice].current_freq = 0; - devc->voc[voice].mode = 0; + if (map->voice_mode == 0) + return 0; - return 0; + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20); + devc->voc[voice].keyon_byte = 0; + devc->voc[voice].bender = 0; + devc->voc[voice].volume = 64; + devc->voc[voice].panning = 0xffff; /* Not set */ + devc->voc[voice].bender_range = 200; + devc->voc[voice].orig_freq = 0; + devc->voc[voice].current_freq = 0; + devc->voc[voice].mode = 0; + return 0; } #define HIHAT 0 @@ -344,28 +284,23 @@ opl3_kill_note (int devno, int voice, int note, int velocity) #define UNDEFINED TOMTOM #define DEFAULT TOMTOM -static int -store_instr (int instr_no, struct sbi_instrument *instr) +static int store_instr(int instr_no, struct sbi_instrument *instr) { - - if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2)) - printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key); - memcpy ((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof (*instr)); - - return 0; + if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2)) + printk(KERN_WARNING "FM warning: Invalid patch format field (key) 0x%x\n", instr->key); + memcpy((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof(*instr)); + return 0; } -static int -opl3_set_instr (int dev, int voice, int instr_no) +static int opl3_set_instr (int dev, int voice, int instr_no) { - if (voice < 0 || voice >= devc->nr_voice) - return 0; - - if (instr_no < 0 || instr_no >= SBFM_MAXINSTR) - return 0; + if (voice < 0 || voice >= devc->nr_voice) + return 0; + if (instr_no < 0 || instr_no >= SBFM_MAXINSTR) + instr_no = 0; /* Acoustic piano (usually) */ - devc->act_i[voice] = &devc->i_map[instr_no]; - return 0; + devc->act_i[voice] = &devc->i_map[instr_no]; + return 0; } /* @@ -377,873 +312,916 @@ opl3_set_instr (int dev, int voice, int instr_no) * volume -8 it was implemented as a table because it is only 128 bytes and * it saves a lot of log() calculations. (RH) */ -char fm_volume_table[128] = -{-64, -48, -40, -35, -32, -29, -27, -26, - -24, -23, -21, -20, -19, -18, -18, -17, - -16, -15, -15, -14, -13, -13, -12, -12, - -11, -11, -10, -10, -10, -9, -9, -8, - -8, -8, -7, -7, -7, -6, -6, -6, - -5, -5, -5, -5, -4, -4, -4, -4, - -3, -3, -3, -3, -2, -2, -2, -2, - -2, -1, -1, -1, -1, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 4, - 4, 4, 4, 4, 4, 4, 4, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 8, 8, 8, 8, 8}; - -static void -calc_vol (unsigned char *regbyte, int volume, int main_vol) + +static char fm_volume_table[128] = { - int level = (~*regbyte & 0x3f); + -64, -48, -40, -35, -32, -29, -27, -26, + -24, -23, -21, -20, -19, -18, -18, -17, + -16, -15, -15, -14, -13, -13, -12, -12, + -11, -11, -10, -10, -10, -9, -9, -8, + -8, -8, -7, -7, -7, -6, -6, -6, + -5, -5, -5, -5, -4, -4, -4, -4, + -3, -3, -3, -3, -2, -2, -2, -2, + -2, -1, -1, -1, -1, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8 +}; - if (main_vol > 127) - main_vol = 127; +static void calc_vol(unsigned char *regbyte, int volume, int main_vol) +{ + int level = (~*regbyte & 0x3f); - volume = (volume * main_vol) / 127; + if (main_vol > 127) + main_vol = 127; + volume = (volume * main_vol) / 127; - if (level) - level += fm_volume_table[volume]; + if (level) + level += fm_volume_table[volume]; - if (level > 0x3f) - level = 0x3f; - if (level < 0) - level = 0; + if (level > 0x3f) + level = 0x3f; + if (level < 0) + level = 0; - *regbyte = (*regbyte & 0xc0) | (~level & 0x3f); + *regbyte = (*regbyte & 0xc0) | (~level & 0x3f); } -static void -set_voice_volume (int voice, int volume, int main_vol) +static void set_voice_volume(int voice, int volume, int main_vol) { - unsigned char vol1, vol2, vol3, vol4; - struct sbi_instrument *instr; - struct physical_voice_info *map; + unsigned char vol1, vol2, vol3, vol4; + struct sbi_instrument *instr; + struct physical_voice_info *map; - if (voice < 0 || voice >= devc->nr_voice) - return; + if (voice < 0 || voice >= devc->nr_voice) + return; - map = &pv_map[devc->lv_map[voice]]; + map = &pv_map[devc->lv_map[voice]]; + instr = devc->act_i[voice]; - instr = devc->act_i[voice]; + if (!instr) + instr = &devc->i_map[0]; - if (!instr) - instr = &devc->i_map[0]; + if (instr->channel < 0) + return; - if (instr->channel < 0) - return; + if (devc->voc[voice].mode == 0) + return; - if (devc->voc[voice].mode == 0) - return; - - if (devc->voc[voice].mode == 2) - { - - vol1 = instr->operators[2]; - vol2 = instr->operators[3]; - - if ((instr->operators[10] & 0x01)) + if (devc->voc[voice].mode == 2) { - calc_vol (&vol1, volume, main_vol); - calc_vol (&vol2, volume, main_vol); + vol1 = instr->operators[2]; + vol2 = instr->operators[3]; + if ((instr->operators[10] & 0x01)) + { + calc_vol(&vol1, volume, main_vol); + calc_vol(&vol2, volume, main_vol); + } + else + { + calc_vol(&vol2, volume, main_vol); + } + opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2); } - else - { - calc_vol (&vol2, volume, main_vol); - } - - opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); - opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); - } - else - { /* - * 4 OP voice - */ - int connection; - - vol1 = instr->operators[2]; - vol2 = instr->operators[3]; - vol3 = instr->operators[OFFS_4OP + 2]; - vol4 = instr->operators[OFFS_4OP + 3]; - - /* - * The connection method for 4 OP devc->voc is defined by the rightmost - * bits at the offsets 10 and 10+OFFS_4OP - */ - - connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); - - switch (connection) - { - case 0: - calc_vol (&vol4, volume, main_vol); - break; - - case 1: - calc_vol (&vol2, volume, main_vol); - calc_vol (&vol4, volume, main_vol); - break; - - case 2: - calc_vol (&vol1, volume, main_vol); - calc_vol (&vol4, volume, main_vol); - break; - - case 3: - calc_vol (&vol1, volume, main_vol); - calc_vol (&vol3, volume, main_vol); - calc_vol (&vol4, volume, main_vol); - break; - - default:; + else + { /* + * 4 OP voice + */ + int connection; + + vol1 = instr->operators[2]; + vol2 = instr->operators[3]; + vol3 = instr->operators[OFFS_4OP + 2]; + vol4 = instr->operators[OFFS_4OP + 3]; + + /* + * The connection method for 4 OP devc->voc is defined by the rightmost + * bits at the offsets 10 and 10+OFFS_4OP + */ + + connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + + switch (connection) + { + case 0: + calc_vol(&vol4, volume, main_vol); + break; + + case 1: + calc_vol(&vol2, volume, main_vol); + calc_vol(&vol4, volume, main_vol); + break; + + case 2: + calc_vol(&vol1, volume, main_vol); + calc_vol(&vol4, volume, main_vol); + break; + + case 3: + calc_vol(&vol1, volume, main_vol); + calc_vol(&vol3, volume, main_vol); + calc_vol(&vol4, volume, main_vol); + break; + + default: + ; + } + opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], vol3); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], vol4); } - - opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); - opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); - opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3); - opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4); - } } -static int -opl3_start_note (int dev, int voice, int note, int volume) +static int opl3_start_note (int dev, int voice, int note, int volume) { - unsigned char data, fpc; - int block, fnum, freq, voice_mode; - struct sbi_instrument *instr; - struct physical_voice_info *map; + unsigned char data, fpc; + int block, fnum, freq, voice_mode, pan; + struct sbi_instrument *instr; + struct physical_voice_info *map; - if (voice < 0 || voice >= devc->nr_voice) - return 0; + if (voice < 0 || voice >= devc->nr_voice) + return 0; - map = &pv_map[devc->lv_map[voice]]; + map = &pv_map[devc->lv_map[voice]]; + pan = devc->voc[voice].panning; - if (map->voice_mode == 0) - return 0; + if (map->voice_mode == 0) + return 0; - if (note == 255) /* + if (note == 255) /* * Just change the volume */ - { - set_voice_volume (voice, volume, devc->voc[voice].volume); - return 0; - } - - /* - * Kill previous note before playing - */ - opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* - * Carrier - * volume to - * min - */ - opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* - * Modulator - * volume to - */ + { + set_voice_volume(voice, volume, devc->voc[voice].volume); + return 0; + } - if (map->voice_mode == 4) - { - opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff); - opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff); - } + /* + * Kill previous note before playing + */ + + opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* + * Carrier + * volume to + * min + */ + opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* + * Modulator + * volume to + */ + + if (map->voice_mode == 4) + { + opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff); + } - opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* * Note * off */ - instr = devc->act_i[voice]; - - if (!instr) - instr = &devc->i_map[0]; + instr = devc->act_i[voice]; + + if (!instr) + instr = &devc->i_map[0]; - if (instr->channel < 0) - { - printk ( - "OPL3: Initializing voice %d with undefined instrument\n", - voice); - return 0; - } + if (instr->channel < 0) + { + printk(KERN_WARNING "OPL3: Initializing voice %d with undefined instrument\n", voice); + return 0; + } - if (map->voice_mode == 2 && instr->key == OPL3_PATCH) - return 0; /* + if (map->voice_mode == 2 && instr->key == OPL3_PATCH) + return 0; /* * Cannot play */ - voice_mode = map->voice_mode; + voice_mode = map->voice_mode; - if (voice_mode == 4) - { - int voice_shift; + if (voice_mode == 4) + { + int voice_shift; - voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3; - voice_shift += map->voice_num; + voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3; + voice_shift += map->voice_num; - if (instr->key != OPL3_PATCH) /* - * Just 2 OP patch - */ - { - voice_mode = 2; - devc->cmask &= ~(1 << voice_shift); + if (instr->key != OPL3_PATCH) /* + * Just 2 OP patch + */ + { + voice_mode = 2; + devc->cmask &= ~(1 << voice_shift); + } + else + { + devc->cmask |= (1 << voice_shift); + } + + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); } - else + + /* + * Set Sound Characteristics + */ + + opl3_command(map->ioaddr, AM_VIB + map->op[0], instr->operators[0]); + opl3_command(map->ioaddr, AM_VIB + map->op[1], instr->operators[1]); + + /* + * Set Attack/Decay + */ + + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]); + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]); + + /* + * Set Sustain/Release + */ + + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]); + + /* + * Set Wave Select + */ + + opl3_command(map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]); + opl3_command(map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]); + + /* + * Set Feedback/Connection + */ + + fpc = instr->operators[10]; + + if (pan != 0xffff) { - devc->cmask |= (1 << voice_shift); + fpc &= ~STEREO_BITS; + if (pan < -64) + fpc |= VOICE_TO_LEFT; + else + if (pan > 64) + fpc |= VOICE_TO_RIGHT; + else + fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT); } - opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); - } - - /* - * Set Sound Characteristics - */ - opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]); - opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]); - - /* - * Set Attack/Decay - */ - opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]); - opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]); - - /* - * Set Sustain/Release - */ - opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); - opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]); - - /* - * Set Wave Select - */ - opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]); - opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]); - - /* - * Set Feedback/Connection - */ - fpc = instr->operators[10]; - if (!(fpc & 0x30)) - fpc |= 0x30; /* + if (!(fpc & 0x30)) + fpc |= 0x30; /* * Ensure that at least one chn is enabled */ - opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, - fpc); - - /* - * If the voice is a 4 OP one, initialize the operators 3 and 4 also - */ - - if (voice_mode == 4) - { - - /* - * Set Sound Characteristics - */ - opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); - opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]); - - /* - * Set Attack/Decay - */ - opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); - opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]); - - /* - * Set Sustain/Release - */ - opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); - opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]); - - /* - * Set Wave Select - */ - opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); - opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]); - - /* - * Set Feedback/Connection - */ - fpc = instr->operators[OFFS_4OP + 10]; - if (!(fpc & 0x30)) - fpc |= 0x30; /* - * Ensure that at least one chn is enabled - */ - opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc); - } + opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc); + + /* + * If the voice is a 4 OP one, initialize the operators 3 and 4 also + */ - devc->voc[voice].mode = voice_mode; + if (voice_mode == 4) + { + /* + * Set Sound Characteristics + */ + + opl3_command(map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); + opl3_command(map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]); + + /* + * Set Attack/Decay + */ + + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); + opl3_command(map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]); + + /* + * Set Sustain/Release + */ + + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); + opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]); + + /* + * Set Wave Select + */ + + opl3_command(map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); + opl3_command(map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]); + + /* + * Set Feedback/Connection + */ + + fpc = instr->operators[OFFS_4OP + 10]; + if (!(fpc & 0x30)) + fpc |= 0x30; /* + * Ensure that at least one chn is enabled + */ + opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc); + } - set_voice_volume (voice, volume, devc->voc[voice].volume); + devc->voc[voice].mode = voice_mode; + set_voice_volume(voice, volume, devc->voc[voice].volume); - freq = devc->voc[voice].orig_freq = note_to_freq (note) / 1000; + freq = devc->voc[voice].orig_freq = note_to_freq(note) / 1000; - /* - * Since the pitch bender may have been set before playing the note, we - * have to calculate the bending now. - */ + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ - freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range); - devc->voc[voice].current_freq = freq; + freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0); + devc->voc[voice].current_freq = freq; - freq_to_fnum (freq, &block, &fnum); + freq_to_fnum(freq, &block, &fnum); - /* - * Play note - */ + /* + * Play note + */ - data = fnum & 0xff; /* + data = fnum & 0xff; /* * Least significant bits of fnumber */ - opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); + opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); - data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); - devc->voc[voice].keyon_byte = data; - opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); - if (voice_mode == 4) - opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); + devc->voc[voice].keyon_byte = data; + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); + if (voice_mode == 4) + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); - return 0; + return 0; } -static void -freq_to_fnum (int freq, int *block, int *fnum) +static void freq_to_fnum (int freq, int *block, int *fnum) { - int f, octave; + int f, octave; - /* - * Converts the note frequency to block and fnum values for the FM chip - */ - /* - * First try to compute the block -value (octave) where the note belongs - */ + /* + * Converts the note frequency to block and fnum values for the FM chip + */ + /* + * First try to compute the block -value (octave) where the note belongs + */ - f = freq; + f = freq; - octave = 5; + octave = 5; - if (f == 0) - octave = 0; - else if (f < 261) - { - while (f < 261) + if (f == 0) + octave = 0; + else if (f < 261) { - octave--; - f <<= 1; + while (f < 261) + { + octave--; + f <<= 1; + } } - } - else if (f > 493) - { - while (f > 493) + else if (f > 493) { - octave++; - f >>= 1; + while (f > 493) + { + octave++; + f >>= 1; + } } - } - if (octave > 7) - octave = 7; + if (octave > 7) + octave = 7; - *fnum = freq * (1 << (20 - octave)) / 49716; - *block = octave; + *fnum = freq * (1 << (20 - octave)) / 49716; + *block = octave; } -static void -opl3_command (int io_addr, unsigned int addr, unsigned int val) +static void opl3_command (int io_addr, unsigned int addr, unsigned int val) { - int i; - - /* - * The original 2-OP synth requires a quite long delay after writing to a - * register. The OPL-3 survives with just two INBs - */ - - outb ((unsigned char) (addr & 0xff), io_addr); - - if (!devc->model != 2) - tenmicrosec (devc->osp); - else - for (i = 0; i < 2; i++) - inb (io_addr); - - outb ((unsigned char) (val & 0xff), io_addr + 1); - - if (devc->model != 2) - { - tenmicrosec (devc->osp); - tenmicrosec (devc->osp); - tenmicrosec (devc->osp); - } - else - for (i = 0; i < 2; i++) - inb (io_addr); -} + int i; -static void -opl3_reset (int devno) -{ - int i; + /* + * The original 2-OP synth requires a quite long delay after writing to a + * register. The OPL-3 survives with just two INBs + */ + + outb(((unsigned char) (addr & 0xff)), io_addr); + + if (devc->model != 2) + udelay(10); + else + for (i = 0; i < 2; i++) + inb(io_addr); + + outb(((unsigned char) (val & 0xff)), io_addr + 1); - for (i = 0; i < 18; i++) - devc->lv_map[i] = i; + if (devc->model != 2) + udelay(30); + else + for (i = 0; i < 2; i++) + inb(io_addr); +} - for (i = 0; i < devc->nr_voice; i++) - { - opl3_command (pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff); +static void opl3_reset(int devno) +{ + int i; - opl3_command (pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff); + for (i = 0; i < 18; i++) + devc->lv_map[i] = i; - if (pv_map[devc->lv_map[i]].voice_mode == 4) + for (i = 0; i < devc->nr_voice; i++) { - opl3_command (pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff); + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff); - opl3_command (pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff); - } + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff); - opl3_kill_note (devno, i, 0, 64); - } + if (pv_map[devc->lv_map[i]].voice_mode == 4) + { + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff); - if (devc->model == 2) - { - devc->v_alloc->max_voice = devc->nr_voice = 18; + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff); + } - for (i = 0; i < 18; i++) - pv_map[i].voice_mode = 2; + opl3_kill_note(devno, i, 0, 64); + } - } + if (devc->model == 2) + { + devc->v_alloc->max_voice = devc->nr_voice = 18; + for (i = 0; i < 18; i++) + pv_map[i].voice_mode = 2; + + } } -static int -opl3_open (int dev, int mode) +static int opl3_open(int dev, int mode) { - int i; + int i; - if (devc->busy) - return -(EBUSY); - devc->busy = 1; + if (devc->busy) + return -EBUSY; + MOD_INC_USE_COUNT; + devc->busy = 1; - devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; - devc->v_alloc->timestamp = 0; + devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; + devc->v_alloc->timestamp = 0; - for (i = 0; i < 18; i++) - { - devc->v_alloc->map[i] = 0; - devc->v_alloc->alloc_times[i] = 0; - } + for (i = 0; i < 18; i++) + { + devc->v_alloc->map[i] = 0; + devc->v_alloc->alloc_times[i] = 0; + } - devc->cmask = 0x00; /* + devc->cmask = 0x00; /* * Just 2 OP mode */ - if (devc->model == 2) - opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); - return 0; + if (devc->model == 2) + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); + return 0; } -static void -opl3_close (int dev) +static void opl3_close(int dev) { - devc->busy = 0; - devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; + devc->busy = 0; + devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; - devc->fm_info.nr_drums = 0; - devc->fm_info.perc_mode = 0; + devc->fm_info.nr_drums = 0; + devc->fm_info.perc_mode = 0; - opl3_reset (dev); + opl3_reset(dev); + MOD_DEC_USE_COUNT; } -static void -opl3_hw_control (int dev, unsigned char *event) +static void opl3_hw_control(int dev, unsigned char *event) { } -static int -opl3_load_patch (int dev, int format, const char *addr, - int offs, int count, int pmgr_flag) +static int opl3_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) { - struct sbi_instrument ins; + struct sbi_instrument ins; - if (count < sizeof (ins)) - { - printk ("FM Error: Patch record too short\n"); - return -(EINVAL); - } + if (count = SBFM_MAXINSTR) - { - printk ("FM Error: Invalid instrument number %d\n", ins.channel); - return -(EINVAL); - } - ins.key = format; + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) + { + printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); + return -EINVAL; + } + ins.key = format; - return store_instr (ins.channel, &ins); + return store_instr(ins.channel, &ins); } -static void -opl3_panning (int dev, int voice, int pressure) +static void opl3_panning(int dev, int voice, int value) { + devc->voc[voice].panning = value; } -static void -opl3_volume_method (int dev, int mode) +static void opl3_volume_method(int dev, int mode) { } #define SET_VIBRATO(cell) { \ - tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ - if (pressure > 110) \ - tmp |= 0x40; /* Vibrato on */ \ - opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);} + tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ + if (pressure > 110) \ + tmp |= 0x40; /* Vibrato on */ \ + opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);} -static void -opl3_aftertouch (int dev, int voice, int pressure) +static void opl3_aftertouch(int dev, int voice, int pressure) { - int tmp; - struct sbi_instrument *instr; - struct physical_voice_info *map; - - if (voice < 0 || voice >= devc->nr_voice) - return; + int tmp; + struct sbi_instrument *instr; + struct physical_voice_info *map; - map = &pv_map[devc->lv_map[voice]]; + if (voice < 0 || voice >= devc->nr_voice) + return; - DEB (printk ("Aftertouch %d\n", voice)); + map = &pv_map[devc->lv_map[voice]]; - if (map->voice_mode == 0) - return; + DEB(printk("Aftertouch %d\n", voice)); - /* - * Adjust the amount of vibrato depending the pressure - */ + if (map->voice_mode == 0) + return; - instr = devc->act_i[voice]; + /* + * Adjust the amount of vibrato depending the pressure + */ - if (!instr) - instr = &devc->i_map[0]; + instr = devc->act_i[voice]; - if (devc->voc[voice].mode == 4) - { - int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + if (!instr) + instr = &devc->i_map[0]; - switch (connection) + if (devc->voc[voice].mode == 4) + { + int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); + + switch (connection) + { + case 0: + SET_VIBRATO(4); + break; + + case 1: + SET_VIBRATO(2); + SET_VIBRATO(4); + break; + + case 2: + SET_VIBRATO(1); + SET_VIBRATO(4); + break; + + case 3: + SET_VIBRATO(1); + SET_VIBRATO(3); + SET_VIBRATO(4); + break; + + } + /* + * Not implemented yet + */ + } + else { - case 0: - SET_VIBRATO (4); - break; - - case 1: - SET_VIBRATO (2); - SET_VIBRATO (4); - break; - - case 2: - SET_VIBRATO (1); - SET_VIBRATO (4); - break; - - case 3: - SET_VIBRATO (1); - SET_VIBRATO (3); - SET_VIBRATO (4); - break; + SET_VIBRATO(1); + if ((instr->operators[10] & 0x01)) /* + * Additive synthesis + */ + SET_VIBRATO(2); } - /* - * Not implemented yet - */ - } - else - { - SET_VIBRATO (1); - - if ((instr->operators[10] & 0x01)) /* - * Additive synthesis - */ - SET_VIBRATO (2); - } } #undef SET_VIBRATO -static void -bend_pitch (int dev, int voice, int value) +static void bend_pitch(int dev, int voice, int value) { - unsigned char data; - int block, fnum, freq; - struct physical_voice_info *map; + unsigned char data; + int block, fnum, freq; + struct physical_voice_info *map; - map = &pv_map[devc->lv_map[voice]]; + map = &pv_map[devc->lv_map[voice]]; - if (map->voice_mode == 0) - return; + if (map->voice_mode == 0) + return; - devc->voc[voice].bender = value; - if (!value) - return; - if (!(devc->voc[voice].keyon_byte & 0x20)) - return; /* - * Not keyed on - */ + devc->voc[voice].bender = value; + if (!value) + return; + if (!(devc->voc[voice].keyon_byte & 0x20)) + return; /* + * Not keyed on + */ - freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range); - devc->voc[voice].current_freq = freq; + freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0); + devc->voc[voice].current_freq = freq; - freq_to_fnum (freq, &block, &fnum); + freq_to_fnum(freq, &block, &fnum); - data = fnum & 0xff; /* + data = fnum & 0xff; /* * Least significant bits of fnumber */ - opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); - - data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); /* - * * - * KEYON|OCTAVE|MS - * - * * bits * * - * of * f-num - * - */ - devc->voc[voice].keyon_byte = data; - opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); -} + opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); -static void -opl3_controller (int dev, int voice, int ctrl_num, int value) -{ - if (voice < 0 || voice >= devc->nr_voice) - return; - - switch (ctrl_num) - { - case CTRL_PITCH_BENDER: - bend_pitch (dev, voice, value); - break; - - case CTRL_PITCH_BENDER_RANGE: - devc->voc[voice].bender_range = value; - break; - - case CTL_MAIN_VOLUME: - devc->voc[voice].volume = value / 128; - break; - } + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); + devc->voc[voice].keyon_byte = data; + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); } -static int -opl3_patchmgr (int dev, struct patmgr_info *rec) +static void opl3_controller (int dev, int voice, int ctrl_num, int value) { - return -(EINVAL); + if (voice < 0 || voice >= devc->nr_voice) + return; + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + bend_pitch(dev, voice, value); + break; + + case CTRL_PITCH_BENDER_RANGE: + devc->voc[voice].bender_range = value; + break; + + case CTL_MAIN_VOLUME: + devc->voc[voice].volume = value / 128; + break; + + case CTL_PAN: + devc->voc[voice].panning = (value * 2) - 128; + break; + } } -static void -opl3_bender (int dev, int voice, int value) +static void opl3_bender(int dev, int voice, int value) { - if (voice < 0 || voice >= devc->nr_voice) - return; + if (voice < 0 || voice >= devc->nr_voice) + return; - bend_pitch (dev, voice, value - 8192); + bend_pitch(dev, voice, value - 8192); } -static int -opl3_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc) +static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc) { - int i, p, best, first, avail, best_time = 0x7fffffff; - struct sbi_instrument *instr; - int is4op; - int instr_no; - - if (chn < 0 || chn > 15) - instr_no = 0; - else - instr_no = devc->chn_info[chn].pgm_num; - - instr = &devc->i_map[instr_no]; - if (instr->channel < 0 || /* Instrument not loaded */ - devc->nr_voice != 12) /* Not in 4 OP mode */ - is4op = 0; - else if (devc->nr_voice == 12) /* 4 OP mode */ - is4op = (instr->key == OPL3_PATCH); - else - is4op = 0; - - if (is4op) - { - first = p = 0; - avail = 6; - } - else - { - if (devc->nr_voice == 12) /* 4 OP mode. Use the '2 OP only' operators first */ - first = p = 6; - else - first = p = 0; - avail = devc->nr_voice; - } - - /* - * Now try to find a free voice - */ - best = first; - - for (i = 0; i < avail; i++) - { - if (alloc->map[p] == 0) + int i, p, best, first, avail, best_time = 0x7fffffff; + struct sbi_instrument *instr; + int is4op; + int instr_no; + + if (chn < 0 || chn > 15) + instr_no = 0; + else + instr_no = devc->chn_info[chn].pgm_num; + + instr = &devc->i_map[instr_no]; + if (instr->channel < 0 || /* Instrument not loaded */ + devc->nr_voice != 12) /* Not in 4 OP mode */ + is4op = 0; + else if (devc->nr_voice == 12) /* 4 OP mode */ + is4op = (instr->key == OPL3_PATCH); + else + is4op = 0; + + if (is4op) { - return p; + first = p = 0; + avail = 6; } - if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */ + else { - best_time = alloc->alloc_times[p]; - best = p; + if (devc->nr_voice == 12) /* 4 OP mode. Use the '2 OP only' operators first */ + first = p = 6; + else + first = p = 0; + avail = devc->nr_voice; } - p = (p + 1) % avail; - } - /* - * Insert some kind of priority mechanism here. - */ + /* + * Now try to find a free voice + */ + best = first; - if (best < 0) - best = 0; - if (best > devc->nr_voice) - best -= devc->nr_voice; + for (i = 0; i < avail; i++) + { + if (alloc->map[p] == 0) + { + return p; + } + if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */ + { + best_time = alloc->alloc_times[p]; + best = p; + } + p = (p + 1) % avail; + } + + /* + * Insert some kind of priority mechanism here. + */ - return best; /* All devc->voc in use. Select the first one. */ + if (best < 0) + best = 0; + if (best > devc->nr_voice) + best -= devc->nr_voice; + + return best; /* All devc->voc in use. Select the first one. */ } -static void -opl3_setup_voice (int dev, int voice, int chn) +static void opl3_setup_voice(int dev, int voice, int chn) { - struct channel_info *info = - &synth_devs[dev]->chn_info[chn]; + struct channel_info *info = + &synth_devs[dev]->chn_info[chn]; - opl3_set_instr (dev, voice, - info->pgm_num); + opl3_set_instr(dev, voice, info->pgm_num); - devc->voc[voice].bender = info->bender_value; - devc->voc[voice].volume = - info->controllers[CTL_MAIN_VOLUME]; + devc->voc[voice].bender = 0; + devc->voc[voice].bender_range = info->bender_range; + devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME]; + devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; } static struct synth_operations opl3_operations = { - NULL, - 0, - SYNTH_TYPE_FM, - FM_TYPE_ADLIB, - opl3_open, - opl3_close, - opl3_ioctl, - opl3_kill_note, - opl3_start_note, - opl3_set_instr, - opl3_reset, - opl3_hw_control, - opl3_load_patch, - opl3_aftertouch, - opl3_controller, - opl3_panning, - opl3_volume_method, - opl3_patchmgr, - opl3_bender, - opl3_alloc_voice, - opl3_setup_voice + "OPL", + NULL, + 0, + SYNTH_TYPE_FM, + FM_TYPE_ADLIB, + opl3_open, + opl3_close, + opl3_ioctl, + opl3_kill_note, + opl3_start_note, + opl3_set_instr, + opl3_reset, + opl3_hw_control, + opl3_load_patch, + opl3_aftertouch, + opl3_controller, + opl3_panning, + opl3_volume_method, + opl3_bender, + opl3_alloc_voice, + opl3_setup_voice }; -void -opl3_init (int ioaddr, int *osp) +int opl3_init(int ioaddr, int *osp) { - int i; - - if (num_synths >= MAX_SYNTH_DEV) - { - printk ("OPL3 Error: Too many synthesizers\n"); - return; - } - - if (devc == NULL) - { - printk ("OPL3: Device control structure not initialized.\n"); - return; - } - - memset ((char *) devc, 0x00, sizeof (*devc)); - devc->osp = osp; - devc->base = ioaddr; - - devc->nr_voice = 9; - strcpy (devc->fm_info.name, "OPL2"); - - devc->fm_info.device = 0; - devc->fm_info.synth_type = SYNTH_TYPE_FM; - devc->fm_info.synth_subtype = FM_TYPE_ADLIB; - devc->fm_info.perc_mode = 0; - devc->fm_info.nr_voices = 9; - devc->fm_info.nr_drums = 0; - devc->fm_info.instr_bank_size = SBFM_MAXINSTR; - devc->fm_info.capabilities = 0; - devc->left_io = ioaddr; - devc->right_io = ioaddr + 2; - - if (detected_model <= 2) - devc->model = 1; - else - { - devc->model = 2; - if (detected_model == 4) - devc->is_opl4 = 1; - } - - opl3_operations.info = &devc->fm_info; - - synth_devs[num_synths++] = &opl3_operations; - devc->v_alloc = &opl3_operations.alloc; - devc->chn_info = &opl3_operations.chn_info[0]; - - if (devc->model == 2) - { - if (devc->is_opl4) - conf_printf2 ("Yamaha OPL4/OPL3 FM", ioaddr, 0, -1, -1); - else - conf_printf2 ("Yamaha OPL3 FM", ioaddr, 0, -1, -1); - - devc->v_alloc->max_voice = devc->nr_voice = 18; - devc->fm_info.nr_drums = 0; - devc->fm_info.synth_subtype = FM_TYPE_OPL3; - devc->fm_info.capabilities |= SYNTH_CAP_OPL3; - strcpy (devc->fm_info.name, "Yamaha OPL-3"); - - for (i = 0; i < 18; i++) - if (pv_map[i].ioaddr == USE_LEFT) - pv_map[i].ioaddr = devc->left_io; + int i; + int me; + + if (devc == NULL) + { + printk(KERN_ERR "opl3_init: Device control structure not initialized.\n"); + return -1; + } + + if ((me = sound_alloc_synthdev()) == -1) + { + printk(KERN_WARNING "opl3: Too many synthesizers\n"); + return -1; + } + + memset((char *) devc, 0x00, sizeof(*devc)); + devc->osp = osp; + devc->base = ioaddr; + + devc->nr_voice = 9; + strcpy(devc->fm_info.name, "OPL2"); + + devc->fm_info.device = 0; + devc->fm_info.synth_type = SYNTH_TYPE_FM; + devc->fm_info.synth_subtype = FM_TYPE_ADLIB; + devc->fm_info.perc_mode = 0; + devc->fm_info.nr_voices = 9; + devc->fm_info.nr_drums = 0; + devc->fm_info.instr_bank_size = SBFM_MAXINSTR; + devc->fm_info.capabilities = 0; + devc->left_io = ioaddr; + devc->right_io = ioaddr + 2; + + if (detected_model <= 2) + devc->model = 1; else - pv_map[i].ioaddr = devc->right_io; + { + devc->model = 2; + if (detected_model == 4) + devc->is_opl4 = 1; + } + + opl3_operations.info = &devc->fm_info; - opl3_command (devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE); - opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, 0x00); - } - else - { - conf_printf2 ("Yamaha OPL2 FM", ioaddr, 0, -1, -1); - devc->v_alloc->max_voice = devc->nr_voice = 9; - devc->fm_info.nr_drums = 0; + synth_devs[me] = &opl3_operations; + sequencer_init(); + devc->v_alloc = &opl3_operations.alloc; + devc->chn_info = &opl3_operations.chn_info[0]; - for (i = 0; i < 18; i++) - pv_map[i].ioaddr = devc->left_io; - }; + if (devc->model == 2) + { + if (devc->is_opl4) + conf_printf2("Yamaha OPL4/OPL3 FM", ioaddr, 0, -1, -1); + else + conf_printf2("Yamaha OPL3 FM", ioaddr, 0, -1, -1); + + devc->v_alloc->max_voice = devc->nr_voice = 18; + devc->fm_info.nr_drums = 0; + devc->fm_info.synth_subtype = FM_TYPE_OPL3; + devc->fm_info.capabilities |= SYNTH_CAP_OPL3; + strcpy(devc->fm_info.name, "Yamaha OPL-3"); + + for (i = 0; i < 18; i++) + { + if (pv_map[i].ioaddr == USE_LEFT) + pv_map[i].ioaddr = devc->left_io; + else + pv_map[i].ioaddr = devc->right_io; + } + opl3_command(devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE); + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x00); + } + else + { + conf_printf2("Yamaha OPL2 FM", ioaddr, 0, -1, -1); + devc->v_alloc->max_voice = devc->nr_voice = 9; + devc->fm_info.nr_drums = 0; - for (i = 0; i < SBFM_MAXINSTR; i++) - devc->i_map[i].channel = -1; + for (i = 0; i < 18; i++) + pv_map[i].ioaddr = devc->left_io; + }; + for (i = 0; i < SBFM_MAXINSTR; i++) + devc->i_map[i].channel = -1; + + return me; +} + +struct symbol_table opl3_syms = +{ +#include + X(opl3_init), + X(opl3_detect), +#include +}; + +#ifdef MODULE + +/* + * We provide OPL3 functions. + */ + +int io = -1; +int me; + +int init_module (void) +{ + printk("YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996\n"); + if (io != -1) /* User loading pure OPL3 module */ + { + if (!opl3_detect(io, NULL)) + { + return -ENODEV; + } + me = opl3_init(io, NULL); + } + register_symtab(&opl3_syms); + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + if (devc) + { + kfree(devc); + devc = NULL; + sound_unload_synthdev(me); + } + SOUND_LOCK_END; } #endif + + + +MODULE_PARM(io, "i"); + +#endif diff --git a/drivers/sound/opl3.h b/drivers/sound/opl3.h index a3813964982b..5529d19d8a65 100644 --- a/drivers/sound/opl3.h +++ b/drivers/sound/opl3.h @@ -2,9 +2,9 @@ * opl3.h - Definitions of the OPL-3 registers */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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. */ @@ -69,10 +69,10 @@ #define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ #define KEYBOARD_SPLIT 0x40 -#define PERCUSSION_REGISTER 0xbd /* Left side only */ +#define PERCOSSION_REGISTER 0xbd /* Left side only */ #define TREMOLO_DEPTH 0x80 #define VIBRATO_DEPTH 0x40 -#define PERCUSSION_ENABLE 0x20 +#define PERCOSSION_ENABLE 0x20 #define BASSDRUM_ON 0x10 #define SNAREDRUM_ON 0x08 #define TOMTOM_ON 0x04 diff --git a/drivers/sound/opl3sa.c b/drivers/sound/opl3sa.c new file mode 100644 index 000000000000..8be3b91a92d5 --- /dev/null +++ b/drivers/sound/opl3sa.c @@ -0,0 +1,338 @@ +/* + * sound/opl3sa.c + * + * Low level driver for Yamaha YMF701B aka OPL3-SA 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 + * + * FIXME: + * Check for install of mpu etc is wrong, should check result of the mss stuff + */ + +#include +#include + +#undef SB_OK + +#include "sound_config.h" +#include "soundmodule.h" +#ifdef SB_OK +#include "sb.h" +static int sb_initialized = 0; + +#endif + +#ifdef CONFIG_OPL3SA1 + +static int kilroy_was_here = 0; /* Don't detect twice */ +static int mpu_initialized = 0; + +static int *opl3sa_osp = NULL; + +static unsigned char opl3sa_read(int addr) +{ + unsigned long flags; + unsigned char tmp; + + save_flags(flags); + cli(); + outb((0x1d), 0xf86); /* password */ + outb(((unsigned char) addr), 0xf86); /* address */ + tmp = inb(0xf87); /* data */ + restore_flags(flags); + + return tmp; +} + +static void opl3sa_write(int addr, int data) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outb((0x1d), 0xf86); /* password */ + outb(((unsigned char) addr), 0xf86); /* address */ + outb(((unsigned char) data), 0xf87); /* data */ + restore_flags(flags); +} + +static int opl3sa_detect(void) +{ + int tmp; + + if (((tmp = opl3sa_read(0x01)) & 0xc4) != 0x04) + { + DDB(printk("OPL3-SA detect error 1 (%x)\n", opl3sa_read(0x01))); + /* return 0; */ + } + + /* + * Check that the password feature has any effect + */ + + if (inb(0xf87) == tmp) + { + DDB(printk("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb(0xf87))); + return 0; + } + tmp = (opl3sa_read(0x04) & 0xe0) >> 5; + + if (tmp != 0 && tmp != 1) + { + DDB(printk("OPL3-SA detect failed 3 (%d)\n", tmp)); + return 0; + } + DDB(printk("OPL3-SA mode %x detected\n", tmp)); + + opl3sa_write(0x01, 0x00); /* Disable MSS */ + opl3sa_write(0x02, 0x00); /* Disable SB */ + opl3sa_write(0x03, 0x00); /* Disable MPU */ + + return 1; +} + +/* + * Probe and attach routines for the Windows Sound System mode of + * OPL3-SA + */ + +int probe_opl3sa_wss(struct address_info *hw_config) +{ + int ret; + unsigned char tmp = 0x24; /* WSS enable */ + + if (check_region(0xf86, 2)) /* Control port is busy */ + return 0; + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (OPL3-SA for example) + * return 0x00. + */ + + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "OPL3-SA: MSS I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + opl3sa_osp = hw_config->osp; + + if (!opl3sa_detect()) + { + printk(KERN_ERR "OSS: OPL3-SA chip not found\n"); + return 0; + } + + switch (hw_config->io_base) + { + case 0x530: + tmp |= 0x00; + break; + case 0xe80: + tmp |= 0x08; + break; + case 0xf40: + tmp |= 0x10; + break; + case 0x604: + tmp |= 0x18; + break; + default: + printk(KERN_ERR "OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base); + return 0; + } + + opl3sa_write(0x01, tmp); /* WSS setup register */ + kilroy_was_here = 1; + + ret = probe_ms_sound(hw_config); + if (ret) + request_region(0xf86, 2, "OPL3-SA"); + + return ret; +} + +void attach_opl3sa_wss(struct address_info *hw_config) +{ + int nm = num_mixers; + + /* FIXME */ + attach_ms_sound(hw_config); + if (num_mixers > nm) /* A mixer was installed */ + { + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } +} + + +void attach_opl3sa_mpu(struct address_info *hw_config) +{ +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + hw_config->name = "OPL3-SA (MPU401)"; + attach_uart401(hw_config); +#endif +} + +int probe_opl3sa_mpu(struct address_info *hw_config) +{ +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + unsigned char conf; + static char irq_bits[] = { + -1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4 + }; + + if (!kilroy_was_here) + return 0; /* OPL3-SA has not been detected earlier */ + + if (mpu_initialized) + { + DDB(printk("OPL3-SA: MPU mode already initialized\n")); + return 0; + } + if (check_region(hw_config->io_base, 4)) + { + printk(KERN_ERR "OPL3-SA: MPU I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + if (hw_config->irq > 10) + { + printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + if (irq_bits[hw_config->irq] == -1) + { + printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + switch (hw_config->io_base) + { + case 0x330: + conf = 0x00; + break; + case 0x332: + conf = 0x20; + break; + case 0x334: + conf = 0x40; + break; + case 0x300: + conf = 0x60; + break; + default: + return 0; /* Invalid port */ + } + + conf |= 0x83; /* MPU & OPL3 (synth) & game port enable */ + conf |= irq_bits[hw_config->irq] << 2; + + opl3sa_write(0x03, conf); + + mpu_initialized = 1; + + return probe_uart401(hw_config); +#else + return 0; +#endif +} + +void unload_opl3sa_wss(struct address_info *hw_config) +{ + int dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = hw_config->dma; + + release_region(0xf86, 2); + release_region(hw_config->io_base, 4); + + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + dma2, + 0); +} + +void unload_opl3sa_mpu(struct address_info *hw_config) +{ +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + unload_uart401(hw_config); +#endif +} + +#ifdef SB_OK +void unload_opl3sa_sb(struct address_info *hw_config) +{ +#ifdef CONFIG_SBDSP + sb_dsp_unload(hw_config); +#endif +} +#endif + +#ifdef MODULE +int io = -1; +int irq = -1; +int dma = -1; +int dma2 = -1; + +int mpu_io = -1; +int mpu_irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(mpu_io,"i"); +MODULE_PARM(mpu_irq,"i"); + +struct address_info cfg; +struct address_info mpu_cfg; +static int found_mpu; + +int init_module(void) +{ + if (io == -1 || irq == -1 || dma == -1) + { + printk(KERN_ERR "opl3sa: dma, irq and io must be set.\n"); + return -EINVAL; + } + cfg.io_base = io; + cfg.irq = irq; + cfg.dma = dma; + cfg.dma2 = dma2; + + mpu_cfg.io_base = mpu_io; + mpu_cfg.irq = mpu_irq; + + if (probe_opl3sa_wss(&cfg) == 0) + return -ENODEV; + + found_mpu=probe_opl3sa_mpu(&mpu_cfg); + + attach_opl3sa_wss(&cfg); + if(found_mpu) + attach_opl3sa_mpu(&mpu_cfg); + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + if(found_mpu) + unload_opl3sa_mpu(&mpu_cfg); + unload_opl3sa_wss(&cfg); + SOUND_LOCK_END; +} + +#endif + +#endif diff --git a/drivers/sound/os.h b/drivers/sound/os.h index 4f4ea3651f31..f91f6d3a93d6 100644 --- a/drivers/sound/os.h +++ b/drivers/sound/os.h @@ -1,51 +1,52 @@ - #define ALLOW_SELECT #undef NO_INLINE_ASM #define SHORT_BANNERS +#define MANUAL_PNP +#undef DO_TIMINGS #ifdef MODULE #define __NO_VERSION__ #include #include -#ifdef MODVERSIONS -#include #endif +#if LINUX_VERSION_CODE > 131328 +#define LINUX21X #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifdef __KERNEL__ +#include +#include +#include #include -#include +#include +#include +#include +#include #include -#include #include -#include +#include +#include +#ifdef __alpha__ +#include +#endif +#include +#endif #include - #include #define FALSE 0 #define TRUE 1 -struct snd_wait { - int flags; - }; - 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 RUNTIME_DMA_ALLOC +#define USE_AUTOINIT_DMA extern caddr_t sound_mem_blocks[1024]; extern int sound_nblocks; @@ -53,3 +54,5 @@ extern int sound_nblocks; #undef PSEUDO_DMA_AUTOINIT #define ALLOW_BUFFER_MAPPING +extern struct file_operations oss_sound_fops; +extern struct file_operations oss_sound_fops; diff --git a/drivers/sound/pas2_card.c b/drivers/sound/pas2_card.c index 1d324d872098..ab87cae403dd 100644 --- a/drivers/sound/pas2_card.c +++ b/drivers/sound/pas2_card.c @@ -1,4 +1,3 @@ -#define _PAS2_CARD_C_ /* * sound/pas2_card.c * @@ -6,368 +5,433 @@ */ #include - +#include #include "sound_config.h" +#include "soundmodule.h" + +#ifdef CONFIG_PAS + +static unsigned char dma_bits[] = { + 4, 1, 2, 3, 0, 5, 6, 7 +}; -#if defined(CONFIG_PAS) +static unsigned char irq_bits[] = { + 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 +}; -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}; +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 translate_code; +int translate_code = 0; static int pas_intr_mask = 0; static int pas_irq = 0; static int pas_sb_base = 0; +#ifndef CONFIG_PAS_JOYSTICK +static int joystick = 0; +#else +static int joystick = 1; +#endif +#ifdef SYMPHONY_PAS +static int symphony = 1; +#else +static int symphony = 0; +#endif +#ifdef BROKEN_BUS_CLOCK +static int broken_bus_clock = 1; +#else +static int broken_bus_clock = 0; +#endif -int *pas_osp; -char pas_model; -static char *pas_model_names[] = -{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"}; +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 */ -extern void mix_write (unsigned char data, int ioaddr); -unsigned char -pas_read (int ioaddr) +extern void mix_write(unsigned char data, int ioaddr); + +unsigned char pas_read(int ioaddr) { - return inb (ioaddr ^ translate_code); + return inb(ioaddr ^ translate_code); } -void -pas_write (unsigned char data, int ioaddr) +void pas_write(unsigned char data, int ioaddr) { - outb (data, ioaddr ^ translate_code); + outb((data), ioaddr ^ translate_code); } /******************* Begin of the Interrupt Handler ********************/ -void -pasintr (int irq, void *dev_id, struct pt_regs *dummy) +static void pasintr(int irq, void *dev_id, struct pt_regs *dummy) { - int status; + int status; - status = pas_read (0x0B89); - pas_write (status, 0x0B89); /* Clear interrupt */ + status = pas_read(0x0B89); + pas_write(status, 0x0B89); /* Clear interrupt */ - if (status & 0x08) - { + if (status & 0x08) + { #ifdef CONFIG_AUDIO - pas_pcm_interrupt (status, 1); + pas_pcm_interrupt(status, 1); #endif - status &= ~0x08; - } - if (status & 0x10) - { + status &= ~0x08; + } + if (status & 0x10) + { #ifdef CONFIG_MIDI - pas_midi_interrupt (); + pas_midi_interrupt(); #endif - status &= ~0x10; - } + status &= ~0x10; + } } -int -pas_set_intr (int mask) +int pas_set_intr(int mask) { - if (!mask) - return 0; + if (!mask) + return 0; - pas_intr_mask |= mask; + pas_intr_mask |= mask; - pas_write (pas_intr_mask, 0x0B8B); - return 0; + pas_write(pas_intr_mask, 0x0B8B); + return 0; } -int -pas_remove_intr (int mask) +int pas_remove_intr(int mask) { - if (!mask) - return 0; + if (!mask) + return 0; - pas_intr_mask &= ~mask; - pas_write (pas_intr_mask, 0x0B8B); + pas_intr_mask &= ~mask; + pas_write(pas_intr_mask, 0x0B8B); - return 0; + return 0; } /******************* End of the Interrupt handler **********************/ /******************* Begin of the Initialization Code ******************/ -int -config_pas_hw (struct address_info *hw_config) +extern struct address_info sbhw_config; + +static int 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); /* - * Local timer control * - * register - */ - - pas_write (0x36, 0x1388); /* - * Sample rate timer (16 bit) - */ - pas_write (0, 0x1388); - - pas_write (0x74, 0x138B); /* - * Local timer control * - * register - */ - - pas_write (0x74, 0x1389); /* - * Sample count register (16 - * * bit) - */ - 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 -#ifdef PAS_JOYSTICK_ENABLE - | 0x40 -#endif - ,0xF388); - - if (pas_irq < 0 || pas_irq > 15) - { - printk ("PAS2: Invalid IRQ %d", pas_irq); - ok = 0; - } - else - { - int_ptrs = pas_read (0xF38A); - int_ptrs |= irq_bits[pas_irq] & 0xf; - pas_write (int_ptrs, 0xF38A); - if (!irq_bits[pas_irq]) + 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 ("PAS2: Invalid IRQ %d", pas_irq); - ok = 0; + printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); + ok = 0; } - else + else { - if (snd_set_irq_handler (pas_irq, pasintr, "PAS16", hw_config->osp) < 0) - ok = 0; + int_ptrs = pas_read(0xF38A); + int_ptrs |= irq_bits[pas_irq] & 0xf; + pas_write(int_ptrs, 0xF38A); + if (!irq_bits[pas_irq]) + { + printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); + ok = 0; + } + else + { + if (request_irq(pas_irq, pasintr, 0, "PAS16",NULL) < 0) + ok = 0; + } } - } - - if (hw_config->dma < 0 || hw_config->dma > 7) - { - printk ("PAS2: Invalid DMA selection %d", hw_config->dma); - ok = 0; - } - else - { - pas_write (dma_bits[hw_config->dma], 0xF389); - if (!dma_bits[hw_config->dma]) + + if (hw_config->dma < 0 || hw_config->dma > 7) { - printk ("PAS2: Invalid DMA selection %d", hw_config->dma); - ok = 0; + printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); + ok = 0; } - else + else { - if (sound_alloc_dma (hw_config->dma, "PAS16")) - { - printk ("pas2_card.c: Can't allocate DMA channel\n"); - ok = 0; - } + 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); + ok = 0; + } + else + { + if (sound_alloc_dma(hw_config->dma, "PAS16")) + { + printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n"); + 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. - */ -#ifdef SYMPHONY_PAS - outb (0x05, 0xa8); - outb (0x60, 0xa9); -#endif - -#ifdef BROKEN_BUS_CLOCK - pas_write (0x01 | 0x10 | 0x20 | 0x04, 0x8388); -#else - /* - * pas_write(0x01, 0x8388); - */ - pas_write (0x01 | 0x10 | 0x20, 0x8388); -#endif - 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); - -#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB) - - { - struct address_info *sb_config; - - if ((sb_config = sound_getconf (SNDCARD_SB))) - { - unsigned char irq_dma; /* - * Turn on Sound Blaster compatibility - * bit 1 = SB emulation - * bit 0 = MPU401 emulation (CDPC only :-( ) + * 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. */ - pas_write (0x02, 0xF788); - /* - * "Emulation address" - */ - pas_write ((sb_config->io_base >> 4) & 0x0f, 0xF789); - pas_sb_base = sb_config->io_base; + if(symphony) + { + outb((0x05), 0xa8); + outb((0x60), 0xa9); + } - if (!sb_dma_bits[sb_config->dma]) - printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n", - sb_config->dma); + if(broken_bus_clock) + pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388); + else + /* + * pas_write(0x01, 0x8388); + */ + pas_write(0x01 | 0x10 | 0x20, 0x8388); - if (!sb_irq_bits[sb_config->irq]) - printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n", - sb_config->irq); + pas_write(0x18, 0x838A); /* ??? */ + pas_write(0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */ + pas_write(8, 0xBF8A); - irq_dma = sb_dma_bits[sb_config->dma] | - sb_irq_bits[sb_config->irq]; + mix_write(0x80 | 5, 0x078B); + mix_write(5, 0x078B); - pas_write (irq_dma, 0xFB8A); - } - else - pas_write (0x00, 0xF788); - } +#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB) + + { + struct address_info *sb_config; + +#ifndef MODULE + if ((sb_config = sound_getconf(SNDCARD_SB))) #else - pas_write (0x00, 0xF788); + sb_config = &sbhw_config; + if (sb_config->io_base) +#endif + { + 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); + } +#else + pas_write(0x00, 0xF788); #endif - if (!ok) - printk ("PAS16: Driver not enabled\n"); + if (!ok) + printk(KERN_WARNING "PAS16: Driver not enabled\n"); - return ok; + return ok; } -int -detect_pas_hw (struct address_info *hw_config) +static int detect_pas_hw(struct address_info *hw_config) { - unsigned char board_id, foo; + 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. - */ + /* + * 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 */ - translate_code = 0x388 ^ hw_config->io_base; - pas_write (1, 0xBF88); /* Select one wait states */ + outb((0xBC), 0x9A01); /* Activate first board */ + outb((hw_config->io_base >> 2), 0x9A01); /* Set base address */ + translate_code = 0x388 ^ hw_config->io_base; + pas_write(1, 0xBF88); /* Select one wait states */ - board_id = pas_read (0x0B8B); + board_id = pas_read(0x0B8B); - if (board_id == 0xff) - return 0; + if (board_id == 0xff) + return 0; - /* - * We probably have a PAS-series board, now check for a PAS2-series board - * by trying to change the board revision bits. PAS2-series hardware won't - * let you do this - the bits are read-only. - */ + /* + * 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; + foo = board_id ^ 0xe0; - pas_write (foo, 0x0B8B); - foo = inb (0x0B8B); - pas_write (board_id, 0x0B8B); + pas_write(foo, 0x0B8B); + foo = pas_read(0x0B8B); + pas_write(board_id, 0x0B8B); - if (board_id != foo) /* - * Not a PAS2 - */ - return 0; + if (board_id != foo) + return 0; - pas_model = pas_read (0xFF88); + pas_model = pas_read(0xFF88); - return pas_model; + return pas_model; } -void -attach_pas_card (struct address_info *hw_config) +void attach_pas_card(struct address_info *hw_config) { - pas_irq = hw_config->irq; - pas_osp = hw_config->osp; - - if (detect_pas_hw (hw_config)) - { + pas_irq = hw_config->irq; - if ((pas_model = pas_read (0xFF88))) - { - char temp[100]; - - 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)) + if (detect_pas_hw(hw_config)) { + if ((pas_model = pas_read(0xFF88))) + { + char temp[100]; + + 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)) + { #ifdef CONFIG_AUDIO - pas_pcm_init (hw_config); + pas_pcm_init(hw_config); #endif #if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB) - sb_dsp_disable_midi (pas_sb_base); /* No MIDI capability */ + sb_dsp_disable_midi(pas_sb_base); /* No MIDI capability */ #endif - #ifdef CONFIG_MIDI - pas_midi_init (); + pas_midi_init(); #endif - - pas_init_mixer (); + pas_init_mixer(); + } } - } +} +int probe_pas(struct address_info *hw_config) +{ + return detect_pas_hw(hw_config); } -int -probe_pas (struct address_info *hw_config) +void unload_pas(struct address_info *hw_config) { - pas_osp = hw_config->osp; - return detect_pas_hw (hw_config); + sound_free_dma(hw_config->dma); + free_irq(hw_config->irq, NULL); } -void -unload_pas (struct address_info *hw_config) +#ifdef MODULE + +int io = -1; +int irq = -1; +int dma = -1; +int dma16 = -1; /* Set this for modules that need it */ + +int sb_io = 0; +int sb_irq = -1; +int sb_dma = -1; +int sb_dma16 = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma16,"i"); + +MODULE_PARM(sb_io,"i"); +MODULE_PARM(sb_irq,"i"); +MODULE_PARM(sb_dma,"i"); +MODULE_PARM(sb_dma16,"i"); + +MODULE_PARM(joystick,"i"); +MODULE_PARM(symphony,"i"); +MODULE_PARM(broken_bus_clock,"i"); + +struct address_info config; +struct address_info sbhw_config; + +int init_module(void) { - sound_free_dma (hw_config->dma); - snd_release_irq (hw_config->irq); + printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + if (io == -1 || dma == -1 || irq == -1) + { + printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); + return -EINVAL; + } + config.io_base = io; + config.irq = irq; + config.dma = dma; + config.dma2 = dma16; + + sbhw_config.io_base = sb_io; + sbhw_config.irq = sb_irq; + sbhw_config.dma = sb_dma; + sbhw_config.dma2 = sb_dma16; + + if (!probe_pas(&config)) + return -ENODEV; + attach_pas_card(&config); + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + unload_pas(&config); + SOUND_LOCK_END; } + +#endif #endif diff --git a/drivers/sound/pas2_midi.c b/drivers/sound/pas2_midi.c index b1d4315921f0..29203071bcbd 100644 --- a/drivers/sound/pas2_midi.c +++ b/drivers/sound/pas2_midi.c @@ -3,16 +3,23 @@ * * 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. + */ #include + #include "sound_config.h" -#if defined(CONFIG_PAS) && defined(CONFIG_MIDI) +#ifdef CONFIG_PAS +#ifdef CONFIG_MIDI static int midi_busy = 0, input_opened = 0; static int my_dev; -static volatile int ofifo_bytes = 0; static unsigned char tmp_queue[256]; static volatile int qlen; @@ -21,192 +28,173 @@ 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) +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) - { - printk ("PAS2: Midi busy\n"); - return -(EBUSY); - } - - /* - * Reset input and output FIFO pointers - */ - pas_write (0x20 | 0x40, - 0x178b); - - save_flags (flags); - cli (); - - if ((err = pas_set_intr (0x10)) < 0) - 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 | /* - * Enable output - */ - 0x10; - } - - pas_write (ctrl, - 0x178b); - - /* - * Acknowledge any pending interrupts - */ - - pas_write (0xff, 0x1B88); - ofifo_bytes = 0; - - restore_flags (flags); - - midi_busy = 1; - qlen = qhead = qtail = 0; - return 0; + int err; + unsigned long flags; + unsigned char ctrl; + + + if (midi_busy) + { + printk("PAS16: Midi busy\n"); + return -EBUSY; + } + /* + * Reset input and output FIFO pointers + */ + pas_write(0x20 | 0x40, + 0x178b); + + save_flags(flags); + cli(); + + if ((err = pas_set_intr(0x10)) < 0) + { + restore_flags(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); + + restore_flags(flags); + + midi_busy = 1; + qlen = qhead = qtail = 0; + return 0; } static void -pas_midi_close (int dev) +pas_midi_close(int dev) { - /* - * Reset FIFO pointers, disable intrs - */ - pas_write (0x20 | 0x40, 0x178b); + /* + * Reset FIFO pointers, disable intrs + */ + pas_write(0x20 | 0x40, 0x178b); - pas_remove_intr (0x10); - midi_busy = 0; + pas_remove_intr(0x10); + midi_busy = 0; } static int -dump_to_midi (unsigned char midi_byte) +dump_to_midi(unsigned char midi_byte) { - int fifo_space, x; + int fifo_space, x; - fifo_space = ((x = pas_read (0x1B89)) >> 4) & 0x0f; + fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f; - if (fifo_space == 15 || (fifo_space < 2 && ofifo_bytes > 13)) /* - * Fifo - * full - */ - { - return 0; /* - * Upper layer will call again - */ - } - - ofifo_bytes++; +/* + * 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. + */ - pas_write (midi_byte, 0x178A); + 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; + return 1; } static int -pas_midi_out (int dev, unsigned char midi_byte) +pas_midi_out(int dev, unsigned char midi_byte) { - unsigned long flags; - - /* - * Drain the local queue first - */ + unsigned long flags; - save_flags (flags); - cli (); + /* + * Drain the local queue first + */ - while (qlen && dump_to_midi (tmp_queue[qhead])) - { - qlen--; - qhead++; - } + save_flags(flags); + cli(); - restore_flags (flags); + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } - /* - * Output the byte if the local queue is empty. - */ + restore_flags(flags); - if (!qlen) - if (dump_to_midi (midi_byte)) - return 1; /* - * OK - */ + /* + * Output the byte if the local queue is empty. + */ - /* - * Put to the local queue - */ + if (!qlen) + if (dump_to_midi(midi_byte)) + return 1; - if (qlen >= 256) - return 0; /* - * Local queue full - */ + /* + * Put to the local queue + */ - save_flags (flags); - cli (); + if (qlen >= 256) + return 0; /* Local queue full */ - tmp_queue[qtail] = midi_byte; - qlen++; - qtail++; + save_flags(flags); + cli(); - restore_flags (flags); + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; - return 1; -} + restore_flags(flags); -static int -pas_midi_start_read (int dev) -{ - return 0; + return 1; } static int -pas_midi_end_read (int dev) +pas_midi_start_read(int dev) { - return 0; + return 0; } static int -pas_midi_ioctl (int dev, unsigned cmd, caddr_t arg) +pas_midi_end_read(int dev) { - return -(EINVAL); + return 0; } static void -pas_midi_kick (int dev) +pas_midi_kick(int dev) { - ofifo_bytes = 0; } static int -pas_buffer_status (int dev) +pas_buffer_status(int dev) { - return qlen; + return qlen; } #define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi" @@ -215,99 +203,77 @@ pas_buffer_status (int dev) static struct midi_operations pas_midi_operations = { - {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS}, - &std_midi_synth, - {0}, - pas_midi_open, - pas_midi_close, - pas_midi_ioctl, - pas_midi_out, - pas_midi_start_read, - pas_midi_end_read, - pas_midi_kick, - NULL, /* - * command - */ - pas_buffer_status, - NULL + {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS}, + &std_midi_synth, + {0}, + pas_midi_open, + pas_midi_close, + NULL, + pas_midi_out, + pas_midi_start_read, + pas_midi_end_read, + pas_midi_kick, + NULL, + pas_buffer_status, + NULL }; void -pas_midi_init (void) +pas_midi_init(void) { - if (num_midis >= MAX_MIDI_DEV) - { - printk ("Sound: Too many midi devices detected\n"); - return; - } - - std_midi_synth.midi_dev = my_dev = num_midis; - midi_devs[num_midis++] = &pas_midi_operations; + 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; + sequencer_init(); } void -pas_midi_interrupt (void) +pas_midi_interrupt(void) { - unsigned char stat; - int i, incount; - unsigned long flags; - - stat = pas_read (0x1B88); - - if (stat & 0x04) /* - * Input byte available - */ - { - incount = pas_read (0x1B89) & 0x0f; /* - * Input FIFO count - */ - if (!incount) - incount = 16; - - for (i = 0; i < incount; i++) - if (input_opened) + unsigned char stat; + int i, incount; + unsigned long flags; + + stat = pas_read(0x1B88); + + if (stat & 0x04) /* Input data available */ { - midi_input_intr (my_dev, pas_read (0x178A)); + 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 */ } - else - pas_read (0x178A); /* - * Flush - */ - } - - if (stat & (0x08 | 0x10)) - { - if (!(stat & 0x08)) - { - ofifo_bytes = 8; - } - else - { - ofifo_bytes = 0; - } - - save_flags (flags); - cli (); - - while (qlen && dump_to_midi (tmp_queue[qhead])) - { - qlen--; - qhead++; - } - - restore_flags (flags); - } - - - if (stat & 0x40) - { - printk ("MIDI output overrun %x,%x,%d \n", pas_read (0x1B89), stat, ofifo_bytes); - ofifo_bytes = 100; - } - - pas_write (stat, 0x1B88); /* - * Acknowledge interrupts - */ + if (stat & (0x08 | 0x10)) + { + save_flags(flags); + cli(); + + while (qlen && dump_to_midi(tmp_queue[qhead])) + { + qlen--; + qhead++; + } + + restore_flags(flags); + } + if (stat & 0x40) + { + printk("MIDI output overrun %x,%x\n", pas_read(0x1B89), stat); + } + pas_write(stat, 0x1B88); /* Acknowledge interrupts */ } #endif +#endif diff --git a/drivers/sound/pas2_mixer.c b/drivers/sound/pas2_mixer.c index bcc9326ea9bb..04a98feadb7d 100644 --- a/drivers/sound/pas2_mixer.c +++ b/drivers/sound/pas2_mixer.c @@ -1,16 +1,25 @@ -#define _PAS2_MIXER_C_ /* * sound/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) + */ #include #include "sound_config.h" -#if defined(CONFIG_PAS) +#ifdef CONFIG_PAS #ifndef DEB #define DEB(what) /* (what) */ @@ -31,324 +40,297 @@ static int mode_control = 0; 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 unsigned short levels[SOUND_MIXER_NRDEVICES] = +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 */ + 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) +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 ^ translate_code) - 1); - outb (0x80, 0); - } - else - pas_write (data, 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 ^ 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 */ +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) - { /* - * Select input or output mixer - */ - left |= mixer; - right |= mixer; - } - - if (bits == 0x03 || bits == 0x04) - { /* - * Bass and treble are mono devices - */ - 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)); + 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)); } -void -set_mode (int new_mode) +static void +set_mode(int new_mode) { - mix_write (0x80 | 0x05, 0x078B); - mix_write (new_mode, 0x078B); + mix_write(0x80 | 0x05, 0x078B); + mix_write(new_mode, 0x078B); - mode_control = new_mode; + mode_control = new_mode; } static int -pas_mixer_set (int whichDev, unsigned int level) +pas_mixer_set(int whichDev, unsigned int level) { - int left, right, devmask, changed, i, mixer = 0; - - DEB (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level)); - - left = level & 0x7f; - right = (level & 0x7f00) >> 8; - - if (whichDev < SOUND_MIXER_NRDEVICES) - if ((1 << whichDev) & rec_devices) - mixer = 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)) + int left, right, devmask, changed, i, mixer = 0; + + DEB(printk("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level)); + + left = level & 0x7f; + right = (level & 0x7f00) >> 8; + + if (whichDev < SOUND_MIXER_NRDEVICES) { + if ((1 << whichDev) & rec_devices) + mixer = 0x20; + else + mixer = 0x00; + } + + switch (whichDev) { - pas_mixer_set (i, levels[i]); + 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 rec_devices; - break; - default: - return -(EINVAL); - } - - return (levels[whichDev]); + return (levels[whichDev]); } /*****/ static void -pas_mixer_reset (void) +pas_mixer_reset(void) { - int foo; + int foo; - DEB (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n")); + DEB(printk("pas2_mixer.c: void pas_mixer_reset(void)\n")); - for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) - pas_mixer_set (foo, levels[foo]); + for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) + pas_mixer_set(foo, levels[foo]); - set_mode (0x04 | 0x01); + set_mode(0x04 | 0x01); } -int -pas_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +static int pas_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) { - DEB (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); - - if (cmd == SOUND_MIXER_PRIVATE1) /* Set loudness bit */ - { - int level = get_user ((int *) arg); - - if (level == -1) /* Return current settings */ - { - if (mode_control & 0x04) - return snd_ioctl_return ((int *) arg, 1); - else - return snd_ioctl_return ((int *) arg, 0); - } - else - { - mode_control &= ~0x04; - if (level) - mode_control |= 0x04; - set_mode (mode_control); - return snd_ioctl_return ((int *) arg, !!level); /* 0 or 1 */ + int level,v ; + + DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); + if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */ + if (get_user(level, (int *)arg)) + 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, (int *)arg); } - } - - - if (cmd == SOUND_MIXER_PRIVATE2) /* Set enhance bit */ - { - int level = get_user ((int *) arg); - - if (level == -1) /* Return current settings */ - { - if (!(mode_control & 0x03)) - return snd_ioctl_return ((int *) arg, 0); - return snd_ioctl_return ((int *) arg, ((mode_control & 0x03) + 1) * 20); + if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */ + if (get_user(level, (int *)arg)) + 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, (int *)arg); } - 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; - - return i; + if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */ + if (get_user(level, (int *)arg)) + 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, (int *)arg); } - } - - if (cmd == SOUND_MIXER_PRIVATE3) /* Set mute bit */ - { - int level = get_user ((int *) arg); - - if (level == -1) /* Return current settings */ - { - return snd_ioctl_return ((int *) arg, - !(pas_read (0x0B8A) & - 0x20)); - } - else - { - if (level) - pas_write (pas_read (0x0B8A) & (~0x20), - 0x0B8A); - else - pas_write (pas_read (0x0B8A) | 0x20, - 0x0B8A); - - return !(pas_read (0x0B8A) & 0x20); + if (((cmd >> 8) & 0xff) == 'M') { + if (get_user(v, (int *)arg)) + 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, (int *)arg); } - } - - if (((cmd >> 8) & 0xff) == 'M') - { - if (_IOC_DIR (cmd) & _IOC_WRITE) - return snd_ioctl_return ((int *) arg, pas_mixer_set (cmd & 0xff, get_user ((int *) arg))); - else - { /* - * Read parameters - */ - - switch (cmd & 0xff) - { - - case SOUND_MIXER_RECSRC: - return snd_ioctl_return ((int *) arg, rec_devices); - break; - - case SOUND_MIXER_STEREODEVS: - return snd_ioctl_return ((int *) arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE)); - break; - - case SOUND_MIXER_DEVMASK: - return snd_ioctl_return ((int *) arg, SUPPORTED_MIXER_DEVICES); - break; - - case SOUND_MIXER_RECMASK: - return snd_ioctl_return ((int *) arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES); - break; - - case SOUND_MIXER_CAPS: - return snd_ioctl_return ((int *) arg, 0); /* No special capabilities */ - break; - - - default: - return snd_ioctl_return ((int *) arg, levels[cmd & 0xff]); - } - } - } - return -(EINVAL); + return -EINVAL; } static struct mixer_operations pas_mixer_operations = { - "PAS16", - "Pro Audio Spectrum 16", - pas_mixer_ioctl + "PAS16", + "Pro Audio Spectrum 16", + pas_mixer_ioctl }; int -pas_init_mixer (void) +pas_init_mixer(void) { - pas_mixer_reset (); - - if (num_mixers < MAX_MIXER_DEV) - { - audio_devs[pas_audiodev]->mixer_dev = num_mixers; - mixer_devs[num_mixers++] = &pas_mixer_operations; - } - return 1; + 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; } #endif diff --git a/drivers/sound/pas2_pcm.c b/drivers/sound/pas2_pcm.c index a107d2c3d8a5..068252002889 100644 --- a/drivers/sound/pas2_pcm.c +++ b/drivers/sound/pas2_pcm.c @@ -1,15 +1,25 @@ -#define _PAS2_PCM_C_ /* - * sound/pas2_pcm.c + * pas2_pcm.c Audio routines for PAS16 * - * The low level driver for the Pro Audio Spectrum ADC/DAC. + * + * 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. */ - + #include #include "sound_config.h" -#if defined(CONFIG_PAS) && defined(CONFIG_AUDIO) +#ifdef CONFIG_PAS +#ifdef CONFIG_AUDIO #ifndef DEB #define DEB(WHAT) @@ -35,453 +45,401 @@ static int pcm_busy = 0; int pas_audiodev = 0; static int open_mode = 0; -int -pcm_set_speed (int arg) +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 = (596590 + (arg / 2)) / arg; - arg = 596590 / foo; - } - else - { - foo = (1193180 + (arg / 2)) / arg; - arg = 1193180 / 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 - - save_flags (flags); - cli (); + int foo, tmp; + unsigned long 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); + if (arg == 0) + return pcm_speed; - restore_flags (flags); + if (arg > 44100) + arg = 44100; + if (arg < 5000) + arg = 5000; - return pcm_speed; -} + if (pcm_channels & 2) + { + foo = (596590 + (arg / 2)) / arg; + arg = (596590 + (foo / 2)) / foo; + } + else + { + foo = (1193180 + (arg / 2)) / arg; + arg = (1193180 + (foo / 2)) / foo; + } -int -pcm_set_channels (int arg) -{ + 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 - if ((arg != 1) && (arg != 2)) - return pcm_channels; + save_flags(flags); + cli(); - if (arg != pcm_channels) - { - pas_write (pas_read (0xF8A) ^ 0x20, 0xF8A); + 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); - pcm_channels = arg; - pcm_set_speed (pcm_speed); /* - * The speed must be reinitialized - */ - } + restore_flags(flags); - return pcm_channels; + return pcm_speed; } -int -pcm_set_bits (int arg) +static int pcm_set_channels(int arg) { - if (arg == 0) - return pcm_bits; - if ((arg & pcm_bitsok) != arg) - return pcm_bits; + if ((arg != 1) && (arg != 2)) + return pcm_channels; - if (arg != pcm_bits) - { - pas_write (pas_read (0x8389) ^ 0x04, 0x8389); - - pcm_bits = arg; - } + if (arg != pcm_channels) + { + pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A); - return pcm_bits; + pcm_channels = arg; + pcm_set_speed(pcm_speed); /* The speed must be reinitialized */ + } + return pcm_channels; } -static int -pas_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local) +static int pcm_set_bits(int arg) { - DEB (printk ("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); - - switch (cmd) - { - case SOUND_PCM_WRITE_RATE: - if (local) - return pcm_set_speed ((int) arg); - return snd_ioctl_return ((int *) arg, pcm_set_speed (get_user ((int *) arg))); - break; - - case SOUND_PCM_READ_RATE: - if (local) - return pcm_speed; - return snd_ioctl_return ((int *) arg, pcm_speed); - break; - - case SNDCTL_DSP_STEREO: - if (local) - return pcm_set_channels ((int) arg + 1) - 1; - return snd_ioctl_return ((int *) arg, pcm_set_channels (get_user ((int *) arg) + 1) - 1); - break; - - case SOUND_PCM_WRITE_CHANNELS: - if (local) - return pcm_set_channels ((int) arg); - return snd_ioctl_return ((int *) arg, pcm_set_channels (get_user ((int *) arg))); - break; - - case SOUND_PCM_READ_CHANNELS: - if (local) - return pcm_channels; - return snd_ioctl_return ((int *) arg, pcm_channels); - break; + if (arg == 0) + return pcm_bits; - case SNDCTL_DSP_SETFMT: - if (local) - return pcm_set_bits ((int) arg); - return snd_ioctl_return ((int *) arg, pcm_set_bits (get_user ((int *) arg))); - break; + if ((arg & pcm_bitsok) != arg) + return pcm_bits; - case SOUND_PCM_READ_BITS: - if (local) - return pcm_bits; - return snd_ioctl_return ((int *) arg, pcm_bits); + if (arg != pcm_bits) + { + pas_write(pas_read(0x8389) ^ 0x04, 0x8389); - case SOUND_PCM_WRITE_FILTER: /* - * NOT YET IMPLEMENTED - */ - if (get_user ((int *) arg) > 1) - return -(EINVAL); - pcm_filter = get_user ((int *) arg); - break; + pcm_bits = arg; + } + return pcm_bits; +} - case SOUND_PCM_READ_FILTER: - return snd_ioctl_return ((int *) arg, pcm_filter); - break; +static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int val, ret; - default: - return -(EINVAL); - } + DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); - return -(EINVAL); + switch (cmd) + { + case SOUND_PCM_WRITE_RATE: + if (get_user(val, (int *)arg)) + 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, (int *)arg)) + return -EFAULT; + ret = pcm_set_channels(val + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (get_user(val, (int *)arg)) + 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, (int *)arg)) + return -EFAULT; + ret = pcm_set_bits(val); + break; + + case SOUND_PCM_READ_BITS: + ret = pcm_bits; + break; + + default: + return -EINVAL; + } + return put_user(ret, (int *)arg); } -static void -pas_audio_reset (int dev) +static void pas_audio_reset(int dev) { - DEB (printk ("pas2_pcm.c: static void pas_audio_reset(void)\n")); + DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n")); - pas_write (pas_read (0xF8A) & ~0x40, 0xF8A); + pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); /* Disable PCM */ } -static int -pas_audio_open (int dev, int mode) +static int pas_audio_open(int dev, int mode) { - int err; - unsigned long flags; - - DEB (printk ("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode)); + int err; + unsigned long flags; - save_flags (flags); - cli (); - if (pcm_busy) - { - restore_flags (flags); - return -(EBUSY); - } + DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode)); - pcm_busy = 1; - restore_flags (flags); + save_flags(flags); + cli(); + if (pcm_busy) + { + restore_flags(flags); + return -EBUSY; + } + pcm_busy = 1; + restore_flags(flags); - if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0) - return err; + if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0) + return err; - pcm_count = 0; - open_mode = mode; + pcm_count = 0; + open_mode = mode; - return 0; + return 0; } -static void -pas_audio_close (int dev) +static void pas_audio_close(int dev) { - unsigned long flags; + unsigned long flags; - DEB (printk ("pas2_pcm.c: static void pas_audio_close(void)\n")); + DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n")); - save_flags (flags); - cli (); + save_flags(flags); + cli(); - pas_audio_reset (dev); - pas_remove_intr (PAS_PCM_INTRBITS); - pcm_mode = PCM_NON; + pas_audio_reset(dev); + pas_remove_intr(PAS_PCM_INTRBITS); + pcm_mode = PCM_NON; - pcm_busy = 0; - restore_flags (flags); + pcm_busy = 0; + restore_flags(flags); } -static void -pas_audio_output_block (int dev, unsigned long buf, int count, - int intrflag, int restart_dma) +static void pas_audio_output_block(int dev, unsigned long buf, int count, + int intrflag) { - unsigned long flags, cnt; + unsigned long flags, cnt; - DEB (printk ("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count)); + DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count)); - cnt = count; - if (audio_devs[dev]->dmachan1 > 3) - cnt >>= 1; + cnt = count; + if (audio_devs[dev]->dmap_out->dma > 3) + cnt >>= 1; - if (audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - cnt == pcm_count) - return; /* - * Auto mode on. No need to react - */ + if (audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == pcm_count) + return; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - pas_write (pas_read (0xF8A) & ~0x40, - 0xF8A); + pas_write(pas_read(0xF8A) & ~0x40, + 0xF8A); - if (restart_dma) - DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - if (audio_devs[dev]->dmachan1 > 3) - count >>= 1; + 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); + 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); + pcm_count = count; + } + pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); #ifdef NO_TRIGGER - pas_write (pas_read (0xF8A) | 0x40 | 0x10, 0xF8A); + pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); #endif - pcm_mode = PCM_DAC; + pcm_mode = PCM_DAC; - restore_flags (flags); + restore_flags(flags); } -static void -pas_audio_start_input (int dev, unsigned long buf, int count, - int intrflag, int restart_dma) +static void pas_audio_start_input(int dev, unsigned long buf, int count, + int intrflag) { - unsigned long flags; - int cnt; - - DEB (printk ("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count)); - - cnt = count; - if (audio_devs[dev]->dmachan1 > 3) - cnt >>= 1; - - if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE && - intrflag && - cnt == pcm_count) - return; /* - * Auto mode on. No need to react - */ - - save_flags (flags); - cli (); - - if (restart_dma) - DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); - - if (audio_devs[dev]->dmachan1 > 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); + unsigned long flags; + int cnt; + + DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count)); + + 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; + + save_flags(flags); + cli(); + + /* 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); + pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); #endif - pcm_mode = PCM_ADC; + pcm_mode = PCM_ADC; - restore_flags (flags); + restore_flags(flags); } #ifndef NO_TRIGGER -static void -pas_audio_trigger (int dev, int state) +static void pas_audio_trigger(int dev, int state) { - unsigned long flags; + unsigned long flags; - save_flags (flags); - cli (); - state &= open_mode; + save_flags(flags); + cli(); + 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); + 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); - restore_flags (flags); + restore_flags(flags); } #endif -static int -pas_audio_prepare_for_input (int dev, int bsize, int bcount) +static int pas_audio_prepare_for_input(int dev, int bsize, int bcount) { - return 0; + pas_audio_reset(dev); + return 0; } -static int -pas_audio_prepare_for_output (int dev, int bsize, int bcount) +static int pas_audio_prepare_for_output(int dev, int bsize, int bcount) { - return 0; + pas_audio_reset(dev); + return 0; } static struct audio_driver pas_audio_driver = { - pas_audio_open, - pas_audio_close, - pas_audio_output_block, - pas_audio_start_input, - pas_audio_ioctl, - pas_audio_prepare_for_input, - pas_audio_prepare_for_output, - pas_audio_reset, - pas_audio_reset, - NULL, - NULL, - NULL, - NULL, - pas_audio_trigger + pas_audio_open, + pas_audio_close, + pas_audio_output_block, + pas_audio_start_input, + pas_audio_ioctl, + pas_audio_prepare_for_input, + pas_audio_prepare_for_output, + pas_audio_reset, + NULL, + NULL, + NULL, + NULL, + pas_audio_trigger }; -static struct audio_operations pas_audio_operations = +void pas_pcm_init(struct address_info *hw_config) { - "Pro Audio Spectrum", - DMA_AUTOMODE, - AFMT_U8 | AFMT_S16_LE, - NULL, - &pas_audio_driver -}; - -void -pas_pcm_init (struct address_info *hw_config) -{ - DEB (printk ("pas2_pcm.c: long pas_pcm_init()\n")); - - pcm_bitsok = 8; - if (pas_read (0xEF8B) & 0x08) - pcm_bitsok |= 16; - - pcm_set_speed (DSP_DEFAULT_SPEED); - - if (num_audiodevs < MAX_AUDIO_DEV) - { - audio_devs[pas_audiodev = num_audiodevs++] = &pas_audio_operations; - audio_devs[pas_audiodev]->dmachan1 = hw_config->dma; - audio_devs[pas_audiodev]->buffsize = DSP_BUFFSIZE; - } - else - printk ("PAS2: Too many PCM devices available\n"); + DEB(printk("pas2_pcm.c: long pas_pcm_init()\n")); + + 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) +void pas_pcm_interrupt(unsigned char status, int cause) { - if (cause == 1) /* - * PCM buffer done - */ - { - /* - * Halt the PCM first. Otherwise we don't have time to start a new - * block before the PCM chip proceeds to the next sample - */ - - if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE)) - { - pas_write (pas_read (0xF8A) & ~0x40, - 0xF8A); - } - - switch (pcm_mode) + if (cause == 1) { - - case PCM_DAC: - DMAbuf_outputintr (pas_audiodev, 1); - break; - - case PCM_ADC: - DMAbuf_inputintr (pas_audiodev); - break; - - default: - printk ("PAS: Unexpected PCM interrupt\n"); + /* + * 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"); + } } - } } #endif +#endif diff --git a/drivers/sound/patmgr.c b/drivers/sound/patmgr.c deleted file mode 100644 index 8f57d6236af3..000000000000 --- a/drivers/sound/patmgr.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * sound/patmgr.c - * - * The patch manager interface for the /dev/sequencer - */ -/* - * 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. - */ -#include - - -#define PATMGR_C -#include "sound_config.h" - -#if defined(CONFIG_SEQUENCER) - -static wait_handle *server_procs[MAX_SYNTH_DEV] = -{NULL}; -static volatile struct snd_wait server_wait_flag[MAX_SYNTH_DEV] = -{ - {0}}; - -static struct patmgr_info *mbox[MAX_SYNTH_DEV] = -{NULL}; -static volatile int msg_direction[MAX_SYNTH_DEV] = -{0}; - -static int pmgr_opened[MAX_SYNTH_DEV] = -{0}; - -#define A_TO_S 1 -#define S_TO_A 2 - -static wait_handle *appl_proc = NULL; -static volatile struct snd_wait appl_wait_flag = -{0}; - -int -pmgr_open (int dev) -{ - if (dev < 0 || dev >= num_synths) - return -(ENXIO); - - if (pmgr_opened[dev]) - return -(EBUSY); - pmgr_opened[dev] = 1; - - server_wait_flag[dev].flags = WK_NONE; - - return 0; -} - -void -pmgr_release (int dev) -{ - - if (mbox[dev]) /* - * Killed in action. Inform the client - */ - { - - mbox[dev]->key = PM_ERROR; - mbox[dev]->parm1 = -(EIO); - - if ((appl_wait_flag.flags & WK_SLEEP)) - { - appl_wait_flag.flags = WK_WAKEUP; - module_wake_up (&appl_proc); - }; - } - - pmgr_opened[dev] = 0; -} - -int -pmgr_read (int dev, struct fileinfo *file, char *buf, int count) -{ - unsigned long flags; - int ok = 0; - - if (count != sizeof (struct patmgr_info)) - { - printk ("PATMGR%d: Invalid read count\n", dev); - return -(EIO); - } - - while (!ok && !current_got_fatal_signal ()) - { - save_flags (flags); - cli (); - - while (!(mbox[dev] && msg_direction[dev] == A_TO_S) && - !current_got_fatal_signal ()) - { - - server_wait_flag[dev].flags = WK_SLEEP; - module_interruptible_sleep_on (&server_procs[dev]); - server_wait_flag[dev].flags &= ~WK_SLEEP;; - } - - if (mbox[dev] && msg_direction[dev] == A_TO_S) - { - memcpy_tofs (&(buf)[0], (char *) mbox[dev], count); - msg_direction[dev] = 0; - ok = 1; - } - - restore_flags (flags); - - } - - if (!ok) - return -(EINTR); - return count; -} - -int -pmgr_write (int dev, struct fileinfo *file, const char *buf, int count) -{ - unsigned long flags; - - if (count < 4) - { - printk ("PATMGR%d: Write count < 4\n", dev); - return -(EIO); - } - - memcpy_fromfs ((char *) mbox[dev], &(buf)[0], 4); - - if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE) - { - int tmp_dev; - - tmp_dev = ((unsigned short *) mbox[dev])[2]; - if (tmp_dev != dev) - return -(ENXIO); - - return synth_devs[dev]->load_patch (dev, *(unsigned short *) mbox[dev], - buf, 4, count, 1); - } - - if (count != sizeof (struct patmgr_info)) - { - printk ("PATMGR%d: Invalid write count\n", dev); - return -(EIO); - } - - /* - * If everything went OK, there should be a preallocated buffer in the - * mailbox and a client waiting. - */ - - save_flags (flags); - cli (); - - if (mbox[dev] && !msg_direction[dev]) - { - memcpy_fromfs (&((char *) mbox[dev])[4], &(buf)[4], count - 4); - msg_direction[dev] = S_TO_A; - - if ((appl_wait_flag.flags & WK_SLEEP)) - { - { - appl_wait_flag.flags = WK_WAKEUP; - module_wake_up (&appl_proc); - }; - } - } - - restore_flags (flags); - - return count; -} - -int -pmgr_access (int dev, struct patmgr_info *rec) -{ - unsigned long flags; - int err = 0; - - save_flags (flags); - cli (); - - if (mbox[dev]) - printk (" PATMGR: Server %d mbox full. Why?\n", dev); - else - { - rec->key = PM_K_COMMAND; - mbox[dev] = rec; - msg_direction[dev] = A_TO_S; - - if ((server_wait_flag[dev].flags & WK_SLEEP)) - { - { - server_wait_flag[dev].flags = WK_WAKEUP; - module_wake_up (&server_procs[dev]); - }; - } - - - appl_wait_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&appl_proc); - appl_wait_flag.flags &= ~WK_SLEEP;; - - if (msg_direction[dev] != S_TO_A) - { - rec->key = PM_ERROR; - rec->parm1 = -(EIO); - } - else if (rec->key == PM_ERROR) - { - err = rec->parm1; - if (err > 0) - err = -err; - } - - mbox[dev] = NULL; - msg_direction[dev] = 0; - } - - restore_flags (flags); - - return err; -} - -int -pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2, - unsigned long p3, unsigned long p4) -{ - unsigned long flags; - int err = 0; - - struct patmgr_info *tmp_mbox; - - if (!pmgr_opened[dev]) - return 0; - - tmp_mbox = (struct patmgr_info *) vmalloc (sizeof (struct patmgr_info)); - - if (tmp_mbox == NULL) - { - printk ("pmgr: Couldn't allocate memory for a message\n"); - return 0; - } - - save_flags (flags); - cli (); - - if (mbox[dev]) - printk (" PATMGR: Server %d mbox full. Why?\n", dev); - else - { - - mbox[dev] = tmp_mbox; - mbox[dev]->key = PM_K_EVENT; - mbox[dev]->command = event; - mbox[dev]->parm1 = p1; - mbox[dev]->parm2 = p2; - mbox[dev]->parm3 = p3; - msg_direction[dev] = A_TO_S; - - if ((server_wait_flag[dev].flags & WK_SLEEP)) - { - { - server_wait_flag[dev].flags = WK_WAKEUP; - module_wake_up (&server_procs[dev]); - }; - } - - - appl_wait_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&appl_proc); - appl_wait_flag.flags &= ~WK_SLEEP;; - mbox[dev] = NULL; - msg_direction[dev] = 0; - } - - restore_flags (flags); - vfree (tmp_mbox); - - return err; -} - -#endif diff --git a/drivers/sound/pss.c b/drivers/sound/pss.c index 2b892b932e28..fd6e0b6f5f7d 100644 --- a/drivers/sound/pss.c +++ b/drivers/sound/pss.c @@ -2,20 +2,40 @@ * sound/pss.c * * The low level driver for the Personal Sound System (ECHO ESC614). - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1996 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * + * 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 modularisation, clean up. + * + * 98-02-21: Vladimir Michl + * Added mixer device for Beethoven ADSP-16 (master volume, + * bass, treble, synth), only for speakers. + * Fixed bug in pss_write (exchange parameters) + * Fixed config port of SB + * Requested two regions for PSS (PSS mixer, PSS config) + * Modified pss_download_boot + * To probe_pss_mss added test for initialize AD1848 + * 98-05-28: Vladimir Michl + * Fixed computation of mixer volumes */ -#include +#include +#include + #include "sound_config.h" +#include "sound_firmware.h" +#include "soundmodule.h" -#if defined(CONFIG_PSS) && defined(CONFIG_AUDIO) +#ifdef CONFIG_PSS +#ifdef CONFIG_AUDIO /* * PSS registers. @@ -33,7 +53,7 @@ */ #define CONF_PSS 0x10 #define CONF_WSS 0x12 -#define CONF_SB 0x13 +#define CONF_SB 0x14 #define CONF_CDROM 0x16 #define CONF_MIDI 0x18 @@ -47,800 +67,1053 @@ #define PSS_WRITE_EMPTY 0x8000 #define PSS_READ_FULL 0x4000 +/* + * WSS registers + */ +#define WSS_INDEX 4 +#define WSS_DATA 5 + +/* + * WSS status bits + */ +#define WSS_INITIALIZING 0x80 +#define WSS_AUTOCALIBRATION 0x20 + +#define NO_WSS_MIXER -1 + #include "coproc.h" #ifdef PSS_HAVE_LD -#include "synth-ld.h" +#include "pss_boot.h" #else -static int pss_synthLen = 0; +static int pss_synthLen = 0; static unsigned char *pss_synth = NULL; - #endif -typedef struct pss_confdata - { - int base; - int irq; - int dma; - int *osp; - } +/* If compiled into kernel, it enable or disable pss mixer */ +#ifdef CONFIG_PSS_MIXER +static unsigned char pss_mixer = 1; +#else +static unsigned char pss_mixer = 0; +#endif -pss_confdata; +typedef struct pss_mixerdata { + unsigned int volume_l; + unsigned int volume_r; + unsigned int bass; + unsigned int treble; + unsigned int synth; +} pss_mixerdata; + +typedef struct pss_confdata { + int base; + int irq; + int dma; + int *osp; + pss_mixerdata mixer; + int ad_mixer_dev; +} pss_confdata; + static pss_confdata pss_data; static pss_confdata *devc = &pss_data; static int pss_initialized = 0; static int nonstandard_microcode = 0; -int -probe_pss (struct address_info *hw_config) +static void pss_write(pss_confdata *devc, int data) { - unsigned short id; - int irq, dma; - - devc->base = hw_config->io_base; - irq = devc->irq = hw_config->irq; - dma = devc->dma = hw_config->dma; - devc->osp = hw_config->osp; + int i, limit; + + limit = jiffies + HZ/10; /* The timeout is 0.1 seconds */ + /* + * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes + * called while interrupts are disabled. This means that the timer is + * disabled also. However the timeout situation is a abnormal condition. + * Normally the DSP should be ready to accept commands after just couple of + * loops. + */ + + for (i = 0; i < 5000000 && jiffies < limit; i++) + { + if (inw(REG(PSS_STATUS)) & PSS_WRITE_EMPTY) + { + outw(data, REG(PSS_DATA)); + return; + } + } + printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data); +} - if (devc->base != 0x220 && devc->base != 0x240) - if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */ - return 0; +int probe_pss(struct address_info *hw_config) +{ + unsigned short id; + int irq, dma; - if (check_region (devc->base, 16)) - { - printk ("PSS: I/O port conflict\n"); - return 0; - } + devc->base = hw_config->io_base; + irq = devc->irq = hw_config->irq; + dma = devc->dma = hw_config->dma; + devc->osp = hw_config->osp; - id = inw (REG (PSS_ID)); - if ((id >> 8) != 'E') - { - /* printk ("No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); */ - return 0; - } + if (devc->base != 0x220 && devc->base != 0x240) + if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */ + return 0; - return 1; + if (check_region(devc->base, 0x19 /*16*/)) { + printk(KERN_ERR "PSS: I/O port conflict\n"); + return 0; + } + id = inw(REG(PSS_ID)); + if ((id >> 8) != 'E') { + printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); + return 0; + } + return 1; } -static int -set_irq (pss_confdata * devc, int dev, int irq) +static int set_irq(pss_confdata * devc, int dev, int irq) { - static unsigned short irq_bits[16] = - { - 0x0000, 0x0000, 0x0000, 0x0008, - 0x0000, 0x0010, 0x0000, 0x0018, - 0x0000, 0x0020, 0x0028, 0x0030, - 0x0038, 0x0000, 0x0000, 0x0000 - }; - - unsigned short tmp, bits; + static unsigned short irq_bits[16] = + { + 0x0000, 0x0000, 0x0000, 0x0008, + 0x0000, 0x0010, 0x0000, 0x0018, + 0x0000, 0x0020, 0x0028, 0x0030, + 0x0038, 0x0000, 0x0000, 0x0000 + }; - if (irq < 0 || irq > 15) - return 0; + unsigned short tmp, bits; - tmp = inw (REG (dev)) & ~0x38; /* Load confreg, mask IRQ bits out */ + if (irq < 0 || irq > 15) + return 0; - if ((bits = irq_bits[irq]) == 0 && irq != 0) - { - printk ("PSS: Invalid IRQ %d\n", irq); - return 0; - } + tmp = inw(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */ - outw (tmp | bits, REG (dev)); - return 1; + if ((bits = irq_bits[irq]) == 0 && irq != 0) + { + printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq); + return 0; + } + outw(tmp | bits, REG(dev)); + return 1; } -static int -set_io_base (pss_confdata * devc, int dev, int base) +static int set_io_base(pss_confdata * devc, int dev, int base) { - unsigned short tmp = inw (REG (dev)) & 0x003f; - unsigned short bits = (base & 0x0ffc) << 4; + unsigned short tmp = inw(REG(dev)) & 0x003f; + unsigned short bits = (base & 0x0ffc) << 4; - outw (bits | tmp, REG (dev)); + outw(bits | tmp, REG(dev)); - return 1; + return 1; } -static int -set_dma (pss_confdata * devc, int dev, int dma) +static int set_dma(pss_confdata * devc, int dev, int dma) { - static unsigned short dma_bits[8] = - { - 0x0001, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0006, 0x0007 - }; - - unsigned short tmp, bits; + static unsigned short dma_bits[8] = + { + 0x0001, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0006, 0x0007 + }; - if (dma < 0 || dma > 7) - return 0; + unsigned short tmp, bits; - tmp = inw (REG (dev)) & ~0x07; /* Load confreg, mask DMA bits out */ + if (dma < 0 || dma > 7) + return 0; - if ((bits = dma_bits[dma]) == 0 && dma != 4) - { - printk ("PSS: Invalid DMA %d\n", dma); - return 0; - } + tmp = inw(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */ - outw (tmp | bits, REG (dev)); - return 1; + if ((bits = dma_bits[dma]) == 0 && dma != 4) + { + printk(KERN_ERR "PSS: Invalid DMA %d\n", dma); + return 0; + } + outw(tmp | bits, REG(dev)); + return 1; } -static int -pss_reset_dsp (pss_confdata * devc) +static int pss_reset_dsp(pss_confdata * devc) { - unsigned long i, limit = jiffies + 10; - - outw (0x2000, REG (PSS_CONTROL)); - - for (i = 0; i < 32768 && jiffies < limit; i++) - inw (REG (PSS_CONTROL)); + unsigned long i, limit = jiffies + HZ/10; - outw (0x0000, REG (PSS_CONTROL)); - - return 1; + outw(0x2000, REG(PSS_CONTROL)); + for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) + inw(REG(PSS_CONTROL)); + outw(0x0000, REG(PSS_CONTROL)); + return 1; } -static int -pss_put_dspword (pss_confdata * devc, unsigned short word) +static int pss_put_dspword(pss_confdata * devc, unsigned short word) { - int i, val; + int i, val; - for (i = 0; i < 327680; i++) - { - val = inw (REG (PSS_STATUS)); - if (val & PSS_WRITE_EMPTY) + for (i = 0; i < 327680; i++) { - outw (word, REG (PSS_DATA)); - return 1; + val = inw(REG(PSS_STATUS)); + if (val & PSS_WRITE_EMPTY) + { + outw(word, REG(PSS_DATA)); + return 1; + } } - } - return 0; + return 0; } -static int -pss_get_dspword (pss_confdata * devc, unsigned short *word) +static int pss_get_dspword(pss_confdata * devc, unsigned short *word) { - int i, val; + int i, val; - for (i = 0; i < 327680; i++) - { - val = inw (REG (PSS_STATUS)); - if (val & PSS_READ_FULL) + for (i = 0; i < 327680; i++) { - *word = inw (REG (PSS_DATA)); - return 1; + val = inw(REG(PSS_STATUS)); + if (val & PSS_READ_FULL) + { + *word = inw(REG(PSS_DATA)); + return 1; + } } - } - - return 0; + return 0; } -static int -pss_download_boot (pss_confdata * devc, unsigned char *block, int size, int flags) +static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags) { - int i, limit, val, count; + int i, limit, val, count; - if (flags & CPF_FIRST) - { + if (flags & CPF_FIRST) + { /*_____ Warn DSP software that a boot is coming */ - outw (0x00fe, REG (PSS_DATA)); - - limit = jiffies + 10; - - for (i = 0; i < 32768 && jiffies < limit; i++) - if (inw (REG (PSS_DATA)) == 0x5500) - break; - - outw (*block++, REG (PSS_DATA)); - - pss_reset_dsp (devc); - } + outw(0x00fe, REG(PSS_DATA)); - count = 1; - while (1) - { - int j; + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && jiffies < limit; i++) + if (inw(REG(PSS_DATA)) == 0x5500) + break; - for (j = 0; j < 327670; j++) - { -/*_____ Wait for BG to appear */ - if (inw (REG (PSS_STATUS)) & PSS_FLAG3) - break; + outw(*block++, REG(PSS_DATA)); + pss_reset_dsp(devc); } - - if (j == 327670) + count = 1; + while ((flags&CPF_LAST) || count= size && flags & CPF_LAST) - break; - else - { - printk ("\nPSS: Download timeout problems, byte %d=%d\n", - count, size); - return 0; - } - } -/*_____ Send the next byte */ - outw (*block++, REG (PSS_DATA)); - count++; - } - - if (flags & CPF_LAST) - { -/*_____ Why */ - outw (0, REG (PSS_DATA)); - - limit = jiffies + 10; - for (i = 0; i < 32768 && jiffies < limit; i++) - val = inw (REG (PSS_STATUS)); + int j; - limit = jiffies + 10; - for (i = 0; i < 32768 && jiffies < limit; i++) - { - val = inw (REG (PSS_STATUS)); - if (val & 0x4000) - break; + for (j = 0; j < 327670; j++) + { +/*_____ Wait for BG to appear */ + if (inw(REG(PSS_STATUS)) & PSS_FLAG3) + break; + } + + if (j == 327670) + { + /* It's ok we timed out when the file was empty */ + if (count >= size && flags & CPF_LAST) + break; + else + { + printk("\n"); + printk(KERN_ERR "PSS: Download timeout problems, byte %d=%d\n", count, size); + return 0; + } + } +/*_____ Send the next byte */ + if (count >= size) + { + /* If not data in block send 0xffff */ + outw (0xffff, REG (PSS_DATA)); + } + else + { + /*_____ Send the next byte */ + outw (*block++, REG (PSS_DATA)); + }; + count++; } - /* now read the version */ - for (i = 0; i < 32000; i++) + if (flags & CPF_LAST) { - val = inw (REG (PSS_STATUS)); - if (val & PSS_READ_FULL) - break; +/*_____ Why */ + outw(0, REG(PSS_DATA)); + + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && (limit - jiffies >= 0); i++) + val = inw(REG(PSS_STATUS)); + + limit = jiffies + HZ/10; + for (i = 0; i < 32768 && (limit-jiffies >= 0); i++) + { + val = inw(REG(PSS_STATUS)); + if (val & 0x4000) + break; + } + + /* now read the version */ + for (i = 0; i < 32000; i++) + { + val = inw(REG(PSS_STATUS)); + if (val & PSS_READ_FULL) + break; + } + if (i == 32000) + return 0; + + val = inw(REG(PSS_DATA)); + /* printk( "", val/16, val % 16); */ } - if (i == 32000) - return 0; - - val = inw (REG (PSS_DATA)); - /* printk("", val/16, val % 16); */ - } - - return 1; + return 1; } -void -attach_pss (struct address_info *hw_config) +/* Mixer */ +static void set_master_volume(pss_confdata *devc, int left, int right) { - unsigned short id; - char tmp[100]; - - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->dma = hw_config->dma; - devc->osp = hw_config->osp; - - if (!probe_pss (hw_config)) - return; - - id = inw (REG (PSS_ID)) & 0x00ff; - - /* - * Disable all emulations. Will be enabled later (if required). - */ - outw (0x0000, REG (CONF_PSS)); - outw (0x0000, REG (CONF_WSS)); - outw (0x0000, REG (CONF_SB)); - outw (0x0000, REG (CONF_MIDI)); - outw (0x0000, REG (CONF_CDROM)); - -#if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES - if (sound_alloc_dma (hw_config->dma, "PSS")) - { - printk ("pss.c: Can't allocate DMA channel\n"); - return; - } - - if (!set_irq (devc, CONF_PSS, devc->irq)) - { - printk ("PSS: IRQ error\n"); - return; - } - - if (!set_dma (devc, CONF_PSS, devc->dma)) - { - printk ("PSS: DRQ error\n"); - return; - } -#endif - - pss_initialized = 1; - sprintf (tmp, "ECHO-PSS Rev. %d", id); - conf_printf (tmp, hw_config); + static unsigned char log_scale[101] = { + 0xdb, 0xe0, 0xe3, 0xe5, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, + 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xff, 0xff, 0xff + }; + pss_write(devc, 0x0010); + pss_write(devc, log_scale[left] | 0x0000); + pss_write(devc, 0x0010); + pss_write(devc, log_scale[right] | 0x0100); } -int -probe_pss_mpu (struct address_info *hw_config) +static void set_synth_volume(pss_confdata *devc, int volume) { - int timeout; - - if (!pss_initialized) - return 0; - - if (check_region (hw_config->io_base, 2)) - { - printk ("PSS: MPU I/O port conflict\n"); - return 0; - } - - if (!set_io_base (devc, CONF_MIDI, hw_config->io_base)) - { - printk ("PSS: MIDI base error.\n"); - return 0; - } - - if (!set_irq (devc, CONF_MIDI, hw_config->irq)) - { - printk ("PSS: MIDI IRQ error.\n"); - return 0; - } - - if (!pss_synthLen) - { - printk ("PSS: Can't enable MPU. MIDI synth microcode not available.\n"); - return 0; - } - - if (!pss_download_boot (devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk ("PSS: Unable to load MIDI synth microcode to DSP.\n"); - return 0; - } - -/* - * Finally wait until the DSP algorithm has initialized itself and - * deactivates receive interrupt. - */ - - for (timeout = 900000; timeout > 0; timeout--) - { - if ((inb (hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */ - inb (hw_config->io_base); /* Discard it */ - else - break; /* No more input */ - } - -#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) - return probe_mpu401 (hw_config); -#else - return 0; -#endif + int vol = ((0x8000*volume)/100L); + pss_write(devc, 0x0080); + pss_write(devc, vol); + pss_write(devc, 0x0081); + pss_write(devc, vol); } -static int -pss_coproc_open (void *dev_info, int sub_device) +static void set_bass(pss_confdata *devc, int level) { - switch (sub_device) - { - case COPR_MIDI: + int vol = (int)(((0xfd - 0xf0) * level)/100L) + 0xf0; + pss_write(devc, 0x0010); + pss_write(devc, vol | 0x0200); +}; - if (pss_synthLen == 0) +static void set_treble(pss_confdata *devc, int level) +{ + int vol = (((0xfd - 0xf0) * level)/100L) + 0xf0; + pss_write(devc, 0x0010); + pss_write(devc, vol | 0x0300); +}; + +static void pss_mixer_reset(pss_confdata *devc) +{ + set_master_volume(devc, 33, 33); + set_bass(devc, 50); + set_treble(devc, 50); + set_synth_volume(devc, 30); + pss_write (devc, 0x0010); + pss_write (devc, 0x0800 | 0xce); /* Stereo */ + + if(pss_mixer) { - printk ("PSS: MIDI synth microcode not available.\n"); - return -(EIO); + devc->mixer.volume_l = devc->mixer.volume_r = 33; + devc->mixer.bass = 50; + devc->mixer.treble = 50; + devc->mixer.synth = 30; } - - if (nonstandard_microcode) - if (!pss_download_boot (devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk ("PSS: Unable to load MIDI synth microcode to DSP.\n"); - return -(EIO); - } - nonstandard_microcode = 0; - break; - - default:; - } - return 0; } -static void -pss_coproc_close (void *dev_info, int sub_device) +static void arg_to_volume_mono(unsigned int volume, int *aleft) { - return; + int left; + + left = volume & 0x00ff; + if (left > 100) + left = 100; + *aleft = left; } -static void -pss_coproc_reset (void *dev_info) +static void arg_to_volume_stereo(unsigned int volume, int *aleft, int *aright) { - if (pss_synthLen) - if (!pss_download_boot (devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk ("PSS: Unable to load MIDI synth microcode to DSP.\n"); - } - nonstandard_microcode = 0; + arg_to_volume_mono(volume, aleft); + arg_to_volume_mono(volume >> 8, aright); } -static int -download_boot_block (void *dev_info, copr_buffer * buf) +static int ret_vol_mono(int left) { - if (buf->len <= 0 || buf->len > sizeof (buf->data)) - return -(EINVAL); - - if (!pss_download_boot (devc, buf->data, buf->len, buf->flags)) - { - printk ("PSS: Unable to load microcode block to DSP.\n"); - return -(EIO); - } - nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */ - - return 0; + return ((left << 8) | left); } -static int -pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local) +static int ret_vol_stereo(int left, int right) { - /* printk("PSS coproc ioctl %x %x %d\n", cmd, arg, local); */ - - switch (cmd) - { - case SNDCTL_COPR_RESET: - pss_coproc_reset (dev_info); - return 0; - break; - - case SNDCTL_COPR_LOAD: - { - copr_buffer *buf; - int err; - - buf = (copr_buffer *) vmalloc (sizeof (copr_buffer)); - if (buf == NULL) - return -(ENOSPC); - - memcpy_fromfs ((char *) buf, &((char *) arg)[0], sizeof (*buf)); - err = download_boot_block (dev_info, buf); - vfree (buf); - return err; - } - break; - - case SNDCTL_COPR_SENDMSG: - { - copr_msg *buf; - unsigned long flags; - unsigned short *data; - int i; - - buf = (copr_msg *) vmalloc (sizeof (copr_msg)); - if (buf == NULL) - return -(ENOSPC); - - memcpy_fromfs ((char *) buf, &((char *) arg)[0], sizeof (*buf)); - - data = (unsigned short *) (buf->data); + return ((right << 8) | left); +} - save_flags (flags); - cli (); +static int call_ad_mixer(pss_confdata *devc,unsigned int cmd, caddr_t arg) +{ + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return mixer_devs[devc->ad_mixer_dev]->ioctl(devc->ad_mixer_dev, cmd, arg); + else + return -EINVAL; +} - for (i = 0; i < buf->len; i++) - { - if (!pss_put_dspword (devc, *data++)) - { - restore_flags (flags); - buf->len = i; /* feed back number of WORDs sent */ - memcpy_tofs (&((char *) arg)[0], &buf, sizeof (buf)); - vfree (buf); - return -(EIO); - } - } +static int pss_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + pss_confdata *devc = mixer_devs[dev]->devc; + int cmdf = cmd & 0xff; + + if ((cmdf != SOUND_MIXER_VOLUME) && (cmdf != SOUND_MIXER_BASS) && + (cmdf != SOUND_MIXER_TREBLE) && (cmdf != SOUND_MIXER_SYNTH) && + (cmdf != SOUND_MIXER_DEVMASK) && (cmdf != SOUND_MIXER_STEREODEVS) && + (cmdf != SOUND_MIXER_RECMASK) && (cmdf != SOUND_MIXER_CAPS) && + (cmdf != SOUND_MIXER_RECSRC)) + { + return call_ad_mixer(devc, cmd, arg); + } + + if (((cmd >> 8) & 0xff) != 'M') + return -EINVAL; + + if (_SIOC_DIR (cmd) & _SIOC_WRITE) + { + switch (cmdf) + { + case SOUND_MIXER_RECSRC: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + { + if (*(int *)arg != 0) + return -EINVAL; + return 0; + } + case SOUND_MIXER_VOLUME: + arg_to_volume_stereo(*(unsigned int *)arg, &devc->mixer.volume_l, + &devc->mixer.volume_r); + set_master_volume(devc, devc->mixer.volume_l, + devc->mixer.volume_r); + return ret_vol_stereo(devc->mixer.volume_l, + devc->mixer.volume_r); + + case SOUND_MIXER_BASS: + arg_to_volume_mono(*(unsigned int *)arg, + &devc->mixer.bass); + set_bass(devc, devc->mixer.bass); + return ret_vol_mono(devc->mixer.bass); + + case SOUND_MIXER_TREBLE: + arg_to_volume_mono(*(unsigned int *)arg, + &devc->mixer.treble); + set_treble(devc, devc->mixer.treble); + return ret_vol_mono(devc->mixer.treble); + + case SOUND_MIXER_SYNTH: + arg_to_volume_mono(*(unsigned int *)arg, + &devc->mixer.synth); + set_synth_volume(devc, devc->mixer.synth); + return ret_vol_mono(devc->mixer.synth); + + default: + return -EINVAL; + } + } + else + { + /* + * Return parameters + */ + switch (cmdf) + { + + case SOUND_MIXER_DEVMASK: + if (call_ad_mixer(devc, cmd, arg) == -EINVAL) + *(int *)arg = 0; /* no mixer devices */ + return (*(int *)arg |= SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH); + + case SOUND_MIXER_STEREODEVS: + if (call_ad_mixer(devc, cmd, arg) == -EINVAL) + *(int *)arg = 0; /* no stereo devices */ + return (*(int *)arg |= SOUND_MASK_VOLUME); + + case SOUND_MIXER_RECMASK: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + return (*(int *)arg = 0); /* no record devices */ + + case SOUND_MIXER_CAPS: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + return (*(int *)arg = SOUND_CAP_EXCL_INPUT); + + case SOUND_MIXER_RECSRC: + if (devc->ad_mixer_dev != NO_WSS_MIXER) + return call_ad_mixer(devc, cmd, arg); + else + return (*(int *)arg = 0); /* no record source */ + + case SOUND_MIXER_VOLUME: + return (*(int *)arg = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r)); + + case SOUND_MIXER_BASS: + return (*(int *)arg = ret_vol_mono(devc->mixer.bass)); + + case SOUND_MIXER_TREBLE: + return (*(int *)arg = ret_vol_mono(devc->mixer.treble)); + + case SOUND_MIXER_SYNTH: + return (*(int *)arg = ret_vol_mono(devc->mixer.synth)); + default: + return -EINVAL; + } + } +} - restore_flags (flags); - vfree (buf); +static struct mixer_operations pss_mixer_operations = +{ + "SOUNDPORT", + "PSS-AD1848", + pss_mixer_ioctl +}; - return 0; - } - break; +void attach_pss(struct address_info *hw_config) +{ + unsigned short id; + char tmp[100]; + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->dma = hw_config->dma; + devc->osp = hw_config->osp; + devc->ad_mixer_dev = NO_WSS_MIXER; - case SNDCTL_COPR_RCVMSG: - { - copr_msg *buf; - unsigned long flags; - unsigned short *data; - unsigned int i; - int err = 0; + if (!probe_pss(hw_config)) + return; - buf = (copr_msg *) vmalloc (sizeof (copr_msg)); - if (buf == NULL) - return -(ENOSPC); + request_region(hw_config->io_base, 0x10, "PSS mixer, SB emulation"); + request_region(hw_config->io_base + 0x10, 0x9, "PSS config"); + id = inw(REG(PSS_ID)) & 0x00ff; - data = (unsigned short *) buf->data; + /* + * Disable all emulations. Will be enabled later (if required). + */ + outw(0x0000, REG(CONF_PSS)); /* 0x0400 enables joystick */ + outw(0x0000, REG(CONF_WSS)); + outw(0x0000, REG(CONF_SB)); + outw(0x0000, REG(CONF_MIDI)); + outw(0x0000, REG(CONF_CDROM)); - save_flags (flags); - cli (); +#if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES + if (sound_alloc_dma(hw_config->dma, "PSS")) + { + printk("pss.c: Can't allocate DMA channel.\n"); + return; + } + if (!set_irq(devc, CONF_PSS, devc->irq)) + { + printk("PSS: IRQ allocation error.\n"); + return; + } + if (!set_dma(devc, CONF_PSS, devc->dma)) + { + printk(KERN_ERR "PSS: DMA allocation error\n"); + return; + } +#endif - for (i = 0; i < buf->len; i++) - { - buf->len = i; /* feed back number of WORDs read */ - if (!pss_get_dspword (devc, data++)) - { - if (i == 0) - err = -(EIO); - break; - } - } + pss_initialized = 1; + sprintf(tmp, "ECHO-PSS Rev. %d", id); + conf_printf(tmp, hw_config); +} - restore_flags (flags); +int probe_pss_mpu(struct address_info *hw_config) +{ + int timeout; - memcpy_tofs (&((char *) arg)[0], &buf, sizeof (buf)); - vfree (buf); + if (!pss_initialized) + return 0; - return err; - } - break; + if (check_region(hw_config->io_base, 2)) + { + printk(KERN_ERR "PSS: MPU I/O port conflict\n"); + return 0; + } + if (!set_io_base(devc, CONF_MIDI, hw_config->io_base)) + { + printk(KERN_ERR "PSS: MIDI base could not be set.\n"); + return 0; + } + if (!set_irq(devc, CONF_MIDI, hw_config->irq)) + { + printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n"); + return 0; + } + if (!pss_synthLen) + { + printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n"); + return 0; + } + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + return 0; + } + /* + * Finally wait until the DSP algorithm has initialized itself and + * deactivates receive interrupt. + */ - case SNDCTL_COPR_RDATA: - { - copr_debug_buf buf; - unsigned long flags; - unsigned short tmp; + for (timeout = 900000; timeout > 0; timeout--) + { + if ((inb(hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */ + inb(hw_config->io_base); /* Discard it */ + else + break; /* No more input */ + } - memcpy_fromfs ((char *) &buf, &((char *) arg)[0], sizeof (buf)); +#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) + return probe_mpu401(hw_config); +#else + return 0; +#endif +} - save_flags (flags); - cli (); - if (!pss_put_dspword (devc, 0x00d0)) - { - restore_flags (flags); - return -(EIO); - } +static int pss_coproc_open(void *dev_info, int sub_device) +{ + switch (sub_device) + { + case COPR_MIDI: + if (pss_synthLen == 0) + { + printk(KERN_ERR "PSS: MIDI synth microcode not available.\n"); + return -EIO; + } + if (nonstandard_microcode) + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + return -EIO; + } + nonstandard_microcode = 0; + break; + + default: + } + return 0; +} - if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff))) - { - restore_flags (flags); - return -(EIO); - } +static void pss_coproc_close(void *dev_info, int sub_device) +{ + return; +} - if (!pss_get_dspword (devc, &tmp)) - { - restore_flags (flags); - return -(EIO); - } +static void pss_coproc_reset(void *dev_info) +{ + if (pss_synthLen) + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) + { + printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); + } + nonstandard_microcode = 0; +} - buf.parm1 = tmp; - restore_flags (flags); +static int download_boot_block(void *dev_info, copr_buffer * buf) +{ + if (buf->len <= 0 || buf->len > sizeof(buf->data)) + return -EINVAL; - memcpy_tofs (&((char *) arg)[0], &buf, sizeof (buf)); - return 0; - } - break; - - case SNDCTL_COPR_WDATA: - { - copr_debug_buf buf; - unsigned long flags; - unsigned short tmp; - - memcpy_fromfs ((char *) &buf, &((char *) arg)[0], sizeof (buf)); - - save_flags (flags); - cli (); - if (!pss_put_dspword (devc, 0x00d1)) - { - restore_flags (flags); - return -(EIO); - } - - if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff))) - { - restore_flags (flags); - return -(EIO); - } - - tmp = (unsigned int) buf.parm2 & 0xffff; - if (!pss_put_dspword (devc, tmp)) - { - restore_flags (flags); - return -(EIO); - } - - restore_flags (flags); - return 0; - } - break; - - case SNDCTL_COPR_WCODE: - { - copr_debug_buf buf; - unsigned long flags; - unsigned short tmp; - - memcpy_fromfs ((char *) &buf, &((char *) arg)[0], sizeof (buf)); - - save_flags (flags); - cli (); - if (!pss_put_dspword (devc, 0x00d3)) - { - restore_flags (flags); - return -(EIO); - } - - if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff))) - { - restore_flags (flags); - return -(EIO); - } - - tmp = (unsigned int) buf.parm2 & 0x00ff; - if (!pss_put_dspword (devc, tmp)) - { - restore_flags (flags); - return -(EIO); - } - - tmp = ((unsigned int) buf.parm2 >> 8) & 0xffff; - if (!pss_put_dspword (devc, tmp)) - { - restore_flags (flags); - return -(EIO); - } - - restore_flags (flags); - return 0; - } - break; - - case SNDCTL_COPR_RCODE: - { - copr_debug_buf buf; - unsigned long flags; - unsigned short tmp; - - memcpy_fromfs ((char *) &buf, &((char *) arg)[0], sizeof (buf)); - - save_flags (flags); - cli (); - if (!pss_put_dspword (devc, 0x00d2)) - { - restore_flags (flags); - return -(EIO); - } - - if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff))) - { - restore_flags (flags); - return -(EIO); - } - - if (!pss_get_dspword (devc, &tmp)) /* Read MSB */ - { - restore_flags (flags); - return -(EIO); - } - - buf.parm1 = tmp << 8; - - if (!pss_get_dspword (devc, &tmp)) /* Read LSB */ - { - restore_flags (flags); - return -(EIO); - } - - buf.parm1 |= tmp & 0x00ff; - - restore_flags (flags); - - memcpy_tofs (&((char *) arg)[0], &buf, sizeof (buf)); + if (!pss_download_boot(devc, buf->data, buf->len, buf->flags)) + { + printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n"); + return -EIO; + } + nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */ return 0; - } - break; - - default: - return -(EINVAL); - } +} - return -(EINVAL); +static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local) +{ + copr_buffer *buf; + copr_msg *mbuf; + copr_debug_buf dbuf; + unsigned short tmp; + unsigned long flags; + unsigned short *data; + int i, err; + /* printk( "PSS coproc ioctl %x %x %d\n", cmd, arg, local); */ + + switch (cmd) + { + case SNDCTL_COPR_RESET: + pss_coproc_reset(dev_info); + return 0; + + case SNDCTL_COPR_LOAD: + buf = (copr_buffer *) vmalloc(sizeof(copr_buffer)); + if (buf == NULL) + return -ENOSPC; + if (copy_from_user(buf, arg, sizeof(copr_buffer))) { + vfree(buf); + return -EFAULT; + } + err = download_boot_block(dev_info, buf); + vfree(buf); + return err; + + case SNDCTL_COPR_SENDMSG: + mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); + if (mbuf == NULL) + return -ENOSPC; + if (copy_from_user(mbuf, arg, sizeof(copr_msg))) { + vfree(mbuf); + return -EFAULT; + } + data = (unsigned short *)(mbuf->data); + save_flags(flags); + cli(); + for (i = 0; i < mbuf->len; i++) { + if (!pss_put_dspword(devc, *data++)) { + restore_flags(flags); + mbuf->len = i; /* feed back number of WORDs sent */ + err = copy_to_user(arg, mbuf, sizeof(copr_msg)); + vfree(mbuf); + return err ? -EFAULT : -EIO; + } + } + restore_flags(flags); + vfree(mbuf); + return 0; + + case SNDCTL_COPR_RCVMSG: + err = 0; + mbuf = (copr_msg *)vmalloc(sizeof(copr_msg)); + if (mbuf == NULL) + return -ENOSPC; + data = (unsigned short *)mbuf->data; + save_flags(flags); + cli(); + for (i = 0; i < mbuf->len; i++) { + mbuf->len = i; /* feed back number of WORDs read */ + if (!pss_get_dspword(devc, data++)) { + if (i == 0) + err = -EIO; + break; + } + } + restore_flags(flags); + if (copy_to_user(arg, mbuf, sizeof(copr_msg))) + err = -EFAULT; + vfree(mbuf); + return err; + + case SNDCTL_COPR_RDATA: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d0)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + if (!pss_get_dspword(devc, &tmp)) { + restore_flags(flags); + return -EIO; + } + dbuf.parm1 = tmp; + restore_flags(flags); + if (copy_to_user(arg, &dbuf, sizeof(dbuf))) + return -EFAULT; + return 0; + + case SNDCTL_COPR_WDATA: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d1)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + tmp = (unsigned int)dbuf.parm2 & 0xffff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } + restore_flags(flags); + return 0; + + case SNDCTL_COPR_WCODE: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d3)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + tmp = (unsigned int)dbuf.parm2 & 0x00ff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } + tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff; + if (!pss_put_dspword(devc, tmp)) { + restore_flags(flags); + return -EIO; + } + restore_flags(flags); + return 0; + + case SNDCTL_COPR_RCODE: + if (copy_from_user(&dbuf, arg, sizeof(dbuf))) + return -EFAULT; + save_flags(flags); + cli(); + if (!pss_put_dspword(devc, 0x00d2)) { + restore_flags(flags); + return -EIO; + } + if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { + restore_flags(flags); + return -EIO; + } + if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */ + restore_flags(flags); + return -EIO; + } + dbuf.parm1 = tmp << 8; + if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */ + restore_flags(flags); + return -EIO; + } + dbuf.parm1 |= tmp & 0x00ff; + restore_flags(flags); + if (copy_to_user(arg, &dbuf, sizeof(dbuf))) + return -EFAULT; + return 0; + + default: + return -EINVAL; + } + return -EINVAL; } static coproc_operations pss_coproc_operations = { - "ADSP-2115", - pss_coproc_open, - pss_coproc_close, - pss_coproc_ioctl, - pss_coproc_reset, - &pss_data + "ADSP-2115", + pss_coproc_open, + pss_coproc_close, + pss_coproc_ioctl, + pss_coproc_reset, + &pss_data }; -void -attach_pss_mpu (struct address_info *hw_config) +void attach_pss_mpu(struct address_info *hw_config) { #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) - { - int prev_devs; - - prev_devs = num_midis; - attach_mpu401 (hw_config); - - if (num_midis == (prev_devs + 1)) /* The MPU driver installed itself */ - midi_devs[prev_devs]->coproc = &pss_coproc_operations; - } + attach_mpu401(hw_config); /* Slot 1 */ + if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ + midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations; #endif } -int -probe_pss_mss (struct address_info *hw_config) +int probe_pss_mss(struct address_info *hw_config) { - int timeout; - - if (!pss_initialized) - return 0; - - if (check_region (hw_config->io_base, 8)) - { - printk ("PSS: WSS I/O port conflict\n"); - return 0; - } + volatile int timeout; - if (!set_io_base (devc, CONF_WSS, hw_config->io_base)) - { - printk ("PSS: WSS base error.\n"); - return 0; - } + if (!pss_initialized) + return 0; - if (!set_irq (devc, CONF_WSS, hw_config->irq)) - { - printk ("PSS: WSS IRQ error.\n"); - return 0; - } + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "PSS: WSS I/O port conflicts.\n"); + return 0; + } + if (!set_io_base(devc, CONF_WSS, hw_config->io_base)) + { + printk("PSS: WSS base not settable.\n"); + return 0; + } + if (!set_irq(devc, CONF_WSS, hw_config->irq)) + { + printk("PSS: WSS IRQ allocation error.\n"); + return 0; + } + if (!set_dma(devc, CONF_WSS, hw_config->dma)) + { + printk(KERN_ERR "PSS: WSS DMA allocation error\n"); + return 0; + } + /* + * For some reason the card returns 0xff in the WSS status register + * immediately after boot. Probably MIDI+SB emulation algorithm + * downloaded to the ADSP2115 spends some time initializing the card. + * Let's try to wait until it finishes this task. + */ + for (timeout = 0; timeout < 100000 && (inb(hw_config->io_base + WSS_INDEX) & WSS_INITIALIZING); timeout++); - if (!set_dma (devc, CONF_WSS, hw_config->dma)) - { - printk ("PSS: WSS DRQ error\n"); - return 0; - } + outb((0x0b), hw_config->io_base + WSS_INDEX); /* Required by some cards */ - /* - * For some reason the card returns 0xff in the WSS status register - * immediately after boot. Probably MIDI+SB emulation algorithm - * downloaded to the ADSP2115 spends some time initializing the card. - * Let's try to wait until it finishes this task. - */ - for (timeout = 0; - timeout < 100000 && (inb (hw_config->io_base + 3) & 0x3f) != 0x04; - timeout++); + for (timeout = 0; (inb(hw_config->io_base + WSS_DATA) & WSS_AUTOCALIBRATION) && (timeout < 100000); timeout++); - outb (0x0b, hw_config->io_base + 4); /* Required by some cards */ - return probe_ms_sound (hw_config); + return probe_ms_sound(hw_config); } -void -attach_pss_mss (struct address_info *hw_config) +void attach_pss_mss(struct address_info *hw_config) { - int prev_devs; - - prev_devs = num_audiodevs; - attach_ms_sound (hw_config); + int my_mix = -999; /* gcc shut up */ + + devc->ad_mixer_dev = NO_WSS_MIXER; + if (pss_mixer) + { + if ((my_mix = sound_install_mixer (MIXER_DRIVER_VERSION, + "PSS-SPEAKERS and AD1848 (through MSS audio codec)", + &pss_mixer_operations, + sizeof (struct mixer_operations), + devc)) < 0) + { + printk(KERN_ERR "Could not install PSS mixer\n"); + return; + } + } + pss_mixer_reset(devc); + attach_ms_sound(hw_config); /* Slot 0 */ - if (num_audiodevs == (prev_devs + 1)) /* The MSS driver installed itself */ - audio_devs[prev_devs]->coproc = &pss_coproc_operations; + if (hw_config->slots[0] != -1) + { + /* The MSS driver installed itself */ + audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations; + if (pss_mixer && (num_mixers == (my_mix + 2))) + { + /* The MSS mixer installed */ + devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev; + } + } } -void -unload_pss (struct address_info *hw_config) +void unload_pss(struct address_info *hw_config) { + release_region(hw_config->io_base, 0x10); + release_region(hw_config->io_base+0x10, 0x9); } -void -unload_pss_mpu (struct address_info *hw_config) +void unload_pss_mpu(struct address_info *hw_config) { #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) - unload_mpu401 (hw_config); + unload_mpu401(hw_config); #endif } -void -unload_pss_mss (struct address_info *hw_config) +void unload_pss_mss(struct address_info *hw_config) { - unload_ms_sound (hw_config); + unload_ms_sound(hw_config); } +#ifdef MODULE + +int pss_io = -1; + +int mss_io = -1; +int mss_irq = -1; +int mss_dma = -1; + +int mpu_io = -1; +int mpu_irq = -1; + +struct address_info cfgpss = { 0 /* pss_io */, 0, -1, -1 }; +struct address_info cfgmpu = { 0 /* mpu_io */, 0 /* mpu_irq */, 0, -1 }; +struct address_info cfgmss = { 0 /* mss_io */, 0 /* mss_irq */, 0 /* mss_dma */, -1 }; + +MODULE_PARM(pss_io, "i"); +MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)"); +MODULE_PARM(mss_io, "i"); +MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)"); +MODULE_PARM(mss_irq, "i"); +MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)"); +MODULE_PARM(mss_dma, "i"); +MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)"); +MODULE_PARM(mpu_io, "i"); +MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)"); +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)"); +MODULE_PARM(pss_mixer, "b"); +MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards."); +MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl"); +MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards).\n"); + +static int fw_load = 0; +static int pssmpu = 0, pssmss = 0; + +/* + * Load a PSS sound card module + */ + +int init_module(void) +{ + if (pss_io == -1 || mss_io == -1 || mss_irq == -1 || mss_dma == -1) { + printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n"); + return -EINVAL; + } + + cfgpss.io_base = pss_io; + + cfgmss.io_base = mss_io; + cfgmss.irq = mss_irq; + cfgmss.dma = mss_dma; + + cfgmpu.io_base = mpu_io; + cfgmpu.irq = mpu_irq; + + if (!pss_synth) + { + fw_load = 1; + pss_synthLen = mod_firmware_load("/etc/sound/pss_synth", (void *) &pss_synth); + } + if (!probe_pss(&cfgpss)) + return -ENODEV; + attach_pss(&cfgpss); + /* + * Attach stuff + */ + if (probe_pss_mpu(&cfgmpu)) { + pssmpu = 1; + attach_pss_mpu(&cfgmpu); + } + if (probe_pss_mss(&cfgmss)) { + pssmss = 1; + attach_pss_mss(&cfgmss); + } + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + if (fw_load && pss_synth) + vfree(pss_synth); + if (pssmss) + unload_pss_mss(&cfgmss); + if (pssmpu) + unload_pss_mpu(&cfgmpu); + unload_pss(&cfgpss); + SOUND_LOCK_END; +} +#endif +#endif #endif diff --git a/drivers/sound/sb.h b/drivers/sound/sb.h index 40a415c14b37..8d7ffe1a716f 100644 --- a/drivers/sound/sb.h +++ b/drivers/sound/sb.h @@ -1,3 +1,7 @@ +#include +#include "legacy.h" + +#ifdef CONFIG_SBDSP #define DSP_RESET (devc->base + 0x6) #define DSP_READ (devc->base + 0xA) #define DSP_WRITE (devc->base + 0xC) @@ -36,11 +40,16 @@ #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 SUBMDL_ALS007 42 /* ALS-007 differs from SB16 only in mixer */ + /* register assignment */ /* * Config flags */ @@ -83,11 +92,12 @@ typedef struct sb_devc { volatile int intr_active, irq_mode; /* Mixer fields */ - unsigned short levels[SOUND_MIXER_NRDEVICES]; + int *levels; mixer_tab *iomap; int mixer_caps, recmask, supported_devices; int supported_rec_devices; int my_mixerdev; + int sbmixnum; /* Audio fields */ unsigned long trg_buf; @@ -101,11 +111,12 @@ typedef struct sb_devc { /* 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 */ } sb_devc; 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); @@ -113,9 +124,14 @@ int sb_dsp_detect (struct address_info *hw_config); void sb_dsp_init (struct address_info *hw_config); void sb_dsp_unload(struct address_info *hw_config); int sb_mixer_init(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); void sb_audio_init (sb_devc *devc, char *name); void sb_midi_interrupt (sb_devc *devc); int ess_write (sb_devc *devc, unsigned char reg, unsigned char data); int ess_read (sb_devc *devc, unsigned char reg); + +extern int acer; +extern sb_devc *last_sb; +#endif diff --git a/drivers/sound/sb_audio.c b/drivers/sound/sb_audio.c index 47ba13e3478a..971a7414f290 100644 --- a/drivers/sound/sb_audio.c +++ b/drivers/sound/sb_audio.c @@ -2,1201 +2,1147 @@ * sound/sb_audio.c * * Audio routines for Sound Blaster compatible cards. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1996 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * + * 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 : Formatting and clean ups + * + * Status + * Mostly working. Weird uart bug causing irq storms */ -#include - +#include #include "sound_config.h" -#if defined(CONFIG_SBDSP) +#ifdef CONFIG_SBDSP #include "sb_mixer.h" #include "sb.h" -static int -sb_audio_open (int dev, int mode) +static int sb_audio_open(int dev, int mode) { - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - { - printk ("SB: Incomplete initialization\n"); - return -(ENXIO); - } - - if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ) - { - printk ("SB: Recording is not possible with this device\n"); - return -(EPERM); - } - - save_flags (flags); - cli (); - if (devc->opened) - { - restore_flags (flags); - return -(EBUSY); - } - - if (devc->dma16 != -1 && devc->dma16 != devc->dma8) - { - if (sound_open_dma (devc->dma16, "Sound Blaster 16 bit")) + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + { + printk(KERN_ERR "Sound Blaster: incomplete initialization.\n"); + return -ENXIO; + } + if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ) + { + if (mode == OPEN_READ) + return -EPERM; + } + save_flags(flags); + cli(); + if (devc->opened) + { + restore_flags(flags); + return -EBUSY; + } + if (devc->dma16 != -1 && devc->dma16 != devc->dma8) { - return -(EBUSY); + if (sound_open_dma(devc->dma16, "Sound Blaster 16 bit")) + { + restore_flags(flags); + return -EBUSY; + } } - } - devc->opened = mode; - restore_flags (flags); + devc->opened = mode; + restore_flags(flags); + + devc->irq_mode = IMODE_NONE; + sb_dsp_reset(devc); - devc->irq_mode = IMODE_NONE; - sb_dsp_reset (devc); + /* The ALS007 seems to require that the DSP be removed from the output */ + /* in order for recording to be activated properly. This is done by */ + /* setting the appropriate bits of the output control register 4ch to */ + /* zero. This code assumes that the output control registers are not */ + /* used anywhere else and therefore the DSP bits are *always* ON for */ + /* output and OFF for sampling. */ - return 0; + if (devc->submodel == SUBMDL_ALS007) + { + if (mode & OPEN_READ) + sb_setmixer(devc,ALS007_OUTPUT_CTRL2, + sb_getmixer(devc,ALS007_OUTPUT_CTRL2) & 0xf9); + else + sb_setmixer(devc,ALS007_OUTPUT_CTRL2, + sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06); + } + return 0; } -static void -sb_audio_close (int dev) +static void sb_audio_close(int dev) { - sb_devc *devc = audio_devs[dev]->devc; + sb_devc *devc = audio_devs[dev]->devc; - audio_devs[dev]->dmachan1 = - audio_devs[dev]->dmachan2 = - devc->dma8; + audio_devs[dev]->dmap_in->dma = audio_devs[dev]->dmap_out->dma = devc->dma8; - if (devc->dma16 != -1 && devc->dma16 != devc->dma8) - sound_close_dma (devc->dma16); + if (devc->dma16 != -1 && devc->dma16 != devc->dma8) + sound_close_dma(devc->dma16); - devc->opened = 0; + /* For ALS007, turn DSP output back on if closing the device for read */ + + if ((devc->submodel == SUBMDL_ALS007) && (devc->opened & OPEN_READ)) + { + sb_setmixer(devc,ALS007_OUTPUT_CTRL2, + sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06); + } + devc->opened = 0; } -static void -sb_set_output_parms (int dev, unsigned long buf, int nr_bytes, - int intrflag, int restart_dma) +static void sb_set_output_parms(int dev, unsigned long buf, int nr_bytes, + int intrflag) { - sb_devc *devc = audio_devs[dev]->devc; + sb_devc *devc = audio_devs[dev]->devc; - devc->trg_buf = buf; - devc->trg_bytes = nr_bytes; - devc->trg_intrflag = intrflag; - devc->trg_restart = restart_dma; - devc->irq_mode = IMODE_OUTPUT; + devc->trg_buf = buf; + devc->trg_bytes = nr_bytes; + devc->trg_intrflag = intrflag; + devc->irq_mode = IMODE_OUTPUT; } -static void -sb_set_input_parms (int dev, unsigned long buf, int count, int intrflag, - int restart_dma) +static void sb_set_input_parms(int dev, unsigned long buf, int count, int intrflag) { - sb_devc *devc = audio_devs[dev]->devc; + sb_devc *devc = audio_devs[dev]->devc; - devc->trg_buf = buf; - devc->trg_bytes = count; - devc->trg_intrflag = intrflag; - devc->trg_restart = restart_dma; - devc->irq_mode = IMODE_INPUT; + devc->trg_buf = buf; + devc->trg_bytes = count; + devc->trg_intrflag = intrflag; + devc->irq_mode = IMODE_INPUT; } /* * SB1.x compatible routines */ -static void -sb1_audio_output_block (int dev, unsigned long buf, int nr_bytes, - int intrflag, int restart_dma) +static void sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes, int intrflag) { - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - - DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); - - if (audio_devs[dev]->dmachan1 > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_OUTPUT; - - save_flags (flags); - cli (); - if (sb_dsp_command (devc, 0x14)) /* 8 bit DAC using DMA */ - { - sb_dsp_command (devc, (unsigned char) (count & 0xff)); - sb_dsp_command (devc, (unsigned char) ((count >> 8) & 0xff)); - } - else - printk ("SB: Unable to start DAC\n"); - restore_flags (flags); - devc->intr_active = 1; -} + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; -static void -sb1_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag, - int restart_dma) -{ - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - - /* - * Start a DMA input to the buffer pointed by dmaqtail - */ - - DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); - - if (audio_devs[dev]->dmachan1 > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_INPUT; - - save_flags (flags); - cli (); - if (sb_dsp_command (devc, 0x24)) /* 8 bit ADC using DMA */ - { - sb_dsp_command (devc, (unsigned char) (count & 0xff)); - sb_dsp_command (devc, (unsigned char) ((count >> 8) & 0xff)); - } - else - printk ("SB Error: Unable to start ADC\n"); - restore_flags (flags); - - devc->intr_active = 1; + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_OUTPUT; + + save_flags(flags); + cli(); + if (sb_dsp_command(devc, 0x14)) /* 8 bit DAC using DMA */ + { + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + } + else + printk(KERN_WARNING "Sound Blaster: unable to start DAC.\n"); + restore_flags(flags); + devc->intr_active = 1; } -static void -sb1_audio_trigger (int dev, int bits) +static void sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) { - sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ - bits &= devc->irq_mode; + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - if (!bits) - sb_dsp_command (devc, 0xd0); /* Halt DMA */ - else - { - switch (devc->irq_mode) + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_INPUT; + + save_flags(flags); + cli(); + if (sb_dsp_command(devc, 0x24)) /* 8 bit ADC using DMA */ { - case IMODE_INPUT: - sb1_audio_start_input (dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag, devc->trg_restart); - break; - - case IMODE_OUTPUT: - sb1_audio_output_block (dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag, devc->trg_restart); - break; + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); } - } + else + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); + restore_flags(flags); - devc->trigger_bits = bits; + devc->intr_active = 1; } -static int -sb1_audio_prepare_for_input (int dev, int bsize, int bcount) +static void sb1_audio_trigger(int dev, int bits) { - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - - save_flags (flags); - cli (); - if (sb_dsp_command (devc, 0x40)) - sb_dsp_command (devc, devc->tconst); - sb_dsp_command (devc, DSP_CMD_SPKOFF); - restore_flags (flags); - - devc->trigger_bits = 0; - return 0; + sb_devc *devc = audio_devs[dev]->devc; + + bits &= devc->irq_mode; + + if (!bits) + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + else + { + switch (devc->irq_mode) + { + case IMODE_INPUT: + sb1_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + sb1_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + } + } + devc->trigger_bits = bits; } -static int -sb1_audio_prepare_for_output (int dev, int bsize, int bcount) +static int sb1_audio_prepare_for_input(int dev, int bsize, int bcount) { - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - - save_flags (flags); - cli (); - if (sb_dsp_command (devc, 0x40)) - sb_dsp_command (devc, devc->tconst); - sb_dsp_command (devc, DSP_CMD_SPKON); - restore_flags (flags); - devc->trigger_bits = 0; - return 0; + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + + save_flags(flags); + cli(); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKOFF); + restore_flags(flags); + + devc->trigger_bits = 0; + return 0; } -static int -sb1_audio_set_speed (int dev, int speed) +static int sb1_audio_prepare_for_output(int dev, int bsize, int bcount) { - int max_speed = 23000; - sb_devc *devc = audio_devs[dev]->devc; - int tmp; - - if (devc->opened & OPEN_READ) - max_speed = 13000; + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + + save_flags(flags); + cli(); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKON); + restore_flags(flags); + devc->trigger_bits = 0; + return 0; +} - if (speed > 0) - { - if (speed < 4000) - speed = 4000; +static int sb1_audio_set_speed(int dev, int speed) +{ + int max_speed = 23000; + sb_devc *devc = audio_devs[dev]->devc; + int tmp; - if (speed > max_speed) - speed = max_speed; + if (devc->opened & OPEN_READ) + max_speed = 13000; - devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; + if (speed > 0) + { + if (speed < 4000) + speed = 4000; - tmp = 256 - devc->tconst; - speed = (1000000 + tmp / 2) / tmp; + if (speed > max_speed) + speed = max_speed; - devc->speed = speed; - } + devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; + tmp = 256 - devc->tconst; + speed = (1000000 + tmp / 2) / tmp; - return devc->speed; + devc->speed = speed; + } + return devc->speed; } -static short -sb1_audio_set_channels (int dev, short channels) +static short sb1_audio_set_channels(int dev, short channels) { - sb_devc *devc = audio_devs[dev]->devc; - - return devc->channels = 1; + sb_devc *devc = audio_devs[dev]->devc; + return devc->channels = 1; } -static unsigned int -sb1_audio_set_bits (int dev, unsigned int bits) +static unsigned int sb1_audio_set_bits(int dev, unsigned int bits) { - sb_devc *devc = audio_devs[dev]->devc; - - return devc->bits = 8; + sb_devc *devc = audio_devs[dev]->devc; + return devc->bits = 8; } -static void -sb1_audio_halt_xfer (int dev) +static void sb1_audio_halt_xfer(int dev) { - sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + sb_devc *devc = audio_devs[dev]->devc; - sb_dsp_reset (devc); + save_flags(flags); + cli(); + sb_dsp_reset(devc); + restore_flags(flags); } /* * SB 2.0 and SB 2.01 compatible routines */ -static void -sb20_audio_output_block (int dev, unsigned long buf, int nr_bytes, - int intrflag, int restart_dma) +static void sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes, + int intrflag) { - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - unsigned char cmd; - - DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); - - if (audio_devs[dev]->dmachan1 > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_OUTPUT; - - save_flags (flags); - cli (); - if (sb_dsp_command (devc, 0x48)) /* DSP Block size */ - { - sb_dsp_command (devc, (unsigned char) (count & 0xff)); - sb_dsp_command (devc, (unsigned char) ((count >> 8) & 0xff)); - - if (devc->speed * devc->channels <= 23000) - cmd = 0x1c; /* 8 bit PCM output */ - else - cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */ - - if (!sb_dsp_command (devc, cmd)) - printk ("SB: Unable to start DAC\n"); - - } - else - printk ("SB: Unable to start DAC\n"); - restore_flags (flags); - devc->intr_active = 1; -} + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + unsigned char cmd; -static void -sb20_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag, - int restart_dma) -{ - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - unsigned char cmd; - - /* - * Start a DMA input to the buffer pointed by dmaqtail - */ - - DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); - - if (audio_devs[dev]->dmachan1 > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_INPUT; - - save_flags (flags); - cli (); - if (sb_dsp_command (devc, 0x48)) /* DSP Block size */ - { - sb_dsp_command (devc, (unsigned char) (count & 0xff)); - sb_dsp_command (devc, (unsigned char) ((count >> 8) & 0xff)); - - if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000)) - cmd = 0x2c; /* 8 bit PCM input */ - else - cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */ - - if (!sb_dsp_command (devc, cmd)) - printk ("SB: Unable to start ADC\n"); - } - else - printk ("SB Error: Unable to start ADC\n"); - restore_flags (flags); - - devc->intr_active = 1; + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; + + devc->irq_mode = IMODE_OUTPUT; + + save_flags(flags); + cli(); + if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ + { + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + + if (devc->speed * devc->channels <= 23000) + cmd = 0x1c; /* 8 bit PCM output */ + else + cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */ + + if (!sb_dsp_command(devc, cmd)) + printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); + } + else + printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); + restore_flags(flags); + devc->intr_active = 1; } -static void -sb20_audio_trigger (int dev, int bits) +static void sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) { - sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + unsigned char cmd; + + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ + + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ + + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; - bits &= devc->irq_mode; + devc->irq_mode = IMODE_INPUT; - if (!bits) - sb_dsp_command (devc, 0xd0); /* Halt DMA */ - else - { - switch (devc->irq_mode) + save_flags(flags); + cli(); + if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ { - case IMODE_INPUT: - sb20_audio_start_input (dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag, devc->trg_restart); - break; - - case IMODE_OUTPUT: - sb20_audio_output_block (dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag, devc->trg_restart); - break; + sb_dsp_command(devc, (unsigned char) (count & 0xff)); + sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); + + if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000)) + cmd = 0x2c; /* 8 bit PCM input */ + else + cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */ + + if (!sb_dsp_command(devc, cmd)) + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); } - } + else + printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); + restore_flags(flags); + devc->intr_active = 1; +} + +static void sb20_audio_trigger(int dev, int bits) +{ + sb_devc *devc = audio_devs[dev]->devc; + bits &= devc->irq_mode; - devc->trigger_bits = bits; + if (!bits) + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + else + { + switch (devc->irq_mode) + { + case IMODE_INPUT: + sb20_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + sb20_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + } + } + devc->trigger_bits = bits; } /* * SB2.01 specific speed setup */ -static int -sb201_audio_set_speed (int dev, int speed) +static int sb201_audio_set_speed(int dev, int speed) { - sb_devc *devc = audio_devs[dev]->devc; - int tmp; - int s = speed * devc->channels; - - if (speed > 0) - { - if (speed < 4000) - speed = 4000; - - if (speed > 44100) - speed = 44100; - - if (devc->opened & OPEN_READ && speed > 15000) - speed = 15000; - - devc->tconst = ((65536 - ((256000000 + s / 2) / - s)) >> 8) & 0xff; - - tmp = 256 - devc->tconst; - speed = ((1000000 + tmp / 2) / tmp) / devc->channels; + sb_devc *devc = audio_devs[dev]->devc; + int tmp; + int s = speed * devc->channels; - devc->speed = speed; - } - - return devc->speed; + if (speed > 0) + { + if (speed < 4000) + speed = 4000; + if (speed > 44100) + speed = 44100; + if (devc->opened & OPEN_READ && speed > 15000) + speed = 15000; + devc->tconst = ((65536 - ((256000000 + s / 2) / s)) >> 8) & 0xff; + tmp = 256 - devc->tconst; + speed = ((1000000 + tmp / 2) / tmp) / devc->channels; + + devc->speed = speed; + } + return devc->speed; } /* * SB Pro specific routines */ -static int -sbpro_audio_prepare_for_input (int dev, int bsize, int bcount) +static int sbpro_audio_prepare_for_input(int dev, int bsize, int bcount) { /* For SB Pro and Jazz16 */ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - unsigned char bits = 0; - - if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) - audio_devs[dev]->dmachan1 = - audio_devs[dev]->dmachan2 = - devc->bits == 16 ? devc->dma16 : devc->dma8; - - if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) - if (devc->bits == AFMT_S16_LE) - bits = 0x04; /* 16 bit mode */ - - save_flags (flags); - cli (); - if (sb_dsp_command (devc, 0x40)) - sb_dsp_command (devc, devc->tconst); - sb_dsp_command (devc, DSP_CMD_SPKOFF); - if (devc->channels == 1) - sb_dsp_command (devc, 0xa0 | bits); /* Mono input */ - else - sb_dsp_command (devc, 0xa8 | bits); /* Stereo input */ - restore_flags (flags); - - devc->trigger_bits = 0; - return 0; + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + unsigned char bits = 0; + + if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) + audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = + devc->bits == 16 ? devc->dma16 : devc->dma8; + + if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) + if (devc->bits == AFMT_S16_LE) + bits = 0x04; /* 16 bit mode */ + + save_flags(flags); + cli(); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKOFF); + if (devc->channels == 1) + sb_dsp_command(devc, 0xa0 | bits); /* Mono input */ + else + sb_dsp_command(devc, 0xa8 | bits); /* Stereo input */ + restore_flags(flags); + + devc->trigger_bits = 0; + return 0; } -static int -sbpro_audio_prepare_for_output (int dev, int bsize, int bcount) +static int sbpro_audio_prepare_for_output(int dev, int bsize, int bcount) { /* For SB Pro and Jazz16 */ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - unsigned char tmp; - unsigned char bits = 0; - - if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) - audio_devs[dev]->dmachan1 = - audio_devs[dev]->dmachan2 = - devc->bits == 16 ? devc->dma16 : devc->dma8; - - save_flags (flags); - cli (); - if (sb_dsp_command (devc, 0x40)) - sb_dsp_command (devc, devc->tconst); - sb_dsp_command (devc, DSP_CMD_SPKON); - - if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) - { - if (devc->bits == AFMT_S16_LE) - bits = 0x04; /* 16 bit mode */ - - if (devc->channels == 1) - sb_dsp_command (devc, 0xa0 | bits); /* Mono output */ - else - sb_dsp_command (devc, 0xa8 | bits); /* Stereo output */ - } - else - { - tmp = sb_getmixer (devc, 0x0e); - if (devc->channels == 1) - tmp &= ~0x02; - else - tmp |= 0x02; - sb_setmixer (devc, 0x0e, tmp); - } - restore_flags (flags); - devc->trigger_bits = 0; - return 0; + sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags; + unsigned char tmp; + unsigned char bits = 0; + + if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) + audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = devc->bits == 16 ? devc->dma16 : devc->dma8; + if (devc->model == MDL_SBPRO) + sb_mixer_set_stereo(devc, devc->channels == 2); + + save_flags(flags); + cli(); + if (sb_dsp_command(devc, 0x40)) + sb_dsp_command(devc, devc->tconst); + sb_dsp_command(devc, DSP_CMD_SPKON); + + if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) + { + if (devc->bits == AFMT_S16_LE) + bits = 0x04; /* 16 bit mode */ + + if (devc->channels == 1) + sb_dsp_command(devc, 0xa0 | bits); /* Mono output */ + else + sb_dsp_command(devc, 0xa8 | bits); /* Stereo output */ + } + else + { + tmp = sb_getmixer(devc, 0x0e); + if (devc->channels == 1) + tmp &= ~0x02; + else + tmp |= 0x02; + sb_setmixer(devc, 0x0e, tmp); + } + restore_flags(flags); + devc->trigger_bits = 0; + return 0; } -static int -sbpro_audio_set_speed (int dev, int speed) +static int sbpro_audio_set_speed(int dev, int speed) { - sb_devc *devc = audio_devs[dev]->devc; - - if (speed > 0) - { - if (speed < 4000) - speed = 4000; + sb_devc *devc = audio_devs[dev]->devc; - if (speed > 44100) - speed = 44100; - - if (devc->channels > 1 && speed > 22050) - speed = 22050; - - sb201_audio_set_speed (dev, speed); - } - - return devc->speed; + if (speed > 0) + { + if (speed < 4000) + speed = 4000; + if (speed > 44100) + speed = 44100; + if (devc->channels > 1 && speed > 22050) + speed = 22050; + sb201_audio_set_speed(dev, speed); + } + return devc->speed; } -static short -sbpro_audio_set_channels (int dev, short channels) +static short sbpro_audio_set_channels(int dev, short channels) { - sb_devc *devc = audio_devs[dev]->devc; - - if (channels == 1 || channels == 2) - if (channels != devc->channels) - { - devc->channels = channels; - sbpro_audio_set_speed (dev, devc->speed); - } - return devc->channels; + sb_devc *devc = audio_devs[dev]->devc; + + if (channels == 1 || channels == 2) + { + if (channels != devc->channels) + { + devc->channels = channels; + if (devc->model == MDL_SBPRO && devc->channels == 2) + sbpro_audio_set_speed(dev, devc->speed); + } + } + return devc->channels; } -static int -jazz16_audio_set_speed (int dev, int speed) +static int jazz16_audio_set_speed(int dev, int speed) { - sb_devc *devc = audio_devs[dev]->devc; + sb_devc *devc = audio_devs[dev]->devc; - if (speed > 0) - { - int tmp; - int s = speed * devc->channels; - - if (speed < 5000) - speed = 4000; - - if (speed > 44100) - speed = 44100; + if (speed > 0) + { + int tmp; + int s = speed * devc->channels; - devc->tconst = ((65536 - ((256000000 + s / 2) / - s)) >> 8) & 0xff; + if (speed < 5000) + speed = 5000; + if (speed > 44100) + speed = 44100; - tmp = 256 - devc->tconst; - speed = ((1000000 + tmp / 2) / tmp) / devc->channels; + devc->tconst = ((65536 - ((256000000 + s / 2) / s)) >> 8) & 0xff; - devc->speed = speed; - } + tmp = 256 - devc->tconst; + speed = ((1000000 + tmp / 2) / tmp) / devc->channels; - return devc->speed; + devc->speed = speed; + } + return devc->speed; } /* * ESS specific routines */ -static void -ess_speed (sb_devc * devc) +static int ess_audio_set_speed(int dev, int speed) { - int divider; - unsigned char bits = 0; - int speed = devc->speed; - - if (speed < 4000) - speed = 4000; - else if (speed > 48000) - speed = 48000; - - if (speed > 22000) - { - bits = 0x80; - divider = 256 - (795500 + speed / 2) / speed; - } - else - { - divider = 128 - (397700 + speed / 2) / speed; - } - - bits |= (unsigned char) divider; - ess_write (devc, 0xa1, bits); - -/* - * Set filter divider register - */ - - speed = (speed * 9) / 20; /* Set filter roll-off to 90% of speed/2 */ - divider = 256 - 7160000 / (speed * 82); - ess_write (devc, 0xa2, divider); + sb_devc *devc = audio_devs[dev]->devc; + int divider; - return; + if (speed > 0) + { + if (speed < 5000) + speed = 5000; + if (speed > 48000) + speed = 48000; + + if (speed > 22000) + { + divider = (795500 + speed / 2) / speed; + speed = (795500 + divider / 2) / divider; + } + else + { + divider = (397700 + speed / 2) / speed; + speed = (397700 + divider / 2) / divider; + } + devc->speed = speed; + } + return devc->speed; } -static int -ess_audio_prepare_for_input (int dev, int bsize, int bcount) +static void ess_speed(sb_devc * devc) { - sb_devc *devc = audio_devs[dev]->devc; - - ess_speed (devc); - sb_dsp_command (devc, DSP_CMD_SPKOFF); - - ess_write (devc, 0xb8, 0x0e); /* Auto init DMA mode */ - ess_write (devc, 0xa8, (ess_read (devc, 0xa8) & ~0x03) | - (3 - devc->channels)); /* Mono/stereo */ - ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */ - - if (devc->channels == 1) - { - if (devc->bits == AFMT_U8) - { /* 8 bit mono */ - ess_write (devc, 0xb7, 0x51); - ess_write (devc, 0xb7, 0xd0); - } - else - { /* 16 bit mono */ - ess_write (devc, 0xb7, 0x71); - ess_write (devc, 0xb7, 0xf4); - } - } - else - { /* Stereo */ - if (devc->bits == AFMT_U8) - { /* 8 bit stereo */ - ess_write (devc, 0xb7, 0x51); - ess_write (devc, 0xb7, 0x98); + int divider; + unsigned char bits = 0; + int speed = devc->speed; + + if (speed < 4000) + speed = 4000; + else if (speed > 48000) + speed = 48000; + + if (speed > 22000) + { + bits = 0x80; + divider = 256 - (795500 + speed / 2) / speed; } - else - { /* 16 bit stereo */ - ess_write (devc, 0xb7, 0x71); - ess_write (devc, 0xb7, 0xbc); + else + { + divider = 128 - (397700 + speed / 2) / speed; } - } - ess_write (devc, 0xb1, (ess_read (devc, 0xb1) & 0x0f) | 0x50); - ess_write (devc, 0xb2, (ess_read (devc, 0xb2) & 0x0f) | 0x50); + bits |= (unsigned char) divider; + ess_write(devc, 0xa1, bits); - devc->trigger_bits = 0; - return 0; + /* + * Set filter divider register + */ + + speed = (speed * 9) / 20; /* Set filter roll-off to 90% of speed/2 */ + divider = 256 - 7160000 / (speed * 82); + ess_write(devc, 0xa2, divider); + return; } -static int -ess_audio_prepare_for_output (int dev, int bsize, int bcount) +static int ess_audio_prepare_for_input(int dev, int bsize, int bcount) { - sb_devc *devc = audio_devs[dev]->devc; - - sb_dsp_reset (devc); - ess_speed (devc); - - ess_write (devc, 0xb8, 4); /* Auto init DMA mode */ - ess_write (devc, 0xa8, (ess_read (devc, 0xa8) & ~0x03) | - (3 - devc->channels)); /* Mono/stereo */ - ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/request) */ - - if (devc->channels == 1) - { - if (devc->bits == AFMT_U8) - { /* 8 bit mono */ - ess_write (devc, 0xb6, 0x80); - ess_write (devc, 0xb7, 0x51); - ess_write (devc, 0xb7, 0xd0); + sb_devc *devc = audio_devs[dev]->devc; + ess_speed(devc); + sb_dsp_command(devc, DSP_CMD_SPKOFF); + + ess_write(devc, 0xb8, 0x0e); /* Auto init DMA mode */ + ess_write(devc, 0xa8, (ess_read(devc, 0xa8) & ~0x03) | (3 - devc->channels)); /* Mono/stereo */ + ess_write(devc, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */ + + if (devc->channels == 1) + { + if (devc->bits == AFMT_U8) + { + /* 8 bit mono */ + ess_write(devc, 0xb7, 0x51); + ess_write(devc, 0xb7, 0xd0); + } + else + { + /* 16 bit mono */ + ess_write(devc, 0xb7, 0x71); + ess_write(devc, 0xb7, 0xf4); + } } - else - { /* 16 bit mono */ - ess_write (devc, 0xb6, 0x00); - ess_write (devc, 0xb7, 0x71); - ess_write (devc, 0xb7, 0xf4); + else + { + /* Stereo */ + if (devc->bits == AFMT_U8) + { + /* 8 bit stereo */ + ess_write(devc, 0xb7, 0x51); + ess_write(devc, 0xb7, 0x98); + } + else + { + /* 16 bit stereo */ + ess_write(devc, 0xb7, 0x71); + ess_write(devc, 0xb7, 0xbc); + } } - } - else - { /* Stereo */ - if (devc->bits == AFMT_U8) - { /* 8 bit stereo */ - ess_write (devc, 0xb6, 0x80); - ess_write (devc, 0xb7, 0x51); - ess_write (devc, 0xb7, 0x98); + ess_write(devc, 0xb1, (ess_read(devc, 0xb1) & 0x0f) | 0x50); + ess_write(devc, 0xb2, (ess_read(devc, 0xb2) & 0x0f) | 0x50); + devc->trigger_bits = 0; + return 0; +} + +static int ess_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + sb_devc *devc = audio_devs[dev]->devc; + + sb_dsp_reset(devc); + ess_speed(devc); + + ess_write(devc, 0xb8, 4); /* Auto init DMA mode */ + ess_write(devc, 0xa8, (ess_read(devc, 0xa8) & ~0x03) | (3 - devc->channels)); /* Mono/stereo */ + ess_write(devc, 0xb9, 2); /* Demand mode (4 bytes/request) */ + + if (devc->channels == 1) + { + if (devc->bits == AFMT_U8) + { /* 8 bit mono */ + ess_write(devc, 0xb6, 0x80); + ess_write(devc, 0xb7, 0x51); + ess_write(devc, 0xb7, 0xd0); + } + else + { /* 16 bit mono */ + ess_write(devc, 0xb6, 0x00); + ess_write(devc, 0xb7, 0x71); + ess_write(devc, 0xb7, 0xf4); + } } - else - { /* 16 bit stereo */ - ess_write (devc, 0xb6, 0x00); - ess_write (devc, 0xb7, 0x71); - ess_write (devc, 0xb7, 0xbc); + else + { /* Stereo */ + if (devc->bits == AFMT_U8) + { /* 8 bit stereo */ + ess_write(devc, 0xb6, 0x80); + ess_write(devc, 0xb7, 0x51); + ess_write(devc, 0xb7, 0x98); + } + else + { /* 16 bit stereo */ + ess_write(devc, 0xb6, 0x00); + ess_write(devc, 0xb7, 0x71); + ess_write(devc, 0xb7, 0xbc); + } } - } - ess_write (devc, 0xb1, (ess_read (devc, 0xb1) & 0x0f) | 0x50); - ess_write (devc, 0xb2, (ess_read (devc, 0xb2) & 0x0f) | 0x50); - sb_dsp_command (devc, DSP_CMD_SPKON); + ess_write(devc, 0xb1, (ess_read(devc, 0xb1) & 0x0f) | 0x50); + ess_write(devc, 0xb2, (ess_read(devc, 0xb2) & 0x0f) | 0x50); + sb_dsp_command(devc, DSP_CMD_SPKON); - devc->trigger_bits = 0; - return 0; + devc->trigger_bits = 0; + return 0; } -static void -ess_audio_output_block (int dev, unsigned long buf, int nr_bytes, - int intrflag, int restart_dma) +static void ess_audio_output_block(int dev, unsigned long buf, int nr_bytes, + int intrflag) { - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - short c = -nr_bytes; - - if (!restart_dma) - return; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + short c = -nr_bytes; - DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - if (audio_devs[dev]->dmachan1 > 3) - count >>= 1; - count--; + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; - devc->irq_mode = IMODE_OUTPUT; + devc->irq_mode = IMODE_OUTPUT; - ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); - ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); + ess_write(devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); + ess_write(devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); - ess_write (devc, 0xb8, ess_read (devc, 0xb8) | 0x05); /* Go */ - devc->intr_active = 1; + ess_write(devc, 0xb8, ess_read(devc, 0xb8) | 0x05); /* Go */ + devc->intr_active = 1; } -static void -ess_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag, - int restart_dma) +static void ess_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) { - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - short c = -nr_bytes; + int count = nr_bytes; + sb_devc *devc = audio_devs[dev]->devc; + short c = -nr_bytes; - if (!restart_dma) - return; + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ - /* - * Start a DMA input to the buffer pointed by dmaqtail - */ + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + if (audio_devs[dev]->dmap_out->dma > 3) + count >>= 1; + count--; - if (audio_devs[dev]->dmachan1 > 3) - count >>= 1; - count--; + devc->irq_mode = IMODE_INPUT; - devc->irq_mode = IMODE_INPUT; + ess_write(devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); + ess_write(devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); - ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); - ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); - - ess_write (devc, 0xb8, ess_read (devc, 0xb8) | 0x0f); /* Go */ - devc->intr_active = 1; + ess_write(devc, 0xb8, ess_read(devc, 0xb8) | 0x0f); /* Go */ + devc->intr_active = 1; } -static void -ess_audio_trigger (int dev, int bits) +static void ess_audio_trigger(int dev, int bits) { - sb_devc *devc = audio_devs[dev]->devc; + sb_devc *devc = audio_devs[dev]->devc; - bits &= devc->irq_mode; + bits &= devc->irq_mode; - if (!bits) - sb_dsp_command (devc, 0xd0); /* Halt DMA */ - else - { - switch (devc->irq_mode) + if (!bits) + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + else { - case IMODE_INPUT: - ess_audio_start_input (dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag, devc->trg_restart); - break; - - case IMODE_OUTPUT: - ess_audio_output_block (dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag, devc->trg_restart); - break; + switch (devc->irq_mode) + { + case IMODE_INPUT: + ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + } } - } - devc->trigger_bits = bits; + devc->trigger_bits = bits; } /* * SB16 specific routines */ -static int -sb16_audio_set_speed (int dev, int speed) +static int sb16_audio_set_speed(int dev, int speed) { - sb_devc *devc = audio_devs[dev]->devc; - - if (speed > 0) - { - if (speed < 5000) - speed = 4000; + sb_devc *devc = audio_devs[dev]->devc; - if (speed > 44100) - speed = 44100; + if (speed > 0) + { + if (speed < 5000) + speed = 4000; - devc->speed = speed; - } + if (speed > 44100) + speed = 44100; - return devc->speed; + devc->speed = speed; + } + return devc->speed; } -static unsigned int -sb16_audio_set_bits (int dev, unsigned int bits) +static unsigned int sb16_audio_set_bits(int dev, unsigned int bits) { - sb_devc *devc = audio_devs[dev]->devc; + sb_devc *devc = audio_devs[dev]->devc; - if (bits != 0) - if (devc->bits == AFMT_U8 || bits == AFMT_S16_LE) - devc->bits = bits; - else - devc->bits = AFMT_U8; + if (bits != 0) + { + if (bits == AFMT_U8 || bits == AFMT_S16_LE) + devc->bits = bits; + else + devc->bits = AFMT_U8; + } - return devc->bits; + return devc->bits; } -static int -sb16_audio_prepare_for_input (int dev, int bsize, int bcount) +static int sb16_audio_prepare_for_input(int dev, int bsize, int bcount) { - sb_devc *devc = audio_devs[dev]->devc; + sb_devc *devc = audio_devs[dev]->devc; - audio_devs[dev]->dmachan1 = - audio_devs[dev]->dmachan2 = - devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8; + audio_devs[dev]->dmap_out->dma = + audio_devs[dev]->dmap_in->dma = + devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8; - devc->trigger_bits = 0; - return 0; + devc->trigger_bits = 0; + return 0; } -static int -sb16_audio_prepare_for_output (int dev, int bsize, int bcount) +static int sb16_audio_prepare_for_output(int dev, int bsize, int bcount) { - sb_devc *devc = audio_devs[dev]->devc; + sb_devc *devc = audio_devs[dev]->devc; - audio_devs[dev]->dmachan1 = - audio_devs[dev]->dmachan2 = - devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8; + audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = + devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8; - devc->trigger_bits = 0; - return 0; + devc->trigger_bits = 0; + return 0; } -static void -sb16_audio_output_block (int dev, unsigned long buf, int count, - int intrflag, int restart_dma) +static void sb16_audio_output_block(int dev, unsigned long buf, int count, + int intrflag) { - unsigned long flags, cnt; - sb_devc *devc = audio_devs[dev]->devc; + unsigned long flags, cnt; + sb_devc *devc = audio_devs[dev]->devc; - devc->irq_mode = IMODE_OUTPUT; - devc->intr_active = 1; + devc->irq_mode = IMODE_OUTPUT; + devc->intr_active = 1; - if (!restart_dma) - return; + cnt = count; + if (devc->bits == AFMT_S16_LE) + cnt >>= 1; + cnt--; - cnt = count; - if (devc->bits == AFMT_S16_LE) - cnt >>= 1; - cnt--; + save_flags(flags); + cli(); - save_flags (flags); - cli (); + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - if (restart_dma) - DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); + sb_dsp_command(devc, 0x41); + sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff)); + sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff)); - sb_dsp_command (devc, 0x41); - sb_dsp_command (devc, (unsigned char) ((devc->speed >> 8) & 0xff)); - sb_dsp_command (devc, (unsigned char) (devc->speed & 0xff)); + sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6)); + sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + + (devc->bits == AFMT_S16_LE ? 0x10 : 0))); + sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); + sb_dsp_command(devc, (unsigned char) (cnt >> 8)); - sb_dsp_command (devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6)); - sb_dsp_command (devc, ((devc->channels == 2 ? 0x20 : 0) + - (devc->bits == AFMT_S16_LE ? 0x10 : 0))); - sb_dsp_command (devc, (unsigned char) (cnt & 0xff)); - sb_dsp_command (devc, (unsigned char) (cnt >> 8)); - - restore_flags (flags); + restore_flags(flags); } -static void -sb16_audio_start_input (int dev, unsigned long buf, int count, int intrflag, - int restart_dma) +static void sb16_audio_start_input(int dev, unsigned long buf, int count, int intrflag) { - unsigned long flags, cnt; - sb_devc *devc = audio_devs[dev]->devc; - - devc->irq_mode = IMODE_INPUT; - devc->intr_active = 1; + unsigned long flags, cnt; + sb_devc *devc = audio_devs[dev]->devc; - if (!restart_dma) - return; + devc->irq_mode = IMODE_INPUT; + devc->intr_active = 1; - cnt = count; - if (devc->bits == AFMT_S16_LE) - cnt >>= 1; - cnt--; + cnt = count; + if (devc->bits == AFMT_S16_LE) + cnt >>= 1; + cnt--; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - if (restart_dma) - DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); + /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - sb_dsp_command (devc, 0x42); - sb_dsp_command (devc, (unsigned char) ((devc->speed >> 8) & 0xff)); - sb_dsp_command (devc, (unsigned char) (devc->speed & 0xff)); + sb_dsp_command(devc, 0x42); + sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff)); + sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff)); - sb_dsp_command (devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce)); - sb_dsp_command (devc, ((devc->channels == 2 ? 0x20 : 0) + - (devc->bits == AFMT_S16_LE ? 0x10 : 0))); - sb_dsp_command (devc, (unsigned char) (cnt & 0xff)); - sb_dsp_command (devc, (unsigned char) (cnt >> 8)); + sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce)); + sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + + (devc->bits == AFMT_S16_LE ? 0x10 : 0))); + sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); + sb_dsp_command(devc, (unsigned char) (cnt >> 8)); - restore_flags (flags); + restore_flags(flags); } -static void -sb16_audio_trigger (int dev, int bits) +static void sb16_audio_trigger(int dev, int bits) { - sb_devc *devc = audio_devs[dev]->devc; + sb_devc *devc = audio_devs[dev]->devc; - bits &= devc->irq_mode; + bits &= devc->irq_mode; - if (!bits) - sb_dsp_command (devc, 0xd0); /* Halt DMA */ - else - { - switch (devc->irq_mode) + if (!bits) + sb_dsp_command(devc, 0xd0); /* Halt DMA */ + else { - case IMODE_INPUT: - sb16_audio_start_input (dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag, devc->trg_restart); - break; - - case IMODE_OUTPUT: - sb16_audio_output_block (dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag, devc->trg_restart); - break; + switch (devc->irq_mode) + { + case IMODE_INPUT: + sb16_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + + case IMODE_OUTPUT: + sb16_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, + devc->trg_intrflag); + break; + } } - } - - devc->trigger_bits = bits; -} - -static int -sb_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local) -{ - return -(EINVAL); -} - -static void -sb_audio_reset (int dev) -{ - unsigned long flags; - sb_devc *devc = audio_devs[dev]->devc; - save_flags (flags); - cli (); - sb_dsp_reset (devc); - restore_flags (flags); + devc->trigger_bits = bits; } static struct audio_driver sb1_audio_driver = /* SB1.x */ { - sb_audio_open, - sb_audio_close, - sb_set_output_parms, - sb_set_input_parms, - sb_audio_ioctl, - sb1_audio_prepare_for_input, - sb1_audio_prepare_for_output, - sb_audio_reset, - sb1_audio_halt_xfer, - NULL, /* local_qlen */ - NULL, /* copy_from_user */ - NULL, - NULL, - sb1_audio_trigger, - sb1_audio_set_speed, - sb1_audio_set_bits, - sb1_audio_set_channels + sb_audio_open, + sb_audio_close, + sb_set_output_parms, + sb_set_input_parms, + NULL, /* ioctl */ + sb1_audio_prepare_for_input, + sb1_audio_prepare_for_output, + sb1_audio_halt_xfer, + NULL, /* local_qlen */ + NULL, /* copy_from_user */ + NULL, + NULL, + sb1_audio_trigger, + sb1_audio_set_speed, + sb1_audio_set_bits, + sb1_audio_set_channels }; static struct audio_driver sb20_audio_driver = /* SB2.0 */ { - sb_audio_open, - sb_audio_close, - sb_set_output_parms, - sb_set_input_parms, - sb_audio_ioctl, - sb1_audio_prepare_for_input, - sb1_audio_prepare_for_output, - sb_audio_reset, - sb1_audio_halt_xfer, - NULL, /* local_qlen */ - NULL, /* copy_from_user */ - NULL, - NULL, - sb20_audio_trigger, - sb1_audio_set_speed, - sb1_audio_set_bits, - sb1_audio_set_channels + sb_audio_open, + sb_audio_close, + sb_set_output_parms, + sb_set_input_parms, + NULL, + sb1_audio_prepare_for_input, + sb1_audio_prepare_for_output, + sb1_audio_halt_xfer, + NULL, /* local_qlen */ + NULL, /* copy_from_user */ + NULL, + NULL, + sb20_audio_trigger, + sb1_audio_set_speed, + sb1_audio_set_bits, + sb1_audio_set_channels }; static struct audio_driver sb201_audio_driver = /* SB2.01 */ { - sb_audio_open, - sb_audio_close, - sb_set_output_parms, - sb_set_input_parms, - sb_audio_ioctl, - sb1_audio_prepare_for_input, - sb1_audio_prepare_for_output, - sb_audio_reset, - sb1_audio_halt_xfer, - NULL, /* local_qlen */ - NULL, /* copy_from_user */ - NULL, - NULL, - sb20_audio_trigger, - sb201_audio_set_speed, - sb1_audio_set_bits, - sb1_audio_set_channels + sb_audio_open, + sb_audio_close, + sb_set_output_parms, + sb_set_input_parms, + NULL, + sb1_audio_prepare_for_input, + sb1_audio_prepare_for_output, + sb1_audio_halt_xfer, + NULL, /* local_qlen */ + NULL, /* copy_from_user */ + NULL, + NULL, + sb20_audio_trigger, + sb201_audio_set_speed, + sb1_audio_set_bits, + sb1_audio_set_channels }; static struct audio_driver sbpro_audio_driver = /* SB Pro */ { - sb_audio_open, - sb_audio_close, - sb_set_output_parms, - sb_set_input_parms, - sb_audio_ioctl, - sbpro_audio_prepare_for_input, - sbpro_audio_prepare_for_output, - sb_audio_reset, - sb1_audio_halt_xfer, - NULL, /* local_qlen */ - NULL, /* copy_from_user */ - NULL, - NULL, - sb20_audio_trigger, - sbpro_audio_set_speed, - sb1_audio_set_bits, - sbpro_audio_set_channels + sb_audio_open, + sb_audio_close, + sb_set_output_parms, + sb_set_input_parms, + NULL, + sbpro_audio_prepare_for_input, + sbpro_audio_prepare_for_output, + sb1_audio_halt_xfer, + NULL, /* local_qlen */ + NULL, /* copy_from_user */ + NULL, + NULL, + sb20_audio_trigger, + sbpro_audio_set_speed, + sb1_audio_set_bits, + sbpro_audio_set_channels }; static struct audio_driver jazz16_audio_driver = /* Jazz16 and SM Wave */ { - sb_audio_open, - sb_audio_close, - sb_set_output_parms, - sb_set_input_parms, - sb_audio_ioctl, - sbpro_audio_prepare_for_input, - sbpro_audio_prepare_for_output, - sb_audio_reset, - sb1_audio_halt_xfer, - NULL, /* local_qlen */ - NULL, /* copy_from_user */ - NULL, - NULL, - sb20_audio_trigger, - jazz16_audio_set_speed, - sb16_audio_set_bits, - sbpro_audio_set_channels + sb_audio_open, + sb_audio_close, + sb_set_output_parms, + sb_set_input_parms, + NULL, + sbpro_audio_prepare_for_input, + sbpro_audio_prepare_for_output, + sb1_audio_halt_xfer, + NULL, /* local_qlen */ + NULL, /* copy_from_user */ + NULL, + NULL, + sb20_audio_trigger, + jazz16_audio_set_speed, + sb16_audio_set_bits, + sbpro_audio_set_channels }; static struct audio_driver sb16_audio_driver = /* SB16 */ { - sb_audio_open, - sb_audio_close, - sb_set_output_parms, - sb_set_input_parms, - sb_audio_ioctl, - sb16_audio_prepare_for_input, - sb16_audio_prepare_for_output, - sb_audio_reset, - sb1_audio_halt_xfer, - NULL, /* local_qlen */ - NULL, /* copy_from_user */ - NULL, - NULL, - sb16_audio_trigger, - sb16_audio_set_speed, - sb16_audio_set_bits, - sbpro_audio_set_channels + sb_audio_open, + sb_audio_close, + sb_set_output_parms, + sb_set_input_parms, + NULL, + sb16_audio_prepare_for_input, + sb16_audio_prepare_for_output, + sb1_audio_halt_xfer, + NULL, /* local_qlen */ + NULL, /* copy_from_user */ + NULL, + NULL, + sb16_audio_trigger, + sb16_audio_set_speed, + sb16_audio_set_bits, + sbpro_audio_set_channels }; static struct audio_driver ess_audio_driver = /* ESS ES688/1688 */ { - sb_audio_open, - sb_audio_close, - sb_set_output_parms, - sb_set_input_parms, - sb_audio_ioctl, - ess_audio_prepare_for_input, - ess_audio_prepare_for_output, - sb_audio_reset, - sb1_audio_halt_xfer, - NULL, /* local_qlen */ - NULL, /* copy_from_user */ - NULL, - NULL, - ess_audio_trigger, - sb16_audio_set_speed, - sb16_audio_set_bits, - sbpro_audio_set_channels + sb_audio_open, + sb_audio_close, + sb_set_output_parms, + sb_set_input_parms, + NULL, + ess_audio_prepare_for_input, + ess_audio_prepare_for_output, + sb1_audio_halt_xfer, + NULL, /* local_qlen */ + NULL, /* copy_from_user */ + NULL, + NULL, + ess_audio_trigger, + ess_audio_set_speed, + sb16_audio_set_bits, + sbpro_audio_set_channels }; -void -sb_audio_init (sb_devc * devc, char *name) +void sb_audio_init(sb_devc * devc, char *name) { - int audio_flags = 0; - int format_mask = AFMT_U8; - - struct audio_driver *driver = &sb1_audio_driver; - - switch (devc->model) - { - case MDL_SB1: /* SB1.0 or SB 1.5 */ - DDB (printk ("Will use standard SB1.x driver\n")); - audio_flags = DMA_HARDSTOP; - break; - - case MDL_SB2: - DDB (printk ("Will use SB2.0 driver\n")); - audio_flags = DMA_AUTOMODE; - driver = &sb20_audio_driver; - break; - - case MDL_SB201: - DDB (printk ("Will use SB2.01 (high speed) driver\n")); - audio_flags = DMA_AUTOMODE; - driver = &sb201_audio_driver; - break; - - case MDL_JAZZ: - case MDL_SMW: - DDB (printk ("Will use Jazz16 driver\n")); - audio_flags = DMA_AUTOMODE; - format_mask |= AFMT_S16_LE; - driver = &jazz16_audio_driver; - break; - - case MDL_ESS: - DDB (printk ("Will use ESS ES688/1688 driver\n")); - audio_flags = DMA_AUTOMODE; - format_mask |= AFMT_S16_LE; - driver = &ess_audio_driver; - break; - - case MDL_SB16: - DDB (printk ("Will use SB16 driver\n")); - audio_flags = DMA_AUTOMODE; - format_mask |= AFMT_S16_LE; - driver = &sb16_audio_driver; - break; - - default: - DDB (printk ("Will use SB Pro driver\n")); - audio_flags = DMA_AUTOMODE; - driver = &sbpro_audio_driver; - } - - if ((devc->my_dev = sound_install_audiodrv (AUDIO_DRIVER_VERSION, - name, - driver, - sizeof (struct audio_driver), - audio_flags, - format_mask, - devc, - devc->dma8, - devc->dma8)) < 0) - { - return; - } - - audio_devs[devc->my_dev]->mixer_dev = devc->my_mixerdev; - audio_devs[devc->my_dev]->min_fragment = 5; + int audio_flags = 0; + int format_mask = AFMT_U8; + + struct audio_driver *driver = &sb1_audio_driver; + + switch (devc->model) + { + case MDL_SB1: /* SB1.0 or SB 1.5 */ + DDB(printk("Will use standard SB1.x driver\n")); + audio_flags = DMA_HARDSTOP; + break; + + case MDL_SB2: + DDB(printk("Will use SB2.0 driver\n")); + audio_flags = DMA_AUTOMODE; + driver = &sb20_audio_driver; + break; + + case MDL_SB201: + DDB(printk("Will use SB2.01 (high speed) driver\n")); + audio_flags = DMA_AUTOMODE; + driver = &sb201_audio_driver; + break; + + case MDL_JAZZ: + case MDL_SMW: + DDB(printk("Will use Jazz16 driver\n")); + audio_flags = DMA_AUTOMODE; + format_mask |= AFMT_S16_LE; + driver = &jazz16_audio_driver; + break; + + case MDL_ESS: + DDB(printk("Will use ESS ES688/1688 driver\n")); + audio_flags = DMA_AUTOMODE; + format_mask |= AFMT_S16_LE; + driver = &ess_audio_driver; + break; + + case MDL_SB16: + DDB(printk("Will use SB16 driver\n")); + audio_flags = DMA_AUTOMODE; + format_mask |= AFMT_S16_LE; + driver = &sb16_audio_driver; + break; + + default: + DDB(printk("Will use SB Pro driver\n")); + audio_flags = DMA_AUTOMODE; + driver = &sbpro_audio_driver; + } + + if ((devc->my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + name,driver, sizeof(struct audio_driver), + audio_flags, format_mask, devc, + devc->dma8, devc->dma8)) < 0) + { + printk(KERN_ERR "Sound Blaster: unable to install audio.\n"); + return; + } + audio_devs[devc->my_dev]->mixer_dev = devc->my_mixerdev; + audio_devs[devc->my_dev]->min_fragment = 5; } #endif diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c index f1a1276abc33..301dee8be732 100644 --- a/drivers/sound/sb_card.c +++ b/drivers/sound/sb_card.c @@ -2,49 +2,157 @@ * sound/sb_card.c * * Detection routine for the Sound Blaster cards. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1996 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * + * 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 +#include +#include #include "sound_config.h" - -#if defined(CONFIG_SBDSP) +#include "soundmodule.h" #include "sb_mixer.h" #include "sb.h" -void -attach_sb_card (struct address_info *hw_config) +void attach_sb_card(struct address_info *hw_config) { #if defined(CONFIG_AUDIO) || defined(CONFIG_MIDI) - sb_dsp_init (hw_config); + sb_dsp_init(hw_config); #endif } -int -probe_sb (struct address_info *hw_config) +int probe_sb(struct address_info *hw_config) { - if (check_region (hw_config->io_base, 16)) - { - printk ("\n\nsb_dsp.c: I/O port %x already in use\n\n", - hw_config->io_base); - return 0; - } - - return sb_dsp_detect (hw_config); + if (check_region(hw_config->io_base, 16)) + { + printk(KERN_ERR "sb_card: I/O port %x is already in use\n\n", hw_config->io_base); + return 0; + } + return sb_dsp_detect(hw_config); } -void -unload_sb (struct address_info *hw_config) +void unload_sb(struct address_info *hw_config) { - sb_dsp_unload (hw_config); + sb_dsp_unload(hw_config); } +int sb_be_quiet=0; + +struct symbol_table sb_syms= +{ +#include + X(sb_dsp_init), + X(sb_dsp_detect), + X(sb_dsp_unload), + X(sb_dsp_disable_midi), + X(attach_sb_card), + X(probe_sb), + X(unload_sb), + X(sb_be_quiet), +#include +}; + +#ifdef MODULE + +static struct address_info config; +static struct address_info config_mpu; + +/* + * Note DMA2 of -1 has the right meaning in the SB16 driver as well + * as here. It will cause either an error if it is needed or a fallback + * to the 8bit channel. + */ + +int mpu_io = 0; +int io = -1; +int irq = -1; +int dma = -1; +int dma16 = -1; /* Set this for modules that need it */ +int type = 0; /* Can set this to a specific card type */ +int mad16 = 0; /* Set mad16=1 to load this as support for mad16 */ +int trix = 0; /* Set trix=1 to load this as support for trix */ +int pas2 = 0; /* Set pas2=1 to load this as support for pas2 */ +int sm_games = 0; /* Mixer - see sb_mixer.c */ +int acer = 0; /* Do acer notebook init */ + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(dma, "i"); +MODULE_PARM(dma16, "i"); +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(type, "i"); +MODULE_PARM(mad16, "i"); +MODULE_PARM(trix, "i"); +MODULE_PARM(pas2, "i"); +MODULE_PARM(sm_games, "i"); + +static int sbmpu = 0; + +void *smw_free = NULL; + +int init_module(void) +{ + printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + if (mad16 == 0 && trix == 0 && pas2 == 0) + { + if (io == -1 || dma == -1 || irq == -1) + { + printk(KERN_ERR "I/O, IRQ, and DMA are mandatory\n"); + return -EINVAL; + } + config.io_base = io; + config.irq = irq; + config.dma = dma; + config.dma2 = dma16; + config.card_subtype = type; + + if (!probe_sb(&config)) + return -ENODEV; + attach_sb_card(&config); +#ifdef CONFIG_MIDI + config_mpu.io_base = mpu_io; + if (mpu_io && probe_sbmpu(&config_mpu)) + sbmpu = 1; +#endif +#ifdef CONFIG_MIDI + if (sbmpu) + attach_sbmpu(&config_mpu); +#endif + } + register_symtab(&sb_syms); + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + if (smw_free) + vfree(smw_free); + if (!mad16 && !trix && !pas2) + unload_sb(&config); + if (sbmpu) + unload_sbmpu(&config_mpu); + SOUND_LOCK_END; +} + +#else + +#ifdef CONFIG_SM_GAMES +int sm_games = 1; +#else +int sm_games = 0; +#endif +#ifdef CONFIG_SB_ACER +int acer = 1; +#else +int acer = 0; #endif +#endif + diff --git a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c index 24d3d32336d5..b93cd49eb8a2 100644 --- a/drivers/sound/sb_common.c +++ b/drivers/sound/sb_common.c @@ -2,20 +2,22 @@ * sound/sb_common.c * * Common routines for Sound Blaster compatible cards. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1996 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * + * 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 +#include +#include #include "sound_config.h" +#include "sound_firmware.h" -#if defined(CONFIG_SBDSP) +#ifdef CONFIG_SBDSP #ifndef CONFIG_AUDIO #error You will need to configure the sound driver with CONFIG_AUDIO option. @@ -26,1236 +28,1322 @@ static sb_devc *detected_devc = NULL; /* For communication from probe to init */ static sb_devc *last_devc = NULL; /* For MPU401 initialization */ -static sb_devc *irq2devc[16] = -{NULL}; -static unsigned char jazz_irq_bits[] = -{0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6}; -static unsigned char jazz_dma_bits[] = -{0, 1, 0, 2, 0, 3, 0, 4}; + +static unsigned char jazz_irq_bits[] = { + 0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6 +}; + +static unsigned char jazz_dma_bits[] = { + 0, 1, 0, 2, 0, 3, 0, 4 +}; /* * Jazz16 chipset specific control variables */ -static int jazz16_base = 0; /* Not detected */ +static int jazz16_base = 0; /* Not detected */ static unsigned char jazz16_bits = 0; /* I/O relocation bits */ /* - * Logitech SoundMan Wave specific initialization code + * Logitech Soundman Wave specific initialization code */ #ifdef SMW_MIDI0001_INCLUDED #include "smw-midi0001.h" #else -unsigned char *smw_ucode = NULL; -int smw_ucodeLen = 0; +static unsigned char *smw_ucode = NULL; +static int smw_ucodeLen = 0; #endif -int -sb_dsp_command (sb_devc * devc, unsigned char val) +sb_devc *last_sb = NULL; /* Last sb loaded */ + +int sb_dsp_command(sb_devc * devc, unsigned char val) { - int i; - unsigned long limit; - - limit = jiffies + HZ / 10; /* - * The timeout is 0.1 seconds - */ - - /* - * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes - * called while interrupts are disabled. This means that the timer is - * disabled also. However the timeout situation is a abnormal condition. - * Normally the DSP should be ready to accept commands after just couple of - * loops. - */ - - for (i = 0; i < 500000 && jiffies < limit; i++) - { - if ((inb (DSP_STATUS) & 0x80) == 0) + int i; + unsigned long limit; + + limit = jiffies + HZ / 10; /* Timeout */ + + /* + * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes + * called while interrupts are disabled. This means that the timer is + * disabled also. However the timeout situation is a abnormal condition. + * Normally the DSP should be ready to accept commands after just couple of + * loops. + */ + + for (i = 0; i < 500000 && (limit-jiffies)>0; i++) { - outb (val, DSP_COMMAND); - return 1; + if ((inb(DSP_STATUS) & 0x80) == 0) + { + outb((val), DSP_COMMAND); + return 1; + } } - } - - printk ("Sound Blaster: DSP Command(%x) Timeout.\n", val); - return 0; + printk(KERN_WARNING "Sound Blaster: DSP command(%x) timeout.\n", val); + return 0; } -int -sb_dsp_get_byte (sb_devc * devc) +static int sb_dsp_get_byte(sb_devc * devc) { - int i; - - for (i = 1000; i; i--) - if (inb (DSP_DATA_AVAIL) & 0x80) - { - return inb (DSP_READ); - } + int i; - return 0xffff; + for (i = 1000; i; i--) + { + if (inb(DSP_DATA_AVAIL) & 0x80) + return inb(DSP_READ); + } + return 0xffff; } -int -ess_write (sb_devc * devc, unsigned char reg, unsigned char data) +int ess_write(sb_devc * devc, unsigned char reg, unsigned char data) { - /* Write a byte to an extended mode register of ES1688 */ + /* Write a byte to an extended mode register of ES1688 */ - if (!sb_dsp_command (devc, reg)) - return 0; + if (!sb_dsp_command(devc, reg)) + return 0; - return sb_dsp_command (devc, data); + return sb_dsp_command(devc, data); } -int -ess_read (sb_devc * devc, unsigned char reg) +int ess_read(sb_devc * devc, unsigned char reg) { /* Read a byte from an extended mode register of ES1688 */ + if (!sb_dsp_command(devc, 0xc0)) /* Read register command */ + return -1; - if (!sb_dsp_command (devc, 0xc0)) /* Read register command */ - return -1; - - if (!sb_dsp_command (devc, reg)) - return -1; + if (!sb_dsp_command(devc, reg)) + return -1; - return sb_dsp_get_byte (devc); + return sb_dsp_get_byte(devc); } -void -sbintr (int irq, void *dev_id, struct pt_regs *dummy) +static void sbintr(int irq, void *dev_id, struct pt_regs *dummy) { - int status; - unsigned char src = 0xff; - - sb_devc *devc = irq2devc[irq]; - - if (devc == NULL || devc->irq != irq) - { - DEB (printk ("sbintr: Bogus interrupt IRQ%d\n", irq)); - return; - } - - devc->irq_ok = 1; + int status; + unsigned char src = 0xff; - if (devc->model == MDL_SB16) - { + sb_devc *devc = dev_id; - src = sb_getmixer (devc, IRQ_STAT); /* Interrupt source register */ + devc->irq_ok = 1; + if (devc->model == MDL_SB16) + { + src = sb_getmixer(devc, IRQ_STAT); /* Interrupt source register */ -#if defined(CONFIG_MIDI) && defined(CONFIG_UART401) - if (src & 4) - uart401intr (devc->irq, NULL, NULL); /* MPU401 interrupt */ +#if defined(CONFIG_MIDI)&& defined(CONFIG_UART401) + if (src & 4) + uart401intr(devc->irq, devc->midi_irq_cookie, NULL); /* MPU401 interrupt */ #endif - if (!(src & 3)) - return; /* Not a DSP interrupt */ - } - - if (devc->intr_active) - switch (devc->irq_mode) - { - case IMODE_OUTPUT: - DMAbuf_outputintr (devc->dev, 1); - break; + if (!(src & 3)) + return; /* Not a DSP interrupt */ + } + if (devc->intr_active) + { + switch (devc->irq_mode) + { + case IMODE_OUTPUT: + DMAbuf_outputintr(devc->dev, 1); + break; - case IMODE_INPUT: - DMAbuf_inputintr (devc->dev); - break; + case IMODE_INPUT: + DMAbuf_inputintr(devc->dev); + break; - case IMODE_INIT: - break; + case IMODE_INIT: + break; - case IMODE_MIDI: + case IMODE_MIDI: #ifdef CONFIG_MIDI - sb_midi_interrupt (devc); + sb_midi_interrupt(devc); #endif - break; + break; - default: - printk ("Sound Blaster: Unexpected interrupt\n"); - } -/* - * Acknowledge interrupts - */ + default: + /* printk( "Sound Blaster: Unexpected interrupt\n"); */ + ; + } + } + /* + * Acknowledge interrupts + */ - if (src & 0x01) - status = inb (DSP_DATA_AVAIL); + if (src & 0x01) + status = inb(DSP_DATA_AVAIL); - if (devc->model == MDL_SB16 && src & 0x02) - status = inb (DSP_DATA_AVL16); + if (devc->model == MDL_SB16 && src & 0x02) + status = inb(DSP_DATA_AVL16); } -int -sb_dsp_reset (sb_devc * devc) + +int sb_dsp_reset(sb_devc * devc) { - int loopc; + int loopc; - if (devc->model == MDL_ESS) - outb (3, DSP_RESET); /* Reset FIFO too */ - else - outb (1, DSP_RESET); + DEB(printk("Entered sb_dsp_reset()\n")); - tenmicrosec (devc->osp); - outb (0, DSP_RESET); - tenmicrosec (devc->osp); - tenmicrosec (devc->osp); - tenmicrosec (devc->osp); + if (devc->model == MDL_ESS) + outb(3, DSP_RESET); /* Reset FIFO too */ + else + outb(1, DSP_RESET); - for (loopc = 0; loopc < 1000 && !(inb (DSP_DATA_AVAIL) & 0x80); loopc++); + udelay(10); + outb(0, DSP_RESET); + udelay(30); - if (inb (DSP_READ) != 0xAA) - return 0; /* Sorry */ + for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++); - if (devc->model == MDL_ESS) - sb_dsp_command (devc, 0xc6); /* Enable extended mode */ + if (inb(DSP_READ) != 0xAA) + { + DDB(printk("sb: No response to RESET\n")); + return 0; /* Sorry */ + } + if (devc->model == MDL_ESS) + sb_dsp_command(devc, 0xc6); /* Enable extended mode */ - return 1; + DEB(printk("sb_dsp_reset() OK\n")); + return 1; } -static void -dsp_get_vers (sb_devc * devc) +static void dsp_get_vers(sb_devc * devc) { - int i; + int i; - unsigned long flags; + unsigned long flags; - save_flags (flags); - cli (); - devc->major = devc->minor = 0; - sb_dsp_command (devc, 0xe1); /* Get version */ + DDB(printk("Entered dsp_get_vers()\n")); + save_flags(flags); + cli(); + devc->major = devc->minor = 0; + sb_dsp_command(devc, 0xe1); /* Get version */ - for (i = 100000; i; i--) - { - if (inb (DSP_DATA_AVAIL) & 0x80) + for (i = 100000; i; i--) { - if (devc->major == 0) - devc->major = inb (DSP_READ); - else - { - devc->minor = inb (DSP_READ); - break; - } + if (inb(DSP_DATA_AVAIL) & 0x80) + { + if (devc->major == 0) + devc->major = inb(DSP_READ); + else + { + devc->minor = inb(DSP_READ); + break; + } + } } - } - restore_flags (flags); + DDB(printk("DSP version %d.%d\n", devc->major, devc->minor)); + restore_flags(flags); } -static int -sb16_set_dma_hw (sb_devc * devc) +static int sb16_set_dma_hw(sb_devc * devc) { - int bits; - - if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3) - { - printk ("SB16: Invalid 8 bit DMA (%d)\n", devc->dma8); - return 0; - } + int bits; - bits = (1 << devc->dma8); + if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3) + { + printk(KERN_ERR "sb16: Invalid 8 bit DMA (%d)\n", devc->dma8); + return 0; + } + bits = (1 << devc->dma8); - if (devc->dma16 >= 5 && devc->dma16 <= 7) - bits |= (1 << devc->dma16); + if (devc->dma16 >= 5 && devc->dma16 <= 7) + bits |= (1 << devc->dma16); - sb_setmixer (devc, DMA_NR, bits); - return 1; + sb_setmixer(devc, DMA_NR, bits); + return 1; } #if defined(CONFIG_MIDI) && defined(CONFIG_UART401) -static void -sb16_set_mpu_port(sb_devc *devc, struct address_info *hw_config) +static void sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config) { -/* - * This routine initializes new MIDI port setup register of SB Vibra. - */ - unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06; + /* + * This routine initializes new MIDI port setup register of SB Vibra (CT2502). + */ + unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06; + switch (hw_config->io_base) { - case 0x300: - sb_setmixer (devc, 0x84, bits | 0x04); - break; + case 0x300: + sb_setmixer(devc, 0x84, bits | 0x04); + break; - case 0x330: - sb_setmixer (devc, 0x84, bits | 0x00); - break; + case 0x330: + sb_setmixer(devc, 0x84, bits | 0x00); + break; - default: - sb_setmixer (devc, 0x84, bits | 0x02); /* Disable MPU */ - printk("SB16: Invalid MIDI I/O port %x\n", hw_config->io_base); + default: + sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */ + printk(KERN_ERR "SB16: Invalid MIDI I/O port %x\n", hw_config->io_base); } } #endif -static int -sb16_set_irq_hw (sb_devc * devc, int level) +static int sb16_set_irq_hw(sb_devc * devc, int level) { - int ival; - - switch (level) - { - case 5: - ival = 2; - break; - case 7: - ival = 4; - break; - case 9: - ival = 1; - break; - case 10: - ival = 8; - break; - default: - printk ("SB16 IRQ%d is not possible\n", level); - return 0; - } - sb_setmixer (devc, IRQ_NR, ival); - return 1; + int ival; + + switch (level) + { + case 5: + ival = 2; + break; + case 7: + ival = 4; + break; + case 9: + ival = 1; + break; + case 10: + ival = 8; + break; + default: + printk(KERN_ERR "SB16 IRQ%d is not possible\n", level); + return 0; + } + sb_setmixer(devc, IRQ_NR, ival); + return 1; } -static void -relocate_Jazz16 (sb_devc * devc, struct address_info *hw_config) +static void relocate_Jazz16(sb_devc * devc, struct address_info *hw_config) { - unsigned char bits = 0; - unsigned long flags; - - if (jazz16_base != 0 && jazz16_base != hw_config->io_base) - return; - - switch (hw_config->io_base) - { - case 0x220: - bits = 1; - break; - case 0x240: - bits = 2; - break; - case 0x260: - bits = 3; - break; - - default: - return; - } - - bits = jazz16_bits = bits << 5; + unsigned char bits = 0; + unsigned long flags; - jazz16_base = hw_config->io_base; + if (jazz16_base != 0 && jazz16_base != hw_config->io_base) + return; -/* - * Magic wake up sequence by writing to 0x201 (aka Joystick port) - */ - save_flags (flags); - cli (); - outb (0xAF, 0x201); - outb (0x50, 0x201); - outb (bits, 0x201); - restore_flags (flags); + switch (hw_config->io_base) + { + case 0x220: + bits = 1; + break; + case 0x240: + bits = 2; + break; + case 0x260: + bits = 3; + break; + default: + return; + } + bits = jazz16_bits = bits << 5; + jazz16_base = hw_config->io_base; + + /* + * Magic wake up sequence by writing to 0x201 (aka Joystick port) + */ + save_flags(flags); + cli(); + outb((0xAF), 0x201); + outb((0x50), 0x201); + outb((bits), 0x201); + restore_flags(flags); } -static int -init_Jazz16 (sb_devc * devc, struct address_info *hw_config) +static int init_Jazz16(sb_devc * devc, struct address_info *hw_config) { - char name[100]; + char name[100]; + /* + * First try to check that the card has Jazz16 chip. It identifies itself + * by returning 0x12 as response to DSP command 0xfa. + */ + + if (!sb_dsp_command(devc, 0xfa)) + return 0; + + if (sb_dsp_get_byte(devc) != 0x12) + return 0; + + /* + * OK so far. Now configure the IRQ and DMA channel used by the card. + */ + if (hw_config->irq < 1 || hw_config->irq > 15 || jazz_irq_bits[hw_config->irq] == 0) + { + printk(KERN_ERR "Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq); + return 0; + } + if (hw_config->dma < 0 || hw_config->dma > 3 || jazz_dma_bits[hw_config->dma] == 0) + { + printk(KERN_ERR "Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma); + return 0; + } + if (hw_config->dma2 < 0) + { + printk(KERN_ERR "Jazz16: No 16 bit DMA channel defined\n"); + return 0; + } + if (hw_config->dma2 < 5 || hw_config->dma2 > 7 || jazz_dma_bits[hw_config->dma2] == 0) + { + printk(KERN_ERR "Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2); + return 0; + } + devc->dma16 = hw_config->dma2; -/* - * First try to check that the card has Jazz16 chip. It identifies itself - * by returning 0x12 as response to DSP command 0xfa. - */ + if (!sb_dsp_command(devc, 0xfb)) + return 0; - if (!sb_dsp_command (devc, 0xfa)) - return 0; + if (!sb_dsp_command(devc, jazz_dma_bits[hw_config->dma] | + (jazz_dma_bits[hw_config->dma2] << 4))) + return 0; - if (sb_dsp_get_byte (devc) != 0x12) - return 0; + if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq])) + return 0; -/* - * OK so far. Now configure the IRQ and DMA channel used by the card. - */ - if (hw_config->irq < 1 || hw_config->irq > 15 || - jazz_irq_bits[hw_config->irq] == 0) - { - printk ("Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq); - return 0; - } - - if (hw_config->dma < 0 || hw_config->dma > 3 || - jazz_dma_bits[hw_config->dma] == 0) - { - printk ("Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma); - return 0; - } - - if (hw_config->dma2 < 0) - { - printk ("Jazz16: No 16 bit DMA channel defined\n"); - return 0; - } - - if (hw_config->dma2 < 5 || hw_config->dma2 > 7 || - jazz_dma_bits[hw_config->dma2] == 0) - { - printk ("Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2); - return 0; - } - - devc->dma16 = hw_config->dma2; - - if (!sb_dsp_command (devc, 0xfb)) - return 0; - - if (!sb_dsp_command (devc, jazz_dma_bits[hw_config->dma] | - (jazz_dma_bits[hw_config->dma2] << 4))) - return 0; - - if (!sb_dsp_command (devc, jazz_irq_bits[hw_config->irq])) - return 0; + /* + * Now we have configured a standard Jazz16 device. + */ + devc->model = MDL_JAZZ; + strcpy(name, "Jazz16"); -/* - * Now we have configured a standard Jazz16 device. - */ - devc->model = MDL_JAZZ; - strcpy (name, "Jazz16"); + hw_config->name = "Jazz16"; + devc->caps |= SB_NO_MIDI; + return 1; +} +static void relocate_ess1688(sb_devc * devc) +{ + unsigned char bits; - hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc (strlen (name + 1))); - if (sound_nblocks < 1024) - sound_nblocks++;; - if (hw_config->name != NULL) - strcpy (hw_config->name, name); - devc->caps |= SB_NO_MIDI; - return 1; + switch (devc->base) + { + case 0x220: + bits = 0x04; + break; + case 0x230: + bits = 0x05; + break; + case 0x240: + bits = 0x06; + break; + case 0x250: + bits = 0x07; + break; + default: + return; /* Wrong port */ + } + + DDB(printk("Doing ESS1688 address selection\n")); + + /* + * ES1688 supports two alternative ways for software address config. + * First try the so called Read-Sequence-Key method. + */ + + /* Reset the sequence logic */ + inb(0x229); + inb(0x229); + inb(0x229); + + /* Perform the read sequence */ + inb(0x22b); + inb(0x229); + inb(0x22b); + inb(0x229); + inb(0x229); + inb(0x22b); + inb(0x229); + + /* Select the base address by reading from it. Then probe using the port. */ + inb(devc->base); + if (sb_dsp_reset(devc)) /* Bingo */ + return; + +#if 0 /* This causes system lockups (Nokia 386/25 at least) */ + /* + * The last resort is the system control register method. + */ + + outb((0x00), 0xfb); /* 0xFB is the unlock register */ + outb((0x00), 0xe0); /* Select index 0 */ + outb((bits), 0xe1); /* Write the config bits */ + outb((0x00), 0xf9); /* 0xFB is the lock register */ +#endif } -static int -ess_init (sb_devc * devc, struct address_info *hw_config) +static int ess_init(sb_devc * devc, struct address_info *hw_config) { - unsigned char cfg, irq_bits = 0, dma_bits = 0; - int ess_major = 0, ess_minor = 0; - int i; - char name[100]; + unsigned char cfg, irq_bits = 0, dma_bits = 0; + int ess_major = 0, ess_minor = 0; + int i; + static char name[100]; -/* - * Try to detect ESS chips. - */ + /* + * Try to detect ESS chips. + */ - sb_dsp_command (devc, 0xe7); /* Return identification */ + sb_dsp_command(devc, 0xe7); /* Return identification */ - for (i = 1000; i; i--) - { - if (inb (DSP_DATA_AVAIL) & 0x80) + for (i = 1000; i; i--) { - if (ess_major == 0) - ess_major = inb (DSP_READ); - else - { - ess_minor = inb (DSP_READ); - break; - } + if (inb(DSP_DATA_AVAIL) & 0x80) + { + if (ess_major == 0) + ess_major = inb(DSP_READ); + else + { + ess_minor = inb(DSP_READ); + break; + } + } } - } - - if (ess_major == 0) - return 0; - - if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) - { - sprintf (name, "ESS ES488 AudioDrive (rev %d)", - ess_minor & 0x0f); - hw_config->name = name; - devc->model = MDL_SBPRO; - return 1; - } - else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) - { - sprintf (name, - "ESS ES1688 AudioDrive (rev %d)", - ess_minor & 0x0f); - } - else - strcpy (name, "Jazz16"); - - devc->model = MDL_ESS; - devc->submodel = ess_minor & 0x0f; - - hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc (strlen (name + 1))); - if (sound_nblocks < 1024) - sound_nblocks++;; - if (hw_config->name != NULL) - strcpy (hw_config->name, name); - - - sb_dsp_reset (devc); /* Turn on extended mode */ -/* - * Set IRQ configuration register - */ + if (ess_major == 0) + return 0; - cfg = 0x50; /* Enable only DMA counter interrupt */ + if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) + { + sprintf(name, "ESS ES488 AudioDrive (rev %d)", + ess_minor & 0x0f); + hw_config->name = name; + devc->model = MDL_SBPRO; + return 1; + } + else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) + { + char *chip = "ES688"; - switch (devc->irq) - { - case 2: - case 9: - irq_bits = 0; - break; + if ((ess_minor & 0x0f) >= 8) + chip = "ES1688"; - case 5: - irq_bits = 1; - break; + sprintf(name,"ESS %s AudioDrive (rev %d)", + chip, ess_minor & 0x0f); + } + else + strcpy(name, "Jazz16"); - case 7: - irq_bits = 2; - break; + devc->model = MDL_ESS; + devc->submodel = ess_minor & 0x0f; + hw_config->name = name; + sb_dsp_reset(devc); /* Turn on extended mode */ - case 10: - irq_bits = 3; - break; + /* + * Set IRQ configuration register + */ - default: - irq_bits = 0; - cfg = 0x10; /* Disable all interrupts */ - printk ("\nESS1688: Invalid IRQ %d\n", devc->irq); - return 0; - } + cfg = 0x50; /* Enable only DMA counter interrupt */ - if (!ess_write (devc, 0xb1, cfg | (irq_bits << 2))) - printk ("\nESS1688: Failed to write to IRQ config register\n"); + switch (devc->irq) + { + case 2: + case 9: + irq_bits = 0; + break; + + case 5: + irq_bits = 1; + break; + + case 7: + irq_bits = 2; + break; + + case 10: + irq_bits = 3; + break; + + default: + irq_bits = 0; + cfg = 0x10; /* Disable all interrupts */ + printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", devc->irq); + return 0; + } -/* - * Set DMA configuration register - */ + if (!ess_write(devc, 0xb1, cfg | (irq_bits << 2))) + printk(KERN_ERR "ESS1688: Failed to write to IRQ config register\n"); - cfg = 0x50; /* Extended mode DMA enable */ - - if (devc->dma8 > 3 || devc->dma8 < 0 || devc->dma8 == 2) - { - dma_bits = 0; - cfg = 0x00; /* Disable all DMA */ - printk ("\nESS1688: Invalid DMA %d\n", devc->dma8); - } - else - { - if (devc->dma8 == 3) - dma_bits = 3; - else - dma_bits = devc->dma8 + 1; - } - - if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) - printk ("\nESS1688: Failed to write to DMA config register\n"); + /* + * Set DMA configuration register + */ -/* - * Enable joystick and OPL3 - */ + cfg = 0x50; /* Extended mode DMA enable */ - cfg = sb_getmixer (devc, 0x40); - sb_setmixer (devc, 0x40, cfg | 0x03); - if (devc->submodel >= 8) /* ES1688 */ - devc->caps |= SB_NO_MIDI; /* ES1688 uses MPU401 MIDI mode */ - sb_dsp_reset (devc); - return 1; -} + if (devc->dma8 > 3 || devc->dma8 < 0 || devc->dma8 == 2) + { + dma_bits = 0; + cfg = 0x00; /* Disable all DMA */ + printk(KERN_ERR "ESS1688: Invalid DMA %d\n", devc->dma8); + } + else + { + if (devc->dma8 == 3) + dma_bits = 3; + else + dma_bits = devc->dma8 + 1; + } -int -sb_dsp_detect (struct address_info *hw_config) -{ - sb_devc sb_info; - sb_devc *devc = &sb_info; + if (!ess_write(devc, 0xb2, cfg | (dma_bits << 2))) + printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n"); -/* - * Initialize variables - */ - DDB (printk ("sb_dsp_detect(%x) entered\n", hw_config->io_base)); - if (check_region (hw_config->io_base, 16)) - return 0; + /* + * Enable joystick and OPL3 + */ - memset ((char *) &sb_info, 0, sizeof (sb_info)); /* Zero everything */ + cfg = sb_getmixer(devc, 0x40); + sb_setmixer(devc, 0x40, cfg | 0x03); + if (devc->submodel >= 8) /* ES1688 */ + devc->caps |= SB_NO_MIDI; /* ES1688 uses MPU401 MIDI mode */ + sb_dsp_reset(devc); + return 1; +} + +int sb_dsp_detect(struct address_info *hw_config) +{ + sb_devc sb_info; + sb_devc *devc = &sb_info; + + sb_info.my_mididev = -1; + sb_info.my_mixerdev = -1; + sb_info.my_dev = -1; + + /* + * Initialize variables + */ + + DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base)); + if (check_region(hw_config->io_base, 16)) + { +#ifdef MODULE + printk(KERN_INFO "sb: I/O region in use.\n"); +#endif + return 0; + } + memset((char *) &sb_info, 0, sizeof(sb_info)); /* Zero everything */ - devc->osp = hw_config->osp; - devc->type = hw_config->card_subtype; + devc->type = hw_config->card_subtype; - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->dma8 = hw_config->dma; + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->dma8 = hw_config->dma; - devc->dma16 = -1; + devc->dma16 = -1; -/* - * Detect the device - */ + if (acer) + { + cli(); + inb(devc->base + 0x09); + inb(devc->base + 0x09); + inb(devc->base + 0x09); + inb(devc->base + 0x0b); + inb(devc->base + 0x09); + inb(devc->base + 0x0b); + inb(devc->base + 0x09); + inb(devc->base + 0x09); + inb(devc->base + 0x0b); + inb(devc->base + 0x09); + inb(devc->base + 0x00); + sti(); + } + /* + * Detect the device + */ - cli(); /* Some ESS1688 cards need this */ - inb (devc->base + 0x9); - inb (devc->base + 0x9); - inb (devc->base + 0x9); - inb (devc->base + 0xb); - inb (devc->base + 0x9); - inb (devc->base + 0xb); - inb (devc->base + 0x9); - inb (devc->base + 0x9); - inb (devc->base + 0xb); - inb (devc->base + 0x9); - inb (devc->base); - sti(); - - if (sb_dsp_reset (devc)) - dsp_get_vers (devc); - else - devc->major = 0; - - if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW) - if (devc->major == 0 || (devc->major == 3 && devc->minor == 1)) - relocate_Jazz16 (devc, hw_config); - - if (!sb_dsp_reset (devc)) - { - DDB (printk ("SB reset failed\n")); - return 0; - } - - if (devc->major == 0) - dsp_get_vers (devc); - - if (devc->major == 3 && devc->minor == 1) - if (devc->type == MDL_AZTECH) /* SG Washington? */ - { - if (sb_dsp_command (devc, 0x09)) - if (sb_dsp_command (devc, 0x00)) /* Enter WSS mode */ - { - int i; - - /* Have some delay */ - for (i = 0; i < 10000; i++) - inb (DSP_DATA_AVAIL); - devc->caps = SB_NO_AUDIO | SB_NO_MIDI; /* Mixer only */ - } - } + if (sb_dsp_reset(devc)) + dsp_get_vers(devc); + else + devc->major = 0; -/* - * Save device information for sb_dsp_init() - */ + if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW) + if (devc->major == 0 || (devc->major == 3 && devc->minor == 1)) + relocate_Jazz16(devc, hw_config); + if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0)) + relocate_ess1688(devc); - detected_devc = (sb_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (sb_devc))); - if (sound_nblocks < 1024) - sound_nblocks++;; + if (!sb_dsp_reset(devc)) + { + DDB(printk("SB reset failed\n")); +#ifdef MODULE + printk(KERN_INFO "sb: dsp reset failed.\n"); +#endif + return 0; + } + if (devc->major == 0) + dsp_get_vers(devc); - if (detected_devc == NULL) - { - printk ("sb: Can't allocate memory for device information\n"); - return 0; - } + if (devc->major == 3 && devc->minor == 1) + { + if (devc->type == MDL_AZTECH) /* SG Washington? */ + { + if (sb_dsp_command(devc, 0x09)) + if (sb_dsp_command(devc, 0x00)) /* Enter WSS mode */ + { + int i; + + /* Have some delay */ + for (i = 0; i < 10000; i++) + inb(DSP_DATA_AVAIL); + devc->caps = SB_NO_AUDIO | SB_NO_MIDI; /* Mixer only */ + devc->model = MDL_AZTECH; + } + } + } + /* + * Save device information for sb_dsp_init() + */ - memcpy ((char *) detected_devc, (char *) devc, sizeof (sb_devc)); - DDB (printk ("SB %d.%d detected OK (%x)\n", devc->major, devc->minor, - hw_config->io_base)); - return 1; + detected_devc = (sb_devc *)kmalloc(sizeof(sb_devc), GFP_KERNEL); + if (detected_devc == NULL) + { + printk(KERN_ERR "sb: Can't allocate memory for device information\n"); + return 0; + } + memcpy((char *) detected_devc, (char *) devc, sizeof(sb_devc)); + MDB(printk("SB %d.%d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base)); + return 1; } -void -sb_dsp_init (struct address_info *hw_config) +void sb_dsp_init(struct address_info *hw_config) { - sb_devc *devc; - char name[100]; - -#ifndef NO_SB_IRQ_TEST - int n; -#endif - + sb_devc *devc; + char name[100]; + extern int sb_be_quiet; + int mixer22, mixer30; + /* * Check if we had detected a SB device earlier */ - DDB (printk ("sb_dsp_init(%x) entered\n", hw_config->io_base)); - - if (detected_devc == NULL) - { - DDB (printk ("No detected device\n")); - return; - } - - devc = detected_devc; - detected_devc = NULL; + DDB(printk("sb_dsp_init(%x) entered\n", hw_config->io_base)); + name[0] = 0; - if (devc->base != hw_config->io_base) - { - DDB (printk ("I/O port mismatch\n")); - return; - } - -/* - * Now continue initialization of the device - */ - devc->dev = num_audiodevs; - devc->caps = hw_config->driver_use_1; - - if (snd_set_irq_handler (hw_config->irq, - sbintr, "sound blaster", devc->osp) < 0) - { - printk ("SB: Can't allocate IRQ%d\n", hw_config->irq); - irq2devc[hw_config->irq] = NULL; - return; - } - - irq2devc[hw_config->irq] = devc; - devc->irq_ok = 0; - - if (devc->major == 4) - if (!sb16_set_irq_hw (devc, devc->irq)) /* Unsupported IRQ */ - { - snd_release_irq (devc->irq); - return; - } - - if ((devc->type == 0 || devc->type == MDL_ESS) && - devc->major == 3 && devc->minor == 1) - { /* Handle various chipsets which claim they are SB Pro compatible */ - if ((devc->type != 0 && devc->type != MDL_ESS) || - !ess_init (devc, hw_config)) - if ((devc->type != 0 && devc->type != MDL_JAZZ && - devc->type != MDL_SMW) || !init_Jazz16 (devc, hw_config)) - { - DDB (printk ("This is a genuine SB Pro\n")); - } - } - -#ifndef NO_SB_IRQ_TEST - if (devc->major != 4 || devc->minor > 11) /* Not Sb16 v4.5 or v4.11 */ - { - for (n = 0; n < 3 && devc->irq_ok == 0; n++) - if (sb_dsp_command (devc, 0xf2)) /* Cause interrupt immediately */ - { - int i; - - for (i = 0; !devc->irq_ok && i < 10000; i++); - } - - if (!devc->irq_ok) - { - printk ("sb: Interrupt test on IRQ%d failed - device disabled\n", devc->irq); - snd_release_irq (devc->irq); - return; - } - else - { - DDB (printk ("IRQ test OK (IRQ%d)\n", devc->irq)); - } - } -#endif - - request_region (hw_config->io_base, 16, "sound blaster"); - - switch (devc->major) - { - case 1: /* SB 1.0 or 1.5 */ - devc->model = hw_config->card_subtype = MDL_SB1; - break; - - case 2: /* SB 2.x */ - if (devc->minor == 0) - devc->model = hw_config->card_subtype = MDL_SB2; - else - devc->model = hw_config->card_subtype = MDL_SB201; - break; - - case 3: /* SB Pro and most clones */ - if (devc->model == 0) + if (detected_devc == NULL) { - devc->model = hw_config->card_subtype = MDL_SBPRO; - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster Pro"; + MDB(printk("No detected device\n")); + return; } - break; - - case 4: - devc->model = hw_config->card_subtype = MDL_SB16; - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster 16"; + devc = detected_devc; + detected_devc = NULL; - if (hw_config->dma2 == -1) - devc->dma16 = devc->dma8; - else if (hw_config->dma2 < 5 || hw_config->dma2 > 7) + if (devc->base != hw_config->io_base) { - printk ("SB16: Bad or missing 16 bit DMA channel\n"); - devc->dma16 = devc->dma8; + DDB(printk("I/O port mismatch\n")); + return; + } + /* + * Now continue initialization of the device + */ + + devc->caps = hw_config->driver_use_1; + + if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && hw_config->irq > 0) + { /* IRQ setup */ + if (request_irq(hw_config->irq, sbintr, 0, "soundblaster", devc) < 0) + { + printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq); + return; + } + devc->irq_ok = 0; + + if (devc->major == 4) + if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */ + { + free_irq(devc->irq, devc); + return; + } + if ((devc->type == 0 || devc->type == MDL_ESS) && + devc->major == 3 && devc->minor == 1) + { /* Handle various chipsets which claim they are SB Pro compatible */ + if ((devc->type != 0 && devc->type != MDL_ESS) || + !ess_init(devc, hw_config)) + { + if ((devc->type != 0 && devc->type != MDL_JAZZ && + devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config)) + { + DDB(printk("This is a genuine SB Pro\n")); + } + } + } +#if defined(__SMP__) + /* Skip IRQ detection if SMP (doesn't work) */ + devc->irq_ok = 1; +#else + if (devc->major == 4 && devc->minor <= 11 ) /* Won't work */ + devc->irq_ok = 1; + else + { + int n; + + for (n = 0; n < 3 && devc->irq_ok == 0; n++) + { + if (sb_dsp_command(devc, 0xf2)) /* Cause interrupt immediately */ + { + int i; + + for (i = 0; !devc->irq_ok && i < 10000; i++); + } + } + if (!devc->irq_ok) + printk(KERN_WARNING "sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq); + else + { + DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq)); + } + } +#endif /* __SMP__ */ + } /* IRQ setup */ + request_region(hw_config->io_base, 16, "soundblaster"); + + last_sb = devc; + + switch (devc->major) + { + case 1: /* SB 1.0 or 1.5 */ + devc->model = hw_config->card_subtype = MDL_SB1; + break; + + case 2: /* SB 2.x */ + if (devc->minor == 0) + devc->model = hw_config->card_subtype = MDL_SB2; + else + devc->model = hw_config->card_subtype = MDL_SB201; + break; + + case 3: /* SB Pro and most clones */ + if (devc->model == 0) + { + devc->model = hw_config->card_subtype = MDL_SBPRO; + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster Pro (8 BIT ONLY)"; + } + break; + + case 4: + devc->model = hw_config->card_subtype = MDL_SB16; + /* + * ALS007 and ALS100 return DSP version 4.2 and have 2 post-reset !=0 + * registers at 0x3c and 0x4c (output ctrl registers on ALS007) whereas + * a "standard" SB16 doesn't have a register at 0x4c. ALS100 actively + * updates register 0x22 whenever 0x30 changes, as per the SB16 spec. + * Since ALS007 doesn't, this can be used to differentiate the 2 cards. + */ + if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c)) + { + mixer30 = sb_getmixer(devc,0x30); + sb_setmixer(devc,0x22,(mixer22=sb_getmixer(devc,0x22)) & 0x0f); + sb_setmixer(devc,0x30,0xff); + /* ALS100 will force 0x30 to 0xf8 like SB16; ALS007 will allow 0xff. */ + /* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10. */ + if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10)) + { + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster 16 (ALS-100)"; + } + else + { + sb_setmixer(devc,0x3c,0x1f); /* Enable all inputs */ + sb_setmixer(devc,0x4c,0x1f); + sb_setmixer(devc,0x22,mixer22); /* Restore 0x22 to original value */ + devc->submodel = SUBMDL_ALS007; + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster 16 (ALS-007)"; + } + sb_setmixer(devc,0x30,mixer30); + } + else if (hw_config->name == NULL) + hw_config->name = "Sound Blaster 16"; + + if (hw_config->dma2 == -1) + devc->dma16 = devc->dma8; + else if (hw_config->dma2 < 5 || hw_config->dma2 > 7) + { + printk(KERN_WARNING "SB16: Bad or missing 16 bit DMA channel\n"); + devc->dma16 = devc->dma8; + } + else + devc->dma16 = hw_config->dma2; + + sb16_set_dma_hw(devc); + devc->caps |= SB_NO_MIDI; } - else - devc->dma16 = hw_config->dma2; - - sb16_set_dma_hw (devc); - devc->caps |= SB_NO_MIDI; - } - if (!(devc->caps & SB_NO_MIXER)) - if (devc->major == 3 || devc->major == 4) - sb_mixer_init (devc); + if (!(devc->caps & SB_NO_MIXER)) + if (devc->major == 3 || devc->major == 4) + sb_mixer_init(devc); #ifdef CONFIG_MIDI - if (!(devc->caps & SB_NO_MIDI)) - sb_dsp_midi_init (devc); + if (!(devc->caps & SB_NO_MIDI)) + sb_dsp_midi_init(devc); #endif - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster"; + if (hw_config->name == NULL) + hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)"; - sprintf (name, "%s (%d.%d)", hw_config->name, devc->major, devc->minor); - conf_printf (name, hw_config); - hw_config->card_subtype = devc->model; - last_devc = devc; /* For SB MPU detection */ + sprintf(name, "%s (%d.%d)", hw_config->name, devc->major, devc->minor); + conf_printf(name, hw_config); - if (!(devc->caps & SB_NO_AUDIO)) - { - if (sound_alloc_dma (devc->dma8, "Sound Blaster8")) + /* + * Assuming that a sound card is Sound Blaster (compatible) is the most common + * configuration error and the mother of all problems. Usually sound cards + * emulate SB Pro but in addition they have a 16 bit native mode which should be + * used in Unix. See Readme.cards for more information about configuring OSS/Free + * properly. + */ + if (devc->model <= MDL_SBPRO) { - printk ("SB: Can't allocate 8 bit DMA channel %d\n", devc->dma8); + if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1 (rare ones may have 3.2). */ + { + printk(KERN_INFO "This sound card may not be fully Sound Blaster Pro compatible.\n"); + printk(KERN_INFO "In many cases there is another way to configure OSS so that\n"); + printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n"); + printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n"); + } + else if (!sb_be_quiet && devc->model == MDL_SBPRO) + { + printk(KERN_INFO "SB DSP version is just %d.%d which means that your card is\n", devc->major, devc->minor); + printk(KERN_INFO "several years old (8 bit only device) or alternatively the sound driver\n"); + printk(KERN_INFO "is incorrectly configured.\n"); + } + } + hw_config->card_subtype = devc->model; + hw_config->slots[0]=devc->dev; + last_devc = devc; /* For SB MPU detection */ + + if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0) + { + if (sound_alloc_dma(devc->dma8, "SoundBlaster8")) + { + printk(KERN_WARNING "SB: Can't allocate 8 bit DMA channel %d\n", devc->dma8); + } + if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) + { + if (sound_alloc_dma(devc->dma16, "SoundBlaster16")) + printk(KERN_WARNING "Sound Blaster: can't allocate 16 bit DMA channel %d.\n", devc->dma16); + } + sb_audio_init(devc, name); + hw_config->slots[0]=devc->dev; + } + else + { + MDB(printk("Sound Blaster: no audio devices found.\n")); } - if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) - if (sound_alloc_dma (devc->dma16, "Sound Blaster16")) - { - printk ("SB: Can't allocate 16 bit DMA channel %d\n", devc->dma16); - } - sb_audio_init (devc, name); - } } -void -sb_dsp_disable_midi (int io_base) +void sb_dsp_disable_midi(int io_base) { } -void -sb_dsp_disable_recording (int io_base) +void sb_dsp_disable_recording(int io_base) { } -void -sb_dsp_unload (struct address_info *hw_config) +void sb_dsp_unload(struct address_info *hw_config) { - sb_devc *devc; - int irq = hw_config->irq; - - if (irq < 0) - irq *= -1; + sb_devc *devc; - devc = irq2devc[irq]; + devc = audio_devs[hw_config->slots[0]]->devc; - if (devc && devc->base == hw_config->io_base) - { - release_region (devc->base, 16); - if (!(devc->caps & SB_NO_AUDIO)) + if (devc && devc->base == hw_config->io_base) { - sound_free_dma (devc->dma8); - - if (devc->dma16 >= 0) - sound_free_dma (devc->dma16); + release_region(devc->base, 16); + + if (!(devc->caps & SB_NO_AUDIO)) + { + sound_free_dma(devc->dma8); + if (devc->dma16 >= 0) + sound_free_dma(devc->dma16); + } + if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && devc->irq > 0) + { + free_irq(devc->irq, devc); + sound_unload_mixerdev(devc->my_mixerdev); + /* We don't have to do this bit any more the UART401 is its own + master -- Krzysztof Halasa */ + /* sound_unload_mididev(devc->my_mididev); */ + sound_unload_audiodev(devc->my_dev); + } + kfree(devc); } - - snd_release_irq (devc->irq); - irq2devc[devc->irq] = NULL; - } + else + release_region(hw_config->io_base, 16); + if(detected_devc) + kfree(detected_devc); } /* - * Mixer access routines + * Mixer access routines */ -void -sb_setmixer (sb_devc * devc, unsigned int port, unsigned int value) +void sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value) { - unsigned long flags; + unsigned long flags; - save_flags (flags); - cli (); - outb ((unsigned char) (port & 0xff), MIXER_ADDR); + save_flags(flags); + cli(); + outb(((unsigned char) (port & 0xff)), MIXER_ADDR); - tenmicrosec (devc->osp); - outb ((unsigned char) (value & 0xff), MIXER_DATA); - tenmicrosec (devc->osp); - restore_flags (flags); + udelay(20); + outb(((unsigned char) (value & 0xff)), MIXER_DATA); + udelay(20); + restore_flags(flags); } -unsigned int -sb_getmixer (sb_devc * devc, unsigned int port) +unsigned int sb_getmixer(sb_devc * devc, unsigned int port) { - unsigned int val; - unsigned long flags; + unsigned int val; + unsigned long flags; - save_flags (flags); - cli (); - outb ((unsigned char) (port & 0xff), MIXER_ADDR); + save_flags(flags); + cli(); + outb(((unsigned char) (port & 0xff)), MIXER_ADDR); - tenmicrosec (devc->osp); - val = inb (MIXER_DATA); - tenmicrosec (devc->osp); - restore_flags (flags); + udelay(20); + val = inb(MIXER_DATA); + udelay(20); + restore_flags(flags); - return val; + return val; } #ifdef CONFIG_MIDI + /* - * MPU401 MIDI initialization. + * MPU401 MIDI initialization. */ -static void -smw_putmem (sb_devc * devc, int base, int addr, unsigned char val) +static void smw_putmem(sb_devc * devc, int base, int addr, unsigned char val) { - unsigned long flags; + unsigned long flags; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - outb (addr & 0xff, base + 1); /* Low address bits */ - outb (addr >> 8, base + 2); /* High address bits */ - outb (val, base); /* Data */ + outb((addr & 0xff), base + 1); /* Low address bits */ + outb((addr >> 8), base + 2); /* High address bits */ + outb((val), base); /* Data */ - restore_flags (flags); + restore_flags(flags); } -static unsigned char -smw_getmem (sb_devc * devc, int base, int addr) +static unsigned char smw_getmem(sb_devc * devc, int base, int addr) { - unsigned long flags; - unsigned char val; + unsigned long flags; + unsigned char val; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - outb (addr & 0xff, base + 1); /* Low address bits */ - outb (addr >> 8, base + 2); /* High address bits */ - val = inb (base); /* Data */ + outb((addr & 0xff), base + 1); /* Low address bits */ + outb((addr >> 8), base + 2); /* High address bits */ + val = inb(base); /* Data */ - restore_flags (flags); - return val; + restore_flags(flags); + return val; } -static int -smw_midi_init (sb_devc * devc, struct address_info *hw_config) +static int smw_midi_init(sb_devc * devc, struct address_info *hw_config) { - int mpu_base = hw_config->io_base; - int mp_base = mpu_base + 4; /* Microcontroller base */ - int i; - unsigned char control; - - - /* - * Reset the microcontroller so that the RAM can be accessed - */ - - control = inb (mpu_base + 7); - outb (control | 3, mpu_base + 7); /* Set last two bits to 1 (?) */ - outb ((control & 0xfe) | 2, mpu_base + 7); /* xxxxxxx0 resets the mc */ + int mpu_base = hw_config->io_base; + int mp_base = mpu_base + 4; /* Microcontroller base */ + int i; + unsigned char control; - for (i = 0; i < 300; i++) /* Wait at least 1ms */ - tenmicrosec (devc->osp); - outb (control & 0xfc, mpu_base + 7); /* xxxxxx00 enables RAM */ + /* + * Reset the microcontroller so that the RAM can be accessed + */ - /* - * Detect microcontroller by probing the 8k RAM area - */ - smw_putmem (devc, mp_base, 0, 0x00); - smw_putmem (devc, mp_base, 1, 0xff); - tenmicrosec (devc->osp); + control = inb(mpu_base + 7); + outb((control | 3), mpu_base + 7); /* Set last two bits to 1 (?) */ + outb(((control & 0xfe) | 2), mpu_base + 7); /* xxxxxxx0 resets the mc */ - if (smw_getmem (devc, mp_base, 0) != 0x00 || smw_getmem (devc, mp_base, 1) != 0xff) - { - DDB (printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n", - smw_getmem (devc, mp_base, 0), smw_getmem (devc, mp_base, 1))); - return 0; /* No RAM */ - } + udelay(3000); /* Wait at least 1ms */ - /* - * There is RAM so assume it's really a SM Wave - */ + outb((control & 0xfc), mpu_base + 7); /* xxxxxx00 enables RAM */ - devc->model = MDL_SMW; - smw_mixer_init (devc); + /* + * Detect microcontroller by probing the 8k RAM area + */ + smw_putmem(devc, mp_base, 0, 0x00); + smw_putmem(devc, mp_base, 1, 0xff); + udelay(10); - if (smw_ucodeLen > 0) - { - if (smw_ucodeLen != 8192) + if (smw_getmem(devc, mp_base, 0) != 0x00 || smw_getmem(devc, mp_base, 1) != 0xff) { - printk ("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n"); - return 1; + DDB(printk("SM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1))); + return 0; /* No RAM */ } + /* + * There is RAM so assume it's really a SM Wave + */ - /* - * Download microcode - */ + devc->model = MDL_SMW; + smw_mixer_init(devc); - for (i = 0; i < 8192; i++) - smw_putmem (devc, mp_base, i, smw_ucode[i]); - - /* - * Verify microcode - */ - - for (i = 0; i < 8192; i++) - if (smw_getmem (devc, mp_base, i) != smw_ucode[i]) - { - printk ("SM Wave: Microcode verification failed\n"); - return 0; - } - } +#ifdef MODULE + if (!smw_ucode) + { + extern void *smw_free; - control = 0; + smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode); + smw_free = smw_ucode; + } +#endif + if (smw_ucodeLen > 0) + { + if (smw_ucodeLen != 8192) + { + printk(KERN_ERR "SM Wave: Invalid microcode (MIDI0001.BIN) length\n"); + return 1; + } + /* + * Download microcode + */ + + for (i = 0; i < 8192; i++) + smw_putmem(devc, mp_base, i, smw_ucode[i]); + + /* + * Verify microcode + */ + + for (i = 0; i < 8192; i++) + if (smw_getmem(devc, mp_base, i) != smw_ucode[i]) + { + printk(KERN_ERR "SM Wave: Microcode verification failed\n"); + return 0; + } + } + control = 0; #ifdef SMW_SCSI_IRQ - /* - * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt - * is disabled by default. - * - * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10. - */ - { - static unsigned char scsi_irq_bits[] = - {0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0}; - - control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6; - } + /* + * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt + * is disabled by default. + * + * FIXME - make this a module option + * + * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10. + */ + { + static unsigned char scsi_irq_bits[] = { + 0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0 + }; + control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6; + } #endif #ifdef SMW_OPL4_ENABLE - /* - * Make the OPL4 chip visible on the PC bus at 0x380. - * - * There is no need to enable this feature since this driver - * doesn't support OPL4 yet. Also there is no RAM in SM Wave so - * enabling OPL4 is pretty useless. - */ - control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */ - /* control |= 0x20; Uncomment this if you want to use IRQ7 */ + /* + * Make the OPL4 chip visible on the PC bus at 0x380. + * + * There is no need to enable this feature since this driver + * doesn't support OPL4 yet. Also there is no RAM in SM Wave so + * enabling OPL4 is pretty useless. + */ + control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */ + /* control |= 0x20; Uncomment this if you want to use IRQ7 */ #endif - - outb (control | 0x03, mpu_base + 7); /* xxxxxx11 restarts */ - hw_config->name = "SoundMan Wave"; - return 1; + outb((control | 0x03), mpu_base + 7); /* xxxxxx11 restarts */ + hw_config->name = "SoundMan Wave"; + return 1; } -static int -ess_midi_init (sb_devc * devc, struct address_info *hw_config) +static int ess_midi_init(sb_devc * devc, struct address_info *hw_config) { - unsigned char cfg, tmp; - - cfg = sb_getmixer (devc, 0x40) & 0x03; - - if (devc->submodel < 8) - { - sb_setmixer (devc, 0x40, cfg | 0x03); /* Enable OPL3 & joystick */ - return 0; /* ES688 doesn't support MPU401 mode */ - } - - tmp = (hw_config->io_base & 0x0f0) >> 4; - - if (tmp > 3) - { - sb_setmixer (devc, 0x40, cfg); - return 0; - } - - cfg |= tmp << 3; - - tmp = 1; /* MPU enabled without interrupts */ - - switch (hw_config->irq) - { - case 9: - tmp = 0x4; - break; - case 5: - tmp = 0x5; - break; - case 7: - tmp = 0x6; - break; - case 10: - tmp = 0x7; - break; - default: - return 0; - } - - cfg |= tmp << 5; - - sb_setmixer (devc, 0x40, cfg | 0x03); - return 1; -} + unsigned char cfg, tmp; -static int -init_Jazz16_midi (sb_devc * devc, struct address_info *hw_config) -{ - int mpu_base = hw_config->io_base; - int sb_base = devc->base; - int irq = hw_config->irq; - - unsigned char bits = 0; - unsigned long flags; - - if (irq < 0) - irq *= -1; - - if (irq < 1 || irq > 15 || - jazz_irq_bits[irq] == 0) - { - printk ("Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq); - return 0; - } - - switch (sb_base) - { - case 0x220: - bits = 1; - break; - case 0x240: - bits = 2; - break; - case 0x260: - bits = 3; - break; - - default: - return 0; - } - - bits = jazz16_bits = bits << 5; - - switch (mpu_base) - { - case 0x310: - bits |= 1; - break; - case 0x320: - bits |= 2; - break; - case 0x330: - bits |= 3; - break; - - default: - printk ("Jazz16: Invalid MIDI I/O port %x\n", mpu_base); - return 0; - } -/* - * Magic wake up sequence by writing to 0x201 (aka Joystick port) - */ - save_flags (flags); - cli (); - outb (0xAF, 0x201); - outb (0x50, 0x201); - outb (bits, 0x201); - restore_flags (flags); + cfg = sb_getmixer(devc, 0x40) & 0x03; + + if (devc->submodel < 8) + { + sb_setmixer(devc, 0x40, cfg | 0x03); /* Enable OPL3 & joystick */ + return 0; /* ES688 doesn't support MPU401 mode */ + } + tmp = (hw_config->io_base & 0x0f0) >> 4; + + if (tmp > 3) + { + sb_setmixer(devc, 0x40, cfg); + return 0; + } + cfg |= tmp << 3; + + tmp = 1; /* MPU enabled without interrupts */ + + /* May be shared: if so the value is -ve */ + + switch(abs(hw_config->irq)) + { + case 9: + tmp = 0x4; + break; + case 5: + tmp = 0x5; + break; + case 7: + tmp = 0x6; + break; + case 10: + tmp = 0x7; + break; + default: + return 0; + } - hw_config->name = "Jazz16"; - smw_midi_init (devc, hw_config); + cfg |= tmp << 5; + sb_setmixer(devc, 0x40, cfg | 0x03); + return 1; +} - if (!sb_dsp_command (devc, 0xfb)) - return 0; +static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config) +{ + int mpu_base = hw_config->io_base; + int sb_base = devc->base; + int irq = hw_config->irq; - if (!sb_dsp_command (devc, jazz_dma_bits[devc->dma8] | - (jazz_dma_bits[devc->dma16] << 4))) - return 0; + unsigned char bits = 0; + unsigned long flags; - if (!sb_dsp_command (devc, jazz_irq_bits[devc->irq] | - (jazz_irq_bits[irq] << 4))) - return 0; + if (irq < 0) + irq *= -1; - return 1; + if (irq < 1 || irq > 15 || + jazz_irq_bits[irq] == 0) + { + printk(KERN_ERR "Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq); + return 0; + } + switch (sb_base) + { + case 0x220: + bits = 1; + break; + case 0x240: + bits = 2; + break; + case 0x260: + bits = 3; + break; + default: + return 0; + } + bits = jazz16_bits = bits << 5; + switch (mpu_base) + { + case 0x310: + bits |= 1; + break; + case 0x320: + bits |= 2; + break; + case 0x330: + bits |= 3; + break; + default: + printk(KERN_ERR "Jazz16: Invalid MIDI I/O port %x\n", mpu_base); + return 0; + } + /* + * Magic wake up sequence by writing to 0x201 (aka Joystick port) + */ + save_flags(flags); + cli(); + outb(0xAF, 0x201); + outb(0x50, 0x201); + outb(bits, 0x201); + restore_flags(flags); + + hw_config->name = "Jazz16"; + smw_midi_init(devc, hw_config); + + if (!sb_dsp_command(devc, 0xfb)) + return 0; + + if (!sb_dsp_command(devc, jazz_dma_bits[devc->dma8] | + (jazz_dma_bits[devc->dma16] << 4))) + return 0; + + if (!sb_dsp_command(devc, jazz_irq_bits[devc->irq] | + (jazz_irq_bits[irq] << 4))) + return 0; + + return 1; } -void -attach_sbmpu (struct address_info *hw_config) +void attach_sbmpu(struct address_info *hw_config) { #if defined(CONFIG_MIDI) && defined(CONFIG_UART401) - attach_uart401 (hw_config); + attach_uart401(hw_config); + last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc; #endif } -int -probe_sbmpu (struct address_info *hw_config) +int probe_sbmpu(struct address_info *hw_config) { #if defined(CONFIG_MIDI) && defined(CONFIG_UART401) - sb_devc *devc = last_devc; + sb_devc *devc = last_devc; - if (last_devc == NULL) - return 0; + if (last_devc == NULL) + return 0; - last_devc = 0; + last_devc = 0; - if (hw_config->io_base <= 0) - return 0; + if (hw_config->io_base <= 0) + return 0; - if (check_region (hw_config->io_base, 4)) - { - printk ("sbmpu: I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - - switch (devc->model) - { - case MDL_SB16: - if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330) + if (check_region(hw_config->io_base, 4)) { - printk ("SB16: Invalid MIDI port %x\n", hw_config->irq); - return 0; + printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base); + return 0; } - hw_config->name = "Sound Blaster 16"; - hw_config->irq = -devc->irq; - hw_config->dma = -1; - hw_config->dma2 = -1; - sb16_set_mpu_port(devc, hw_config); - break; - - case MDL_ESS: - if (hw_config->irq < 3 || hw_config->irq == devc->irq) - hw_config->irq = -devc->irq; - if (!ess_midi_init (devc, hw_config)) - return 0; - hw_config->name = "ESS ES1688"; - break; - - case MDL_JAZZ: - if (hw_config->irq < 3 || hw_config->irq == devc->irq) - hw_config->irq = -devc->irq; - if (!init_Jazz16_midi (devc, hw_config)) - return 0; - break; - - default: - return 0; - } - - return probe_uart401 (hw_config); + switch (devc->model) + { + case MDL_SB16: + if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330) + { + printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base); + return 0; + } + hw_config->name = "Sound Blaster 16"; + hw_config->irq = -devc->irq; +#if defined(CONFIG_MIDI) && defined(CONFIG_UART401) + if (devc->minor > 12) /* What is Vibra's version??? */ + sb16_set_mpu_port(devc, hw_config); +#endif + break; + + case MDL_ESS: + if (hw_config->irq < 3 || hw_config->irq == devc->irq) + hw_config->irq = -devc->irq; + if (!ess_midi_init(devc, hw_config)) + return 0; + hw_config->name = "ESS ES1688"; + break; + + case MDL_JAZZ: + if (hw_config->irq < 3 || hw_config->irq == devc->irq) + hw_config->irq = -devc->irq; + if (!init_Jazz16_midi(devc, hw_config)) + return 0; + break; + + default: + return 0; + } + return probe_uart401(hw_config); #else - return 0; + return 0; #endif } -void -unload_sbmpu (struct address_info *hw_config) +void unload_sbmpu(struct address_info *hw_config) { #if defined(CONFIG_MIDI) && defined(CONFIG_UART401) - unload_uart401 (hw_config); + unload_uart401(hw_config); #endif } -#else /* !CONFIG_MIDI */ +#else /* !CONFIG_MIDI */ -void -unload_sbmpu (struct address_info *hw_config) +void unload_sbmpu(struct address_info *hw_config) { } -int -probe_sbmpu (struct address_info *hw_config) +int probe_sbmpu(struct address_info *hw_config) { - return 0; + return 0; } -void -attach_sbmpu (struct address_info *hw_config) +void attach_sbmpu(struct address_info *hw_config) { } #endif - - #endif diff --git a/drivers/sound/sb_midi.c b/drivers/sound/sb_midi.c index 9d129ccb37ad..970ec4a0bc85 100644 --- a/drivers/sound/sb_midi.c +++ b/drivers/sound/sb_midi.c @@ -2,20 +2,20 @@ * sound/sb_dsp.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) + * + * 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 - +#include #include "sound_config.h" -#if defined(CONFIG_SBDSP) && defined(CONFIG_MIDI) +#ifdef CONFIG_SBDSP +#ifdef CONFIG_MIDI #include "sb.h" #undef SB_TEST_IRQ @@ -28,124 +28,121 @@ * future version of this driver. */ -void (*midi_input_intr) (int dev, unsigned char data); -static int -sb_midi_open (int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) +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); - - save_flags (flags); - cli (); - if (devc->opened) - { - restore_flags (flags); - return -(EBUSY); - } - devc->opened = 1; - restore_flags (flags); - - devc->irq_mode = IMODE_MIDI; - - 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; + sb_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return -ENXIO; + + save_flags(flags); + cli(); + if (devc->opened) + { + restore_flags(flags); + return -EBUSY; + } + devc->opened = 1; + restore_flags(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) +static void sb_midi_close(int dev) { - sb_devc *devc = midi_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - return; - - save_flags (flags); - cli (); - sb_dsp_reset (devc); - devc->intr_active = 0; - devc->input_opened = 0; - devc->opened = 0; - restore_flags (flags); + sb_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return; + + save_flags(flags); + cli(); + sb_dsp_reset(devc); + devc->intr_active = 0; + devc->input_opened = 0; + devc->opened = 0; + restore_flags(flags); } -static int -sb_midi_out (int dev, unsigned char midi_byte) +static int sb_midi_out(int dev, unsigned char midi_byte) { - sb_devc *devc = midi_devs[dev]->devc; + sb_devc *devc = midi_devs[dev]->devc; - if (devc == NULL) - return -(ENXIO); + if (devc == NULL) + return 1; - sb_dsp_command (devc, midi_byte); + if (devc->midi_broken) + return 1; - 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) +static int sb_midi_start_read(int dev) { - return 0; + return 0; } -static int -sb_midi_end_read (int dev) +static int sb_midi_end_read(int dev) { - sb_devc *devc = midi_devs[dev]->devc; + sb_devc *devc = midi_devs[dev]->devc; - if (devc == NULL) - return -(ENXIO); + if (devc == NULL) + return -ENXIO; - sb_dsp_reset (devc); - devc->intr_active = 0; - return 0; + sb_dsp_reset(devc); + devc->intr_active = 0; + return 0; } -static int -sb_midi_ioctl (int dev, unsigned cmd, caddr_t arg) +static int sb_midi_ioctl(int dev, unsigned cmd, caddr_t arg) { - return -(EPERM); + return -EINVAL; } -void -sb_midi_interrupt (sb_devc * devc) +void sb_midi_interrupt(sb_devc * devc) { - unsigned long flags; - unsigned char data; + unsigned long flags; + unsigned char data; - if (devc == NULL) - return; + if (devc == NULL) + return; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - data = inb (DSP_READ); - if (devc->input_opened) - devc->midi_input_intr (devc->my_mididev, data); + data = inb(DSP_READ); + if (devc->input_opened) + devc->midi_input_intr(devc->my_mididev, data); - restore_flags (flags); + restore_flags(flags); } #define MIDI_SYNTH_NAME "Sound Blaster Midi" @@ -154,70 +151,67 @@ sb_midi_interrupt (sb_devc * devc) static struct midi_operations sb_midi_operations = { - {"Sound Blaster", 0, 0, SNDCARD_SB}, - &std_midi_synth, - {0}, - sb_midi_open, - sb_midi_close, - sb_midi_ioctl, - sb_midi_out, - sb_midi_start_read, - sb_midi_end_read, - NULL, - NULL, - NULL, - NULL + { + "Sound Blaster", 0, 0, SNDCARD_SB + }, + &std_midi_synth, + {0}, + sb_midi_open, + sb_midi_close, + sb_midi_ioctl, + sb_midi_out, + sb_midi_start_read, + sb_midi_end_read, + NULL, + NULL, + NULL, + NULL }; -void -sb_dsp_midi_init (sb_devc * devc) +void sb_dsp_midi_init(sb_devc * devc) { - if (devc->model < 2) /* No MIDI support for SB 1.x */ - return; - - if (num_midis >= MAX_MIDI_DEV) - { - printk ("Sound: Too many midi devices detected\n"); - return; - } - - std_midi_synth.midi_dev = num_midis; - devc->my_mididev = num_midis; - - std_midi_synth.midi_dev = devc->my_mididev = num_midis; - - - midi_devs[num_midis] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations))); - - if (sound_nblocks < 1024) - sound_nblocks++;; - if (midi_devs[num_midis] == NULL) - { - printk ("sb MIDI: Failed to allocate memory\n"); - return; - } - - memcpy ((char *) midi_devs[num_midis], (char *) &sb_midi_operations, - sizeof (struct midi_operations)); - - midi_devs[num_midis]->devc = devc; - - - midi_devs[num_midis]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations))); - - if (sound_nblocks < 1024) - sound_nblocks++;; - - if (midi_devs[num_midis]->converter == NULL) - { - printk ("sb MIDI: Failed to allocate memory\n"); - return; - } - - memcpy ((char *) midi_devs[num_midis]->converter, (char *) &std_midi_synth, - sizeof (struct synth_operations)); - - num_midis++; + 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 = dev; + devc->my_mididev = dev; + std_midi_synth.midi_dev = devc->my_mididev = dev; + midi_devs[dev] = (struct midi_operations *)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)); + + midi_devs[dev]->devc = devc; + + + midi_devs[dev]->converter = (struct synth_operations *)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(); } #endif +#endif diff --git a/drivers/sound/sb_mixer.c b/drivers/sound/sb_mixer.c index 7590efff9952..7c4af3d0313a 100644 --- a/drivers/sound/sb_mixer.c +++ b/drivers/sound/sb_mixer.c @@ -5,437 +5,471 @@ * The low level mixer driver for the Sound Blaster compatible cards. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 - +#include #include "sound_config.h" -#if defined(CONFIG_SBDSP) +#ifdef CONFIG_SBDSP #define __SB_MIXER_C__ #include "sb.h" #include "sb_mixer.h" -void sb_mixer_reset (sb_devc * devc); +static int sbmixnum = 1; + +static void sb_mixer_reset(sb_devc * devc); -void -sb_mixer_set_stereo (sb_devc * devc, int mode) +void sb_mixer_set_stereo(sb_devc * devc, int mode) { - sb_setmixer (devc, OUT_FILTER, ((sb_getmixer (devc, OUT_FILTER) & ~STEREO_DAC) - | (mode ? STEREO_DAC : MONO_DAC))); + sb_setmixer(devc, OUT_FILTER, ((sb_getmixer(devc, OUT_FILTER) & ~STEREO_DAC) + | (mode ? STEREO_DAC : MONO_DAC))); } -static int -detect_mixer (sb_devc * devc) +static int detect_mixer(sb_devc * devc) { - /* - * Detect the mixer by changing parameters of two volume channels. If the - * values read back match with the values written, the mixer is there (is - * it?) - */ - sb_setmixer (devc, FM_VOL, 0xff); - sb_setmixer (devc, VOC_VOL, 0x33); - - if (sb_getmixer (devc, FM_VOL) != 0xff) - return 0; - if (sb_getmixer (devc, VOC_VOL) != 0x33) - return 0; - - return 1; + /* Just trust the mixer is there */ + return 1; } -static void -change_bits (sb_devc * devc, unsigned char *regval, int dev, int chn, int newval) +static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval) { - unsigned char mask; - int shift; + unsigned char mask; + int shift; - mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1; - newval = (int) ((newval * mask) + 50) / 100; /* Scale */ + 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; + 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 */ + *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) +static int sb_mixer_get(sb_devc * devc, int dev) { - if (!((1 << dev) & devc->supported_devices)) - return -(EINVAL); - - return devc->levels[dev]; + if (!((1 << dev) & devc->supported_devices)) + return -EINVAL; + return devc->levels[dev]; } -void -smw_mixer_init (sb_devc * devc) +void smw_mixer_init(sb_devc * devc) { - int i; + int i; - sb_setmixer (devc, 0x00, 0x18); /* Mute unused (Telephone) line */ - sb_setmixer (devc, 0x10, 0x38); /* Config register 2 */ + 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_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); + devc->supported_rec_devices = devc->supported_devices & + ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME); + sb_mixer_reset(devc); } -static int -smw_mixer_set (sb_devc * devc, int dev, int value) +static int smw_mixer_set(sb_devc * devc, int dev, int value) { - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int reg, val; - - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - if (dev > 31) - return -(EINVAL); - - if (!(devc->supported_devices & (1 << dev))) /* Not supported */ - return -(EINVAL); - - 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: - 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); + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int reg, val; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + if (dev > 31) + return -EINVAL; + + if (!(devc->supported_devices & (1 << dev))) /* Not supported */ + return -EINVAL; + + 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: + 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) +static int sb_mixer_set(sb_devc * devc, int dev, int value) { - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; - int regoffs; - unsigned char val; + int regoffs; + unsigned char val; - if (devc->model == MDL_SMW) - return smw_mixer_set (devc, dev, value); + if (devc->model == MDL_SMW) + return smw_mixer_set(devc, dev, value); - if (left > 100) - left = 100; - if (right > 100) - right = 100; + if (left > 100) + left = 100; + if (right > 100) + right = 100; - if (dev > 31) - return -(EINVAL); + if (dev > 31) + return -EINVAL; - if (!(devc->supported_devices & (1 << dev))) /* - * Not supported - */ - return -(EINVAL); + if (!(devc->supported_devices & (1 << dev))) /* + * Not supported + */ + return -EINVAL; - regoffs = (*devc->iomap)[dev][LEFT_CHN].regno; + regoffs = (*devc->iomap)[dev][LEFT_CHN].regno; - if (regoffs == 0) - return -(EINVAL); + if (regoffs == 0) + return -EINVAL; - val = sb_getmixer (devc, regoffs); - change_bits (devc, &val, dev, LEFT_CHN, left); + val = sb_getmixer(devc, regoffs); + change_bits(devc, &val, dev, LEFT_CHN, left); - devc->levels[dev] = left | (left << 8); + devc->levels[dev] = left | (left << 8); - if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /* - * Change register + if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /* + * Change register + */ + { + sb_setmixer(devc, regoffs, val); /* + * Save the old one */ - { - 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 - */ + regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno; - val = sb_getmixer (devc, regoffs); /* - * Read the new one - */ - } + if (regoffs == 0) + return left | (left << 8); /* + * Just left channel present + */ - change_bits (devc, &val, dev, RIGHT_CHN, right); + val = sb_getmixer(devc, regoffs); /* + * Read the new one + */ + } + change_bits(devc, &val, dev, RIGHT_CHN, right); - sb_setmixer (devc, regoffs, val); + sb_setmixer(devc, regoffs, val); - devc->levels[dev] = left | (right << 8); - return left | (right << 8); + devc->levels[dev] = left | (right << 8); + return left | (right << 8); } -static void -set_recsrc (sb_devc * devc, int src) +static void set_recsrc(sb_devc * devc, int src) { - sb_setmixer (devc, RECORD_SRC, (sb_getmixer (devc, RECORD_SRC) & ~7) | (src & 0x7)); + sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7)); } -static int -set_recmask (sb_devc * devc, int mask) +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 (devmask != SOUND_MASK_MIC && - devmask != SOUND_MASK_LINE && - devmask != SOUND_MASK_CD) - { /* - * More than one devices selected. Drop the * + 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 (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 devices selected. Default to - * * mic + 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; + 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 sb_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + sb_devc *devc = mixer_devs[dev]->devc; + int val, ret; - if (devmask ^ devc->recmask) /* - * Input source changed - */ + /* + * Use ioctl(fd, SOUND_MIXER_PRIVATE1, &mode) to turn AGC off (0) or on (1). + */ + if (cmd == SOUND_MIXER_PRIVATE1 && devc->model == MDL_SB16) { - 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); - } + if (get_user(val, (int *)arg)) + return -EFAULT; + sb_setmixer(devc, 0x43, (~val) & 0x01); + return 0; } - - break; - - case MDL_SB16: - if (!devmask) - devmask = SOUND_MASK_MIC; - - 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; + if (((cmd >> 8) & 0xff) == 'M') + { + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + { + if (get_user(val, (int *)arg)) + return -EFAULT; + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + ret = set_recmask(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_DEVMASK: + ret = devc->supported_devices; + break; + + case SOUND_MIXER_STEREODEVS: + ret = devc->supported_devices; + 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_CAPS: + ret = devc->mixer_caps; + break; + + default: + ret = sb_mixer_get(devc, cmd & 0xff); + break; + } + return put_user(ret, (int *)arg); + } else + return -EINVAL; } -static int -sb_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg) +static struct mixer_operations sb_mixer_operations = { - sb_devc *devc = mixer_devs[dev]->devc; - - if (((cmd >> 8) & 0xff) == 'M') - { - if (_IOC_DIR (cmd) & _IOC_WRITE) - switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - return snd_ioctl_return ((int *) arg, set_recmask (devc, get_user ((int *) arg))); - break; - - default: - - return snd_ioctl_return ((int *) arg, sb_mixer_set (devc, cmd & 0xff, get_user ((int *) arg))); - } - else - switch (cmd & 0xff) - { - - case SOUND_MIXER_RECSRC: - return snd_ioctl_return ((int *) arg, devc->recmask); - break; - - case SOUND_MIXER_DEVMASK: - return snd_ioctl_return ((int *) arg, devc->supported_devices); - break; - - case SOUND_MIXER_STEREODEVS: - if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) - return snd_ioctl_return ((int *) arg, devc->supported_devices); - else - return snd_ioctl_return ((int *) arg, devc->supported_devices & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER)); - break; - - case SOUND_MIXER_RECMASK: - return snd_ioctl_return ((int *) arg, devc->supported_rec_devices); - break; - - case SOUND_MIXER_CAPS: - return snd_ioctl_return ((int *) arg, devc->mixer_caps); - break; - - default: - return snd_ioctl_return ((int *) arg, sb_mixer_get (devc, cmd & 0xff)); - } - } - else - return -(EINVAL); -} + "SB", + "Sound Blaster", + sb_mixer_ioctl +}; -static struct mixer_operations sb_mixer_operations = +static struct mixer_operations als007_mixer_operations = { - "SB", - "Sound Blaster", - sb_mixer_ioctl + "ALS007", + "Avance ALS-007", + sb_mixer_ioctl }; -void -sb_mixer_reset (sb_devc * devc) +static void sb_mixer_reset(sb_devc * devc) { - int i; + char name[32]; + int i; + extern int sm_games; + + sprintf(name, "SB_%d", devc->sbmixnum); - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - sb_mixer_set (devc, i, devc->levels[i]); - set_recmask (devc, SOUND_MASK_MIC); + if (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]); + set_recmask(devc, SOUND_MASK_MIC); } -int -sb_mixer_init (sb_devc * devc) +int sb_mixer_init(sb_devc * devc) { - int mixer_type = 0; - - sb_setmixer (devc, 0x00, 0); /* Reset mixer */ - - if (!(mixer_type = detect_mixer (devc))) - return 0; /* No mixer. Why? */ - - switch (devc->model) - { - case MDL_SBPRO: - 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; - break; - - case MDL_ESS: - devc->mixer_caps = SOUND_CAP_EXCL_INPUT; - devc->supported_devices = ES688_MIXER_DEVICES; - devc->supported_rec_devices = ES688_RECORDING_DEVICES; - devc->iomap = &es688_mix; - break; - - case MDL_SMW: - devc->mixer_caps = SOUND_CAP_EXCL_INPUT; - devc->supported_devices = 0; - devc->supported_rec_devices = 0; - devc->iomap = &sbpro_mix; - smw_mixer_init (devc); - break; - - case MDL_SB16: - devc->mixer_caps = 0; - devc->supported_devices = SB16_MIXER_DEVICES; - devc->supported_rec_devices = SB16_RECORDING_DEVICES; - devc->iomap = &sb16_mix; - break; - - default: - printk ("SB Warning: Unsupported mixer type %d\n", devc->model); - return 0; - } - - if (num_mixers >= MAX_MIXER_DEV) - return 0; - - - mixer_devs[num_mixers] = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct mixer_operations))); - - if (sound_nblocks < 1024) - sound_nblocks++;; - if (mixer_devs[num_mixers] == NULL) - { - printk ("sb_mixer: Can't allocate memory\n"); - return 0; - } - - memcpy ((char *) mixer_devs[num_mixers], (char *) &sb_mixer_operations, - sizeof (struct mixer_operations)); - - mixer_devs[num_mixers]->devc = devc; - memcpy ((char *) devc->levels, (char *) &default_levels, sizeof (default_levels)); - - sb_mixer_reset (devc); - devc->my_mixerdev = num_mixers++; - return 1; + 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_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; + break; + + case MDL_ESS: + devc->mixer_caps = SOUND_CAP_EXCL_INPUT; + devc->supported_devices = ES688_MIXER_DEVICES; + devc->supported_rec_devices = ES688_RECORDING_DEVICES; + devc->iomap = &es688_mix; + break; + + case MDL_SMW: + devc->mixer_caps = SOUND_CAP_EXCL_INPUT; + devc->supported_devices = 0; + devc->supported_rec_devices = 0; + devc->iomap = &sbpro_mix; + smw_mixer_init(devc); + break; + + case MDL_SB16: + devc->mixer_caps = 0; + devc->supported_rec_devices = SB16_RECORDING_DEVICES; + if (devc->submodel != SUBMDL_ALS007) + { + devc->supported_devices = SB16_MIXER_DEVICES; + devc->iomap = &sb16_mix; + } + else + { + devc->supported_devices = ALS007_MIXER_DEVICES; + devc->iomap = &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] = (struct mixer_operations *)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; + devc->my_mixerdev = m; + sb_mixer_reset(devc); + return 1; } #endif diff --git a/drivers/sound/sb_mixer.h b/drivers/sound/sb_mixer.h index 7635916c7dca..714b8a4d82b0 100644 --- a/drivers/sound/sb_mixer.h +++ b/drivers/sound/sb_mixer.h @@ -4,9 +4,9 @@ * Definitions for the SB Pro and SB16 mixers */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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. */ @@ -17,6 +17,10 @@ * Added defines for the Sound Galaxy NX Pro mixer. * */ +#include +#include "legacy.h" + +#ifdef CONFIG_SBDSP #define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) @@ -43,8 +47,16 @@ #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_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) /* * Mixer registers * @@ -93,11 +105,18 @@ #define LEFT_CHN 0 #define RIGHT_CHN 1 +/* + * 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}} #ifdef __SB_MIXER_C__ -mixer_tab sbpro_mix = { +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), @@ -111,7 +130,7 @@ 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) }; -mixer_tab es688_mix = { +static mixer_tab es688_mix = { MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4), MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), @@ -132,7 +151,8 @@ MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) }; #ifdef __SGNXPRO__ -mixer_tab sgnxpro_mix = { +#if 0 +static mixer_tab sgnxpro_mix = { /* not used anywhere */ MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), MIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0), MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0), @@ -149,8 +169,9 @@ MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0) }; #endif +#endif -mixer_tab sb16_mix = { +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), @@ -160,18 +181,37 @@ 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, 0x00, 0, 0, 0x00, 0, 0), +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) }; -#ifdef SM_GAMES /* Master volume is lower and PCM & FM volumes +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, 6, 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 unsigned short default_levels[SOUND_MIXER_NRDEVICES] = +static int smg_default_levels[32] = { 0x2020, /* Master Volume */ 0x4b4b, /* Bass */ @@ -192,9 +232,7 @@ static unsigned short default_levels[SOUND_MIXER_NRDEVICES] = 0x1515 /* Line3 */ }; -#else /* If the user selected just plain SB Pro */ - -static unsigned short default_levels[SOUND_MIXER_NRDEVICES] = +static int sb_default_levels[32] = { 0x5a5a, /* Master Volume */ 0x4b4b, /* Bass */ @@ -205,7 +243,7 @@ static unsigned short default_levels[SOUND_MIXER_NRDEVICES] = 0x4b4b, /* Ext Line */ 0x1010, /* Mic */ 0x4b4b, /* CD */ - 0x4b4b, /* Recording monitor */ + 0x0000, /* Recording monitor */ 0x4b4b, /* SB PCM */ 0x4b4b, /* Recording level */ 0x4b4b, /* Input gain */ @@ -214,7 +252,6 @@ static unsigned short default_levels[SOUND_MIXER_NRDEVICES] = 0x4040, /* Line2 */ 0x1515 /* Line3 */ }; -#endif /* SM_GAMES */ static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = { @@ -281,4 +318,14 @@ static char smw_mix_regs[] = /* Left mixer registers */ #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 + +#endif #endif diff --git a/drivers/sound/sequencer.c b/drivers/sound/sequencer.c index 9f6a3ae6cd71..0bc6e9e0481b 100644 --- a/drivers/sound/sequencer.c +++ b/drivers/sound/sequencer.c @@ -4,19 +4,29 @@ * The sequencer personality manager. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 : reformatted and fixed a pair of null pointer bugs + */ #include +#ifdef CONFIG_KERNELD +#include +#endif + #define SEQUENCER_C #include "sound_config.h" #ifdef CONFIG_SEQUENCER +#include "softoss.h" +int (*softsynthp) (int cmd, int parm1, int parm2, unsigned long parm3) = NULL; #include "midi_ctrl.h" @@ -24,6 +34,9 @@ static int sequencer_ok = 0; static struct sound_timer_operations *tmr; static int tmr_no = -1; /* Currently selected timer */ static int pending_timer = -1; /* For timer change operation */ +extern unsigned long seq_time; + +static int obsolete_api_used = 0; /* * Local counts for number of synth and MIDI devices. These are initialized @@ -42,26 +55,25 @@ static int max_synthdev = 0; #define SEQ_2 2 static int seq_mode = SEQ_1; -static wait_handle *seq_sleeper = NULL; -static volatile struct snd_wait seq_sleep_flag = -{0}; -static wait_handle *midi_sleeper = NULL; -static volatile struct snd_wait midi_sleep_flag = -{0}; +static struct wait_queue *seq_sleeper = NULL; +static struct wait_queue *midi_sleeper = NULL; + +static int midi_opened[MAX_MIDI_DEV] = { + 0 +}; -static int midi_opened[MAX_MIDI_DEV] = -{0}; -static int midi_written[MAX_MIDI_DEV] = -{0}; +static int midi_written[MAX_MIDI_DEV] = { + 0 +}; -unsigned long prev_input_time = 0; -int prev_event_time; -unsigned long seq_time = 0; +static unsigned long prev_input_time = 0; +static int prev_event_time; #include "tuning.h" #define EV_SZ 8 #define IEV_SZ 8 + static unsigned char *queue = NULL; static unsigned char *iqueue = NULL; @@ -73,1980 +85,1674 @@ static int output_threshold; static int pre_event_timeout; static unsigned synth_open_mask; -static int seq_queue (unsigned char *note, char nonblock); -static void seq_startplay (void); -static int seq_sync (void); -static void seq_reset (void); -static int pmgr_present[MAX_SYNTH_DEV] = -{0}; +static int seq_queue(unsigned char *note, char nonblock); +static void seq_startplay(void); +static int seq_sync(void); +static void seq_reset(void); #if MAX_SYNTH_DEV > 15 #error Too many synthesizer devices enabled. #endif -int -sequencer_read (int dev, struct fileinfo *file, char *buf, int count) +int sequencer_read(int dev, struct file *file, char *buf, int count) { - int c = count, p = 0; - int ev_len; - unsigned long flags; - - dev = dev >> 4; - - ev_len = seq_mode == SEQ_1 ? 4 : 8; + int c = count, p = 0; + int ev_len; + unsigned long flags; - if (dev) /* - * Patch manager device - */ - return pmgr_read (dev - 1, file, buf, count); - - save_flags (flags); - cli (); - if (!iqlen) - { - if ((file->flags & (O_NONBLOCK) ? - 1 : 0)) - { - restore_flags (flags); - return -(EAGAIN); - } + dev = dev >> 4; + ev_len = seq_mode == SEQ_1 ? 4 : 8; - { - unsigned long tlimit; + save_flags(flags); + cli(); - if (pre_event_timeout) - current_set_timeout (tlimit = jiffies + (pre_event_timeout)); - else - tlimit = (unsigned long) -1; - midi_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&midi_sleeper); - if (!(midi_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - midi_sleep_flag.flags |= WK_TIMEOUT; - } - midi_sleep_flag.flags &= ~WK_SLEEP; - }; - - if (!iqlen) + if (!iqlen) { - restore_flags (flags); - return 0; + if (file->f_flags & O_NONBLOCK) { + restore_flags(flags); + return -EAGAIN; + } + current->timeout = pre_event_timeout ? jiffies + pre_event_timeout : 0; + interruptible_sleep_on(&midi_sleeper); + current->timeout = 0; + if (!iqlen) + { + restore_flags(flags); + return 0; + } } - } - - while (iqlen && c >= ev_len) - { - - memcpy_tofs (&(buf)[p], (char *) &iqueue[iqhead * IEV_SZ], ev_len); - p += ev_len; - c -= ev_len; - - iqhead = (iqhead + 1) % SEQ_MAX_QUEUE; - iqlen--; - } - restore_flags (flags); + while (iqlen && c >= ev_len) + { + char *fixit = (char *) &iqueue[iqhead * IEV_SZ]; + copy_to_user(&(buf)[p], fixit, ev_len); + p += ev_len; + c -= ev_len; - return count - c; + iqhead = (iqhead + 1) % SEQ_MAX_QUEUE; + iqlen--; + } + restore_flags(flags); + return count - c; } -static void -sequencer_midi_output (int dev) +static void sequencer_midi_output(int dev) { - /* - * Currently NOP - */ + /* + * Currently NOP + */ } -void -seq_copy_to_input (unsigned char *event_rec, int len) +void seq_copy_to_input(unsigned char *event_rec, int len) { - unsigned long flags; - - /* - * Verify that the len is valid for the current mode. - */ - - if (len != 4 && len != 8) - return; - if ((seq_mode == SEQ_1) != (len == 4)) - return; - - if (iqlen >= (SEQ_MAX_QUEUE - 1)) - return; /* Overflow */ - - save_flags (flags); - cli (); - memcpy (&iqueue[iqtail * IEV_SZ], event_rec, len); - iqlen++; - iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; - - if ((midi_sleep_flag.flags & WK_SLEEP)) - { - { - midi_sleep_flag.flags = WK_WAKEUP; - module_wake_up (&midi_sleeper); - }; - } - restore_flags (flags); + unsigned long flags; + + /* + * Verify that the len is valid for the current mode. + */ + + if (len != 4 && len != 8) + return; + if ((seq_mode == SEQ_1) != (len == 4)) + return; + + if (iqlen >= (SEQ_MAX_QUEUE - 1)) + return; /* Overflow */ + + save_flags(flags); + cli(); + memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len); + iqlen++; + iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; + wake_up(&midi_sleeper); + restore_flags(flags); } -static void -sequencer_midi_input (int dev, unsigned char data) +static void sequencer_midi_input(int dev, unsigned char data) { - unsigned int tstamp; - unsigned char event_rec[4]; + unsigned int tstamp; + unsigned char event_rec[4]; - if (data == 0xfe) /* Ignore active sensing */ - return; + if (data == 0xfe) /* Ignore active sensing */ + return; - tstamp = jiffies - seq_time; - if (tstamp != prev_input_time) - { - tstamp = (tstamp << 8) | SEQ_WAIT; - - seq_copy_to_input ((unsigned char *) &tstamp, 4); - prev_input_time = tstamp; - } + if (softsynthp != NULL) + tstamp = softsynthp(SSYN_GETTIME, 0, 0, 0); + else + tstamp = jiffies - seq_time; - event_rec[0] = SEQ_MIDIPUTC; - event_rec[1] = data; - event_rec[2] = dev; - event_rec[3] = 0; + if (tstamp != prev_input_time) + { + tstamp = (tstamp << 8) | SEQ_WAIT; + seq_copy_to_input((unsigned char *) &tstamp, 4); + prev_input_time = tstamp; + } + event_rec[0] = SEQ_MIDIPUTC; + event_rec[1] = data; + event_rec[2] = dev; + event_rec[3] = 0; - seq_copy_to_input (event_rec, 4); + seq_copy_to_input(event_rec, 4); } -void -seq_input_event (unsigned char *event_rec, int len) +void seq_input_event(unsigned char *event_rec, int len) { - unsigned long this_time; - - if (seq_mode == SEQ_2) - this_time = tmr->get_time (tmr_no); - else - this_time = jiffies - seq_time; + unsigned long this_time; - if (this_time != prev_input_time) - { - unsigned char tmp_event[8]; + if (seq_mode == SEQ_2) + this_time = tmr->get_time(tmr_no); + else if (softsynthp != NULL) + this_time = softsynthp(SSYN_GETTIME, 0, 0, 0); + else + this_time = jiffies - seq_time; - tmp_event[0] = EV_TIMING; - tmp_event[1] = TMR_WAIT_ABS; - tmp_event[2] = 0; - tmp_event[3] = 0; - *(unsigned int *) &tmp_event[4] = this_time; + if (this_time != prev_input_time) + { + unsigned char tmp_event[8]; - seq_copy_to_input (tmp_event, 8); - prev_input_time = this_time; - } + tmp_event[0] = EV_TIMING; + tmp_event[1] = TMR_WAIT_ABS; + tmp_event[2] = 0; + tmp_event[3] = 0; + *(unsigned int *) &tmp_event[4] = this_time; - seq_copy_to_input (event_rec, len); + seq_copy_to_input(tmp_event, 8); + prev_input_time = this_time; + } + seq_copy_to_input(event_rec, len); } -int -sequencer_write (int dev, struct fileinfo *file, const char *buf, int count) +int sequencer_write(int dev, struct file *file, const char *buf, int count) { - unsigned char event_rec[EV_SZ], ev_code; - int p = 0, c, ev_size; - int err; - int mode = file->mode & O_ACCMODE; - - dev = dev >> 4; - - DEB (printk ("sequencer_write(dev=%d, count=%d)\n", dev, count)); - - if (mode == OPEN_READ) - return -(EIO); - - if (dev) - return pmgr_write (dev - 1, file, buf, count); - - c = count; + unsigned char event_rec[EV_SZ], ev_code; + int p = 0, c, ev_size; + int err; + int mode = translate_mode(file); - while (c >= 4) - { - memcpy_fromfs ((char *) event_rec, &(buf)[p], 4); - ev_code = event_rec[0]; + dev = dev >> 4; - if (ev_code == SEQ_FULLSIZE) - { - int err; - - dev = *(unsigned short *) &event_rec[2]; - if (dev < 0 || dev >= max_synthdev) - return -(ENXIO); - - if (!(synth_open_mask & (1 << dev))) - return -(ENXIO); + DEB(printk("sequencer_write(dev=%d, count=%d)\n", dev, count)); - err = synth_devs[dev]->load_patch (dev, *(short *) &event_rec[0], buf, p + 4, c, 0); - if (err < 0) - return err; + if (mode == OPEN_READ) + return -EIO; - return err; - } + c = count; - if (ev_code >= 128) + while (c >= 4) { - if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) - { - printk ("Sequencer: Invalid level 2 event %x\n", ev_code); - return -(EINVAL); - } + copy_from_user((char *) event_rec, &(buf)[p], 4); + ev_code = event_rec[0]; - ev_size = 8; + if (ev_code == SEQ_FULLSIZE) + { + int err, fmt; - if (c < ev_size) - { - if (!seq_playing) - seq_startplay (); - return count - c; - } + dev = *(unsigned short *) &event_rec[2]; + if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL) + return -ENXIO; - memcpy_fromfs ((char *) &event_rec[4], &(buf)[p + 4], 4); + if (!(synth_open_mask & (1 << dev))) + return -ENXIO; - } - else - { - if (seq_mode == SEQ_2) - { - printk ("Sequencer: 4 byte event in level 2 mode\n"); - return -(EINVAL); - } - ev_size = 4; - } + fmt = (*(short *) &event_rec[0]) & 0xffff; + err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0); + if (err < 0) + return err; - if (event_rec[0] == SEQ_MIDIPUTC) - { - - if (!midi_opened[event_rec[2]]) - { - int mode; - int dev = event_rec[2]; + return err; + } + if (ev_code >= 128) + { + if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) + { + printk(KERN_WARNING "Sequencer: Invalid level 2 event %x\n", ev_code); + return -EINVAL; + } + ev_size = 8; + + if (c < ev_size) + { + if (!seq_playing) + seq_startplay(); + return count - c; + } + copy_from_user((char *) &event_rec[4], &(buf)[p + 4], 4); - if (dev >= max_mididev) + } + else { - printk ("Sequencer Error: Nonexistent MIDI device %d\n", dev); - return -(ENXIO); + if (seq_mode == SEQ_2) + { + printk(KERN_WARNING "Sequencer: 4 byte event in level 2 mode\n"); + return -EINVAL; + } + ev_size = 4; + + if (event_rec[0] != SEQ_MIDIPUTC) + obsolete_api_used = 1; } - mode = file->mode & O_ACCMODE; - - if ((err = midi_devs[dev]->open (dev, mode, - sequencer_midi_input, sequencer_midi_output)) < 0) + if (event_rec[0] == SEQ_MIDIPUTC) { - seq_reset (); - printk ("Sequencer Error: Unable to open Midi #%d\n", dev); - return err; + if (!midi_opened[event_rec[2]]) + { + int mode; + int dev = event_rec[2]; + + if (dev >= max_mididev || midi_devs[dev]==NULL) + { + /*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/ + return -ENXIO; + } + mode = translate_mode(file); + + if ((err = midi_devs[dev]->open(dev, mode, + sequencer_midi_input, sequencer_midi_output)) < 0) + { + seq_reset(); + printk(KERN_WARNING "Sequencer Error: Unable to open Midi #%d\n", dev); + return err; + } + midi_opened[dev] = 1; + } } + if (!seq_queue(event_rec, (file->f_flags & (O_NONBLOCK) ? 1 : 0))) + { + int processed = count - c; - midi_opened[dev] = 1; - } - - } - - if (!seq_queue (event_rec, (file->flags & (O_NONBLOCK) ? - 1 : 0))) - { - int processed = count - c; - - if (!seq_playing) - seq_startplay (); + if (!seq_playing) + seq_startplay(); - if (!processed && (file->flags & (O_NONBLOCK) ? - 1 : 0)) - return -(EAGAIN); - else - return processed; + if (!processed && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + else + return processed; + } + p += ev_size; + c -= ev_size; } - p += ev_size; - c -= ev_size; - } - - if (!seq_playing) - seq_startplay (); + if (!seq_playing) + seq_startplay(); - return count; /* This will "eat" chunks shorter than 4 bytes (if written - * alone) Should we really do that ? - */ + return count; } -static int -seq_queue (unsigned char *note, char nonblock) +static int seq_queue(unsigned char *note, char nonblock) { - /* - * Test if there is space in the queue - */ - - if (qlen >= SEQ_MAX_QUEUE) - if (!seq_playing) - seq_startplay (); /* - * Give chance to drain the queue - */ - - if (!nonblock && qlen >= SEQ_MAX_QUEUE && !(seq_sleep_flag.flags & WK_SLEEP)) - { - /* - * Sleep until there is enough space on the queue - */ - - seq_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&seq_sleeper); - seq_sleep_flag.flags &= ~WK_SLEEP;; - } - - if (qlen >= SEQ_MAX_QUEUE) - { - return 0; /* + /* + * Test if there is space in the queue + */ + + if (qlen >= SEQ_MAX_QUEUE) + if (!seq_playing) + seq_startplay(); /* + * Give chance to drain the queue + */ + + if (!nonblock && qlen >= SEQ_MAX_QUEUE && !waitqueue_active(&seq_sleeper)) { + /* + * Sleep until there is enough space on the queue + */ + current->timeout = 0; + interruptible_sleep_on(&seq_sleeper); + } + if (qlen >= SEQ_MAX_QUEUE) + { + return 0; /* * To be sure */ - } - memcpy (&queue[qtail * EV_SZ], note, EV_SZ); + } + memcpy(&queue[qtail * EV_SZ], note, EV_SZ); - qtail = (qtail + 1) % SEQ_MAX_QUEUE; - qlen++; + qtail = (qtail + 1) % SEQ_MAX_QUEUE; + qlen++; - return 1; + return 1; } -static int -extended_event (unsigned char *q) +static int extended_event(unsigned char *q) { - int dev = q[2]; + int dev = q[2]; - if (dev < 0 || dev >= max_synthdev) - return -(ENXIO); - - if (!(synth_open_mask & (1 << dev))) - return -(ENXIO); + if (dev < 0 || dev >= max_synthdev) + return -ENXIO; - switch (q[1]) - { - case SEQ_NOTEOFF: - synth_devs[dev]->kill_note (dev, q[3], q[4], q[5]); - break; + if (!(synth_open_mask & (1 << dev))) + return -ENXIO; - case SEQ_NOTEON: - if (q[4] > 127 && q[4] != 255) + switch (q[1]) + { + case SEQ_NOTEOFF: + synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]); + break; + + case SEQ_NOTEON: + if (q[4] > 127 && q[4] != 255) + return 0; + + if (q[5] == 0) + { + synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]); + break; + } + synth_devs[dev]->start_note(dev, q[3], q[4], q[5]); + break; + + case SEQ_PGMCHANGE: + synth_devs[dev]->set_instr(dev, q[3], q[4]); + break; + + case SEQ_AFTERTOUCH: + synth_devs[dev]->aftertouch(dev, q[3], q[4]); + break; + + case SEQ_BALANCE: + synth_devs[dev]->panning(dev, q[3], (char) q[4]); + break; + + case SEQ_CONTROLLER: + synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8))); + break; + + case SEQ_VOLMODE: + if (synth_devs[dev]->volume_method != NULL) + synth_devs[dev]->volume_method(dev, q[3]); + break; + + default: + return -EINVAL; + } return 0; - - synth_devs[dev]->start_note (dev, q[3], q[4], q[5]); - break; - - case SEQ_PGMCHANGE: - synth_devs[dev]->set_instr (dev, q[3], q[4]); - break; - - case SEQ_AFTERTOUCH: - synth_devs[dev]->aftertouch (dev, q[3], q[4]); - break; - - case SEQ_BALANCE: - synth_devs[dev]->panning (dev, q[3], (char) q[4]); - break; - - case SEQ_CONTROLLER: - synth_devs[dev]->controller (dev, q[3], q[4], (short) (q[5] | (q[6] << 8))); - break; - - case SEQ_VOLMODE: - if (synth_devs[dev]->volume_method != NULL) - synth_devs[dev]->volume_method (dev, q[3]); - break; - - default: - return -(EINVAL); - } - - return 0; } -static int -find_voice (int dev, int chn, int note) +static int find_voice(int dev, int chn, int note) { - unsigned short key; - int i; - - key = (chn << 8) | (note + 1); - - for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) - if (synth_devs[dev]->alloc.map[i] == key) - return i; - - return -1; + unsigned short key; + int i; + + key = (chn << 8) | (note + 1); + for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) + if (synth_devs[dev]->alloc.map[i] == key) + return i; + return -1; } -static int -alloc_voice (int dev, int chn, int note) +static int alloc_voice(int dev, int chn, int note) { - unsigned short key; - int voice; + unsigned short key; + int voice; - key = (chn << 8) | (note + 1); + key = (chn << 8) | (note + 1); - voice = synth_devs[dev]->alloc_voice (dev, chn, note, - &synth_devs[dev]->alloc); - synth_devs[dev]->alloc.map[voice] = key; - synth_devs[dev]->alloc.alloc_times[voice] = - synth_devs[dev]->alloc.timestamp++; - return voice; + voice = synth_devs[dev]->alloc_voice(dev, chn, note, + &synth_devs[dev]->alloc); + synth_devs[dev]->alloc.map[voice] = key; + synth_devs[dev]->alloc.alloc_times[voice] = + synth_devs[dev]->alloc.timestamp++; + return voice; } -static void -seq_chn_voice_event (unsigned char *event_rec) +static void seq_chn_voice_event(unsigned char *event_rec) { - unsigned char dev = event_rec[1]; - unsigned char cmd = event_rec[2]; - unsigned char chn = event_rec[3]; - unsigned char note = event_rec[4]; - unsigned char parm = event_rec[5]; - int voice = -1; - - if ((int) dev > max_synthdev) - return; - if (!(synth_open_mask & (1 << dev))) - return; - if (!synth_devs[dev]) - return; - - if (seq_mode == SEQ_2) - { - if (synth_devs[dev]->alloc_voice) - voice = find_voice (dev, chn, note); - - if (cmd == MIDI_NOTEON && parm == 0) - { - cmd = MIDI_NOTEOFF; - parm = 64; - } - } +#define dev event_rec[1] +#define cmd event_rec[2] +#define chn event_rec[3] +#define note event_rec[4] +#define parm event_rec[5] - switch (cmd) - { - case MIDI_NOTEON: - if (note > 127 && note != 255) /* Not a seq2 feature */ - return; + int voice = -1; - if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice) - { /* Internal synthesizer (FM, GUS, etc) */ - voice = alloc_voice (dev, chn, note); - } - - if (voice == -1) - voice = chn; + if ((int) dev > max_synthdev || synth_devs[dev] == NULL) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; - if (seq_mode == SEQ_2 && (int) dev < num_synths) + if (seq_mode == SEQ_2) { - /* - * The MIDI channel 10 is a percussive channel. Use the note - * number to select the proper patch (128 to 255) to play. - */ - - if (chn == 9) - { - synth_devs[dev]->set_instr (dev, voice, 128 + note); - synth_devs[dev]->chn_info[chn].pgm_num = 128 + note; - note = 60; /* Middle C */ - - } + if (synth_devs[dev]->alloc_voice) + voice = find_voice(dev, chn, note); + + if (cmd == MIDI_NOTEON && parm == 0) + { + cmd = MIDI_NOTEOFF; + parm = 64; + } } - if (seq_mode == SEQ_2) + switch (cmd) { - synth_devs[dev]->setup_voice (dev, voice, chn); + case MIDI_NOTEON: + if (note > 127 && note != 255) /* Not a seq2 feature */ + return; + + if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice) + { + /* Internal synthesizer (FM, GUS, etc) */ + voice = alloc_voice(dev, chn, note); + } + if (voice == -1) + voice = chn; + + if (seq_mode == SEQ_2 && (int) dev < num_synths) + { + /* + * The MIDI channel 10 is a percussive channel. Use the note + * number to select the proper patch (128 to 255) to play. + */ + + if (chn == 9) + { + synth_devs[dev]->set_instr(dev, voice, 128 + note); + synth_devs[dev]->chn_info[chn].pgm_num = 128 + note; + } + synth_devs[dev]->setup_voice(dev, voice, chn); + } + synth_devs[dev]->start_note(dev, voice, note, parm); + break; + + case MIDI_NOTEOFF: + if (voice == -1) + voice = chn; + synth_devs[dev]->kill_note(dev, voice, note, parm); + break; + + case MIDI_KEY_PRESSURE: + if (voice == -1) + voice = chn; + synth_devs[dev]->aftertouch(dev, voice, parm); + break; + + default: } +#undef dev +#undef cmd +#undef chn +#undef note +#undef parm +} - synth_devs[dev]->start_note (dev, voice, note, parm); - break; - case MIDI_NOTEOFF: - if (voice == -1) - voice = chn; - synth_devs[dev]->kill_note (dev, voice, note, parm); - break; +static void seq_chn_common_event(unsigned char *event_rec) +{ + unsigned char dev = event_rec[1]; + unsigned char cmd = event_rec[2]; + unsigned char chn = event_rec[3]; + unsigned char p1 = event_rec[4]; - case MIDI_KEY_PRESSURE: - if (voice == -1) - voice = chn; - synth_devs[dev]->aftertouch (dev, voice, parm); - break; + /* unsigned char p2 = event_rec[5]; */ + unsigned short w14 = *(short *) &event_rec[6]; - default:; - } -} + if ((int) dev > max_synthdev || synth_devs[dev] == NULL) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; -static void -seq_chn_common_event (unsigned char *event_rec) -{ - unsigned char dev = event_rec[1]; - unsigned char cmd = event_rec[2]; - unsigned char chn = event_rec[3]; - unsigned char p1 = event_rec[4]; - - /* unsigned char p2 = event_rec[5]; */ - unsigned short w14 = *(short *) &event_rec[6]; - - if ((int) dev > max_synthdev) - return; - if (!(synth_open_mask & (1 << dev))) - return; - if (!synth_devs[dev]) - return; - - switch (cmd) - { - case MIDI_PGM_CHANGE: - if (seq_mode == SEQ_2) + switch (cmd) { - synth_devs[dev]->chn_info[chn].pgm_num = p1; - if ((int) dev >= num_synths) - synth_devs[dev]->set_instr (dev, chn, p1); + case MIDI_PGM_CHANGE: + if (seq_mode == SEQ_2) + { + synth_devs[dev]->chn_info[chn].pgm_num = p1; + if ((int) dev >= num_synths) + synth_devs[dev]->set_instr(dev, chn, p1); + } + else + synth_devs[dev]->set_instr(dev, chn, p1); + + break; + + case MIDI_CTL_CHANGE: + if (seq_mode == SEQ_2) + { + if (chn > 15 || p1 > 127) + break; + + synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f; + + if (p1 < 32) /* Setting MSB should clear LSB to 0 */ + synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0; + + if ((int) dev < num_synths) + { + int val = w14 & 0x7f; + int i, key; + + if (p1 < 64) /* Combine MSB and LSB */ + { + val = ((synth_devs[dev]-> + chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7) + | (synth_devs[dev]-> + chn_info[chn].controllers[p1 | 32] & 0x7f); + p1 &= ~32; + } + /* Handle all playing notes on this channel */ + + key = ((int) chn << 8); + + for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) + if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) + synth_devs[dev]->controller(dev, i, p1, val); + } + else + synth_devs[dev]->controller(dev, chn, p1, w14); + } + else /* Mode 1 */ + synth_devs[dev]->controller(dev, chn, p1, w14); + break; + + case MIDI_PITCH_BEND: + if (seq_mode == SEQ_2) + { + synth_devs[dev]->chn_info[chn].bender_value = w14; + + if ((int) dev < num_synths) + { + /* Handle all playing notes on this channel */ + int i, key; + + key = (chn << 8); + + for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) + if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) + synth_devs[dev]->bender(dev, i, w14); + } + else + synth_devs[dev]->bender(dev, chn, w14); + } + else /* MODE 1 */ + synth_devs[dev]->bender(dev, chn, w14); + break; + + default: } - else - synth_devs[dev]->set_instr (dev, chn, p1); +} - break; +static int seq_timing_event(unsigned char *event_rec) +{ + unsigned char cmd = event_rec[1]; + unsigned int parm = *(int *) &event_rec[4]; - case MIDI_CTL_CHANGE: - if (seq_mode == SEQ_2) + if (seq_mode == SEQ_2) { - if (chn > 15 || p1 > 127) - break; - - synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f; + int ret; - if (p1 < 32) /* Setting MSB should clear LSB to 0 */ - synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0; - - if ((int) dev < num_synths) - { - int val = w14 & 0x7f; - int i, key; - - if (p1 < 64) /* Combine MSB and LSB */ - { - val = ((synth_devs[dev]-> - chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7) - | (synth_devs[dev]-> - chn_info[chn].controllers[p1 | 32] & 0x7f); - p1 &= ~32; - } - - /* Handle all playing notes on this channel */ - - key = ((int) chn << 8); - - for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) - if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) - synth_devs[dev]->controller (dev, i, p1, val); - } - else - synth_devs[dev]->controller (dev, chn, p1, w14); + if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED) + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); + return ret; } - else /* Mode 1 */ - synth_devs[dev]->controller (dev, chn, p1, w14); - break; - - case MIDI_PITCH_BEND: - if (seq_mode == SEQ_2) + switch (cmd) { - synth_devs[dev]->chn_info[chn].bender_value = w14; - - if ((int) dev < num_synths) - { /* Handle all playing notes on this channel */ - int i, key; - - key = (chn << 8); - - for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) - if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) - synth_devs[dev]->bender (dev, i, w14); - } - else - synth_devs[dev]->bender (dev, chn, w14); + case TMR_WAIT_REL: + parm += prev_event_time; + + /* + * NOTE! No break here. Execution of TMR_WAIT_REL continues in the + * next case (TMR_WAIT_ABS) + */ + + case TMR_WAIT_ABS: + if (parm > 0) + { + long time; + + time = parm; + prev_event_time = time; + + seq_playing = 1; + if (softsynthp != NULL) + softsynthp(SSYN_REQUEST, time, 0, 0); + else + request_sound_timer(time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); + return TIMER_ARMED; + } + break; + + case TMR_START: + if (softsynthp != NULL) + { + softsynthp(SSYN_START, 0, 0, 0); + seq_time = 0; + } + else + seq_time = jiffies; + prev_input_time = 0; + prev_event_time = 0; + break; + + case TMR_STOP: + break; + + case TMR_CONTINUE: + break; + + case TMR_TEMPO: + break; + + case TMR_ECHO: + if (seq_mode == SEQ_2) + seq_copy_to_input(event_rec, 8); + else + { + parm = (parm << 8 | SEQ_ECHO); + seq_copy_to_input((unsigned char *) &parm, 4); + } + break; + + default: } - else /* MODE 1 */ - synth_devs[dev]->bender (dev, chn, w14); - break; - default:; - } + return TIMER_NOT_ARMED; } -static int -seq_timing_event (unsigned char *event_rec) +static void seq_local_event(unsigned char *event_rec) { - unsigned char cmd = event_rec[1]; - unsigned int parm = *(int *) &event_rec[4]; + unsigned char cmd = event_rec[1]; + unsigned int parm = *((unsigned int *) &event_rec[4]); - if (seq_mode == SEQ_2) - { - int ret; - - if ((ret = tmr->event (tmr_no, event_rec)) == TIMER_ARMED) + switch (cmd) { - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - { - unsigned long flags; + case LOCL_STARTAUDIO: +#ifdef CONFIG_AUDIO + DMAbuf_start_devices(parm); +#endif + break; - save_flags (flags); - cli (); - if ((seq_sleep_flag.flags & WK_SLEEP)) - { - { - seq_sleep_flag.flags = WK_WAKEUP; - module_wake_up (&seq_sleeper); - }; - } - restore_flags (flags); - } + default: } - return ret; - } +} - switch (cmd) - { - case TMR_WAIT_REL: - parm += prev_event_time; +static void seq_sysex_message(unsigned char *event_rec) +{ + int dev = event_rec[1]; + int i, l = 0; + unsigned char *buf = &event_rec[2]; - /* - * NOTE! No break here. Execution of TMR_WAIT_REL continues in the - * next case (TMR_WAIT_ABS) - */ + if ((int) dev > max_synthdev) + return; + if (!(synth_open_mask & (1 << dev))) + return; + if (!synth_devs[dev]) + return; + + l = 0; + for (i = 0; i < 6 && buf[i] != 0xff; i++) + l = i + 1; + + if (!synth_devs[dev]->send_sysex) + return; + if (l > 0) + synth_devs[dev]->send_sysex(dev, buf, l); +} - case TMR_WAIT_ABS: - if (parm > 0) +static int play_event(unsigned char *q) +{ + /* + * NOTE! This routine returns + * 0 = normal event played. + * 1 = Timer armed. Suspend playback until timer callback. + * 2 = MIDI output buffer full. Restore queue and suspend until timer + */ + unsigned int *delay; + + switch (q[0]) { - long time; - - seq_playing = 1; - time = parm; - prev_event_time = time; + case SEQ_NOTEOFF: + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->kill_note(0, q[1], 255, q[3]); + break; + + case SEQ_NOTEON: + if (q[4] < 128 || q[4] == 255) + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->start_note(0, q[1], q[2], q[3]); + break; + + case SEQ_WAIT: + delay = (unsigned int *) q; /* + * Bytes 1 to 3 are containing the * + * delay in 'ticks' + */ + *delay = (*delay >> 8) & 0xffffff; + + if (*delay > 0) + { + long time; + + seq_playing = 1; + time = *delay; + prev_event_time = time; + + if (softsynthp != NULL) + softsynthp(SSYN_REQUEST, time, 0, 0); + else + request_sound_timer(time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); + /* + * The timer is now active and will reinvoke this function + * after the timer expires. Return to the caller now. + */ + return 1; + } + break; + + case SEQ_PGMCHANGE: + if (synth_open_mask & (1 << 0)) + if (synth_devs[0]) + synth_devs[0]->set_instr(0, q[1], q[2]); + break; + + case SEQ_SYNCTIMER: /* + * Reset timer + */ + if (softsynthp != NULL) + seq_time = 0; + else + seq_time = jiffies; + prev_input_time = 0; + prev_event_time = 0; + if (softsynthp != NULL) + softsynthp(SSYN_START, 0, 0, 0); + break; + + case SEQ_MIDIPUTC: /* + * Put a midi character + */ + if (midi_opened[q[2]]) + { + int dev; - request_sound_timer (time); + dev = q[2]; - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - { - unsigned long flags; + if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) + break; - save_flags (flags); - cli (); - if ((seq_sleep_flag.flags & WK_SLEEP)) - { - { - seq_sleep_flag.flags = WK_WAKEUP; - module_wake_up (&seq_sleeper); - }; - } - restore_flags (flags); - } + if (!midi_devs[dev]->outputc(dev, q[1])) + { + /* + * Output FIFO is full. Wait one timer cycle and try again. + */ - return TIMER_ARMED; + seq_playing = 1; + if (softsynthp != NULL) + softsynthp(SSYN_REQUEST, -1, 0, 0); + else + request_sound_timer(-1); + return 2; + } + else + midi_written[dev] = 1; + } + break; + + case SEQ_ECHO: + seq_copy_to_input(q, 4); /* + * Echo back to the process + */ + break; + + case SEQ_PRIVATE: + if ((int) q[1] < max_synthdev) + synth_devs[q[1]]->hw_control(q[1], q); + break; + + case SEQ_EXTENDED: + extended_event(q); + break; + + case EV_CHN_VOICE: + seq_chn_voice_event(q); + break; + + case EV_CHN_COMMON: + seq_chn_common_event(q); + break; + + case EV_TIMING: + if (seq_timing_event(q) == TIMER_ARMED) + { + return 1; + } + break; + + case EV_SEQ_LOCAL: + seq_local_event(q); + break; + + case EV_SYSEX: + seq_sysex_message(q); + break; + + default: } - break; - - case TMR_START: - seq_time = jiffies; - prev_input_time = 0; - prev_event_time = 0; - break; - - case TMR_STOP: - break; - - case TMR_CONTINUE: - break; + return 0; +} - case TMR_TEMPO: - break; +static void seq_startplay(void) +{ + unsigned long flags; + int this_one, action; - case TMR_ECHO: - if (seq_mode == SEQ_2) - seq_copy_to_input (event_rec, 8); - else + while (qlen > 0) { - parm = (parm << 8 | SEQ_ECHO); - seq_copy_to_input ((unsigned char *) &parm, 4); + + save_flags(flags); + cli(); + qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; + qlen--; + restore_flags(flags); + + seq_playing = 1; + + if ((action = play_event(&queue[this_one * EV_SZ]))) + { /* Suspend playback. Next timer routine invokes this routine again */ + if (action == 2) + { + qlen++; + qhead = this_one; + } + return; + } } - break; - default:; - } + seq_playing = 0; - return TIMER_NOT_ARMED; + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + wake_up(&seq_sleeper); } -static void -seq_local_event (unsigned char *event_rec) +static void reset_controllers(int dev, unsigned char *controller, int update_dev) { - unsigned char cmd = event_rec[1]; - unsigned int parm = *((unsigned int *) &event_rec[4]); - - switch (cmd) - { - case LOCL_STARTAUDIO: -#ifdef CONFIG_AUDIO - DMAbuf_start_devices (parm); -#endif - break; - - default:; - } + int i; + for (i = 0; i < 128; i++) + controller[i] = ctrl_def_values[i]; } -static void -seq_sysex_message (unsigned char *event_rec) +static void setup_mode2(void) { - int dev = event_rec[1]; - int i, l = 0; - unsigned char *buf = &event_rec[2]; - - if ((int) dev > max_synthdev) - return; - if (!(synth_open_mask & (1 << dev))) - return; - if (!synth_devs[dev]) - return; - if (!synth_devs[dev]->send_sysex) - return; - - l = 0; - for (i = 0; i < 6 && buf[i] != 0xff; i++) - l = i + 1; - - if (l > 0) - synth_devs[dev]->send_sysex (dev, buf, l); -} + int dev; -static int -play_event (unsigned char *q) -{ - /* - * NOTE! This routine returns - * 0 = normal event played. - * 1 = Timer armed. Suspend playback until timer callback. - * 2 = MIDI output buffer full. Restore queue and suspend until timer - */ - unsigned int *delay; - - switch (q[0]) - { - case SEQ_NOTEOFF: - if (synth_open_mask & (1 << 0)) - if (synth_devs[0]) - synth_devs[0]->kill_note (0, q[1], 255, q[3]); - break; - - case SEQ_NOTEON: - if (q[4] < 128 || q[4] == 255) - if (synth_open_mask & (1 << 0)) - if (synth_devs[0]) - synth_devs[0]->start_note (0, q[1], q[2], q[3]); - break; - - case SEQ_WAIT: - delay = (unsigned int *) q; /* - * Bytes 1 to 3 are containing the * - * delay in 'ticks' - */ - *delay = (*delay >> 8) & 0xffffff; + max_synthdev = num_synths; - if (*delay > 0) + for (dev = 0; dev < num_midis; dev++) { - long time; - - seq_playing = 1; - time = *delay; - prev_event_time = time; - - request_sound_timer (time); - - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - { - unsigned long flags; - - save_flags (flags); - cli (); - if ((seq_sleep_flag.flags & WK_SLEEP)) + if (midi_devs[dev] && midi_devs[dev]->converter != NULL) { - { - seq_sleep_flag.flags = WK_WAKEUP; - module_wake_up (&seq_sleeper); - }; + synth_devs[max_synthdev++] = midi_devs[dev]->converter; } - restore_flags (flags); - } - /* - * The timer is now active and will reinvoke this function - * after the timer expires. Return to the caller now. - */ - return 1; } - break; - - case SEQ_PGMCHANGE: - if (synth_open_mask & (1 << 0)) - if (synth_devs[0]) - synth_devs[0]->set_instr (0, q[1], q[2]); - break; - case SEQ_SYNCTIMER: /* - * Reset timer - */ - seq_time = jiffies; - prev_input_time = 0; - prev_event_time = 0; - break; - - case SEQ_MIDIPUTC: /* - * Put a midi character - */ - if (midi_opened[q[2]]) + for (dev = 0; dev < max_synthdev; dev++) { - int dev; - - dev = q[2]; + int chn; - if (dev < 0 || dev >= num_midis) - break; + synth_devs[dev]->sysex_ptr = 0; + synth_devs[dev]->emulation = 0; - if (!midi_devs[dev]->putc (dev, q[1])) - { - /* - * Output FIFO is full. Wait one timer cycle and try again. - */ - - seq_playing = 1; - request_sound_timer (-1); - return 2; - } - else - midi_written[dev] = 1; + for (chn = 0; chn < 16; chn++) + { + synth_devs[dev]->chn_info[chn].pgm_num = 0; + reset_controllers(dev, + synth_devs[dev]->chn_info[chn].controllers,0); + synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */ + synth_devs[dev]->chn_info[chn].bender_range = 200; + } } - break; + max_mididev = 0; + seq_mode = SEQ_2; +} - case SEQ_ECHO: - seq_copy_to_input (q, 4); /* - * Echo back to the process - */ - break; +int sequencer_open(int dev, struct file *file) +{ + int retval, mode, i; + int level, tmp; + unsigned long flags; - case SEQ_PRIVATE: - if ((int) q[1] < max_synthdev) - synth_devs[q[1]]->hw_control (q[1], q); - break; + if (!sequencer_ok) + sequencer_init(); - case SEQ_EXTENDED: - extended_event (q); - break; + level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1; - case EV_CHN_VOICE: - seq_chn_voice_event (q); - break; + dev = dev >> 4; + mode = translate_mode(file); - case EV_CHN_COMMON: - seq_chn_common_event (q); - break; + DEB(printk("sequencer_open(dev=%d)\n", dev)); - case EV_TIMING: - if (seq_timing_event (q) == TIMER_ARMED) + if (!sequencer_ok) { - return 1; +/* printk("Sound card: sequencer not initialized\n");*/ + return -ENXIO; } - break; - - case EV_SEQ_LOCAL: - seq_local_event (q); - break; - - case EV_SYSEX: - seq_sysex_message (q); - break; + if (dev) /* Patch manager device (obsolete) */ + return -ENXIO; - default:; - } - - return 0; -} +#ifdef CONFIG_KERNELD + if(synth_devs[dev] == NULL) + request_module("synth0"); +#endif -static void -seq_startplay (void) -{ - unsigned long flags; - int this_one, action; - - while (qlen > 0) - { - - save_flags (flags); - cli (); - qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; - qlen--; - restore_flags (flags); - - seq_playing = 1; - - if ((action = play_event (&queue[this_one * EV_SZ]))) - { /* Suspend playback. Next timer routine invokes this routine again */ - if (action == 2) - { - qlen++; - qhead = this_one; - } - return; + if (mode == OPEN_READ) + { + if (!num_midis) + { + /*printk("Sequencer: No MIDI devices. Input not possible\n");*/ + sequencer_busy = 0; + return -ENXIO; + } } - - } - - seq_playing = 0; - - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - { - unsigned long flags; - - save_flags (flags); - cli (); - if ((seq_sleep_flag.flags & WK_SLEEP)) + save_flags(flags); + cli(); + if (sequencer_busy) { - { - seq_sleep_flag.flags = WK_WAKEUP; - module_wake_up (&seq_sleeper); - }; + restore_flags(flags); + return -EBUSY; } - restore_flags (flags); - } -} + sequencer_busy = 1; + obsolete_api_used = 0; + restore_flags(flags); -static void -reset_controllers (int dev, unsigned char *controller, int update_dev) -{ + max_mididev = num_midis; + max_synthdev = num_synths; + pre_event_timeout = 0; + seq_mode = SEQ_1; - int i; - - for (i = 0; i < 128; i++) - controller[i] = ctrl_def_values[i]; -} - -static void -setup_mode2 (void) -{ - int dev; + if (pending_timer != -1) + { + tmr_no = pending_timer; + pending_timer = -1; + } + if (tmr_no == -1) /* Not selected yet */ + { + int i, best; + + best = -1; + for (i = 0; i < num_sound_timers; i++) + if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best) + { + tmr_no = i; + best = sound_timer_devs[i]->priority; + } + if (tmr_no == -1) /* Should not be */ + tmr_no = 0; + } + tmr = sound_timer_devs[tmr_no]; - max_synthdev = num_synths; + if (level == 2) + { + if (tmr == NULL) + { + /*printk("sequencer: No timer for level 2\n");*/ + sequencer_busy = 0; + return -ENXIO; + } + setup_mode2(); + } + if (!max_synthdev && !max_mididev) + { + sequencer_busy=0; + return -ENXIO; + } - for (dev = 0; dev < num_midis; dev++) - if (midi_devs[dev]->converter != NULL) - { - synth_devs[max_synthdev++] = - midi_devs[dev]->converter; - } + synth_open_mask = 0; - for (dev = 0; dev < max_synthdev; dev++) - { - int chn; + for (i = 0; i < max_mididev; i++) + { + midi_opened[i] = 0; + midi_written[i] = 0; + } - for (chn = 0; chn < 16; chn++) + for (i = 0; i < max_synthdev; i++) { - synth_devs[dev]->chn_info[chn].pgm_num = 0; - reset_controllers (dev, - synth_devs[dev]->chn_info[chn].controllers, - 0); - synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */ + if (synth_devs[i]==NULL) + continue; + + if ((tmp = synth_devs[i]->open(i, mode)) < 0) + { + printk(KERN_WARNING "Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp); + if (synth_devs[i]->midi_dev) + printk(KERN_WARNING "(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev); + } + else + { + synth_open_mask |= (1 << i); + if (synth_devs[i]->midi_dev) + midi_opened[synth_devs[i]->midi_dev] = 1; + } } - } - max_mididev = 0; - seq_mode = SEQ_2; -} + if (softsynthp != NULL) + seq_time = 0; + else + seq_time = jiffies; -int -sequencer_open (int dev, struct fileinfo *file) -{ - int retval, mode, i; - int level, tmp; - unsigned long flags; - - level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1; - - dev = dev >> 4; - mode = file->mode & O_ACCMODE; - - DEB (printk ("sequencer_open(dev=%d)\n", dev)); - - if (!sequencer_ok) - { - printk ("Soundcard: Sequencer not initialized\n"); - return -(ENXIO); - } - - if (dev) /* Patch manager device */ - { - printk ("Patch manager interface is currently broken. Sorry\n"); - return -(ENXIO); - } - - save_flags (flags); - cli (); - if (sequencer_busy) - { - printk ("Sequencer busy\n"); - restore_flags (flags); - return -(EBUSY); - } - sequencer_busy = 1; - restore_flags (flags); - - max_mididev = num_midis; - max_synthdev = num_synths; - pre_event_timeout = 0; - seq_mode = SEQ_1; - - if (pending_timer != -1) - { - tmr_no = pending_timer; - pending_timer = -1; - } - - if (tmr_no == -1) /* Not selected yet */ - { - int i, best; - - best = -1; - for (i = 0; i < num_sound_timers; i++) - if (sound_timer_devs[i]->priority > best) - { - tmr_no = i; - best = sound_timer_devs[i]->priority; - } - - if (tmr_no == -1) /* Should not be */ - tmr_no = 0; - } - - tmr = sound_timer_devs[tmr_no]; - - if (level == 2) - { - if (tmr == NULL) + prev_input_time = 0; + prev_event_time = 0; + if (softsynthp != NULL) + softsynthp(SSYN_START, 0, 0, 0); + + if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) { - printk ("sequencer: No timer for level 2\n"); - sequencer_busy = 0; - return -(ENXIO); + /* + * Initialize midi input devices + */ + + for (i = 0; i < max_mididev; i++) + if (!midi_opened[i]) + { + if ((retval = midi_devs[i]->open(i, mode, + sequencer_midi_input, sequencer_midi_output)) >= 0) + { + midi_opened[i] = 1; + } + } } - setup_mode2 (); - } - - if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) - if (!max_mididev) - { - printk ("Sequencer: No Midi devices. Input not possible\n"); - sequencer_busy = 0; - return -(ENXIO); - } - - if (!max_synthdev && !max_mididev) - { - sequencer_busy = 0; - return -(ENXIO); - } - synth_open_mask = 0; - - for (i = 0; i < max_mididev; i++) - { - midi_opened[i] = 0; - midi_written[i] = 0; - } - - /* - * if (mode == OPEN_WRITE || mode == OPEN_READWRITE) - */ - for (i = 0; i < max_synthdev; i++) /* - * Open synth devices - */ - if ((tmp = synth_devs[i]->open (i, mode)) < 0) - { - printk ("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp); - if (synth_devs[i]->midi_dev) - printk ("(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev); - } - else - { - synth_open_mask |= (1 << i); - if (synth_devs[i]->midi_dev) /* - * Is a midi interface - */ - midi_opened[synth_devs[i]->midi_dev] = 1; - } + if (seq_mode == SEQ_2) + tmr->open(tmr_no, seq_mode); - seq_time = jiffies; - prev_input_time = 0; - prev_event_time = 0; + init_waitqueue(&seq_sleeper); + init_waitqueue(&midi_sleeper); + output_threshold = SEQ_MAX_QUEUE / 2; - if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) - { /* - * Initialize midi input devices - */ - for (i = 0; i < max_mididev; i++) - if (!midi_opened[i]) - { - if ((retval = midi_devs[i]->open (i, mode, - sequencer_midi_input, sequencer_midi_output)) >= 0) - midi_opened[i] = 1; - } - } - - if (seq_mode == SEQ_2) - { - tmr->open (tmr_no, seq_mode); - } - - seq_sleep_flag.flags = WK_NONE; - midi_sleep_flag.flags = WK_NONE; - output_threshold = SEQ_MAX_QUEUE / 2; - - for (i = 0; i < num_synths; i++) - if (pmgr_present[i]) - pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0); - - return 0; + return 0; } -void -seq_drain_midi_queues (void) +void seq_drain_midi_queues(void) { - int i, n; - - /* - * Give the Midi drivers time to drain their output queues - */ + int i, n; - n = 1; + /* + * Give the Midi drivers time to drain their output queues + */ - while (!current_got_fatal_signal () && n) - { - n = 0; + n = 1; - for (i = 0; i < max_mididev; i++) - if (midi_opened[i] && midi_written[i]) - if (midi_devs[i]->buffer_status != NULL) - if (midi_devs[i]->buffer_status (i)) - n++; - - /* - * Let's have a delay - */ - if (n) + while (!signal_pending(current) && n) { - - { - unsigned long tlimit; - - if (HZ / 10) - current_set_timeout (tlimit = jiffies + (HZ / 10)); - else - tlimit = (unsigned long) -1; - seq_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&seq_sleeper); - if (!(seq_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - seq_sleep_flag.flags |= WK_TIMEOUT; - } - seq_sleep_flag.flags &= ~WK_SLEEP; - }; + n = 0; + + for (i = 0; i < max_mididev; i++) + if (midi_opened[i] && midi_written[i]) + if (midi_devs[i]->buffer_status != NULL) + if (midi_devs[i]->buffer_status(i)) + n++; + + /* + * Let's have a delay + */ + + if (n) { + current->timeout = jiffies + HZ / 10; + interruptible_sleep_on(&seq_sleeper); + current->timeout = 0; + } } - } } -void -sequencer_release (int dev, struct fileinfo *file) +void sequencer_release(int dev, struct file *file) { - int i; - int mode = file->mode & O_ACCMODE; + int i; + int mode = translate_mode(file); - dev = dev >> 4; + dev = dev >> 4; - DEB (printk ("sequencer_release(dev=%d)\n", dev)); + DEB(printk("sequencer_release(dev=%d)\n", dev)); - if (dev) /* - * Patch manager device - */ - { - dev--; - pmgr_release (dev); - pmgr_present[dev] = 0; - return; - } - - /* - * * Wait until the queue is empty (if we don't have nonblock) - */ - - if (mode != OPEN_READ && !(file->flags & (O_NONBLOCK) ? - 1 : 0)) - while (!current_got_fatal_signal () && qlen) - { - seq_sync (); - } - - if (mode != OPEN_READ) - seq_drain_midi_queues (); /* - * Ensure the output queues are empty - */ - seq_reset (); - if (mode != OPEN_READ) - seq_drain_midi_queues (); /* - * Flush the all notes off messages - */ + /* + * Wait until the queue is empty (if we don't have nonblock) + */ - for (i = 0; i < max_synthdev; i++) - if (synth_open_mask & (1 << i)) /* - * Actually opened - */ - if (synth_devs[i]) + if (mode != OPEN_READ && !(file->f_flags & O_NONBLOCK)) { - synth_devs[i]->close (i); - - if (synth_devs[i]->midi_dev) - midi_opened[synth_devs[i]->midi_dev] = 0; + while (!signal_pending(current) && qlen > 0) + { + seq_sync(); + current->timeout = jiffies + 3 * HZ; + interruptible_sleep_on(&seq_sleeper); + current->timeout = 0; + /* Extra delay */ + } } - for (i = 0; i < num_synths; i++) - if (pmgr_present[i]) - pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0); + if (mode != OPEN_READ) + seq_drain_midi_queues(); /* + * Ensure the output queues are empty + */ + seq_reset(); + if (mode != OPEN_READ) + seq_drain_midi_queues(); /* + * Flush the all notes off messages + */ + + for (i = 0; i < max_synthdev; i++) + { + if (synth_open_mask & (1 << i)) /* + * Actually opened + */ + if (synth_devs[i]) + { + synth_devs[i]->close(i); + + if (synth_devs[i]->midi_dev) + midi_opened[synth_devs[i]->midi_dev] = 0; + } + } - for (i = 0; i < max_mididev; i++) - if (midi_opened[i]) - midi_devs[i]->close (i); + for (i = 0; i < max_mididev; i++) + { + if (midi_opened[i]) + midi_devs[i]->close(i); + } - if (seq_mode == SEQ_2) - tmr->close (tmr_no); + if (seq_mode == SEQ_2) + tmr->close(tmr_no); - sequencer_busy = 0; + if (obsolete_api_used) + printk(KERN_WARNING "/dev/music: Obsolete (4 byte) API was used by %s\n", current->comm); + sequencer_busy = 0; } -static int -seq_sync (void) +static int seq_sync(void) { - unsigned long flags; - - if (qlen && !seq_playing && !current_got_fatal_signal ()) - seq_startplay (); - - save_flags (flags); - cli (); - if (qlen && !(seq_sleep_flag.flags & WK_SLEEP)) - { - - { - unsigned long tlimit; - - if (HZ) - current_set_timeout (tlimit = jiffies + (HZ)); - else - tlimit = (unsigned long) -1; - seq_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&seq_sleeper); - if (!(seq_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - seq_sleep_flag.flags |= WK_TIMEOUT; - } - seq_sleep_flag.flags &= ~WK_SLEEP; - }; - } - restore_flags (flags); - - return qlen; + unsigned long flags; + + if (qlen && !seq_playing && !signal_pending(current)) + seq_startplay(); + + save_flags(flags); + cli(); + if (qlen > 0) { + current->timeout = jiffies + HZ; + interruptible_sleep_on(&seq_sleeper); + current->timeout = 0; + } + restore_flags(flags); + return qlen; } -static void -midi_outc (int dev, unsigned char data) +static void midi_outc(int dev, unsigned char data) { - /* - * NOTE! Calls sleep(). Don't call this from interrupt. - */ - - int n; - unsigned long flags; - - /* - * This routine sends one byte to the Midi channel. - * If the output FIFO is full, it waits until there - * is space in the queue - */ - - n = 3 * HZ; /* Timeout */ - - save_flags (flags); - cli (); - while (n && !midi_devs[dev]->putc (dev, data)) - { - - { - unsigned long tlimit; - - if (4) - current_set_timeout (tlimit = jiffies + (4)); - else - tlimit = (unsigned long) -1; - seq_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&seq_sleeper); - if (!(seq_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - seq_sleep_flag.flags |= WK_TIMEOUT; - } - seq_sleep_flag.flags &= ~WK_SLEEP; - }; - n--; - } - restore_flags (flags); + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int n; + unsigned long flags; + + /* + * This routine sends one byte to the Midi channel. + * If the output FIFO is full, it waits until there + * is space in the queue + */ + + n = 3 * HZ; /* Timeout */ + + save_flags(flags); + cli(); + while (n && !midi_devs[dev]->outputc(dev, data)) { + current->timeout = jiffies + 4; + interruptible_sleep_on(&seq_sleeper); + current->timeout = 0; + n--; + } + restore_flags(flags); } -static void -seq_reset (void) +static void seq_reset(void) { - /* - * NOTE! Calls sleep(). Don't call this from interrupt. - */ - - int i; - int chn; - unsigned long flags; + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ - sound_stop_timer (); - seq_time = jiffies; - prev_input_time = 0; - prev_event_time = 0; + int i; + int chn; + unsigned long flags; - qlen = qhead = qtail = 0; - iqlen = iqhead = iqtail = 0; + if (softsynthp != NULL) + softsynthp(SSYN_STOP, 0, 0, 0); + else + sound_stop_timer(); - for (i = 0; i < max_synthdev; i++) - if (synth_open_mask & (1 << i)) - if (synth_devs[i]) - synth_devs[i]->reset (i); + seq_time = jiffies; + prev_input_time = 0; + prev_event_time = 0; - if (seq_mode == SEQ_2) - { + qlen = qhead = qtail = 0; + iqlen = iqhead = iqtail = 0; - for (chn = 0; chn < 16; chn++) for (i = 0; i < max_synthdev; i++) - if (synth_open_mask & (1 << i)) - if (synth_devs[i]) - { - synth_devs[i]->controller (i, chn, 123, 0); /* All notes off */ - synth_devs[i]->controller (i, chn, 121, 0); /* Reset all ctl */ - synth_devs[i]->bender (i, chn, 1 << 13); /* Bender off */ - } - - } - else - /* seq_mode == SEQ_1 */ - { - for (i = 0; i < max_mididev; i++) - if (midi_written[i]) /* - * Midi used. Some notes may still be playing - */ - { - /* - * Sending just a ACTIVE SENSING message should be enough to stop all - * playing notes. Since there are devices not recognizing the - * active sensing, we have to send some all notes off messages also. - */ - midi_outc (i, 0xfe); - - for (chn = 0; chn < 16; chn++) - { - midi_outc (i, - (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */ - midi_outc (i, 0x7b); /* All notes off */ - midi_outc (i, 0); /* Dummy parameter */ - } - - midi_devs[i]->close (i); - - midi_written[i] = 0; - midi_opened[i] = 0; - } - } - - seq_playing = 0; - - save_flags (flags); - cli (); - if ((seq_sleep_flag.flags & WK_SLEEP)) - { - /* printk ("Sequencer Warning: Unexpected sleeping process - Waking up\n"); */ - { - seq_sleep_flag.flags = WK_WAKEUP; - module_wake_up (&seq_sleeper); - }; - } - restore_flags (flags); + if (synth_open_mask & (1 << i)) + if (synth_devs[i]) + synth_devs[i]->reset(i); -} - -static void -seq_panic (void) -{ - /* - * This routine is called by the application in case the user - * wants to reset the system to the default state. - */ - - seq_reset (); - - /* - * Since some of the devices don't recognize the active sensing and - * all notes off messages, we have to shut all notes manually. - * - * TO BE IMPLEMENTED LATER - */ - - /* - * Also return the controllers to their default states - */ -} - -int -sequencer_ioctl (int dev, struct fileinfo *file, - unsigned int cmd, caddr_t arg) -{ - int midi_dev, orig_dev; - int mode = file->mode & O_ACCMODE; - - orig_dev = dev = dev >> 4; - - switch (cmd) - { - case SNDCTL_TMR_TIMEBASE: - case SNDCTL_TMR_TEMPO: - case SNDCTL_TMR_START: - case SNDCTL_TMR_STOP: - case SNDCTL_TMR_CONTINUE: - case SNDCTL_TMR_METRONOME: - case SNDCTL_TMR_SOURCE: - if (dev) /* Patch manager */ - return -(EIO); - - if (seq_mode != SEQ_2) - return -(EINVAL); - return tmr->ioctl (tmr_no, cmd, arg); - break; - - case SNDCTL_TMR_SELECT: - if (dev) /* Patch manager */ - return -(EIO); - - if (seq_mode != SEQ_2) - return -(EINVAL); - pending_timer = get_user ((int *) arg); - - if (pending_timer < 0 || pending_timer >= num_sound_timers) + if (seq_mode == SEQ_2) { - pending_timer = -1; - return -(EINVAL); + for (chn = 0; chn < 16; chn++) + for (i = 0; i < max_synthdev; i++) + if (synth_open_mask & (1 << i)) + if (synth_devs[i]) + { + synth_devs[i]->controller(i, chn, 123, 0); /* All notes off */ + synth_devs[i]->controller(i, chn, 121, 0); /* Reset all ctl */ + synth_devs[i]->bender(i, chn, 1 << 13); /* Bender off */ + } } - - return snd_ioctl_return ((int *) arg, pending_timer); - break; - - case SNDCTL_SEQ_PANIC: - seq_panic (); - break; - - case SNDCTL_SEQ_SYNC: - if (dev) /* - * Patch manager - */ - return -(EIO); - - if (mode == OPEN_READ) - return 0; - while (qlen && !current_got_fatal_signal ()) - seq_sync (); - if (qlen) - return -(EINTR); - else - return 0; - break; - - case SNDCTL_SEQ_RESET: - if (dev) /* - * Patch manager - */ - return -(EIO); - - seq_reset (); - return 0; - break; - - case SNDCTL_SEQ_TESTMIDI: - if (dev) /* - * Patch manager - */ - return -(EIO); - - midi_dev = get_user ((int *) arg); - if (midi_dev < 0 || midi_dev >= max_mididev) - return -(ENXIO); - - if (!midi_opened[midi_dev]) + else /* seq_mode == SEQ_1 */ { - int err, mode; - - mode = file->mode & O_ACCMODE; - if ((err = midi_devs[midi_dev]->open (midi_dev, mode, - sequencer_midi_input, - sequencer_midi_output)) < 0) - return err; - } - - midi_opened[midi_dev] = 1; - - return 0; - break; - - case SNDCTL_SEQ_GETINCOUNT: - if (dev) /* - * Patch manager + for (i = 0; i < max_mididev; i++) + if (midi_written[i]) /* + * Midi used. Some notes may still be playing + */ + { + /* + * Sending just a ACTIVE SENSING message should be enough to stop all + * playing notes. Since there are devices not recognizing the + * active sensing, we have to send some all notes off messages also. */ - return -(EIO); - - if (mode == OPEN_WRITE) - return 0; - return snd_ioctl_return ((int *) arg, iqlen); - break; - - case SNDCTL_SEQ_GETOUTCOUNT: - - if (mode == OPEN_READ) - return 0; - return snd_ioctl_return ((int *) arg, SEQ_MAX_QUEUE - qlen); - break; - - case SNDCTL_SEQ_CTRLRATE: - if (dev) /* Patch manager */ - return -(EIO); - - /* - * If *arg == 0, just return the current rate - */ - if (seq_mode == SEQ_2) - return tmr->ioctl (tmr_no, cmd, arg); - - if (get_user ((int *) arg) != 0) - return -(EINVAL); - - return snd_ioctl_return ((int *) arg, HZ); - break; - - case SNDCTL_SEQ_RESETSAMPLES: - { - int err; + midi_outc(i, 0xfe); - dev = get_user ((int *) arg); - if (dev < 0 || dev >= num_synths) - { - return -(ENXIO); - } + for (chn = 0; chn < 16; chn++) + { + midi_outc(i, (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */ + midi_outc(i, 0x7b); /* All notes off */ + midi_outc(i, 0); /* Dummy parameter */ + } - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - { - return -(EBUSY); - } + midi_devs[i]->close(i); - if (!orig_dev && pmgr_present[dev]) - pmgr_inform (dev, PM_E_PATCH_RESET, 0, 0, 0, 0); - - err = synth_devs[dev]->ioctl (dev, cmd, arg); - return err; - } - break; - - case SNDCTL_SEQ_NRSYNTHS: - return snd_ioctl_return ((int *) arg, max_synthdev); - break; - - case SNDCTL_SEQ_NRMIDIS: - return snd_ioctl_return ((int *) arg, max_mididev); - break; - - case SNDCTL_SYNTH_MEMAVL: - { - int dev = get_user ((int *) arg); - - if (dev < 0 || dev >= num_synths) - return -(ENXIO); - - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - return -(EBUSY); - - return snd_ioctl_return ((int *) arg, synth_devs[dev]->ioctl (dev, cmd, arg)); - } - break; + midi_written[i] = 0; + midi_opened[i] = 0; + } + } - case SNDCTL_FM_4OP_ENABLE: - { - int dev = get_user ((int *) arg); + seq_playing = 0; - if (dev < 0 || dev >= num_synths) - return -(ENXIO); + save_flags(flags); + cli(); - if (!(synth_open_mask & (1 << dev))) - return -(ENXIO); + if (waitqueue_active(&seq_sleeper)) { + /* printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */ + wake_up(&seq_sleeper); + } + restore_flags(flags); +} - synth_devs[dev]->ioctl (dev, cmd, arg); - return 0; - } - break; +static void seq_panic(void) +{ + /* + * This routine is called by the application in case the user + * wants to reset the system to the default state. + */ + + seq_reset(); + + /* + * Since some of the devices don't recognize the active sensing and + * all notes off messages, we have to shut all notes manually. + * + * TO BE IMPLEMENTED LATER + */ + + /* + * Also return the controllers to their default states + */ +} - case SNDCTL_SYNTH_INFO: - { +int sequencer_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg) +{ + int midi_dev, orig_dev, val, err; + int mode = translate_mode(file); struct synth_info inf; - int dev; - - memcpy_fromfs ((char *) &inf, &((char *) arg)[0], sizeof (inf)); - dev = inf.device; - - if (dev < 0 || dev >= max_synthdev) - return -(ENXIO); - - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - return -(EBUSY); - - return synth_devs[dev]->ioctl (dev, cmd, arg); - } - break; - - case SNDCTL_SEQ_OUTOFBAND: - { struct seq_event_rec event_rec; - unsigned long flags; - - memcpy_fromfs ((char *) &event_rec, &((char *) arg)[0], sizeof (event_rec)); + unsigned long flags; - save_flags (flags); - cli (); - play_event (event_rec.arr); - restore_flags (flags); + orig_dev = dev = dev >> 4; - return 0; - } - break; - - case SNDCTL_MIDI_INFO: - { - struct midi_info inf; - int dev; - - memcpy_fromfs ((char *) &inf, &((char *) arg)[0], sizeof (inf)); - dev = inf.device; - - if (dev < 0 || dev >= max_mididev) - return -(ENXIO); - - memcpy_tofs (&((char *) arg)[0], (char *) &(midi_devs[dev]->info), sizeof (inf)); - return 0; - } - break; - - case SNDCTL_PMGR_IFACE: - { - struct patmgr_info *inf; - int dev, err; - - if ((inf = (struct patmgr_info *) vmalloc (sizeof (*inf))) == NULL) - { - printk ("patmgr: Can't allocate memory for a message\n"); - return -(EIO); - } - - memcpy_fromfs ((char *) inf, &((char *) arg)[0], sizeof (*inf)); - dev = inf->device; - - if (dev < 0 || dev >= num_synths) - { - vfree (inf); - return -(ENXIO); - } - - if (!synth_devs[dev]->pmgr_interface) - { - vfree (inf); - return -(ENXIO); - } - - if ((err = synth_devs[dev]->pmgr_interface (dev, inf)) == -1) - { - vfree (inf); - return err; - } - - memcpy_tofs (&((char *) arg)[0], (char *) inf, sizeof (*inf)); - vfree (inf); - return 0; - } - break; - - case SNDCTL_PMGR_ACCESS: - { - struct patmgr_info *inf; - int dev, err; - - if ((inf = (struct patmgr_info *) vmalloc (sizeof (*inf))) == NULL) - { - printk ("patmgr: Can't allocate memory for a message\n"); - return -(EIO); - } - - memcpy_fromfs ((char *) inf, &((char *) arg)[0], sizeof (*inf)); - dev = inf->device; - - if (dev < 0 || dev >= num_synths) - { - vfree (inf); - return -(ENXIO); - } - - if (!pmgr_present[dev]) - { - vfree (inf); - return -(ESRCH); - } - - if ((err = pmgr_access (dev, inf)) < 0) - { - vfree (inf); - return err; - } - - memcpy_tofs (&((char *) arg)[0], (char *) inf, sizeof (*inf)); - vfree (inf); - return 0; - } - break; + switch (cmd) + { + case SNDCTL_TMR_TIMEBASE: + case SNDCTL_TMR_TEMPO: + case SNDCTL_TMR_START: + case SNDCTL_TMR_STOP: + case SNDCTL_TMR_CONTINUE: + case SNDCTL_TMR_METRONOME: + case SNDCTL_TMR_SOURCE: + if (seq_mode != SEQ_2) + return -EINVAL; + return tmr->ioctl(tmr_no, cmd, arg); + + case SNDCTL_TMR_SELECT: + if (seq_mode != SEQ_2) + return -EINVAL; + if (get_user(pending_timer, (int *)arg)) + return -EFAULT; + if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL) + { + pending_timer = -1; + return -EINVAL; + } + val = pending_timer; + break; + + case SNDCTL_SEQ_PANIC: + seq_panic(); + return -EINVAL; + + case SNDCTL_SEQ_SYNC: + if (mode == OPEN_READ) + return 0; + while (qlen > 0 && !signal_pending(current)) + seq_sync(); + return qlen ? -EINTR : 0; + + case SNDCTL_SEQ_RESET: + seq_reset(); + return 0; + + case SNDCTL_SEQ_TESTMIDI: + if (__get_user(midi_dev, (int *)arg)) + return -EFAULT; + if (midi_dev < 0 || midi_dev >= max_mididev) + return -ENXIO; + + if (!midi_opened[midi_dev] && + (err = midi_devs[midi_dev]->open(midi_dev, mode, sequencer_midi_input, + sequencer_midi_output)) < 0) + return err; + midi_opened[midi_dev] = 1; + return 0; + + case SNDCTL_SEQ_GETINCOUNT: + if (mode == OPEN_WRITE) + return 0; + val = iqlen; + break; + + case SNDCTL_SEQ_GETOUTCOUNT: + if (mode == OPEN_READ) + return 0; + val = SEQ_MAX_QUEUE - qlen; + break; + + case SNDCTL_SEQ_GETTIME: + if (seq_mode == SEQ_2) + return tmr->ioctl(tmr_no, cmd, arg); + if (softsynthp != NULL) + val = softsynthp(SSYN_GETTIME, 0, 0, 0); + else + val = jiffies - seq_time; + break; + + case SNDCTL_SEQ_CTRLRATE: + /* + * If *arg == 0, just return the current rate + */ + if (seq_mode == SEQ_2) + return tmr->ioctl(tmr_no, cmd, arg); + + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) + return -EINVAL; + val = HZ; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + case SNDCTL_SYNTH_REMOVESAMPLE: + case SNDCTL_SYNTH_CONTROL: + if (get_user(dev, (int *)arg)) + return -EFAULT; + if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + return synth_devs[dev]->ioctl(dev, cmd, arg); + + case SNDCTL_SEQ_NRSYNTHS: + val = max_synthdev; + break; + + case SNDCTL_SEQ_NRMIDIS: + val = max_mididev; + break; + + case SNDCTL_SYNTH_MEMAVL: + if (get_user(dev, (int *)arg)) + return -EFAULT; + if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + val = synth_devs[dev]->ioctl(dev, cmd, arg); + break; + + case SNDCTL_FM_4OP_ENABLE: + if (get_user(dev, (int *)arg)) + return -EFAULT; + if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) + return -ENXIO; + if (!(synth_open_mask & (1 << dev))) + return -ENXIO; + synth_devs[dev]->ioctl(dev, cmd, arg); + return 0; + + case SNDCTL_SYNTH_INFO: + if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device)))) + return -EFAULT; + if (dev < 0 || dev >= max_synthdev) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + return synth_devs[dev]->ioctl(dev, cmd, arg); + + /* Like SYNTH_INFO but returns ID in the name field */ + case SNDCTL_SYNTH_ID: + if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device)))) + return -EFAULT; + if (dev < 0 || dev >= max_synthdev) + return -ENXIO; + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -EBUSY; + memcpy(&inf, synth_devs[dev]->info, sizeof(inf)); + strncpy(inf.name, synth_devs[dev]->id, sizeof(inf.name)); + inf.device = dev; + return copy_to_user(arg, &inf, sizeof(inf))?-EFAULT:0; + + case SNDCTL_SEQ_OUTOFBAND: + if (copy_from_user(&event_rec, arg, sizeof(event_rec))) + return -EFAULT; + save_flags(flags); + cli(); + play_event(event_rec.arr); + restore_flags(flags); + return 0; + + case SNDCTL_MIDI_INFO: + if (get_user(dev, (int *)(&(((struct synth_info *)arg)->device)))) + return -EFAULT; + if (dev < 0 || dev >= max_mididev) + return -ENXIO; + midi_devs[dev]->info.device = dev; + return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct synth_info))?-EFAULT:0; + + case SNDCTL_SEQ_THRESHOLD: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 1) + val = 1; + if (val >= SEQ_MAX_QUEUE) + val = SEQ_MAX_QUEUE - 1; + output_threshold = val; + return 0; + + case SNDCTL_MIDI_PRETIME: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val < 0) + val = 0; + val = (HZ * val) / 10; + pre_event_timeout = val; + break; + + default: + if (mode == OPEN_READ) + return -EIO; + if (!synth_devs[0]) + return -ENXIO; + if (!(synth_open_mask & (1 << 0))) + return -ENXIO; + if (!synth_devs[0]->ioctl) + return -EINVAL; + return synth_devs[0]->ioctl(0, cmd, arg); + } + return put_user(val, (int *)arg); +} - case SNDCTL_SEQ_THRESHOLD: - { - int tmp = get_user ((int *) arg); +unsigned int sequencer_select(int dev, struct file *file, int sel_type, select_table * wait) +{ + unsigned long flags; - if (dev) /* - * Patch manager - */ - return -(EIO); + dev = dev >> 4; - if (tmp < 1) - tmp = 1; - if (tmp >= SEQ_MAX_QUEUE) - tmp = SEQ_MAX_QUEUE - 1; - output_threshold = tmp; + switch(sel_type) + { + case SEL_IN: + save_flags(flags); + cli(); + /* input */ + if (!iqlen) + { + select_wait(&midi_sleeper, wait); + restore_flags(flags); + return 0; + } + restore_flags(flags); + return 1; + + case SEL_OUT: + /* output */ + save_flags(flags); + cli(); + if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) + { + select_wait(&seq_sleeper, wait); + restore_flags(flags); + return 0; + } + restore_flags(flags); + return 1; + case SEL_EX: + return 0; + } return 0; - } - break; - - case SNDCTL_MIDI_PRETIME: - { - int val = get_user ((int *) arg); - - if (val < 0) - val = 0; - - val = (HZ * val) / 10; - pre_event_timeout = val; - return snd_ioctl_return ((int *) arg, val); - } - break; - - default: - if (dev) /* - * Patch manager - */ - return -(EIO); - - if (mode == OPEN_READ) - return -(EIO); +} - if (!synth_devs[0]) - return -(ENXIO); - if (!(synth_open_mask & (1 << 0))) - return -(ENXIO); - return synth_devs[0]->ioctl (0, cmd, arg); - break; - } - return -(EINVAL); +void sequencer_timer(unsigned long dummy) +{ + seq_startplay(); } -int -sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table_handle * wait) +int note_to_freq(int note_num) { - unsigned long flags; - dev = dev >> 4; + /* + * This routine converts a midi note to a frequency (multiplied by 1000) + */ - switch (sel_type) - { - case SEL_IN: - save_flags (flags); - cli (); - if (!iqlen) + int note, octave, note_freq; + static int notes[] = { + 261632, 277189, 293671, 311132, 329632, 349232, + 369998, 391998, 415306, 440000, 466162, 493880 + }; - midi_sleep_flag.flags = WK_SLEEP; - module_select_wait (&midi_sleeper, wait); - restore_flags (flags); - return 0; - } - restore_flags (flags); - return 1; - break; - - case SEL_OUT: - save_flags (flags); - cli (); - if ((SEQ_MAX_QUEUE - qlen) < output_threshold) - { +#define BASE_OCTAVE 5 - seq_sleep_flag.flags = WK_SLEEP; - module_select_wait (&seq_sleeper, wait); - restore_flags (flags); - return 0; - } - restore_flags (flags); - return 1; - break; + octave = note_num / 12; + note = note_num % 12; - case SEL_EX: - return 0; - } + note_freq = notes[note]; - return 0; -} + if (octave < BASE_OCTAVE) + note_freq >>= (BASE_OCTAVE - octave); + else if (octave > BASE_OCTAVE) + note_freq <<= (octave - BASE_OCTAVE); + /* + * note_freq >>= 1; + */ -void -sequencer_timer (unsigned long dummy) -{ - seq_startplay (); + return note_freq; } -int -note_to_freq (int note_num) +unsigned long compute_finetune(unsigned long base_freq, int bend, int range, + int vibrato_cents) { + unsigned long amount; + int negative, semitones, cents, multiplier = 1; - /* - * This routine converts a midi note to a frequency (multiplied by 1000) - */ + if (!bend) + return base_freq; + if (!range) + return base_freq; - int note, octave, note_freq; - int notes[] = - { - 261632, 277189, 293671, 311132, 329632, 349232, - 369998, 391998, 415306, 440000, 466162, 493880 - }; + if (!base_freq) + return base_freq; -#define BASE_OCTAVE 5 + if (range >= 8192) + range = 8192; - octave = note_num / 12; - note = note_num % 12; + bend = bend * range / 8192; /* Convert to cents */ + bend += vibrato_cents; - note_freq = notes[note]; + if (!bend) + return base_freq; - if (octave < BASE_OCTAVE) - note_freq >>= (BASE_OCTAVE - octave); - else if (octave > BASE_OCTAVE) - note_freq <<= (octave - BASE_OCTAVE); + negative = bend < 0 ? 1 : 0; - /* - * note_freq >>= 1; - */ - - return note_freq; -} + if (bend < 0) + bend *= -1; + if (bend > range) + bend = range; -unsigned long -compute_finetune (unsigned long base_freq, int bend, int range) -{ - unsigned long amount; - int negative, semitones, cents, multiplier = 1; - - if (!bend) - return base_freq; - if (!range) - return base_freq; - - if (!base_freq) - return base_freq; - - if (range >= 8192) - range = 8192; - - bend = bend * range / 8192; - if (!bend) - return base_freq; - - negative = bend < 0 ? 1 : 0; - - if (bend < 0) - bend *= -1; - if (bend > range) - bend = range; - - /* - if (bend > 2399) - bend = 2399; - */ - while (bend > 2399) - { - multiplier *= 4; - bend -= 2400; - } - - semitones = bend / 100; - cents = bend % 100; - - amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) - / 10000; - - if (negative) - return (base_freq * 10000) / amount; /* Bend down */ - else - return (base_freq * amount) / 10000; /* Bend up */ -} + /* + if (bend > 2399) + bend = 2399; + */ + while (bend > 2399) + { + multiplier *= 4; + bend -= 2400; + } + semitones = bend / 100; + if (semitones > 99) + semitones = 99; + cents = bend % 100; -void -sequencer_init (void) -{ + amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000; + if (negative) + return (base_freq * 10000) / amount; /* Bend down */ + else + return (base_freq * amount) / 10000; /* Bend up */ +} - queue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * EV_SZ)); - if (sound_nblocks < 1024) - sound_nblocks++;; - if (queue == NULL) - { - printk ("Sound: Can't allocate memory for sequencer output queue\n"); - return; - } +void sequencer_init(void) +{ + /* drag in sequencer_syms.o */ + { + extern char sequencer_syms_symbol; + sequencer_syms_symbol = 0; + } - iqueue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * IEV_SZ)); - if (sound_nblocks < 1024) - sound_nblocks++;; - if (iqueue == NULL) - { - printk ("Sound: Can't allocate memory for sequencer input queue\n"); - return; - } + if (sequencer_ok) + return; +#ifdef CONFIG_MIDI + MIDIbuf_init(); +#endif + queue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * EV_SZ); + if (queue == NULL) + { + printk(KERN_ERR "sequencer: Can't allocate memory for sequencer output queue\n"); + return; + } + iqueue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * IEV_SZ); + if (iqueue == NULL) + { + printk(KERN_ERR "sequencer: Can't allocate memory for sequencer input queue\n"); + vfree(queue); + return; + } + sequencer_ok = 1; +} - sequencer_ok = 1; +void sequencer_unload(void) +{ + if(queue) + { + vfree(queue); + queue=NULL; + } + if(iqueue) + { + vfree(iqueue); + iqueue=NULL; + } } #endif diff --git a/drivers/sound/sequencer_syms.c b/drivers/sound/sequencer_syms.c new file mode 100644 index 000000000000..889d61e0b5cc --- /dev/null +++ b/drivers/sound/sequencer_syms.c @@ -0,0 +1,38 @@ +/* + * Exported symbols for sequencer driver. + * __NO_VERSION__ because this is still part of sound.o. + */ + +#define __NO_VERSION__ +#include + +char sequencer_syms_symbol; + +#include "sound_config.h" +#include "softoss.h" +#include "sound_calls.h" +/* Tuning */ +#define _SEQUENCER_C_ +#include "tuning.h" + + +struct symbol_table sequencer_symbols= +{ +#include + X(note_to_freq), + X(compute_finetune), + X(seq_copy_to_input), + X(seq_input_event), + X(sequencer_init), + X(sequencer_timer), + + X(sound_timer_init), + X(sound_timer_interrupt), + X(sound_timer_syncinterval), + X(reprogram_timer), + + X(softsynthp), + X(cent_tuning), + X(semitone_tuning), +#include +}; diff --git a/drivers/sound/sgalaxy.c b/drivers/sound/sgalaxy.c new file mode 100644 index 000000000000..6d74ade7426a --- /dev/null +++ b/drivers/sound/sgalaxy.c @@ -0,0 +1,187 @@ +/* + * sound/sgalaxy.c + * + * Low level driver for Aztech Sound Galaxy cards. + * Copyright 1998 Artur Skawina + * + * Supported cards: + * Aztech Sound Galaxy Waverider Pro 32 - 3D + * Aztech Sound Galaxy Washington 16 + * + * Based on cs4232.c by Hannu Savolainen and Alan Cox. + */ +/* + * 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 +#include + +#include "sound_config.h" +#include "soundmodule.h" + +#if defined(CONFIG_SGALAXY) || defined (MODULE) + +static void sleep( unsigned howlong ) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + howlong; + schedule(); + current->timeout = 0; +} + +#define DPORT 0x80 + +/* Sound Blaster regs */ + +#define SBDSP_RESET 0x6 +#define SBDSP_READ 0xA +#define SBDSP_COMMAND 0xC +#define SBDSP_STATUS SBDSP_COMMAND +#define SBDSP_DATA_AVAIL 0xE + +static int sb_rst(int base) +{ + int i; + + outb( 1, base+SBDSP_RESET ); /* reset the DSP */ + outb( 0, base+SBDSP_RESET ); + + for ( i=0; i<500; i++ ) /* delay */ + inb(DPORT); + + for ( i=0; i<100000; i++ ) + { + if ( inb( base+SBDSP_DATA_AVAIL )&0x80 ) + break; + } + + if ( inb( base+SBDSP_READ )!=0xAA ) + return 0; + + return 1; +} + +static int sb_cmd( int base, unsigned char val ) +{ + int i; + + for ( i=100000; i; i-- ) + { + if ( (inb( base+SBDSP_STATUS )&0x80)==0 ) + { + outb( val, base+SBDSP_COMMAND ); + break; + } + } + return i; /* i>0 == success */ +} + + +#define ai_sgbase driver_use_1 + +int probe_sgalaxy( struct address_info *ai ) +{ + if ( check_region( ai->io_base, 8 ) ) + { + printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); + return 0; + } + + if ( ad1848_detect( ai->io_base+4, NULL, ai->osp ) ) + return 1; /* The card is already active */ + + if ( check_region( ai->ai_sgbase, 0x10 ) ) + { + printk(KERN_ERR "sgalaxy: SB IO port 0x%03x not available\n", ai->ai_sgbase); + return 0; + } + + /* switch to MSS/WSS mode */ + + sb_rst( ai->ai_sgbase ); + + sb_cmd( ai->ai_sgbase, 9 ); + sb_cmd( ai->ai_sgbase, 0 ); + + sleep( HZ/10 ); + + if ( ad1848_detect( ai->io_base+4, NULL, ai->osp ) ) + return 1; + return 0; +} + +void attach_sgalaxy( struct address_info *ai ) +{ + int n; + + request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB" ); + + attach_ms_sound( ai ); + n=ai->slots[0]; + if (n!=-1 && audio_devs[n]->mixer_dev != -1 ) + { + AD1848_REROUTE( SOUND_MIXER_LINE1, SOUND_MIXER_LINE ); /* Line-in */ + AD1848_REROUTE( SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH ); /* FM+Wavetable*/ + AD1848_REROUTE( SOUND_MIXER_LINE3, SOUND_MIXER_CD ); /* CD */ + } +} + +void unload_sgalaxy( struct address_info *ai ) +{ + unload_ms_sound( ai ); + release_region( ai->ai_sgbase, 0x10 ); +} + +#ifdef MODULE + +int io = -1; +int irq = -1; +int dma = -1; +int dma2 = -1; +int sgbase = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(sgbase,"i"); + +struct address_info ai; + + +int init_module(void) +{ + if ( io==-1 || irq==-1 || dma==-1 || sgbase==-1 ) + { + printk(KERN_ERR "sgalaxy: io, irq, dma and sgbase must be set.\n"); + return -EINVAL; + } + + ai.io_base = io; + ai.irq = irq; + ai.dma = dma; + ai.dma2 = dma2; + ai.ai_sgbase = sgbase; + + if ( probe_sgalaxy( &ai )==0 ) + return -ENODEV; + + attach_sgalaxy( &ai ); + + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + unload_sgalaxy( &ai ); + SOUND_LOCK_END; +} + +#endif +#endif diff --git a/drivers/sound/softoss.c b/drivers/sound/softoss.c new file mode 100644 index 000000000000..2a9302bbce5f --- /dev/null +++ b/drivers/sound/softoss.c @@ -0,0 +1,1533 @@ +/* + * sound/softoss.c + * + * Software based MIDI synthsesizer driver. + * + * + * 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 +#include + +/* + * When POLLED_MODE is defined, the resampling loop is run using a timer + * callback routine. Normally the resampling loop is executed inside + * audio buffer interrupt handler which doesn't work with single mode DMA. + */ +#define SOFTSYN_MAIN +#undef POLLED_MODE +#define HANDLE_LFO + +#define ENVELOPE_SCALE 8 +#define NO_SAMPLE 0xffff + +#include "sound_config.h" +#include "soundmodule.h" + +#ifdef CONFIG_SOFTOSS +#include "softoss.h" +#include + +int softsynth_disabled = 0; + +static volatile int intr_pending = 0; + +#ifdef POLLED_MODE + +static struct timer_list poll_timer = { + NULL, NULL, 0, 0, softsyn_poll +}; + +#else +#endif + +#ifdef HANDLE_LFO +/* + * LFO table. Playback at 128 Hz gives 1 Hz LFO frequency. + */ +static int tremolo_table[128] = +{ + 0, 39, 158, 355, 630, 982, 1411, 1915, + 2494, 3146, 3869, 4662, 5522, 6448, 7438, 8489, + 9598, 10762, 11980, 13248, 14563, 15922, 17321, 18758, + 20228, 21729, 23256, 24806, 26375, 27960, 29556, 31160, + 32768, 34376, 35980, 37576, 39161, 40730, 42280, 43807, + 45308, 46778, 48215, 49614, 50973, 52288, 53556, 54774, + 55938, 57047, 58098, 59088, 60014, 60874, 61667, 62390, + 63042, 63621, 64125, 64554, 64906, 65181, 65378, 65497, + 65536, 65497, 65378, 65181, 64906, 64554, 64125, 63621, + 63042, 62390, 61667, 60874, 60014, 59087, 58098, 57047, + 55938, 54774, 53556, 52288, 50973, 49614, 48215, 46778, + 45308, 43807, 42280, 40730, 39161, 37576, 35980, 34376, + 32768, 31160, 29556, 27960, 26375, 24806, 23256, 21729, + 20228, 18758, 17321, 15922, 14563, 13248, 11980, 10762, + 9598, 8489, 7438, 6448, 5522, 4662, 3869, 3146, + 2494, 1915, 1411, 982, 630, 355, 158, 39 +}; + +static int vibrato_table[128] = +{ + 0, 1608, 3212, 4808, 6393, 7962, 9512, 11039, + 12540, 14010, 15447, 16846, 18205, 19520, 20788, 22006, + 23170, 24279, 25330, 26320, 27246, 28106, 28899, 29622, + 30274, 30853, 31357, 31786, 32138, 32413, 32610, 32729, + 32768, 32729, 32610, 32413, 32138, 31786, 31357, 30853, + 30274, 29622, 28899, 28106, 27246, 26320, 25330, 24279, + 23170, 22006, 20788, 19520, 18205, 16846, 15447, 14010, + 12540, 11039, 9512, 7962, 6393, 4808, 3212, 1608, + 0, -1608, -3212, -4808, -6393, -7962, -9512, -11039, + -12540, -14010, -15447, -16846, -18205, -19520, -20788, -22006, + -23170, -24279, -25330, -26320, -27246, -28106, -28899, -29622, + -30274, -30853, -31357, -31786, -32138, -32413, -32610, -32729, + -32768, -32729, -32610, -32413, -32138, -31786, -31357, -30853, + -30274, -29622, -28899, -28106, -27246, -26320, -25330, -24279, + -23170, -22006, -20788, -19520, -18205, -16846, -15447, -14010, + -12540, -11039, -9512, -7962, -6393, -4808, -3212, -1608 +}; + +#endif + +static unsigned long last_resample_jiffies; +static unsigned long resample_counter; + +extern int *sound_osp; + +static volatile int is_running = 0; +static int softsynth_loaded = 0; + +static struct synth_info softsyn_info = { + "SoftOSS", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH +}; + +static struct softsyn_devc sdev_info = { + 0 +}; + +softsyn_devc *devc = &sdev_info; /* used in softoss_rs.c */ + +static struct voice_alloc_info *voice_alloc; + +static int softsyn_open(int synthdev, int mode); +static void init_voice(softsyn_devc * devc, int voice); +static void compute_step(int voice); + +static volatile int tmr_running = 0; +static int voice_limit = 24; + + +static void set_max_voices(int nr) +{ + int i; + + if (nr < 4) + nr = 4; + + if (nr > voice_limit) + nr = voice_limit; + + voice_alloc->max_voice = devc->maxvoice = nr; + devc->afterscale = 5; + + for (i = 31; i > 0; i--) + if (nr & (1 << i)) + { + devc->afterscale = i + 1; + return; + } +} + +static void update_vibrato(int voice) +{ + voice_info *v = &softoss_voices[voice]; + +#ifdef HANDLE_LFO + int x; + + x = vibrato_table[v->vibrato_phase >> 8]; + v->vibrato_phase = (v->vibrato_phase + v->vibrato_step) & 0x7fff; + + x = (x * v->vibrato_depth) >> 15; + v->vibrato_level = (x * 600) >> 8; + + compute_step(voice); +#else + v->vibrato_level = 0; +#endif +} + +#ifdef HANDLE_LFO +static void update_tremolo(int voice) +{ + voice_info *v = &softoss_voices[voice]; + int x; + + x = tremolo_table[v->tremolo_phase >> 8]; + v->tremolo_phase = (v->tremolo_phase + v->tremolo_step) & 0x7fff; + + v->tremolo_level = (x * v->tremolo_depth) >> 20; +} +#endif + +static void start_vibrato(int voice) +{ + voice_info *v = &softoss_voices[voice]; + int rate; + + if (!v->vibrato_depth) + return; + + rate = v->vibrato_rate * 6 * 128; + v->vibrato_step = (rate * devc->control_rate) / devc->speed; + + devc->vibratomap |= (1 << voice); /* Enable vibrato */ +} + +static void start_tremolo(int voice) +{ + voice_info *v = &softoss_voices[voice]; + int rate; + + if (!v->tremolo_depth) + return; + + rate = v->tremolo_rate * 6 * 128; + v->tremolo_step = (rate * devc->control_rate) / devc->speed; + + devc->tremolomap |= (1 << voice); /* Enable tremolo */ +} + +static void update_volume(int voice) +{ + voice_info *v = &softoss_voices[voice]; + unsigned int vol; + + /* + * Compute plain volume + */ + + vol = (v->velocity * v->expression_vol * v->main_vol) >> 12; + +#ifdef HANDLE_LFO + /* + * Handle LFO + */ + + if (devc->tremolomap & (1 << voice)) + { + int t; + + t = 32768 - v->tremolo_level; + vol = (vol * t) >> 15; + update_tremolo(voice); + } +#endif + /* + * Envelope + */ + + if (v->mode & WAVE_ENVELOPES && !v->percussive_voice) + vol = (vol * (v->envelope_vol >> 16)) >> 19; + else + vol >>= 4; + + /* + * Handle panning + */ + + if (v->panning < 0) /* Pan left */ + v->rightvol = (vol * (128 + v->panning)) / 128; + else + v->rightvol = vol; + + if (v->panning > 0) /* Pan right */ + v->leftvol = (vol * (128 - v->panning)) / 128; + else + v->leftvol = vol; +} + +static void step_envelope(int voice, int do_release, int velocity) +{ + voice_info *v = &softoss_voices[voice]; + int r, rate, time, dif; + unsigned int vol; + unsigned long flags; + + save_flags(flags); + cli(); + + if (!voice_active[voice] || v->sample == NULL) + { + restore_flags(flags); + return; + } + if (!do_release) + { + if (v->mode & WAVE_SUSTAIN_ON && v->envelope_phase == 2) + { + /* Stop envelope until note off */ + v->envelope_volstep = 0; + v->envelope_time = 0x7fffffff; + if (v->mode & WAVE_VIBRATO) + start_vibrato(voice); + if (v->mode & WAVE_TREMOLO) + start_tremolo(voice); + restore_flags(flags); + return; + } + } + if (do_release) + v->envelope_phase = 3; + else + v->envelope_phase++; + + if (v->envelope_phase >= 5) /* Finished */ + { + init_voice(devc, voice); + restore_flags(flags); + return; + } + vol = v->envelope_target = v->sample->env_offset[v->envelope_phase] << 22; + + + rate = v->sample->env_rate[v->envelope_phase]; + r = 3 - ((rate >> 6) & 0x3); + r *= 3; + r = (int) (rate & 0x3f) << r; + rate = (((r * 44100) / devc->speed) * devc->control_rate) << 8; + + if (rate < (1 << 20)) /* Avoid infinitely "releasing" voices */ + rate = 1 << 20; + + dif = (v->envelope_vol - vol); + if (dif < 0) + dif *= -1; + if (dif < rate * 2) /* Too close */ + { + step_envelope(voice, 0, 60); + restore_flags(flags); + return; + } + + if (vol > v->envelope_vol) + { + v->envelope_volstep = rate; + time = (vol - v->envelope_vol) / rate; + } + else + { + v->envelope_volstep = -rate; + time = (v->envelope_vol - vol) / rate; + } + + time--; + if (time <= 0) + time = 1; + v->envelope_time = time; + restore_flags(flags); +} + +static void step_envelope_lfo(int voice) +{ + voice_info *v = &softoss_voices[voice]; + + /* + * Update pitch (vibrato) LFO + */ + + if (devc->vibratomap & (1 << voice)) + update_vibrato(voice); + + /* + * Update envelope + */ + + if (v->mode & WAVE_ENVELOPES) + { + v->envelope_vol += v->envelope_volstep; + /* Overshoot protection */ + if (v->envelope_vol < 0) + { + v->envelope_vol = v->envelope_target; + v->envelope_volstep = 0; + } + if (v->envelope_time-- <= 0) + { + v->envelope_vol = v->envelope_target; + step_envelope(voice, 0, 60); + } + } +} + +static void compute_step(int voice) +{ + voice_info *v = &softoss_voices[voice]; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + v->current_freq = compute_finetune(v->orig_freq, + v->bender, + v->bender_range, + v->vibrato_level); + v->step = (((v->current_freq << 9) + (devc->speed >> 1)) / devc->speed); + + if (v->mode & WAVE_LOOP_BACK) + v->step *= -1; /* Reversed playback */ +} + +static void init_voice(softsyn_devc * devc, int voice) +{ + voice_info *v = &softoss_voices[voice]; + unsigned long flags; + + save_flags(flags); + cli(); + voice_active[voice] = 0; + devc->vibratomap &= ~(1 << voice); + devc->tremolomap &= ~(1 << voice); + v->mode = 0; + v->wave = NULL; + v->sample = NULL; + v->ptr = 0; + v->step = 0; + v->startloop = 0; + v->startbackloop = 0; + v->endloop = 0; + v->looplen = 0; + v->bender = 0; + v->bender_range = 200; + v->panning = 0; + v->main_vol = 127; + v->expression_vol = 127; + v->patch_vol = 127; + v->percussive_voice = 0; + v->sustain_mode = 0; + v->envelope_phase = 1; + v->envelope_vol = 1 << 24; + v->envelope_volstep = 256; + v->envelope_time = 0; + v->vibrato_phase = 0; + v->vibrato_step = 0; + v->vibrato_level = 0; + v->vibrato_rate = 0; + v->vibrato_depth = 0; + v->tremolo_phase = 0; + v->tremolo_step = 0; + v->tremolo_level = 0; + v->tremolo_rate = 0; + v->tremolo_depth = 0; + voice_alloc->map[voice] = 0; + voice_alloc->alloc_times[voice] = 0; + restore_flags(flags); +} + +static void reset_samples(softsyn_devc * devc) +{ + int i; + + for (i = 0; i < MAX_VOICE; i++) + voice_active[i] = 0; + for (i = 0; i < devc->maxvoice; i++) + { + init_voice(devc, i); + softoss_voices[i].instr = 0; + } + + devc->ram_used = 0; + + for (i = 0; i < MAX_PATCH; i++) + devc->programs[i] = NO_SAMPLE; + + for (i = 0; i < devc->nrsamples; i++) + { + vfree(devc->samples[i]); + vfree(devc->wave[i]); + devc->samples[i] = NULL; + devc->wave[i] = NULL; + } + devc->nrsamples = 0; +} + +static void init_engine(softsyn_devc * devc) +{ + int i, fz, srate, sz = devc->channels; + + set_max_voices(devc->default_max_voices); + voice_alloc->timestamp = 0; + + if (devc->bits == 16) + sz *= 2; + + fz = devc->fragsize / sz; /* Samples per fragment */ + devc->samples_per_fragment = fz; + + devc->usecs = 0; + devc->usecs_per_frag = (1000000 * fz) / devc->speed; + + for (i = 0; i < devc->maxvoice; i++) + { + init_voice(devc, i); + softoss_voices[i].instr = 0; + } + devc->engine_state = ES_STOPPED; + + /* + * Initialize delay + */ + + for (i = 0; i < DELAY_SIZE; i++) + left_delay[i] = right_delay[i] = 0; + delayp = 0; + srate = (devc->speed / 10000); /* 1 to 4 */ + if (srate <= 0) + srate = 1; + devc->delay_size = (DELAY_SIZE * srate) / 4; + if (devc->delay_size == 0 || devc->delay_size > DELAY_SIZE) + devc->delay_size = DELAY_SIZE; +} + +void softsyn_control_loop(void) +{ + int voice; + + /* + * Recompute envlope, LFO, etc. + */ + for (voice = 0; voice < devc->maxvoice; voice++) + { + if (voice_active[voice]) + { + update_volume(voice); + step_envelope_lfo(voice); + } + else + voice_alloc->map[voice] = 0; + } +} + +static void start_engine(softsyn_devc * devc); + +static void do_resample(int dummy) +{ + struct dma_buffparms *dmap = audio_devs[devc->audiodev]->dmap_out; + struct voice_info *vinfo; + unsigned long flags, jif; + + int voice, loops; + short *buf; + + if (softsynth_disabled) + return; + + save_flags(flags); + cli(); + + if (is_running) + { + printk(KERN_WARNING "SoftOSS: Playback overrun\n"); + restore_flags(flags); + return; + } + jif = jiffies; + if (jif == last_resample_jiffies) + { + if (resample_counter++ > 50) + { + for (voice = 0; voice < devc->maxvoice; voice++) + init_voice(devc, voice); + voice_limit--; + resample_counter = 0; + printk(KERN_WARNING "SoftOSS: CPU overload. Limiting # of voices to %d\n", voice_limit); + + if (voice_limit < 10) + { + voice_limit = 10; + devc->speed = (devc->speed * 2) / 3; + + printk(KERN_WARNING "SoftOSS: Dropping sampling rate and stopping the device.\n"); + softsynth_disabled = 1; + } + } + } + else + { + last_resample_jiffies = jif; + resample_counter = 0; + } + + /* is_running = 1; */ + + if (dmap->qlen > devc->max_playahead) + { + printk(KERN_WARNING "SoftOSS: audio buffers full\n"); + is_running = 0; + restore_flags(flags); + return; + } + /* + * First verify that all active voices are valid (do this just once per block). + */ + + for (voice = 0; voice < devc->maxvoice; voice++) + { + if (voice_active[voice]) + { + int ptr; + + vinfo = &softoss_voices[voice]; + ptr = vinfo->ptr >> 9; + + if (vinfo->wave == NULL || ptr < 0 || ptr > vinfo->sample->len) + init_voice(devc, voice); + else if (!(vinfo->mode & WAVE_LOOPING) && (vinfo->ptr + vinfo->step) > vinfo->endloop) + voice_active[voice] = 0; + } + } + + /* + * Start the resampling process + */ + + loops = devc->samples_per_fragment; + buf = (short *) (dmap->raw_buf + (dmap->qtail * dmap->fragment_size)); + + softsynth_resample_loop(buf, loops); /* In Xsoftsynth_rs.c */ + + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + dmap->qlen++; + dmap->user_counter += dmap->fragment_size; + + devc->usecs += devc->usecs_per_frag; + + if (tmr_running) + sound_timer_interrupt(); + /* + * Execute timer + */ + + if (!tmr_running) + { + if (devc->usecs >= devc->next_event_usecs) + { + devc->next_event_usecs = ~0; + sequencer_timer(0); + } + } + + is_running = 0; + restore_flags(flags); +} + +static void delayed_resample(int dummy) +{ + struct dma_buffparms *dmap = audio_devs[devc->audiodev]->dmap_out; + int n = 0; + + if (is_running) + return; + + while (devc->engine_state != ES_STOPPED && dmap->qlen < devc->max_playahead && n++ < 2) + do_resample(0); + intr_pending = 0; +} + +#ifdef POLLED_MODE +static void softsyn_poll(unsigned long dummy) +{ + delayed_resample(0); + + if (devc->engine_state != ES_STOPPED) + { + poll_timer.expires = jiffies+1; + add_timer(&poll_timer); + } +} + +#else +static void softsyn_callback(int dev, int parm) +{ + delayed_resample(0); +} +#endif + +static void start_engine(softsyn_devc * devc) +{ + struct dma_buffparms *dmap; + int trig, n; + mm_segment_t fs; + + if (!devc->audio_opened) + if (softsyn_open(devc->synthdev, 0) < 0) + return; + + if (devc->audiodev >= num_audiodevs) + return; + + dmap = audio_devs[devc->audiodev]->dmap_out; + + devc->usecs = 0; + devc->next_event_usecs = ~0; + devc->control_rate = 64; + devc->control_counter = 0; + + if (devc->engine_state == ES_STOPPED) + { + n = trig = 0; + fs = get_fs(); + set_fs(get_ds()); + dma_ioctl(devc->audiodev, SNDCTL_DSP_SETTRIGGER, (caddr_t)&trig); +#ifdef POLLED_MODE + poll_timer.expires = jiffies+1; + add_timer(&poll_timer); + /* Start polling */ +#else + dmap->audio_callback = softsyn_callback; + dmap->qhead = dmap->qtail = dmap->qlen = 0; +#endif + while (dmap->qlen < devc->max_playahead && n++ < 2) + do_resample(0); + devc->engine_state = ES_STARTED; + last_resample_jiffies = jiffies; + resample_counter = 0; + trig = PCM_ENABLE_OUTPUT; + if (dma_ioctl(devc->audiodev, SNDCTL_DSP_SETTRIGGER, (caddr_t)&trig) < 0) + printk(KERN_ERR "SoftOSS: Trigger failed\n"); + set_fs(fs); + } +} + +static void stop_engine(softsyn_devc * devc) +{ +} + +static void request_engine(softsyn_devc * devc, int ticks) +{ + if (ticks < 0) /* Relative time */ + devc->next_event_usecs = devc->usecs - ticks * (1000000 / HZ); + else + devc->next_event_usecs = ticks * (1000000 / HZ); +} + +/* + * Softsync hook serves mode1 (timing) calls made by sequencer.c + */ + +static int softsynth_hook(int cmd, int parm1, int parm2, unsigned long parm3) +{ + switch (cmd) + { + case SSYN_START: + start_engine(devc); + break; + + case SSYN_STOP: + stop_engine(devc); + break; + + case SSYN_REQUEST: + request_engine(devc, parm1); + break; + + case SSYN_GETTIME: + return devc->usecs / (1000000 / HZ); + break; + + default: + printk(KERN_WARNING "SoftOSS: Unknown request %d\n", cmd); + } + return 0; +} + +static int softsyn_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + switch (cmd) + { + case SNDCTL_SYNTH_INFO: + softsyn_info.nr_voices = devc->maxvoice; + if (copy_to_user(arg, &softsyn_info, sizeof(softsyn_info))) + return -EFAULT; + return 0; + + case SNDCTL_SEQ_RESETSAMPLES: + stop_engine(devc); + reset_samples(devc); + return 0; + + case SNDCTL_SYNTH_MEMAVL: + return devc->ram_size - devc->ram_used; + + default: + return -EINVAL; + } +} + +static int softsyn_kill_note(int devno, int voice, int note, int velocity) +{ + if (voice < 0 || voice > devc->maxvoice) + return 0; + voice_alloc->map[voice] = 0xffff; /* Releasing */ + + if (softoss_voices[voice].sustain_mode & 1) /* Sustain controller on */ + { + softoss_voices[voice].sustain_mode = 3; /* Note off pending */ + return 0; + } + if (velocity > 127 || softoss_voices[voice].mode & WAVE_FAST_RELEASE) + { + init_voice(devc, voice); /* Mark it inactive */ + return 0; + } + if (softoss_voices[voice].mode & WAVE_ENVELOPES) + step_envelope(voice, 1, velocity); /* Enter sustain phase */ + else + init_voice(devc, voice); /* Mark it inactive */ + return 0; +} + +static int softsyn_set_instr(int dev, int voice, int instr) +{ + if (voice < 0 || voice > devc->maxvoice) + return 0; + + if (instr < 0 || instr > MAX_PATCH) + { + printk(KERN_ERR "SoftOSS: Invalid instrument number %d\n", instr); + return 0; + } + softoss_voices[voice].instr = instr; + return 0; +} + +static int softsyn_start_note(int dev, int voice, int note, int volume) +{ + int instr = 0; + int best_sample, best_delta, delta_freq, selected; + unsigned long note_freq, freq, base_note, flags; + voice_info *v = &softoss_voices[voice]; + + struct patch_info *sample; + + if (voice < 0 || voice > devc->maxvoice) + return 0; + + if (volume == 0) /* Actually note off */ + softsyn_kill_note(dev, voice, note, volume); + + save_flags(flags); + cli(); + + if (note == 255) + { /* Just volume update */ + v->velocity = volume; + if (voice_active[voice]) + update_volume(voice); + restore_flags(flags); + return 0; + } + voice_active[voice] = 0; /* Stop the voice for a while */ + devc->vibratomap &= ~(1 << voice); + devc->tremolomap &= ~(1 << voice); + + instr = v->instr; + if (instr < 0 || instr > MAX_PATCH || devc->programs[instr] == NO_SAMPLE) + { + printk(KERN_WARNING "SoftOSS: Undefined MIDI instrument %d\n", instr); + restore_flags(flags); + return 0; + } + instr = devc->programs[instr]; + + if (instr < 0 || instr >= devc->nrsamples) + { + printk(KERN_WARNING "SoftOSS: Corrupted MIDI instrument %d (%d)\n", v->instr, instr); + restore_flags(flags); + return 0; + } + note_freq = note_to_freq(note); + + selected = -1; + + best_sample = instr; + best_delta = 1000000; + + while (instr != NO_SAMPLE && instr >= 0 && selected == -1) + { + delta_freq = note_freq - devc->samples[instr]->base_note; + + if (delta_freq < 0) + delta_freq = -delta_freq; + if (delta_freq < best_delta) + { + best_sample = instr; + best_delta = delta_freq; + } + if (devc->samples[instr]->low_note <= note_freq && + note_freq <= devc->samples[instr]->high_note) + { + selected = instr; + } + else instr = devc->samples[instr]->key; /* Link to next sample */ + + if (instr < 0 || instr >= devc->nrsamples) + instr = NO_SAMPLE; + } + + if (selected == -1) + instr = best_sample; + else + instr = selected; + + if (instr < 0 || instr == NO_SAMPLE || instr > devc->nrsamples) + { + printk(KERN_WARNING "SoftOSS: Unresolved MIDI instrument %d\n", v->instr); + restore_flags(flags); + return 0; + } + sample = devc->samples[instr]; + v->sample = sample; + + if (v->percussive_voice) /* No key tracking */ + v->orig_freq = sample->base_freq; /* Fixed pitch */ + else + { + base_note = sample->base_note / 100; + note_freq /= 100; + + freq = sample->base_freq * note_freq / base_note; + v->orig_freq = freq; + } + + if (!(sample->mode & WAVE_LOOPING)) + sample->loop_end = sample->len; + + v->wave = devc->wave[instr]; + if (volume < 0) + volume = 0; + else if (volume > 127) + volume = 127; + v->ptr = 0; + v->startloop = sample->loop_start * 512; + v->startbackloop = 0; + v->endloop = sample->loop_end * 512; + v->looplen = (sample->loop_end - sample->loop_start) * 512; + v->leftvol = 64; + v->rightvol = 64; + v->patch_vol = sample->volume; + v->velocity = volume; + v->mode = sample->mode; + v->vibrato_phase = 0; + v->vibrato_step = 0; + v->vibrato_level = 0; + v->vibrato_rate = 0; + v->vibrato_depth = 0; + v->tremolo_phase = 0; + v->tremolo_step = 0; + v->tremolo_level = 0; + v->tremolo_rate = 0; + v->tremolo_depth = 0; + + if (!(v->mode & WAVE_LOOPING)) + v->mode &= ~(WAVE_BIDIR_LOOP | WAVE_LOOP_BACK); + else if (v->mode & WAVE_LOOP_BACK) + { + v->ptr = sample->len; + v->startbackloop = v->startloop; + } + if (v->mode & WAVE_VIBRATO) + { + v->vibrato_rate = sample->vibrato_rate; + v->vibrato_depth = sample->vibrato_depth; + } + if (v->mode & WAVE_TREMOLO) + { + v->tremolo_rate = sample->tremolo_rate; + v->tremolo_depth = sample->tremolo_depth; + } + if (v->mode & WAVE_ENVELOPES) + { + v->envelope_phase = -1; + v->envelope_vol = 0; + step_envelope(voice, 0, 60); + } + update_volume(voice); + compute_step(voice); + + voice_active[voice] = 1; /* Mark it active */ + restore_flags(flags); + return 0; +} + +static int softsyn_open(int synthdev, int mode) +{ + int err; + extern int softoss_dev; + int frags = 0x7fff0007; /* fragment size of 128 bytes */ + mm_segment_t fs; + + if (devc->audio_opened) /* Already opened */ + return 0; + + softsynth_disabled = 0; + devc->finfo.f_mode = FMODE_WRITE; + devc->finfo.f_flags = 0; + + if (softoss_dev >= num_audiodevs) + softoss_dev = num_audiodevs - 1; + + if (softoss_dev < 0) + softoss_dev = 0; + if (softoss_dev >= num_audiodevs) + return -ENXIO; + devc->audiodev = softoss_dev; + + if (!(audio_devs[devc->audiodev]->format_mask & AFMT_S16_LE)) + { +/* printk(KERN_ERR "SoftOSS: The audio device doesn't support 16 bits\n"); */ + return -ENXIO; + } + if ((err = audio_open((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo)) < 0) + return err; + + devc->speed = audio_devs[devc->audiodev]->d->set_speed( + devc->audiodev, devc->speed); + devc->channels = audio_devs[devc->audiodev]->d->set_channels( + devc->audiodev, devc->channels); + devc->bits = audio_devs[devc->audiodev]->d->set_bits( + devc->audiodev, devc->bits); + + + DDB(printk("SoftOSS: Using audio dev %d, speed %d, bits %d, channels %d\n", devc->audiodev, devc->speed, devc->bits, devc->channels)); + fs = get_fs(); + set_fs(get_ds()); + dma_ioctl(devc->audiodev, SNDCTL_DSP_SETFRAGMENT, (caddr_t) & frags); + dma_ioctl(devc->audiodev, SNDCTL_DSP_GETBLKSIZE, (caddr_t) & devc->fragsize); + set_fs(fs); + + if (devc->bits != 16 || devc->channels != 2) + { + audio_release((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo); +/* printk("SoftOSS: A 16 bit stereo sound card is required\n");*/ + return -EINVAL; + } + if (devc->max_playahead >= audio_devs[devc->audiodev]->dmap_out->nbufs) + devc->max_playahead = audio_devs[devc->audiodev]->dmap_out->nbufs; + + DDB(printk("SoftOSS: Using %d fragments of %d bytes\n", devc->max_playahead, devc->fragsize)); + + init_engine(devc); + devc->audio_opened = 1; + devc->sequencer_mode = mode; + return 0; +} + +static void softsyn_close(int synthdev) +{ + mm_segment_t fs; + + devc->engine_state = ES_STOPPED; +#ifdef POLLED_MODE + del_timer(&poll_timer); +#endif + fs = get_fs(); + set_fs(get_ds()); + dma_ioctl(devc->audiodev, SNDCTL_DSP_RESET, 0); + set_fs(fs); + if (devc->audio_opened) + audio_release((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo); + devc->audio_opened = 0; +} + +static void softsyn_hw_control(int dev, unsigned char *event_rec) +{ + int voice, cmd; + unsigned short p1, p2; + unsigned int plong; + + cmd = event_rec[2]; + voice = event_rec[3]; + p1 = *(unsigned short *) &event_rec[4]; + p2 = *(unsigned short *) &event_rec[6]; + plong = *(unsigned int *) &event_rec[4]; + + switch (cmd) + { + + case _GUS_NUMVOICES: + set_max_voices(p1); + break; + + default:; + } +} + +static int softsyn_load_patch(int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info *patch = NULL; + + int i, p, instr; + long sizeof_patch; + int memlen, adj; + unsigned short data; + short *wave = NULL; + + sizeof_patch = (long) &patch->data[0] - (long) patch; /* Header size */ + + if (format != GUS_PATCH) + { +/* printk(KERN_ERR "SoftOSS: Invalid patch format (key) 0x%x\n", format);*/ + return -EINVAL; + } + if (count < sizeof_patch) + { +/* printk(KERN_ERR "SoftOSS: Patch header too short\n");*/ + return -EINVAL; + } + count -= sizeof_patch; + + if (devc->nrsamples >= MAX_SAMPLE) + { +/* printk(KERN_ERR "SoftOSS: Sample table full\n");*/ + return -ENOBUFS; + } + + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + patch = vmalloc(sizeof(*patch)); + + if (patch == NULL) + { +/* printk(KERN_ERR "SoftOSS: Out of memory\n");*/ + return -ENOMEM; + } + if(copy_from_user(&((char *) patch)[offs], &(addr)[offs], sizeof_patch - offs)) + return -EFAULT; + + if (patch->mode & WAVE_ROM) + { + vfree(patch); + return -EINVAL; + } + instr = patch->instr_no; + + if (instr < 0 || instr > MAX_PATCH) + { +/* printk(KERN_ERR "SoftOSS: Invalid patch number %d\n", instr);*/ + vfree(patch); + return -EINVAL; + } + if (count < patch->len) + { +/* printk(KERN_ERR "SoftOSS: Patch record too short (%d<%d)\n", count, (int) patch->len);*/ + patch->len = count; + } + if (patch->len <= 0 || patch->len > (devc->ram_size - devc->ram_used)) + { +/* printk(KERN_ERR "SoftOSS: Invalid sample length %d\n", (int) patch->len); */ + vfree(patch); + return -EINVAL; + } + if (patch->mode & WAVE_LOOPING) + { + if (patch->loop_start < 0 || patch->loop_start >= patch->len) + { +/* printk(KERN_ERR "SoftOSS: Invalid loop start %d\n", patch->loop_start);*/ + vfree(patch); + return -EINVAL; + } + if (patch->loop_end < patch->loop_start || patch->loop_end > patch->len) + { +/* printk(KERN_ERR "SoftOSS: Invalid loop start or end point (%d, %d)\n", patch->loop_start, patch->loop_end);*/ + vfree(patch); + return -EINVAL; + } + } + /* + * Next load the wave data to memory + */ + + memlen = patch->len; + adj = 1; + + if (!(patch->mode & WAVE_16_BITS)) + memlen *= 2; + else + adj = 2; + + wave = vmalloc(memlen); + + if (wave == NULL) + { +/* printk(KERN_ERR "SoftOSS: Can't allocate %d bytes of mem for a sample\n", memlen);*/ + vfree(patch); + return -ENOMEM; + } + p = 0; + for (i = 0; i < memlen / 2; i++) /* Handle words */ + { + unsigned char tmp; + data = 0; + if (patch->mode & WAVE_16_BITS) + { + get_user(*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++])); /* Get lsb */ + data = tmp; + get_user(*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++])); /* Get msb */ + if (patch->mode & WAVE_UNSIGNED) + tmp ^= 0x80; /* Convert to signed */ + data |= (tmp << 8); + } + else + { + get_user(*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++])); + if (patch->mode & WAVE_UNSIGNED) + tmp ^= 0x80; /* Convert to signed */ + data = (tmp << 8); /* Convert to 16 bits */ + } + wave[i] = (short) data; + } + + devc->ram_used += patch->len; + + /* + * Convert pointers to 16 bit indexes + */ + patch->len /= adj; + patch->loop_start /= adj; + patch->loop_end /= adj; + + /* + * Finally link the loaded patch to the chain + */ + + patch->key = devc->programs[instr]; + devc->programs[instr] = devc->nrsamples; + devc->wave[devc->nrsamples] = (short *) wave; + devc->samples[devc->nrsamples++] = patch; + + return 0; +} + +static void softsyn_panning(int dev, int voice, int pan) +{ + if (voice < 0 || voice > devc->maxvoice) + return; + + if (pan < -128) + pan = -128; + if (pan > 127) + pan = 127; + + softoss_voices[voice].panning = pan; + if (voice_active[voice]) + update_volume(voice); +} + +static void softsyn_volume_method(int dev, int mode) +{ +} + +static void softsyn_aftertouch(int dev, int voice, int pressure) +{ + if (voice < 0 || voice > devc->maxvoice) + return; + + if (voice_active[voice]) + update_volume(voice); +} + +static void softsyn_controller(int dev, int voice, int ctrl_num, int value) +{ + unsigned long flags; + + if (voice < 0 || voice > devc->maxvoice) + return; + save_flags(flags); + cli(); + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + softoss_voices[voice].bender = value; + if (voice_active[voice]) + compute_step(voice); /* Update pitch */ + break; + + + case CTRL_PITCH_BENDER_RANGE: + softoss_voices[voice].bender_range = value; + break; + + case CTL_EXPRESSION: + value /= 128; + case CTRL_EXPRESSION: + softoss_voices[voice].expression_vol = value; + if (voice_active[voice]) + update_volume(voice); + break; + + case CTL_PAN: + softsyn_panning(dev, voice, (value * 2) - 128); + break; + + case CTL_MAIN_VOLUME: + value = (value * 100) / 16383; + + case CTRL_MAIN_VOLUME: + softoss_voices[voice].main_vol = value; + if (voice_active[voice]) + update_volume(voice); + break; + + default: + break; + } + restore_flags(flags); +} + +static void softsyn_bender(int dev, int voice, int value) +{ + if (voice < 0 || voice > devc->maxvoice) + return; + + softoss_voices[voice].bender = value - 8192; + if (voice_active[voice]) + compute_step(voice); /* Update pitch */ +} + +static int softsyn_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + int i, p, best = -1, best_time = 0x7fffffff; + + p = alloc->ptr; + + /* + * First look for a completely stopped voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0) + { + alloc->ptr = p; + voice_active[p] = 0; + return p; + } + if (alloc->alloc_times[p] < best_time) + { + best = p; + best_time = alloc->alloc_times[p]; + } + p = (p + 1) % alloc->max_voice; + } + + /* + * Then look for a releasing voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0xffff) + { + alloc->ptr = p; + voice_active[p] = 0; + return p; + } + p = (p + 1) % alloc->max_voice; + } + + if (best >= 0) + p = best; + + alloc->ptr = p; + voice_active[p] = 0; + return p; +} + +static void softsyn_setup_voice(int dev, int voice, int chn) +{ + unsigned long flags; + + struct channel_info *info = &synth_devs[dev]->chn_info[chn]; + + save_flags(flags); + cli(); + + /* init_voice(devc, voice); */ + softsyn_set_instr(dev, voice, info->pgm_num); + + softoss_voices[voice].expression_vol = info->controllers[CTL_EXPRESSION]; /* Just MSB */ + softoss_voices[voice].main_vol = (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128; + softsyn_panning(dev, voice, (info->controllers[CTL_PAN] * 2) - 128); + softoss_voices[voice].bender = 0; /* info->bender_value; */ + softoss_voices[voice].bender_range = info->bender_range; + + if (chn == 9) + softoss_voices[voice].percussive_voice = 1; + restore_flags(flags); +} + +static void softsyn_reset(int devno) +{ + int i; + unsigned long flags; + + save_flags(flags); + cli(); + + for (i = 0; i < devc->maxvoice; i++) + init_voice(devc, i); + restore_flags(flags); +} + +static struct synth_operations softsyn_operations = +{ + "SoftOSS", + &softsyn_info, + 0, + SYNTH_TYPE_SAMPLE, + 0, + softsyn_open, + softsyn_close, + softsyn_ioctl, + softsyn_kill_note, + softsyn_start_note, + softsyn_set_instr, + softsyn_reset, + softsyn_hw_control, + softsyn_load_patch, + softsyn_aftertouch, + softsyn_controller, + softsyn_panning, + softsyn_volume_method, + softsyn_bender, + softsyn_alloc_voice, + softsyn_setup_voice +}; + +/* + * Timer stuff (for /dev/music). + */ + +static unsigned int soft_tmr_start(int dev, unsigned int usecs) +{ + tmr_running = 1; + start_engine(devc); + return devc->usecs_per_frag; +} + +static void soft_tmr_disable(int dev) +{ + stop_engine(devc); + tmr_running = 0; +} + +static void soft_tmr_restart(int dev) +{ + tmr_running = 1; +} + +static struct sound_lowlev_timer soft_tmr = +{ + 0, + 9999, + soft_tmr_start, + soft_tmr_disable, + soft_tmr_restart +}; + +int probe_softsyn(struct address_info *hw_config) +{ + int i; + + if (softsynth_loaded) + return 0; + + devc->ram_size = 8 * 1024 * 1024; + devc->ram_used = 0; + devc->nrsamples = 0; + for (i = 0; i < MAX_PATCH; i++) + { + devc->programs[i] = NO_SAMPLE; + devc->wave[i] = NULL; + } + + devc->maxvoice = DEFAULT_VOICES; + + devc->audiodev = 0; + devc->audio_opened = 0; + devc->channels = 2; + devc->bits = 16; + devc->max_playahead = 32; + +#ifdef CONFIG_SOFTOSS_RATE + devc->speed = CONFIG_SOFTOSS_RATE; +#else + devc->speed = 32000; +#endif + +#ifdef CONFIG_SOFTOSS_VOICES + devc->default_max_voices = CONFIG_SOFTOSS_VOICES; +#else + devc->default_max_voices = 32; +#endif + softsynth_loaded = 1; + return 1; +} + +void attach_softsyn_card(struct address_info *hw_config) +{ + voice_alloc = &softsyn_operations.alloc; + synth_devs[devc->synthdev = num_synths++] = &softsyn_operations; + sequencer_init(); + sound_timer_init(&soft_tmr, "SoftOSS"); + devc->timerdev = num_sound_timers; + softsynthp = softsynth_hook; + +#ifndef POLLED_MODE +#endif +} + +void unload_softsyn(struct address_info *hw_config) +{ + if (!softsynth_loaded) + return; + softsynthp = NULL; + softsynth_loaded = 0; + reset_samples(devc); +} + +#ifdef MODULE + +static struct address_info config; + +int init_module(void) +{ + printk(KERN_INFO "SoftOSS driver Copyright (C) by Hannu Savolainen 1993-1997\n"); + if (!probe_softsyn(&config)) + return -ENODEV; + attach_softsyn_card(&config); + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + unload_softsyn(&config); + sound_unload_synthdev(devc->synthdev); + sound_unload_timerdev(devc->timerdev); + SOUND_LOCK_END; +} +#endif +#endif diff --git a/drivers/sound/softoss.h b/drivers/sound/softoss.h new file mode 100644 index 000000000000..42dab13dcd56 --- /dev/null +++ b/drivers/sound/softoss.h @@ -0,0 +1,161 @@ +/* + * softoss.h - Definitions for Software MIDI Synthesizer. + */ +/* + * 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. + */ + + +/* + * Sequencer mode1 timer calls made by sequencer.c + */ +extern int (*softsynthp) (int cmd, int parm1, int parm2, unsigned long parm3); + +#define SSYN_START 1 +#define SSYN_REQUEST 2 /* parm1 = time */ +#define SSYN_STOP 3 +#define SSYN_GETTIME 4 /* Returns number of ticks since reset */ + +#define MAX_PATCH 256 +#define MAX_SAMPLE 512 +#define MAX_VOICE 32 +#define DEFAULT_VOICES 16 + +typedef struct voice_info +{ +/* + * Don't change anything in the beginning of this struct. These fields are used + * by the resampling loop which may have been written in assembly for some + * architectures. Any change may make the resampling code incompatible + */ + int instr; + short *wave; + struct patch_info *sample; + + unsigned int ptr; int step; /* Pointer to the wave data and pointer increment */ + + int mode; + int startloop, startbackloop, endloop, looplen; + + unsigned int leftvol, rightvol; +/***** Don't change anything above this */ + + volatile unsigned long orig_freq, current_freq; + volatile int bender, bender_range, panning; + volatile int main_vol, expression_vol, patch_vol, velocity; + +/* Envelope parameters */ + + int envelope_phase; + volatile int envelope_vol; + volatile int envelope_volstep; + int envelope_time; /* Number of remaining envelope steps */ + unsigned int envelope_target; + int percussive_voice; + int sustain_mode; /* 0=off, 1=sustain on, 2=sustain on+key released */ + +/* Vibrato */ + int vibrato_rate; + int vibrato_depth; + int vibrato_phase; + int vibrato_step; + int vibrato_level; + +/* Tremolo */ + int tremolo_rate; + int tremolo_depth; + int tremolo_phase; + int tremolo_step; + int tremolo_level; +} voice_info; + +extern voice_info softoss_voices[MAX_VOICE]; /* Voice spesific info */ + +typedef struct softsyn_devc +{ +/* + * Don't change anything in the beginning of this struct. These fields are used + * by the resampling loop which may have been written in assembly for some + * architectures. Any change may make the resampling code incompatible + */ + int maxvoice; /* # of voices to be processed */ + int afterscale; + int delay_size; + int control_rate, control_counter; +/***** Don't change anything above this */ + + int ram_size; + int ram_used; + + int synthdev; + int timerdev; + int sequencer_mode; +/* + * Audio parameters + */ + + int audiodev; + int audio_opened; + int speed; + int channels; + int bits; + int default_max_voices; + int max_playahead; + struct file finfo; + int fragsize; + int samples_per_fragment; + +/* + * Sample storage + */ + int nrsamples; + struct patch_info *samples[MAX_SAMPLE]; + short *wave[MAX_SAMPLE]; + +/* + * Programs + */ + int programs[MAX_PATCH]; + +/* + * Timer parameters + */ + volatile unsigned long usecs; + volatile unsigned long usecs_per_frag; + volatile unsigned long next_event_usecs; + +/* + * Engine state + */ + + volatile int engine_state; +#define ES_STOPPED 0 +#define ES_STARTED 1 + + /* Voice spesific bitmaps */ + volatile int tremolomap; + volatile int vibratomap; + +} softsyn_devc; + +void softsynth_resample_loop(short *buf, int loops); +extern void softsyn_control_loop(void); + +#define DELAY_SIZE 4096 + +#ifdef SOFTSYN_MAIN + short voice_active[MAX_VOICE] = {0}; + voice_info softoss_voices[MAX_VOICE] = {{0}}; /* Voice spesific info */ + int left_delay[DELAY_SIZE]={0}, right_delay[DELAY_SIZE]={0}; + int delayp=0; +#else + extern softsyn_devc *devc; + + extern int left_delay[DELAY_SIZE], right_delay[DELAY_SIZE]; + extern int delayp; + extern short voice_active[MAX_VOICE]; +#endif diff --git a/drivers/sound/softoss_rs.c b/drivers/sound/softoss_rs.c new file mode 100644 index 000000000000..ac9714ff5617 --- /dev/null +++ b/drivers/sound/softoss_rs.c @@ -0,0 +1,133 @@ + +/* + * sound/softoss_rs.c + * + * Software based MIDI synthsesizer driver, the actual mixing loop. + * Keep the loop as simple as possible to make it easier to rewrite this + * routine in assembly. + * + * + * 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 + + +#include "sound_config.h" + +#ifdef CONFIG_SOFTOSS +#include "softoss.h" + +void softsynth_resample_loop(short *buf, int loops) +{ + int iloop, voice; + volatile voice_info *v; + +#ifdef OSS_BIG_ENDIAN + unsigned char *cbuf = (unsigned char *) buf; + +#endif + + for (iloop = 0; iloop < loops; iloop++) + { /* Mix one sample */ + int accum, left = 0, right = 0; + int ix, position; + + for (voice = 0; voice < devc->maxvoice; voice++) + { + if (voice_active[voice]) + { /* Compute voice */ + v = &softoss_voices[voice]; +#ifdef SOFTOSS_TEST + ix = iloop << 3; + position = v->ptr; +#else + ix = (position = v->ptr) >> 9; +#endif + /* Interpolation (resolution of 512 steps) */ + { + int fract = v->ptr & 0x1f; /* 9 bits */ + + /* This method works with less arithmetic operations */ + register int v1 = v->wave[ix]; + accum = v1 + ((((v->wave[ix + 1] - v1)) * (fract)) >> 9); + } + + left += (accum * v->leftvol); + right += (accum * v->rightvol); + + /* Update sample pointer */ + position += v->step; + if (position <= v->endloop) + v->ptr = position; + else if (v->mode & WAVE_LOOPING) + { + if (v->mode & WAVE_BIDIR_LOOP) + { v->mode ^= WAVE_LOOP_BACK; /* Turn around */ + v->step *= -1; + } + else + { + position -= v->looplen; + v->ptr = position; + } + } + /* else leave the voice looping the current sample */ + + if (v->mode & WAVE_LOOP_BACK && position < v->startloop) + { + if (v->mode & WAVE_BIDIR_LOOP) + { v->mode ^= WAVE_LOOP_BACK; /* Turn around */ + v->step *= -1; + } + else + { + position += v->looplen; + v->ptr = position; + } + } + } /* Compute voice */ + } +#if 1 /* Delay */ + left += left_delay[delayp]; + right += right_delay[delayp]; + + left_delay[delayp] = right >> 2; + right_delay[delayp] = left >> 2; + delayp = (delayp + 1) % devc->delay_size; +#endif + +#define AFTERSCALE devc->afterscale; + + left >>= AFTERSCALE; + right >>= AFTERSCALE; + + if (left > 32767) + left = 32767; + if (left < -32768) + left = -32768; + if (right > 32767) + right = 32767; + if (right < -32768) + right = -32768; + +#ifdef OSS_BIG_ENDIAN + *cbuf++ = left & 0xff; + *cbuf++ = (left >> 8) & 0xff; + *cbuf++ = right & 0xff; + *cbuf++ = (right >> 8) & 0xff; +#else + *buf++ = left; + *buf++ = right; +#endif + if (devc->control_counter++ >= devc->control_rate) + { + devc->control_counter = 0; + softsyn_control_loop(); + } + } /* Mix one sample */ +} +#endif diff --git a/drivers/sound/sonicvibes.c b/drivers/sound/sonicvibes.c new file mode 100644 index 000000000000..609edbdedb5a --- /dev/null +++ b/drivers/sound/sonicvibes.c @@ -0,0 +1,2884 @@ +/*****************************************************************************/ + +/* + * sonicvibes.c -- S3 Sonic Vibes audio driver. + * + * Copyright (C) 1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * 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. + * + * Special thanks to David C. Niemi + * + * + * Module command line parameters: + * none so far + * + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * /dev/midi simple MIDI UART interface, no ioctl + * + * The card has both an FM and a Wavetable synth, but I have to figure + * out first how to drive them... + * + * Revision history + * 06.05.98 0.1 Initial release + * 10.05.98 0.2 Fixed many bugs, esp. ADC rate calculation + * First stab at a simple midi interface (no bells&whistles) + * 13.05.98 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of + * set_dac_rate in the FMODE_WRITE case in sv_open + * Fix hwptr out of bounds (now mpg123 works) + * 14.05.98 0.4 Don't allow excessive interrupt rates + * 08.06.98 0.5 First release using Alan Cox' soundcore instead of miscdevice + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < 131421 +#include +#endif +#include +#include +#include + +#include "dm.h" + +/* --------------------------------------------------------------------- */ + +#ifndef PCI_VENDOR_ID_S3 +#define PCI_VENDOR_ID_S3 0x5333 +#endif +#ifndef PCI_DEVICE_ID_S3_SONICVIBES +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 +#endif + +#define SV_MAGIC ((PCI_VENDOR_ID_S3<<16)|PCI_DEVICE_ID_S3_SONICVIBES) + +#define SV_EXTENT_SB 0x10 +#define SV_EXTENT_ENH 0x10 +#define SV_EXTENT_SYNTH 0x4 +#define SV_EXTENT_MIDI 0x4 +#define SV_EXTENT_GAME 0x8 +#define SV_EXTENT_DMA 0x10 + + +#define SV_MIDI_DATA 0 +#define SV_MIDI_COMMAND 1 +#define SV_MIDI_STATUS 1 + +#define SV_DMA_ADDR0 0 +#define SV_DMA_ADDR1 1 +#define SV_DMA_ADDR2 2 +#define SV_DMA_ADDR3 3 +#define SV_DMA_COUNT0 4 +#define SV_DMA_COUNT1 5 +#define SV_DMA_COUNT2 6 +#define SV_DMA_MODE 0xb +#define SV_DMA_RESET 0xd +#define SV_DMA_MASK 0xf + +/* + * DONT reset the DMA controllers unless you understand + * the reset semantics. Assuming reset semantics as in + * the 8237 does not work. + */ + +#define DMA_MODE_AUTOINIT 0x10 +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ + +#define SV_CODEC_CONTROL 0 +#define SV_CODEC_INTMASK 1 +#define SV_CODEC_STATUS 2 +#define SV_CODEC_IADDR 4 +#define SV_CODEC_IDATA 5 + +#define SV_CCTRL_RESET 0x80 +#define SV_CCTRL_INTADRIVE 0x20 +#define SV_CCTRL_WAVETABLE 0x08 +#define SV_CCTRL_REVERB 0x04 +#define SV_CCTRL_ENHANCED 0x01 + +#define SV_CINTMASK_DMAA 0x01 +#define SV_CINTMASK_DMAC 0x04 +#define SV_CINTMASK_SPECIAL 0x08 +#define SV_CINTMASK_UPDOWN 0x40 +#define SV_CINTMASK_MIDI 0x80 + +#define SV_CSTAT_DMAA 0x01 +#define SV_CSTAT_DMAC 0x04 +#define SV_CSTAT_SPECIAL 0x08 +#define SV_CSTAT_UPDOWN 0x40 +#define SV_CSTAT_MIDI 0x80 + +#define SV_CIADDR_TRD 0x80 +#define SV_CIADDR_MCE 0x40 + +/* codec indirect registers */ +#define SV_CIMIX_ADCINL 0x00 +#define SV_CIMIX_ADCINR 0x01 +#define SV_CIMIX_AUX1INL 0x02 +#define SV_CIMIX_AUX1INR 0x03 +#define SV_CIMIX_CDINL 0x04 +#define SV_CIMIX_CDINR 0x05 +#define SV_CIMIX_LINEINL 0x06 +#define SV_CIMIX_LINEINR 0x07 +#define SV_CIMIX_MICIN 0x08 +#define SV_CIMIX_SYNTHINL 0x0A +#define SV_CIMIX_SYNTHINR 0x0B +#define SV_CIMIX_AUX2INL 0x0C +#define SV_CIMIX_AUX2INR 0x0D +#define SV_CIMIX_ANALOGINL 0x0E +#define SV_CIMIX_ANALOGINR 0x0F +#define SV_CIMIX_PCMINL 0x10 +#define SV_CIMIX_PCMINR 0x11 + +#define SV_CIGAMECONTROL 0x09 +#define SV_CIDATAFMT 0x12 +#define SV_CIENABLE 0x13 +#define SV_CIUPDOWN 0x14 +#define SV_CIREVISION 0x15 +#define SV_CIADCOUTPUT 0x16 +#define SV_CIDMAABASECOUNT1 0x18 +#define SV_CIDMAABASECOUNT0 0x19 +#define SV_CIDMACBASECOUNT1 0x1c +#define SV_CIDMACBASECOUNT0 0x1d +#define SV_CIPCMSR0 0x1e +#define SV_CIPCMSR1 0x1f +#define SV_CISYNTHSR0 0x20 +#define SV_CISYNTHSR1 0x21 +#define SV_CIADCCLKSOURCE 0x22 +#define SV_CIADCALTSR 0x23 +#define SV_CIADCPLLM 0x24 +#define SV_CIADCPLLN 0x25 +#define SV_CISYNTHPLLM 0x26 +#define SV_CISYNTHPLLN 0x27 +#define SV_CIUARTCONTROL 0x2a +#define SV_CIDRIVECONTROL 0x2b +#define SV_CISRSSPACE 0x2c +#define SV_CISRSCENTER 0x2d +#define SV_CIWAVETABLESRC 0x2e +#define SV_CIANALOGPWRDOWN 0x30 +#define SV_CIDIGITALPWRDOWN 0x31 + + +#define SV_CIMIX_ADCSRC_CD 0x20 +#define SV_CIMIX_ADCSRC_DAC 0x40 +#define SV_CIMIX_ADCSRC_AUX2 0x60 +#define SV_CIMIX_ADCSRC_LINE 0x80 +#define SV_CIMIX_ADCSRC_AUX1 0xa0 +#define SV_CIMIX_ADCSRC_MIC 0xc0 +#define SV_CIMIX_ADCSRC_MIXOUT 0xe0 +#define SV_CIMIX_ADCSRC_MASK 0xe0 + +#define SV_CFMT_STEREO 0x01 +#define SV_CFMT_16BIT 0x02 +#define SV_CFMT_MASK 0x03 +#define SV_CFMT_ASHIFT 0 +#define SV_CFMT_CSHIFT 4 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +#define SV_CENABLE_PPE 0x4 +#define SV_CENABLE_RE 0x2 +#define SV_CENABLE_PE 0x1 + + +/* MIDI buffer sizes */ + +#define MIDIINBUF 256 +#define MIDIOUTBUF 256 + +#define FMODE_MIDI_SHIFT 2 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define FMODE_DMFM 0x10 + +#define SND_DEV_DSP16 5 + +/* --------------------------------------------------------------------- */ + +/* Linux 2.0 compatibility stuff */ + +#ifndef SNDCTL_DSP_GETODELAY +#define SNDCTL_DSP_GETODELAY _IOR ('P', 23, int) +#endif + +#if LINUX_VERSION_CODE < 131328 + +#define __init +#define __initdata +#define __initfunc(x) x + +typedef unsigned long mm_segment_t; + +#include +#include + +#undef put_user +#undef get_user + +#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) +#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) + +#define put_user_ret(x,ptr,ret) ({ if (put_user(x,ptr)) return ret; }) +#define get_user_ret(x,ptr,ret) ({ if (get_user(x,ptr)) return ret; }) + +extern inline int copy_to_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_WRITE, to, n); + if (i) + return i; + memcpy_tofs(to, from, n); + return 0; +} + +extern inline int copy_from_user(void *to, const void *from, unsigned long n) +{ + int i = verify_area(VERIFY_READ, from, n); + if (i) + return i; + memcpy_fromfs(to, from, n); + return 0; +} + +#define access_ok(x,y,z) (!verify_area(x,y,z)) + +typedef struct { } spinlock_t; +#define SPIN_LOCK_UNLOCKED { } + +#define spin_lock_init(lock) do { } while(0) +#define spin_lock(lock) do { } while(0) +#define spin_trylock(lock) do { } while(0) +#define spin_unlock_wait(lock) do { } while(0) +#define spin_unlock(lock) do { } while(0) +#define spin_lock_irq(lock) cli() +#define spin_unlock_irq(lock) sti() + +#define spin_lock_irqsave(lock, flags) \ + do { save_flags(flags); cli(); } while (0) +#define spin_unlock_irqrestore(lock, flags) \ + restore_flags(flags) + +#define signal_pending(x) ((x)->signal & ~(x)->blocked) + +#define synchronize_irq() + +#else + +#include +#include +#include +#include +#include + +#endif + +#ifndef OSS_GETVERSION +#define OSS_GETVERSION _IOR ('M', 118, int) +#endif + +/* --------------------------------------------------------------------- */ + +struct sv_state { + /* magic */ + unsigned int magic; + + /* we keep sv cards in a linked list */ + struct sv_state *next; + + /* soundcore stuff */ + int dev_audio; + int dev_mixer; + int dev_midi; + int dev_dmfm; + + /* hardware resources */ + unsigned int iosb, ioenh, iosynth, iomidi, iogame, iodmaa, iodmac, irq; + + /* mixer stuff */ + struct { + unsigned int modcnt; + } mix; + + /* wave stuff */ + unsigned int rateadc, ratedac; + unsigned char fmt, enable; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + struct wait_queue *open_wait; + + struct dmabuf { + void *rawbuf; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + unsigned hwptr, swptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + struct wait_queue *wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + /* midi stuff */ + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + struct wait_queue *iwait; + struct wait_queue *owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; +}; + +/* --------------------------------------------------------------------- */ + +static struct sv_state *devs = NULL; +static unsigned long wavetable_mem = 0; + +/* --------------------------------------------------------------------- */ + +extern __inline__ unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ + +#ifdef hweight32 +#undef hweight32 +#endif + +extern __inline__ unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + +/* --------------------------------------------------------------------- */ + +/* + * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver. + */ + +#undef DMABYTEIO + +static void set_dmaa(struct sv_state *s, unsigned int addr, unsigned int count) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmaa, u; + + count--; + for (u = 4; u > 0; u--, addr >>= 8, io++) + outb(addr & 0xff, io); + for (u = 3; u > 0; u--, count >>= 8, io++) + outb(count & 0xff, io); +#else /* DMABYTEIO */ + count--; + outl(addr, s->iodmaa + SV_DMA_ADDR0); + outl(count, s->iodmaa + SV_DMA_COUNT0); +#endif /* DMABYTEIO */ + outb(0x18, s->iodmaa + SV_DMA_MODE); +} + +static void set_dmac(struct sv_state *s, unsigned int addr, unsigned int count) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmac, u; + + count >>= 1; + count--; + for (u = 4; u > 0; u--, addr >>= 8, io++) + outb(addr & 0xff, io); + for (u = 3; u > 0; u--, count >>= 8, io++) + outb(count & 0xff, io); +#else /* DMABYTEIO */ + count >>= 1; + count--; + outl(addr, s->iodmac + SV_DMA_ADDR0); + outl(count, s->iodmac + SV_DMA_COUNT0); +#endif /* DMABYTEIO */ + outb(0x14, s->iodmac + SV_DMA_MODE); +} + +extern __inline__ unsigned get_dmaa(struct sv_state *s) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmaa+6, v = 0, u; + + for (u = 3; u > 0; u--, io--) { + v <<= 8; + v |= inb(io); + } + return v + 1; +#else /* DMABYTEIO */ + return (inl(s->iodmaa + SV_DMA_COUNT0) & 0xffffff) + 1; +#endif /* DMABYTEIO */ +} + +extern __inline__ unsigned get_dmac(struct sv_state *s) +{ +#ifdef DMABYTEIO + unsigned io = s->iodmac+6, v = 0, u; + + for (u = 3; u > 0; u--, io--) { + v <<= 8; + v |= inb(io); + } + return (v + 1) << 1; +#else /* DMABYTEIO */ + return ((inl(s->iodmac + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; +#endif /* DMABYTEIO */ +} + +static void wrindir(struct sv_state *s, unsigned char idx, unsigned char data) +{ + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(data, s->ioenh + SV_CODEC_IDATA); + udelay(10); +} + +static unsigned char rdindir(struct sv_state *s, unsigned char idx) +{ + unsigned char v; + + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + v = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + return v; +} + +static void set_fmt(struct sv_state *s, unsigned char mask, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + outb(SV_CIDATAFMT | SV_CIADDR_MCE, s->ioenh + SV_CODEC_IADDR); + if (mask) { + s->fmt = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + } + s->fmt = (s->fmt & mask) | data; + outb(s->fmt, s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(0, s->ioenh + SV_CODEC_IADDR); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); +} + +static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, unsigned char data) +{ + outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb((inb(s->ioenh + SV_CODEC_IDATA) & mask) ^ data, s->ioenh + SV_CODEC_IDATA); + udelay(10); +} + +#define REFFREQUENCY 24576000 +#define ADCMULT 512 +#define FULLRATE 48000 + +static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate) +{ + unsigned long flags; + unsigned char r, m, n; + unsigned xm, xn, xr, xd, metric = ~0U; + + if (rate < 625000/ADCMULT) + rate = 625000/ADCMULT; + if (rate > 150000000/ADCMULT) + rate = 150000000/ADCMULT; + /* slight violation of specs, needed for continuous sampling rates */ + for (r = 0; rate < 75000000/ADCMULT; r += 0x20, rate <<= 1); + for (xn = 3; xn < 35; xn++) + for (xm = 3; xm < 130; xm++) { + xr = REFFREQUENCY/ADCMULT * xm / xn; + xd = abs((signed)(xr - rate)); + if (xd < metric) { + metric = xd; + m = xm - 2; + n = xn - 2; + } + } + reg &= 0x3f; + spin_lock_irqsave(&s->lock, flags); + outb(reg, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(m, s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(reg+1, s->ioenh + SV_CODEC_IADDR); + udelay(10); + outb(r | n, s->ioenh + SV_CODEC_IDATA); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); + return (REFFREQUENCY/ADCMULT * (m + 2) / (n + 2)) >> ((r >> 5) & 7); +} + +#if 0 + +static unsigned getpll(struct sv_state *s, unsigned char reg) +{ + unsigned long flags; + unsigned char m, n; + + reg &= 0x3f; + spin_lock_irqsave(&s->lock, flags); + outb(reg, s->ioenh + SV_CODEC_IADDR); + udelay(10); + m = inb(s->ioenh + SV_CODEC_IDATA); + udelay(10); + outb(reg+1, s->ioenh + SV_CODEC_IADDR); + udelay(10); + n = inb(s->ioenh + SV_CODEC_IDATA); + spin_unlock_irqrestore(&s->lock, flags); + udelay(10); + return (REFFREQUENCY/ADCMULT * (m + 2) / ((n & 0x1f) + 2)) >> ((n >> 5) & 7); +} + +#endif + +static void set_dac_rate(struct sv_state *s, unsigned rate) +{ + unsigned div; + unsigned long flags; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + div = (rate * 65536 + FULLRATE/2) / FULLRATE; + if (div > 65535) + div = 65535; + spin_lock_irqsave(&s->lock, flags); + wrindir(s, SV_CIPCMSR1, div >> 8); + wrindir(s, SV_CIPCMSR0, div); + spin_unlock_irqrestore(&s->lock, flags); + s->ratedac = (div * FULLRATE + 32768) / 65536; +} + +static void set_adc_rate(struct sv_state *s, unsigned rate) +{ + unsigned long flags; + unsigned rate1, rate2, div; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + rate1 = setpll(s, SV_CIADCPLLM, rate); + div = (48000 + rate/2) / rate; + if (div > 8) + div = 8; + rate2 = (48000 + div/2) / div; + spin_lock_irqsave(&s->lock, flags); + wrindir(s, SV_CIADCALTSR, (div-1) << 4); + if (abs((signed)(rate-rate2)) <= abs((signed)(rate-rate1))) { + wrindir(s, SV_CIADCCLKSOURCE, 0x10); + s->rateadc = rate2; + } else { + wrindir(s, SV_CIADCCLKSOURCE, 0x00); + s->rateadc = rate1; + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +extern inline void stop_adc(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->enable &= ~SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + s->enable &= ~(SV_CENABLE_PPE | SV_CENABLE_PE); + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) { + s->enable = (s->enable & ~SV_CENABLE_PPE) | SV_CENABLE_PE; + wrindir(s, SV_CIENABLE, s->enable); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct sv_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + if ((s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize) + && s->dma_adc.ready) { + s->enable |= SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + } + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER 8 +#define DMABUF_MINORDER 1 + + +static void dealloc_dmabuf(struct dmabuf *db) +{ + unsigned long map, mapend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + free_pages((unsigned long)db->rawbuf, db->buforder); + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + + +/* DMAA is used for playback, DMAC is used for recording */ + +static int prog_dmabuf(struct sv_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + int order; + unsigned bytepersec; + unsigned bufs; + unsigned long map, mapend; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + fmt = s->fmt; + if (rec) { + s->enable &= ~SV_CENABLE_RE; + fmt >>= SV_CFMT_CSHIFT; + } else { + s->enable &= ~SV_CENABLE_PE; + fmt >>= SV_CFMT_ASHIFT; + } + wrindir(s, SV_CIENABLE, s->enable); + spin_unlock_irqrestore(&s->lock, flags); + fmt &= SV_CFMT_MASK; + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + if (!db->rawbuf) { + db->ready = db->mapped = 0; +#if LINUX_VERSION_CODE < 131328 + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) + db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order, 1); +#else + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) + db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order); +#endif + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff) + printk(KERN_DEBUG "sv: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n", + virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff) + printk(KERN_DEBUG "sv: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n", + virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder); + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &mem_map[map].flags); + } + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & SV_CFMT_16BIT) ? 0 : 0x80, db->dmasize); + spin_lock_irqsave(&s->lock, flags); + if (rec) { + set_dmac(s, virt_to_bus(db->rawbuf), db->numfrag << db->fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMACBASECOUNT1, (db->fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, db->fragsamples-1); + } else { + set_dmaa(s, virt_to_bus(db->rawbuf), db->numfrag << db->fragshift); + /* program enhanced mode registers */ + wrindir(s, SV_CIDMAABASECOUNT1, (db->fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, db->fragsamples-1); + } + spin_unlock_irqrestore(&s->lock, flags); + db->ready = 1; + return 0; +} + +extern __inline__ void clear_advance(struct sv_state *s) +{ + unsigned char c = (s->fmt & (SV_CFMT_16BIT << SV_CFMT_ASHIFT)) ? 0 : 0x80; + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); +} + +/* call with spinlock held! */ +static void sv_update_ptr(struct sv_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + hwptr = (s->dma_adc.dmasize - get_dmac(s)) % s->dma_adc.dmasize; + diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + wake_up(&s->dma_adc.wait); + } else { + if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) { + s->enable &= ~SV_CENABLE_RE; + wrindir(s, SV_CIENABLE, s->enable); + s->dma_adc.error++; + } + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + hwptr = (s->dma_dac.dmasize - get_dmaa(s)) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + s->enable &= ~SV_CENABLE_PE; + wrindir(s, SV_CIENABLE, s->enable); + s->dma_dac.error++; + } else if (s->dma_dac.count <= s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count < s->dma_dac.dmasize) + wake_up(&s->dma_dac.wait); + } + } +} + +/* hold spinlock for the following! */ +static void sv_handle_midi(struct sv_state *s) +{ + unsigned char ch; + int wake; + + wake = 0; + while (!(inb(s->iomidi+1) & 0x80)) { + ch = inb(s->iomidi); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) { + outb(s->midi.obuf[s->midi.ord], s->iomidi); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF-16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + +static void sv_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sv_state *s = (struct sv_state *)dev_id; + unsigned int intsrc; + + /* fastpath out, to ease interrupt sharing */ + intsrc = inb(s->ioenh + SV_CODEC_STATUS); + if (!(intsrc & (SV_CSTAT_DMAA | SV_CSTAT_DMAC | SV_CSTAT_MIDI))) + return; + spin_lock(&s->lock); + sv_update_ptr(s); + sv_handle_midi(s); + spin_unlock(&s->lock); +} + +static void sv_midi_timer(unsigned long data) +{ + struct sv_state *s = (struct sv_state *)data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies+1; + add_timer(&s->midi.timer); +} + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "sv: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != SV_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +/* --------------------------------------------------------------------- */ + +#define MT_4 1 +#define MT_5MUTE 2 +#define MT_4MUTEMONO 3 +#define MT_6MUTE 4 + +static const struct { + unsigned left:5; + unsigned right:5; + unsigned type:3; + unsigned rec:3; +} mixtable[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_RECLEV] = { SV_CIMIX_ADCINL, SV_CIMIX_ADCINR, MT_4, 0 }, + [SOUND_MIXER_LINE1] = { SV_CIMIX_AUX1INL, SV_CIMIX_AUX1INR, MT_5MUTE, 5 }, + [SOUND_MIXER_CD] = { SV_CIMIX_CDINL, SV_CIMIX_CDINR, MT_5MUTE, 1 }, + [SOUND_MIXER_LINE] = { SV_CIMIX_LINEINL, SV_CIMIX_LINEINR, MT_5MUTE, 4 }, + [SOUND_MIXER_MIC] = { SV_CIMIX_MICIN, SV_CIMIX_ADCINL, MT_4MUTEMONO, 6 }, + [SOUND_MIXER_SYNTH] = { SV_CIMIX_SYNTHINL, SV_CIMIX_SYNTHINR, MT_5MUTE, 2 }, + [SOUND_MIXER_LINE2] = { SV_CIMIX_AUX2INL, SV_CIMIX_AUX2INR, MT_5MUTE, 3 }, + [SOUND_MIXER_VOLUME] = { SV_CIMIX_ANALOGINL, SV_CIMIX_ANALOGINR, MT_5MUTE, 7 }, + [SOUND_MIXER_PCM] = { SV_CIMIX_PCMINL, SV_CIMIX_PCMINR, MT_6MUTE, 0 } +}; + +static int return_mixval(struct sv_state *s, unsigned i, int *arg) +{ + unsigned long flags; + unsigned char l, r, rl, rr; + + spin_lock_irqsave(&s->lock, flags); + l = rdindir(s, mixtable[i].left); + r = rdindir(s, mixtable[i].right); + spin_unlock_irqrestore(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + r &= 0xf; + l &= 0xf; + rl = 10 + 6 * (l & 15); + rr = 10 + 6 * (r & 15); + break; + + case MT_4MUTEMONO: + rl = 55 - 3 * (l & 15); + if (r & 0x10) + rl += 45; + rr = rl; + r = l; + break; + + case MT_5MUTE: + default: + rl = 100 - 3 * (l & 31); + rr = 100 - 3 * (r & 31); + break; + + case MT_6MUTE: + rl = 100 - 3 * (l & 63) / 2; + rr = 100 - 3 * (r & 63) / 2; + break; + } + if (l & 0x80) + rl = 0; + if (r & 0x80) + rr = 0; + return put_user((rr << 8) | rl, arg); +} + +static unsigned mixer_recmask(struct sv_state *s) +{ + unsigned long flags; + int i, j; + + spin_lock_irqsave(&s->lock, flags); + j = rdindir(s, SV_CIMIX_ADCINL) >> 5; + spin_unlock_irqrestore(&s->lock, flags); + j &= 7; + for (i = 0; i < SOUND_MIXER_NRDEVICES && mixtable[i].rec != j; i++); + return 1 << i; +} + +static int mixer_ioctl(struct sv_state *s, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + int i, val; + unsigned char l, r, rl, rr; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE < 131328 + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } +#else + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "SonicVibes", sizeof(info.id)); + strncpy(info.name, "S3 SonicVibes", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } +#endif + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (cmd == SOUND_MIXER_PRIVATE1) { /* SRS settings */ + get_user_ret(val, (int *)arg, -EFAULT); + spin_lock_irqsave(&s->lock, flags); + if (val & 1) { + if (val & 2) { + l = 4 - ((val >> 2) & 7); + if (l & ~3) + l = 4; + r = 4 - ((val >> 5) & 7); + if (r & ~3) + r = 4; + wrindir(s, SV_CISRSSPACE, l); + wrindir(s, SV_CISRSCENTER, r); + } else + wrindir(s, SV_CISRSSPACE, 0x80); + } + l = rdindir(s, SV_CISRSSPACE); + r = rdindir(s, SV_CISRSCENTER); + spin_unlock_irqrestore(&s->lock, flags); + if (l & 0x80) + return put_user(0, (int *)arg); + return put_user(((4 - (l & 7)) << 2) | ((4 - (r & 7)) << 5) | 2, (int *)arg); + } + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + return put_user(mixer_recmask(s), (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].rec) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO) + val |= 1 << i; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + return return_mixval(s, i, (int *)arg); + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + s->mix.modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + get_user_ret(val, (int *)arg, -EFAULT); + i = hweight32(val); + if (i == 0) + return 0; /*val = mixer_recmask(s);*/ + else if (i > 1) + val &= ~mixer_recmask(s); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (mixtable[i].rec) + break; + } + if (!mixtable[i].rec) + return 0; + spin_lock_irqsave(&s->lock, flags); + frobindir(s, SV_CIMIX_ADCINL, 0x1f, mixtable[i].rec << 5); + frobindir(s, SV_CIMIX_ADCINR, 0x1f, mixtable[i].rec << 5); + spin_unlock_irqrestore(&s->lock, flags); + return 0; + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + l = val & 0xff; + r = (val >> 8) & 0xff; + if (mixtable[i].type == MT_4MUTEMONO) + l = (r + l) / 2; + if (l > 100) + l = 100; + if (r > 100) + r = 100; + spin_lock_irqsave(&s->lock, flags); + switch (mixtable[i].type) { + case MT_4: + if (l >= 10) + l -= 10; + if (r >= 10) + r -= 10; + frobindir(s, mixtable[i].left, 0xf0, l / 6); + frobindir(s, mixtable[i].right, 0xf0, l / 6); + break; + + case MT_4MUTEMONO: + rr = 0; + if (l < 10) + rl = 0x80; + else { + if (l >= 55) { + rr = 0x10; + l -= 45; + } + rl = (55 - l) / 3; + } + wrindir(s, mixtable[i].left, rl); + frobindir(s, mixtable[i].right, ~0x10, rr); + break; + + case MT_5MUTE: + if (l < 7) + rl = 0x80; + else + rl = (100 - l) / 3; + if (r < 7) + rr = 0x80; + else + rr = (100 - r) / 3; + wrindir(s, mixtable[i].left, rl); + wrindir(s, mixtable[i].right, rr); + break; + + case MT_6MUTE: + if (l < 6) + rl = 0x80; + else + rl = (100 - l) * 2 / 3; + if (r < 6) + rr = 0x80; + else + rr = (100 - r) * 2 / 3; + wrindir(s, mixtable[i].left, rl); + wrindir(s, mixtable[i].right, rr); + break; + } + spin_unlock_irqrestore(&s->lock, flags); + return return_mixval(s, i, (int *)arg); + } +} + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int sv_llseek(struct inode *ino, struct file *file, off_t offset, int origin) +{ + return -ESPIPE; +} +#else +static loff_t sv_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} +#endif + +/* --------------------------------------------------------------------- */ + +static int sv_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct sv_state *s = devs; + + while (s && s->dev_mixer != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + MOD_INC_USE_COUNT; + return 0; +} + +static int sv_release_mixdev(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + + VALIDATE_STATE(s); + MOD_DEC_USE_COUNT; + return 0; +} + +static int sv_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct sv_state *)file->private_data, cmd, arg); +} + +static /*const*/ struct file_operations sv_mixer_fops = { + &sv_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &sv_ioctl_mixdev, + NULL, /* mmap */ + &sv_open_mixdev, + &sv_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct sv_state *s, int nonblock) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped) + return 0; + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / s->ratedac; + tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; + current->timeout = jiffies + (tmo ? tmo : 1); + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "sv: dma timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int sv_read(struct inode *ino, struct file *file, char *buffer, int count) +#else +static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct sv_state *s = (struct sv_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; +#if 0 + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); +#endif + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 +static int sv_write(struct inode *ino, struct file *file, const char *buffer, int count) +#else +static ssize_t sv_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct sv_state *s = (struct sv_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; +#if 0 + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + spin_unlock_irqrestore(&s->lock, flags); +#endif + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->dma_dac.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 + +/* well select really */ +static int sv_poll(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + int ret = 0; + + VALIDATE_STATE(s); + if (sel_type == SEL_IN && file->f_mode & FMODE_READ) { + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + ret = 1; + } else { + if (s->dma_adc.count > 0) + ret = 1; + } + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->dma_adc.wait, wait); + } + if (sel_type == SEL_OUT && file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= s->dma_dac.fragsize) + ret = 1; + } else { + if (s->dma_dac.dmasize > s->dma_dac.count) + ret = 1; + } + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->dma_dac.wait, wait); + } + return 0; +} + +#else + +static unsigned int sv_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + if (file->f_flags & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + if (file->f_flags & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if (s->dma_dac.dmasize > s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +#endif + +#if LINUX_VERSION_CODE < 131328 +static int sv_mmap(struct inode *ino, struct file *file, struct vm_area_struct *vma) +#else +static int sv_mmap(struct file *file, struct vm_area_struct *vma) +#endif +{ + struct sv_state *s = (struct sv_state *)file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 1)) != 0) + return ret; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; +#if LINUX_VERSION_CODE < 131328 + vma->vm_inode = ino; + ino->i_count++; +#else + vma->vm_file = file; + file->f_count++; +#endif + return 0; +} + +static int sv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + get_user_ret(val, (int *)arg, -EFAULT); + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val) + fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) + : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + fmtd = 0; + fmtm = ~0; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; + else + fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_CSHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; + else + fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_ASHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) + : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && s->enable & SV_CENABLE_RE) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && s->enable & SV_CENABLE_PE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->enable & SV_CENABLE_PE) && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(s->enable & SV_CENABLE_RE) && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + sv_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + cinfo.blocks = s->dma_dac.total_bytes >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_RATE: + case SOUND_PCM_READ_CHANNELS: + case SOUND_PCM_READ_BITS: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return mixer_ioctl(s, cmd, arg); +} + +static int sv_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct sv_state *s = devs; + unsigned char fmtm = ~0, fmts = 0; + + while (s && ((s->dev_audio ^ minor) & ~0xf)) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + if (file->f_mode & FMODE_READ) { + fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_CSHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= SV_CFMT_16BIT << SV_CFMT_CSHIFT; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_ASHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= SV_CFMT_16BIT << SV_CFMT_ASHIFT; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int sv_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_flags & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(&s->dma_dac); + } + if (file->f_flags & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(&s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations sv_audio_fops = { + &sv_llseek, + &sv_read, + &sv_write, + NULL, /* readdir */ + &sv_poll, + &sv_ioctl, + &sv_mmap, + &sv_open, + &sv_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE < 131328 +static int sv_midi_read(struct inode *ino, struct file *file, char *buffer, int count) +#else +static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct sv_state *s = (struct sv_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 +static int sv_midi_write(struct inode *ino, struct file *file, const char *buffer, int count) +#else +static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +#endif +{ + struct sv_state *s = (struct sv_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); +#if LINUX_VERSION_CODE >= 131328 + if (ppos != &file->f_pos) + return -ESPIPE; +#endif + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EBUSY; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + sv_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + +#if LINUX_VERSION_CODE < 131328 + +/* well select really */ +static int sv_midi_poll(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + int ret = 0; + + VALIDATE_STATE(s); + if (sel_type == SEL_IN && file->f_mode & FMODE_READ) { + spin_lock_irqsave(&s->lock, flags); + if (s->midi.icnt > 0) + ret = 1; + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->dma_adc.wait, wait); + } + if (sel_type == SEL_OUT && file->f_mode & FMODE_WRITE) { + spin_lock_irqsave(&s->lock, flags); + if (s->midi.ocnt < MIDIOUTBUF) + ret = 1; + spin_unlock_irqrestore(&s->lock, flags); + if (ret) + return 1; + select_wait(&s->dma_dac.wait, wait); + } + return 0; +} + +#else + +static unsigned int sv_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +#endif + +static int sv_midi_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct sv_state *s = devs; + unsigned long flags; + + while (s && s->dev_midi != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + //outb(inb(s->ioenh + SV_CODEC_CONTROL) | SV_CCTRL_WAVETABLE, s->ioenh + SV_CODEC_CONTROL); + outb(inb(s->ioenh + SV_CODEC_INTMASK) | SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); + wrindir(s, SV_CIUARTCONTROL, 5); /* output MIDI data to external and internal synth */ + wrindir(s, SV_CIWAVETABLESRC, 1); /* Wavetable in PC RAM */ + outb(0xff, s->iomidi+1); /* reset command */ + outb(0x3f, s->iomidi+1); /* uart command */ + if (!(inb(s->iomidi+1) & 0x80)) + inb(s->iomidi); + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies+1; + s->midi.timer.data = (unsigned long)s; + s->midi.timer.function = sv_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int sv_midi_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + struct wait_queue wait = { current, NULL }; + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + current->timeout = tmo ? jiffies + tmo : 0; + schedule(); + if (tmo && !current->timeout) + printk(KERN_DEBUG "sv: midi timed out??\n"); + current->timeout = 0; + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&s->open_sem); + s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + outb(inb(s->ioenh + SV_CODEC_INTMASK) & ~SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK); + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations sv_midi_fops = { + &sv_llseek, + &sv_midi_read, + &sv_midi_write, + NULL, /* readdir */ + &sv_midi_poll, + NULL, /* ioctl */ + NULL, /* mmap */ + &sv_midi_open, + &sv_midi_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +static int sv_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const unsigned char op_offset[18] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 + }; + struct sv_state *s = (struct sv_state *)file->private_data; + struct dm_fm_voice v; + struct dm_fm_note n; + struct dm_fm_params p; + unsigned int io; + unsigned int regb; + + switch (cmd) { + case FM_IOCTL_RESET: + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + return 0; + + case FM_IOCTL_PLAY_NOTE: + if (copy_from_user(&n, (void *)arg, sizeof(n))) + return -EFAULT; + if (n.voice >= 18) + return -EINVAL; + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xa0 + regb, io); + outb(n.fnum & 0xff, io+1); + outb(0xb0 + regb, io); + outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1); + return 0; + + case FM_IOCTL_SET_VOICE: + if (copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.voice >= 18) + return -EINVAL; + regb = op_offset[v.voice]; + io = s->iosynth + ((v.op & 1) << 1); + outb(0x20 + regb, io); + outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | + ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1); + outb(0x40 + regb, io); + outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1); + outb(0x60 + regb, io); + outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1); + outb(0x80 + regb, io); + outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1); + outb(0xe0 + regb, io); + outb(v.waveform & 0x7, io+1); + if (n.voice >= 9) { + regb = n.voice - 9; + io = s->iosynth+2; + } else { + regb = n.voice; + io = s->iosynth; + } + outb(0xc0 + regb, io); + outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) | + (v.connection & 1), io+1); + return 0; + + case FM_IOCTL_SET_PARAMS: + if (copy_from_user(&p, (void *)arg, sizeof(p))) + return -EFAULT; + outb(0x08, s->iosynth); + outb((p.kbd_split & 1) << 6, s->iosynth+1); + outb(0xbd, s->iosynth); + outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) | + ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1); + return 0; + + case FM_IOCTL_SET_OPL: + outb(4, s->iosynth+2); + outb(arg, s->iosynth+3); + return 0; + + case FM_IOCTL_SET_MODE: + outb(5, s->iosynth+2); + outb(arg & 1, s->iosynth+3); + return 0; + + default: + return -EINVAL; + } +} + +static int sv_dmfm_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct sv_state *s = devs; + + while (s && s->dev_dmfm != minor) + s = s->next; + if (!s) + return -ENODEV; + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & FMODE_DMFM) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + /* init the stuff */ + outb(1, s->iosynth); + outb(0x20, s->iosynth+1); /* enable waveforms */ + outb(4, s->iosynth+2); + outb(0, s->iosynth+3); /* no 4op enabled */ + outb(5, s->iosynth+2); + outb(1, s->iosynth+3); /* enable OPL3 */ + s->open_mode |= FMODE_DMFM; + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int sv_dmfm_release(struct inode *inode, struct file *file) +{ + struct sv_state *s = (struct sv_state *)file->private_data; + unsigned int regb; + + VALIDATE_STATE(s); + down(&s->open_sem); + s->open_mode &= ~FMODE_DMFM; + for (regb = 0xb0; regb < 0xb9; regb++) { + outb(regb, s->iosynth); + outb(0, s->iosynth+1); + outb(regb, s->iosynth+2); + outb(0, s->iosynth+3); + } + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations sv_dmfm_fops = { + &sv_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &sv_dmfm_ioctl, + NULL, /* mmap */ + &sv_dmfm_open, + &sv_dmfm_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ +#if 0 + NULL, /* revalidate */ + NULL, /* lock */ +#endif +}; + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices */ +#define NR_DEVICE 5 + +static int reverb[NR_DEVICE] = { 0, }; +static int wavetable[NR_DEVICE] = { 0, }; + +static unsigned dmaio = 0xac00; + +/* --------------------------------------------------------------------- */ + +static const struct initvol { + int mixch; + int vol; +} initvol[] __initdata = { + { SOUND_MIXER_WRITE_RECLEV, 0x4040 }, + { SOUND_MIXER_WRITE_LINE1, 0x4040 }, + { SOUND_MIXER_WRITE_CD, 0x4040 }, + { SOUND_MIXER_WRITE_LINE, 0x4040 }, + { SOUND_MIXER_WRITE_MIC, 0x4040 }, + { SOUND_MIXER_WRITE_SYNTH, 0x4040 }, + { SOUND_MIXER_WRITE_LINE2, 0x4040 }, + { SOUND_MIXER_WRITE_VOLUME, 0x4040 }, + { SOUND_MIXER_WRITE_PCM, 0x4040 } +}; + +#if LINUX_VERSION_CODE < 131421 + +#ifdef MODULE +__initfunc(int init_module(void)) +#else +__initfunc(int init_sonicvibes(void)) +#endif +{ + struct sv_state *s; + unsigned char bus, dev_fn, irq; + unsigned short index, vendid, devid; + unsigned int ioaddr; + mm_segment_t fs; + int i, val; + + if (!pcibios_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "sv: version v0.5 time " __TIME__ " " __DATE__ "\n"); +#if 0 + if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) + printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); +#endif + for (index = 0; index < NR_DEVICE; index++) { + if (pcibios_find_class((PCI_CLASS_MULTIMEDIA_AUDIO << 8), index, &bus, &dev_fn) != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_word(bus, dev_fn, PCI_VENDOR_ID, &vendid); + pcibios_read_config_word(bus, dev_fn, PCI_DEVICE_ID, &devid); + if (vendid != PCI_VENDOR_ID_S3 || devid != PCI_DEVICE_ID_S3_SONICVIBES) + continue; + pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_1, &ioaddr); + if (ioaddr == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_2, &ioaddr); + if (ioaddr == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_2, &ioaddr); + if (ioaddr == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + pcibios_read_config_byte(bus, dev_fn, PCI_INTERRUPT_LINE, &irq); + if (irq == 0 || irq == 0xff) + continue; + if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) { + printk(KERN_WARNING "sv: out of memory\n"); + continue; + } + memset(s, 0, sizeof(struct sv_state)); + init_waitqueue(&s->dma_adc.wait); + init_waitqueue(&s->dma_dac.wait); + init_waitqueue(&s->open_wait); + init_waitqueue(&s->midi.iwait); + init_waitqueue(&s->midi.owait); + s->open_sem = MUTEX; + s->magic = SV_MAGIC; + pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_0, &s->iosb); + s->iosb &= PCI_BASE_ADDRESS_IO_MASK; + pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_1, &s->ioenh); + s->ioenh &= PCI_BASE_ADDRESS_IO_MASK; + pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_2, &s->iosynth); + s->iosynth &= PCI_BASE_ADDRESS_IO_MASK; + pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_3, &s->iomidi); + s->iomidi &= PCI_BASE_ADDRESS_IO_MASK; + pcibios_read_config_dword(bus, dev_fn, PCI_BASE_ADDRESS_4, &s->iogame); + s->iogame &= PCI_BASE_ADDRESS_IO_MASK; + pcibios_read_config_dword(bus, dev_fn, 0x40, &s->iodmaa); + pcibios_read_config_dword(bus, dev_fn, 0x48, &s->iodmac); + dmaio &= ~(SV_EXTENT_DMA-1); + s->iodmaa &= ~(SV_EXTENT_DMA-1); + s->iodmac &= ~(SV_EXTENT_DMA-1); + if (!(s->iodmaa)) { + s->iodmaa = dmaio; + dmaio += SV_EXTENT_DMA; + printk(KERN_INFO "sv: BIOS did not allocate DDMA channel A io, allocated at %#x\n", + s->iodmaa); + } + if (!(s->iodmac)) { + s->iodmac = dmaio; + dmaio += SV_EXTENT_DMA; + printk(KERN_INFO "sv: BIOS did not allocate DDMA channel C io, allocated at %#x\n", + s->iodmac); + } + pcibios_write_config_dword(bus, dev_fn, 0x40, s->iodmaa | 9); /* enable and use extended mode */ + pcibios_write_config_dword(bus, dev_fn, 0x48, s->iodmac | 9); /* enable */ + printk(KERN_DEBUG "sv: io ports: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + s->iosb, s->ioenh, s->iosynth, s->iomidi, s->iogame, s->iodmaa, s->iodmac); + if (s->ioenh == 0 || s->iodmaa == 0 || s->iodmac == 0) + continue; + s->irq = irq; + + /* hack */ + pcibios_write_config_dword(bus, dev_fn, 0x60, wavetable_mem >> 12); /* wavetable base address */ + + + if (check_region(s->ioenh, SV_EXTENT_ENH)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1); + goto err_region5; + } + request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM"); + if (check_region(s->iodmaa, SV_EXTENT_DMA)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1); + goto err_region4; + } + request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA"); + if (check_region(s->iodmac, SV_EXTENT_DMA)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1); + goto err_region3; + } + request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC"); + if (check_region(s->iomidi, SV_EXTENT_MIDI)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1); + goto err_region2; + } + request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi"); + if (check_region(s->iosynth, SV_EXTENT_SYNTH)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1); + goto err_region1; + } + request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth"); + /* initialize codec registers */ + outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */ + udelay(50); + outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */ + udelay(50); + outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE | SV_CCTRL_REVERB*/, + s->ioenh + SV_CODEC_CONTROL); + inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */ + wrindir(s, SV_CIDRIVECONTROL, 0); /* drive current 16mA */ + wrindir(s, SV_CIENABLE, s->enable = 0); /* disable DMAA and DMAC */ + outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK); + //outb(0xff, s->iodmaa + SV_DMA_RESET); + //outb(0xff, s->iodmac + SV_DMA_RESET); + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */ + wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */ + wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */ + setpll(s, SV_CIADCPLLM, 8000); + wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */ + wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff); + wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff); + wrindir(s, SV_CIADCOUTPUT, 0); + /* request irq */ + if (request_irq(s->irq, sv_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", s)) { + printk(KERN_ERR "sv: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "sv: found adapter at io %#06x irq %u dmaa %#06x dmac %#06x revision %u\n", + s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION)); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&sv_audio_fops)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops)) < 0) + goto err_dev2; + if ((s->dev_midi = register_sound_midi(&sv_midi_fops)) < 0) + goto err_dev3; + if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) + goto err_dev4; + /* initialize the chips */ + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* queue it for later freeing */ + s->next = devs; + devs = s; + continue; + + err_dev4: + unregister_sound_midi(s->dev_midi); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "sv: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->iosynth, SV_EXTENT_SYNTH); + err_region1: + release_region(s->iomidi, SV_EXTENT_MIDI); + err_region2: + release_region(s->iodmac, SV_EXTENT_DMA); + err_region3: + release_region(s->iodmaa, SV_EXTENT_DMA); + err_region4: + release_region(s->ioenh, SV_EXTENT_ENH); + err_region5: + kfree_s(s, sizeof(struct sv_state)); + } + if (!devs) { + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); + return -ENODEV; + } + return 0; +} + +#else /* Linux Version >= 2.1.93 */ + +#ifdef MODULE +__initfunc(int init_module(void)) +#else +__initfunc(int init_sonicvibes(void)) +#endif +{ + struct sv_state *s; + struct pci_dev *pcidev = NULL; + mm_segment_t fs; + int i, val, index = 0; + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "sv: version v0.5 time " __TIME__ " " __DATE__ "\n"); +#if 0 + if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) + printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); +#endif + while (index < NR_DEVICE && + (pcidev = pci_find_device(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, pcidev))) { + if (pcidev->base_address[1] == 0 || + (pcidev->base_address[1] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->base_address[2] == 0 || + (pcidev->base_address[2] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->base_address[3] == 0 || + (pcidev->base_address[3] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO) + continue; + if (pcidev->irq == 0) + continue; + if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) { + printk(KERN_WARNING "sv: out of memory\n"); + continue; + } + memset(s, 0, sizeof(struct sv_state)); + init_waitqueue(&s->dma_adc.wait); + init_waitqueue(&s->dma_dac.wait); + init_waitqueue(&s->open_wait); + init_waitqueue(&s->midi.iwait); + init_waitqueue(&s->midi.owait); + s->open_sem = MUTEX; + s->magic = SV_MAGIC; + s->iosb = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + s->ioenh = pcidev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; + s->iosynth = pcidev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK; + s->iomidi = pcidev->base_address[3] & PCI_BASE_ADDRESS_IO_MASK; + s->iogame = pcidev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK; + pci_read_config_dword(pcidev, 0x40, &s->iodmaa); + pci_read_config_dword(pcidev, 0x48, &s->iodmac); + dmaio &= ~(SV_EXTENT_DMA-1); + s->iodmaa &= ~(SV_EXTENT_DMA-1); + s->iodmac &= ~(SV_EXTENT_DMA-1); + if (!(s->iodmaa)) { + s->iodmaa = dmaio; + dmaio += SV_EXTENT_DMA; + printk(KERN_INFO "sv: BIOS did not allocate DDMA channel A io, allocated at %#x\n", + s->iodmaa); + } + if (!(s->iodmac)) { + s->iodmac = dmaio; + dmaio += SV_EXTENT_DMA; + printk(KERN_INFO "sv: BIOS did not allocate DDMA channel C io, allocated at %#x\n", + s->iodmac); + } + pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9); /* enable and use extended mode */ + pci_write_config_dword(pcidev, 0x48, s->iodmac | 9); /* enable */ + printk(KERN_DEBUG "sv: io ports: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + s->iosb, s->ioenh, s->iosynth, s->iomidi, s->iogame, s->iodmaa, s->iodmac); + if (s->ioenh == 0 || s->iodmaa == 0 || s->iodmac == 0) + continue; + s->irq = pcidev->irq; + + /* hack */ + pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12); /* wavetable base address */ + + + if (check_region(s->ioenh, SV_EXTENT_ENH)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1); + goto err_region5; + } + request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM"); + if (check_region(s->iodmaa, SV_EXTENT_DMA)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1); + goto err_region4; + } + request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA"); + if (check_region(s->iodmac, SV_EXTENT_DMA)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1); + goto err_region3; + } + request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC"); + if (check_region(s->iomidi, SV_EXTENT_MIDI)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1); + goto err_region2; + } + request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi"); + if (check_region(s->iosynth, SV_EXTENT_SYNTH)) { + printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1); + goto err_region1; + } + request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth"); + /* initialize codec registers */ + outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */ + udelay(50); + outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */ + udelay(50); + outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE | SV_CCTRL_REVERB*/, + s->ioenh + SV_CODEC_CONTROL); + inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */ + wrindir(s, SV_CIDRIVECONTROL, 0); /* drive current 16mA */ + wrindir(s, SV_CIENABLE, s->enable = 0); /* disable DMAA and DMAC */ + outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK); + //outb(0xff, s->iodmaa + SV_DMA_RESET); + //outb(0xff, s->iodmac + SV_DMA_RESET); + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */ + wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */ + wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */ + setpll(s, SV_CIADCPLLM, 8000); + wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */ + wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff); + wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff); + wrindir(s, SV_CIADCOUTPUT, 0); + /* request irq */ + if (request_irq(s->irq, sv_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", s)) { + printk(KERN_ERR "sv: irq %u in use\n", s->irq); + goto err_irq; + } + printk(KERN_INFO "sv: found adapter at io %#06x irq %u dmaa %#06x dmac %#06x revision %u\n", + s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION)); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&sv_audio_fops)) < 0) + goto err_dev1; + if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops)) < 0) + goto err_dev2; + if ((s->dev_midi = register_sound_midi(&sv_midi_fops)) < 0) + goto err_dev3; + if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) + goto err_dev4; + /* initialize the chips */ + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE|SOUND_MASK_SYNTH; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val); + } + set_fs(fs); + /* queue it for later freeing */ + s->next = devs; + devs = s; + index++; + continue; + + err_dev4: + unregister_sound_midi(s->dev_midi); + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR "sv: cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->iosynth, SV_EXTENT_SYNTH); + err_region1: + release_region(s->iomidi, SV_EXTENT_MIDI); + err_region2: + release_region(s->iodmac, SV_EXTENT_DMA); + err_region3: + release_region(s->iodmaa, SV_EXTENT_DMA); + err_region4: + release_region(s->ioenh, SV_EXTENT_ENH); + err_region5: + kfree_s(s, sizeof(struct sv_state)); + } + if (!devs) { + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); + return -ENODEV; + } + return 0; +} + +#endif +/* --------------------------------------------------------------------- */ + +#ifdef MODULE + +#if LINUX_VERSION_CODE >= 131328 +MODULE_PARM(reverb, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(reverb, "if 1 enables joystick interface (still need separate driver)"); +MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(wavetable, "if 1 the LINE input is converted to LINE out"); + +MODULE_PARM(dmaio, "i"); +MODULE_PARM_DESC(dmaio, "if the motherboard BIOS did not allocate DDMA io, allocate them starting at this address"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("S3 SonicVibes Driver"); +#endif + +void cleanup_module(void) +{ + struct sv_state *s; + + while ((s = devs)) { + devs = devs->next; + outb(~0, s->ioenh + SV_CODEC_INTMASK); /* disable ints */ + synchronize_irq(); + inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ + wrindir(s, SV_CIENABLE, 0); /* disable DMAA and DMAC */ + //outb(0, s->iodmaa + SV_DMA_RESET); + //outb(0, s->iodmac + SV_DMA_RESET); + free_irq(s->irq, s); + release_region(s->iodmac, SV_EXTENT_DMA); + release_region(s->iodmaa, SV_EXTENT_DMA); + release_region(s->ioenh, SV_EXTENT_ENH); + release_region(s->iomidi, SV_EXTENT_MIDI); + release_region(s->iosynth, SV_EXTENT_SYNTH); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + unregister_sound_special(s->dev_dmfm); + kfree_s(s, sizeof(struct sv_state)); + } + if (wavetable_mem) + free_pages(wavetable_mem, 20-PAGE_SHIFT); + printk(KERN_INFO "sv: unloading\n"); +} + +#endif /* MODULE */ diff --git a/drivers/sound/sound_calls.h b/drivers/sound/sound_calls.h index 16207733862d..729e7c1f28ed 100644 --- a/drivers/sound/sound_calls.h +++ b/drivers/sound/sound_calls.h @@ -5,102 +5,91 @@ 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_get_curr_buffer(int dev, int *buff_no, char **dma_buf, int *buff_ptr, int *buff_size); 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_set_count(int dev, int buff_no, int l); -int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local); -void DMAbuf_init(void); +int DMAbuf_move_wrpointer(int dev, int l); +/* int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t 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); int DMAbuf_open_dma (int dev); void DMAbuf_close_dma (int dev); -void DMAbuf_reset_dma (int dev); void DMAbuf_inputintr(int dev); void DMAbuf_outputintr(int dev, int underflow_flag); -int DMAbuf_select(int dev, struct fileinfo *file, int sel_type, select_table_handle * wait); +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_select(struct file *file, int dev, int sel_type, select_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 + * System calls for /dev/dsp and /dev/audio (audio.c) */ -int audio_read (int dev, struct fileinfo *file, char *buf, int count); -int audio_write (int dev, struct fileinfo *file, const char *buf, int count); -int audio_open (int dev, struct fileinfo *file); -void audio_release (int dev, struct fileinfo *file); -int audio_ioctl (int dev, struct fileinfo *file, +int audio_read (int dev, struct file *file, char *buf, int count); +int audio_write (int dev, struct file *file, const char *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, caddr_t arg); -int audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig); -void audio_init (void); - -int audio_select(int dev, struct fileinfo *file, int sel_type, select_table_handle * wait); +void audio_init_devices (void); +void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording); +int dma_ioctl (int dev, unsigned int cmd, caddr_t arg); /* * System calls for the /dev/sequencer */ -int sequencer_read (int dev, struct fileinfo *file, char *buf, int count); -int sequencer_write (int dev, struct fileinfo *file, const char *buf, int count); -int sequencer_open (int dev, struct fileinfo *file); -void sequencer_release (int dev, struct fileinfo *file); -int sequencer_ioctl (int dev, struct fileinfo *file, - unsigned int cmd, caddr_t arg); -int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +int sequencer_read (int dev, struct file *file, char *buf, int count); +int sequencer_write (int dev, struct file *file, const char *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, caddr_t arg); +unsigned int sequencer_select(int dev, struct file *file, int sel_type, select_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); +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); -int sequencer_select(int dev, struct fileinfo *file, int sel_type, select_table_handle * wait); - /* * System calls for the /dev/midi */ -int MIDIbuf_read (int dev, struct fileinfo *file, char *buf, int count); -int MIDIbuf_write (int dev, struct fileinfo *file, const char *buf, int count); -int MIDIbuf_open (int dev, struct fileinfo *file); -void MIDIbuf_release (int dev, struct fileinfo *file); -int MIDIbuf_ioctl (int dev, struct fileinfo *file, - unsigned int cmd, caddr_t arg); -int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +int MIDIbuf_read (int dev, struct file *file, char *buf, int count); +int MIDIbuf_write (int dev, struct file *file, const char *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, caddr_t arg); +unsigned int MIDIbuf_select(int dev, struct file *file, int sel_type, select_table * wait); +int MIDIbuf_avail(int dev); + void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count); void MIDIbuf_init(void); -int MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, select_table_handle * wait); - /* * * Misc calls from various sources */ /* From soundcard.c */ -void soundcard_init(void); -void tenmicrosec(int *osp); void request_sound_timer (int count); void sound_stop_timer(void); -int snd_ioctl_return(int *addr, int value); -int snd_set_irq_handler (int interrupt_level, void(*iproc)(int, void*, struct pt_regs *), char *name, int *osp); -void snd_release_irq(int vect); -void sound_dma_malloc(int dev); -void sound_dma_free(int dev); 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_switch.c */ -int sound_read_sw (int dev, struct fileinfo *file, char *buf, int count); -int sound_write_sw (int dev, struct fileinfo *file, const char *buf, int count); -int sound_open_sw (int dev, struct fileinfo *file); -void sound_release_sw (int dev, struct fileinfo *file); -int sound_ioctl_sw (int dev, struct fileinfo *file, - unsigned int cmd, caddr_t arg); - /* From opl3.c */ int opl3_detect (int ioaddr, int *osp); -void opl3_init(int ioaddr, int *osp); +int opl3_init(int ioaddr, int *osp); /* From sb_card.c */ void attach_sb_card(struct address_info *hw_config); @@ -153,9 +142,8 @@ int probe_gus_db16(struct address_info *hw_config); /* From gus_wave.c */ int gus_wave_detect(int baseaddr); void gus_wave_init(struct address_info *hw_config); -void gus_wave_unload (void); +void gus_wave_unload (struct address_info *hw_config); void gus_voice_irq(void); -unsigned char gus_read8 (int reg); void gus_write8(int reg, unsigned int data); void guswave_dma_irq(void); void gus_delay(void); @@ -163,7 +151,7 @@ int gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg); void gus_timer_command (unsigned int addr, unsigned int val); /* From gus_midi.c */ -void gus_midi_init(void); +void gus_midi_init(struct address_info *hw_config); void gus_midi_interrupt(int dummy); /* From mpu401.c */ @@ -178,36 +166,30 @@ int probe_uart6850(struct address_info *hw_config); /* From opl3.c */ void enable_opl3_mode(int left, int right, int both); -/* From patmgr.c */ -int pmgr_open(int dev); -void pmgr_release(int dev); -int pmgr_read (int dev, struct fileinfo *file, char * buf, int count); -int pmgr_write (int dev, struct fileinfo *file, const char * buf, int count); -int pmgr_access(int dev, struct patmgr_info *rec); -int pmgr_inform(int dev, int event, unsigned long parm1, unsigned long parm2, - unsigned long parm3, unsigned long parm4); - /* From ics2101.c */ -void ics2101_mixer_init(void); +int ics2101_mixer_init(void); /* From sound_timer.c */ void sound_timer_interrupt(void); void sound_timer_syncinterval(unsigned int new_usecs); /* From ad1848.c */ -void ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *osp); +int ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *osp); void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma); int ad1848_detect (int io_base, int *flags, int *osp); #define AD_F_CS4231 0x0001 /* Returned if a CS4232 (or compatible) detected */ #define AD_F_CS4248 0x0001 /* Returned if a CS4248 (or compatible) detected */ -void ad1848_interrupt (int irq, void *dev_id, struct pt_regs * dummy); +int ad1848_control(int cmd, int arg); +#define AD1848_SET_XTAL 1 +#define AD1848_MIXER_REROUTE 2 +#define AD1848_REROUTE(oldctl, newctl) \ + ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl)) + +void adintr(int irq, void *dev_id, struct pt_regs * dummy); void attach_ms_sound(struct address_info * hw_config); int probe_ms_sound(struct address_info *hw_config); -void attach_pnp_ad1848(struct address_info * hw_config); -int probe_pnp_ad1848(struct address_info *hw_config); -void unload_pnp_ad1848(struct address_info *hw_info); /* From pss.c */ int probe_pss (struct address_info *hw_config); @@ -223,15 +205,6 @@ void attach_sscape (struct address_info *hw_config); int probe_ss_ms_sound (struct address_info *hw_config); void attach_ss_ms_sound(struct address_info * hw_config); -int pss_read (int dev, struct fileinfo *file, char *buf, int count); -int pss_write (int dev, struct fileinfo *file, char *buf, int count); -int pss_open (int dev, struct fileinfo *file); -void pss_release (int dev, struct fileinfo *file); -int pss_ioctl (int dev, struct fileinfo *file, - unsigned int cmd, caddr_t arg); -int pss_lseek (int dev, struct fileinfo *file, off_t offset, int orig); -void pss_init(void); - /* From aedsp16.c */ int InitAEDSP16_SBPRO(struct address_info *hw_config); int InitAEDSP16_MSS(struct address_info *hw_config); @@ -253,7 +226,6 @@ void attach_mad16 (struct address_info *hw_config); int probe_mad16 (struct address_info *hw_config); void attach_mad16_mpu (struct address_info *hw_config); int probe_mad16_mpu (struct address_info *hw_config); -int mad16_sb_dsp_detect (struct address_info *hw_config); /* Unload routines from various source files*/ void unload_pss(struct address_info *hw_info); @@ -291,6 +263,48 @@ void attach_cs4232_mpu (struct address_info *hw_config); void attach_maui(struct address_info * hw_config); int probe_maui(struct address_info *hw_config); -/* From sound_pnp.c */ -void sound_pnp_init(int *osp); -void sound_pnp_disconnect(void); +/* From v_midi.c */ +void attach_v_midi (struct address_info *hw_config); +int probe_v_midi (struct address_info *hw_config); +void unload_v_midi (struct address_info *hw_config); + +/* From vidc.c */ +void attach_vidc(struct address_info *hw_config); +int probe_vidc(struct address_info *hw_config); +void unload_vidc(struct address_info *hw_config); + +/* From wavefront.c */ +void attach_wavefront (struct address_info *hw_config); +int probe_wavefront (struct address_info *hw_config); +void unload_wavefront (struct address_info *hw_config); + +/* From wf_midi.c */ +void attach_wf_mpu(struct address_info * hw_config); +int probe_wf_mpu(struct address_info *hw_config); +void unload_wf_mpu(struct address_info *hw_config); +int virtual_midi_enable (int mididev, struct address_info *); +void virtual_midi_disable (int mididev); + +/* From wavefront.c */ +void attach_wavefront (struct address_info *hw_config); +int probe_wavefront (struct address_info *hw_config); +void unload_wavefront (struct address_info *hw_config); + +/* From wf_midi.c */ +void attach_wf_mpu(struct address_info * hw_config); +int probe_wf_mpu(struct address_info *hw_config); +void unload_wf_mpu(struct address_info *hw_config); +int virtual_midi_enable (int mididev, struct address_info *); +void virtual_midi_disable (int mididev); + +/* From wavefront.c */ +void attach_wavefront (struct address_info *hw_config); +int probe_wavefront (struct address_info *hw_config); +void unload_wavefront (struct address_info *hw_config); + +/* From wf_midi.c */ +void attach_wf_mpu(struct address_info * hw_config); +int probe_wf_mpu(struct address_info *hw_config); +void unload_wf_mpu(struct address_info *hw_config); +int virtual_midi_enable (int mididev, struct address_info *); +void virtual_midi_disable (int mididev); diff --git a/drivers/sound/sound_config.h b/drivers/sound/sound_config.h index 75aec488ffb0..8aed70737116 100644 --- a/drivers/sound/sound_config.h +++ b/drivers/sound/sound_config.h @@ -1,27 +1,95 @@ /* sound_config.h * - * A driver for Soundcards, misc configuration parameters. + * A driver for sound cards, misc. configuration parameters. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 "local.h" -#include "os.h" -#include "soundvers.h" - -#if defined(ISC) || defined(SCO) || defined(SVR42) -#define GENERIC_SYSV -#endif - +#ifndef _SOUND_CONFIG_H_ +#define _SOUND_CONFIG_H_ +#include +#include +#include +#include "legacy.h" +#include "os.h" +#include "soundvers.h" +/* + * 2.0 compatibility functions + */ + +extern inline int copy_from_user(void *to, const void *from, int len) +{ + if(verify_area(VERIFY_READ, from , len)) + return len; + memcpy_fromfs(to,from,len); + return 0; +} + +extern inline int copy_to_user(void *to, const void *from, int len) +{ + if(verify_area(VERIFY_WRITE, to , len)) + return len; + memcpy_tofs(to,from,len); + return 0; +} + +#define __copy_from_user copy_from_user +#define __copy_to_user copy_to_user + +extern inline int access_ok(int type, void *ptr, int len) +{ + if(verify_area(type,ptr,len)==0) + return 1; + return 0; +} + +#undef put_user +#undef get_user + +typedef unsigned long mm_segment_t; + +#define MODULE_PARM(a,b) +#define MODULE_PARM_DESC(a,b) +#define MODULE_AUTHOR(a) +#define MODULE_DESCRIPTION(a) + +extern inline int compat_put_user(void *from, size_t s, void *mem) +{ + if(verify_area(VERIFY_WRITE, mem, s)) + return -EFAULT; + memcpy_tofs(mem, from, s); + return 0; +} + +extern inline int compat_get_user(void *to, size_t s, void *mem) +{ + if(verify_area(VERIFY_READ, mem, s)) + return -EFAULT; + memcpy_fromfs(to, mem, s); + return 0; +} + +#define put_user(x,y) compat_put_user(&(x), sizeof(x), y) +#define get_user(x,y) compat_get_user(&(x), sizeof(x), y) + +#define __get_user(x,y) compat_get_user(&(x), sizeof(x), y) +#define __put_user(x,y) compat_put_user(&(x), sizeof(x), y) + +extern inline int signal_pending(struct task_struct *t) +{ + if(t->signal & ~t->blocked) + return 1; + return 0; +} #ifndef SND_DEFAULT_ENABLE @@ -32,20 +100,11 @@ #define MAX_REALTIME_FACTOR 4 #endif -/************* PCM DMA buffer sizes *******************/ - -/* If you are using high playback or recording speeds, the default buffer size - is too small. DSP_BUFFSIZE must be 64k or less. - - A rule of thumb is 64k for PAS16, 32k for PAS+, 16k for SB Pro and - 4k for SB. - - If you change the DSP_BUFFSIZE, don't modify this file. - Use the make config command instead. */ - -#ifndef DSP_BUFFSIZE -#define DSP_BUFFSIZE (4096) -#endif +/* + * 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. */ @@ -55,20 +114,20 @@ #define FM_MONO 0x388 /* This is the I/O address used by AdLib */ -#ifndef PAS_BASE -#define PAS_BASE 0x388 +#ifndef CONFIG_PAS_BASE +#define CONFIG_PAS_BASE 0x388 #endif -#if defined(SB16_DMA) && !defined(SB_DMA2) -# define SB_DMA2 SB16_DMA +#if defined(CONFIG_SB16_DMA) && !defined(CONFIG_SB_DMA2) +# define CONFIG_SB_DMA2 CONFIG_SB16_DMA #endif -#if defined(SB16MIDI_BASE) && !defined(SB_MPU_BASE) -# define SB_MPU_BASE SB16MIDI_BASE +#if defined(SB16MIDI_BASE) && !defined(CONFIG_SB_MPU_BASE) +# define CONFIG_SB_MPU_BASE SB16MIDI_BASE #endif -#ifndef SB_MPU_IRQ -# define SB_MPU_IRQ SBC_IRQ +#ifndef CONFIG_SB_MPU_IRQ +# define CONFIG_SB_MPU_IRQ CONFIG_SB_IRQ #endif /* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the @@ -103,20 +162,11 @@ #define DSP_DEFAULT_SPEED 8000 -#define ON 1 -#define OFF 0 - #define MAX_AUDIO_DEV 5 #define MAX_MIXER_DEV 5 -#define MAX_SYNTH_DEV 3 +#define MAX_SYNTH_DEV 5 #define MAX_MIDI_DEV 6 -#define MAX_TIMER_DEV 3 - -struct fileinfo { - int mode; /* Open mode */ - int flags; - int dummy; /* Reference to file-flags. OS-dependent. */ - }; +#define MAX_TIMER_DEV 4 struct address_info { int io_base; @@ -129,6 +179,8 @@ struct address_info { 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 @@ -145,6 +197,7 @@ struct voice_alloc_info { struct channel_info { int pgm_num; int bender_value; + int bender_range; unsigned char controllers[128]; }; @@ -157,11 +210,30 @@ struct channel_info { #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) +#if OPEN_READ == FMODE_READ && OPEN_WRITE == FMODE_WRITE + +extern __inline__ int translate_mode(struct file *file) +{ + return file->f_mode; +} + +#else + +extern __inline__ int translate_mode(struct file *file) +{ + return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) | + ((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0); +} + +#endif + + #include "sound_calls.h" #include "dev_table.h" @@ -170,8 +242,18 @@ struct channel_info { #endif #ifndef DDB -#define DDB(x) +#define DDB(x) {} +#endif + +#ifndef MDB +#ifdef MODULE +#define MDB(x) x +#else +#define MDB(x) +#endif #endif #define TIMER_ARMED 121234 #define TIMER_NOT_ARMED 1 + +#endif diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c new file mode 100644 index 000000000000..0e925df8f55d --- /dev/null +++ b/drivers/sound/sound_core.c @@ -0,0 +1,314 @@ +/* + * Sound core handling. Breaks out sound functions to submodules + * + * Author: Alan Cox + * + * Fixes: + * + * + * 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. + * + * -------------------- + * + * Top level handler for the sound subsystem. Various devices can + * plug into this. The fact they dont all go via OSS doesn't mean + * they don't have to implement the OSS API. There is a lot of logic + * to keeping much of the OSS weight out of the code in a compatibility + * module, but its up to the driver to rember to load it... + * + * The code provides a set of functions for registration of devices + * by type. This is done rather than providing a single call so that + * we can hide any future changes in the internals (eg when we go to + * 32bit dev_t) from the modules and their interface. + * + * Secondly we need to allocate the dsp, dsp16 and audio devices as + * one. Thus we misuse the chains a bit to simplify this. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct sound_unit +{ + int unit_minor; + struct file_operations *unit_fops; + struct sound_unit *next; +}; + +/* + * Low level list operator. Scan the ordered list, find a hole and + * join into it. Called with the lock asserted + */ + +static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, struct file_operations *fops, int low, int top) +{ + int n=low; + + while(nunit_minor>n) + break; + list=&((*list)->next); + n+=16; + } + + if(n==top) + return -1; + + + /* + * Fill it in + */ + + s->unit_minor=n; + s->unit_fops=fops; + + /* + * Link it + */ + + s->next=*list; + *list=s; + + MOD_INC_USE_COUNT; + return n; +} + +/* + * Remove a node from the chain. Called with the lock asserted + */ + +static void __sound_remove_unit(struct sound_unit **list, int unit) +{ + while(*list) + { + struct sound_unit *p=*list; + if(p->unit_minor==unit) + { + *list=p->next; + kfree(p); + MOD_DEC_USE_COUNT; + return; + } + list=&(p->next); + } +} + +/* + * Allocate the controlling structure and add it to the sound driver + * list. Acquires locks as needed (none in 2.0) + */ + +static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int low, int top) +{ + int r; + struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL); + if(s==NULL) + return -1; + + r=__sound_insert_unit(s,list,fops,low,top); + + if(r==-1) + kfree(s); + return r; +} + +/* + * Remove a unit. Acquires locks as needed. The drivers MUST have + * completed the removal before their file operations become + * invalid. + */ + +static void sound_remove_unit(struct sound_unit **list, int unit) +{ + __sound_remove_unit(list, unit); +} + +/* + * Allocations + * + * 0 *16 Mixers + * 1 *8 Sequencers + * 2 *16 Midi + * 3 *16 DSP + * 4 *16 SunDSP + * 5 *16 DSP16 + * 6 -- sndstat (obsolete) + * 7 *16 unused + * 8 -- alternate sequencer (see above) + * 9 *16 unused + * 10 *16 unused + * 11 *16 unused + * 12 *16 unused + * 13 *16 unused + * 14 *16 unused + * 15 *16 unused + */ + +static struct sound_unit *chains[16]; + +int register_sound_special(struct file_operations *fops, int unit) +{ + return sound_insert_unit(&chains[unit&15], fops, unit, unit+1); +} + +int register_sound_mixer(struct file_operations *fops) +{ + return sound_insert_unit(&chains[0], fops, 0, 128); +} + +int register_sound_midi(struct file_operations *fops) +{ + return sound_insert_unit(&chains[2], fops, 2, 130); +} + +/* + * DSP's are registered as a triple. Register only one and cheat + * in open - see below. + */ + +int register_sound_dsp(struct file_operations *fops) +{ + return sound_insert_unit(&chains[3], fops, 3, 131); +} + +void unregister_sound_special(int unit) +{ + sound_remove_unit(&chains[unit&15], unit); +} + +void unregister_sound_mixer(int unit) +{ + sound_remove_unit(&chains[0], unit); +} + +void unregister_sound_midi(int unit) +{ + return sound_remove_unit(&chains[2], unit); +} + +void unregister_sound_dsp(int unit) +{ + return sound_remove_unit(&chains[3], unit); +} + +/* + * Now our file operations + */ + +static int soundcore_open(struct inode *, struct file *); + +static struct file_operations soundcore_fops= +{ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + soundcore_open, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +int soundcore_open(struct inode *inode, struct file *file) +{ + int chain; + int unit=MINOR(inode->i_rdev); + struct sound_unit *s; + + chain=unit&0x0F; + if(chain==4 || chain==5) /* dsp/audio/dsp16 */ + { + unit&=0xF0; + unit|=3; + chain=3; + } + + s=chains[chain]; + + while(s && s->unit_minor <= unit) + { + if(s->unit_minor==unit) + { + file->f_op=s->unit_fops; + if(file->f_op->open) + return file->f_op->open(inode,file); + else + return 0; + break; + } + s=s->next; + } + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + /* We have nothing to really do here - we know the lists must be + empty */ + unregister_chrdev(SOUND_MAJOR, "sound"); +} + +int init_module(void) +#else +int soundcore_init(void) +#endif +{ + if(register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) + { + printk(KERN_ERR "soundcore: sound device already in use.\n"); + return -EBUSY; + } + /* + * Now init non OSS drivers + */ +#ifdef CONFIG_SOUND_SONICVIBES + init_sonicvibes(); +#endif +#ifdef CONFIG_SOUND_ES1370 + init_es1370(); +#endif +#ifdef CONFIG_SOUND_ES1371 + init_es1371(); +#endif +#ifdef CONFIG_SOUND_MSNDCLAS + msnd_classic_init(); +#endif +#ifdef CONFIG_SOUND_MSNDPIN + msnd_pinnacle_init(); +#endif + return 0; +} + +struct symbol_table soundcore_symtab= +{ +#include + X(register_sound_special), + X(register_sound_mixer), + X(register_sound_midi), + X(register_sound_dsp), + X(unregister_sound_special), + X(unregister_sound_mixer), + X(unregister_sound_midi), + X(unregister_sound_dsp), +#include +}; diff --git a/drivers/sound/sound_firmware.c b/drivers/sound/sound_firmware.c new file mode 100644 index 000000000000..545a7063435c --- /dev/null +++ b/drivers/sound/sound_firmware.c @@ -0,0 +1,68 @@ +#include "os.h" +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include + +#include "sound_config.h" + +static int errno; +static int do_mod_firmware_load(const char *fn, char **fp) +{ + int fd; + long l; + char *dp; + + fd = sys_open(fn, 0, 0); + if (fd == -1) + { + printk(KERN_INFO "Unable to load '%s'.\n", fn); + return 0; + } + l = lseek(fd, 0L, 2); + if (l <= 0 || l > 131072) + { + printk(KERN_INFO "Invalid firmware '%s'\n", fn); + sys_close(fd); + return 0; + } + lseek(fd, 0L, 0); + dp = vmalloc(l); + if (dp == NULL) + { + printk(KERN_INFO "Out of memory loading '%s'.\n", fn); + sys_close(fd); + return 0; + } + if (sys_read(fd, dp, l) != l) + { + printk(KERN_INFO "Failed to read '%s'.\n", fn); + vfree(dp); + sys_close(fd); + return 0; + } + close(fd); + *fp = dp; + return (int) l; +} + +int mod_firmware_load(const char *fn, char **fp) +{ + int r; + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + r = do_mod_firmware_load(fn, fp); + set_fs(fs); + return r; +} + +struct symbol_table firmware_symbols = +{ +#include + X(mod_firmware_load), +#include +}; + diff --git a/drivers/sound/sound_firmware.h b/drivers/sound/sound_firmware.h new file mode 100644 index 000000000000..0a0cbfdfb855 --- /dev/null +++ b/drivers/sound/sound_firmware.h @@ -0,0 +1,2 @@ +extern int mod_firmware_load(const char *fn, char **fp); + diff --git a/drivers/sound/sound_switch.c b/drivers/sound/sound_switch.c deleted file mode 100644 index 239489f90c62..000000000000 --- a/drivers/sound/sound_switch.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * sound/sound_switch.c - * - * The system call switch handler - */ -/* - * 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. - */ -#include - - -#include "sound_config.h" - -struct sbc_device - { - int usecount; - }; - -static int in_use = 0; /* Total # of open devices */ - -/* - * /dev/sndstatus -device - */ -static char *status_buf = NULL; -static int status_len, status_ptr; -static int status_busy = 0; - -static int -put_status (char *s) -{ - int l = strlen (s); - - if (status_len + l >= 4000) - return 0; - - memcpy (&status_buf[status_len], s, l); - status_len += l; - - return 1; -} - -static int -put_status_int (unsigned int val, int radix) -{ - int l, v; - - static char hx[] = "0123456789abcdef"; - char buf[11]; - - if (!val) - return put_status ("0"); - - l = 0; - buf[10] = 0; - - while (val) - { - v = val % radix; - val = val / radix; - - buf[9 - l] = hx[v]; - l++; - } - - if (status_len + l >= 4000) - return 0; - - memcpy (&status_buf[status_len], &buf[10 - l], l); - status_len += l; - - return 1; -} - -static void -init_status (void) -{ - /* - * Write the status information to the status_buf and update status_len. - * There is a limit of 4000 bytes for the data. - */ - - int i; - - status_ptr = 0; - -#ifdef SOUND_UNAME_A - put_status ("Sound Driver:" SOUND_VERSION_STRING - " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY ",\n" - SOUND_UNAME_A ")" - "\n"); -#else - put_status ("Sound Driver:" SOUND_VERSION_STRING - " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@" - SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")" - "\n"); -#endif - - put_status ("Kernel: "); - put_status (system_utsname.sysname); - put_status (" "); - put_status (system_utsname.nodename); - put_status (" "); - put_status (system_utsname.release); - put_status (" "); - put_status (system_utsname.version); - put_status (" "); - put_status (system_utsname.machine); - put_status ("\n"); - - if (!put_status ("Config options: ")) - return; - if (!put_status_int (SELECTED_SOUND_OPTIONS, 16)) - return; - - if (!put_status ("\n\nInstalled drivers: \n")) - return; - - for (i = 0; i < num_sound_drivers; i++) - if (sound_drivers[i].card_type != 0) - { - if (!put_status ("Type ")) - return; - if (!put_status_int (sound_drivers[i].card_type, 10)) - return; - if (!put_status (": ")) - return; - if (!put_status (sound_drivers[i].name)) - return; - - if (!put_status ("\n")) - return; - } - - if (!put_status ("\nCard config: \n")) - return; - - for (i = 0; i < num_sound_cards; i++) - if (snd_installed_cards[i].card_type != 0) - { - int drv, tmp; - - if (!snd_installed_cards[i].enabled) - if (!put_status ("(")) - return; - - /* - * if (!put_status_int(snd_installed_cards[i].card_type, 10)) return; - * if (!put_status (": ")) return; - */ - - if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1) - if (!put_status (sound_drivers[drv].name)) - return; - - if (snd_installed_cards[i].config.io_base) - { - if (!put_status (" at 0x")) - return; - if (!put_status_int (snd_installed_cards[i].config.io_base, 16)) - return; - } - - tmp = snd_installed_cards[i].config.irq; - if (tmp != 0) - { - if (!put_status (" irq ")) - return; - if (tmp < 0) - tmp = -tmp; - if (!put_status_int (tmp, 10)) - return; - } - - if (snd_installed_cards[i].config.dma != -1) - { - if (!put_status (" drq ")) - return; - if (!put_status_int (snd_installed_cards[i].config.dma, 10)) - return; - if (snd_installed_cards[i].config.dma2 != -1) - { - if (!put_status (",")) - return; - if (!put_status_int (snd_installed_cards[i].config.dma2, 10)) - return; - } - } - - if (!snd_installed_cards[i].enabled) - if (!put_status (")")) - return; - - if (!put_status ("\n")) - return; - } - - if (!sound_started) - { - put_status ("\n\n***** Sound driver not started *****\n\n"); - return; - } - -#ifndef CONFIG_AUDIO - if (!put_status ("\nAudio devices: NOT ENABLED IN CONFIG\n")) - return; -#else - if (!put_status ("\nAudio devices:\n")) - return; - - for (i = 0; i < num_audiodevs; i++) - { - if (!put_status_int (i, 10)) - return; - if (!put_status (": ")) - return; - if (!put_status (audio_devs[i]->name)) - return; - - if (audio_devs[i]->flags & DMA_DUPLEX) - if (!put_status (" (DUPLEX)")) - return; - - if (!put_status ("\n")) - return; - } -#endif - -#ifndef CONFIG_SEQUENCER - if (!put_status ("\nSynth devices: NOT ENABLED IN CONFIG\n")) - return; -#else - if (!put_status ("\nSynth devices:\n")) - return; - - for (i = 0; i < num_synths; i++) - { - if (!put_status_int (i, 10)) - return; - if (!put_status (": ")) - return; - if (!put_status (synth_devs[i]->info->name)) - return; - if (!put_status ("\n")) - return; - } -#endif - -#ifndef CONFIG_MIDI - if (!put_status ("\nMidi devices: NOT ENABLED IN CONFIG\n")) - return; -#else - if (!put_status ("\nMidi devices:\n")) - return; - - for (i = 0; i < num_midis; i++) - { - if (!put_status_int (i, 10)) - return; - if (!put_status (": ")) - return; - if (!put_status (midi_devs[i]->info.name)) - return; - if (!put_status ("\n")) - return; - } -#endif - - if (!put_status ("\nTimers:\n")) - return; - - for (i = 0; i < num_sound_timers; i++) - { - if (!put_status_int (i, 10)) - return; - if (!put_status (": ")) - return; - if (!put_status (sound_timer_devs[i]->info.name)) - return; - if (!put_status ("\n")) - return; - } - - if (!put_status ("\nMixers:\n")) - return; - - for (i = 0; i < num_mixers; i++) - { - if (!put_status_int (i, 10)) - return; - if (!put_status (": ")) - return; - if (!put_status (mixer_devs[i]->name)) - return; - if (!put_status ("\n")) - return; - } -} - -static int -read_status (char *buf, int count) -{ - /* - * Return at most 'count' bytes from the status_buf. - */ - int l, c; - - l = count; - c = status_len - status_ptr; - - if (l > c) - l = c; - if (l <= 0) - return 0; - - memcpy_tofs (&(buf)[0], &status_buf[status_ptr], l); - status_ptr += l; - - return l; -} - -int -sound_read_sw (int dev, struct fileinfo *file, char *buf, int count) -{ - DEB (printk ("sound_read_sw(dev=%d, count=%d)\n", dev, count)); - - switch (dev & 0x0f) - { - case SND_DEV_STATUS: - return read_status (buf, count); - break; - -#ifdef CONFIG_AUDIO - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - return audio_read (dev, file, buf, count); - break; -#endif - -#ifdef CONFIG_SEQUENCER - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - return sequencer_read (dev, file, buf, count); - break; -#endif - -#ifdef CONFIG_MIDI - case SND_DEV_MIDIN: - return MIDIbuf_read (dev, file, buf, count); -#endif - - default: - printk ("Sound: Undefined minor device %d\n", dev); - } - - return -(EPERM); -} - -int -sound_write_sw (int dev, struct fileinfo *file, const char *buf, int count) -{ - - DEB (printk ("sound_write_sw(dev=%d, count=%d)\n", dev, count)); - - switch (dev & 0x0f) - { - -#ifdef CONFIG_SEQUENCER - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - return sequencer_write (dev, file, buf, count); - break; -#endif - -#ifdef CONFIG_AUDIO - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - return audio_write (dev, file, buf, count); - break; -#endif - -#ifdef CONFIG_MIDI - case SND_DEV_MIDIN: - return MIDIbuf_write (dev, file, buf, count); -#endif - - } - - return -(EPERM); -} - -int -sound_open_sw (int dev, struct fileinfo *file) -{ - int retval; - - DEB (printk ("sound_open_sw(dev=%d)\n", dev)); - - if ((dev >= SND_NDEVS) || (dev < 0)) - { - printk ("Invalid minor device %d\n", dev); - return -(ENXIO); - } - - switch (dev & 0x0f) - { - case SND_DEV_STATUS: - if (status_busy) - return -(EBUSY); - status_busy = 1; - if ((status_buf = (char *) vmalloc (4000)) == NULL) - return -(EIO); - status_len = status_ptr = 0; - init_status (); - break; - - case SND_DEV_CTL: - if ((dev & 0xf0) && ((dev & 0xf0) >> 4) >= num_mixers) - return -(ENXIO); - return 0; - break; - -#ifdef CONFIG_SEQUENCER - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - if ((retval = sequencer_open (dev, file)) < 0) - return retval; - break; -#endif - -#ifdef CONFIG_MIDI - case SND_DEV_MIDIN: - if ((retval = MIDIbuf_open (dev, file)) < 0) - return retval; - break; -#endif - -#ifdef CONFIG_AUDIO - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - if ((retval = audio_open (dev, file)) < 0) - return retval; - break; -#endif - - default: - printk ("Invalid minor device %d\n", dev); - return -(ENXIO); - } - - in_use++; - - return 0; -} - -void -sound_release_sw (int dev, struct fileinfo *file) -{ - - DEB (printk ("sound_release_sw(dev=%d)\n", dev)); - - switch (dev & 0x0f) - { - case SND_DEV_STATUS: - if (status_buf) - vfree (status_buf); - status_buf = NULL; - status_busy = 0; - break; - - case SND_DEV_CTL: - break; - -#ifdef CONFIG_SEQUENCER - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - sequencer_release (dev, file); - break; -#endif - -#ifdef CONFIG_MIDI - case SND_DEV_MIDIN: - MIDIbuf_release (dev, file); - break; -#endif - -#ifdef CONFIG_AUDIO - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - audio_release (dev, file); - break; -#endif - - default: - printk ("Sound error: Releasing unknown device 0x%02x\n", dev); - } - in_use--; -} - -static int -get_mixer_info (int dev, caddr_t arg) -{ - mixer_info info; - - if (dev < 0 || dev >= num_mixers) - return -(ENXIO); - - strcpy (info.id, mixer_devs[dev]->id); - strcpy (info.name, mixer_devs[dev]->name); - - memcpy_tofs (&((char *) arg)[0], (char *) &info, sizeof (info)); - return 0; -} - -int -sound_ioctl_sw (int dev, struct fileinfo *file, - unsigned int cmd, caddr_t arg) -{ - DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); - - if (((cmd >> 8) & 0xff) == 'M' && num_mixers > 0) /* Mixer ioctl */ - if ((dev & 0x0f) != SND_DEV_CTL) - { - int dtype = dev & 0x0f; - int mixdev; - - switch (dtype) - { -#ifdef CONFIG_AUDIO - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - mixdev = audio_devs[dev >> 4]->mixer_dev; - if (mixdev < 0 || mixdev >= num_mixers) - return -(ENXIO); - if (cmd == SOUND_MIXER_INFO) - return get_mixer_info (mixdev, arg); - return mixer_devs[mixdev]->ioctl (mixdev, cmd, arg); - break; -#endif - - default: - if (cmd == SOUND_MIXER_INFO) - return get_mixer_info (0, arg); - return mixer_devs[0]->ioctl (0, cmd, arg); - } - } - - switch (dev & 0x0f) - { - - case SND_DEV_CTL: - - if (!num_mixers) - return -(ENXIO); - - dev = dev >> 4; - - if (dev >= num_mixers) - return -(ENXIO); - - if (cmd == SOUND_MIXER_INFO) - return get_mixer_info (dev, arg); - return mixer_devs[dev]->ioctl (dev, cmd, arg); - break; - -#ifdef CONFIG_SEQUENCER - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - return sequencer_ioctl (dev, file, cmd, arg); - break; -#endif - -#ifdef CONFIG_AUDIO - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - return audio_ioctl (dev, file, cmd, arg); - break; -#endif - -#ifdef CONFIG_MIDI - case SND_DEV_MIDIN: - return MIDIbuf_ioctl (dev, file, cmd, arg); - break; -#endif - - } - - return -(EPERM); -} diff --git a/drivers/sound/sound_syms.c b/drivers/sound/sound_syms.c new file mode 100644 index 000000000000..c3c4f84b0596 --- /dev/null +++ b/drivers/sound/sound_syms.c @@ -0,0 +1,62 @@ +/* + * The sound core exports the following symbols to the rest of + * modulespace. + * + * (C) Copyright 1997 Alan Cox, Licensed under the GNU GPL + */ + +#include +#include "sound_config.h" +#include "sound_calls.h" +#include "soundmodule.h" + +char sound_syms_symbol; +extern int softoss_dev; + +struct symbol_table sound_syms={ +#include + X(mixer_devs), + X(audio_devs), + X(num_mixers), + X(num_audiodevs), + + X(midi_devs), + X(num_midis), + X(synth_devs), + X(num_synths), + + X(sound_timer_devs), + X(num_sound_timers), + + X(sound_install_audiodrv), + X(sound_install_mixer), + X(sound_alloc_dma), + X(sound_free_dma), + X(sound_open_dma), + X(sound_close_dma), + X(sound_alloc_audiodev), + X(sound_alloc_mididev), + X(sound_alloc_mixerdev), + X(sound_alloc_timerdev), + X(sound_alloc_synthdev), + X(sound_unload_audiodev), + X(sound_unload_mididev), + X(sound_unload_mixerdev), + X(sound_unload_timerdev), + X(sound_unload_synthdev), + + X(load_mixer_volumes), + + X(conf_printf), + X(conf_printf2), + + X(softoss_dev), + +/* Locking */ + X(sound_locker), + X(sound_notifier_chain_register), +#include +}; + +MODULE_DESCRIPTION("Sound subsystem"); +MODULE_AUTHOR("Hannu Savolainen, et al."); diff --git a/drivers/sound/sound_tables.h b/drivers/sound/sound_tables.h new file mode 100644 index 000000000000..9aaeff7f254f --- /dev/null +++ b/drivers/sound/sound_tables.h @@ -0,0 +1,15 @@ +/* + * Sound symbol tables used in 2.0.34 + modular sound + */ + +extern struct symbol_table ad1848_symbol_table; +extern struct symbol_table audio_symbol_table; +extern struct symbol_table midi_syms; +extern struct symbol_table mpu401_symbols; +extern struct symbol_table opl3_syms ; +extern struct symbol_table sb_syms; +extern struct symbol_table sequencer_symbols; +extern struct symbol_table soundcore_symtab; +extern struct symbol_table firmware_symbols ; +extern struct symbol_table sound_syms; +extern struct symbol_table uart401_syms; diff --git a/drivers/sound/sound_timer.c b/drivers/sound/sound_timer.c index fdc5cb26b472..8033437f151f 100644 --- a/drivers/sound/sound_timer.c +++ b/drivers/sound/sound_timer.c @@ -2,16 +2,18 @@ * sound/sound_timer.c */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 -#define SEQUENCER_C #include "sound_config.h" #if defined(CONFIG_SEQUENCER) @@ -27,312 +29,293 @@ static volatile unsigned long usecs_per_tmr; /* Length of the current interval * static struct sound_lowlev_timer *tmr = NULL; -static unsigned long -tmr2ticks (int tmr_value) +static unsigned long tmr2ticks(int tmr_value) { - /* - * Convert timer ticks to MIDI ticks - */ - - unsigned long tmp; - unsigned long scale; + /* + * Convert timer ticks to MIDI ticks + */ - tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */ + unsigned long tmp; + unsigned long scale; - scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ - - return (tmp + (scale / 2)) / 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; } -static void -reprogram_timer (void) +void reprogram_timer(void) { - unsigned long usecs_per_tick; - - 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); + 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) +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; + /* + * 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; } -static void -tmr_reset (void) +static void tmr_reset(void) { - unsigned long flags; - - save_flags (flags); - cli (); - tmr_offs = 0; - ticks_offs = 0; - tmr_ctr = 0; - next_event_time = (unsigned long) -1; - prev_event_time = 0; - curr_ticks = 0; - restore_flags (flags); + unsigned long flags; + + save_flags(flags); + cli(); + tmr_offs = 0; + ticks_offs = 0; + tmr_ctr = 0; + next_event_time = (unsigned long) -1; + prev_event_time = 0; + curr_ticks = 0; + restore_flags(flags); } -static int -timer_open (int dev, int mode) +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; + if (opened) + return -EBUSY; + tmr_reset(); + curr_tempo = 60; + curr_timebase = 100; + opened = 1; + reprogram_timer(); + return 0; } -static void -timer_close (int dev) +static void timer_close(int dev) { - opened = tmr_running = 0; - tmr->tmr_disable (tmr->dev); + opened = tmr_running = 0; + tmr->tmr_disable(tmr->dev); } -static int -timer_event (int dev, unsigned char *event) +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; + unsigned char cmd = event[1]; + unsigned long parm = *(int *) &event[4]; - case TMR_STOP: - tmr_running = 0; - break; - - case TMR_CONTINUE: - tmr_running = 1; - reprogram_timer (); - break; - - case TMR_TEMPO: - if (parm) + switch (cmd) { - 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 (); + 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: } - break; - - case TMR_ECHO: - seq_copy_to_input (event, 8); - break; - - default:; - } - - return TIMER_NOT_ARMED; + return TIMER_NOT_ARMED; } -static unsigned long -timer_get_time (int dev) +static unsigned long timer_get_time(int dev) { - if (!opened) - return 0; - - return curr_ticks; + if (!opened) + return 0; + return curr_ticks; } -static int -timer_ioctl (int dev, - unsigned int cmd, caddr_t arg) +static int timer_ioctl(int dev, unsigned int cmd, caddr_t arg) { - switch (cmd) - { - case SNDCTL_TMR_SOURCE: - return snd_ioctl_return ((int *) arg, TMR_INTERNAL); - break; - - case SNDCTL_TMR_START: - tmr_reset (); - tmr_running = 1; - return 0; - break; - - case SNDCTL_TMR_STOP: - tmr_running = 0; - return 0; - break; - - case SNDCTL_TMR_CONTINUE: - tmr_running = 1; - return 0; - break; - - case SNDCTL_TMR_TIMEBASE: - { - int val = get_user ((int *) arg); - - if (val) - { - if (val < 1) - val = 1; - if (val > 1000) - val = 1000; - curr_timebase = val; - } - - return snd_ioctl_return ((int *) arg, curr_timebase); - } - break; - - case SNDCTL_TMR_TEMPO: - { - int val = get_user ((int *) arg); - - 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 snd_ioctl_return ((int *) arg, curr_tempo); - } - break; - - case SNDCTL_SEQ_CTRLRATE: - if (get_user ((int *) arg) != 0) /* Can't change */ - return -(EINVAL); - - return snd_ioctl_return ((int *) arg, ((curr_tempo * curr_timebase) + 30) / 60); - break; - - case SNDCTL_TMR_METRONOME: - /* NOP */ - break; - - default:; - } - - return -(EINVAL); + 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, (int *)arg)) + 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, (int *)arg)) + 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, (int *)arg)) + 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, (int *)arg); } -static void -timer_arm (int dev, long time) +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; + 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; + next_event_time = prev_event_time = time; + return; } static struct sound_timer_operations sound_timer = { - {"GUS Timer", 0}, - 1, /* Priority */ - 0, /* Local device link */ - timer_open, - timer_close, - timer_event, - timer_get_time, - timer_ioctl, - timer_arm + {"Sound Timer", 0}, + 1, /* Priority */ + 0, /* Local device link */ + timer_open, + timer_close, + timer_event, + timer_get_time, + timer_ioctl, + timer_arm }; -void -sound_timer_interrupt (void) +void sound_timer_interrupt(void) { - if (!opened) - return; + if (!opened) + return; - tmr->tmr_restart (tmr->dev); + tmr->tmr_restart(tmr->dev); - if (!tmr_running) - return; + if (!tmr_running) + return; - tmr_ctr++; - curr_ticks = ticks_offs + tmr2ticks (tmr_ctr); + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); - if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer (0); - } + if (curr_ticks >= next_event_time) + { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } } -void -sound_timer_init (struct sound_lowlev_timer *t, char *name) +void sound_timer_init(struct sound_lowlev_timer *t, char *name) { - int n; - - if (initialized || t == NULL) - return; /* There is already a similar timer */ - - initialized = 1; - tmr = t; + int n; - if (num_sound_timers >= MAX_TIMER_DEV) - n = 0; /* Overwrite the system timer */ - else - n = num_sound_timers++; - - strcpy (sound_timer.info.name, name); - - sound_timer_devs[n] = &sound_timer; + 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 */ + strcpy(sound_timer.info.name, name); + sound_timer_devs[n] = &sound_timer; } #endif diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index 15068a41fcdd..92a654fdacfe 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -1,27 +1,61 @@ /* - * linux/kernel/chr_drv/sound/soundcard.c + * linux/drivers/sound/soundcard.c * - * Soundcard driver for Linux + * Sound card driver for Linux */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 +/* + * 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) + */ +#include #include "sound_config.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#endif /* __KERNEL__ */ +#include +#include + +#include "soundmodule.h" +struct notifier_block *sound_locker=(struct notifier_block *)0; +static int lock_depth = 0; -#include +#ifdef MODULE +#define modular 1 +#else +#define modular 0 +#endif +/* + * 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 -int *sound_global_osp = NULL; static int chrdev_registered = 0; -static int sound_major = SOUND_MAJOR; - static int is_unloading = 0; /* @@ -32,9 +66,7 @@ int sound_nblocks = 0; static int soundcard_configured = 0; -static struct fileinfo files[SND_NDEVS]; - -static char dma_alloc_map[8] = +static char dma_alloc_map[MAX_DMA_CHANNELS] = {0}; #define DMA_MAP_UNAVAIL 0 @@ -42,749 +74,1016 @@ static char dma_alloc_map[8] = #define DMA_MAP_BUSY 2 -int -snd_ioctl_return (int *addr, int value) -{ - if (value < 0) - return value; +static int in_use = 0; /* Total # of open devices */ +unsigned long seq_time = 0; /* Time for /dev/sequencer */ - put_user (value, addr); - return 0; -} +/* + * Table for configurable mixer volume handling + */ +static mixer_vol_table mixer_vols[MAX_MIXER_DEV]; +static int num_mixer_volumes = 0; -static int -sound_read (inode_handle * inode, file_handle * file, char *buf, int count) + +int *load_mixer_volumes(char *name, int *levels, int present) { - int dev; + int i, n; - dev = MINOR (inode_get_rdev (inode)); + for (i = 0; i < num_mixer_volumes; i++) + { + if (strcmp(name, mixer_vols[i].name) == 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++; + + strcpy(mixer_vols[n].name, name); - files[dev].flags = file_get_flags (file); + if (present) + mixer_vols[n].num = n; + else + mixer_vols[n].num = -1; - return sound_read_sw (dev, &files[dev], buf, count); + for (i = 0; i < 32; i++) + mixer_vols[n].levels[i] = levels[i]; + return mixer_vols[n].levels; } -static int -sound_write (inode_handle * inode, file_handle * file, const char *buf, int count) +static int set_mixer_levels(caddr_t arg) { - int dev; - - dev = MINOR (inode_get_rdev (inode)); - - files[dev].flags = file_get_flags (file); - - return sound_write_sw (dev, &files[dev], buf, count); + /* 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 -sound_lseek (inode_handle * inode, file_handle * file, off_t offset, int orig) +static int get_mixer_levels(caddr_t arg) { - return -(EPERM); + int n; + + if (__get_user(n, (int *)(&(((mixer_vol_table *)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; } -static int -sound_open (inode_handle * inode, file_handle * file) +static int sound_proc_get_info(char *buffer, char **start, off_t offset, int length, int inout) { - int dev, retval; - struct fileinfo tmp_file; - - if (is_unloading) - { - printk ("Sound: Driver partially removed. Can't open device\n"); - return -(EBUSY); - } + int len, i, drv; + off_t pos = 0; + off_t begin = 0; - dev = MINOR (inode_get_rdev (inode)); - - if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS) - { - printk ("Sound Card Error: The soundcard system has not been configured\n"); - return -(ENXIO); - } +#ifdef MODULE +#define MODULEPROCSTRING "Driver loaded as a module" +#else +#define MODULEPROCSTRING "Driver compiled into kernel" +#endif + + len = sprintf(buffer, "OSS/Free:" SOUND_VERSION_STRING "\n" + "Load type: " MODULEPROCSTRING "\n" + "Kernel: %s %s %s %s %s\n" + "Config options: %x\n\nInstalled drivers: \n", + system_utsname.sysname, system_utsname.nodename, system_utsname.release, + system_utsname.version, system_utsname.machine, SELECTED_SOUND_OPTIONS); + + for (i = 0; (i < num_sound_drivers) && (pos <= offset + length); i++) { + if (!sound_drivers[i].card_type) + continue; + len += sprintf(buffer + len, "Type %d: %s\n", + sound_drivers[i].card_type, sound_drivers[i].name); + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + } + len += sprintf(buffer + len, "\nCard config: \n"); - tmp_file.mode = 0; - tmp_file.flags = file_get_flags (file); + for (i = 0; (i < num_sound_cards) && (pos <= offset + length); i++) + { + if (!snd_installed_cards[i].card_type) + continue; + if (!snd_installed_cards[i].enabled) + len += sprintf(buffer + len, "("); + if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) != -1) + len += sprintf(buffer + len, "%s", sound_drivers[drv].name); + if (snd_installed_cards[i].config.io_base) + len += sprintf(buffer + len, " at 0x%x", snd_installed_cards[i].config.io_base); + if (snd_installed_cards[i].config.irq != 0) + len += sprintf(buffer + len, " irq %d", abs(snd_installed_cards[i].config.irq)); + if (snd_installed_cards[i].config.dma != -1) { + len += sprintf(buffer + len, " drq %d", snd_installed_cards[i].config.dma); + if (snd_installed_cards[i].config.dma2 != -1) + len += sprintf(buffer + len, ",%d", snd_installed_cards[i].config.dma2); + } + if (!snd_installed_cards[i].enabled) + len += sprintf(buffer + len, ")"); + len += sprintf(buffer + len, "\n"); + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + } + if (!sound_started) + len += sprintf(buffer + len, "\n\n***** Sound driver not started *****\n\n"); +#ifndef CONFIG_AUDIO + len += sprintf(buffer + len, "\nAudio devices: NOT ENABLED IN CONFIG\n"); +#else + len += sprintf(buffer + len, "\nAudio devices:\n"); + for (i = 0; (i < num_audiodevs) && (pos <= offset + length); i++) { + if (audio_devs[i] == NULL) + continue; + len += sprintf(buffer + len, "%d: %s%s\n", i, audio_devs[i]->name, + audio_devs[i]->flags & DMA_DUPLEX ? " (DUPLEX)" : ""); + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + } +#endif - if ((tmp_file.flags & O_ACCMODE) == O_RDWR) - tmp_file.mode = OPEN_READWRITE; - if ((tmp_file.flags & O_ACCMODE) == O_RDONLY) - tmp_file.mode = OPEN_READ; - if ((tmp_file.flags & O_ACCMODE) == O_WRONLY) - tmp_file.mode = OPEN_WRITE; +#ifndef CONFIG_SEQUENCER + len += sprintf(buffer + len, "\nSynth devices: NOT ENABLED IN CONFIG\n"); +#else + len += sprintf(buffer + len, "\nSynth devices:\n"); + for (i = 0; (i < num_synths) && (pos <= offset + length); i++) { + if (synth_devs[i] == NULL) + continue; + len += sprintf(buffer + len, "%d: %s\n", i, synth_devs[i]->info->name); + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + } +#endif - if ((retval = sound_open_sw (dev, &tmp_file)) < 0) - return retval; +#ifndef CONFIG_MIDI + len += sprintf(buffer + len, "\nMidi devices: NOT ENABLED IN CONFIG\n"); +#else + len += sprintf(buffer + len, "\nMidi devices:\n"); + for (i = 0; (i < num_midis) && (pos <= offset + length); i++) { + if (midi_devs[i] == NULL) + continue; + len += sprintf(buffer + len, "%d: %s\n", i, midi_devs[i]->info.name); + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + } +#endif -#ifdef MODULE - MOD_INC_USE_COUNT; +#ifdef CONFIG_SEQUENCER + len += sprintf(buffer + len, "\nTimers:\n"); + + for (i = 0; (i < num_sound_timers) && (pos <= offset + length); i++) { + if (sound_timer_devs[i] == NULL) + continue; + len += sprintf(buffer + len, "%d: %s\n", i, sound_timer_devs[i]->info.name); + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + } #endif - memcpy ((char *) &files[dev], (char *) &tmp_file, sizeof (tmp_file)); - return retval; + len += sprintf(buffer + len, "\nMixers:\n"); + for (i = 0; (i < num_mixers) && (pos <= offset + length); i++) { + if (mixer_devs[i] == NULL) + continue; + len += sprintf(buffer + len, "%d: %s\n", i, mixer_devs[i]->name); + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + } + *start = buffer + (offset - begin); + len -= (offset - begin); + if (len > length) + len = length; + return len; } -static void -sound_release (inode_handle * inode, file_handle * file) -{ - int dev; +#if 0 +static struct proc_dir_entry proc_root_sound = { + PROC_SOUND, 5, "sound", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, NULL, sound_proc_get_info +}; +#endif - dev = MINOR (inode_get_rdev (inode)); +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif - files[dev].flags = file_get_flags (file); +/* 4K page size but our output routines use some slack for overruns */ +#define PROC_BLOCK_SIZE (3*1024) - sound_release_sw (dev, &files[dev]); -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif +/* + * basically copied from fs/proc/generic.c:proc_file_read + * should be removed sometime in the future together with /dev/sndstat + * (a symlink /dev/sndstat -> /proc/sound will do as well) + */ +static int sndstat_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) +{ + char *page; + int retval=0; + int eof=0; + int n, count; + char *start; + + if (!(page = (char*) __get_free_page(GFP_KERNEL))) + return -ENOMEM; + + while ((nbytes > 0) && !eof) + { + count = MIN(PROC_BLOCK_SIZE, nbytes); + + start = NULL; + n = sound_proc_get_info(page, &start, *ppos, count, 0); + if (n < count) + eof = 1; + + if (!start) { + /* + * For proc files that are less than 4k + */ + start = page + *ppos; + n -= *ppos; + if (n <= 0) + break; + if (n > count) + n = count; + } + if (n == 0) + break; /* End of file */ + if (n < 0) { + if (retval == 0) + retval = n; + break; + } + + n -= copy_to_user(buf, start, n); /* BUG ??? */ + if (n == 0) { + if (retval == 0) + retval = -EFAULT; + break; + } + + *ppos += n; /* Move down the file */ + nbytes -= n; + buf += n; + retval += n; + } + free_page((unsigned long) page); + return retval; } -static int -sound_ioctl (inode_handle * inode, file_handle * file, - unsigned int cmd, unsigned long arg) + +static int sound_read(struct inode *inode, struct file *file, char *buf, int count) { - int dev, err; + int dev = MINOR(inode->i_rdev); - dev = MINOR (inode_get_rdev (inode)); + DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count)); + switch (dev & 0x0f) { + case SND_DEV_STATUS: + return sndstat_file_read(file, buf, count, &file->f_pos); - files[dev].flags = file_get_flags (file); +#ifdef CONFIG_AUDIO + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_read(dev, file, buf, count); +#endif - if (_IOC_DIR (cmd) != _IOC_NONE) - { - /* - * Have to validate the address given by the process. - */ - int len; +#ifdef CONFIG_SEQUENCER + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_read(dev, file, buf, count); +#endif - len = _IOC_SIZE (cmd); +#ifdef CONFIG_MIDI + case SND_DEV_MIDIN: + return MIDIbuf_read(dev, file, buf, count); +#endif - if (_IOC_DIR (cmd) & _IOC_WRITE) - { - if ((err = verify_area (VERIFY_READ, (void *) arg, len)) < 0) - return err; + default:; } + return -EINVAL; +} - if (_IOC_DIR (cmd) & _IOC_READ) - { - if ((err = verify_area (VERIFY_WRITE, (void *) arg, len)) < 0) - return err; - } +static int sound_write(struct inode *inode, struct file *file, const char *buf, int count) +{ + int dev = MINOR(inode->i_rdev); - } + DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count)); + switch (dev & 0x0f) { +#ifdef CONFIG_SEQUENCER + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_write(dev, file, buf, count); +#endif - err = sound_ioctl_sw (dev, &files[dev], cmd, (caddr_t) arg); +#ifdef CONFIG_AUDIO + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_write(dev, file, buf, count); +#endif - return err; +#ifdef CONFIG_MIDI + case SND_DEV_MIDIN: + return MIDIbuf_write(dev, file, buf, count); +#endif + } + return -EINVAL; } -static int -sound_select (inode_handle * inode, file_handle * file, int sel_type, select_table_handle * wait) +static int sound_lseek(struct inode *inode, struct file *file, off_t offset, int orig) { - int dev; - - dev = MINOR (inode_get_rdev (inode)); + return -ESPIPE; +} - files[dev].flags = file_get_flags (file); +static int sound_open(struct inode *inode, struct file *file) +{ + int dev, retval; - DEB (printk ("sound_select(dev=%d, type=0x%x)\n", dev, sel_type)); + if (is_unloading) { + /* printk(KERN_ERR "Sound: Driver partially removed. Can't open device\n");*/ + return -EBUSY; + } + dev = MINOR(inode->i_rdev); + if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS) { + /* printk("SoundCard Error: The sound system has not been configured\n");*/ + return -ENXIO; + } + DEB(printk("sound_open(dev=%d)\n", dev)); + if ((dev >= SND_NDEVS) || (dev < 0)) { + /* printk(KERN_ERR "Invalid minor device %d\n", dev);*/ + return -ENXIO; + } + switch (dev & 0x0f) { + case SND_DEV_STATUS: + break; + + case SND_DEV_CTL: + dev >>= 4; +#ifdef CONFIG_KERNELD + if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { + char modname[20]; + sprintf(modname, "mixer%d", dev); + request_module(modname); + } +#endif + if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL)) + return -ENXIO; + break; - switch (dev & 0x0f) - { #ifdef CONFIG_SEQUENCER - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - return sequencer_select (dev, &files[dev], sel_type, wait); - break; + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + if ((retval = sequencer_open(dev, file)) < 0) + return retval; + break; #endif #ifdef CONFIG_MIDI - case SND_DEV_MIDIN: - return MIDIbuf_select (dev, &files[dev], sel_type, wait); - break; + case SND_DEV_MIDIN: + if ((retval = MIDIbuf_open(dev, file)) < 0) + return retval; + break; #endif #ifdef CONFIG_AUDIO - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - return audio_select (dev, &files[dev], sel_type, wait); - break; + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + if ((retval = audio_open(dev, file)) < 0) + return retval; + break; #endif - default: - return 0; - } + default: + printk(KERN_ERR "Invalid minor device %d\n", dev); + return -ENXIO; + } + in_use++; - return 0; -} +#ifdef CONFIG_MODULES + notifier_call_chain(&sound_locker, 1, 0); + lock_depth++; +#endif -static int -sound_mmap (inode_handle * inode, file_handle * file, vm_area_handle * vma) -{ - int dev, dev_class; - unsigned long size; - struct dma_buffparms *dmap = NULL; - - dev = MINOR (inode_get_rdev (inode)); - - files[dev].flags = file_get_flags (file); - - dev_class = dev & 0x0f; - dev >>= 4; - - if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) - { - printk ("Sound: mmap() not supported for other than audio devices\n"); - return -EINVAL; - } - - if ((vma_get_flags (vma) & (VM_READ | VM_WRITE)) == (VM_READ | VM_WRITE)) - { - printk ("Sound: Cannot do read/write mmap()\n"); - return -EINVAL; - } - - if (vma_get_flags (vma) & VM_READ) - { - dmap = audio_devs[dev]->dmap_in; - } - else if (vma_get_flags (vma) & VM_WRITE) - { - dmap = audio_devs[dev]->dmap_out; - } - else - { - printk ("Sound: Undefined mmap() access\n"); - return -EINVAL; - } - - if (dmap == NULL) - { - printk ("Sound: mmap() error. dmap == NULL\n"); - return -EIO; - } - - if (dmap->raw_buf == NULL) - { - printk ("Sound: mmap() called when raw_buf == NULL\n"); - return -EIO; - } - - if (dmap->mapping_flags) - { - printk ("Sound: mmap() called twice for the same DMA buffer\n"); - return -EIO; - } - - if (vma_get_offset (vma) != 0) - { - printk ("Sound: mmap() offset must be 0.\n"); - return -EINVAL; - } - - size = vma_get_end (vma) - vma_get_start (vma); - - if (size != dmap->bytes_in_use) - { - printk ("Sound: mmap() size = %ld. Should be %d\n", - size, dmap->bytes_in_use); - } - - if (remap_page_range (vma_get_start (vma), (unsigned long)dmap->raw_buf, - vma_get_end (vma) - vma_get_start (vma), - vma_get_page_prot (vma))) - return -EAGAIN; - - vma_set_inode (vma, inode); - inode_inc_count (inode); - - dmap->mapping_flags |= DMA_MAP_MAPPED; - - memset (dmap->raw_buf, - dmap->neutral_byte, - dmap->bytes_in_use); - return 0; + return 0; } -static struct file_operation_handle sound_fops = -{ - sound_lseek, - sound_read, - sound_write, - NULL, /* sound_readdir */ - sound_select, - sound_ioctl, - sound_mmap, - sound_open, - sound_release -}; - -void -soundcard_init (void) +static void sound_release(struct inode *inode, struct file *file) { -#ifndef MODULE - module_register_chrdev (sound_major, "sound", &sound_fops); - chrdev_registered = 1; + int dev = MINOR(inode->i_rdev); + + DEB(printk("sound_release(dev=%d)\n", dev)); + switch (dev & 0x0f) { + case SND_DEV_STATUS: + case SND_DEV_CTL: + break; + +#ifdef CONFIG_SEQUENCER + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + sequencer_release(dev, file); + break; #endif - soundcard_configured = 1; - - sndtable_init (); /* Initialize call tables and detect cards */ - - - -#ifdef CONFIG_LOWLEVEL_SOUND - { - extern void sound_init_lowlevel_drivers (void); - - sound_init_lowlevel_drivers (); - } +#ifdef CONFIG_MIDI + case SND_DEV_MIDIN: + MIDIbuf_release(dev, file); + break; #endif - if (sndtable_get_cardcount () == 0) - return; /* No cards detected */ - #ifdef CONFIG_AUDIO - if (num_audiodevs) /* Audio devices present */ - { - DMAbuf_init (); - audio_init (); - } + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + audio_release(dev, file); + break; #endif -#ifdef CONFIG_MIDI - if (num_midis) - MIDIbuf_init (); -#endif + default: + printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev); + } + in_use--; -#ifdef CONFIG_SEQUENCER - if (num_midis + num_synths) - sequencer_init (); +#ifdef CONFIG_MODULES + notifier_call_chain(&sound_locker, 0, 0); + lock_depth--; #endif - } -static unsigned int irqs = 0; - -#ifdef MODULE -static void -free_all_irqs (void) +static int get_mixer_info(int dev, caddr_t arg) { - int i; - - for (i = 0; i < 31; i++) - if (irqs & (1ul << i)) - { - printk ("Sound warning: IRQ%d was left allocated - fixed.\n", i); - snd_release_irq (i); - } - irqs = 0; + mixer_info info; + + strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); + strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + info.modify_counter = mixer_devs[dev]->modify_counter; + if (__copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; } -char kernel_version[] = UTS_RELEASE; - -#endif - -static int debugmem = 0; /* switched off by default */ - -static int sound[20] = -{0}; - -int -init_module (void) +static int get_old_mixer_info(int dev, caddr_t arg) { - int err; - int ints[21]; - int i; - - if (connect_wrapper (WRAPPER_VERSION) < 0) - { - printk ("Sound: Incompatible kernel (wrapper) version\n"); - return -EINVAL; - } - - /* - * "sound=" command line handling by Harald Milz. - */ - i = 0; - while (i < 20 && sound[i]) - ints[i + 1] = sound[i++]; - ints[0] = i; - - if (i) - sound_setup ("sound=", ints); - - err = module_register_chrdev (sound_major, "sound", &sound_fops); - if (err) - { - printk ("sound: driver already loaded/included in kernel\n"); - return err; - } - - chrdev_registered = 1; - soundcard_init (); - - if (sound_nblocks >= 1024) - printk ("Sound warning: Deallocation table was too small.\n"); - - return 0; + _old_mixer_info info; + + strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); + strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); + info.name[sizeof(info.name)-1] = 0; + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; } -#ifdef MODULE - - -void -cleanup_module (void) +static int sound_mixer_ioctl(int mixdev, unsigned int cmd, caddr_t arg) { - int i; - - if (MOD_IN_USE) - { - return; - } + if (mixdev < 0 || mixdev >= MAX_MIXER_DEV) + return -ENXIO; +#ifdef CONFIG_KERNELD + /* Try to load the mixer... */ + if (mixer_devs[mixdev] == NULL) { + char modname[20]; + sprintf(modname, "mixer%d", mixdev); + request_module(modname); + } +#endif /* CONFIG_KERNELD */ + 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); +} - if (chrdev_registered) - module_unregister_chrdev (sound_major, "sound"); +static int sound_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err, len = 0, dtype; + int dev = MINOR(inode->i_rdev); + + 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 || arg == 0) + return -EFAULT; + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + if ((err = verify_area(VERIFY_READ, (void *)arg, len)) < 0) + return err; + if (_SIOC_DIR(cmd) & _SIOC_READ) + if ((err = verify_area(VERIFY_WRITE, (void *)arg, len)) < 0) + return err; + } + DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); + if (cmd == OSS_GETVERSION) + { + int v=SOUND_VERSION; + __put_user(v, (int *)arg); + } + + if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */ + (dev & 0x0f) != SND_DEV_CTL) { + dtype = dev & 0x0f; + switch (dtype) { +#ifdef CONFIG_AUDIO + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev, + cmd, (caddr_t)arg); +#endif + + default: + return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg); + } + } + switch (dev & 0x0f) { + case SND_DEV_CTL: + if (cmd == SOUND_MIXER_GETLEVELS) + return get_mixer_levels((caddr_t)arg); + if (cmd == SOUND_MIXER_SETLEVELS) + return set_mixer_levels((caddr_t)arg); + return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg); #ifdef CONFIG_SEQUENCER - sound_stop_timer (); + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_ioctl(dev, file, cmd, (caddr_t)arg); #endif -#ifdef CONFIG_LOWLEVEL_SOUND - { - extern void sound_unload_lowlevel_drivers (void); - - sound_unload_lowlevel_drivers (); - } +#ifdef CONFIG_AUDIO + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return audio_ioctl(dev, file, cmd, (caddr_t)arg); + break; #endif - sound_unload_drivers (); - for (i = 0; i < sound_nblocks; i++) - vfree (sound_mem_blocks[i]); - - free_all_irqs (); /* If something was left allocated by accident */ - - for (i = 0; i < 8; i++) - if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) - { - printk ("Sound: Hmm, DMA%d was left allocated - fixed\n", i); - sound_free_dma (i); - } - - -} +#ifdef CONFIG_MIDI + case SND_DEV_MIDIN: + return MIDIbuf_ioctl(dev, file, cmd, (caddr_t)arg); + break; #endif -void -tenmicrosec (int *osp) -{ - int i; - - for (i = 0; i < 16; i++) - inb (0x80); + } + return -EINVAL; } -int -snd_set_irq_handler (int interrupt_level, void (*iproc) (int, void *, struct pt_regs *), char *name, int *osp) +static int sound_select(struct inode *inode, struct file *file, int sel_type, + select_table * wait) { - int retcode; - unsigned long flags; - - save_flags (flags); - cli (); + int dev = MINOR(inode->i_rdev); - retcode = request_irq (interrupt_level, iproc, 0 /* SA_INTERRUPT */ , name, NULL); - if (retcode < 0) - { - printk ("Sound: IRQ%d already in use\n", interrupt_level); - } - else - irqs |= (1ul << interrupt_level); - - restore_flags (flags); - return retcode; -} + DEB(printk("sound_poll(dev=%d)\n", dev)); + switch (dev & 0x0f) { +#ifdef CONFIG_SEQUENCER + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_select(dev, file, sel_type, wait); +#endif -void -snd_release_irq (int vect) -{ - if (!(irqs & (1ul << vect))) - return; +#ifdef CONFIG_MIDI + case SND_DEV_MIDIN: + return MIDIbuf_select(dev, file, sel_type, wait); +#endif - irqs &= ~(1ul << vect); - free_irq (vect, NULL); +#ifdef CONFIG_AUDIO + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + return DMAbuf_select(file, dev >> 4, sel_type, wait); +#endif + } + return 0; } -int -sound_alloc_dma (int chn, char *deviceID) +static int sound_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma) { - int err; - - if ((err = request_dma (chn, deviceID)) != 0) - return err; + int dev_class; + unsigned long size; + struct dma_buffparms *dmap = NULL; + int dev = MINOR(inode->i_rdev); - dma_alloc_map[chn] = DMA_MAP_FREE; - - return 0; -} + dev_class = dev & 0x0f; + dev >>= 4; -int -sound_open_dma (int chn, char *deviceID) -{ - unsigned long flags; + if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) + { +/* printk("Sound: mmap() not supported for other than audio devices\n");*/ + return -EINVAL; + } + 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("Sound: Undefined mmap() access\n");*/ + return -EINVAL; + } - if (chn < 0 || chn > 7 || chn == 4) - { - printk ("sound_open_dma: Invalid DMA channel %d\n", chn); - return 1; - } + if (dmap == NULL) + { +/* printk("Sound: mmap() error. dmap == NULL\n");*/ + return -EIO; + } + if (dmap->raw_buf == NULL) + { +/* printk("Sound: mmap() called when raw_buf == NULL\n");*/ + return -EIO; + } + if (dmap->mapping_flags) + { +/* printk("Sound: mmap() called twice for the same DMA buffer\n");*/ + return -EIO; + } + if (vma->vm_offset != 0) + { +/* printk("Sound: mmap() offset must be 0.\n");*/ + return -EINVAL; + } + size = vma->vm_end - vma->vm_start; - save_flags (flags); - cli (); + if (size != dmap->bytes_in_use) + { + printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use); + } + if (remap_page_range(vma->vm_start, virt_to_phys(dmap->raw_buf), + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; - if (dma_alloc_map[chn] != DMA_MAP_FREE) - { - printk ("sound_open_dma: DMA channel %d busy or not allocated\n", chn); - restore_flags (flags); - return 1; - } + vma_set_inode (vma, inode); + inode_inc_count (inode); - dma_alloc_map[chn] = DMA_MAP_BUSY; - restore_flags (flags); - return 0; -} + dmap->mapping_flags |= DMA_MAP_MAPPED; -void -sound_free_dma (int chn) -{ - if (dma_alloc_map[chn] != DMA_MAP_FREE) - { - /* printk ("sound_free_dma: Bad access to DMA channel %d\n", chn); */ - return; - } - free_dma (chn); - dma_alloc_map[chn] = DMA_MAP_UNAVAIL; + memset(dmap->raw_buf, + dmap->neutral_byte, + dmap->bytes_in_use); + return 0; } -void -sound_close_dma (int chn) +struct file_operations oss_sound_fops = { - unsigned long flags; - - save_flags (flags); - cli (); + sound_lseek, + sound_read, + sound_write, + NULL, /* sound_readdir */ + sound_select, + sound_ioctl, + sound_mmap, + sound_open, + sound_release +}; - if (dma_alloc_map[chn] != DMA_MAP_BUSY) - { - printk ("sound_close_dma: Bad access to DMA channel %d\n", chn); - restore_flags (flags); - return; - } - dma_alloc_map[chn] = DMA_MAP_FREE; - restore_flags (flags); +/* + * Create the required special subdevices + */ + +static int create_special_devices(void) +{ + int seq1,seq2; + int sndstat=register_sound_special(&oss_sound_fops, 6); + if(sndstat==-1) + goto bad1; + seq1=register_sound_special(&oss_sound_fops, 1); + if(seq1==-1) + goto bad2; + seq2=register_sound_special(&oss_sound_fops, 8); + if(seq2!=-1) + return 0; + unregister_sound_special(1); +bad2: + unregister_sound_special(6); +bad1: + return -1; } -#ifdef CONFIG_SEQUENCER - - -static struct timer_list seq_timer = -{NULL, NULL, 0, 0, sequencer_timer}; +static void destroy_special_devices(void) +{ + unregister_sound_special(6); + unregister_sound_special(1); + unregister_sound_special(8); +} +#ifdef MODULE +static void +#else void -request_sound_timer (int count) +#endif +soundcard_init(void) { - extern unsigned long seq_time; + /* drag in sound_syms.o */ + { + extern char sound_syms_symbol; + sound_syms_symbol = 0; + } - if (count < 0) - count = jiffies + (-count); - else - count += seq_time; +#ifndef MODULE + create_special_devices(); + chrdev_registered = 1; +#endif - ; + soundcard_configured = 1; - { - seq_timer.expires = ((count - jiffies)) + jiffies; - add_timer (&seq_timer); - }; -} + sndtable_init(); /* Initialize call tables and detect cards */ -void -sound_stop_timer (void) -{ - del_timer (&seq_timer);; -} + +#ifdef FIXME + if (sndtable_get_cardcount() == 0) + return; /* No cards detected */ #endif #ifdef CONFIG_AUDIO - -#ifdef KMALLOC_DMA_BROKEN -fatal_error__This_version_is_not_compatible_with_this_kernel; + if (num_audiodevs || modular) /* Audio devices present */ + { + audio_init_devices(); + } #endif +} -static int dma_buffsize = DSP_BUFFSIZE; +static int sound[20] = { + 0 +}; -int -sound_alloc_dmap (int dev, struct dma_buffparms *dmap, int chan) +#ifdef MODULE + +int init_module(void) { - char *start_addr, *end_addr; - int i, dma_pagesize; + int err; + int ints[21]; + int i; + + /* + * "sound=" command line handling by Harald Milz. + */ + i = 0; + while (i < 20 && sound[i]) + ints[i + 1] = sound[i++]; + ints[0] = i; + + if (i) + sound_setup("sound=", ints); + + err = create_special_devices(); + if (err) + { + printk(KERN_ERR "sound: driver already loaded/included in kernel\n"); + return err; + } + chrdev_registered = 1; + soundcard_init(); + + if (sound_nblocks >= 1024) + printk(KERN_ERR "Sound warning: Deallocation table was too small.\n"); +#if 0 + if (proc_register(&proc_root, &proc_root_sound)) + printk(KERN_ERR "sound: registering /proc/sound failed\n"); +#endif + return 0; +} - dmap->mapping_flags &= ~DMA_MAP_MAPPED; +void cleanup_module(void) +{ + int i; - if (dmap->raw_buf != NULL) - return 0; /* Already done */ + if (MOD_IN_USE) + { + return; + } +#if 0 + if (proc_unregister(&proc_root, PROC_SOUND)) + printk(KERN_ERR "sound: unregistering /proc/sound failed\n"); +#endif + if (chrdev_registered) + destroy_special_devices(); - if (dma_buffsize < 4096) - dma_buffsize = 4096; +#ifdef CONFIG_SEQUENCER + sound_stop_timer(); +#endif - if (chan < 4) - dma_pagesize = 64 * 1024; - else - dma_pagesize = 128 * 1024; +#ifdef CONFIG_LOWLEVEL_SOUND + { + extern void sound_unload_lowlevel_drivers(void); - dmap->raw_buf = NULL; + sound_unload_lowlevel_drivers(); + } +#endif + sound_unload_drivers(); + sequencer_unload(); - if (debugmem) - printk ("sound: buffsize[%d] = %lu\n", dev, audio_devs[dev]->buffsize); + 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]); + } - audio_devs[dev]->buffsize = dma_buffsize; +} +#endif - if (audio_devs[dev]->buffsize > dma_pagesize) - audio_devs[dev]->buffsize = dma_pagesize; +int sound_alloc_dma(int chn, char *deviceID) +{ + int err; - start_addr = NULL; + if ((err = request_dma(chn, deviceID)) != 0) + return err; -/* - * Now loop until we get a free buffer. Try to get smaller buffer if - * it fails. - */ + dma_alloc_map[chn] = DMA_MAP_FREE; - while (start_addr == NULL && audio_devs[dev]->buffsize > PAGE_SIZE) - { - int sz, size; + return 0; +} - for (sz = 0, size = PAGE_SIZE; - size < audio_devs[dev]->buffsize; - sz++, size <<= 1); +int sound_open_dma(int chn, char *deviceID) +{ + unsigned long flags; - audio_devs[dev]->buffsize = PAGE_SIZE * (1 << sz); + if (!valid_dma(chn)) + { + printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn); + return 1; + } + save_flags(flags); + cli(); - if ((start_addr = (char *) __get_dma_pages (GFP_ATOMIC, sz)) == NULL) - audio_devs[dev]->buffsize /= 2; - } + 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]); + restore_flags(flags); + return 1; + } + dma_alloc_map[chn] = DMA_MAP_BUSY; + restore_flags(flags); + return 0; +} - if (start_addr == NULL) - { - printk ("Sound error: Couldn't allocate DMA buffer\n"); - return -(ENOMEM); - } - else - { - /* make some checks */ - end_addr = start_addr + audio_devs[dev]->buffsize - 1; +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; +} - if (debugmem) - printk ("sound: start 0x%lx, end 0x%lx\n", - (long) start_addr, (long) end_addr); +void sound_close_dma(int chn) +{ + unsigned long flags; - /* now check if it fits into the same dma-pagesize */ + save_flags(flags); + cli(); - if (((long) start_addr & ~(dma_pagesize - 1)) - != ((long) end_addr & ~(dma_pagesize - 1)) - || end_addr >= (char *) (MAX_DMA_ADDRESS)) + if (dma_alloc_map[chn] != DMA_MAP_BUSY) { - printk ( - "sound: Got invalid address 0x%lx for %ldb DMA-buffer\n", - (long) start_addr, - audio_devs[dev]->buffsize); - return -(EFAULT); - } - } - dmap->raw_buf = start_addr; - dmap->raw_buf_phys = virt_to_bus (start_addr); - - for (i = MAP_NR (start_addr); i <= MAP_NR (end_addr); i++) - { - mem_map_reserve (i); - } - - return 0; + printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn); + restore_flags(flags); + return; + } + dma_alloc_map[chn] = DMA_MAP_FREE; + restore_flags(flags); } -void -sound_free_dmap (int dev, struct dma_buffparms *dmap) +#ifdef CONFIG_SEQUENCER + +static void do_sequencer_timer(unsigned long dummy) { - int sz, size, i; - unsigned long start_addr, end_addr; + sequencer_timer(0); +} - if (dmap->raw_buf == NULL) - return; - if (dmap->mapping_flags & DMA_MAP_MAPPED) - return; /* Don't free mmapped buffer. Will use it next time */ +static struct timer_list seq_timer = +{NULL, NULL, 0, 0, do_sequencer_timer}; + +void request_sound_timer(int count) +{ + extern unsigned long seq_time; - for (sz = 0, size = PAGE_SIZE; - size < audio_devs[dev]->buffsize; - sz++, size <<= 1); + if (count < 0) + { + seq_timer.expires = (-count) + jiffies; + add_timer(&seq_timer); + return; + } + count += seq_time; - start_addr = (unsigned long) dmap->raw_buf; - end_addr = start_addr + audio_devs[dev]->buffsize; + count -= jiffies; - for (i = MAP_NR (start_addr); i <= MAP_NR (end_addr); i++) - { - mem_map_unreserve (i); - } + if (count < 1) + count = 1; - free_pages ((unsigned long) dmap->raw_buf, sz); - dmap->raw_buf = NULL; + seq_timer.expires = (count) + jiffies; + add_timer(&seq_timer); } -int -sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc * info) +void sound_stop_timer(void) { - printk ("Entered sound_map_buffer()\n"); - printk ("Exited sound_map_buffer()\n"); - return -(EINVAL); + del_timer(&seq_timer);; } #endif -void -conf_printf (char *name, struct address_info *hw_config) +void conf_printf(char *name, struct address_info *hw_config) { - if (!trace_init) - return; + if (!trace_init) + return; - printk ("<%s> at 0x%03x", name, hw_config->io_base); + 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->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"); + 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"); } -void -conf_printf2 (char *name, int base, int irq, int dma, int dma2) +void conf_printf2(char *name, int base, int irq, int dma, int dma2) { - if (!trace_init) - return; + if (!trace_init) + return; - printk ("<%s> at 0x%03x", name, base); + printk("<%s> at 0x%03x", name, base); - if (irq) - printk (" irq %d", (irq > 0) ? irq : -irq); + if (irq) + printk(" irq %d", (irq > 0) ? irq : -irq); - if (dma != -1 || dma2 != -1) - { - printk (" dma %d", dma); - if (dma2 != -1) - printk (",%d", dma2); - } + if (dma != -1 || dma2 != -1) + { + printk(" dma %d", dma); + if (dma2 != -1) + printk(",%d", dma2); + } + printk("\n"); +} - printk ("\n"); +/* + * Module and lock management + */ + +/* + * When a sound module is registered we need to bring it to the current + * lock level... + */ + +void sound_notifier_chain_register(struct notifier_block *bl) +{ + int ct=0; + + notifier_chain_register(&sound_locker, bl); + /* + * Normalise the lock count by calling the entry directly. We + * have to call the module as it owns its own use counter + */ + while(ctnotifier_call(bl, 1, 0); + ct++; + } } diff --git a/drivers/sound/soundmodule.h b/drivers/sound/soundmodule.h new file mode 100644 index 000000000000..b89e74e519f2 --- /dev/null +++ b/drivers/sound/soundmodule.h @@ -0,0 +1,31 @@ +#ifndef _SOUNDMODULE_H +#define _SOUNDMODULE_H + +#include + +extern struct notifier_block *sound_locker; +extern void sound_notifier_chain_register(struct notifier_block *); + +#ifdef MODULE + +#define SOUND_LOCK sound_notifier_chain_register(&sound_notifier); +#define SOUND_LOCK_END notifier_chain_unregister(&sound_locker, &sound_notifier) + +static int my_notifier_call(struct notifier_block *b, unsigned long foo, void *bar) +{ + if(foo) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; + return NOTIFY_DONE; +} + +static struct notifier_block sound_notifier= +{ + my_notifier_call, + (void *)0, + 0 +}; + +#endif +#endif diff --git a/drivers/sound/soundvers.h b/drivers/sound/soundvers.h index 1e6db53217c4..e9084d2f46a9 100644 --- a/drivers/sound/soundvers.h +++ b/drivers/sound/soundvers.h @@ -1,2 +1,2 @@ -#define SOUND_VERSION_STRING "3.5.4-960630" -#define SOUND_INTERNAL_VERSION 0x030504 +#define SOUND_VERSION_STRING "3.8s2++-971130" +#define SOUND_INTERNAL_VERSION 0x030804 diff --git a/drivers/sound/sscape.c b/drivers/sound/sscape.c index f28ed5f996da..1b823692c0aa 100644 --- a/drivers/sound/sscape.c +++ b/drivers/sound/sscape.c @@ -3,696 +3,566 @@ * * Low level driver for Ensoniq SoundScape */ + /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 +/* + * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) + */ + +#include +#include #include "sound_config.h" +#include "soundmodule.h" -#if defined(CONFIG_SSCAPE) +#ifdef CONFIG_SSCAPE #include "coproc.h" /* * I/O ports */ -#define MIDI_DATA 0 -#define MIDI_CTRL 1 -#define HOST_CTRL 2 -#define TX_READY 0x02 -#define RX_READY 0x01 -#define HOST_DATA 3 -#define ODIE_ADDR 4 -#define ODIE_DATA 5 +#define MIDI_DATA 0 +#define MIDI_CTRL 1 +#define HOST_CTRL 2 +#define TX_READY 0x02 +#define RX_READY 0x01 +#define HOST_DATA 3 +#define ODIE_ADDR 4 +#define ODIE_DATA 5 /* * Indirect registers */ -#define GA_INTSTAT_REG 0 -#define GA_INTENA_REG 1 -#define GA_DMAA_REG 2 -#define GA_DMAB_REG 3 -#define GA_INTCFG_REG 4 -#define GA_DMACFG_REG 5 -#define GA_CDCFG_REG 6 -#define GA_SMCFGA_REG 7 -#define GA_SMCFGB_REG 8 -#define GA_HMCTL_REG 9 + +#define GA_INTSTAT_REG 0 +#define GA_INTENA_REG 1 +#define GA_DMAA_REG 2 +#define GA_DMAB_REG 3 +#define GA_INTCFG_REG 4 +#define GA_DMACFG_REG 5 +#define GA_CDCFG_REG 6 +#define GA_SMCFGA_REG 7 +#define GA_SMCFGB_REG 8 +#define GA_HMCTL_REG 9 /* * DMA channel identifiers (A and B) */ -#define SSCAPE_DMA_A 0 -#define SSCAPE_DMA_B 1 + +#define SSCAPE_DMA_A 0 +#define SSCAPE_DMA_B 1 #define PORT(name) (devc->base+name) /* * Host commands recognized by the OBP microcode */ -#define CMD_GEN_HOST_ACK 0x80 -#define CMD_GEN_MPU_ACK 0x81 -#define CMD_GET_BOARD_TYPE 0x82 -#define CMD_SET_CONTROL 0x88 -#define CMD_GET_CONTROL 0x89 -#define CTL_MASTER_VOL 0 -#define CTL_MIC_MODE 2 -#define CTL_SYNTH_VOL 4 -#define CTL_WAVE_VOL 7 -#define CMD_SET_MT32 0x96 -#define CMD_GET_MT32 0x97 -#define CMD_SET_EXTMIDI 0x9b -#define CMD_GET_EXTMIDI 0x9c + +#define CMD_GEN_HOST_ACK 0x80 +#define CMD_GEN_MPU_ACK 0x81 +#define CMD_GET_BOARD_TYPE 0x82 +#define CMD_SET_CONTROL 0x88 /* Old firmware only */ +#define CMD_GET_CONTROL 0x89 /* Old firmware only */ +#define CTL_MASTER_VOL 0 +#define CTL_MIC_MODE 2 +#define CTL_SYNTH_VOL 4 +#define CTL_WAVE_VOL 7 +#define CMD_SET_EXTMIDI 0x8a +#define CMD_GET_EXTMIDI 0x8b +#define CMD_SET_MT32 0x8c +#define CMD_GET_MT32 0x8d #define CMD_ACK 0x80 typedef struct sscape_info - { - int base, irq, dma; - int ok; /* Properly detected */ - int failed; - int dma_allocated; - int my_audiodev; - int opened; - int *osp; - } - -sscape_info; -static struct sscape_info dev_info = -{0}; -static struct sscape_info *devc = &dev_info; - -static wait_handle *sscape_sleeper = NULL; -static volatile struct snd_wait sscape_sleep_flag = -{0}; +{ + int base, irq, dma; + int ok; /* Properly detected */ + int failed; + int dma_allocated; + int codec_audiodev; + int opened; + int *osp; + int my_audiodev; +} sscape_info; + +static struct sscape_info adev_info = { + 0 +}; + +static struct sscape_info *devc = &adev_info; +static int sscape_mididev = -1; /* Some older cards have assigned interrupt bits differently than new ones */ -static char valid_interrupts_old[] = -{9, 7, 5, 15}; +static char valid_interrupts_old[] = { + 9, 7, 5, 15 +}; -static char valid_interrupts_new[] = -{9, 5, 7, 10}; +static char valid_interrupts_new[] = { + 9, 5, 7, 10 +}; -static char *valid_interrupts = valid_interrupts_new; +static char *valid_interrupts = valid_interrupts_new; +/* + * See the bottom of the driver. This can be set by spea =0/1. + */ + #ifdef REVEAL_SPEA -static char old_hardware = 1; - +static char old_hardware = 1; #else -static char old_hardware = 0; - +static char old_hardware = 0; #endif -static unsigned char -sscape_read (struct sscape_info *devc, int reg) +static void sleep(unsigned howlong) { - unsigned long flags; - unsigned char val; - - save_flags (flags); - cli (); - outb (reg, PORT (ODIE_ADDR)); - val = inb (PORT (ODIE_DATA)); - restore_flags (flags); - return val; + current->timeout = jiffies + 1; + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->timeout = 0; } -static void -sscape_write (struct sscape_info *devc, int reg, int data) +static unsigned char sscape_read(struct sscape_info *devc, int reg) { - unsigned long flags; - - save_flags (flags); - cli (); - outb (reg, PORT (ODIE_ADDR)); - outb (data, PORT (ODIE_DATA)); - restore_flags (flags); + unsigned long flags; + unsigned char val; + + save_flags(flags); + cli(); + outb(reg, PORT(ODIE_ADDR)); + val = inb(PORT(ODIE_DATA)); + restore_flags(flags); + return val; } -static void -host_open (struct sscape_info *devc) +static void sscape_write(struct sscape_info *devc, int reg, int data) { - outb (0x00, PORT (HOST_CTRL)); /* Put the board to the host mode */ + unsigned long flags; + + save_flags(flags); + cli(); + outb(reg, PORT(ODIE_ADDR)); + outb(data, PORT(ODIE_DATA)); + restore_flags(flags); } -static void -host_close (struct sscape_info *devc) +static void host_open(struct sscape_info *devc) { - outb (0x03, PORT (HOST_CTRL)); /* Put the board to the MIDI mode */ + outb((0x00), PORT(HOST_CTRL)); /* Put the board to the host mode */ } -static int -host_write (struct sscape_info *devc, unsigned char *data, int count) +static void host_close(struct sscape_info *devc) { - unsigned long flags; - int i, timeout_val; + outb((0x03), PORT(HOST_CTRL)); /* Put the board to the MIDI mode */ +} - save_flags (flags); - cli (); +static int host_write(struct sscape_info *devc, unsigned char *data, int count) +{ + unsigned long flags; + int i, timeout_val; - /* - * Send the command and data bytes - */ + save_flags(flags); + cli(); - for (i = 0; i < count; i++) - { - for (timeout_val = 10000; timeout_val > 0; timeout_val--) - if (inb (PORT (HOST_CTRL)) & TX_READY) - break; + /* + * Send the command and data bytes + */ - if (timeout_val <= 0) + for (i = 0; i < count; i++) { - restore_flags (flags); - return 0; + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + if (inb(PORT(HOST_CTRL)) & TX_READY) + break; + + if (timeout_val <= 0) + { + restore_flags(flags); + return 0; + } + outb(data[i], PORT(HOST_DATA)); } - - outb (data[i], PORT (HOST_DATA)); - } - - - restore_flags (flags); - - return 1; + restore_flags(flags); + return 1; } -static int -host_read (struct sscape_info *devc) +static int host_read(struct sscape_info *devc) { - unsigned long flags; - int timeout_val; - unsigned char data; + unsigned long flags; + int timeout_val; + unsigned char data; - save_flags (flags); - cli (); + save_flags(flags); + cli(); - /* - * Read a byte - */ + /* + * Read a byte + */ - for (timeout_val = 10000; timeout_val > 0; timeout_val--) - if (inb (PORT (HOST_CTRL)) & RX_READY) - break; + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + if (inb(PORT(HOST_CTRL)) & RX_READY) + break; - if (timeout_val <= 0) - { - restore_flags (flags); - return -1; - } - - data = inb (PORT (HOST_DATA)); - - restore_flags (flags); - - return data; + if (timeout_val <= 0) + { + restore_flags(flags); + return -1; + } + data = inb(PORT(HOST_DATA)); + restore_flags(flags); + return data; } -static int -host_command1 (struct sscape_info *devc, int cmd) -{ - unsigned char buf[10]; - - buf[0] = (unsigned char) (cmd & 0xff); - return host_write (devc, buf, 1); -} - -static int -host_command2 (struct sscape_info *devc, int cmd, int parm1) +static int host_command2(struct sscape_info *devc, int cmd, int parm1) { - unsigned char buf[10]; + unsigned char buf[10]; - buf[0] = (unsigned char) (cmd & 0xff); - buf[1] = (unsigned char) (parm1 & 0xff); + buf[0] = (unsigned char) (cmd & 0xff); + buf[1] = (unsigned char) (parm1 & 0xff); - return host_write (devc, buf, 2); + return host_write(devc, buf, 2); } -static int -host_command3 (struct sscape_info *devc, int cmd, int parm1, int parm2) +static int host_command3(struct sscape_info *devc, int cmd, int parm1, int parm2) { - unsigned char buf[10]; - - buf[0] = (unsigned char) (cmd & 0xff); - buf[1] = (unsigned char) (parm1 & 0xff); - buf[2] = (unsigned char) (parm2 & 0xff); + unsigned char buf[10]; - return host_write (devc, buf, 3); + buf[0] = (unsigned char) (cmd & 0xff); + buf[1] = (unsigned char) (parm1 & 0xff); + buf[2] = (unsigned char) (parm2 & 0xff); + return host_write(devc, buf, 3); } -static void -set_mt32 (struct sscape_info *devc, int value) +static void set_mt32(struct sscape_info *devc, int value) { - host_open (devc); - host_command2 (devc, CMD_SET_MT32, - value ? 1 : 0); - if (host_read (devc) != CMD_ACK) - { - /* printk ("SNDSCAPE: Setting MT32 mode failed\n"); */ - } - host_close (devc); -} - -static void -set_control (struct sscape_info *devc, int ctrl, int value) -{ - host_open (devc); - host_command3 (devc, CMD_SET_CONTROL, ctrl, value); - if (host_read (devc) != CMD_ACK) - { - /* printk ("SNDSCAPE: Setting control (%d) failed\n", ctrl); */ - } - host_close (devc); + host_open(devc); + host_command2(devc, CMD_SET_MT32, value ? 1 : 0); + if (host_read(devc) != CMD_ACK) + { + /* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */ + } + host_close(devc); } -static int -get_board_type (struct sscape_info *devc) +static void set_control(struct sscape_info *devc, int ctrl, int value) { - int tmp; - - host_open (devc); - if (!host_command1 (devc, CMD_GET_BOARD_TYPE)) - tmp = -1; - else - tmp = host_read (devc); - host_close (devc); - return tmp; + host_open(devc); + host_command3(devc, CMD_SET_CONTROL, ctrl, value); + if (host_read(devc) != CMD_ACK) + { + /* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */ + } + host_close(devc); } -void -sscapeintr (int irq, void *dev_id, struct pt_regs *dummy) +static void do_dma(struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode) { - unsigned char bits, tmp; - static int debug = 0; - - bits = sscape_read (devc, GA_INTSTAT_REG); - if ((sscape_sleep_flag.flags & WK_SLEEP)) - { - { - sscape_sleep_flag.flags = WK_WAKEUP; - module_wake_up (&sscape_sleeper); - }; - } - - if (bits & 0x02) /* Host interface interrupt */ - { - printk ("SSCAPE: Host interrupt, data=%02x\n", host_read (devc)); - } + unsigned char temp; -#if defined(CONFIG_MPU_EMU) && defined(CONFIG_MIDI) - if (bits & 0x01) - { - mpuintr (irq, NULL, NULL); - if (debug++ > 10) /* Temporary debugging hack */ + if (dma_chan != SSCAPE_DMA_A) { - sscape_write (devc, GA_INTENA_REG, 0x00); /* Disable all interrupts */ + printk(KERN_WARNING "soundscape: Tried to use DMA channel != A. Why?\n"); + return; } - } -#endif - - /* - * Acknowledge interrupts (toggle the interrupt bits) - */ - - tmp = sscape_read (devc, GA_INTENA_REG); - sscape_write (devc, GA_INTENA_REG, (~bits & 0x0e) | (tmp & 0xf1)); - -} - - -static void -do_dma (struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode) -{ - unsigned char temp; - - if (dma_chan != SSCAPE_DMA_A) - { - printk ("SSCAPE: Tried to use DMA channel != A. Why?\n"); - return; - } - - audio_devs[devc->my_audiodev]->flags &= ~DMA_AUTOMODE; - DMAbuf_start_dma (devc->my_audiodev, - buf, - blk_size, mode); - audio_devs[devc->my_audiodev]->flags |= DMA_AUTOMODE; - - temp = devc->dma << 4; /* Setup DMA channel select bits */ - if (devc->dma <= 3) - temp |= 0x80; /* 8 bit DMA channel */ - - temp |= 1; /* Trigger DMA */ - sscape_write (devc, GA_DMAA_REG, temp); - temp &= 0xfe; /* Clear DMA trigger */ - sscape_write (devc, GA_DMAA_REG, temp); + audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE; + DMAbuf_start_dma(devc->codec_audiodev, buf, blk_size, mode); + audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE; + + temp = devc->dma << 4; /* Setup DMA channel select bits */ + if (devc->dma <= 3) + temp |= 0x80; /* 8 bit DMA channel */ + + temp |= 1; /* Trigger DMA */ + sscape_write(devc, GA_DMAA_REG, temp); + temp &= 0xfe; /* Clear DMA trigger */ + sscape_write(devc, GA_DMAA_REG, temp); } -static int -verify_mpu (struct sscape_info *devc) +static int verify_mpu(struct sscape_info *devc) { - /* - * The SoundScape board could be in three modes (MPU, 8250 and host). - * If the card is not in the MPU mode, enabling the MPU driver will - * cause infinite loop (the driver believes that there is always some - * received data in the buffer. - * - * Detect this by looking if there are more than 10 received MIDI bytes - * (0x00) in the buffer. - */ - - int i; - - for (i = 0; i < 10; i++) - { - if (inb (devc->base + HOST_CTRL) & 0x80) - return 1; - - if (inb (devc->base) != 0x00) - return 1; - } + /* + * The SoundScape board could be in three modes (MPU, 8250 and host). + * If the card is not in the MPU mode, enabling the MPU driver will + * cause infinite loop (the driver believes that there is always some + * received data in the buffer. + * + * Detect this by looking if there are more than 10 received MIDI bytes + * (0x00) in the buffer. + */ + + int i; + + for (i = 0; i < 10; i++) + { + if (inb(devc->base + HOST_CTRL) & 0x80) + return 1; - printk ("SoundScape: The device is not in the MPU-401 mode\n"); - return 0; + if (inb(devc->base) != 0x00) + return 1; + } + printk(KERN_WARNING "SoundScape: The device is not in the MPU-401 mode\n"); + return 0; } -static int -sscape_coproc_open (void *dev_info, int sub_device) +static int sscape_coproc_open(void *dev_info, int sub_device) { - if (sub_device == COPR_MIDI) - { - set_mt32 (devc, 0); - if (!verify_mpu (devc)) - return -(EIO); - } - - sscape_sleep_flag.flags = WK_NONE; - return 0; + if (sub_device == COPR_MIDI) + { + set_mt32(devc, 0); + if (!verify_mpu(devc)) + return -EIO; + } + return 0; } -static void -sscape_coproc_close (void *dev_info, int sub_device) +static void sscape_coproc_close(void *dev_info, int sub_device) { - struct sscape_info *devc = dev_info; - unsigned long flags; - - save_flags (flags); - cli (); - if (devc->dma_allocated) - { - sscape_write (devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */ - devc->dma_allocated = 0; - } - sscape_sleep_flag.flags = WK_NONE; - restore_flags (flags); - - return; + struct sscape_info *devc = dev_info; + unsigned long flags; + + save_flags(flags); + cli(); + if (devc->dma_allocated) + { + sscape_write(devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */ + devc->dma_allocated = 0; + } + restore_flags(flags); + return; } -static void -sscape_coproc_reset (void *dev_info) +static void sscape_coproc_reset(void *dev_info) { } -static int -sscape_download_boot (struct sscape_info *devc, unsigned char *block, int size, int flag) +static int sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag) { - unsigned long flags; - unsigned char temp; - int done, timeout_val; - static unsigned char codec_dma_bits = 0; - - if (flag & CPF_FIRST) - { - /* - * First block. Have to allocate DMA and to reset the board - * before continuing. - */ - - save_flags (flags); - cli (); - codec_dma_bits = sscape_read (devc, GA_CDCFG_REG); -#if 0 - sscape_write (devc, GA_CDCFG_REG, - codec_dma_bits & ~0x08); /* Disable codec DMA */ -#endif + unsigned long flags; + unsigned char temp; + volatile int done, timeout_val; + static unsigned char codec_dma_bits = 0; - if (devc->dma_allocated == 0) + if (flag & CPF_FIRST) { - devc->dma_allocated = 1; - } - restore_flags (flags); + /* + * First block. Have to allocate DMA and to reset the board + * before continuing. + */ - sscape_write (devc, GA_HMCTL_REG, - (temp = sscape_read (devc, GA_HMCTL_REG)) & 0x3f); /*Reset */ + save_flags(flags); + cli(); + codec_dma_bits = sscape_read(devc, GA_CDCFG_REG); - for (timeout_val = 10000; timeout_val > 0; timeout_val--) - sscape_read (devc, GA_HMCTL_REG); /* Delay */ + if (devc->dma_allocated == 0) + devc->dma_allocated = 1; - /* Take board out of reset */ - sscape_write (devc, GA_HMCTL_REG, - (temp = sscape_read (devc, GA_HMCTL_REG)) | 0x80); - } + restore_flags(flags); - /* - * Transfer one code block using DMA - */ - memcpy (audio_devs[devc->my_audiodev]->dmap_out->raw_buf, block, size); + sscape_write(devc, GA_HMCTL_REG, + (temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f); /*Reset */ - save_flags (flags); - cli (); -/******** INTERRUPTS DISABLED NOW ********/ - do_dma (devc, SSCAPE_DMA_A, - audio_devs[devc->my_audiodev]->dmap_out->raw_buf_phys, - size, DMA_MODE_WRITE); + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + sscape_read(devc, GA_HMCTL_REG); /* Delay */ - /* - * Wait until transfer completes. - */ - sscape_sleep_flag.flags = WK_NONE; - done = 0; - timeout_val = 100; - while (!done && timeout_val-- > 0) - { - int resid; + /* Take board out of reset */ + sscape_write(devc, GA_HMCTL_REG, + (temp = sscape_read(devc, GA_HMCTL_REG)) | 0x80); + } + /* + * Transfer one code block using DMA + */ + if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL) + { + printk(KERN_WARNING "soundscape: DMA buffer not available\n"); + return 0; + } + memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size); + + save_flags(flags); + cli(); + + /******** INTERRUPTS DISABLED NOW ********/ + + do_dma(devc, SSCAPE_DMA_A, + audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys, + size, DMA_MODE_WRITE); + + /* + * Wait until transfer completes. + */ + + done = 0; + timeout_val = 30; + while (!done && timeout_val-- > 0) + { + int resid; + if (HZ / 50) + sleep(HZ / 50); + clear_dma_ff(devc->dma); + if ((resid = get_dma_residue(devc->dma)) == 0) + done = 1; + } - { - unsigned long tlimit; + restore_flags(flags); + if (!done) + return 0; - if (1) - current_set_timeout (tlimit = jiffies + (1)); - else - tlimit = (unsigned long) -1; - sscape_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&sscape_sleeper); - if (!(sscape_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - sscape_sleep_flag.flags |= WK_TIMEOUT; - } - sscape_sleep_flag.flags &= ~WK_SLEEP; - }; - clear_dma_ff (devc->dma); - if ((resid = get_dma_residue (devc->dma)) == 0) - { - done = 1; - } - } - - restore_flags (flags); - if (!done) - return 0; - - if (flag & CPF_LAST) - { - /* - * Take the board out of reset - */ - outb (0x00, PORT (HOST_CTRL)); - outb (0x00, PORT (MIDI_CTRL)); - - temp = sscape_read (devc, GA_HMCTL_REG); - temp |= 0x40; - sscape_write (devc, GA_HMCTL_REG, temp); /* Kickstart the board */ - - /* - * Wait until the ODB wakes up - */ - - save_flags (flags); - cli (); - done = 0; - timeout_val = 5 * HZ; - while (!done && timeout_val-- > 0) - { - - { - unsigned long tlimit; - - if (1) - current_set_timeout (tlimit = jiffies + (1)); - else - tlimit = (unsigned long) -1; - sscape_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&sscape_sleeper); - if (!(sscape_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - sscape_sleep_flag.flags |= WK_TIMEOUT; - } - sscape_sleep_flag.flags &= ~WK_SLEEP; - }; - if (inb (PORT (HOST_DATA)) == 0xff) /* OBP startup acknowledge */ - done = 1; - } - sscape_write (devc, GA_CDCFG_REG, codec_dma_bits); - - restore_flags (flags); - if (!done) - { - printk ("SoundScape: The OBP didn't respond after code download\n"); - return 0; - } - - save_flags (flags); - cli (); - done = 0; - timeout_val = 5 * HZ; - while (!done && timeout_val-- > 0) - { - - { - unsigned long tlimit; - - if (1) - current_set_timeout (tlimit = jiffies + (1)); - else - tlimit = (unsigned long) -1; - sscape_sleep_flag.flags = WK_SLEEP; - module_interruptible_sleep_on (&sscape_sleeper); - if (!(sscape_sleep_flag.flags & WK_WAKEUP)) - { - if (jiffies >= tlimit) - sscape_sleep_flag.flags |= WK_TIMEOUT; - } - sscape_sleep_flag.flags &= ~WK_SLEEP; - }; - if (inb (PORT (HOST_DATA)) == 0xfe) /* Host startup acknowledge */ - done = 1; - } - restore_flags (flags); - if (!done) - { - printk ("SoundScape: OBP Initialization failed.\n"); - return 0; - } - - printk ("SoundScape board of type %d initialized OK\n", - get_board_type (devc)); - - set_control (devc, CTL_MASTER_VOL, 100); - set_control (devc, CTL_SYNTH_VOL, 100); + if (flag & CPF_LAST) + { + /* + * Take the board out of reset + */ + outb((0x00), PORT(HOST_CTRL)); + outb((0x00), PORT(MIDI_CTRL)); + + temp = sscape_read(devc, GA_HMCTL_REG); + temp |= 0x40; + sscape_write(devc, GA_HMCTL_REG, temp); /* Kickstart the board */ + + /* + * Wait until the ODB wakes up + */ + + save_flags(flags); + cli(); + done = 0; + timeout_val = 5 * HZ; + while (!done && timeout_val-- > 0) + { + unsigned char x; + + sleep(1); + x = inb(PORT(HOST_DATA)); + if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */ + { + DDB(printk("Soundscape: Acknowledge = %x\n", x)); + done = 1; + } + } + sscape_write(devc, GA_CDCFG_REG, codec_dma_bits); + + restore_flags(flags); + if (!done) + { + printk(KERN_ERR "soundscape: The OBP didn't respond after code download\n"); + return 0; + } + save_flags(flags); + cli(); + done = 0; + timeout_val = 5 * HZ; + while (!done && timeout_val-- > 0) + { + sleep(1); + if (inb(PORT(HOST_DATA)) == 0xfe) /* Host startup acknowledge */ + done = 1; + } + restore_flags(flags); + if (!done) + { + printk(KERN_ERR "soundscape: OBP Initialization failed.\n"); + return 0; + } + printk(KERN_INFO "SoundScape board initialized OK\n"); + set_control(devc, CTL_MASTER_VOL, 100); + set_control(devc, CTL_SYNTH_VOL, 100); #ifdef SSCAPE_DEBUG3 - /* - * Temporary debugging aid. Print contents of the registers after - * downloading the code. - */ - { - int i; - - for (i = 0; i < 13; i++) - printk ("I%d = %02x (new value)\n", i, sscape_read (devc, i)); - } + /* + * Temporary debugging aid. Print contents of the registers after + * downloading the code. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x (new value)\n", i, sscape_read(devc, i)); + } #endif - } - - return 1; + } + return 1; } -static int -download_boot_block (void *dev_info, copr_buffer * buf) +static int download_boot_block(void *dev_info, copr_buffer * buf) { - if (buf->len <= 0 || buf->len > sizeof (buf->data)) - return -(EINVAL); - - if (!sscape_download_boot (devc, buf->data, buf->len, buf->flags)) - { - printk ("SSCAPE: Unable to load microcode block to the OBP.\n"); - return -(EIO); - } + if (buf->len <= 0 || buf->len > sizeof(buf->data)) + return -EINVAL; - return 0; + if (!sscape_download_boot(devc, buf->data, buf->len, buf->flags)) + { + printk(KERN_ERR "soundscape: Unable to load microcode block to the OBP.\n"); + return -EIO; + } + return 0; } -static int -sscape_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local) +static int sscape_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local) { + copr_buffer *buf; + int err; - switch (cmd) - { - case SNDCTL_COPR_RESET: - sscape_coproc_reset (dev_info); - return 0; - break; - - case SNDCTL_COPR_LOAD: - { - copr_buffer *buf; - int err; - - buf = (copr_buffer *) vmalloc (sizeof (copr_buffer)); - if (buf == NULL) - return -(ENOSPC); - memcpy_fromfs ((char *) buf, &((char *) arg)[0], sizeof (*buf)); - err = download_boot_block (dev_info, buf); - vfree (buf); - return err; - } - break; - - default: - return -(EINVAL); - } - + switch (cmd) + { + case SNDCTL_COPR_RESET: + sscape_coproc_reset(dev_info); + return 0; + + case SNDCTL_COPR_LOAD: + buf = (copr_buffer *) vmalloc(sizeof(copr_buffer)); + if (buf == NULL) + return -ENOSPC; + if (copy_from_user(buf, arg, sizeof(copr_buffer))) + { + vfree(buf); + return -EFAULT; + } + err = download_boot_block(dev_info, buf); + vfree(buf); + return err; + + default: + return -EINVAL; + } } static coproc_operations sscape_coproc_operations = { - "SoundScape M68K", - sscape_coproc_open, - sscape_coproc_close, - sscape_coproc_ioctl, - sscape_coproc_reset, - &dev_info + "SoundScape M68K", + sscape_coproc_open, + sscape_coproc_close, + sscape_coproc_ioctl, + sscape_coproc_reset, + &adev_info }; -static int sscape_detected = 0; +static int sscape_detected = 0; -void -attach_sscape (struct address_info *hw_config) +void attach_sscape(struct address_info *hw_config) { - #ifndef SSCAPE_REGS - /* - * Config register values for Spea/V7 Media FX and Ensoniq S-2000. - * These values are card - * dependent. If you have another SoundScape based card, you have to - * find the correct values. Do the following: - * - Compile this driver with SSCAPE_DEBUG1 defined. - * - Shut down and power off your machine. - * - Boot with DOS so that the SSINIT.EXE program is run. - * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed - * when detecting the SoundScape. - * - Modify the following list to use the values printed during boot. - * Undefine the SSCAPE_DEBUG1 - */ + /* + * Config register values for Spea/V7 Media FX and Ensoniq S-2000. + * These values are card + * dependent. If you have another SoundScape based card, you have to + * find the correct values. Do the following: + * - Compile this driver with SSCAPE_DEBUG1 defined. + * - Shut down and power off your machine. + * - Boot with DOS so that the SSINIT.EXE program is run. + * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed + * when detecting the SoundScape. + * - Modify the following list to use the values printed during boot. + * Undefine the SSCAPE_DEBUG1 + */ #define SSCAPE_REGS { \ /* I0 */ 0x00, \ 0xf0, /* Note! Ignored. Set always to 0xf0 */ \ @@ -707,304 +577,401 @@ attach_sscape (struct address_info *hw_config) } #endif - unsigned long flags; - static unsigned char regs[10] = SSCAPE_REGS; - - int i, irq_bits = 0xff; - - if (sscape_detected != hw_config->io_base) - return; - - if (old_hardware) - { - valid_interrupts = valid_interrupts_old; - conf_printf ("Ensoniq SoundScape (old)", hw_config); - } - else - conf_printf ("Ensoniq SoundScape", hw_config); - - for (i = 0; i < sizeof (valid_interrupts); i++) - if (hw_config->irq == valid_interrupts[i]) - { - irq_bits = i; - break; - } - - if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff)) - { - printk ("Invalid IRQ%d\n", hw_config->irq); - return; - } - - save_flags (flags); - cli (); - - for (i = 1; i < 10; i++) - switch (i) - { - case 1: /* Host interrupt enable */ - sscape_write (devc, i, 0xf0); /* All interrupts enabled */ - break; - - case 2: /* DMA A status/trigger register */ - case 3: /* DMA B status/trigger register */ - sscape_write (devc, i, 0x20); /* DMA channel disabled */ - break; - - case 4: /* Host interrupt config reg */ - sscape_write (devc, i, 0xf0 | (irq_bits << 2) | irq_bits); - break; - - case 5: /* Don't destroy CD-ROM DMA config bits (0xc0) */ - sscape_write (devc, i, (regs[i] & 0x3f) | - (sscape_read (devc, i) & 0xc0)); - break; - - case 6: /* CD-ROM config. Don't touch. */ - break; - - case 9: /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */ - sscape_write (devc, i, - (sscape_read (devc, i) & 0xf0) | 0x08); - break; - - default: - sscape_write (devc, i, regs[i]); - } - - restore_flags (flags); + unsigned long flags; + static unsigned char regs[10] = SSCAPE_REGS; + + int i, irq_bits = 0xff; + + if (sscape_detected != hw_config->io_base) + return; + + request_region(devc->base + 2, 6, "SoundScape"); + if (old_hardware) + { + valid_interrupts = valid_interrupts_old; + conf_printf("Ensoniq SoundScape (old)", hw_config); + } + else + conf_printf("Ensoniq SoundScape", hw_config); + + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (hw_config->irq == valid_interrupts[i]) + { + irq_bits = i; + break; + } + } + if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff)) + { + printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq); + return; + } + save_flags(flags); + cli(); + + for (i = 1; i < 10; i++) + { + switch (i) + { + case 1: /* Host interrupt enable */ + sscape_write(devc, i, 0xf0); /* All interrupts enabled */ + break; + + case 2: /* DMA A status/trigger register */ + case 3: /* DMA B status/trigger register */ + sscape_write(devc, i, 0x20); /* DMA channel disabled */ + break; + + case 4: /* Host interrupt config reg */ + sscape_write(devc, i, 0xf0 | (irq_bits << 2) | irq_bits); + break; + + case 5: /* Don't destroy CD-ROM DMA config bits (0xc0) */ + sscape_write(devc, i, (regs[i] & 0x3f) | (sscape_read(devc, i) & 0xc0)); + break; + + case 6: /* CD-ROM config (WSS codec actually) */ + sscape_write(devc, i, regs[i]); + break; + + case 9: /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */ + sscape_write(devc, i, (sscape_read(devc, i) & 0xf0) | 0x08); + break; + + default: + sscape_write(devc, i, regs[i]); + } + } + restore_flags(flags); #ifdef SSCAPE_DEBUG2 - /* - * Temporary debugging aid. Print contents of the registers after - * changing them. - */ - { - int i; - - for (i = 0; i < 13; i++) - printk ("I%d = %02x (new value)\n", i, sscape_read (devc, i)); - } + /* + * Temporary debugging aid. Print contents of the registers after + * changing them. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x (new value)\n", i, sscape_read(devc, i)); + } #endif #if defined(CONFIG_MIDI) && defined(CONFIG_MPU_EMU) - if (probe_mpu401 (hw_config)) - hw_config->always_detect = 1; - { - int prev_devs; + if (probe_mpu401(hw_config)) + hw_config->always_detect = 1; + hw_config->name = "SoundScape"; - prev_devs = num_midis; - hw_config->name = "SoundScape"; + hw_config->irq *= -1; /* Negative value signals IRQ sharing */ + attach_mpu401(hw_config); + hw_config->irq *= -1; /* Restore it */ - hw_config->irq *= -1; /* Negative value signals IRQ sharing */ - attach_mpu401 (hw_config); - hw_config->irq *= -1; /* Restore it */ - - if (num_midis == (prev_devs + 1)) /* The MPU driver installed itself */ - midi_devs[prev_devs]->coproc = &sscape_coproc_operations; - } + if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ + { + sscape_mididev = hw_config->slots[1]; + midi_devs[hw_config->slots[1]]->coproc = &sscape_coproc_operations; + } #endif - - sscape_write (devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */ - devc->ok = 1; - devc->failed = 0; + sscape_write(devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */ + devc->ok = 1; + devc->failed = 0; } -int -probe_sscape (struct address_info *hw_config) +static int detect_ga(sscape_info * devc) { - unsigned char save; - - devc->failed = 1; - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->dma = hw_config->dma; - devc->osp = hw_config->osp; + unsigned char save; - if (sscape_detected != 0 && sscape_detected != hw_config->io_base) - return 0; + DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base)); - /* - * First check that the address register of "ODIE" is - * there and that it has exactly 4 writable bits. - * First 4 bits - */ - if ((save = inb (PORT (ODIE_ADDR))) & 0xf0) - return 0; + if (check_region(devc->base, 8)) + return 0; - outb (0x00, PORT (ODIE_ADDR)); - if (inb (PORT (ODIE_ADDR)) != 0x00) - return 0; - - outb (0xff, PORT (ODIE_ADDR)); - if (inb (PORT (ODIE_ADDR)) != 0x0f) - return 0; + /* + * First check that the address register of "ODIE" is + * there and that it has exactly 4 writable bits. + * First 4 bits + */ + + if ((save = inb(PORT(ODIE_ADDR))) & 0xf0) + { + DDB(printk("soundscape: Detect error A\n")); + return 0; + } + outb((0x00), PORT(ODIE_ADDR)); + if (inb(PORT(ODIE_ADDR)) != 0x00) + { + DDB(printk("soundscape: Detect error B\n")); + return 0; + } + outb((0xff), PORT(ODIE_ADDR)); + if (inb(PORT(ODIE_ADDR)) != 0x0f) + { + DDB(printk("soundscape: Detect error C\n")); + return 0; + } + outb((save), PORT(ODIE_ADDR)); - outb (save, PORT (ODIE_ADDR)); + /* + * Now verify that some indirect registers return zero on some bits. + * This may break the driver with some future revisions of "ODIE" but... + */ - /* - * Now verify that some indirect registers return zero on some bits. - * This may break the driver with some future revisions of "ODIE" but... - */ + if (sscape_read(devc, 0) & 0x0c) + { + DDB(printk("soundscape: Detect error D (%x)\n", sscape_read(devc, 0))); + return 0; + } + if (sscape_read(devc, 1) & 0x0f) + { + DDB(printk("soundscape: Detect error E\n")); + return 0; + } + if (sscape_read(devc, 5) & 0x0f) + { + DDB(printk("soundscape: Detect error F\n")); + return 0; + } + return 1; +} - if (sscape_read (devc, 0) & 0x0c) - return 0; +int probe_sscape(struct address_info *hw_config) +{ - if (sscape_read (devc, 1) & 0x0f) - return 0; + if (sscape_detected != 0 && sscape_detected != hw_config->io_base) + return 0; - if (sscape_read (devc, 5) & 0x0f) - return 0; + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->dma = hw_config->dma; + devc->osp = hw_config->osp; #ifdef SSCAPE_DEBUG1 - /* - * Temporary debugging aid. Print contents of the registers before - * changing them. - */ - { - int i; - - for (i = 0; i < 13; i++) - printk ("I%d = %02x (old value)\n", i, sscape_read (devc, i)); - } + /* + * Temporary debugging aid. Print contents of the registers before + * changing them. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x (old value)\n", i, sscape_read(devc, i)); + } #endif + devc->failed = 1; - if (old_hardware) /* Check that it's really an old Spea/Reveal card. */ - { - unsigned char tmp; - int cc; + if (!detect_ga(devc)) + return 0; - if (!((tmp = sscape_read (devc, GA_HMCTL_REG)) & 0xc0)) + if (old_hardware) /* Check that it's really an old Spea/Reveal card. */ { - sscape_write (devc, GA_HMCTL_REG, tmp | 0x80); - for (cc = 0; cc < 200000; ++cc) - inb (devc->base + ODIE_ADDR); + unsigned char tmp; + int cc; + + if (!((tmp = sscape_read(devc, GA_HMCTL_REG)) & 0xc0)) + { + sscape_write(devc, GA_HMCTL_REG, tmp | 0x80); + for (cc = 0; cc < 200000; ++cc) + inb(devc->base + ODIE_ADDR); + } } - else - old_hardware = 0; - } - - - sscape_detected = hw_config->io_base; - - return 1; + sscape_detected = hw_config->io_base; + return 1; } -int -probe_ss_ms_sound (struct address_info *hw_config) +int probe_ss_ms_sound(struct address_info *hw_config) { - int i, irq_bits = 0xff; - - if (devc->failed) - return 0; - - if (devc->ok == 0) - { - printk ("SoundScape: Invalid initialization order.\n"); - return 0; - } - - for (i = 0; i < sizeof (valid_interrupts); i++) - if (hw_config->irq == valid_interrupts[i]) - { - irq_bits = i; - break; - } - if (hw_config->irq > 15 || irq_bits == 0xff) - { - printk ("SoundScape: Invalid MSS IRQ%d\n", hw_config->irq); - return 0; - } - - return ad1848_detect (hw_config->io_base, NULL, hw_config->osp); + int i, irq_bits = 0xff; + int ad_flags = 0; + + if (devc->failed) + { + printk(KERN_ERR "soundscape: Card not detected\n"); + return 0; + } + if (devc->ok == 0) + { + printk(KERN_ERR "soundscape: Invalid initialization order.\n"); + return 0; + } + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (hw_config->irq == valid_interrupts[i]) + { + irq_bits = i; + break; + } + } + if (hw_config->irq > 15 || irq_bits == 0xff) + { + printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq); + return 0; + } + if (old_hardware) + ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ + return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); } -void -attach_ss_ms_sound (struct address_info *hw_config) +void attach_ss_ms_sound(struct address_info *hw_config) { - /* - * This routine configures the SoundScape card for use with the - * Win Sound System driver. The AD1848 codec interface uses the CD-ROM - * config registers of the "ODIE". - */ - - int i, irq_bits = 0xff; + /* + * This routine configures the SoundScape card for use with the + * Win Sound System driver. The AD1848 codec interface uses the CD-ROM + * config registers of the "ODIE". + */ - int prev_devs = num_audiodevs; + int i, irq_bits = 0xff; - hw_config->dma = devc->dma; /* Share the DMA with the ODIE/OPUS chip */ + hw_config->dma = devc->dma; /* Share the DMA with the ODIE/OPUS chip */ - /* - * Setup the DMA polarity. - */ - sscape_write (devc, GA_DMACFG_REG, 0x50); + /* + * Setup the DMA polarity. + */ - /* - * Take the gate-array off of the DMA channel. - */ - sscape_write (devc, GA_DMAB_REG, 0x20); + sscape_write(devc, GA_DMACFG_REG, 0x50); - /* - * Init the AD1848 (CD-ROM) config reg. - */ + /* + * Take the gate-array off of the DMA channel. + */ + + sscape_write(devc, GA_DMAB_REG, 0x20); - for (i = 0; i < sizeof (valid_interrupts); i++) - if (hw_config->irq == valid_interrupts[i]) - { - irq_bits = i; - break; - } + /* + * Init the AD1848 (CD-ROM) config reg. + */ - sscape_write (devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | - (irq_bits << 1)); + for (i = 0; i < sizeof(valid_interrupts); i++) + { + if (hw_config->irq == valid_interrupts[i]) + { + irq_bits = i; + break; + } + } + sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1)); - if (hw_config->irq == devc->irq) - printk ("SoundScape: Warning! The WSS mode can't share IRQ with MIDI\n"); + if (hw_config->irq == devc->irq) + printk(KERN_WARNING "soundscape: Warning! The WSS mode can't share IRQ with MIDI\n"); - ad1848_init ("SoundScape", hw_config->io_base, - hw_config->irq, - hw_config->dma, - hw_config->dma, - 0, - devc->osp); + hw_config->slots[0] = ad1848_init("SoundScape", hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, + 0, + devc->osp); - if (num_audiodevs == (prev_devs + 1)) /* The AD1848 driver installed itself */ - audio_devs[prev_devs]->coproc = &sscape_coproc_operations; - devc->my_audiodev = prev_devs; + if (hw_config->slots[0] != -1) /* The AD1848 driver installed itself */ + { + audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations; + devc->codec_audiodev = hw_config->slots[0]; + devc->my_audiodev = hw_config->slots[0]; + /* Set proper routings here (what are they) */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); + } #ifdef SSCAPE_DEBUG5 - /* - * Temporary debugging aid. Print contents of the registers - * after the AD1848 device has been initialized. - */ - { - int i; - - for (i = 0; i < 13; i++) - printk ("I%d = %02x\n", i, sscape_read (devc, i)); - } + /* + * Temporary debugging aid. Print contents of the registers + * after the AD1848 device has been initialized. + */ + { + int i; + + for (i = 0; i < 13; i++) + printk("I%d = %02x\n", i, sscape_read(devc, i)); + } #endif } -void -unload_sscape (struct address_info *hw_config) +void unload_sscape(struct address_info *hw_config) { + release_region(devc->base + 2, 6); #if defined(CONFIG_MPU_EMU) && defined(CONFIG_MIDI) - unload_mpu401 (hw_config); + unload_mpu401(hw_config); #endif - snd_release_irq (hw_config->irq); } -void -unload_ss_ms_sound (struct address_info *hw_config) +void unload_ss_ms_sound(struct address_info *hw_config) { - ad1848_unload (hw_config->io_base, - hw_config->irq, - devc->dma, - devc->dma, - 0); + ad1848_unload(hw_config->io_base, + hw_config->irq, + devc->dma, + devc->dma, + 0); + sound_unload_audiodev(hw_config->slots[0]); } +#ifdef MODULE +int dma = -1; +int irq = -1; +int io = -1; + +int mpu_irq = -1; +int mpu_io = -1; + +int spea = -1; + +static int mss = 0; + +MODULE_PARM(dma, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(io, "i"); +MODULE_PARM(spea, "i"); /* spea=0/1 set the old_hardware */ +MODULE_PARM(mpu_irq, "i"); +MODULE_PARM(mpu_io, "i"); +MODULE_PARM(mss, "i"); + +struct address_info config; +struct address_info mpu_config; + +int init_module(void) +{ + printk(KERN_INFO "Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + if (dma == -1 || irq == -1 || io == -1) + { + printk(KERN_ERR "DMA, IRQ, and IO port must be specified.\n"); + return -EINVAL; + } + if (mpu_irq == -1 && mpu_io != -1) + { + printk(KERN_ERR "CONFIG_MPU_IRQ must be specified if CONFIG_MPU_IO is set.\n"); + return -EINVAL; + } + config.irq = irq; + config.dma = dma; + config.io_base = io; + + mpu_config.irq = mpu_irq; + mpu_config.io_base = mpu_io; + + if(spea != -1) + { + old_hardware = spea; + printk(KERN_INFO "Forcing %s hardware support.\n", + spea?"new":"old"); + } + if (probe_sscape(&mpu_config) == 0) + return -ENODEV; + + attach_sscape(&mpu_config); + + mss = probe_ss_ms_sound(&config); + + if (mss) + attach_ss_ms_sound(&config); + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + if (mss) + unload_ss_ms_sound(&config); + SOUND_LOCK_END; + unload_sscape(&mpu_config); +} + +#endif #endif diff --git a/drivers/sound/sys_timer.c b/drivers/sound/sys_timer.c index b87f0dbbe34d..cd4c20a98fb9 100644 --- a/drivers/sound/sys_timer.c +++ b/drivers/sound/sys_timer.c @@ -5,16 +5,18 @@ * Uses the (1/HZ sec) timer of kernel. */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 -#define SEQUENCER_C #include "sound_config.h" #ifdef CONFIG_SEQUENCER @@ -27,268 +29,261 @@ 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 void poll_def_tmr(unsigned long dummy); static struct timer_list def_tmr = {NULL, NULL, 0, 0, poll_def_tmr}; static unsigned long -tmr2ticks (int tmr_value) +tmr2ticks(int tmr_value) { - /* - * Convert system timer ticks (HZ) to MIDI ticks - * (divide # of MIDI ticks/minute by # of system ticks/minute). - */ + /* + * Convert system timer ticks (HZ) to MIDI ticks + * (divide # of MIDI ticks/minute by # of system ticks/minute). + */ - return ((tmr_value * curr_tempo * curr_timebase) + (30 * 100)) / (60 * HZ); + return ((tmr_value * curr_tempo * curr_timebase) + (30 * 100)) / (60 * HZ); } static void -poll_def_tmr (unsigned long dummy) +poll_def_tmr(unsigned long dummy) { - if (opened) - { - - { - def_tmr.expires = (1) + jiffies; - add_timer (&def_tmr); - }; + if (opened) + { - if (tmr_running) - { - tmr_ctr++; - curr_ticks = ticks_offs + tmr2ticks (tmr_ctr); - - if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer (0); - } - } - } + { + def_tmr.expires = (1) + jiffies; + add_timer(&def_tmr); + }; + + if (tmr_running) + { + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); + + if (curr_ticks >= next_event_time) + { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } + } + } } static void -tmr_reset (void) +tmr_reset(void) { - unsigned long flags; - - save_flags (flags); - cli (); - tmr_offs = 0; - ticks_offs = 0; - tmr_ctr = 0; - next_event_time = (unsigned long) -1; - prev_event_time = 0; - curr_ticks = 0; - restore_flags (flags); + unsigned long flags; + + save_flags(flags); + cli(); + tmr_offs = 0; + ticks_offs = 0; + tmr_ctr = 0; + next_event_time = (unsigned long) -1; + prev_event_time = 0; + curr_ticks = 0; + restore_flags(flags); } static int -def_tmr_open (int dev, int mode) +def_tmr_open(int dev, int mode) { - if (opened) - return -(EBUSY); + if (opened) + return -EBUSY; - tmr_reset (); - curr_tempo = 60; - curr_timebase = 100; - opened = 1; + tmr_reset(); + curr_tempo = 60; + curr_timebase = 100; + opened = 1; - ; + ; - { - def_tmr.expires = (1) + jiffies; - add_timer (&def_tmr); - }; + { + def_tmr.expires = (1) + jiffies; + add_timer(&def_tmr); + }; - return 0; + return 0; } static void -def_tmr_close (int dev) +def_tmr_close(int dev) { - opened = tmr_running = 0; - del_timer (&def_tmr);; + opened = tmr_running = 0; + del_timer(&def_tmr);; } static int -def_tmr_event (int dev, unsigned char *event) +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; + unsigned char cmd = event[1]; + unsigned long parm = *(int *) &event[4]; - case TMR_ECHO: - seq_copy_to_input (event, 8); - break; - - default:; - } + 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; + return TIMER_NOT_ARMED; } static unsigned long -def_tmr_get_time (int dev) +def_tmr_get_time(int dev) { - if (!opened) - return 0; + if (!opened) + return 0; - return curr_ticks; + return curr_ticks; } -static int -def_tmr_ioctl (int dev, - unsigned int cmd, caddr_t arg) +/* same as sound_timer.c:timer_ioctl!? */ +static int def_tmr_ioctl(int dev, unsigned int cmd, caddr_t arg) { - switch (cmd) - { - case SNDCTL_TMR_SOURCE: - return snd_ioctl_return ((int *) arg, TMR_INTERNAL); - break; - - case SNDCTL_TMR_START: - tmr_reset (); - tmr_running = 1; - return 0; - break; - - case SNDCTL_TMR_STOP: - tmr_running = 0; - return 0; - break; - - case SNDCTL_TMR_CONTINUE: - tmr_running = 1; - return 0; - break; - - case SNDCTL_TMR_TIMEBASE: - { - int val = get_user ((int *) arg); - - if (val) - { - if (val < 1) - val = 1; - if (val > 1000) - val = 1000; - curr_timebase = val; - } - - return snd_ioctl_return ((int *) arg, curr_timebase); - } - break; - - case SNDCTL_TMR_TEMPO: - { - int val = get_user ((int *) arg); - - 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; - } - - return snd_ioctl_return ((int *) arg, curr_tempo); - } - break; - - case SNDCTL_SEQ_CTRLRATE: - if (get_user ((int *) arg) != 0) /* Can't change */ - return -(EINVAL); - - return snd_ioctl_return ((int *) arg, ((curr_tempo * curr_timebase) + 30) / 60); - break; + int val; - case SNDCTL_TMR_METRONOME: - /* NOP */ - break; - - default:; - } + switch (cmd) { + case SNDCTL_TMR_SOURCE: + { + int v=TMR_INTERNAL; + return __put_user(v, (int *)arg); + } - return -(EINVAL); + 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, (int *)arg)) + return -EFAULT; + if (val) { + if (val < 1) + val = 1; + if (val > 1000) + val = 1000; + curr_timebase = val; + } + return __put_user((int)curr_timebase, (int *)arg); + + case SNDCTL_TMR_TEMPO: + if (__get_user(val, (int *)arg)) + 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((int)curr_tempo, (int *)arg); + + case SNDCTL_SEQ_CTRLRATE: + if (__get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) /* Can't change */ + return -EINVAL; + val = ((curr_tempo * curr_timebase) + 30) / 60; + return __put_user(val, (int *)arg); + + case SNDCTL_SEQ_GETTIME: + return __put_user(curr_ticks, (int *)arg); + + case SNDCTL_TMR_METRONOME: + /* NOP */ + break; + + default:; + } + return -EINVAL; } static void -def_tmr_arm (int dev, long time) +def_tmr_arm(int dev, long time) { - if (time < 0) - time = curr_ticks + 1; - else if (time <= curr_ticks) /* It's the time */ - return; + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; - next_event_time = prev_event_time = time; + next_event_time = prev_event_time = time; - return; + return; } struct sound_timer_operations default_sound_timer = { - {"System clock", 0}, - 0, /* Priority */ - 0, /* Local device link */ - def_tmr_open, - def_tmr_close, - def_tmr_event, - def_tmr_get_time, - def_tmr_ioctl, - def_tmr_arm + {"System clock", 0}, + 0, /* Priority */ + 0, /* Local device link */ + def_tmr_open, + def_tmr_close, + def_tmr_event, + def_tmr_get_time, + def_tmr_ioctl, + def_tmr_arm }; #endif diff --git a/drivers/sound/trix.c b/drivers/sound/trix.c index 15f36e7e5a87..c1b2e7ec3f69 100644 --- a/drivers/sound/trix.c +++ b/drivers/sound/trix.c @@ -3,132 +3,131 @@ * * Low level driver for the MediaTrix AudioTrix Pro * (MT-0002-PC Control Chip) - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1996 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * + * 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. */ + #include - +#include #include "sound_config.h" +#include "soundmodule.h" #include "sb.h" +#include "sound_firmware.h" -#if defined(CONFIG_TRIX) +#ifdef CONFIG_TRIX #ifdef INCLUDE_TRIX_BOOT #include "trix_boot.h" #else static unsigned char *trix_boot = NULL; -static int trix_boot_len = 0; - +static int trix_boot_len = 0; #endif -static int kilroy_was_here = 0; /* Don't detect twice */ -static int sb_initialized = 0; -static int mpu_initialized = 0; +static int kilroy_was_here = 0; /* Don't detect twice */ +static int sb_initialized = 0; +static int mpu_initialized = 0; -static int *trix_osp = NULL; +static int *trix_osp = NULL; -static unsigned char -trix_read (int addr) +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 */ + 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) +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 */ + 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) +static void download_boot(int base) { - int i = 0, n = trix_boot_len; + int i = 0, n = trix_boot_len; - if (trix_boot_len == 0) - return; + if (trix_boot_len == 0) + return; - trix_write (0xf8, 0x00); /* ??????? */ - outb (0x01, base + 6); /* Clear the internal data pointer */ - outb (0x00, base + 6); /* Restart */ + 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 */ + /* + * 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); /* ?????? */ + 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) +static int trix_set_wss_port(struct address_info *hw_config) { - unsigned char addr_bits; - - if (check_region (0x390, 2)) - { - printk ("AudioTrix: Config port I/O conflict\n"); - return 0; - } - - if (kilroy_was_here) /* Already initialized */ - return 0; - - if (trix_read (0x15) != 0x71) /* No ASIC signature */ - { - DDB (printk ("No AudioTrix ASIC signature found\n")); - return 0; - } - - kilroy_was_here = 1; - - /* - * 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; + unsigned char addr_bits; + + if (check_region(0x390, 2)) + { + printk(KERN_ERR "AudioTrix: Config port I/O conflict\n"); + return 0; + } + if (kilroy_was_here) /* Already initialized */ + return 0; + + if (trix_read(0x15) != 0x71) /* No ASIC signature */ + { + MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n")); + return 0; + } + kilroy_was_here = 1; + + /* + * 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; } /* @@ -136,316 +135,427 @@ trix_set_wss_port (struct address_info *hw_config) * AudioTrix Pro */ -int -probe_trix_wss (struct address_info *hw_config) +int probe_trix_wss(struct address_info *hw_config) { - int ret; - - /* - * Check if the IO port returns valid signature. The original MS Sound - * system returns 0x04 while some cards (AudioTrix Pro for example) - * return 0x00. - */ - if (check_region (hw_config->io_base, 8)) - { - printk ("AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - - trix_osp = hw_config->osp; - - if (!trix_set_wss_port (hw_config)) - return 0; - - if ((inb (hw_config->io_base + 3) & 0x3f) != 0x00) - { - DDB (printk ("No MSS signature detected on port 0x%x\n", hw_config->io_base)); - return 0; - } - - if (hw_config->irq > 11) - { - printk ("AudioTrix: Bad WSS IRQ %d\n", hw_config->irq); - return 0; - } - - if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) - { - printk ("AudioTrix: Bad WSS DMA %d\n", hw_config->dma); - return 0; - } - - if (hw_config->dma2 != -1) - if (hw_config->dma2 != 0 && hw_config->dma2 != 1 && hw_config->dma2 != 3) - { - printk ("AudioTrix: Bad capture DMA %d\n", hw_config->dma2); - return 0; - } - - /* - * Check that DMA0 is not in use with a 8 bit board. - */ - - if (hw_config->dma == 0 && inb (hw_config->io_base + 3) & 0x80) - { - printk ("AudioTrix: Can't use DMA0 with a 8 bit card\n"); - return 0; - } - - if (hw_config->irq > 7 && hw_config->irq != 9 && inb (hw_config->io_base + 3) & 0x80) - { - printk ("AudioTrix: Can't use IRQ%d with a 8 bit card\n", hw_config->irq); - return 0; - } - - ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp); - - if (ret) - request_region (0x390, 2, "AudioTrix"); - - return ret; + int ret; + + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (AudioTrix Pro for example) + * return 0x00. + */ + if (check_region(hw_config->io_base, 8)) + { + printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + trix_osp = hw_config->osp; + + if (!trix_set_wss_port(hw_config)) + return 0; + + if ((inb(hw_config->io_base + 3) & 0x3f) != 0x00) + { + MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base)); + return 0; + } + if (hw_config->irq > 11) + { + printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) + { + printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", hw_config->dma); + return 0; + } + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + if (hw_config->dma2 != 0 && hw_config->dma2 != 1 && hw_config->dma2 != 3) + { + printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", hw_config->dma2); + return 0; + } + /* + * Check that DMA0 is not in use with a 8 bit board. + */ + + if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n"); + return 0; + } + if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + { + printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq); + return 0; + } + ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); + + if (ret) + { +#ifdef TRIX_ENABLE_JOYSTICK + trix_write(0x15, 0x80); +#endif + request_region(0x390, 2, "AudioTrix"); + } + return ret; } void -attach_trix_wss (struct address_info *hw_config) +attach_trix_wss(struct address_info *hw_config) { - static unsigned char interrupt_bits[12] = - {0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x10, 0x18, 0x20}; - char bits; - - static unsigned char dma_bits[4] = - {1, 2, 0, 3}; - - int config_port = hw_config->io_base + 0; - int dma1 = hw_config->dma, dma2 = hw_config->dma2; - - trix_osp = hw_config->osp; - - if (!kilroy_was_here) - { - DDB (printk ("AudioTrix: Attach called but not probed yet???\n")); - return; - } - - /* - * Set the IRQ and DMA addresses. - */ - - bits = interrupt_bits[hw_config->irq]; - if (bits == 0) - { - printk ("AudioTrix: Bad IRQ (%d)\n", hw_config->irq); - return; - } - - outb (bits | 0x40, config_port); - - if (hw_config->dma2 == -1 || hw_config->dma2 == hw_config->dma) - { - 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 */ - - ad1848_init ("AudioTrix Pro", hw_config->io_base + 4, - hw_config->irq, - dma1, - dma2, - 0, - hw_config->osp); - request_region (hw_config->io_base, 4, "MSS config"); + static unsigned char interrupt_bits[12] = { + 0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x10, 0x18, 0x20 + }; + char bits; + + static unsigned char dma_bits[4] = { + 1, 2, 0, 3 + }; + + int config_port = hw_config->io_base + 0; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + int old_num_mixers = num_mixers; + + trix_osp = hw_config->osp; + + if (!kilroy_was_here) + { + DDB(printk("AudioTrix: Attach called but not probed yet???\n")); + return; + } + + /* + * Set the IRQ and DMA addresses. + */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == 0) + { + printk("AudioTrix: Bad IRQ (%d)\n", hw_config->irq); + return; + } + outb((bits | 0x40), config_port); + + if (hw_config->dma2 == -1 || hw_config->dma2 == hw_config->dma) + { + 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", hw_config->io_base + 4, + hw_config->irq, + dma1, + dma2, + 0, + hw_config->osp); + request_region(hw_config->io_base, 4, "MSS config"); + + 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 */ + } } -int -probe_trix_sb (struct address_info *hw_config) +int probe_trix_sb(struct address_info *hw_config) { - int tmp; - unsigned char conf; - static char irq_translate[] = - {-1, -1, -1, 0, 1, 2, -1, 3}; - - if (trix_boot_len == 0) - return 0; /* No boot code -> no fun */ - - if (!kilroy_was_here) - return 0; /* AudioTrix Pro has not been detected earlier */ - - if (sb_initialized) - return 0; - - if (check_region (hw_config->io_base, 16)) - { - printk ("AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - - 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; - - 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); - sb_initialized = 1; - - hw_config->name = "AudioTrix SB"; + int tmp; + unsigned char conf; + static char irq_translate[] = { + -1, -1, -1, 0, 1, 2, -1, 3 + }; + + if (trix_boot_len == 0) + return 0; /* No boot code -> no fun */ + + if (!kilroy_was_here) + return 0; /* AudioTrix Pro has not been detected earlier */ + + if (sb_initialized) + return 0; + + if (check_region(hw_config->io_base, 16)) + { + printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + 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; + + 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); + sb_initialized = 1; + + hw_config->name = "AudioTrix SB"; #ifdef CONFIG_SBDSP - return probe_sb (hw_config); + return sb_dsp_detect(hw_config); #else - return 0; + return 0; #endif } -void -attach_trix_sb (struct address_info *hw_config) +void attach_trix_sb(struct address_info *hw_config) { + extern int sb_be_quiet; + int old_quiet; + #ifdef CONFIG_SBDSP - hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING; - attach_sb_card (hw_config); + 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); + + sb_be_quiet = old_quiet; #endif } -void -attach_trix_mpu (struct address_info *hw_config) +void attach_trix_mpu(struct address_info *hw_config) { #if defined(CONFIG_UART401) && defined(CONFIG_MIDI) - hw_config->name = "AudioTrix Pro"; - attach_uart401 (hw_config); + hw_config->name = "AudioTrix Pro"; + attach_uart401(hw_config); #endif } -int -probe_trix_mpu (struct address_info *hw_config) +int probe_trix_mpu(struct address_info *hw_config) { #if defined(CONFIG_UART401) && defined(CONFIG_MIDI) - unsigned char conf; - static char irq_bits[] = - {-1, -1, -1, 1, 2, 3, -1, 4, -1, 5}; - - if (!kilroy_was_here) - { - DDB (printk ("Trix: WSS and SB modes must be initialized before MPU\n")); - return 0; /* AudioTrix Pro has not been detected earlier */ - } - - if (!sb_initialized) - { - DDB (printk ("Trix: SB mode must be initialized before MPU\n")); - return 0; - } - - if (mpu_initialized) - { - DDB (printk ("Trix: MPU mode already initialized\n")); - return 0; - } - - if (check_region (hw_config->io_base, 4)) - { - printk ("AudioTrix: MPU I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - - if (hw_config->irq > 9) - { - printk ("AudioTrix: Bad MPU IRQ %d\n", hw_config->irq); - return 0; - } - - if (irq_bits[hw_config->irq] == -1) - { - printk ("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); - - mpu_initialized = 1; - - return probe_uart401 (hw_config); + unsigned char conf; + static char irq_bits[] = { + -1, -1, -1, 1, 2, 3, -1, 4, -1, 5 + }; + + if (!kilroy_was_here) + { + DDB(printk("Trix: WSS and SB modes must be initialized before MPU\n")); + return 0; /* AudioTrix Pro has not been detected earlier */ + } + if (!sb_initialized) + { + DDB(printk("Trix: SB mode must be initialized before MPU\n")); + return 0; + } + if (mpu_initialized) + { + DDB(printk("Trix: MPU mode already initialized\n")); + return 0; + } + if (check_region(hw_config->io_base, 4)) + { + printk(KERN_ERR "AudioTrix: MPU I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + 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); + mpu_initialized = 1; + return probe_uart401(hw_config); #else - return 0; + return 0; #endif } -void -unload_trix_wss (struct address_info *hw_config) +void unload_trix_wss(struct address_info *hw_config) { - int dma2 = hw_config->dma2; + int dma2 = hw_config->dma2; - if (dma2 == -1) - dma2 = hw_config->dma; + if (dma2 == -1) + dma2 = hw_config->dma; - release_region (0x390, 2); - release_region (hw_config->io_base, 4); + 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); + ad1848_unload(hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + dma2, + 0); + sound_unload_audiodev(hw_config->slots[0]); } -void -unload_trix_mpu (struct address_info *hw_config) +void unload_trix_mpu(struct address_info *hw_config) { #if defined(CONFIG_UART401) && defined(CONFIG_MIDI) - unload_uart401 (hw_config); + unload_uart401(hw_config); #endif } -void -unload_trix_sb (struct address_info *hw_config) + +void unload_trix_sb(struct address_info *hw_config) { #ifdef CONFIG_SBDSP - unload_sb (hw_config); + sb_dsp_unload(hw_config); #endif } +#ifdef MODULE + +int io = -1; +int irq = -1; +int dma = -1; +int dma2 = -1; /* Set this for modules that need it */ + +int sb_io = -1; +int sb_dma = -1; +int sb_irq = -1; + +int mpu_io = -1; +int mpu_irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(dma2,"i"); +MODULE_PARM(sb_io,"i"); +MODULE_PARM(sb_dma,"i"); +MODULE_PARM(sb_irq,"i"); +MODULE_PARM(mpu_io,"i"); +MODULE_PARM(mpu_irq,"i"); + +struct address_info config; +struct address_info sb_config; +struct address_info mpu_config; + +static int mpu = 0; +static int sb = 0; + +static int fw_load; + +int init_module(void) +{ + printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + if (io == -1 || dma == -1 || irq == -1) + { + printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); + return -EINVAL; + } + config.io_base = io; + config.irq = irq; + config.dma = dma; + config.dma2 = dma2; + + sb_config.io_base = sb_io; + sb_config.irq = sb_irq; + sb_config.dma = sb_dma; + + mpu_config.io_base = mpu_io; + mpu_config.irq = mpu_irq; + + if (sb_io != -1 && (sb_irq == -1 || sb_dma == -1)) + { + printk(KERN_INFO "CONFIG_SB_IRQ and CONFIG_SB_DMA must be specified if SB_IO is set.\n"); + return -EINVAL; + } + if (mpu_io != -1 && 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 (!probe_trix_wss(&config)) + return -ENODEV; + attach_trix_wss(&config); + + /* + * We must attach in the right order to get the firmware + * loaded up in time. + */ + + if (sb_io != -1) + { + sb = probe_trix_sb(&sb_config); + if (sb) + attach_trix_sb(&sb_config); + } + + if (mpu_io != -1) + { + mpu = probe_trix_mpu(&mpu_config); + if (mpu) + attach_trix_mpu(&mpu_config); + } + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + if (fw_load && trix_boot) + vfree(trix_boot); + if (sb) + unload_trix_sb(&sb_config); + if (mpu) + unload_trix_mpu(&mpu_config); + unload_trix_wss(&config); + SOUND_LOCK_END; +} #endif +#endif diff --git a/drivers/sound/uart401.c b/drivers/sound/uart401.c index 443c46adb470..a1c6fc453843 100644 --- a/drivers/sound/uart401.c +++ b/drivers/sound/uart401.c @@ -4,61 +4,70 @@ * MPU-401 UART driver (formerly uart401_midi.c) */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 + * + * Status: + * Untested */ + #include - +#include #include "sound_config.h" +#include "soundmodule.h" -#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) +#ifdef CONFIG_UART401 +#ifdef CONFIG_MIDI typedef struct uart401_devc - { - int base; - int irq; - int *osp; - void (*midi_input_intr) (int dev, unsigned char data); - int opened; - volatile unsigned char input_byte; - int my_dev; - int share_irq; - } +{ + 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; +} uart401_devc; static uart401_devc *detected_devc = NULL; -static uart401_devc *irq2devc[16] = -{NULL}; #define DATAPORT (devc->base) #define COMDPORT (devc->base+1) #define STATPORT (devc->base+1) -static int -uart401_status (uart401_devc * devc) +static int uart401_status(uart401_devc * devc) { - return inb (STATPORT); + 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 (uart401_devc * devc, unsigned char cmd) + +static void uart401_cmd(uart401_devc * devc, unsigned char cmd) { - outb (cmd, COMDPORT); + outb((cmd), COMDPORT); } -static int -uart401_read (uart401_devc * devc) + +static int uart401_read(uart401_devc * devc) { - return inb (DATAPORT); + return inb(DATAPORT); } -static void -uart401_write (uart401_devc * devc, unsigned char byte) + +static void uart401_write(uart401_devc * devc, unsigned char byte) { - outb (byte, DATAPORT); + outb((byte), DATAPORT); } #define OUTPUT_READY 0x40 @@ -67,132 +76,128 @@ uart401_write (uart401_devc * devc, unsigned char byte) #define MPU_RESET 0xFF #define UART_MODE_ON 0x3F -static int reset_uart401 (uart401_devc * devc); +static int reset_uart401(uart401_devc * devc); +static void enter_uart_mode(uart401_devc * devc); -static void -uart401_input_loop (uart401_devc * devc) +static void uart401_input_loop(uart401_devc * devc) { - while (input_avail (devc)) - { - 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); - } + 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); } -void -uart401intr (int irq, void *dev_id, struct pt_regs *dummy) +void uart401intr(int irq, void *dev_id, struct pt_regs *dummy) { - uart401_devc *devc; - - if (irq < 1 || irq > 15) - return; + uart401_devc *devc = dev_id; - devc = irq2devc[irq]; + if (devc == NULL) + { + printk(KERN_ERR "uart401: bad devc\n"); + return; + } - if (devc == NULL) - return; - - if (input_avail (devc)) - uart401_input_loop (devc); + if (input_avail(devc)) + uart401_input_loop(devc); } static int -uart401_open (int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) +uart401_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) ) { - uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; - - if (devc->opened) - { - return -(EBUSY); - } + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; - while (input_avail (devc)) - uart401_read (devc); + if (devc->opened) + return -EBUSY; - devc->midi_input_intr = input; - devc->opened = mode; + /* Flush the UART */ + + while (input_avail(devc)) + uart401_read(devc); - return 0; -} - -static void -uart401_close (int dev) -{ - uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + devc->midi_input_intr = input; + devc->opened = mode; + enter_uart_mode(devc); + devc->disabled = 0; - devc->opened = 0; + return 0; } -static int -uart401_out (int dev, unsigned char midi_byte) +static void uart401_close(int dev) { - int timeout; - unsigned long flags; - uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; - - /* - * Test for input since pending input seems to block the output. - */ - - save_flags (flags); - cli (); - - if (input_avail (devc)) - uart401_input_loop (devc); - - restore_flags (flags); + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; - /* - * 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 ("MPU-401: Timeout\n"); - return 0; - } - - uart401_write (devc, midi_byte); - return 1; + reset_uart401(devc); + devc->opened = 0; } -static int -uart401_start_read (int dev) +static int uart401_out(int dev, unsigned char midi_byte) { - return 0; + int timeout; + unsigned long flags; + uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc; + + if (devc->disabled) + return 1; + /* + * Test for input since pending input seems to block the output. + */ + + save_flags(flags); + cli(); + + if (input_avail(devc)) + uart401_input_loop(devc); + + restore_flags(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 int -uart401_end_read (int dev) +static int uart401_start_read(int dev) { - return 0; + return 0; } -static int -uart401_ioctl (int dev, unsigned cmd, caddr_t arg) +static int uart401_end_read(int dev) { - return -(EINVAL); + return 0; } -static void -uart401_kick (int dev) +static void uart401_kick(int dev) { } -static int -uart401_buffer_status (int dev) +static int uart401_buffer_status(int dev) { - return 0; + return 0; } #define MIDI_SYNTH_NAME "MPU-401 UART" @@ -201,239 +206,296 @@ uart401_buffer_status (int dev) static struct midi_operations uart401_operations = { - {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401}, - &std_midi_synth, - {0}, - uart401_open, - uart401_close, - uart401_ioctl, - uart401_out, - uart401_start_read, - uart401_end_read, - uart401_kick, - NULL, - uart401_buffer_status, - NULL + { + "MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401 + }, + &std_midi_synth, + {0}, + uart401_open, + uart401_close, + NULL, /* ioctl */ + uart401_out, + uart401_start_read, + uart401_end_read, + uart401_kick, + NULL, + uart401_buffer_status, + NULL }; -static void -enter_uart_mode (uart401_devc * devc) +static void enter_uart_mode(uart401_devc * devc) { - int ok, timeout; - unsigned long flags; + int ok, timeout; + unsigned long flags; - save_flags (flags); - cli (); - for (timeout = 30000; timeout < 0 && !output_ready (devc); timeout--); + save_flags(flags); + cli(); + for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - devc->input_byte = 0; - uart401_cmd (devc, UART_MODE_ON); + 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; + 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; - restore_flags (flags); + restore_flags(flags); } -void -attach_uart401 (struct address_info *hw_config) +void attach_uart401(struct address_info *hw_config) { - uart401_devc *devc; - char *name = "MPU-401 (UART) MIDI"; - - if (hw_config->name) - name = hw_config->name; - - if (detected_devc == NULL) - return; - - - devc = (uart401_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (uart401_devc))); - if (sound_nblocks < 1024) - sound_nblocks++;; - if (devc == NULL) - { - printk ("uart401: Can't allocate memory\n"); - return; - } - - memcpy ((char *) devc, (char *) detected_devc, sizeof (uart401_devc)); - detected_devc = NULL; - - devc->irq = hw_config->irq; - if (devc->irq < 0) - { - devc->share_irq = 1; - devc->irq *= -1; - } - else - devc->share_irq = 0; - - if (devc->irq < 1 || devc->irq > 15) - return; - - if (!devc->share_irq) - if (snd_set_irq_handler (devc->irq, uart401intr, "uart401", devc->osp) < 0) - { - printk ("uart401: Failed to allocate IRQ%d\n", devc->irq); - return; - } - - irq2devc[devc->irq] = devc; - devc->my_dev = num_midis; - - request_region (hw_config->io_base, 4, "SB MIDI"); - enter_uart_mode (devc); - - if (num_midis >= MAX_MIDI_DEV) - { - printk ("Sound: Too many midi devices detected\n"); - return; - } - - conf_printf (name, hw_config); - - std_midi_synth.midi_dev = devc->my_dev = num_midis; - - - midi_devs[num_midis] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations))); - - if (sound_nblocks < 1024) - sound_nblocks++;; - if (midi_devs[num_midis] == NULL) - { - printk ("uart401: Failed to allocate memory\n"); - return; - } - - memcpy ((char *) midi_devs[num_midis], (char *) &uart401_operations, - sizeof (struct midi_operations)); - - midi_devs[num_midis]->devc = devc; - - - midi_devs[num_midis]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations))); - - if (sound_nblocks < 1024) - sound_nblocks++;; - - if (midi_devs[num_midis]->converter == NULL) - { - printk ("uart401: Failed to allocate memory\n"); - return; - } - - memcpy ((char *) midi_devs[num_midis]->converter, (char *) &std_midi_synth, - sizeof (struct synth_operations)); - - strcpy (midi_devs[num_midis]->info.name, name); - num_midis++; - devc->opened = 0; + uart401_devc *devc; + char *name = "MPU-401 (UART) MIDI"; + + + if (hw_config->name) + name = hw_config->name; + + if (detected_devc == NULL) + return; + + + devc = (uart401_devc *) kmalloc(sizeof(uart401_devc), GFP_KERNEL); + if (devc == NULL) + { + printk(KERN_WARNING "uart401: Can't allocate memory\n"); + return; + } + memcpy((char *) devc, (char *) detected_devc, sizeof(uart401_devc)); + detected_devc = NULL; + + devc->irq = hw_config->irq; + 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(); + + request_region(hw_config->io_base, 4, "MPU-401 UART"); + enter_uart_mode(devc); + + if (devc->my_dev == -1) + { + printk(KERN_INFO "uart401: Too many midi devices detected\n"); + kfree(devc); + return; + } + conf_printf(name, hw_config); + + std_midi_synth.midi_dev = devc->my_dev; + midi_devs[devc->my_dev] = (struct midi_operations *)kmalloc(sizeof(struct midi_operations), GFP_KERNEL); + if (midi_devs[devc->my_dev] == NULL) + { + printk(KERN_ERR "uart401: Failed to allocate memory\n"); + sound_unload_mididev(devc->my_dev); + kfree(devc); + devc=NULL; + return; + } + memcpy((char *) midi_devs[devc->my_dev], (char *) &uart401_operations, + sizeof(struct midi_operations)); + + midi_devs[devc->my_dev]->devc = devc; + midi_devs[devc->my_dev]->converter = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL); + if (midi_devs[devc->my_dev]->converter == NULL) + { + printk(KERN_WARNING "uart401: Failed to allocate memory\n"); + kfree(midi_devs[devc->my_dev]); + kfree(devc); + sound_unload_mididev(devc->my_dev); + devc=NULL; + return; + } + memcpy((char *) midi_devs[devc->my_dev]->converter, (char *) &std_midi_synth, + sizeof(struct synth_operations)); + + strcpy(midi_devs[devc->my_dev]->info.name, name); + midi_devs[devc->my_dev]->converter->id = "UART401"; + hw_config->slots[4] = devc->my_dev; + sequencer_init(); + devc->opened = 0; } -static int -reset_uart401 (uart401_devc * devc) +static int reset_uart401(uart401_devc * devc) { - int ok, timeout, n; - - /* - * Send the RESET command. Try again if no success at the first time. - */ - - ok = 0; - - /* save_flags(flags);cli(); */ - - 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; - - } - - if (ok) - uart401_input_loop (devc); /* - * Flush input before enabling interrupts - */ - - /* restore_flags(flags); */ - - return ok; + 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; + } + } + } + + + if (ok) + { + DEB(printk("Reset UART401 OK\n")); + } + else + DDB(printk("Reset UART401 failed - No hardware detected.\n")); + + if (ok) + uart401_input_loop(devc); /* + * Flush input before enabling interrupts + */ + + return ok; } -int -probe_uart401 (struct address_info *hw_config) +int probe_uart401(struct address_info *hw_config) { - int ok = 0; - - static uart401_devc hw_info; - uart401_devc *devc = &hw_info; - - detected_devc = NULL; - - if (check_region (hw_config->io_base, 4)) - return 0; - - 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; - - ok = reset_uart401 (devc); - - if (ok) - detected_devc = devc; - - return ok; + int ok = 0; + unsigned long flags; + static uart401_devc hw_info; + uart401_devc *devc = &hw_info; + + DDB(printk("Entered probe_uart401()\n")); + + /* Default to "not found" */ + hw_config->slots[4] = -1; + detected_devc = NULL; + + if (check_region(hw_config->io_base, 4)) + return 0; + + 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; + + save_flags(flags); + cli(); + ok = reset_uart401(devc); + restore_flags(flags); + + if (ok) + detected_devc = devc; + + return ok; } -void -unload_uart401 (struct address_info *hw_config) +void unload_uart401(struct address_info *hw_config) { - uart401_devc *devc; + 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]); +} - int irq = hw_config->irq; +struct symbol_table uart401_syms= +{ +#include + X(attach_uart401), + X(probe_uart401), + X(unload_uart401), + X(uart401intr), +#include +}; - if (irq < 0) - irq *= -1; +#ifdef MODULE - if (irq < 1 || irq > 15) - return; - devc = irq2devc[irq]; - if (devc == NULL) - return; +int io = -1; +int irq = -1; - reset_uart401 (devc); +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +struct address_info hw; - release_region (hw_config->io_base, 4); +int init_module(void) +{ + /* Can be loaded either for module use or to provide functions + to others */ + if (io != -1 && irq != -1) + { + printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997"); + hw.irq = irq; + hw.io_base = io; + if (probe_uart401(&hw) == 0) + return -ENODEV; + attach_uart401(&hw); + } + register_symtab(&uart401_syms); + SOUND_LOCK; + return 0; +} - if (!devc->share_irq) - snd_release_irq (devc->irq); +void cleanup_module(void) +{ + if (io != -1 && irq != -1) + unload_uart401(&hw); + /* FREE SYMTAB */ + SOUND_LOCK_END; } +#endif + #endif +#endif diff --git a/drivers/sound/uart6850.c b/drivers/sound/uart6850.c index bea0738a78e1..f3b0cdde4e42 100644 --- a/drivers/sound/uart6850.c +++ b/drivers/sound/uart6850.c @@ -2,13 +2,22 @@ * sound/uart6850.c */ /* - * Copyright (C) by Hannu Savolainen 1993-1996 + * Copyright (C) by Hannu Savolainen 1993-1997 * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * 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 + * + * Status: Testing required + * */ #include +#include /* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl: * added 6850 support, used with COVOX SoundMaster II and custom cards. @@ -16,37 +25,39 @@ #include "sound_config.h" -#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) +#ifdef CONFIG_SOUND_UART6850 +#ifdef CONFIG_MIDI +#include "soundmodule.h" -static int uart6850_base = 0x330; +static int uart6850_base = 0x330; -static int *uart6850_osp; +static int *uart6850_osp; #define DATAPORT (uart6850_base) #define COMDPORT (uart6850_base+1) #define STATPORT (uart6850_base+1) -static int -uart6850_status (void) +static int uart6850_status(void) { - return inb (STATPORT); + return inb(STATPORT); } + #define input_avail() (uart6850_status()&INPUT_AVAIL) #define output_ready() (uart6850_status()&OUTPUT_READY) -static void -uart6850_cmd (unsigned char cmd) + +static void uart6850_cmd(unsigned char cmd) { - outb (cmd, COMDPORT); + outb(cmd, COMDPORT); } -static int -uart6850_read (void) + +static int uart6850_read(void) { - return inb (DATAPORT); + return inb(DATAPORT); } -static void -uart6850_write (unsigned char byte) + +static void uart6850_write(unsigned char byte) { - outb (byte, DATAPORT); + outb(byte, DATAPORT); } #define OUTPUT_READY 0x02 /* Mask for data ready Bit */ @@ -55,185 +66,165 @@ uart6850_write (unsigned char byte) #define UART_RESET 0x95 #define UART_MODE_ON 0x03 -static int uart6850_opened = 0; -static int uart6850_irq; -static int uart6850_detected = 0; -static int my_dev; +static int uart6850_opened = 0; +static int uart6850_irq; +static int uart6850_detected = 0; +static int my_dev; -static int reset_uart6850 (void); -static void (*midi_input_intr) (int dev, unsigned char data); -static void poll_uart6850 (unsigned long dummy); +static int reset_uart6850(void); +static void (*midi_input_intr) (int dev, unsigned char data); +static void poll_uart6850(unsigned long dummy); -static struct timer_list uart6850_timer = -{NULL, NULL, 0, 0, poll_uart6850}; +static struct timer_list uart6850_timer = { + NULL, NULL, 0, 0, poll_uart6850 +}; -static void -uart6850_input_loop (void) +static void uart6850_input_loop(void) { - int count; - - 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--; + 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--; + } + } } -void -m6850intr (int irq, void *dev_id, struct pt_regs *dummy) +void m6850intr(int irq, void *dev_id, struct pt_regs *dummy) { - if (input_avail ()) - uart6850_input_loop (); + if (input_avail()) + uart6850_input_loop(); } /* - * It looks like there is no input interrupts in the UART mode. Let's try - * polling. + * It looks like there is no input interrupts in the UART mode. Let's try + * polling. */ -static void -poll_uart6850 (unsigned long dummy) +static void poll_uart6850(unsigned long dummy) { - unsigned long flags; + unsigned long flags; - if (!(uart6850_opened & OPEN_READ)) - return; /* Device has been closed */ + if (!(uart6850_opened & OPEN_READ)) + return; /* Device has been closed */ - save_flags (flags); - cli (); + save_flags(flags); + cli(); - if (input_avail ()) - uart6850_input_loop (); + if (input_avail()) + uart6850_input_loop(); + uart6850_timer.expires = 1 + jiffies; + add_timer(&uart6850_timer); + + /* + * Come back later + */ - { - uart6850_timer.expires = (1) + jiffies; - add_timer (&uart6850_timer); - }; /* - * Come back later - */ - - restore_flags (flags); + restore_flags(flags); } -static int -uart6850_open (int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) +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); /* + if (uart6850_opened) + { +/* printk("Midi6850: Midi busy\n");*/ + return -EBUSY; + }; + + MOD_INC_USE_COUNT; + uart6850_cmd(UART_RESET); + uart6850_input_loop(); + midi_input_intr = input; + uart6850_opened = mode; + poll_uart6850(0); /* * Enable input polling */ - return 0; + return 0; } -static void -uart6850_close (int dev) +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. - */ - - save_flags (flags); - cli (); - - if (input_avail ()) - uart6850_input_loop (); - - restore_flags (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 ("Midi6850: Timeout\n"); - return 0; - } - - uart6850_write (midi_byte); - return 1; + uart6850_cmd(UART_MODE_ON); + del_timer(&uart6850_timer); + uart6850_opened = 0; + MOD_DEC_USE_COUNT; } -static int -uart6850_command (int dev, unsigned char *midi_byte) +static int uart6850_out(int dev, unsigned char midi_byte) { - return 1; + int timeout; + unsigned long flags; + + /* + * Test for input since pending input seems to block the output. + */ + + save_flags(flags); + cli(); + + if (input_avail()) + uart6850_input_loop(); + + restore_flags(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 int -uart6850_start_read (int dev) +static int uart6850_command(int dev, unsigned char *midi_byte) { - return 0; + return 1; } -static int -uart6850_end_read (int dev) +static int uart6850_start_read(int dev) { - return 0; + return 0; } -static int -uart6850_ioctl (int dev, unsigned cmd, caddr_t arg) +static int uart6850_end_read(int dev) { - return -(EINVAL); + return 0; } -static void -uart6850_kick (int dev) +static void uart6850_kick(int dev) { } -static int -uart6850_buffer_status (int dev) +static int uart6850_buffer_status(int dev) { - return 0; /* + return 0; /* * No data in buffers */ } @@ -244,90 +235,122 @@ uart6850_buffer_status (int dev) static struct midi_operations uart6850_operations = { - {"6850 UART", 0, 0, SNDCARD_UART6850}, - &std_midi_synth, - {0}, - uart6850_open, - uart6850_close, - uart6850_ioctl, - uart6850_out, - uart6850_start_read, - uart6850_end_read, - uart6850_kick, - uart6850_command, - uart6850_buffer_status + {"6850 UART", 0, 0, SNDCARD_UART6850}, + &std_midi_synth, + {0}, + uart6850_open, + uart6850_close, + NULL, /* ioctl */ + uart6850_out, + uart6850_start_read, + uart6850_end_read, + uart6850_kick, + uart6850_command, + uart6850_buffer_status }; -void -attach_uart6850 (struct address_info *hw_config) +void attach_uart6850(struct address_info *hw_config) { - int ok, timeout; - unsigned long flags; - - if (num_midis >= MAX_MIDI_DEV) - { - printk ("Sound: Too many midi devices detected\n"); - return; - } - - uart6850_base = hw_config->io_base; - uart6850_osp = hw_config->osp; - uart6850_irq = hw_config->irq; - - if (!uart6850_detected) - return; + int ok, timeout; + unsigned long flags; + + 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; + + if (!uart6850_detected) + { + sound_unload_mididev(my_dev); + return; + } + save_flags(flags); + cli(); + + for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /* + * Wait + */ + uart6850_cmd(UART_MODE_ON); + ok = 1; + restore_flags(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(); +} - save_flags (flags); - cli (); +static int reset_uart6850(void) +{ + uart6850_read(); + return 1; /* + * OK + */ +} - for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* - * Wait - */ - uart6850_cmd (UART_MODE_ON); - ok = 1; +int probe_uart6850(struct address_info *hw_config) +{ + int ok = 0; - restore_flags (flags); + uart6850_osp = hw_config->osp; + uart6850_base = hw_config->io_base; + uart6850_irq = hw_config->irq; - conf_printf ("6850 Midi Interface", hw_config); + if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0) + return 0; - std_midi_synth.midi_dev = my_dev = num_midis; - midi_devs[num_midis++] = &uart6850_operations; + ok = reset_uart6850(); + uart6850_detected = ok; + return ok; } -static int -reset_uart6850 (void) +void unload_uart6850(struct address_info *hw_config) { - uart6850_read (); - return 1; /* - * OK - */ + free_irq(hw_config->irq, NULL); + sound_unload_mididev(hw_config->slots[4]); } -int -probe_uart6850 (struct address_info *hw_config) -{ - int ok = 0; +#ifdef MODULE - uart6850_osp = hw_config->osp; - uart6850_base = hw_config->io_base; - uart6850_irq = hw_config->irq; +int io = -1; +int irq = -1; - if (snd_set_irq_handler (uart6850_irq, m6850intr, "MIDI6850", uart6850_osp) < 0) - return 0; +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); - ok = reset_uart6850 (); +struct address_info cfg; - uart6850_detected = ok; - return ok; +int init_module(void) +{ + if (io == -1 || irq == -1) + { + printk(KERN_INFO "uart6850: irq and io must be set.\n"); + return -EINVAL; + } + cfg.io_base = io; + cfg.irq = irq; + + if (probe_uart6850(&cfg)) + return -ENODEV; + + SOUND_LOCK; + return 0; } -void -unload_uart6850 (struct address_info *hw_config) +void cleanup_module(void) { - snd_release_irq (hw_config->irq); + unload_uart6850(&cfg); + SOUND_LOCK_END; } - +#endif +#endif #endif diff --git a/drivers/sound/v_midi.c b/drivers/sound/v_midi.c new file mode 100644 index 000000000000..c1d695c99aba --- /dev/null +++ b/drivers/sound/v_midi.c @@ -0,0 +1,301 @@ +/* + * sound/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 + * + * Status + * Untested + */ + +#include +#include + +#include "sound_config.h" +#include "soundmodule.h" + +#ifdef CONFIG_VMIDI + +#include "v_midi.h" + +static vmidi_devc *v_devc[2] = { NULL, NULL}; +static int midi1,midi2; +static void *midi_mem = NULL; + +#ifdef MODULE + +static struct address_info config; /* dummy */ + +int init_module(void) +{ + printk("MIDI Loopback device driver\n"); + if (!probe_v_midi(&config)) + return -ENODEV; + attach_v_midi(&config); + SOUND_LOCK; + return 0; +} + +void cleanup_module(void) +{ + unload_v_midi(&config); + SOUND_LOCK_END; +} + +#endif + +/* + * 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. + */ + + +void (*midi_input_intr) (int dev, unsigned char data); + +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); + + save_flags (flags); + cli(); + if (devc->opened) + { + restore_flags (flags); + return -(EBUSY); + } + devc->opened = 1; + restore_flags (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; + + save_flags (flags); + cli (); + devc->intr_active = 0; + devc->input_opened = 0; + devc->opened = 0; + restore_flags (flags); +} + +static int v_midi_out (int dev, unsigned char midi_byte) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + vmidi_devc *pdevc = midi_devs[devc->pair_mididev]->devc; + + if (devc == NULL) + return -(ENXIO); + + 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 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 int v_midi_ioctl (int dev, unsigned cmd, caddr_t 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 = +{ + {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI}, + &std_midi_synth, + {0}, + v_midi_open, + v_midi_close, + v_midi_ioctl, + v_midi_out, + v_midi_start_read, + v_midi_end_read, + NULL, + NULL, + NULL, + NULL +}; + +static struct midi_operations v_midi_operations2 = +{ + {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI}, + &std_midi_synth, + {0}, + v_midi_open, + v_midi_close, + v_midi_ioctl, + v_midi_out, + v_midi_start_read, + v_midi_end_read, + NULL, + NULL, + NULL, + NULL +}; + +/* + * 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]; +}; + +void 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=(struct vmidi_memory *)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; + + 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; + + 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"); */ +} + +int probe_v_midi(struct address_info *hw_config) +{ + return(1); /* always OK */ +} + + +void unload_v_midi(struct address_info *hw_config) +{ + sound_unload_mididev(midi1); + sound_unload_mididev(midi2); + kfree(midi_mem); +} + +#endif diff --git a/drivers/sound/v_midi.h b/drivers/sound/v_midi.h new file mode 100644 index 000000000000..ae759dc6b732 --- /dev/null +++ b/drivers/sound/v_midi.h @@ -0,0 +1,15 @@ +typedef struct vmidi_devc { + int dev; + + /* State variables */ + int opened; + + + /* 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; + diff --git a/drivers/sound/wavfront.c b/drivers/sound/wavfront.c new file mode 100644 index 000000000000..fe8b784cf857 --- /dev/null +++ b/drivers/sound/wavfront.c @@ -0,0 +1,3617 @@ +/* -*- linux-c -*- + * + * sound/wavfront.c + * + * A Linux driver for Turtle Beach WaveFront Series (Maui, Tropez, Tropez Plus) + * + * This driver supports the onboard wavetable synthesizer (an ICS2115), + * including patch, sample and program loading and unloading, conversion + * of GUS patches during loading, and full user-level access to all + * WaveFront commands. It tries to provide semi-intelligent patch and + * sample management as well. + * + * It also provides support for the ICS emulation of an MPU-401. Full + * support for the ICS emulation's "virtual MIDI mode" is provided in + * wf_midi.c. + * + * Support is also provided for the Tropez Plus' onboard FX processor, + * a Yamaha YSS225. Currently, code exists to configure the YSS225, + * and there is an interface allowing tweaking of any of its memory + * addresses. However, I have been unable to decipher the logical + * positioning of the configuration info for various effects, so for + * now, you just get the YSS225 in the same state as Turtle Beach's + * "SETUPSND.EXE" utility leaves it. + * + * The boards' CODEC (a Crystal CS4232) is supported by cs4232.[co], + * This chip also controls the configuration of the card: the wavefront + * synth is logical unit 4. + * + ********************************************************************** + * + * Copyright (C) by Paul Barton-Davis 1998 + * + * Some portions of this file are taken from work that is + * copyright (C) by Hannu Savolainen 1993-1996 + * + * Although the relevant code here is all new, the handling of + * sample/alias/multi- samples is entirely based on a driver by Matt + * Martin and Rutger Nijlunsing which demonstrated how to get things + * to most aspects of this to work correctly. The GUS patch loading + * code has been almost unaltered by me, except to fit formatting and + * function names in the rest of the file. Many thanks to them. + * + * Appreciation and thanks to Hannu Savolainen for his early work on the Maui + * driver, and answering a few questions while this one was developed. + * + * Absolutely NO thanks to Turtle Beach/Voyetra and Yamaha for their + * complete lack of help in developing this driver, and in particular + * for their utter silence in response to questions about undocumented + * aspects of configuring a WaveFront soundcard, particularly the + * effects processor. + * + * $Id: wavfront.c,v 0.4 1998/07/22 02:12:11 pbd Exp $ + * + * This program 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 +#include +#include + +#include "sound_config.h" +#include "soundmodule.h" + +#include + +#define MIDI_SYNTH_NAME "WaveFront MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +/* This thing is meant to work as a module */ + +#if defined(CONFIG_SOUND_WAVEFRONT_MODULE) && defined(MODULE) + +/* if WF_DEBUG not defined, no run-time debugging messages will + be available via the debug flag setting. Given the current + beta state of the driver, this will remain set until a future + version. +*/ + +#define WF_DEBUG 1 + +/* bitmasks for WaveFront status port value */ + +#define STAT_INTR_WRITE 0x40 +#define STAT_CAN_WRITE 0x20 +#define STAT_WINTR_ENABLED 0x10 +#define STAT_INTR_READ 0x04 +#define STAT_CAN_READ 0x02 +#define STAT_RINTR_ENABLED 0x01 + +/*** Module-accessible parameters ***************************************/ + +int wf_raw = 0; /* we normally check for "raw state" to firmware + loading. if set, then during driver loading, the + state of the board is ignored, and we reset the + board and load the firmware anyway. + */ + +int fx_raw = 1; /* if this is zero, we'll leave the FX processor in + whatever state it is when the driver is loaded. + The default is to download the microprogram and + associated coefficients to set it up for "default" + operation, whatever that means. + */ + +int debug_default = 0; /* you can set this to control debugging + during driver loading. it takes any combination + of the WF_DEBUG_* flags defined in + wavefront.h + */ + +/* XXX this needs to be made firmware and hardware version dependent */ + +char *ospath = "/etc/sound/wavefront.os"; /* where to find a processed + version of the WaveFront OS + */ + +int sleep_interval = 100; /* HZ/sleep_interval seconds per sleep */ +int sleep_tries = 50; /* number of times we'll try to sleep */ + +int wait_usecs = 150; /* This magic number seems to give pretty optimal + throughput based on my limited experimentation. + If you want to play around with it and find a better + value, be my guest. Remember, the idea is to + get a number that causes us to just busy wait + for as many WaveFront commands as possible, without + coming up with a number so large that we hog the + whole CPU. + + Specifically, with this number, out of about 134,000 + status waits, only about 250 result in a sleep. + */ + +MODULE_PARM(wf_raw,"i"); +MODULE_PARM(fx_raw,"i"); +MODULE_PARM(debug_default,"i"); +MODULE_PARM(sleep_interval,"i"); +MODULE_PARM(sleep_tries,"i"); +MODULE_PARM(wait_usecs,"i"); +MODULE_PARM(ospath,"s"); + +/***************************************************************************/ + +static struct synth_info wavefront_info = +{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT, + 0, 32, 0, 0, SYNTH_CAP_INPUT}; + +static int (*midi_load_patch) (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) = NULL; + +typedef struct wf_config { + int devno; /* device number from kernel */ + int irq; /* "you were one, one of the few ..." */ + int base; /* low i/o port address */ + +#define mpu_data_port base +#define mpu_command_port base + 1 /* write semantics */ +#define mpu_status_port base + 1 /* read semantics */ +#define data_port base + 2 +#define status_port base + 3 /* read semantics */ +#define control_port base + 3 /* write semantics */ +#define block_port base + 4 /* 16 bit, writeonly */ +#define last_block_port base + 6 /* 16 bit, writeonly */ + + /* FX ports. These are mapped through the ICS2115 to the YS225. + The ICS2115 takes care of flipping the relevant pins on the + YS225 so that access to each of these ports does the right + thing. Note: these are NOT documented by Turtle Beach. + */ + +#define fx_status base + 8 +#define fx_op base + 8 +#define fx_lcr base + 9 +#define fx_dsp_addr base + 0xa +#define fx_dsp_page base + 0xb +#define fx_dsp_lsb base + 0xc +#define fx_dsp_msb base + 0xd +#define fx_mod_addr base + 0xe +#define fx_mod_data base + 0xf + + volatile int irq_ok; /* set by interrupt handler */ + int opened; /* flag, holds open(1) mode */ + char debug; /* debugging flags */ + int freemem; /* installed RAM, in bytes */ + int synthdev; /* OSS minor devnum for synth */ + int mididev; /* OSS minor devno for internal MIDI */ + int ext_mididev; /* OSS minor devno for external MIDI */ + char fw_version[2]; /* major = [0], minor = [1] */ + char hw_version[2]; /* major = [0], minor = [1] */ + char israw; /* needs Motorola microcode */ + char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ + char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ + char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ + int samples_used; /* how many */ + char interrupts_on; /* h/w MPU interrupts enabled ? */ + char rom_samples_rdonly; /* can we write on ROM samples */ + struct wait_queue *interrupt_sleeper; +#ifdef WF_STATS + unsigned long status_found_during_loop; + unsigned long status_found_during_sleep[4]; +#endif WF_STATS + +} wf_config; + +/* Note: because this module doesn't export any symbols, this really isn't + a global variable, even if it looks like one. I was quite confused by + this when I started writing this as a (newer) module -- pbd. +*/ + +static wf_config wavefront_configuration; + +#define wavefront_status(hw) (inb (hw->status_port)) + +/* forward references */ + +static int wffx_ioctl (struct wf_config *, wavefront_fx_info *); +static int wffx_init (struct wf_config *hw); +static int wavefront_delete_sample (struct wf_config *hw, int sampnum); + +typedef struct { + int cmd; + char *action; + unsigned int read_cnt; + unsigned int write_cnt; + int need_ack; +} wavefront_command; + +static struct { + int errno; + const char *errstr; +} wavefront_errors[] = { + { 0x01, "Bad sample number" }, + { 0x02, "Out of sample memory" }, + { 0x03, "Bad patch number" }, + { 0x04, "Error in number of voices" }, + { 0x06, "Sample load already in progress" }, + { 0x0B, "No sample load request pending" }, + { 0x0E, "Bad MIDI channel number" }, + { 0x10, "Download Record Error" }, + { 0x80, "Success" }, + { 0x0, 0x0 } +}; + +#define NEEDS_ACK 1 + +static wavefront_command wavefront_commands[] = { + { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, + { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, + { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, + { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, + { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, + { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, + { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, + { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, + { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, + { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, + { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, + { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, + { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, + { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, + { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, + { WFC_DOWNLOAD_SAMPLE, "download sample", + 0, WF_SAMPLE_BYTES, NEEDS_ACK }, + { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, + { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", + 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, + + /* This command requires a variable number of bytes to be written. + There is a hack in wavefront_cmd() to support this. The actual + count is passed in as the read buffer ptr, cast appropriately. + Ugh. + */ + + { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, + + /* This one is a hack as well. We just read the first byte of the + response, don't fetch an ACK, and leave the rest to the + calling function. Ugly, ugly, ugly. + */ + + { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, + { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", + 0, WF_ALIAS_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, + { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, + { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, + { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, + { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, + { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, + { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, + { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, + { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, + { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, + NEEDS_ACK}, + { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, + { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", + 0, 1, NEEDS_ACK }, + { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, + { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", + 32, 0, 0 }, + { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, + { 0x00 } +}; + +static const char * +wavefront_errorstr (int errnum) + +{ + int i; + + for (i = 0; wavefront_errors[i].errstr; i++) { + if (wavefront_errors[i].errno == errnum) { + return wavefront_errors[i].errstr; + } + } + + return "Unknown WaveFront error"; +} + +static wavefront_command * +wavefront_get_command (int cmd) + +{ + int i; + + for (i = 0; wavefront_commands[i].cmd != 0; i++) { + if (cmd == wavefront_commands[i].cmd) { + return &wavefront_commands[i]; + } + } + + return (wavefront_command *) 0; +} + +static int +wavefront_sleep (wf_config *hw, int limit) + +{ + current->timeout = jiffies + limit; + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->timeout = 0; + + return signal_pending(current); +} + +static int +wavefront_wait (wf_config *hw, int mask) + +{ + int i; + + for (i = 0; i < wait_usecs; i++) { + if (wavefront_status(hw) & mask) { +#ifdef WF_STATS + hw->status_found_during_loop++; +#endif WF_STATS + return 1; + } + udelay(1); + } + + for (i = 0; i < sleep_tries; i++) { + + if (wavefront_status(hw) & mask) { +#ifdef WF_STATS + if (i < 4) { + hw->status_found_during_sleep[i]++; + } +#endif WF_STATS + return 1; + } + + if (wavefront_sleep (hw, HZ/sleep_interval)) { + return (0); + } + } + + return 0; +} + +static int +wavefront_read (wf_config *hw) +{ + if (wavefront_wait (hw, STAT_CAN_READ)) + return inb (hw->data_port); + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: read timeout.\n"); + } +#endif WF_DEBUG + + return -1; +} + +static int +wavefront_write (wf_config *hw, unsigned char data) + +{ + if (wavefront_wait (hw, STAT_CAN_WRITE)) { + outb (data, hw->data_port); + return 1; + } + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: write timeout.\n"); + } +#endif WF_DEBUG + + return 0; +} + +static int +wavefront_cmd (wf_config *hw, int cmd, + unsigned char *rbuf, + unsigned char *wbuf) + +{ + int ack; + int i; + int c; + wavefront_command *wfcmd; + + if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { + printk (KERN_WARNING "WaveFront: command 0x%x not supported.\n", + cmd); + return 1; + } + + /* Hack to handle the one variable-size write command. See + wavefront_send_multisample() for the other half of this + gross and ugly strategy. + */ + + if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { + wfcmd->write_cnt = (unsigned int) rbuf; + rbuf = 0; + } + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG "Wavefront: 0x%x [%s] (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, wfcmd->write_cnt, + wfcmd->need_ack); + } +#endif WF_DEBUG + + if (!wavefront_write (hw, cmd)) { +#ifdef WF_DEBUG + if (hw->debug & (WF_DEBUG_IO|WF_DEBUG_CMD)) { + printk (KERN_DEBUG "WaveFront: cannot request " + "0x%x [%s].\n", + cmd, wfcmd->action); + } +#endif WF_DEBUG + return 1; + } + + if (wfcmd->write_cnt > 0) { +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: writing %d bytes " + "for 0x%x\n", + wfcmd->write_cnt, cmd); + } +#endif WF_DEBUG + + for (i = 0; i < wfcmd->write_cnt; i++) { + if (!wavefront_write (hw, wbuf[i])) { +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: bad write for byte %d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + } +#endif WF_DEBUG + return 1; + } +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG + "WaveFront: write[%d] = 0x%x\n", + i, wbuf[i]); +#endif WF_DEBUG + } + } + } + + if (wfcmd->read_cnt > 0) { +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: reading %d ints " + "for 0x%x\n", + wfcmd->read_cnt, cmd); + } +#endif WF_DEBUG + + for (i = 0; i < wfcmd->read_cnt; i++) { + + if ((c = wavefront_read(hw)) == -1) { +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: bad read for byte %d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + } +#endif WF_DEBUG + return 1; + } + + /* Now handle errors. Lots of special cases here */ + + if (c == 0xff) { + if ((c = wavefront_read (hw)) == -1) { +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: bad read for error byte at " + "read byte %d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + } +#endif WF_DEBUG + return 1; + } + + /* Can you believe this madness ? */ + + if (c == 1 && + wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { + rbuf[0] = WF_ST_EMPTY; + return (0); + + } else if (c == 3 && + wfcmd->cmd == WFC_UPLOAD_PATCH) { + + return 3; + + } else if (c == 1 && + wfcmd->cmd == WFC_UPLOAD_PROGRAM) { + + return 1; + + } else { + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: error %d (%s) during " + "read for byte " + "%d of 0x%x [%s].\n", + c, + wavefront_errorstr (c), + i, cmd, wfcmd->action); + } +#endif WF_DEBUG + return 1; + + } + } else { + rbuf[i] = c; + } + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG + "WaveFront: read[%d] = 0x%x\n", + i, rbuf[i]); + } +#endif WF_DEBUG + } + } + + if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG "WaveFront: reading ACK for 0x%x\n", + cmd); + } +#endif WF_DEBUG + + /* Some commands need an ACK, but return zero instead + of the standard value. + */ + + if ((ack = wavefront_read(hw)) == 0) { + ack = WF_ACK; + } + + if (ack != WF_ACK) { + if (ack == -1) { +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: cannot read ack for 0x%x [%s].\n", + cmd, wfcmd->action); + } +#endif WF_DEBUG + return 1; + + } else { + int err = -1; /* something unknown */ + + if (ack == 0xff) { /* explicit error */ + + if ((err = wavefront_read (hw)) == -1) { +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG + "WaveFront: cannot read err for 0x%x [%s].\n", + cmd, wfcmd->action); + } +#endif WF_DEBUG + } + } + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: 0x%x [%s] " + "failed (0x%x, 0x%x, %s)\n", + cmd, wfcmd->action, ack, err, + wavefront_errorstr (err)); + } +#endif WF_DEBUG + return -err; + } + } + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: ack received " + "for 0x%x [%s]\n", + cmd, wfcmd->action); + } +#endif WF_DEBUG + } else { +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG + "Wavefront: 0x%x [%s] does not need " + "ACK (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); +#endif WF_DEBUG + } + } + + return 0; + +} + +/*********************************************************************** +WaveFront: data munging + +Things here are weird. All data written to the board cannot +have its most significant bit set. Any data item with values +potentially > 0x7F (127) must be split across multiple bytes. + +Sometimes, we need to munge numeric values that are represented on +the x86 side as 8- to 32-bit values. Sometimes, we need to munge data +that is represented on the x86 side as an array of bytes. The most +efficient approach to handling both cases seems to be to use 2 +different functions for munging and 2 for de-munging. This avoids +weird casting and worrying about bit-level offsets. + +**********************************************************************/ + +static +unsigned char * +munge_int32 (unsigned int src, + unsigned char *dst, + unsigned int dst_size) +{ + int i; + + for (i = 0;i < dst_size; i++) { + *dst = src & 0x7F; /* Mask high bit of LSB */ + src = src >> 7; /* Rotate Right 7 bits */ + /* Note: we leave the upper bits in place */ + + dst++; + }; + return dst; +}; + +static int +demunge_int32 (unsigned char* src, int src_size) + +{ + int i; + int outval = 0; + + for (i = src_size - 1; i >= 0; i--) { + outval=(outval<<7)+src[i]; + } + + return outval; +}; + +static +unsigned char * +munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) + +{ + int i; + unsigned int last = dst_size / 2; + + for (i = 0; i < last; i++) { + *dst++ = src[i] & 0x7f; + *dst++ = src[i] >> 7; + } + return dst; +} + +static +unsigned char * +demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) + +{ + int i; + unsigned char *end = src + src_bytes; + + end = src + src_bytes; + + /* NOTE: src and dst *CAN* point to the same address */ + + for (i = 0; src != end; i++) { + dst[i] = *src++; + dst[i] |= (*src++)<<7; + } + + return dst; +} + +/*********************************************************************** +WaveFront: sample, patch and program management. +***********************************************************************/ + +static int +wavefront_delete_sample (wf_config *hw, int sample_num) + +{ + unsigned char wbuf[2]; + int x; + + wbuf[0] = sample_num & 0x7f; + wbuf[1] = sample_num >> 7; + + if ((x = wavefront_cmd (hw, WFC_DELETE_SAMPLE, 0, wbuf)) == 0) { + hw->sample_status[sample_num] = WF_ST_EMPTY; + } + + return x; +} + +static int +wavefront_get_sample_status (struct wf_config *hw, int assume_rom) + +{ + int i; + unsigned char rbuf[32], wbuf[32]; + unsigned int sc_real, sc_alias, sc_multi; + + /* check sample status */ + + if (wavefront_cmd (hw, WFC_GET_NSAMPLES, rbuf, wbuf)) { + printk ("WaveFront: cannot request sample count.\n"); + } + + sc_real = sc_alias = sc_multi = hw->samples_used = 0; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + + wbuf[0] = i & 0x7f; + wbuf[1] = i >> 7; + + if (wavefront_cmd (hw, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { + printk (KERN_WARNING + "WaveFront: cannot identify sample " + "type of slot %d\n", i); + hw->sample_status[i] = WF_ST_EMPTY; + continue; + } + + hw->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); + + if (assume_rom) { + hw->sample_status[i] |= WF_SLOT_ROM; + } + + switch (rbuf[0] & WF_ST_MASK) { + case WF_ST_SAMPLE: + sc_real++; + break; + case WF_ST_MULTISAMPLE: + sc_multi++; + break; + case WF_ST_ALIAS: + sc_alias++; + break; + case WF_ST_EMPTY: + break; + + default: + printk (KERN_WARNING + "WaveFront: unknown sample type for " + "slot %d (0x%x)\n", + i, rbuf[0]); + } + + if (rbuf[0] != WF_ST_EMPTY) { + hw->samples_used++; + } + } + + printk (KERN_INFO + "WaveFront: %d samples used (%d real, %d aliases, %d multi), " + "%d empty\n", hw->samples_used, sc_real, sc_alias, sc_multi, + WF_MAX_SAMPLE - hw->samples_used); + + + return (0); + +} + +static int +wavefront_get_patch_status (struct wf_config *hw) +{ + unsigned char patchbuf[WF_PATCH_BYTES]; + unsigned char patchnum[2]; + wavefront_patch *p; + int i, x, cnt, cnt2; + + for (i = 0; i < WF_MAX_PATCH; i++) { + patchnum[0] = i & 0x7f; + patchnum[1] = i >> 7; + + if ((x = wavefront_cmd (hw, WFC_UPLOAD_PATCH, patchbuf, + patchnum)) == 0) { + + hw->patch_status[i] |= WF_SLOT_FILLED; + p = (wavefront_patch *) patchbuf; + hw->sample_status + [p->sample_number|(p->sample_msb<<7)] |= + WF_SLOT_USED; + + } else if (x == 3) { /* Bad patch number */ + hw->patch_status[i] = 0; + } else { + printk (KERN_ERR "WaveFront: upload patch " + "error 0x%x\n", x); + hw->patch_status[i] = 0; + return 1; + } + } + + /* program status has already filled in slot_used bits */ + + for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { + if (hw->patch_status[i] & WF_SLOT_FILLED) { + cnt++; + } + if (hw->patch_status[i] & WF_SLOT_USED) { + cnt2++; + } + + } + printk (KERN_INFO + "WaveFront: %d patch slots filled, %d in use\n", cnt, cnt2); + + return (0); +} + +static int +wavefront_get_program_status (struct wf_config *hw) +{ + unsigned char progbuf[WF_PROGRAM_BYTES]; + wavefront_program prog; + unsigned char prognum; + int i, x, l, cnt; + + for (i = 0; i < WF_MAX_PROGRAM; i++) { + prognum = i; + + if ((x = wavefront_cmd (hw, WFC_UPLOAD_PROGRAM, progbuf, + &prognum)) == 0) { + + hw->prog_status[i] |= WF_SLOT_USED; + + demunge_buf (progbuf, (unsigned char *) &prog, + WF_PROGRAM_BYTES); + + for (l = 0; l < WF_NUM_LAYERS; l++) { + if (prog.layer[l].mute) { + hw->patch_status + [prog.layer[l].patch_number] |= + WF_SLOT_USED; + } + } + } else if (x == 1) { /* Bad program number */ + hw->prog_status[i] = 0; + } else { + printk (KERN_ERR "WaveFront: upload program " + "error 0x%x\n", x); + hw->prog_status[i] = 0; + } + } + + for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { + if (hw->prog_status[i]) { + cnt++; + } + } + + printk (KERN_INFO "WaveFront: %d programs slots in use\n", cnt); + + return (0); +} + +static int +wavefront_send_patch (wf_config *hw, + wavefront_patch_info *header) + +{ + unsigned char buf[WF_PATCH_BYTES+2]; + unsigned char *bptr; + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: downloading patch %d\n", + header->number); + } +#endif WF_DEBUG + + hw->patch_status[header->number] |= WF_SLOT_FILLED; + + bptr = buf; + bptr = munge_int32 (header->number, buf, 2); + munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); + + if (wavefront_cmd (hw, WFC_DOWNLOAD_PATCH, 0, buf)) { + printk (KERN_ERR "WaveFront: download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_send_program (wf_config *hw, + wavefront_patch_info *header) + +{ + unsigned char buf[WF_PROGRAM_BYTES+1]; + int i; + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG + "WaveFront: downloading program %d\n", header->number); + } +#endif WF_DEBUG + + hw->prog_status[header->number] = WF_SLOT_USED; + + /* XXX need to zero existing SLOT_USED bit for program_status[i] + where `i' is the program that's being (potentially) overwritten. + */ + + for (i = 0; i < WF_NUM_LAYERS; i++) { + if (header->hdr.pr.layer[i].mute) { + hw->patch_status[header->hdr.pr.layer[i].patch_number] |= + WF_SLOT_USED; + + /* XXX need to mark SLOT_USED for sample used by + patch_number, but this means we have to load it. Ick. + */ + } + } + + buf[0] = header->number; + munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); + + if (wavefront_cmd (hw, WFC_DOWNLOAD_PROGRAM, 0, buf)) { + printk (KERN_WARNING "WaveFront: download patch failed\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_freemem (wf_config *hw) + +{ + char rbuf[8]; + + if (wavefront_cmd (hw, WFC_REPORT_FREE_MEMORY, rbuf, 0)) { + printk (KERN_WARNING "WaveFront: can't get memory stats.\n"); + return -1; + } else { + return demunge_int32 (rbuf, 4); + } +} + +static int +wavefront_send_sample (wf_config *hw, + wavefront_patch_info *header, + UINT16 *dataptr, + int data_is_unsigned) + +{ + /* samples are downloaded via a 16-bit wide i/o port + (you could think of it as 2 adjacent 8-bit wide ports + but its less efficient that way). therefore, all + the blocksizes and so forth listed in the documentation, + and used conventionally to refer to sample sizes, + which are given in 8-bit units (bytes), need to be + divided by 2. + */ + + UINT16 sample_short; + UINT32 length; + UINT16 *data_end = 0; + unsigned int i; + const int max_blksize = 4096/2; + unsigned int written; + unsigned int blocksize; + int dma_ack; + int blocknum; + unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; + unsigned char *shptr; + int skip = 0; + int initial_skip = 0; + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: sample %sdownload for slot %d, " + "type %d, %d bytes from 0x%x\n", + header->size ? "" : "header ", + header->number, header->subkey, header->size, + (int) header->dataptr); + } +#endif WF_DEBUG + + if (header->size) { + + /* XXX its a debatable point whether or not RDONLY semantics + on the ROM samples should cover just the sample data or + the sample header. For now, it only covers the sample data, + so anyone is free at all times to rewrite sample headers. + + My reason for this is that we have the sample headers + available in the WFB file for General MIDI, and so these + can always be reset if needed. The sample data, however, + cannot be recovered without a complete reset and firmware + reload of the ICS2115, which is a very expensive operation. + + So, doing things this way allows us to honor the notion of + "RESETSAMPLES" reasonably cheaply. Note however, that this + is done purely at user level: there is no WFB parser in + this driver, and so a complete reset (back to General MIDI, + or theoretically some other configuration) is the + responsibility of the user level library. + + To try to do this in the kernel would be a little + crazy: we'd need 158K of kernel space just to hold + a copy of the patch/program/sample header data. + */ + + if (hw->rom_samples_rdonly) { + if (hw->sample_status[header->number] & WF_SLOT_ROM) { + printk (KERN_ERR "WaveFront: sample slot %d " + "write protected\n", + header->number); + return -EACCES; + } + } + + wavefront_delete_sample (hw, header->number); + } + + if (header->size) { + hw->freemem = wavefront_freemem (hw); + + if (hw->freemem < header->size) { + printk (KERN_ERR + "WaveFront: insufficient memory to " + "load %d byte sample.\n", + header->size); + return -ENOMEM; + } + + } + + skip = WF_GET_CHANNEL(&header->hdr.s); + + if (skip > 0) { + switch (header->hdr.s.SampleResolution) { + case LINEAR_16BIT: + break; + default: + printk (KERN_ERR + "WaveFront: channel selection only possible " + "on 16-bit samples"); + return -(EINVAL); + } + } + + switch (skip) { + case 0: + initial_skip = 0; + skip = 1; + break; + case 1: + initial_skip = 0; + skip = 2; + break; + case 2: + initial_skip = 1; + skip = 2; + break; + case 3: + initial_skip = 2; + skip = 3; + break; + case 4: + initial_skip = 3; + skip = 4; + break; + case 5: + initial_skip = 4; + skip = 5; + break; + case 6: + initial_skip = 5; + skip = 6; + break; + } + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: channel selection: %d => " + "initial skip = %d, skip = %d\n", + WF_GET_CHANNEL (&header->hdr.s), initial_skip, skip); + } +#endif WF_DEBUG + + /* Be safe, and zero the "Unused" bits ... */ + + WF_SET_CHANNEL(&header->hdr.s, 0); + + /* adjust size for 16 bit samples by dividing by two. We always + send 16 bits per write, even for 8 bit samples, so the length + is always half the size of the sample data in bytes. + */ + + length = header->size / 2; + + /* the data we're sent has not been munged, and in fact, the + header we have to send isn't just a munged copy either. + so, build the sample header right here. + */ + + shptr = &sample_hdr[0]; + + shptr = munge_int32 (header->number, shptr, 2); + + if (header->size) { + shptr = munge_int32 (length, shptr, 4); + } + + /* Yes, a 4 byte result doesn't contain all of the offset bits, + but the offset only uses 24 bits. + */ + + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopEndOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset), + shptr, 4); + + /* This one is truly weird. What kind of weirdo decided that in + a system dominated by 16- and 32-bit integers, they would use + just 12 bits ? + */ + + shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); + + /* Why is this nybblified, when the MSB is *always* zero? + Anyway, we can't take address of bitfield, so make a + good-faith guess at where it starts. + */ + + shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), + shptr, 2); + + if (wavefront_cmd (hw, header->size ? + WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, + 0, sample_hdr)) { + printk (KERN_WARNING "WaveFront: sample %sdownload refused.\n", + header->size ? "" : "header "); + return -(EIO); + } + + if (header->size == 0) { + goto sent; /* Sorry. Just had to have one somewhere */ + } + + data_end = dataptr + length; + + /* Do any initial skip over an unused channel's data */ + + dataptr += initial_skip; + + for (written = 0, blocknum = 0; + written < length; written += max_blksize, blocknum++) { + + if ((length - written) > max_blksize) { + blocksize = max_blksize; + } else { + /* round to nearest 16-byte value */ + blocksize = ((length-written+7)&~0x7); + } + + if (wavefront_cmd (hw, WFC_DOWNLOAD_BLOCK, 0, 0)) { + printk (KERN_WARNING "WaveFront: download block " + "request refused.\n"); + return -(EIO); + } + + for (i = 0; i < blocksize; i++) { + + if (dataptr < data_end) { + + get_user (sample_short, dataptr); + dataptr += skip; + + if (data_is_unsigned) { /* GUS ? */ + + if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { + + /* 8 bit sample + resolution, sign + extend both bytes. + */ + + ((unsigned char*) + &sample_short)[0] += 0x7f; + ((unsigned char*) + &sample_short)[1] += 0x7f; + + } else { + + /* 16 bit sample + resolution, sign + extend the MSB. + */ + + sample_short += 0x7fff; + } + } + + } else { + + /* In padding section of final block: + + Don't fetch unsupplied data from + user space, just continue with + whatever the final value was. + */ + } + + if (i < blocksize - 1) { + outw (sample_short, hw->block_port); + } else { + outw (sample_short, hw->last_block_port); + } + } + + /* Get "DMA page acknowledge" */ + + if ((dma_ack = wavefront_read (hw)) != WF_DMA_ACK) { + if (dma_ack == -1) { + printk (KERN_ERR "WaveFront: upload sample " + "DMA ack timeout\n"); + return -(EIO); + } else { + printk (KERN_ERR "WaveFront: upload sample " + "DMA ack error 0x%x\n", + dma_ack); + return -(EIO); + } + } + } + + hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); + + /* Note, label is here because sending the sample header shouldn't + alter the sample_status info at all. + */ + + sent: + return (0); +} + +static int +wavefront_send_alias (struct wf_config *hw, + wavefront_patch_info *header) + +{ + unsigned char alias_hdr[WF_ALIAS_BYTES]; + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: download alias, %d is " + "alias for %d\n", + header->number, + header->hdr.a.OriginalSample); + } +#endif WF_DEBUG + + munge_int32 (header->number, &alias_hdr[0], 2); + munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), + &alias_hdr[4], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), + &alias_hdr[8], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), + &alias_hdr[12], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), + &alias_hdr[16], 4); + munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); + munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); + + if (wavefront_cmd (hw, WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) { + printk (KERN_ERR "WaveFront: download alias failed.\n"); + return -(EIO); + } + + hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); + + return (0); +} + +static int +wavefront_send_multisample (struct wf_config *hw, + wavefront_patch_info *header) +{ + int i; + int num_samples; + unsigned char msample_hdr[WF_MSAMPLE_BYTES]; + + munge_int32 (header->number, &msample_hdr[0], 2); + + /* You'll recall at this point that the "number of samples" value + in a wavefront_multisample struct is actually the log2 of the + real number of samples. + */ + + num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); + msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: multi %d with %d=%d samples\n", + header->number, header->hdr.ms.NumberOfSamples, num_samples); + } +#endif WF_DEBUG + + for (i = 0; i < num_samples; i++) { +#ifdef WF_DEBUG + if ((hw->debug & (WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA)) == + (WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA)) { + printk (KERN_DEBUG "WaveFront: sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } +#endif WF_DEBUG + munge_int32 (header->hdr.ms.SampleNumber[i], + &msample_hdr[3+(i*2)], 2); + } + + /* Need a hack here to pass in the number of bytes + to be written to the synth. This is ugly, and perhaps + one day, I'll fix it. + */ + + if (wavefront_cmd (hw, WFC_DOWNLOAD_MULTISAMPLE, + (unsigned char *) ((num_samples*2)+3), + msample_hdr)) { + printk (KERN_ERR "WaveFront: download of multisample failed.\n"); + return -(EIO); + } + + hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); + + return (0); +} + +static int +wavefront_fetch_multisample (struct wf_config *hw, + wavefront_patch_info *header) +{ + int i; + unsigned char log_ns[1]; + unsigned char number[2]; + int num_samples; + + munge_int32 (header->number, number, 2); + + if (wavefront_cmd (hw, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { + printk (KERN_ERR "WaveFront: upload multisample failed.\n"); + return -(EIO); + } + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: msample %d has %d samples\n", + header->number, log_ns[0]); + } +#endif WF_DEBUG + + header->hdr.ms.NumberOfSamples = log_ns[0]; + + /* get the number of samples ... */ + + num_samples = (1 << log_ns[0]); + + for (i = 0; i < num_samples; i++) { + char d[2]; + + if ((d[0] = wavefront_read (hw)) == -1) { + printk (KERN_ERR "WaveFront: upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + + if ((d[1] = wavefront_read (hw)) == -1) { + printk (KERN_ERR "WaveFront: upload multisample failed " + "during sample loop.\n"); + return -(EIO); + } + + header->hdr.ms.SampleNumber[i] = + demunge_int32 ((unsigned char *) d, 2); + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: msample " + "sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } +#endif WF_DEBUG + } + + return (0); +} + + +static int +wavefront_send_drum (struct wf_config *hw, wavefront_patch_info *header) + +{ + unsigned char drumbuf[WF_DRUM_BYTES]; + wavefront_drum *drum = &header->hdr.d; + int i; + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG + "WaveFront: downloading edrum for MIDI " + "note %d, patch = %d\n", + header->number, drum->PatchNumber); + } +#endif WF_DEBUG + + drumbuf[0] = header->number & 0x7f; + + for (i = 0; i < 4; i++) { + munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); + } + + if (wavefront_cmd (hw, WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) { + printk (KERN_ERR "WaveFront: download drum failed.\n"); + return -(EIO); + } + + return (0); +} + +static int +wavefront_find_free_sample (struct wf_config *hw) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(hw->sample_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING "WaveFront: no free sample slots!\n"); + return -1; +} + +static int +wavefront_find_free_patch (struct wf_config *hw) + +{ + int i; + + for (i = 0; i < WF_MAX_PATCH; i++) { + if (!(hw->patch_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING "WaveFront: no free patch slots!\n"); + return -1; +} + +static int +log2_2048(int n) + +{ + int tbl[]={0, 0, 2048, 3246, 4096, 4755, 5294, 5749, 6143, + 6492, 6803, 7084, 7342, 7578, 7797, 8001, 8192, + 8371, 8540, 8699, 8851, 8995, 9132, 9264, 9390, + 9510, 9626, 9738, 9845, 9949, 10049, 10146}; + int i; + + /* Returns 2048*log2(n) */ + + /* FIXME: this is like doing integer math + on quantum particles (RuN) */ + + i=0; + while(n>=32*256) { + n>>=8; + i+=2048*8; + } + while(n>=32) { + n>>=1; + i+=2048; + } + i+=tbl[n]; + return(i); +} + +static int +wavefront_load_gus_patch (struct wf_config *hw, + int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info guspatch; + wavefront_patch_info samp, pat, prog; + wavefront_patch *patp; + wavefront_sample *sampp; + wavefront_program *progp; + + int i,base_note; + long sizeof_patch; + + /* Copy in the header of the GUS patch */ + + sizeof_patch = (long) &guspatch.data[0] - (long) &guspatch; + copy_from_user (&((char *) &guspatch)[offs], + &(addr)[offs], sizeof_patch - offs); + + if ((i = wavefront_find_free_patch (hw)) == -1) { + return -EBUSY; + } + pat.number = i; + pat.subkey = WF_ST_PATCH; + patp = &pat.hdr.p; + + if ((i = wavefront_find_free_sample (hw)) == -1) { + return -EBUSY; + } + samp.number = i; + samp.subkey = WF_ST_SAMPLE; + samp.size = guspatch.len; + sampp = &samp.hdr.s; + + prog.number = guspatch.instr_no; + progp = &prog.hdr.pr; + + /* Setup the patch structure */ + + patp->amplitude_bias=guspatch.volume; + patp->portamento=0; + patp->sample_number= samp.number & 0xff; + patp->sample_msb= samp.number>>8; + patp->pitch_bend= /*12*/ 0; + patp->mono=1; + patp->retrigger=1; + patp->nohold=(guspatch.mode & WAVE_SUSTAIN_ON) ? 0:1; + patp->frequency_bias=0; + patp->restart=0; + patp->reuse=0; + patp->reset_lfo=1; + patp->fm_src2=0; + patp->fm_src1=WF_MOD_MOD_WHEEL; + patp->am_src=WF_MOD_PRESSURE; + patp->am_amount=127; + patp->fc1_mod_amount=0; + patp->fc2_mod_amount=0; + patp->fm_amount1=0; + patp->fm_amount2=0; + patp->envelope1.attack_level=127; + patp->envelope1.decay1_level=127; + patp->envelope1.decay2_level=127; + patp->envelope1.sustain_level=127; + patp->envelope1.release_level=0; + patp->envelope2.attack_velocity=127; + patp->envelope2.attack_level=127; + patp->envelope2.decay1_level=127; + patp->envelope2.decay2_level=127; + patp->envelope2.sustain_level=127; + patp->envelope2.release_level=0; + patp->envelope2.attack_velocity=127; + patp->randomizer=0; + + /* Program for this patch */ + + progp->layer[0].patch_number= pat.number; /* XXX is this right ? */ + progp->layer[0].mute=1; + progp->layer[0].pan_or_mod=1; + progp->layer[0].pan=7; + progp->layer[0].mix_level=127 /* guspatch.volume */; + progp->layer[0].split_type=0; + progp->layer[0].split_point=0; + progp->layer[0].updown=0; + + for (i = 1; i < 4; i++) { + progp->layer[i].mute=0; + } + + /* Sample data */ + + sampp->SampleResolution=((~guspatch.mode & WAVE_16_BITS)<<1); + + for (base_note=0; + note_to_freq (base_note) < guspatch.base_note; + base_note++); + + if ((guspatch.base_note-note_to_freq(base_note)) + >(note_to_freq(base_note)-guspatch.base_note)) + base_note++; + + printk(KERN_DEBUG "ref freq=%d,base note=%d\n", + guspatch.base_freq, + base_note); + + sampp->FrequencyBias = (29550 - log2_2048(guspatch.base_freq) + + base_note*171); + printk(KERN_DEBUG "Freq Bias is %d\n", sampp->FrequencyBias); + sampp->Loop=(guspatch.mode & WAVE_LOOPING) ? 1:0; + sampp->sampleStartOffset.Fraction=0; + sampp->sampleStartOffset.Integer=0; + sampp->loopStartOffset.Fraction=0; + sampp->loopStartOffset.Integer=guspatch.loop_start + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->loopEndOffset.Fraction=0; + sampp->loopEndOffset.Integer=guspatch.loop_end + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->sampleEndOffset.Fraction=0; + sampp->sampleEndOffset.Integer=guspatch.len >> (guspatch.mode&1); + sampp->Bidirectional=(guspatch.mode&WAVE_BIDIR_LOOP) ? 1:0; + sampp->Reverse=(guspatch.mode&WAVE_LOOP_BACK) ? 1:0; + + /* Now ship it down */ + + wavefront_send_sample (hw, &samp, + (unsigned short *) &(addr)[sizeof_patch], + (guspatch.mode & WAVE_UNSIGNED) ? 1:0); + wavefront_send_patch (hw, &pat); + wavefront_send_program (hw, &prog); + + /* Now pan as best we can ... use the slave/internal MIDI device + number if it exists (since it talks to the WaveFront), or the + master otherwise. + */ + +#ifdef CONFIG_MIDI + if (hw->mididev > 0) { + midi_synth_controller (hw->mididev, guspatch.instr_no, 10, + ((guspatch.panning << 4) > 127) ? + 127 : (guspatch.panning << 4)); + } +#endif CONFIG_MIDI + + return(0); +} + +int +wavefront_load_patch (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + + struct wf_config *hw = &wavefront_configuration; + wavefront_patch_info header; + + if (format == SYSEX_PATCH) { /* Handled by midi_synth.c */ + if (midi_load_patch == NULL) { + printk (KERN_ERR + "WaveFront: SYSEX not loadable: " + "no midi patch loader!\n"); + return -(EINVAL); + } + return midi_load_patch (dev, format, addr, + offs, count, pmgr_flag); + + } else if (format == GUS_PATCH) { + return wavefront_load_gus_patch (hw, dev, format, + addr, offs, count, pmgr_flag); + + } else if (format != WAVEFRONT_PATCH) { + printk (KERN_ERR "WaveFront: unknown patch format %d\n", format); + return -(EINVAL); + } + + if (count < sizeof (wavefront_patch_info)) { + printk (KERN_ERR "WaveFront: sample header too short\n"); + return -(EINVAL); + } + + /* copied in so far: `offs' bytes from `addr'. We shouldn't copy + them in again, and they correspond to header->key and header->devno. + So now, copy the rest of the wavefront_patch_info struct, except + for the 'hdr' field, since this is handled via indirection + through the 'hdrptr' field. + */ + + copy_from_user (&((char *) &header)[offs], &(addr)[offs], + sizeof(wavefront_patch_info) - + sizeof(wavefront_any) - offs); + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: download " + "Sample type: %d " + "Sample number: %d " + "Sample size: %d\n", + header.subkey, + header.number, + header.size); + } +#endif WF_DEBUG + + switch (header.subkey) { + case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ + + copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_sample)); + + return wavefront_send_sample (hw, &header, header.dataptr, 0); + + case WF_ST_MULTISAMPLE: + + copy_from_user ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_multisample)); + + return wavefront_send_multisample (hw, &header); + + + case WF_ST_ALIAS: + + copy_from_user ((unsigned char *) &header.hdr.a, + (unsigned char *) header.hdrptr, + sizeof (wavefront_alias)); + + return wavefront_send_alias (hw, &header); + + case WF_ST_DRUM: + copy_from_user ((unsigned char *) &header.hdr.d, + (unsigned char *) header.hdrptr, + sizeof (wavefront_drum)); + + return wavefront_send_drum (hw, &header); + + case WF_ST_PATCH: + copy_from_user ((unsigned char *) &header.hdr.p, + (unsigned char *) header.hdrptr, + sizeof (wavefront_patch)); + + return wavefront_send_patch (hw, &header); + + case WF_ST_PROGRAM: + copy_from_user ((unsigned char *) &header.hdr.pr, + (unsigned char *) header.hdrptr, + sizeof (wavefront_program)); + + return wavefront_send_program (hw, &header); + + default: + printk (KERN_ERR "WaveFront: unknown patch type %d.\n", + header.subkey); + return -(EINVAL); + } + + return 0; +} + +/*********************************************************************** +WaveFront: /dev/sequencer{,2} and other hardware-dependent interfaces +***********************************************************************/ + +static void +process_sample_hdr (UCHAR8 *buf) + +{ + wavefront_sample s; + UCHAR8 *ptr; + + ptr = buf; + + /* The board doesn't send us an exact copy of a "wavefront_sample" + in response to an Upload Sample Header command. Instead, we + have to convert the data format back into our data structure, + just as in the Download Sample command, where we have to do + something very similar in the reverse direction. + */ + + *((UINT32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; + + s.SampleResolution = *ptr & 0x3; + s.Loop = *ptr & 0x8; + s.Bidirectional = *ptr & 0x10; + s.Reverse = *ptr & 0x40; + + /* Now copy it back to where it came from */ + + memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); +} + +static int +wavefront_synth_control (int dev, int cmd, caddr_t arg) + +{ + struct wf_config *hw = &wavefront_configuration; + wavefront_control wc; + unsigned char patchnumbuf[2]; + int i; + + copy_from_user (&wc, arg, sizeof (wc)); + +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG "WaveFront: synth control with " + "cmd 0x%x\n", wc.cmd); + } +#endif WF_DEBUG + + /* special case handling of or for various commands */ + + switch (wc.cmd) { + case WFC_DISABLE_INTERRUPTS: + printk (KERN_INFO "WaveFront: interrupts disabled.\n"); + outb (0x80|0x20, hw->control_port); + hw->interrupts_on = 0; + return 0; + + case WFC_ENABLE_INTERRUPTS: + printk (KERN_INFO "WaveFront: interrupts enabled.\n"); + outb (0x80|0x20|0x40, hw->control_port); + hw->interrupts_on = 1; + return 0; + + case WFC_INTERRUPT_STATUS: + wc.rbuf[0] = hw->interrupts_on; + return 0; + + case WFC_ROMSAMPLES_RDONLY: + hw->rom_samples_rdonly = wc.wbuf[0]; + wc.status = 0; + return 0; + + case WFC_IDENTIFY_SLOT_TYPE: + i = wc.wbuf[0] | (wc.wbuf[1] << 7); + if (i <0 || i >= WF_MAX_SAMPLE) { + printk (KERN_WARNING "WaveFront: invalid slot ID %d\n", + i); + wc.status = EINVAL; + return 0; + } + wc.rbuf[0] = hw->sample_status[i]; + wc.status = 0; + return 0; + + case WFC_DEBUG_DRIVER: + hw->debug = wc.wbuf[0]; + printk (KERN_INFO "WaveFront: debug = 0x%x\n", hw->debug); + return 0; + + case WFC_FX_IOCTL: + wffx_ioctl (hw, (wavefront_fx_info *) &wc.wbuf[0]); + return 0; + + case WFC_UPLOAD_PATCH: + munge_int32 (*((UINT32 *) wc.wbuf), patchnumbuf, 2); + memcpy (wc.wbuf, patchnumbuf, 2); + break; + + case WFC_UPLOAD_MULTISAMPLE: + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO "WaveFront: support for various uploads " + "being considered.\n"); + wc.status = EINVAL; + return -EINVAL; + } + + wc.status = wavefront_cmd (hw, wc.cmd, wc.rbuf, wc.wbuf); + + /* Special case handling of certain commands. + + In particular, if the command was an upload, demunge the data + so that the user-level doesn't have to think about it. + */ + + if (wc.status == 0) { + switch (wc.cmd) { + /* intercept any freemem requests so that we know + we are always current with the user-level view + of things. + */ + + case WFC_REPORT_FREE_MEMORY: + hw->freemem = demunge_int32 (wc.rbuf, 4); + break; + + case WFC_UPLOAD_PATCH: + demunge_buf (wc.rbuf, wc.rbuf, WF_PATCH_BYTES); + break; + + case WFC_UPLOAD_PROGRAM: + demunge_buf (wc.rbuf, wc.rbuf, WF_PROGRAM_BYTES); + break; + + case WFC_UPLOAD_EDRUM_PROGRAM: + demunge_buf (wc.rbuf, wc.rbuf, WF_DRUM_BYTES - 1); + break; + + case WFC_UPLOAD_SAMPLE_HEADER: + process_sample_hdr (wc.rbuf); + break; + + case WFC_UPLOAD_MULTISAMPLE: + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO "WaveFront: support for " + "various uploads " + "being considered.\n"); + break; + + case WFC_VMIDI_OFF: + virtual_midi_disable (hw->mididev); + break; + + case WFC_VMIDI_ON: + virtual_midi_enable (hw->mididev, 0); + break; + + break; + } + } + + /* XXX It would be nice to avoid a complete copy of the whole + struct sometimes. But I think its fast enough that this + is a low priority fix. + */ + + copy_to_user (arg, &wc, sizeof (wc)); + return 0; +} + + +/*********************************************************************** +WaveFront: MIDI synth interface +***********************************************************************/ + + +static int +wavefront_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + wf_config *hw = &wavefront_configuration; + unsigned char rbuf[4]; + + switch (cmd) { + case SNDCTL_SYNTH_INFO: + memcpy (&((char *) arg)[0], &wavefront_info, + sizeof (wavefront_info)); + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + printk (KERN_WARNING + "WaveFront: cannot reset sample status in kernel.\n"); + return 0; /* don't force an error */ + break; + + case SNDCTL_SEQ_PERCMODE: + /* XXX does this correspond to anything obvious ?*/ + return 0; /* don't force an error */ + break; + + case SNDCTL_SYNTH_MEMAVL: + if (wavefront_cmd (hw, WFC_REPORT_FREE_MEMORY, rbuf, 0) != 0) { + printk (KERN_ERR + "WaveFront: cannot get free memory size\n"); + return 0; + } else { + hw->freemem = demunge_int32 (rbuf, 4); + return hw->freemem; + } + + case SNDCTL_SYNTH_CONTROL: + return wavefront_synth_control (dev, cmd, arg); + + default: + return -(EINVAL); + } +} + +static int +wavefront_open (int dev, int mode) + +{ + struct wf_config *hw = &wavefront_configuration; + + if (hw->opened) { + printk (KERN_WARNING "WaveFront: warning: device in use\n"); + } + + hw->opened = mode; + return (0); +} + +static void +wavefront_close (int dev) + +{ + struct wf_config *hw = &wavefront_configuration; + +#ifdef WF_STATS + int i; + printk ("Status during loop: %ld\n", hw->status_found_during_loop); + for (i = 0; i < 4; i++) { + printk ("Status during sleep[%d]: %ld\n", + i, hw->status_found_during_sleep[i]); + } +#endif WF_STATS + hw->opened = 0; + hw->debug = 0; + + return; +} + +static void +wavefront_aftertouch (int dev, int channel, int pressure) +{ + midi_synth_aftertouch (wavefront_configuration.mididev,channel,pressure); +}; + +static void +wavefront_bender (int dev, int chn, int value) +{ + midi_synth_bender (wavefront_configuration.mididev, chn, value); +}; + +static void +wavefront_controller (int dev, int channel, int ctrl_num, int value) +{ + if(ctrl_num==CTRL_PITCH_BENDER) wavefront_bender(0,channel,value); + midi_synth_controller (wavefront_configuration.mididev, + channel,ctrl_num,value); +}; + +static void +wavefront_panning(int dev, int channel, int pressure) +{ + midi_synth_controller (wavefront_configuration.mididev, + channel,CTL_PAN,pressure); +}; + +static int +wavefront_set_instr (int dev, int channel, int instr_no) +{ + return(midi_synth_set_instr (wavefront_configuration.mididev, + channel,instr_no)); +}; + +static int +wavefront_kill_note (int dev, int channel, int note, int volume) +{ + if (note==255) + return (midi_synth_start_note (wavefront_configuration.mididev, + channel, 0, 0)); + return(midi_synth_kill_note (wavefront_configuration.mididev, + channel, note, volume)); +}; + +static int +wavefront_start_note (int dev, int channel, int note, int volume) +{ + if (note==255) { + midi_synth_aftertouch (wavefront_configuration.mididev, + channel,volume); + return(0); + }; + + if (volume==0) { + volume=127; + midi_synth_aftertouch + (wavefront_configuration.mididev, + channel,0); + }; + + midi_synth_start_note (wavefront_configuration.mididev, + channel, note, volume); + return(0); +}; + +static void +wavefront_setup_voice (int dev, int voice, int chn) +{ +}; + +static void wavefront_reset (int dev) + +{ + int i; + + for (i = 0; i < 16; i++) { + midi_synth_kill_note (dev,i,0,0); + }; +}; + +static struct synth_operations wavefront_operations = +{ + "WaveFront", + &wavefront_info, + 0, + SYNTH_TYPE_SAMPLE, + SAMPLE_TYPE_WAVEFRONT, + wavefront_open, + wavefront_close, + wavefront_ioctl, + wavefront_kill_note, + wavefront_start_note, + wavefront_set_instr, + wavefront_reset, + NULL, + wavefront_load_patch, + wavefront_aftertouch, + wavefront_controller, + wavefront_panning, + NULL, + wavefront_bender, + NULL, + wavefront_setup_voice +}; + + +/*********************************************************************** +WaveFront: OSS/Free and/or Linux kernel installation interface +***********************************************************************/ + +void +wavefrontintr (int irq, void *dev_id, struct pt_regs *dummy) +{ + /* We don't use this handler except during device + configuration. While the module is installed, the + interrupt is used to signal MIDI interrupts, and is + handled by the interrupt routine in wf_midi.c + */ + + wf_config *hw = (wf_config *) dev_id; + hw->irq_ok = 1; + + if ((wavefront_status(hw) & STAT_INTR_WRITE) || + (wavefront_status(hw) & STAT_INTR_READ)) { + wake_up (&hw->interrupt_sleeper); + } +} + +/* STATUS REGISTER + +0 Host Rx Interrupt Enable (1=Enabled) +1 Host Rx Register Full (1=Full) +2 Host Rx Interrupt Pending (1=Interrupt) +3 Unused +4 Host Tx Interrupt (1=Enabled) +5 Host Tx Register empty (1=Empty) +6 Host Tx Interrupt Pending (1=Interrupt) +7 Unused + +11111001 + Rx Intr enable + nothing to read from board + no rx interrupt pending + unused + tx interrupt enabled + space to transmit + tx interrupt pending + +*/ + +int +wavefront_interrupt_bits (int irq) + +{ + int bits; + + switch (irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + printk (KERN_WARNING "WaveFront: invalid IRQ %d\n", irq); + bits = -1; + } + + return bits; +} + +void +wavefront_should_cause_interrupt (wf_config *hw, int val, int port, int timeout) + +{ + unsigned long flags; + + save_flags (flags); + cli(); + hw->irq_ok = 0; + outb (val,port); + current->timeout = jiffies + timeout; + interruptible_sleep_on (&hw->interrupt_sleeper); + restore_flags (flags); +} + +static int +wavefront_hw_reset (wf_config *hw) + +{ + int bits; + int hwv[2]; + + if (request_irq (hw->irq, wavefrontintr, + 0, "WaveFront", (void *) hw) < 0) { + printk (KERN_WARNING "WaveFront: IRQ %d not available!\n", + hw->irq); + return 1; + } + + /* try reset of port */ + + outb (0x0, hw->control_port); + + /* At this point, the board is in reset, and the H/W initialization + register is accessed at the same address as the data port. + + Bit 7 - Enable IRQ Driver + 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs + 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. + + Bit 6 - MIDI Interface Select + + 0 - Use the MIDI Input from the 26-pin WaveBlaster + compatible header as the serial MIDI source + 1 - Use the MIDI Input from the 9-pin D connector as the serial MIDI + source. + + Bits 5:3 - IRQ Selection + 0 0 0 - IRQ 2/9 + 0 0 1 - IRQ 5 + 0 1 0 - IRQ 12 + 0 1 1 - IRQ 15 + 1 0 0 - Reserved + 1 0 1 - Reserved + 1 1 0 - Reserved + 1 1 1 - Reserved + + Bits 2:1 - Reserved + Bit 0 - Disable Boot ROM + 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM + 1 - memory accesses to 03FC30-03FFFFH are directed to external + storage. + + */ + + /* configure hardware: IRQ, enable interrupts, + plus external 9-pin MIDI interface selected + */ + + if ((bits = wavefront_interrupt_bits (hw->irq)) < 0) { + return 1; + } + + outb (0x80 | 0x40 | bits, hw->data_port); + + /* CONTROL REGISTER + + 0 Host Rx Interrupt Enable (1=Enabled) 0x1 + 1 Unused 0x2 + 2 Unused 0x4 + 3 Unused 0x8 + 4 Host Tx Interrupt Enable 0x10 + 5 Mute (0=Mute; 1=Play) 0x20 + 6 Master Interrupt Enable (1=Enabled) 0x40 + 7 Master Reset (0=Reset; 1=Run) 0x80 + + Take us out of reset, unmute, master + TX + RX interrupts on. + + We'll get an interrupt presumably to tell us that the TX + register is clear. However, this doesn't mean that the + board is ready. We actually have to send it a command, and + wait till it gets back to use. After a cold boot, this can + take some time. + + + I think this is because its only after a cold boot that the + onboard ROM does its memory check, which can take "up to 4 + seconds" according to the WaveFront SDK. So, since sleeping + doesn't cost us much, we'll give it *plenty* of time. It + turns out that with 12MB of RAM, it can take up to 16 + seconds or so!! See the code after "ABOUT INTERRUPTS" + */ + + wavefront_should_cause_interrupt(hw, + 0x80|0x40|0x10|0x1, + hw->control_port, + (2*HZ)/100); + + /* Note: data port is now the data port, not the h/w initialization + port. + */ + + if (!hw->irq_ok) { + printk (KERN_WARNING + "WaveFront: intr not received after h/w un-reset.\n"); + goto gone_bad; + } + + hw->interrupts_on = 1; + + /* ABOUT INTERRUPTS: + ----------------- + + When we talk about interrupts, there are two kinds + generated by the ICS2115. The first is to signal MPU data + ready to read, and the second is to signal RX or TX status + changes. We *always* want interrupts for MPU stuff but we + generally avoid using RX/TX interrupts. + + In theory, we could use the TX and RX interrupts for all + communication with the card. However, there are 2 good + reasons not to do this. + + First of all, the MIDI interface is going to use the same + interrupt. This presents no practical problem since Linux + allows us to share IRQ's. However, there are times when it + makes sense for a user to ask the driver to disable + interrupts, to avoid bothering Linux with a stream of MIDI + interrupts that aren't going to be used because nothing + cares about them. If we rely on them for communication with + the WaveFront synth as well, this disabling would be + crippling. Since being able to disable them can save quite + a bit of overhead (consider the interrupt frequency of a + physical MIDI controller like a modwheel being shunted back + and forth - its higher than the mouse, and much of + the time is of absolutely no interest to the kernel or any + user space processes whatsoever), we don't want to do this. + + Secondly, much of the time, there's no reason to go to + sleep on a TX or RX status: the WaveFront gets back to us + quickly enough that its a lot more efficient to just busy + wait on the relevant status. Once we go to sleep, all is + lost anyway, and so interrupts don't really help us much anyway. + + Therefore, we don't use interrupts for communication with + the WaveFront synth. We just poll the relevant RX/TX status. + + However, there is one broad exception to this. During module + loading, to deal with several situations where timing would + be an issue, we use TX/RX interrupts to help us avoid busy + waiting for indeterminate and hard to manage periods of + time. So, TX/RX interrupts are enabled until the end of + wavefront_init(), and not used again after that. + + */ + + /* Note: data port is now the data port, not the h/w initialization + port. + + At this point, only "HW VERSION" or "DOWNLOAD OS" commands + will work. So, issue one of them, and wait for TX + interrupt. This can take a *long* time after a cold boot, + while the ISC ROM does its RAM test. The SDK says up to 4 + seconds - with 12MB of RAM on a Tropez+, it takes a lot + longer than that (~16secs). Note that the card understands + the difference between a warm and a cold boot, so + subsequent ISC2115 reboots (say, caused by module + reloading) will get through this much faster. + + Interesting question: why is no RX interrupt received first ? + */ + + wavefront_should_cause_interrupt(hw, WFC_HARDWARE_VERSION, + hw->data_port, 20*HZ); + + if (!hw->irq_ok) { + printk (KERN_WARNING + "WaveFront: post-RAM-check interrupt not received.\n"); + goto gone_bad; + } + + if (!(wavefront_status(hw) & STAT_CAN_READ)) { + printk (KERN_WARNING + "WaveFront: no response to HW version cmd.\n"); + goto gone_bad; + } + + if ((hwv[0] = wavefront_read (hw)) == -1) { + printk (KERN_WARNING + "WaveFront: board not responding correctly.\n"); + goto gone_bad; + } + + if (hwv[0] == 0xFF) { /* NAK */ + + /* Board's RAM test failed. Try to read error code, + and tell us about it either way. + */ + + if ((hwv[0] = wavefront_read (hw)) == -1) { + printk (KERN_WARNING + "WaveFront: on-board RAM test failed " + "(bad error code).\n"); + } else { + printk (KERN_WARNING + "WaveFront: on-board RAM test failed " + "(error code: 0x%x).\n", + hwv[0]); + } + goto gone_bad; + } + + /* We're OK, just get the next byte of the HW version response */ + + if ((hwv[1] = wavefront_read (hw)) == -1) { + printk (KERN_WARNING + "WaveFront: board not responding correctly(2).\n"); + goto gone_bad; + } + + printk (KERN_INFO "WaveFront: hardware version %d.%d\n", + hwv[0], hwv[1]); + + return 0; + + + gone_bad: + free_irq (hw->irq, hw); + return (1); + } + +int +probe_wavefront (struct address_info *hw_config) + +{ + unsigned char rbuf[4], wbuf[4]; + wf_config *hw; + + if (hw_config->irq < 0 || hw_config->irq > 16) { + printk (KERN_WARNING "WaveFront: impossible IRQ suggested(%d)\n", + hw_config->irq); + return 0; + } + + /* Yeah yeah, TB docs say 8, but the FX device on the Tropez Plus + takes up another 8 ... + */ + + if (check_region (hw_config->io_base, 16)) { + printk (KERN_ERR "WaveFront: IO address range 0x%x - 0x%x " + "already in use - ignored\n", hw_config->io_base, + hw_config->io_base+15); + return 0; + } + + hw = &wavefront_configuration; + + hw->irq = hw_config->irq; + hw->base = hw_config->io_base; + + hw->israw = 0; + hw->debug = debug_default; + hw->interrupts_on = 0; + hw->rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */ + +#ifdef WF_STATS + hw->status_found_during_sleep[0] = 0; + hw->status_found_during_sleep[1] = 0; + hw->status_found_during_sleep[2] = 0; + hw->status_found_during_sleep[3] = 0; + hw->status_found_during_loop = 0; +#endif WF_STATS + + hw_config->slots[WF_SYNTH_SLOT] = hw->synthdev = -1; + hw_config->slots[WF_INTERNAL_MIDI_SLOT] = hw->mididev = -1; + hw_config->slots[WF_EXTERNAL_MIDI_SLOT] = hw->ext_mididev = -1; + + if (wavefront_cmd (hw, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { + + hw->fw_version[0] = rbuf[0]; + hw->fw_version[1] = rbuf[1]; + printk (KERN_INFO + "WaveFront: firmware %d.%d already loaded.\n", + rbuf[0], rbuf[1]); + + /* check that a command actually works */ + + if (wavefront_cmd (hw, WFC_HARDWARE_VERSION, + rbuf, wbuf) == 0) { + hw->hw_version[0] = rbuf[0]; + hw->hw_version[1] = rbuf[1]; + } else { + printk (KERN_INFO "WaveFront: not raw, but no " + "hardware version!\n"); + return 0; + } + + if (!wf_raw) { + return 1; + } else { + printk (KERN_INFO + "WaveFront: reloading firmware anyway.\n"); + } + + } else { + + hw->israw = 1; + printk (KERN_INFO "WaveFront: no response to firmware probe, " + "assume raw.\n"); + + } + + init_waitqueue (&hw->interrupt_sleeper); + + if (wavefront_hw_reset (hw)) { + printk (KERN_WARNING "WaveFront: hardware reset failed\n"); + return 0; + } + + return 1; +} + +#include "os.h" +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include + +static int errno; + +static int +wavefront_download_firmware (wf_config *hw, char *path) + +{ + unsigned char section[WF_SECTION_MAX]; + char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ + int section_cnt_downloaded = 0; + int fd; + int c; + int i; + unsigned long fs; + + /* This tries to be a bit cleverer than the stuff Alan Cox did for + the generic sound firmware, in that it actually knows + something about the structure of the Motorola firmware. In + particular, it uses a version that has been stripped of the + 20K of useless header information, and had section lengths + added, making it possible to load the entire OS without any + [kv]malloc() activity, since the longest entity we ever read is + 42 bytes (well, WF_SECTION_MAX) long. + */ + + fs = get_fs(); + set_fs (get_ds()); + + if ((fd = sys_open (path, 0, 0)) < 0) { + printk (KERN_WARNING "WaveFront: Unable to load \"%s\".\n", + path); + return 1; + } + + while (1) { + int x; + + if ((x = sys_read (fd, §ion_length, sizeof (section_length))) != + sizeof (section_length)) { + printk (KERN_ERR "WaveFront: firmware read error.\n"); + goto failure; + } + + if (section_length == 0) { + break; + } + + if (sys_read (fd, section, section_length) != section_length) { + printk (KERN_ERR "WaveFront: firmware section " + "read error.\n"); + goto failure; + } + + /* Send command */ + + if (!wavefront_write (hw, WFC_DOWNLOAD_OS)) { + goto failure; + } + + for (i = 0; i < section_length; i++) { + if (!wavefront_write (hw, section[i])) { + goto failure; + } + } + + /* get ACK */ + + if (wavefront_wait (hw, STAT_CAN_READ)) { + + if ((c = inb (hw->data_port)) != WF_ACK) { + + printk (KERN_ERR "WaveFront: download " + "of section #%d not " + "acknowledged, ack = 0x%x\n", + section_cnt_downloaded + 1, c); + goto failure; + + } else { +#ifdef WF_DEBUG + if ((hw->debug & WF_DEBUG_IO) && + !(++section_cnt_downloaded % 10)) { + printk (KERN_DEBUG "."); + } +#endif WF_DEBUG + } + + } else { + printk (KERN_ERR "WaveFront: timed out " + "for download ACK.\n"); + } + + } + + sys_close (fd); + set_fs (fs); +#ifdef WF_DEBUG + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG "\n"); + } +#endif WF_DEBUG + return 0; + + failure: + sys_close (fd); + set_fs (fs); + printk (KERN_ERR "\nWaveFront: firmware download failed!!!\n"); + return 1; +} + +static int +wavefront_config_midi (wf_config *hw, struct address_info *hw_config) + +{ + unsigned char rbuf[4], wbuf[4]; + + if (!probe_wf_mpu (hw_config)) { + printk (KERN_WARNING "WaveFront: could not install " + "MPU-401 device.\n"); + return 1; + } + + /* Attach an modified MPU-401 driver to the master MIDI interface */ + + hw_config->name = "WaveFront Internal MIDI"; + attach_wf_mpu (hw_config); + + if (hw_config->slots[WF_INTERNAL_MIDI_SLOT] == -1) { + printk (KERN_WARNING "WaveFront: MPU-401 not configured.\n"); + return 1; + } + + hw->mididev = hw_config->slots[WF_INTERNAL_MIDI_SLOT]; + + /* Route external MIDI to WaveFront synth (by default) */ + + if (wavefront_cmd (hw, WFC_MISYNTH_ON, rbuf, wbuf)) { + printk (KERN_WARNING + "WaveFront: cannot enable MIDI-IN to synth routing.\n"); + /* XXX error ? */ + } + + /* Get the regular MIDI patch loading function, so we can + use it if we ever get handed a SYSEX patch. This is + unlikely, because its so damn slow, but we may as well + leave this functionality from maui.c behind, since it + could be useful for sequencer applications that can + only use MIDI to do patch loading. + */ + + if (midi_devs[hw->mididev]->converter != NULL) { + midi_load_patch = midi_devs[hw->mididev]->converter->load_patch; + midi_devs[hw->mididev]->converter->load_patch = + &wavefront_load_patch; + } + + /* Turn on Virtual MIDI, but first *always* turn it off, + since otherwise consectutive reloads of the driver will + never cause the hardware to generate the initial "internal" or + "external" source bytes in the MIDI data stream. This + is pretty important, since the internal hardware generally will + be used to generate none or very little MIDI output, and + thus the only source of MIDI data is actually external. Without + the switch bytes, the driver will think it all comes from + the internal interface. Duh. + */ + + if (wavefront_cmd (hw, WFC_VMIDI_OFF, rbuf, wbuf)) { + printk (KERN_WARNING "WaveFront: cannot disable " + "virtual MIDI mode\n"); + /* XXX go ahead and try anyway ? */ + } + + hw_config->name = "WaveFront External MIDI"; + + if (virtual_midi_enable (hw->mididev, hw_config)) { + printk (KERN_WARNING "WaveFront: no virtual MIDI access.\n"); + } else { + hw->ext_mididev = hw_config->slots[WF_EXTERNAL_MIDI_SLOT]; + if (wavefront_cmd (hw, WFC_VMIDI_ON, rbuf, wbuf)) { + printk (KERN_WARNING + "WaveFront: cannot enable virtual MIDI mode.\n"); + virtual_midi_disable (hw->mididev); + } + } + + return 0; +} + +static int +wavefront_do_reset (wf_config *hw, int atboot) + +{ + char voices[1]; + + if (!atboot && wavefront_hw_reset (hw)) { + printk (KERN_WARNING "WaveFront: hw reset failed.\n"); + goto gone_bad; + } + + if (hw->israw || wf_raw) { + if (wavefront_download_firmware (hw, ospath)) { + goto gone_bad; + return 1; + } + } + + if (fx_raw) { + wffx_init (hw); + } + + /* If we loaded the OS, we now have to wait for it to be ready + to roll. We can't guarantee that interrupts are enabled, + because we might be reloading the module without forcing a + reset/reload of the firmware. + + Rather than busy-wait, lets just turn interrupts on. + */ + + outb (0x80|0x40|0x10|0x1, hw->control_port); + + wavefront_should_cause_interrupt (hw, WFC_NOOP, + hw->data_port, (10*HZ)); + + if (!hw->irq_ok) { + printk (KERN_WARNING "WaveFront: no post-OS interrupt.\n"); + goto gone_bad; + } + + /* Now, do it again ! */ + + wavefront_should_cause_interrupt (hw, WFC_NOOP, + hw->data_port, (10*HZ)); + + if (!hw->irq_ok) { + printk (KERN_WARNING "WaveFront: no post-OS interrupt(2).\n"); + goto gone_bad; + } + + /* OK, no (RX/TX) interrupts any more, but leave mute + on. Master interrupts get enabled when we're done here. + */ + + outb (0x80, hw->control_port); + + /* No need for the IRQ anymore */ + + free_irq (hw->irq, hw); + + /* SETUPSND.EXE asks for sample memory config here, but since i + have no idea how to interpret the result, we'll forget + about it. + */ + + if ((hw->freemem = wavefront_freemem (hw)) < 0) { + goto gone_bad; + } + + printk (KERN_INFO "WaveFront: available DRAM %dk\n", hw->freemem / 1024); + + if (!wavefront_write (hw, 0xf0) || + !wavefront_write (hw, 1) || + (wavefront_read (hw) < 0)) { + hw->debug = 0; + printk (KERN_WARNING "WaveFront: MPU emulation mode not set.\n"); + goto gone_bad; + } + + voices[0] = 32; + + if (wavefront_cmd (hw, WFC_SET_NVOICES, 0, voices)) { + printk (KERN_WARNING + "WaveFront: cannot set number of voices to 32.\n"); + } + + return 0; + + gone_bad: + /* reset that sucker so that it doesn't bother us. */ + + outb (0x0, hw->control_port); + free_irq (hw->irq, hw); + return 1; +} + +static int +wavefront_init (wf_config *hw, int atboot) + +{ + int samples_are_from_rom; + + if (hw->israw || wf_raw) { + samples_are_from_rom = 1; + } else { + samples_are_from_rom = 0; + } + + if (hw->israw || wf_raw || fx_raw) { + if (wavefront_do_reset (hw, atboot)) { + return 1; + } + } + + wavefront_get_sample_status (hw, samples_are_from_rom); + wavefront_get_program_status (hw); + wavefront_get_patch_status (hw); + + /* Start normal operation: unreset, master interrupt enable + (for MPU interrupts) no mute + */ + + outb (0x80|0x40|0x20, hw->control_port); + + return (0); +} + +void +attach_wavefront (struct address_info *hw_config) +{ + int i; + struct wf_config *hw = &wavefront_configuration; + + if ((i = sound_alloc_synthdev()) == -1) { + printk (KERN_ERR "WaveFront: Too many synthesizers\n"); + return; + } else { + hw_config->slots[WF_SYNTH_SLOT] = i; + hw->synthdev = i; + synth_devs[hw->synthdev] = &wavefront_operations; + } + + if (wavefront_init (hw, 1)) { + printk (KERN_WARNING "WaveFront: board could not " + "be initialized.\n"); + sound_unload_synthdev (i); + return; + } + + request_region (hw_config->io_base+2, 6, "WaveFront synth"); + request_region (hw_config->io_base+8, 8, "WaveFront FX"); + + conf_printf2 ("WaveFront Synth", hw_config->io_base, 0, -1, -1); + +#if defined(CONFIG_MIDI) + if (wavefront_config_midi (hw, hw_config)) { + printk (KERN_WARNING "WaveFront: could not initialize MIDI.\n"); + } +#else + printk (KERN_WARNING + "WaveFront: MIDI not configured at kernel-config time.\n"); +#endif CONFIG_MIDI + + return; +} +void +unload_wavefront (struct address_info *hw_config) +{ + struct wf_config *hw = &wavefront_configuration; + + /* the first two are freed by the wf_mpu code */ + release_region (hw->base+2, 6); + release_region (hw->base+8, 8); + sound_unload_synthdev (hw->synthdev); +#if defined(CONFIG_MIDI) + unload_wf_mpu (hw_config); +#endif +} + +/***********************************************************************/ +/* WaveFront FX control */ +/***********************************************************************/ + +#include "yss225.h" + +/* Control bits for the Load Control Register + */ + +#define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ +#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ +#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ + +static int +wffx_idle (struct wf_config *hw) + +{ + int i; + unsigned int x = 0x80; + + for (i = 0; i < 1000; i++) { + x = inb (hw->fx_status); + if ((x & 0x80) == 0) { + break; + } + } + + if (x & 0x80) { + printk (KERN_ERR "WaveFront: FX device never idle.\n"); + return 0; + } + + return (1); +} + +static void +wffx_mute (struct wf_config *hw, int onoff) + +{ + if (!wffx_idle(hw)) { + return; + } + + outb (onoff ? 0x02 : 0x00, hw->fx_op); +} + +static int +wffx_memset (struct wf_config *hw, int page, + int addr, int cnt, unsigned short *data) +{ + if (page < 0 || page > 7) { + printk (KERN_ERR "WaveFront: FX memset: " + "page must be >= 0 and <= 7\n"); + return -(EINVAL); + } + + if (addr < 0 || addr > 0x7f) { + printk (KERN_ERR "WaveFront: FX memset: " + "addr must be >= 0 and <= 7f\n"); + return -(EINVAL); + } + + if (cnt == 1) { + + outb (FX_LSB_TRANSFER, hw->fx_lcr); + outb (page, hw->fx_dsp_page); + outb (addr, hw->fx_dsp_addr); + outb ((data[0] >> 8), hw->fx_dsp_msb); + outb ((data[0] & 0xff), hw->fx_dsp_lsb); + + printk (KERN_INFO "WaveFront: FX: addr %d:%x set to 0x%x\n", + page, addr, data[0]); + + } else { + int i; + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (page, hw->fx_dsp_page); + outb (addr, hw->fx_dsp_addr); + + for (i = 0; i < cnt; i++) { + outb ((data[i] >> 8), hw->fx_dsp_msb); + outb ((data[i] & 0xff), hw->fx_dsp_lsb); + if (!wffx_idle (hw)) { + break; + } + } + + if (i != cnt) { + printk (KERN_WARNING + "WaveFront: FX memset " + "(0x%x, 0x%x, 0x%x, %d) incomplete\n", + page, addr, (int) data, cnt); + return -(EIO); + } + } + + return 0; +} + +static int +wffx_ioctl (struct wf_config *hw, wavefront_fx_info *r) + +{ + unsigned short page_data[256]; + unsigned short *pd; + + switch (r->request) { + case WFFX_MUTE: + wffx_mute (hw, r->data[0]); + return 0; + + case WFFX_MEMSET: + + if (r->data[2] <= 0) { + printk (KERN_ERR "WaveFront: cannot write " + "<= 0 bytes to FX\n"); + return -(EINVAL); + } else if (r->data[2] == 1) { + pd = (unsigned short *) &r->data[3]; + } else { + if (r->data[2] > sizeof (page_data)) { + printk (KERN_ERR "WaveFront: cannot write " + "> 255 bytes to FX\n"); + return -(EINVAL); + } + copy_from_user (page_data, (unsigned char *) r->data[3], + r->data[2]); + pd = page_data; + } + + return wffx_memset (hw, + r->data[0], /* page */ + r->data[1], /* addr */ + r->data[2], /* cnt */ + pd); + + default: + printk (KERN_WARNING + "WaveFront: FX: ioctl %d not yet supported\n", + r->request); + return -(EINVAL); + } +} + +/* YSS225 initialization. + + This code was developed using DOSEmu. The Turtle Beach SETUPSND + utility was run with I/O tracing in DOSEmu enabled, and a reconstruction + of the port I/O done, using the Yamaha faxback document as a guide + to add more logic to the code. It's really pretty weird. + + There was an alternative approach of just dumping the whole I/O + sequence as a series of port/value pairs and a simple loop + that output it. However, I hope that eventually I'll get more + control over what this code does, and so I tried to stick with + a somewhat "algorithmic" approach. +*/ + +static int +wffx_init (struct wf_config *hw) + +{ + int i; + int j; + + /* Set all bits for all channels on the MOD unit to zero */ + /* XXX But why do this twice ? */ + + for (j = 0; j < 2; j++) { + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle (hw)) { + return (-1); + } + + outb (i, hw->fx_mod_addr); + outb (0x0, hw->fx_mod_data); + } + } + + if (!wffx_idle(hw)) return (-1); + outb (0x02, hw->fx_op); /* mute on */ + + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x44, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x42, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x43, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x7c, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x7e, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x46, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x49, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x47, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x4a, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + + /* either because of stupidity by TB's programmers, or because it + actually does something, rezero the MOD page. + */ + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle (hw)) { + return (-1); + } + + outb (i, hw->fx_mod_addr); + outb (0x0, hw->fx_mod_data); + } + /* load page zero */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x00, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero); i += 2) { + outb (page_zero[i], hw->fx_dsp_msb); + outb (page_zero[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Now load page one */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x01, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one); i += 2) { + outb (page_one[i], hw->fx_dsp_msb); + outb (page_one[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x02, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two); i++) { + outb (page_two[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x03, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three); i++) { + outb (page_three[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x04, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four); i++) { + outb (page_four[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Load memory area (page six) */ + + outb (FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x06, hw->fx_dsp_page); + + for (i = 0; i < sizeof (page_six); i += 3) { + outb (page_six[i], hw->fx_dsp_addr); + outb (page_six[i+1], hw->fx_dsp_msb); + outb (page_six[i+2], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x07, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven); i += 2) { + outb (page_seven[i], hw->fx_dsp_msb); + outb (page_seven[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Now setup the MOD area. We do this algorithmically in order to + save a little data space. It could be done in the same fashion + as the "pages". + */ + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, hw->fx_mod_addr); + outb (i, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0x02, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0xb0; i <= 0xbf; i++) { + outb (i, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0xf0; i <= 0xff; i++) { + outb (i, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x10; i <= 0x1d; i++) { + outb (i, hw->fx_mod_addr); + outb (0xff, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x1e, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x1f; i <= 0x2d; i++) { + outb (i, hw->fx_mod_addr); + outb (0xff, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x2e, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x2f; i <= 0x3e; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x3f, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x40; i <= 0x4d; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x4e, hw->fx_mod_addr); + outb (0x0e, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0x4f, hw->fx_mod_addr); + outb (0x0e, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + + for (i = 0x50; i <= 0x6b; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x6c, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + outb (0x6d, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + outb (0x6e, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + outb (0x6f, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x70; i <= 0x7f; i++) { + outb (i, hw->fx_mod_addr); + outb (0xc0, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x80; i <= 0xaf; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0xc0; i <= 0xdd; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0xde, hw->fx_mod_addr); + outb (0x10, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0xdf, hw->fx_mod_addr); + outb (0x10, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0xe0; i <= 0xef; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, hw->fx_mod_addr); + outb (i, hw->fx_mod_data); + outb (0x02, hw->fx_mod_addr); + outb (0x01, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x02, hw->fx_op); /* mute on */ + + /* Now set the coefficients and so forth for the programs above */ + + for (i = 0; i < sizeof (coefficients); i += 4) { + outb (coefficients[i], hw->fx_dsp_page); + outb (coefficients[i+1], hw->fx_dsp_addr); + outb (coefficients[i+2], hw->fx_dsp_msb); + outb (coefficients[i+3], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Some settings (?) that are too small to bundle into loops */ + + if (!wffx_idle(hw)) return (-1); + outb (0x1e, hw->fx_mod_addr); + outb (0x14, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0xde, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0xdf, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + + /* some more coefficients */ + + if (!wffx_idle(hw)) return (-1); + outb (0x06, hw->fx_dsp_page); + outb (0x78, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x40, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x03, hw->fx_dsp_addr); + outb (0x0f, hw->fx_dsp_msb); + outb (0xff, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x0b, hw->fx_dsp_addr); + outb (0x0f, hw->fx_dsp_msb); + outb (0xff, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x02, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x0a, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x46, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x49, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + + /* Now, for some strange reason, lets reload every page + and all the coefficients over again. I have *NO* idea + why this is done. I do know that no sound is produced + is this phase is omitted. + */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x00, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero_v2); i += 2) { + outb (page_zero_v2[i], hw->fx_dsp_msb); + outb (page_zero_v2[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x01, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one_v2); i += 2) { + outb (page_one_v2[i], hw->fx_dsp_msb); + outb (page_one_v2[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + if (!wffx_idle(hw)) return (-1); + if (!wffx_idle(hw)) return (-1); + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x02, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two_v2); i++) { + outb (page_two_v2[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x03, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three_v2); i++) { + outb (page_three_v2[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x04, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four_v2); i++) { + outb (page_four_v2[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x06, hw->fx_dsp_page); + + /* Page six v.2 is algorithmic */ + + for (i = 0x10; i <= 0x3e; i += 2) { + outb (i, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x07, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven_v2); i += 2) { + outb (page_seven_v2[i], hw->fx_dsp_msb); + outb (page_seven_v2[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x00; i < sizeof(mod_v2); i += 2) { + outb (mod_v2[i], hw->fx_mod_addr); + outb (mod_v2[i+1], hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0; i < sizeof (coefficients2); i += 4) { + outb (coefficients2[i], hw->fx_dsp_page); + outb (coefficients2[i+1], hw->fx_dsp_addr); + outb (coefficients2[i+2], hw->fx_dsp_msb); + outb (coefficients2[i+3], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x00, hw->fx_op); + if (!wffx_idle(hw)) return (-1); + + for (i = 0; i < sizeof (coefficients3); i += 2) { + int x; + + outb (0x07, hw->fx_dsp_page); + x = (i % 4) ? 0x4e : 0x4c; + outb (x, hw->fx_dsp_addr); + outb (coefficients3[i], hw->fx_dsp_msb); + outb (coefficients3[i+1], hw->fx_dsp_lsb); + } + + outb (0x00, hw->fx_op); /* mute off */ + + return (0); +} + +struct address_info cfg; + +int io = -1; +int irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); + +int init_module (void) + +{ + printk ("Turtle Beach WaveFront Driver\n" + "Copyright (C) by Hannu Savolainen, " + "Paul Barton-Davis 1993-1998.\n"); + + if (io == -1 || irq == -1) { + printk (KERN_INFO "WaveFront: irq and io " + "options must be set.\n"); + return -EINVAL; + } + + cfg.io_base = io; + cfg.irq = irq; + + if (probe_wavefront (&cfg) == 0) { + return -ENODEV; + } + attach_wavefront (&cfg); + SOUND_LOCK; + return 0; +} + +void cleanup_module (void) + +{ + unload_wavefront (&cfg); + SOUND_LOCK_END; +} + +#endif CONFIG_SOUND_WAVEFRONT_MODULE_AND_MODULE + + diff --git a/drivers/sound/wf_midi.c b/drivers/sound/wf_midi.c new file mode 100644 index 000000000000..0cef53cccb8b --- /dev/null +++ b/drivers/sound/wf_midi.c @@ -0,0 +1,982 @@ +/* + * sound/wf_midi.c + * + * The low level driver for the WaveFront ICS2115 MIDI interface(s) + * Note that there is also an MPU-401 emulation (actually, a UART-401 + * emulation) on the CS4232 on the Tropez Plus. This code has nothing + * to do with that interface at all. + * + * The interface is essentially just a UART-401, but is has the + * interesting property of supporting what Turtle Beach called + * "Virtual MIDI" mode. In this mode, there are effectively *two* + * MIDI buses accessible via the interface, one that is routed + * solely to/from the external WaveFront synthesizer and the other + * corresponding to the pin/socket connector used to link external + * MIDI devices to the board. + * + * This driver fully supports this mode, allowing two distinct + * midi devices (/dev/midiNN and /dev/midiNN+1) to be used + * completely independently, giving 32 channels of MIDI routing, + * 16 to the WaveFront synth and 16 to the external MIDI bus. + * + * Switching between the two is accomplished externally by the driver + * using the two otherwise unused MIDI bytes. See the code for more details. + * + * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see wavefront.c) + * + * The main reason to turn off Virtual MIDI mode is when you want to + * tightly couple the WaveFront synth with an external MIDI + * device. You won't be able to distinguish the source of any MIDI + * data except via SysEx ID, but thats probably OK, since for the most + * part, the WaveFront won't be sending any MIDI data at all. + * + * The main reason to turn on Virtual MIDI Mode is to provide two + * completely independent 16-channel MIDI buses, one to the + * WaveFront and one to any external MIDI devices. Given the 32 + * voice nature of the WaveFront, its pretty easy to find a use + * for all 16 channels driving just that synth. + * + */ + +/* + * Copyright (C) by Paul Barton-Davis 1998 + * Substantial portions of this file are derived from work that is: + * + * 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. + */ + +#include +#include "sound_config.h" +#include "soundmodule.h" + +#include + +#if (defined(CONFIG_WAVEFRONT) && defined(CONFIG_MIDI)) || defined(MODULE) + +struct wf_mpu_config { + int base; /* I/O base */ + int irq; + int opened; /* Open mode */ + int devno; + int synthno; + int mode; +#define MODE_MIDI 1 +#define MODE_SYNTH 2 + + void (*inputintr) (int dev, unsigned char data); + + /* Virtual MIDI support */ + + char configured_for_virtual; /* setup for virtual completed */ + char isvirtual; /* do virtual I/O stuff */ + char isexternal; /* i am an external interface */ + int internal; /* external interface midi_devno */ + int external; /* external interface midi_devno */ +}; + +#define DATAPORT(base) (base) +#define COMDPORT(base) (base+1) +#define STATPORT(base) (base+1) + +static void start_uart_mode (struct wf_mpu_config *devc); + +static int +wf_mpu_status (struct wf_mpu_config *devc) +{ + return inb (STATPORT (devc->base)); +} + +static void +wf_mpu_cmd (struct wf_mpu_config *devc, unsigned char cmd) +{ + outb ((cmd), COMDPORT(devc->base)); +} + +#define input_avail(devc) (!(wf_mpu_status(devc)&INPUT_AVAIL)) +#define output_ready(devc) (!(wf_mpu_status(devc)&OUTPUT_READY)) + +static int +read_data (struct wf_mpu_config *devc) +{ + return inb (DATAPORT (devc->base)); +} + +static void +write_data (struct wf_mpu_config *devc, unsigned char byte) +{ + outb (byte, DATAPORT (devc->base)); +} + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define MPU_RESET 0xFF +#define UART_MODE_ON 0x3F + +static struct wf_mpu_config dev_conf[MAX_MIDI_DEV] = +{ + {0} +}; + +static volatile int irq2dev[17] = +{-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +static struct synth_info wf_mpu_synth_info_proto = +{"WaveFront MPU-401 interface", 0, + SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT}; + +static struct synth_info wf_mpu_synth_info[MAX_MIDI_DEV]; + +/* + * States for the input scanner (should be in dev_table.h) + */ + +#define MST_SYSMSG 100 /* System message (sysx etc). */ +#define MST_MTC 102 /* Midi Time Code (MTC) qframe msg */ +#define MST_SONGSEL 103 /* Song select */ +#define MST_SONGPOS 104 /* Song position pointer */ +#define MST_TIMED 105 /* Leading timing byte rcvd */ + +/* buffer space check for input scanner */ + +#define BUFTEST(mi) if (mi->m_ptr >= MI_MAX || mi->m_ptr < 0) \ +{printk(KERN_ERR "WF-MPU: Invalid buffer pointer %d/%d, s=%d\n", \ + mi->m_ptr, mi->m_left, mi->m_state);mi->m_ptr--;} + +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 */ +}; + +static int +wf_mpu_input_scanner (struct wf_mpu_config *devc, unsigned char midic) +{ + struct midi_input_info *mi; + + mi = &midi_devs[devc->devno]->in_info; + + switch (mi->m_state) { + case MST_INIT: + switch (midic) { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + break; + + case 0xfd: + /* XXX do something useful with this. If there is + an external MIDI timer (e.g. a hardware sequencer, + a useful timer can be derived ... + + For now, no timer support. + */ + break; + + case 0xfe: + return MPU_ACK; + break; + + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + break; + + case 0xf9: + break; + + case 0xff: + mi->m_state = MST_SYSMSG; + break; + + default: + if (midic <= 0xef) { + mi->m_state = MST_TIMED; + } + else + printk (KERN_ERR " ", + midic); + } + break; + + case MST_TIMED: + { + int msg = ((int) (midic & 0xf0) >> 4); + + mi->m_state = MST_DATA; + + if (msg < 8) { /* Data byte */ + + msg = ((int) (mi->m_prev_status & 0xf0) >> 4); + msg -= 8; + mi->m_left = len_tab[msg] - 1; + + mi->m_ptr = 2; + mi->m_buf[0] = mi->m_prev_status; + mi->m_buf[1] = midic; + + if (mi->m_left <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (devc->synthno, mi->m_buf, + mi->m_ptr); + mi->m_ptr = 0; + } + } else if (msg == 0xf) { /* MPU MARK */ + + mi->m_state = MST_INIT; + + switch (midic) { + case 0xf8: + break; + + case 0xf9: + break; + + case 0xfc: + break; + + default: + } + } else { + mi->m_prev_status = midic; + msg -= 8; + mi->m_left = len_tab[msg]; + + mi->m_ptr = 1; + mi->m_buf[0] = midic; + + if (mi->m_left <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (devc->synthno, mi->m_buf, + mi->m_ptr); + mi->m_ptr = 0; + } + } + } + break; + + case MST_SYSMSG: + switch (midic) { + case 0xf0: + mi->m_state = MST_SYSEX; + break; + + case 0xf1: + mi->m_state = MST_MTC; + break; + + case 0xf2: + mi->m_state = MST_SONGPOS; + mi->m_ptr = 0; + break; + + case 0xf3: + mi->m_state = MST_SONGSEL; + break; + + case 0xf6: + mi->m_state = MST_INIT; + + /* + * Real time messages + */ + case 0xf8: + /* midi clock */ + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xfA: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFB: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFC: + mi->m_state = MST_INIT; + /* XXX need ext MIDI timer support */ + break; + + case 0xFE: + /* active sensing */ + mi->m_state = MST_INIT; + break; + + case 0xff: + mi->m_state = MST_INIT; + break; + + default: + printk (KERN_ERR "unknown MIDI sysmsg %0x\n", midic); + mi->m_state = MST_INIT; + } + break; + + case MST_MTC: + mi->m_state = MST_INIT; + break; + + case MST_SYSEX: + if (midic == 0xf7) { + mi->m_state = MST_INIT; + } else { + /* XXX fix me */ + } + break; + + case MST_SONGPOS: + BUFTEST (mi); + mi->m_buf[mi->m_ptr++] = midic; + if (mi->m_ptr == 2) { + mi->m_state = MST_INIT; + mi->m_ptr = 0; + /* XXX need ext MIDI timer support */ + } + break; + + case MST_DATA: + BUFTEST (mi); + mi->m_buf[mi->m_ptr++] = midic; + if ((--mi->m_left) <= 0) { + mi->m_state = MST_INIT; + do_midi_msg (devc->synthno, mi->m_buf, mi->m_ptr); + mi->m_ptr = 0; + } + break; + + default: + printk (KERN_ERR "Bad state %d ", mi->m_state); + mi->m_state = MST_INIT; + } + + return 1; +} + +void +wf_mpuintr (int irq, void *dev_id, struct pt_regs *dummy) +{ + struct wf_mpu_config *devc; + int dev; + static struct wf_mpu_config *isrc = 0; + int n; + struct midi_input_info *mi; + + if (irq < 0 || irq > 15) { + printk (KERN_ERR "WF-MPU: bogus interrupt #%d", irq); + return; + } + dev = irq2dev[irq]; + mi = &midi_devs[dev]->in_info; + if (mi->m_busy) return; + mi->m_busy = 1; + sti (); + + n = 50; + + /* guarantee that we're working with the "real" (internal) + interface before doing anything physical. + */ + + devc = &dev_conf[dev]; + devc = &dev_conf[devc->internal]; + + if (isrc == 0) { + + /* This is just an initial setting. If Virtual MIDI mode is + enabled on the ICS2115, we'll get a switch char before + anything else, and if it isn't, then the guess will be + correct for our purposes. + */ + + isrc = &dev_conf[devc->internal]; + } + + while (input_avail (devc) && n-- > 0) { + unsigned char c = read_data (devc); + + if (devc->isvirtual) { + if (c == WF_EXTERNAL_SWITCH) { + isrc = &dev_conf[devc->external]; + continue; + } else if (c == WF_INTERNAL_SWITCH) { + isrc = &dev_conf[devc->internal]; + continue; + } /* else just leave it as it is */ + } else { + isrc = &dev_conf[devc->internal]; + } + + if (isrc->mode == MODE_SYNTH) { + + wf_mpu_input_scanner (isrc, c); + + } else if (isrc->opened & OPEN_READ) { + + if (isrc->inputintr) { + isrc->inputintr (isrc->devno, c); + } + } + } + + mi->m_busy = 0; +} + +static int +wf_mpu_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) + ) +{ + struct wf_mpu_config *devc; + + if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) + return -(ENXIO); + + devc = &dev_conf[dev]; + + if (devc->opened) { + return -(EBUSY); + } + + devc->mode = MODE_MIDI; + devc->opened = mode; + devc->synthno = 0; + + devc->inputintr = input; + return 0; +} + +static void +wf_mpu_close (int dev) +{ + struct wf_mpu_config *devc; + + devc = &dev_conf[dev]; + devc->mode = 0; + devc->inputintr = NULL; + devc->opened = 0; +} + +static int +wf_mpu_out (int dev, unsigned char midi_byte) +{ + int timeout; + unsigned long flags; + static int lastoutdev = -1; + + struct wf_mpu_config *devc; + unsigned char switchch; + + /* The actual output has to occur using the "internal" config info + */ + + devc = &dev_conf[dev_conf[dev].internal]; + + if (devc->isvirtual && lastoutdev != dev) { + + if (dev == devc->internal) { + switchch = WF_INTERNAL_SWITCH; + } else if (dev == devc->external) { + switchch = WF_EXTERNAL_SWITCH; + } else { + printk (KERN_ERR "WF-MPU: bad device number %d", dev); + return (0); + } + + for (timeout = 30000; timeout > 0 && !output_ready (devc); + timeout--); + + save_flags (flags); + cli (); + + if (!output_ready (devc)) { + printk (KERN_WARNING "WF-MPU: Send switch " + "byte timeout\n"); + restore_flags (flags); + return 0; + } + + write_data (devc, switchch); + restore_flags (flags); + } + + lastoutdev = dev; + + /* + * Sometimes it takes about 30000 loops before the output becomes ready + * (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); + + save_flags (flags); + cli (); + if (!output_ready (devc)) { + printk (KERN_WARNING "WF-MPU: Send data timeout\n"); + restore_flags (flags); + return 0; + } + + write_data (devc, midi_byte); + restore_flags (flags); + + return 1; +} + +static int +wf_mpu_start_read (int dev) +{ + return 0; +} + +static int +wf_mpu_end_read (int dev) +{ + return 0; +} + +static int +wf_mpu_ioctl (int dev, unsigned cmd, caddr_t arg) +{ + printk (KERN_WARNING + "WF-MPU: Intelligent mode not supported by hardware.\n"); + return -(EINVAL); +} + +static void +wf_mpu_kick (int dev) +{ +} + +static int +wf_mpu_buffer_status (int dev) +{ + return 0; /* + * No data in buffers + */ +} + +static int +wf_mpu_synth_ioctl (int dev, + unsigned int cmd, caddr_t arg) +{ + int midi_dev; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) + return -(ENXIO); + + switch (cmd) { + + case SNDCTL_SYNTH_INFO: + copy_to_user (&((char *) arg)[0], + &wf_mpu_synth_info[midi_dev], + sizeof (struct synth_info)); + + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + break; + + default: + return -(EINVAL); + } +} + +static int +wf_mpu_synth_open (int dev, int mode) +{ + int midi_dev; + struct wf_mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) { + return -(ENXIO); + } + + devc = &dev_conf[midi_dev]; + if (devc->opened) { + return -(EBUSY); + } + + devc->mode = MODE_SYNTH; + devc->synthno = dev; + devc->opened = mode; + devc->inputintr = NULL; + return 0; +} + +static void +wf_mpu_synth_close (int dev) +{ + int midi_dev; + struct wf_mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + devc = &dev_conf[midi_dev]; + devc->inputintr = NULL; + devc->opened = 0; + devc->mode = 0; +} + +#define MIDI_SYNTH_NAME "WaveFront (MIDI)" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +static struct synth_operations wf_mpu_synth_proto = +{ + "WaveFront (ICS2115)", + NULL, /* info field, filled in during configuration */ + 0, /* MIDI dev XXX should this be -1 ? */ + SYNTH_TYPE_MIDI, + SAMPLE_TYPE_WAVEFRONT, + wf_mpu_synth_open, + wf_mpu_synth_close, + wf_mpu_synth_ioctl, + midi_synth_kill_note, + midi_synth_start_note, + midi_synth_set_instr, + midi_synth_reset, + midi_synth_hw_control, + midi_synth_load_patch, + midi_synth_aftertouch, + midi_synth_controller, + midi_synth_panning, + NULL, + midi_synth_bender, + NULL, /* alloc */ + midi_synth_setup_voice, + midi_synth_send_sysex +}; + +static struct synth_operations wf_mpu_synth_operations[2]; +static struct midi_operations wf_mpu_midi_operations[2]; +static int wfmpu_cnt = 0; + +static struct midi_operations wf_mpu_midi_proto = +{ + {"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, + NULL, /*converter*/ + {0}, /* in_info */ + wf_mpu_open, + wf_mpu_close, + wf_mpu_ioctl, + wf_mpu_out, + wf_mpu_start_read, + wf_mpu_end_read, + wf_mpu_kick, + NULL, + wf_mpu_buffer_status, + NULL +}; + + +static int +config_wf_mpu (int dev, struct address_info *hw_config) + +{ + struct wf_mpu_config *devc; + int internal; + + if (wfmpu_cnt >= 2) { + printk (KERN_ERR "WF-MPU: more MPU devices than cards ?!!\n"); + return (-1); + } + + /* There is no synth available on the external interface, + so do the synth stuff to the internal interface only. + */ + + internal = dev_conf[dev].internal; + devc = &dev_conf[internal]; + + if (!dev_conf[dev].isexternal) { + memcpy ((char *) &wf_mpu_synth_operations[wfmpu_cnt], + (char *) &wf_mpu_synth_proto, + sizeof (struct synth_operations)); + } + + memcpy ((char *) &wf_mpu_midi_operations[wfmpu_cnt], + (char *) &wf_mpu_midi_proto, + sizeof (struct midi_operations)); + + if (dev_conf[dev].isexternal) { + wf_mpu_midi_operations[wfmpu_cnt].converter = NULL; + } else { + wf_mpu_midi_operations[wfmpu_cnt].converter = + &wf_mpu_synth_operations[wfmpu_cnt]; + } + + memcpy ((char *) &wf_mpu_synth_info[dev], + (char *) &wf_mpu_synth_info_proto, + sizeof (struct synth_info)); + + strcpy (wf_mpu_synth_info[dev].name, hw_config->name); + strcpy (wf_mpu_midi_operations[wfmpu_cnt].info.name, hw_config->name); + + conf_printf (hw_config->name, hw_config); + + if (!dev_conf[dev].isexternal) { + wf_mpu_synth_operations[wfmpu_cnt].midi_dev = dev; + } + wf_mpu_synth_operations[wfmpu_cnt].info = &wf_mpu_synth_info[dev]; + + midi_devs[dev] = &wf_mpu_midi_operations[wfmpu_cnt]; + + dev_conf[dev].opened = 0; + dev_conf[dev].mode = 0; + dev_conf[dev].configured_for_virtual = 0; + dev_conf[dev].devno = dev; + + midi_devs[dev]->in_info.m_busy = 0; + midi_devs[dev]->in_info.m_state = MST_INIT; + midi_devs[dev]->in_info.m_ptr = 0; + midi_devs[dev]->in_info.m_left = 0; + midi_devs[dev]->in_info.m_prev_status = 0; + + wfmpu_cnt++; + + return (0); +} + +int +virtual_midi_enable (int dev, struct address_info *hw_config) + +{ + int idev; + int edev; + struct wf_mpu_config *devc; + + devc = &dev_conf[dev]; + + if (devc->configured_for_virtual) { + + idev = devc->internal; + edev = devc->external; + + } else { + + if (hw_config == NULL) { + printk (KERN_ERR + "WF-MPU: virtual midi first " + "enabled without hw_config!\n"); + return -EINVAL; + } + + idev = devc->internal; + + if ((edev = sound_alloc_mididev()) == -1) { + printk (KERN_ERR + "WF-MPU: too many midi devices detected\n"); + return -1; + } + + hw_config->slots[WF_EXTERNAL_MIDI_SLOT] = edev; + } + + dev_conf[edev].isvirtual = 1; + dev_conf[idev].isvirtual = 1; + + if (dev_conf[idev].configured_for_virtual) { + return 0; + } + + /* Configure external interface struct */ + + devc = &dev_conf[edev]; + devc->internal = idev; + devc->external = edev; + devc->isexternal = 1; + + /* Configure external interface struct + (devc->isexternal and devc->internal set in attach_wf_mpu()) + */ + + devc = &dev_conf[idev]; + devc->external = edev; + + /* Configure the tables for the external */ + + if (config_wf_mpu (edev, hw_config)) { + printk (KERN_WARNING "WF-MPU: configuration for MIDI " + "device %d failed\n", edev); + return (-1); + } + + /* Don't bother to do this again if we are toggled back and + forth between virtual MIDI mode and "normal" operation. + */ + + dev_conf[edev].configured_for_virtual = 1; + dev_conf[idev].configured_for_virtual = 1; + + return 0; +} + +void +virtual_midi_disable (int dev) + +{ + struct wf_mpu_config *devc; + unsigned long flags; + + save_flags (flags); + cli(); + + /* Assumes for logical purposes that the caller has taken + care of fiddling with WaveFront hardware commands to + turn off Virtual MIDI mode. + */ + + devc = &dev_conf[dev]; + + devc = &dev_conf[devc->internal]; + devc->isvirtual = 0; + + devc = &dev_conf[devc->external]; + devc->isvirtual = 0; + + restore_flags (flags); +} + +void +attach_wf_mpu (struct address_info *hw_config) +{ + int m; + struct wf_mpu_config *devc; + + if (request_irq (hw_config->irq, wf_mpuintr, + 0, "WaveFront MIDI", NULL) < 0) { + printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n", + hw_config->irq); + return; + } + + if ((m = sound_alloc_mididev()) == -1){ + printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n"); + free_irq (hw_config->irq, NULL); + release_region (hw_config->io_base, 2); + return; + } + + request_region (hw_config->io_base, 2, "WaveFront MPU"); + + hw_config->slots[WF_INTERNAL_MIDI_SLOT] = m; + devc = &dev_conf[m]; + devc->base = hw_config->io_base; + devc->irq = hw_config->irq; + devc->isexternal = 0; + devc->internal = m; + devc->external = -1; + devc->isvirtual = 0; + + irq2dev[devc->irq] = m; + + if (config_wf_mpu (m, hw_config)) { + printk (KERN_WARNING + "WF-MPU: configuration for MIDI device %d failed\n", m); + sound_unload_mididev (m); + } + + /* This being a WaveFront (ICS-2115) emulated MPU-401, we have + to switch it into UART (dumb) mode, because otherwise, it + won't do anything at all. + */ + + start_uart_mode (devc); + +} + +int +probe_wf_mpu (struct address_info *hw_config) + +{ + if (hw_config->irq < 0 || hw_config->irq > 16) { + printk (KERN_WARNING "WF-MPU: bogus IRQ value requested (%d)\n", + hw_config->irq); + return 0; + } + + if (check_region (hw_config->io_base, 2)) { + printk (KERN_WARNING "WF-MPU: I/O port %x already in use\n\n", + hw_config->io_base); + return 0; + } + + if (inb (hw_config->io_base + 1) == 0xff) { /* Just bus float? */ + printk ("WF-MPU: Port %x looks dead.\n", hw_config->io_base); + return 0; + } + + return 1; +} + +void +unload_wf_mpu (struct address_info *hw_config) +{ + + release_region (hw_config->io_base, 2); + sound_unload_mididev (hw_config->slots[WF_INTERNAL_MIDI_SLOT]); + if (hw_config->irq > 0) { + free_irq (hw_config->irq, NULL); + } + if (hw_config->slots[WF_EXTERNAL_MIDI_SLOT] > 0) { + sound_unload_mididev (hw_config->slots[WF_EXTERNAL_MIDI_SLOT]); + } +} + +static void +start_uart_mode (struct wf_mpu_config *devc) +{ + int ok, timeout; + unsigned long flags; + + save_flags (flags); + cli (); + + for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); + + wf_mpu_cmd (devc, UART_MODE_ON); + + for (ok = 0, timeout = 50000; timeout > 0 && !ok; timeout--) { + if (input_avail (devc)) { + if (read_data (devc) == MPU_ACK) { + ok = 1; + } + } + } + + restore_flags (flags); +} + +#endif + + diff --git a/drivers/sound/yss225.c b/drivers/sound/yss225.c new file mode 100644 index 000000000000..e5d7ec0988d2 --- /dev/null +++ b/drivers/sound/yss225.c @@ -0,0 +1,317 @@ +unsigned char page_zero[] = { +0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, +0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, +0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x19, +0x01, 0x1a, 0x01, 0x20, 0x01, 0x40, 0x01, 0x17, 0x00, 0x00, 0x01, +0x80, 0x01, 0x20, 0x00, 0x10, 0x01, 0xa0, 0x03, 0xd1, 0x00, 0x00, +0x01, 0xf2, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xf4, 0x02, +0xe0, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, +0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x60, 0x00, 0x00, +0x00, 0x92, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb3, 0x02, +0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x40, +0x00, 0x80, 0x00, 0xf5, 0x00, 0x20, 0x00, 0x70, 0x00, 0xa0, 0x02, +0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x02, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x17, 0x00, 0x1b, 0x00, +0x1d, 0x02, 0xdf +}; + +unsigned char page_one[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, +0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, +0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, +0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x02, 0x60, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x80, 0x00, +0x00, 0x02, 0xfb, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x1b, 0x02, 0xd7, +0x00, 0x00, 0x02, 0xf7, 0x03, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, +0x1c, 0x03, 0x3c, 0x00, 0x00, 0x03, 0x3f, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0xdf, 0x00, 0x00, 0x00, 0x00, 0x03, 0x5d, 0x00, +0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0x7d, 0x00, 0x00, 0x03, 0xc0, +0x00, 0x00, 0x03, 0x9e, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, +0xbe, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, +0xdb, 0x00, 0x00, 0x02, 0xdb, 0x00, 0x00, 0x02, 0xe0, 0x00, 0x00, +0x02, 0xfb, 0x00, 0x00, 0x02, 0xc0, 0x02, 0x40, 0x02, 0xfb, 0x02, +0x60, 0x00, 0x1b +}; + +unsigned char page_two[] = { +0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, +0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x07, +0x05, 0x05, 0x05, 0x04, 0x07, 0x05, 0x04, 0x07, 0x05, 0x44, 0x46, +0x44, 0x46, 0x46, 0x07, 0x05, 0x44, 0x46, 0x05, 0x46, 0x05, 0x46, +0x05, 0x46, 0x05, 0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, +0x44, 0x46, 0x05, 0x07, 0x44, 0x46, 0x05, 0x07, 0x44, 0x05, 0x05, +0x05, 0x44, 0x05, 0x05, 0x05, 0x46, 0x05, 0x46, 0x05, 0x46, 0x05, +0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 +}; + +unsigned char page_three[] = { +0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, +0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, +0xc0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, +0x60, 0x00, 0x70, 0x00, 0x40, 0x00, 0x40, 0x00, 0x42, 0x00, 0x40, +0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, +0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, +0x00, 0x42, 0x00, 0x40, 0x00, 0x42, 0x00, 0x02, 0x00, 0x02, 0x00, +0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 +}; + +unsigned char page_four[] = { +0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, +0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x60, 0x00, +0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, +0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, +0x20, 0x00, 0x22, 0x02, 0x22, 0x02, 0x20, 0x00, 0x60, 0x00, 0x22, +0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 +}; + +unsigned char page_six[] = { +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, +0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, +0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, +0x16, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x1c, 0x00, +0x00, 0x1e, 0x00, 0x00, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x24, +0x00, 0x00, 0x26, 0x00, 0x00, 0x28, 0x00, 0x00, 0x2a, 0x00, 0x00, +0x2c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x30, 0x00, 0x00, 0x32, 0x00, +0x00, 0x34, 0x00, 0x00, 0x36, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3a, +0x00, 0x00, 0x3c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x40, 0x00, 0x00, +0x42, 0x03, 0x00, 0x44, 0x01, 0x00, 0x46, 0x0a, 0x21, 0x48, 0x0d, +0x23, 0x4a, 0x23, 0x1b, 0x4c, 0x37, 0x8f, 0x4e, 0x45, 0x77, 0x50, +0x52, 0xe2, 0x52, 0x1c, 0x92, 0x54, 0x1c, 0x52, 0x56, 0x07, 0x00, +0x58, 0x2f, 0xc6, 0x5a, 0x0b, 0x00, 0x5c, 0x30, 0x06, 0x5e, 0x17, +0x00, 0x60, 0x3d, 0xda, 0x62, 0x29, 0x00, 0x64, 0x3e, 0x41, 0x66, +0x39, 0x00, 0x68, 0x4c, 0x48, 0x6a, 0x49, 0x00, 0x6c, 0x4c, 0x6c, +0x6e, 0x11, 0xd2, 0x70, 0x16, 0x0c, 0x72, 0x00, 0x00, 0x74, 0x00, +0x80, 0x76, 0x0f, 0x00, 0x78, 0x00, 0x80, 0x7a, 0x13, 0x00, 0x7c, +0x80, 0x00, 0x7e, 0x80, 0x80 +}; + +unsigned char page_seven[] = { +0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, +0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, +0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x0f, 0xff, +0x0f, 0xff, 0x0f, 0xff, 0x02, 0xe9, 0x06, 0x8c, 0x06, 0x8c, 0x0f, +0xff, 0x1a, 0x75, 0x0d, 0x8b, 0x04, 0xe9, 0x0b, 0x16, 0x1a, 0x38, +0x0d, 0xc8, 0x04, 0x6f, 0x0b, 0x91, 0x0f, 0xff, 0x06, 0x40, 0x06, +0x40, 0x02, 0x8f, 0x0f, 0xff, 0x06, 0x62, 0x06, 0x62, 0x02, 0x7b, +0x0f, 0xff, 0x06, 0x97, 0x06, 0x97, 0x02, 0x52, 0x0f, 0xff, 0x06, +0xf6, 0x06, 0xf6, 0x02, 0x19, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, +0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x05, 0x55, 0x14, +0xda, 0x0d, 0x93, 0x04, 0xda, 0x05, 0x93, 0x14, 0xda, 0x0d, 0x93, +0x04, 0xda, 0x05, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x02, 0x00 +}; + +unsigned char page_zero_v2[] = { +0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_one_v2[] = { +0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_two_v2[] = { +0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +unsigned char page_three_v2[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; +unsigned char page_four_v2[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00 +}; + +unsigned char page_seven_v2[] = { +0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned char mod_v2[] = { +0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, +0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, +0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, +0x20, 0xb1, 0x20, 0xb2, 0x20, 0xb3, 0x20, 0xb4, 0x20, 0xb5, 0x20, +0xb6, 0x20, 0xb7, 0x20, 0xf0, 0x20, 0xf1, 0x20, 0xf2, 0x20, 0xf3, +0x20, 0xf4, 0x20, 0xf5, 0x20, 0xf6, 0x20, 0xf7, 0x20, 0x10, 0xff, +0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 0xff, 0x15, 0xff, 0x16, +0xff, 0x17, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, +0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x30, 0x00, 0x31, +0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, +0x37, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, +0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, 0x50, 0x00, 0x51, 0x00, +0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, +0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, +0x65, 0x00, 0x66, 0x00, 0x67, 0x00, 0x70, 0xc0, 0x71, 0xc0, 0x72, +0xc0, 0x73, 0xc0, 0x74, 0xc0, 0x75, 0xc0, 0x76, 0xc0, 0x77, 0xc0, +0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, +0x00, 0x86, 0x00, 0x87, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, +0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xa0, +0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, +0xa6, 0x00, 0xa7, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, +0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xd0, 0x00, +0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, +0x00, 0xd7, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, +0xe4, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x01, 0x00, 0x02, +0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x03, +0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, +0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 +}; +unsigned char coefficients[] = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, +0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, +0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, +0x40, 0x02, 0x40, 0x01, 0x41, 0x02, 0x60, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, +0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x4a, 0x01, 0x20, 0x07, 0x47, +0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x7c, 0x00, 0x00, 0x07, +0x7e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x07, 0x7c, 0x00, 0x00, +0x07, 0x7e, 0x00, 0x00, 0x07, 0x44, 0x00, 0x00, 0x00, 0x44, 0x01, +0x00, 0x07, 0x44, 0x00, 0x00, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, +0x00, 0x00, 0x00, 0x42, 0x01, 0x1a, 0x00, 0x43, 0x01, 0x20, 0x07, +0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, +0x07, 0x41, 0x00, 0x00, 0x01, 0x40, 0x02, 0x40, 0x01, 0x41, 0x02, +0x60, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x44, +0x0f, 0xff, 0x07, 0x42, 0x00, 0x00, 0x07, 0x43, 0x00, 0x00, 0x07, +0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x07, 0x51, 0x06, 0x40, +0x07, 0x50, 0x06, 0x40, 0x07, 0x4f, 0x03, 0x81, 0x07, 0x53, 0x1a, +0x76, 0x07, 0x54, 0x0d, 0x8b, 0x07, 0x55, 0x04, 0xe9, 0x07, 0x56, +0x0b, 0x17, 0x07, 0x57, 0x1a, 0x38, 0x07, 0x58, 0x0d, 0xc9, 0x07, +0x59, 0x04, 0x6f, 0x07, 0x5a, 0x0b, 0x91, 0x07, 0x73, 0x14, 0xda, +0x07, 0x74, 0x0d, 0x93, 0x07, 0x75, 0x04, 0xd9, 0x07, 0x76, 0x05, +0x93, 0x07, 0x77, 0x14, 0xda, 0x07, 0x78, 0x0d, 0x93, 0x07, 0x79, +0x04, 0xd9, 0x07, 0x7a, 0x05, 0x93, 0x07, 0x5e, 0x03, 0x68, 0x07, +0x5c, 0x04, 0x31, 0x07, 0x5d, 0x04, 0x31, 0x07, 0x62, 0x03, 0x52, +0x07, 0x60, 0x04, 0x76, 0x07, 0x61, 0x04, 0x76, 0x07, 0x66, 0x03, +0x2e, 0x07, 0x64, 0x04, 0xda, 0x07, 0x65, 0x04, 0xda, 0x07, 0x6a, +0x02, 0xf6, 0x07, 0x68, 0x05, 0x62, 0x07, 0x69, 0x05, 0x62, 0x06, +0x46, 0x0a, 0x22, 0x06, 0x48, 0x0d, 0x24, 0x06, 0x6e, 0x11, 0xd3, +0x06, 0x70, 0x15, 0xcb, 0x06, 0x52, 0x20, 0x93, 0x06, 0x54, 0x20, +0x54, 0x06, 0x4a, 0x27, 0x1d, 0x06, 0x58, 0x2f, 0xc8, 0x06, 0x5c, +0x30, 0x07, 0x06, 0x4c, 0x37, 0x90, 0x06, 0x60, 0x3d, 0xdb, 0x06, +0x64, 0x3e, 0x42, 0x06, 0x4e, 0x45, 0x78, 0x06, 0x68, 0x4c, 0x48, +0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, +0xba +}; +unsigned char coefficients2[] = { +0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, +0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, +0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, +0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, +0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 +}; +unsigned char coefficients3[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, +0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, +0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, +0x47, 0x01, 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x99, 0x01, 0x99, +0x01, 0xc2, 0x01, 0xc2, 0x01, 0xeb, 0x01, 0xeb, 0x02, 0x14, 0x02, +0x14, 0x02, 0x3d, 0x02, 0x3d, 0x02, 0x66, 0x02, 0x66, 0x02, 0x8f, +0x02, 0x8f, 0x02, 0xb8, 0x02, 0xb8, 0x02, 0xe1, 0x02, 0xe1, 0x03, +0x0a, 0x03, 0x0a, 0x03, 0x33, 0x03, 0x33, 0x03, 0x5c, 0x03, 0x5c, +0x03, 0x85, 0x03, 0x85, 0x03, 0xae, 0x03, 0xae, 0x03, 0xd7, 0x03, +0xd7, 0x04, 0x00, 0x04, 0x00, 0x04, 0x28, 0x04, 0x28, 0x04, 0x51, +0x04, 0x51, 0x04, 0x7a, 0x04, 0x7a, 0x04, 0xa3, 0x04, 0xa3, 0x04, +0xcc, 0x04, 0xcc, 0x04, 0xf5, 0x04, 0xf5, 0x05, 0x1e, 0x05, 0x1e, +0x05, 0x47, 0x05, 0x47, 0x05, 0x70, 0x05, 0x70, 0x05, 0x99, 0x05, +0x99, 0x05, 0xc2, 0x05, 0xc2, 0x05, 0xeb, 0x05, 0xeb, 0x06, 0x14, +0x06, 0x14, 0x06, 0x3d, 0x06, 0x3d, 0x06, 0x66, 0x06, 0x66, 0x06, +0x8f, 0x06, 0x8f, 0x06, 0xb8, 0x06, 0xb8, 0x06, 0xe1, 0x06, 0xe1, +0x07, 0x0a, 0x07, 0x0a, 0x07, 0x33, 0x07, 0x33, 0x07, 0x5c, 0x07, +0x5c, 0x07, 0x85, 0x07, 0x85, 0x07, 0xae, 0x07, 0xae, 0x07, 0xd7, +0x07, 0xd7, 0x08, 0x00, 0x08, 0x00, 0x08, 0x28, 0x08, 0x28, 0x08, +0x51, 0x08, 0x51, 0x08, 0x7a, 0x08, 0x7a, 0x08, 0xa3, 0x08, 0xa3, +0x08, 0xcc, 0x08, 0xcc, 0x08, 0xf5, 0x08, 0xf5, 0x09, 0x1e, 0x09, +0x1e, 0x09, 0x47, 0x09, 0x47, 0x09, 0x70, 0x09, 0x70, 0x09, 0x99, +0x09, 0x99, 0x09, 0xc2, 0x09, 0xc2, 0x09, 0xeb, 0x09, 0xeb, 0x0a, +0x14, 0x0a, 0x14, 0x0a, 0x3d, 0x0a, 0x3d, 0x0a, 0x66, 0x0a, 0x66, +0x0a, 0x8f, 0x0a, 0x8f, 0x0a, 0xb8, 0x0a, 0xb8, 0x0a, 0xe1, 0x0a, +0xe1, 0x0b, 0x0a, 0x0b, 0x0a, 0x0b, 0x33, 0x0b, 0x33, 0x0b, 0x5c, +0x0b, 0x5c, 0x0b, 0x85, 0x0b, 0x85, 0x0b, 0xae, 0x0b, 0xae, 0x0b, +0xd7, 0x0b, 0xd7, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x28, 0x0c, 0x28, +0x0c, 0x51, 0x0c, 0x51, 0x0c, 0x7a, 0x0c, 0x7a, 0x0c, 0xa3, 0x0c, +0xa3, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xf5, 0x0c, 0xf5, 0x0d, 0x1e, +0x0d, 0x1e, 0x0d, 0x47, 0x0d, 0x47, 0x0d, 0x70, 0x0d, 0x70, 0x0d, +0x99, 0x0d, 0x99, 0x0d, 0xc2, 0x0d, 0xc2, 0x0d, 0xeb, 0x0d, 0xeb, +0x0e, 0x14, 0x0e, 0x14, 0x0e, 0x3d, 0x0e, 0x3d, 0x0e, 0x66, 0x0e, +0x66, 0x0e, 0x8f, 0x0e, 0x8f, 0x0e, 0xb8, 0x0e, 0xb8, 0x0e, 0xe1, +0x0e, 0xe1, 0x0f, 0x0a, 0x0f, 0x0a, 0x0f, 0x33, 0x0f, 0x33, 0x0f, +0x5c, 0x0f, 0x5c, 0x0f, 0x85, 0x0f, 0x85, 0x0f, 0xae, 0x0f, 0xae, +0x0f, 0xd7, 0x0f, 0xd7, 0x0f, 0xff, 0x0f, 0xff +}; + diff --git a/drivers/sound/yss225.h b/drivers/sound/yss225.h new file mode 100644 index 000000000000..7e4cd65719fc --- /dev/null +++ b/drivers/sound/yss225.h @@ -0,0 +1,24 @@ +#ifndef __yss255_h__ +#define __yss255_h__ + +extern unsigned char page_zero[256]; +extern unsigned char page_one[256]; +extern unsigned char page_two[128]; +extern unsigned char page_three[128]; +extern unsigned char page_four[128]; +extern unsigned char page_six[192]; +extern unsigned char page_seven[256]; +extern unsigned char page_zero_v2[96]; +extern unsigned char page_one_v2[96]; +extern unsigned char page_two_v2[48]; +extern unsigned char page_three_v2[48]; +extern unsigned char page_four_v2[48]; +extern unsigned char page_seven_v2[96]; +extern unsigned char mod_v2[304]; +extern unsigned char coefficients[364]; +extern unsigned char coefficients2[56]; +extern unsigned char coefficients3[404]; + + +#endif __ys225_h__ + diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e746093faf06..ef27572b8ddb 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -8,6 +8,11 @@ * 10 Apr 1996 Added silly rename for unlink --okir */ +/* + * Fixes: + * Ion Badulescu : FIFO's need special handling in NFSv2 + */ + #include #include #include @@ -443,7 +448,10 @@ static int nfs_mknod(struct inode *dir, const char *name, int len, iput(dir); return -ENAMETOOLONG; } - sattr.mode = mode; + if (mode & S_IFIFO) + sattr.mode = (mode & ~S_IFMT) | S_IFCHR; + else + sattr.mode = mode; sattr.uid = sattr.gid = (unsigned) -1; if (S_ISCHR(mode) || S_ISBLK(mode)) sattr.size = rdev; /* get out your barf bag */ @@ -452,6 +460,11 @@ static int nfs_mknod(struct inode *dir, const char *name, int len, sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), name, &sattr, &fhandle, &fattr); + if (error == -EINVAL && (mode & S_IFIFO)) { + sattr.mode = mode; + error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), + name, &sattr, &fhandle, &fattr); + } if (!error) { nfs_lookup_cache_add(dir, name, &fhandle, &fattr); diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index d9b14da88d6a..e59ed3121d3c 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -23,6 +23,11 @@ * Feel free to fix it and mail me the diffs if it worries you. */ +/* + * Fixes: + * Ion Badulescu : FIFO's need special handling in NFSv2 + */ + /* * Defining NFS_PROC_DEBUG causes a lookup of a file named * "xyzzy" to toggle debugging. Just cd to an NFS-mounted @@ -180,6 +185,11 @@ static int *xdr_decode_fattr(int *p, struct nfs_fattr *fattr) fattr->mtime.useconds = ntohl(*p++); fattr->ctime.seconds = ntohl(*p++); fattr->ctime.useconds = ntohl(*p++); + if (fattr->type == NFCHR && fattr->rdev == NFS_FIFO_DEV) { + fattr->type = NFFIFO; + fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO; + fattr->rdev = 0; + } return p; } diff --git a/fs/pipe.c b/fs/pipe.c index cbb4e252ad8f..92104442ea5a 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -413,7 +413,7 @@ int do_pipe(int *fd) int error; int i,j; - error = ENFILE; + error = -ENFILE; f1 = get_empty_filp(); if (!f1) goto no_files; diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index fb374b80ef4b..b1b485596f45 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -1221,15 +1221,15 @@ smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos, WSET(param, 10, 8 + 4 + 2); /* resume required + close on end + continue */ + } #ifdef CONFIG_SMB_WIN95 - /* Windows 95 is not able to deliver answers - to FIND_NEXT fast enough, so sleep 0.2 seconds */ - current->timeout = jiffies + HZ / 5; - current->state = TASK_INTERRUPTIBLE; - schedule(); - current->timeout = 0; + /* Windows 95 is not able to deliver answers + to FIND_NEXT fast enough, so sleep 0.2 seconds */ + current->timeout = jiffies + HZ / 5; + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->timeout = 0; #endif - } result = smb_trans2_request(server, command, 0, NULL, 12 + mask_len + 2, param, diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index 3d8ca8fbd435..d28097262843 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -275,6 +275,8 @@ static inline _syscall0(int,pause) static inline _syscall0(int,setup) static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) +static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) +static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall1(int,dup,int,fd) static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) diff --git a/include/linux/awe_voice.h b/include/linux/awe_voice.h new file mode 100644 index 000000000000..aa131313141b --- /dev/null +++ b/include/linux/awe_voice.h @@ -0,0 +1,490 @@ +/* + * sound/awe_voice.h + * + * Voice information definitions for the low level driver for the + * AWE32/Sound Blaster 32 wave table synth. + * version 0.4.2c; Oct. 7, 1997 + * + * Copyright (C) 1996,1997 Takashi Iwai + * + * 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 AWE_VOICE_H +#define AWE_VOICE_H + +#ifndef SAMPLE_TYPE_AWE32 +#define SAMPLE_TYPE_AWE32 0x20 +#endif + +#ifndef _PATCHKEY +#define _PATCHKEY(id) ((id<<8)|0xfd) +#endif + +/*---------------------------------------------------------------- + * patch information record + *----------------------------------------------------------------*/ + +/* patch interface header: 16 bytes */ +typedef struct awe_patch_info { + short key; /* use AWE_PATCH here */ +#define AWE_PATCH _PATCHKEY(0x07) + + short device_no; /* synthesizer number */ + unsigned short sf_id; /* file id (should be zero) */ + short optarg; /* optional argument */ + int len; /* data length (without this header) */ + + short type; /* patch operation type */ +#define AWE_LOAD_INFO 0 /* awe_voice_rec */ +#define AWE_LOAD_DATA 1 /* awe_sample_info */ +#define AWE_OPEN_PATCH 2 /* awe_open_parm */ +#define AWE_CLOSE_PATCH 3 /* none */ +#define AWE_UNLOAD_PATCH 4 /* none */ +#define AWE_REPLACE_DATA 5 /* awe_sample_info (optarg=#channels)*/ +#define AWE_MAP_PRESET 6 /* awe_voice_map */ +#define AWE_LOAD_CHORUS_FX 0x10 /* awe_chorus_fx_rec (optarg=mode) */ +#define AWE_LOAD_REVERB_FX 0x11 /* awe_reverb_fx_rec (optarg=mode) */ + + short reserved; /* word alignment data */ + + /* the actual patch data begins after this */ +#if defined(AWE_COMPAT_030) && AWE_COMPAT_030 + char data[0]; +#endif +} awe_patch_info; + +/*#define AWE_PATCH_INFO_SIZE 16*/ +#define AWE_PATCH_INFO_SIZE sizeof(awe_patch_info) + + +/*---------------------------------------------------------------- + * open patch + *----------------------------------------------------------------*/ + +#define AWE_PATCH_NAME_LEN 32 + +typedef struct _awe_open_parm { + unsigned short type; /* sample type */ +#define AWE_PAT_TYPE_MISC 0 +#define AWE_PAT_TYPE_GM 1 +#define AWE_PAT_TYPE_GS 2 +#define AWE_PAT_TYPE_MT32 3 +#define AWE_PAT_TYPE_XG 4 +#define AWE_PAT_TYPE_SFX 5 +#define AWE_PAT_TYPE_GUS 6 +#define AWE_PAT_TYPE_MAP 7 + +#define AWE_PAT_LOCKED 0x100 /* lock the samples */ + + short reserved; + char name[AWE_PATCH_NAME_LEN]; +} awe_open_parm; + +/*#define AWE_OPEN_PARM_SIZE 28*/ +#define AWE_OPEN_PARM_SIZE sizeof(awe_open_parm) + + +/*---------------------------------------------------------------- + * raw voice information record + *----------------------------------------------------------------*/ + +/* wave table envelope & effect parameters to control EMU8000 */ +typedef struct _awe_voice_parm { + unsigned short moddelay; /* modulation delay (0x8000) */ + unsigned short modatkhld; /* modulation attack & hold time (0x7f7f) */ + unsigned short moddcysus; /* modulation decay & sustain (0x7f7f) */ + unsigned short modrelease; /* modulation release time (0x807f) */ + short modkeyhold, modkeydecay; /* envelope change per key (not used) */ + unsigned short voldelay; /* volume delay (0x8000) */ + unsigned short volatkhld; /* volume attack & hold time (0x7f7f) */ + unsigned short voldcysus; /* volume decay & sustain (0x7f7f) */ + unsigned short volrelease; /* volume release time (0x807f) */ + short volkeyhold, volkeydecay; /* envelope change per key (not used) */ + unsigned short lfo1delay; /* LFO1 delay (0x8000) */ + unsigned short lfo2delay; /* LFO2 delay (0x8000) */ + unsigned short pefe; /* modulation pitch & cutoff (0x0000) */ + unsigned short fmmod; /* LFO1 pitch & cutoff (0x0000) */ + unsigned short tremfrq; /* LFO1 volume & freq (0x0000) */ + unsigned short fm2frq2; /* LFO2 pitch & freq (0x0000) */ + unsigned char cutoff; /* initial cutoff (0xff) */ + unsigned char filterQ; /* initial filter Q [0-15] (0x0) */ + unsigned char chorus; /* chorus send (0x00) */ + unsigned char reverb; /* reverb send (0x00) */ + unsigned short reserved[4]; /* not used */ +} awe_voice_parm; + +#define AWE_VOICE_PARM_SIZE 48 + + +/* wave table parameters: 92 bytes */ +typedef struct _awe_voice_info { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* sample offset correction */ + int loopstart, loopend; /* loop offset correction */ + short rate_offset; /* sample rate pitch offset */ + unsigned short mode; /* sample mode */ +#define AWE_MODE_ROMSOUND 0x8000 +#define AWE_MODE_STEREO 1 +#define AWE_MODE_LOOPING 2 +#define AWE_MODE_NORELEASE 4 /* obsolete */ +#define AWE_MODE_INIT_PARM 8 + + short root; /* midi root key */ + short tune; /* pitch tuning (in cents) */ + char low, high; /* key note range */ + char vellow, velhigh; /* velocity range */ + char fixkey, fixvel; /* fixed key, velocity */ + char pan, fixpan; /* panning, fixed panning */ + short exclusiveClass; /* exclusive class (0 = none) */ + unsigned char amplitude; /* sample volume (127 max) */ + unsigned char attenuation; /* attenuation (0.375dB) */ + short scaleTuning; /* pitch scale tuning(%), normally 100 */ + awe_voice_parm parm; /* voice envelope parameters */ + short index; /* internal index (set by driver) */ +} awe_voice_info; + +/*#define AWE_VOICE_INFO_SIZE 92*/ +#define AWE_VOICE_INFO_SIZE sizeof(awe_voice_info) + +/*----------------------------------------------------------------*/ + +/* The info entry of awe_voice_rec is changed from 0 to 1 + * for some compilers refusing zero size array. + * Due to this change, sizeof(awe_voice_rec) becomes different + * from older versions. + * Use AWE_VOICE_REC_SIZE instead. + */ + +/* instrument info header: 4 bytes */ +typedef struct _awe_voice_rec_hdr { + unsigned char bank; /* midi bank number */ + unsigned char instr; /* midi preset number */ + char nvoices; /* number of voices */ + char write_mode; /* write mode; normally 0 */ +#define AWE_WR_APPEND 0 /* append anyway */ +#define AWE_WR_EXCLUSIVE 1 /* skip if already exists */ +#define AWE_WR_REPLACE 2 /* replace if already exists */ +} awe_voice_rec_hdr; + +/*#define AWE_VOICE_REC_SIZE 4*/ +#define AWE_VOICE_REC_SIZE sizeof(awe_voice_rec_hdr) + +/* the standard patch structure for one sample */ +typedef struct _awe_voice_rec_patch { + awe_patch_info patch; + awe_voice_rec_hdr hdr; + awe_voice_info info; +} awe_voice_rec_patch; + + +/* obsolete data type */ +#if defined(AWE_COMPAT_030) && AWE_COMPAT_030 +#define AWE_INFOARRAY_SIZE 0 +#else +#define AWE_INFOARRAY_SIZE 1 +#endif + +typedef struct _awe_voice_rec { + unsigned char bank; /* midi bank number */ + unsigned char instr; /* midi preset number */ + short nvoices; /* number of voices */ + /* voice information follows here */ + awe_voice_info info[AWE_INFOARRAY_SIZE]; +} awe_voice_rec; + + +/*---------------------------------------------------------------- + * sample wave information + *----------------------------------------------------------------*/ + +/* wave table sample header: 32 bytes */ +typedef struct awe_sample_info { + unsigned short sf_id; /* file id (should be zero) */ + unsigned short sample; /* sample id */ + int start, end; /* start & end offset */ + int loopstart, loopend; /* loop start & end offset */ + int size; /* size (0 = ROM) */ + short checksum_flag; /* use check sum = 1 */ + unsigned short mode_flags; /* mode flags */ +#define AWE_SAMPLE_8BITS 1 /* wave data is 8bits */ +#define AWE_SAMPLE_UNSIGNED 2 /* wave data is unsigned */ +#define AWE_SAMPLE_NO_BLANK 4 /* no blank loop is attached */ +#define AWE_SAMPLE_SINGLESHOT 8 /* single-shot w/o loop */ +#define AWE_SAMPLE_BIDIR_LOOP 16 /* bidirectional looping */ +#define AWE_SAMPLE_STEREO_LEFT 32 /* stereo left sound */ +#define AWE_SAMPLE_STEREO_RIGHT 64 /* stereo right sound */ +#define AWE_SAMPLE_REVERSE_LOOP 128 /* reverse looping */ + unsigned int checksum; /* check sum */ +#if defined(AWE_COMPAT_030) && AWE_COMPAT_030 + unsigned short data[0]; /* sample data follows here */ +#endif +} awe_sample_info; + +/*#define AWE_SAMPLE_INFO_SIZE 32*/ +#define AWE_SAMPLE_INFO_SIZE sizeof(awe_sample_info) + + +/*---------------------------------------------------------------- + * voice preset mapping + *----------------------------------------------------------------*/ + +typedef struct awe_voice_map { + int map_bank, map_instr, map_key; /* key = -1 means all keys */ + int src_bank, src_instr, src_key; +} awe_voice_map; + +#define AWE_VOICE_MAP_SIZE sizeof(awe_voice_map) + + +/*---------------------------------------------------------------- + * awe hardware controls + *----------------------------------------------------------------*/ + +#define _AWE_DEBUG_MODE 0x00 +#define _AWE_REVERB_MODE 0x01 +#define _AWE_CHORUS_MODE 0x02 +#define _AWE_REMOVE_LAST_SAMPLES 0x03 +#define _AWE_INITIALIZE_CHIP 0x04 +#define _AWE_SEND_EFFECT 0x05 +#define _AWE_TERMINATE_CHANNEL 0x06 +#define _AWE_TERMINATE_ALL 0x07 +#define _AWE_INITIAL_VOLUME 0x08 +#define _AWE_INITIAL_ATTEN _AWE_INITIAL_VOLUME +#define _AWE_RESET_CHANNEL 0x09 +#define _AWE_CHANNEL_MODE 0x0a +#define _AWE_DRUM_CHANNELS 0x0b +#define _AWE_MISC_MODE 0x0c +#define _AWE_RELEASE_ALL 0x0d +#define _AWE_NOTEOFF_ALL 0x0e +#define _AWE_CHN_PRESSURE 0x0f +/*#define _AWE_GET_CURRENT_MODE 0x10*/ +#define _AWE_EQUALIZER 0x11 +/*#define _AWE_GET_MISC_MODE 0x12*/ +/*#define _AWE_GET_FONTINFO 0x13*/ + +#define _AWE_MODE_FLAG 0x80 +#define _AWE_COOKED_FLAG 0x40 /* not supported */ +#define _AWE_MODE_VALUE_MASK 0x3F + +/*----------------------------------------------------------------*/ + +#define _AWE_SET_CMD(p,dev,voice,cmd,p1,p2) \ +{((char*)(p))[0] = SEQ_PRIVATE;\ + ((char*)(p))[1] = dev;\ + ((char*)(p))[2] = _AWE_MODE_FLAG|(cmd);\ + ((char*)(p))[3] = voice;\ + ((unsigned short*)(p))[2] = p1;\ + ((unsigned short*)(p))[3] = p2;} + +/* buffered access */ +#define _AWE_CMD(dev, voice, cmd, p1, p2) \ +{_SEQ_NEEDBUF(8);\ + _AWE_SET_CMD(_seqbuf + _seqbufptr, dev, voice, cmd, p1, p2);\ + _SEQ_ADVBUF(8);} + +/* direct access */ +#define _AWE_CMD_NOW(seqfd,dev,voice,cmd,p1,p2) \ +{struct seq_event_rec tmp;\ + _AWE_SET_CMD(&tmp, dev, voice, cmd, p1, p2);\ + ioctl(seqfd, SNDCTL_SEQ_OUTOFBAND, &tmp);} + +/*----------------------------------------------------------------*/ + +/* set debugging mode */ +#define AWE_DEBUG_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0) +/* set reverb mode; from 0 to 7 */ +#define AWE_REVERB_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0) +/* set chorus mode; from 0 to 7 */ +#define AWE_CHORUS_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_CHORUS_MODE, p1, 0) + +/* reset channel */ +#define AWE_RESET_CHANNEL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 0, 0) +#define AWE_RESET_CONTROL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 1, 0) + +/* send an effect to all layers */ +#define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value) +#define AWE_ADD_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x80),value) +#define AWE_UNSET_EFFECT(dev,voice,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x40),0) +/* send an effect to a layer */ +#define AWE_SEND_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)),value) +#define AWE_ADD_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x80),value) +#define AWE_UNSET_LAYER_EFFECT(dev,voice,layer,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x40),0) + +/* terminate sound on the channel/voice */ +#define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0) +/* terminate all sounds */ +#define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0) +/* release all sounds (w/o sustain effect) */ +#define AWE_RELEASE_ALL(dev) _AWE_CMD(dev, 0, _AWE_RELEASE_ALL, 0, 0) +/* note off all sounds (w sustain effect) */ +#define AWE_NOTEOFF_ALL(dev) _AWE_CMD(dev, 0, _AWE_NOTEOFF_ALL, 0, 0) + +/* set initial attenuation */ +#define AWE_INITIAL_VOLUME(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 0) +#define AWE_INITIAL_ATTEN AWE_INITIAL_VOLUME +/* relative attenuation */ +#define AWE_SET_ATTEN(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 1) + +/* set channel playing mode; mode=0/1/2 */ +#define AWE_SET_CHANNEL_MODE(dev,mode) _AWE_CMD(dev, 0, _AWE_CHANNEL_MODE, mode, 0) +#define AWE_PLAY_INDIRECT 0 /* indirect voice mode (default) */ +#define AWE_PLAY_MULTI 1 /* multi note voice mode */ +#define AWE_PLAY_DIRECT 2 /* direct single voice mode */ +#define AWE_PLAY_MULTI2 3 /* sequencer2 mode; used internally */ + +/* set drum channel mask; channels is 32bit long value */ +#define AWE_DRUM_CHANNELS(dev,channels) _AWE_CMD(dev, 0, _AWE_DRUM_CHANNELS, ((channels) & 0xffff), ((channels) >> 16)) + +/* set bass and treble control; values are from 0 to 11 */ +#define AWE_EQUALIZER(dev,bass,treble) _AWE_CMD(dev, 0, _AWE_EQUALIZER, bass, treble) + +/* remove last loaded samples */ +#define AWE_REMOVE_LAST_SAMPLES(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0) +/* initialize emu8000 chip */ +#define AWE_INITIALIZE_CHIP(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_INITIALIZE_CHIP, 0, 0) + +/* set miscellaneous modes; meta command */ +#define AWE_MISC_MODE(dev,mode,value) _AWE_CMD(dev, 0, _AWE_MISC_MODE, mode, value) +/* exclusive sound off; 1=off */ +#define AWE_EXCLUSIVE_SOUND(dev,mode) AWE_MISC_MODE(dev,AWE_MD_EXCLUSIVE_SOUND,mode) +/* default GUS bank number */ +#define AWE_SET_GUS_BANK(dev,bank) AWE_MISC_MODE(dev,AWE_MD_GUS_BANK,bank) +/* change panning position in realtime; 0=don't 1=do */ +#define AWE_REALTIME_PAN(dev,mode) AWE_MISC_MODE(dev,AWE_MD_REALTIME_PAN,mode) + +/* extended pressure controls; not portable with other sound drivers */ +#define AWE_KEY_PRESSURE(dev,ch,note,vel) SEQ_START_NOTE(dev,ch,(note)+128,vel) +#define AWE_CHN_PRESSURE(dev,ch,vel) _AWE_CMD(dev,ch,_AWE_CHN_PRESSURE,vel,0) + +/*----------------------------------------------------------------*/ + +/* reverb mode parameters */ +#define AWE_REVERB_ROOM1 0 +#define AWE_REVERB_ROOM2 1 +#define AWE_REVERB_ROOM3 2 +#define AWE_REVERB_HALL1 3 +#define AWE_REVERB_HALL2 4 +#define AWE_REVERB_PLATE 5 +#define AWE_REVERB_DELAY 6 +#define AWE_REVERB_PANNINGDELAY 7 +#define AWE_REVERB_PREDEFINED 8 +/* user can define reverb modes up to 32 */ +#define AWE_REVERB_NUMBERS 32 + +typedef struct awe_reverb_fx_rec { + unsigned short parms[28]; +} awe_reverb_fx_rec; + +/*----------------------------------------------------------------*/ + +/* chorus mode parameters */ +#define AWE_CHORUS_1 0 +#define AWE_CHORUS_2 1 +#define AWE_CHORUS_3 2 +#define AWE_CHORUS_4 3 +#define AWE_CHORUS_FEEDBACK 4 +#define AWE_CHORUS_FLANGER 5 +#define AWE_CHORUS_SHORTDELAY 6 +#define AWE_CHORUS_SHORTDELAY2 7 +#define AWE_CHORUS_PREDEFINED 8 +/* user can define chorus modes up to 32 */ +#define AWE_CHORUS_NUMBERS 32 + +typedef struct awe_chorus_fx_rec { + unsigned short feedback; /* feedback level (0xE600-0xE6FF) */ + unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */ + unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */ + unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */ + unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */ +} awe_chorus_fx_rec; + +/*----------------------------------------------------------------*/ + +/* misc mode types */ +enum { +/* 0*/ AWE_MD_EXCLUSIVE_OFF, /* obsolete */ +/* 1*/ AWE_MD_EXCLUSIVE_ON, /* obsolete */ +/* 2*/ AWE_MD_VERSION, /* read only */ +/* 3*/ AWE_MD_EXCLUSIVE_SOUND, /* ignored */ +/* 4*/ AWE_MD_REALTIME_PAN, /* 0/1: do realtime pan change (default=1) */ +/* 5*/ AWE_MD_GUS_BANK, /* bank number for GUS patches (default=0) */ +/* 6*/ AWE_MD_KEEP_EFFECT, /* 0/1: keep effect values, (default=0) */ +/* 7*/ AWE_MD_ZERO_ATTEN, /* attenuation of max volume (default=32) */ +/* 8*/ AWE_MD_CHN_PRIOR, /* 0/1: set MIDI channel priority mode (default=1) */ +/* 9*/ AWE_MD_MOD_SENSE, /* integer: modwheel sensitivity (def=18) */ +/*10*/ AWE_MD_DEF_PRESET, /* integer: default preset number (def=0) */ +/*11*/ AWE_MD_DEF_BANK, /* integer: default bank number (def=0) */ +/*12*/ AWE_MD_DEF_DRUM, /* integer: default drumset number (def=0) */ +/*13*/ AWE_MD_TOGGLE_DRUM_BANK, /* 0/1: toggle drum flag with bank# (def=0) */ + AWE_MD_END, +}; + +/*----------------------------------------------------------------*/ + +/* effect parameters */ +enum { + +/* modulation envelope parameters */ +/* 0*/ AWE_FX_ENV1_DELAY, /* WORD: ENVVAL */ +/* 1*/ AWE_FX_ENV1_ATTACK, /* BYTE: up ATKHLD */ +/* 2*/ AWE_FX_ENV1_HOLD, /* BYTE: lw ATKHLD */ +/* 3*/ AWE_FX_ENV1_DECAY, /* BYTE: lw DCYSUS */ +/* 4*/ AWE_FX_ENV1_RELEASE, /* BYTE: lw DCYSUS */ +/* 5*/ AWE_FX_ENV1_SUSTAIN, /* BYTE: up DCYSUS */ +/* 6*/ AWE_FX_ENV1_PITCH, /* BYTE: up PEFE */ +/* 7*/ AWE_FX_ENV1_CUTOFF, /* BYTE: lw PEFE */ + +/* volume envelope parameters */ +/* 8*/ AWE_FX_ENV2_DELAY, /* WORD: ENVVOL */ +/* 9*/ AWE_FX_ENV2_ATTACK, /* BYTE: up ATKHLDV */ +/*10*/ AWE_FX_ENV2_HOLD, /* BYTE: lw ATKHLDV */ +/*11*/ AWE_FX_ENV2_DECAY, /* BYTE: lw DCYSUSV */ +/*12*/ AWE_FX_ENV2_RELEASE, /* BYTE: lw DCYSUSV */ +/*13*/ AWE_FX_ENV2_SUSTAIN, /* BYTE: up DCYSUSV */ + +/* LFO1 (tremolo & vibrato) parameters */ +/*14*/ AWE_FX_LFO1_DELAY, /* WORD: LFO1VAL */ +/*15*/ AWE_FX_LFO1_FREQ, /* BYTE: lo TREMFRQ */ +/*16*/ AWE_FX_LFO1_VOLUME, /* BYTE: up TREMFRQ */ +/*17*/ AWE_FX_LFO1_PITCH, /* BYTE: up FMMOD */ +/*18*/ AWE_FX_LFO1_CUTOFF, /* BYTE: lo FMMOD */ + +/* LFO2 (vibrato) parameters */ +/*19*/ AWE_FX_LFO2_DELAY, /* WORD: LFO2VAL */ +/*20*/ AWE_FX_LFO2_FREQ, /* BYTE: lo FM2FRQ2 */ +/*21*/ AWE_FX_LFO2_PITCH, /* BYTE: up FM2FRQ2 */ + +/* Other overall effect parameters */ +/*22*/ AWE_FX_INIT_PITCH, /* SHORT: pitch offset */ +/*23*/ AWE_FX_CHORUS, /* BYTE: chorus effects send (0-255) */ +/*24*/ AWE_FX_REVERB, /* BYTE: reverb effects send (0-255) */ +/*25*/ AWE_FX_CUTOFF, /* BYTE: up IFATN */ +/*26*/ AWE_FX_FILTERQ, /* BYTE: up CCCA */ + +/* Sample / loop offset changes */ +/*27*/ AWE_FX_SAMPLE_START, /* SHORT: offset */ +/*28*/ AWE_FX_LOOP_START, /* SHORT: offset */ +/*29*/ AWE_FX_LOOP_END, /* SHORT: offset */ +/*30*/ AWE_FX_COARSE_SAMPLE_START, /* SHORT: upper word offset */ +/*31*/ AWE_FX_COARSE_LOOP_START, /* SHORT: upper word offset */ +/*32*/ AWE_FX_COARSE_LOOP_END, /* SHORT: upper word offset */ +/*33*/ AWE_FX_ATTEN, /* BYTE: lo IFATN */ + + AWE_FX_END, +}; + +#endif /* AWE_VOICE_H */ diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index 4ede3d0b0ac7..de9a3d072c9f 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -1,4 +1,4 @@ -/* $Revision: 2.4 $$Date: 1998/06/01 12:09:53 $ +/* $Revision: 2.5 $$Date: 1998/08/03 16:57:01 $ * linux/include/linux/cyclades.h * * This file is maintained by Ivan Passos , @@ -7,6 +7,9 @@ * * This file contains the general definitions for the cyclades.c driver *$Log: cyclades.h,v $ + *Revision 2.5 1998/08/03 16:57:01 ivan + *added cyclades_idle_stats structure; + * *Revision 2.4 1998/06/01 12:09:53 ivan *removed closing_wait2 from cyclades_port structure; * @@ -60,6 +63,22 @@ struct cyclades_monitor { unsigned long char_last; }; +/* + * These stats all reflect activity since the device was last initialized. + * (i.e., since the port was opened with no other processes already having it + * open) + */ +struct cyclades_idle_stats { + time_t in_use; /* Time device has been in use (secs) */ + time_t recv_idle; /* Time since last char received (secs) */ + time_t xmit_idle; /* Time since last char transmitted (secs) */ + unsigned long recv_bytes; /* Bytes received */ + unsigned long xmit_bytes; /* Bytes transmitted */ + unsigned long overruns; /* Input overruns */ + unsigned long frame_errs; /* Input framing errors */ + unsigned long parity_errs; /* Input parity errors */ +}; + #define CYCLADES_MAGIC 0x4359 #define CYGETMON 0x435901 @@ -75,11 +94,12 @@ struct cyclades_monitor { #define CYGETRFLOW 0x43590b #define CYSETRTSDTR_INV 0x43590c #define CYGETRTSDTR_INV 0x43590d -#define CYZPOLLCYCLE 0x43590e -#define CYGETCD1400VER 0x43590f -#define CYGETCARDINFO 0x435910 -#define CYSETWAIT 0x435911 -#define CYGETWAIT 0x435912 +#define CYZSETPOLLCYCLE 0x43590e +#define CYZGETPOLLCYCLE 0x43590f +#define CYGETCD1400VER 0x435910 +#define CYGETCARDINFO 0x435911 +#define CYSETWAIT 0x435912 +#define CYGETWAIT 0x435913 /*************** CYCLOM-Z ADDITIONS ***************/ @@ -534,6 +554,7 @@ struct cyclades_port { struct cyclades_monitor mon; unsigned long jiffies[3]; unsigned long rflush_count; + struct cyclades_idle_stats idle_stats; }; /* diff --git a/include/linux/fs.h b/include/linux/fs.h index 37ffba76b5f4..511c101139f6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -552,6 +552,7 @@ extern int unregister_filesystem(struct file_system_type *); asmlinkage int sys_open(const char *, int, int); asmlinkage int sys_close(unsigned int); /* yes, it's really unsigned */ +asmlinkage int sys_read(unsigned int, char *, int); extern void kill_fasync(struct fasync_struct *fa, int sig); diff --git a/include/linux/if_wic.h b/include/linux/if_wic.h deleted file mode 100644 index cca15f85bcc6..000000000000 --- a/include/linux/if_wic.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef _LINUX_IF_WIC_H -#define _LINUX_IF_WIC_H - -#include - -#define SIOCDEVWIC SIOCDEVPRIVATE - -struct wicconf -{ - unsigned char pcmd; - unsigned char data[120]; - unsigned char len; -}; - -/* WIC host to controller commands */ - -#define WIC_AYT 0x10 /* test dki */ -#define WIC_RESET 0x11 /* reset controller */ -#define WIC_SETSN 0x21 /* set station name */ -#define WIC_SETPS 0x22 /* set power saving mode */ -#define WIC_SETAF 0x23 /* set announce filter */ -#define WIC_SETGPF 0x24 /* set GPSP filter */ -#define WIC_GETVERH 0x61 /* get interface controller version */ -#define WIC_GETNL 0x62 /* get neighbor list */ -#define WIC_GETSN 0x65 /* get station name */ -#define WIC_CLRSTATS 0x83 /* clear controller statistics */ -#define WIC_SETNET 0x84 /* set network configuration */ -#define WIC_SETSYS 0x85 /* set system configuration */ -#define WIC_GETSTATS 0xc1 /* get statistics */ -#define WIC_GETVERM 0xc3 /* get MAC version */ -#define WIC_GETNET 0xc4 /* get network configuration */ -#define WIC_GETSYS 0xc5 /* get system configuration */ - -/* - * structure used for the GETNET/SETNET command - */ - -struct wic_net { - unsigned char ula[6]; /* ula of interface */ - unsigned char mode; /* operating mode */ -#define NET_MODE_ME 0x01 /* receive my ula */ -#define NET_MODE_BCAST 0x02 /* receive bcasts */ -#define NET_MODE_MCAST 0x04 /* receive mcasts */ -#define NET_MODE_PROM 0x08 /* promiscuous */ -#define NET_MODE_HC 0x10 /* is a hop coordinator */ -#define NET_MODE_HC_VALID 0x20 /* hc address is valid */ -#define NET_MODE_HCAP 0x40 /* hc is also ap */ -#define NET_MODE_HC_KNOWN 0x80 /* hc is known */ - unsigned char rts_lo; /* rts threshold */ - unsigned char rts_hi; /* rts threshold */ - unsigned char retry; /* retry limit */ - unsigned char hc_ula[6]; /* ula of hc */ - unsigned char key[4]; /* network key */ - unsigned char dsl; /* direct send limit */ - unsigned char res1; /* reserved */ -}; - -/* - * structure used for the GETSYS/SETSYS command - */ - -struct wic_sys { - unsigned char mode; /* set operating mode */ -#define SYS_MODE_ANT_DIV 0x00 /* use antenna diversity */ -#define SYS_MODE_ANT_1 0x01 /* use ant 1 for tx */ -#define SYS_MODE_ANT_2 0x02 /* use ant 2 for tx */ -#define SYS_MODE_HC_LOCK 0x04 /* lock onto current hc */ -#define SYS_MODE_DEBUG 0x08 /* upload failed frames */ -#define SYS_MODE_IAM_AP 0x10 /* I am AP */ -#define SYS_MODE_IAM_HC 0x20 /* I am HC */ -#define SYS_MODE_USE_SKIP 0x40 /* use skipping mechanism */ -#define SYS_MODE_AUTO 0x80 /* station is in auto mode */ - unsigned char switches; /* radio/controller switches */ -#define SYS_SWITCH_STDBY 0x01 /* switch radio to standby */ -#define SYS_SWITCH_TXRX 0x02 /* 1 = tx, manual mode only */ -#define SYS_SWITCH_PA 0x04 /* 1 = enable PA on radio */ -#define SYS_SWITCH_PWR 0x10 /* 1 = hi, 0 = lo power output */ -#define SYS_SWITCH_RES1 0x20 /* reserved, must be 0 */ -#define SYS_SWITCH_LIGHTS 0x40 /* light for tx & rx */ -#define SYS_SWITCH_LIGHTS_HC 0x80 /* light for rx while coordinated */ - unsigned char hop_min; /* hop range */ - unsigned char hop_max; /* hop range */ - unsigned char pre_len; /* preamble length (bytes) */ - unsigned char pre_match; /* valid preamble match (bytes) */ - unsigned char mod; /* data mod: 1 = 8:1, 0 = none */ - unsigned char cca_mode; /* cca flags */ -#define CCA_PKT_DET_BSY 0x01 /* busy if packet is detected */ -#define CCA_VIRT_CARR 0x02 /* use virtual carrier */ -#define CCA_RSSI_BSY 0x04 /* busy if rssi > threshold */ -#define CCA_DATA_BSY 0x08 /* busy if valid data > XXX usec */ - unsigned char dwell_hi; /* dwell time */ - unsigned char dwell_lo; /* dwell time */ - unsigned char hc_timeout; /* HC timeout */ - unsigned char rssi; /* rssi threshold */ - unsigned char hc_rssi; /* rssi of last hc frame */ - unsigned char hc_rssi_chan; /* channel of hc rssi value */ -}; - - -#endif /* _LINUX_IF_WIC_H */ - - diff --git a/include/linux/kerneld.h b/include/linux/kerneld.h index 5418b60d56bb..6d66c1a88722 100644 --- a/include/linux/kerneld.h +++ b/include/linux/kerneld.h @@ -1,6 +1,8 @@ #ifndef _LINUX_KERNELD_H #define _LINUX_KERNELD_H +#include + #define KERNELD_SYSTEM 1 #define KERNELD_REQUEST_MODULE 2 /* "insmod" */ #define KERNELD_RELEASE_MODULE 3 /* "rmmod" */ diff --git a/include/linux/rose.h b/include/linux/rose.h index 3ea354972cae..1746c67ca7a6 100644 --- a/include/linux/rose.h +++ b/include/linux/rose.h @@ -7,7 +7,6 @@ #ifndef ROSE_KERNEL_H #define ROSE_KERNEL_H -#define PF_ROSE AF_ROSE #define ROSE_MTU 251 #define ROSE_DEFER 1 diff --git a/include/linux/sound.h b/include/linux/sound.h new file mode 100644 index 000000000000..7dabc388ccb1 --- /dev/null +++ b/include/linux/sound.h @@ -0,0 +1,13 @@ +/* + * Sound core interface functions + */ + +extern int register_sound_special(struct file_operations *, int); +extern int register_sound_mixer(struct file_operations *fops); +extern int register_sound_midi(struct file_operations *fops); +extern int register_sound_dsp(struct file_operations *fops); + +extern void unregister_sound_special(int unit); +extern void unregister_sound_mixer(int unit); +extern void unregister_sound_midi(int unit); +extern void unregister_sound_dsp(int unit); diff --git a/include/linux/soundcard.h b/include/linux/soundcard.h index c1753186143c..9706e6b536cd 100644 --- a/include/linux/soundcard.h +++ b/include/linux/soundcard.h @@ -1,7 +1,7 @@ #ifndef SOUNDCARD_H #define SOUNDCARD_H /* - * Copyright by Hannu Savolainen 1993-1996 + * Copyright by Hannu Savolainen 1993-1997 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -24,9 +24,19 @@ * SUCH DAMAGE. */ -#define SOUND_VERSION 350 -#define UNIX_SOUND_SYSTEM +/* + * OSS interface version. With versions earlier than 3.6 this value is + * an integer with value less than 361. In versions 3.6 and later + * it's a six digit hexadecimal value. For example value + * of 0x030600 represents OSS version 3.6.0. + * Use ioctl(fd, OSS_GETVERSION, &int) to get the version number of + * the currently active driver. + */ +#define SOUND_VERSION 0x030802 +#define OPEN_SOUND_SYSTEM + +/* In Linux we need to be prepared for cross compiling */ #include /* @@ -65,90 +75,134 @@ * IOCTL Commands for /dev/sequencer */ -#ifndef _IOWR -/* @(#)ioctlp.h */ +#ifndef _SIOWR +#if defined(_IOWR) && (defined(_AIX) || (!defined(sun) && !defined(sparc) && !defined(__INCioctlh) && !defined(__Lynx__))) +/* Use already defined ioctl defines if they exist (except with Sun) */ +#define SIOCPARM_MASK IOCPARM_MASK +#define SIOC_VOID IOC_VOID +#define SIOC_OUT IOC_OUT +#define SIOC_IN IOC_IN +#define SIOC_INOUT IOC_INOUT +#define _SIOC_SIZE _IOC_SIZE +#define _SIOC_DIR _IOC_DIR +#define _SIOC_NONE _IOC_NONE +#define _SIOC_READ _IOC_READ +#define _SIOC_WRITE _IOC_WRITE +#define _SIO _IO +#define _SIOR _IOR +#define _SIOW _IOW +#define _SIOWR _IOWR +#else /* Ioctl's have the command encoded in the lower word, * and the size of any in or out parameters in the upper * word. The high 2 bits of the upper word are used * to encode the in/out status of the parameter; for now - * we restrict parameters to at most 128 bytes. + * we restrict parameters to at most 8191 bytes. */ -/* #define IOCTYPE (0xff<<8) */ -#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */ -#define IOC_VOID 0x00000000 /* no parameters */ -#define IOC_OUT 0x20000000 /* copy out parameters */ -#define IOC_IN 0x40000000 /* copy in parameters */ -#define IOC_INOUT (IOC_IN|IOC_OUT) +/* #define SIOCTYPE (0xff<<8) */ +#define SIOCPARM_MASK 0x1fff /* parameters must be < 8192 bytes */ +#define SIOC_VOID 0x00000000 /* no parameters */ +#define SIOC_OUT 0x20000000 /* copy out parameters */ +#define SIOC_IN 0x40000000 /* copy in parameters */ +#define SIOC_INOUT (SIOC_IN|SIOC_OUT) /* the 0x20000000 is so we can distinguish new ioctl's from old */ -#define _IO(x,y) ((int)(IOC_VOID|(x<<8)|y)) -#define _IOR(x,y,t) ((int)(IOC_OUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)) -#define _IOW(x,y,t) ((int)(IOC_IN|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)) -/* this should be _IORW, but stdio got there first */ -#define _IOWR(x,y,t) ((int)(IOC_INOUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)) -#endif /* !_IOWR */ - -#define SNDCTL_SEQ_RESET _IO ('Q', 0) -#define SNDCTL_SEQ_SYNC _IO ('Q', 1) -#define SNDCTL_SYNTH_INFO _IOWR('Q', 2, struct synth_info) -#define SNDCTL_SEQ_CTRLRATE _IOWR('Q', 3, int) /* Set/get timer resolution (HZ) */ -#define SNDCTL_SEQ_GETOUTCOUNT _IOR ('Q', 4, int) -#define SNDCTL_SEQ_GETINCOUNT _IOR ('Q', 5, int) -#define SNDCTL_SEQ_PERCMODE _IOW ('Q', 6, int) -#define SNDCTL_FM_LOAD_INSTR _IOW ('Q', 7, struct sbi_instrument) /* Valid for FM only */ -#define SNDCTL_SEQ_TESTMIDI _IOW ('Q', 8, int) -#define SNDCTL_SEQ_RESETSAMPLES _IOW ('Q', 9, int) -#define SNDCTL_SEQ_NRSYNTHS _IOR ('Q',10, int) -#define SNDCTL_SEQ_NRMIDIS _IOR ('Q',11, int) -#define SNDCTL_MIDI_INFO _IOWR('Q',12, struct midi_info) -#define SNDCTL_SEQ_THRESHOLD _IOW ('Q',13, int) -#define SNDCTL_SEQ_TRESHOLD SNDCTL_SEQ_THRESHOLD /* there was once a typo */ -#define SNDCTL_SYNTH_MEMAVL _IOWR('Q',14, int) /* in=dev#, out=memsize */ -#define SNDCTL_FM_4OP_ENABLE _IOW ('Q',15, int) /* in=dev# */ -#define SNDCTL_PMGR_ACCESS _IOWR('Q',16, struct patmgr_info) -#define SNDCTL_SEQ_PANIC _IO ('Q',17) -#define SNDCTL_SEQ_OUTOFBAND _IOW ('Q',18, struct seq_event_rec) - - struct seq_event_rec { - unsigned char arr[8]; - }; - -#define SNDCTL_TMR_TIMEBASE _IOWR('T', 1, int) -#define SNDCTL_TMR_START _IO ('T', 2) -#define SNDCTL_TMR_STOP _IO ('T', 3) -#define SNDCTL_TMR_CONTINUE _IO ('T', 4) -#define SNDCTL_TMR_TEMPO _IOWR('T', 5, int) -#define SNDCTL_TMR_SOURCE _IOWR('T', 6, int) +#define _SIO(x,y) ((int)(SIOC_VOID|(x<<8)|y)) +#define _SIOR(x,y,t) ((int)(SIOC_OUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y)) +#define _SIOW(x,y,t) ((int)(SIOC_IN|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y)) +/* this should be _SIORW, but stdio got there first */ +#define _SIOWR(x,y,t) ((int)(SIOC_INOUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y)) +#define _SIOC_SIZE(x) ((x>>16)&SIOCPARM_MASK) +#define _SIOC_DIR(x) (x & 0xf0000000) +#define _SIOC_NONE SIOC_VOID +#define _SIOC_READ SIOC_OUT +#define _SIOC_WRITE SIOC_IN +# endif /* _IOWR */ +#endif /* !_SIOWR */ + +#define SNDCTL_SEQ_RESET _SIO ('Q', 0) +#define SNDCTL_SEQ_SYNC _SIO ('Q', 1) +#define SNDCTL_SYNTH_INFO _SIOWR('Q', 2, struct synth_info) +#define SNDCTL_SEQ_CTRLRATE _SIOWR('Q', 3, int) /* Set/get timer resolution (HZ) */ +#define SNDCTL_SEQ_GETOUTCOUNT _SIOR ('Q', 4, int) +#define SNDCTL_SEQ_GETINCOUNT _SIOR ('Q', 5, int) +#define SNDCTL_SEQ_PERCMODE _SIOW ('Q', 6, int) +#define SNDCTL_FM_LOAD_INSTR _SIOW ('Q', 7, struct sbi_instrument) /* Obsolete. Don't use!!!!!! */ +#define SNDCTL_SEQ_TESTMIDI _SIOW ('Q', 8, int) +#define SNDCTL_SEQ_RESETSAMPLES _SIOW ('Q', 9, int) +#define SNDCTL_SEQ_NRSYNTHS _SIOR ('Q',10, int) +#define SNDCTL_SEQ_NRMIDIS _SIOR ('Q',11, int) +#define SNDCTL_MIDI_INFO _SIOWR('Q',12, struct midi_info) +#define SNDCTL_SEQ_THRESHOLD _SIOW ('Q',13, int) +#define SNDCTL_SYNTH_MEMAVL _SIOWR('Q',14, int) /* in=dev#, out=memsize */ +#define SNDCTL_FM_4OP_ENABLE _SIOW ('Q',15, int) /* in=dev# */ +#define SNDCTL_SEQ_PANIC _SIO ('Q',17) +#define SNDCTL_SEQ_OUTOFBAND _SIOW ('Q',18, struct seq_event_rec) +#define SNDCTL_SEQ_GETTIME _SIOR ('Q',19, int) +#define SNDCTL_SYNTH_ID _SIOWR('Q',20, struct synth_info) +#define SNDCTL_SYNTH_CONTROL _SIOWR('Q',21, struct synth_control) +#define SNDCTL_SYNTH_REMOVESAMPLE _SIOWR('Q',22, struct remove_sample) + +typedef struct synth_control +{ + int devno; /* Synthesizer # */ + char data[4000]; /* Device spesific command/data record */ +}synth_control; + +typedef struct remove_sample +{ + int devno; /* Synthesizer # */ + int bankno; /* MIDI bank # (0=General MIDI) */ + int instrno; /* MIDI instrument number */ +} remove_sample; + +typedef struct seq_event_rec { + unsigned char arr[8]; +} seq_event_rec; + +#define SNDCTL_TMR_TIMEBASE _SIOWR('T', 1, int) +#define SNDCTL_TMR_START _SIO ('T', 2) +#define SNDCTL_TMR_STOP _SIO ('T', 3) +#define SNDCTL_TMR_CONTINUE _SIO ('T', 4) +#define SNDCTL_TMR_TEMPO _SIOWR('T', 5, int) +#define SNDCTL_TMR_SOURCE _SIOWR('T', 6, int) # define TMR_INTERNAL 0x00000001 # define TMR_EXTERNAL 0x00000002 # define TMR_MODE_MIDI 0x00000010 # define TMR_MODE_FSK 0x00000020 # define TMR_MODE_CLS 0x00000040 # define TMR_MODE_SMPTE 0x00000080 -#define SNDCTL_TMR_METRONOME _IOW ('T', 7, int) -#define SNDCTL_TMR_SELECT _IOW ('T', 8, int) +#define SNDCTL_TMR_METRONOME _SIOW ('T', 7, int) +#define SNDCTL_TMR_SELECT _SIOW ('T', 8, int) /* - * Endian aware patch key generation algorithm. + * Some big endian/little endian handling macros */ -#if defined(_AIX) || defined(AIX) +#if defined(_AIX) || defined(AIX) || defined(sparc) || defined(HPPA) || defined(PPC) +/* Big endian machines */ # define _PATCHKEY(id) (0xfd00|id) +# define AFMT_S16_NE AFMT_S16_BE #else # define _PATCHKEY(id) ((id<<8)|0xfd) +# define AFMT_S16_NE AFMT_S16_LE #endif /* * Sample loading mechanism for internal synthesizers (/dev/sequencer) * The following patch_info structure has been designed to support * Gravis UltraSound. It tries to be universal format for uploading - * sample based patches but is propably too limited. + * sample based patches but is probably too limited. + * + * (PBD) As Hannu guessed, the GUS structure is too limited for + * the WaveFront, but this is the right place for a constant definition. */ struct patch_info { - unsigned short key; /* Use GUS_PATCH here */ -#define GUS_PATCH _PATCHKEY(0x04) -#define OBSOLETE_GUS_PATCH _PATCHKEY(0x02) + unsigned short key; /* Use WAVE_PATCH here */ +#define WAVE_PATCH _PATCHKEY(0x04) +#define GUS_PATCH WAVE_PATCH +#define WAVEFRONT_PATCH _PATCHKEY(0x06) short device_no; /* Synthesizer number */ short instr_no; /* Midi pgm# */ @@ -165,12 +219,16 @@ struct patch_info { #define WAVE_LOOP_BACK 0x10 /* bit 4 = Set is looping backward. */ #define WAVE_SUSTAIN_ON 0x20 /* bit 5 = Turn sustaining on. (Env. pts. 3)*/ #define WAVE_ENVELOPES 0x40 /* bit 6 = Enable envelopes - 1 */ +#define WAVE_FAST_RELEASE 0x80 /* bit 7 = Shut off immediately after note off */ /* (use the env_rate/env_offs fields). */ /* Linux specific bits */ #define WAVE_VIBRATO 0x00010000 /* The vibrato info is valid */ #define WAVE_TREMOLO 0x00020000 /* The tremolo info is valid */ #define WAVE_SCALE 0x00040000 /* The scaling info is valid */ #define WAVE_FRACTIONS 0x00080000 /* Fraction information is valid */ +/* Reserved bits */ +#define WAVE_ROM 0x40000000 /* For future use */ +#define WAVE_MULAW 0x20000000 /* For future use */ /* Other bits must be zeroed */ int len; /* Size of the wave data in bytes */ @@ -184,7 +242,7 @@ struct patch_info { * * The low_note and high_note fields define the minimum and maximum note * frequencies for which this sample is valid. It is possible to define - * more than one samples for a instrument number at the same time. The + * more than one samples for an instrument number at the same time. The * low_note and high_note fields are used to select the most suitable one. * * The fields base_note, high_note and low_note should contain @@ -224,12 +282,13 @@ struct patch_info { int volume; int fractions; - int spare[3]; + int reserved1; + int spare[2]; char data[1]; /* The waveform data starts here */ }; struct sysex_info { - short key; /* Use GUS_PATCH here */ + short key; /* Use SYSEX_PATCH or MAUI_PATCH here */ #define SYSEX_PATCH _PATCHKEY(0x05) #define MAUI_PATCH _PATCHKEY(0x06) short device_no; /* Synthesizer number */ @@ -237,90 +296,6 @@ struct sysex_info { unsigned char data[1]; /* Sysex data starts here */ }; -/* - * Patch management interface (/dev/sequencer, /dev/patmgr#) - * Don't use these calls if you want to maintain compatibility with - * the future versions of the driver. - */ - -#define PS_NO_PATCHES 0 /* No patch support on device */ -#define PS_MGR_NOT_OK 1 /* Plain patch support (no mgr) */ -#define PS_MGR_OK 2 /* Patch manager supported */ -#define PS_MANAGED 3 /* Patch manager running */ - -#define SNDCTL_PMGR_IFACE _IOWR('P', 1, struct patmgr_info) - -/* - * The patmgr_info is a fixed size structure which is used for two - * different purposes. The intended use is for communication between - * the application using /dev/sequencer and the patch manager daemon - * associated with a synthesizer device (ioctl(SNDCTL_PMGR_ACCESS)). - * - * This structure is also used with ioctl(SNDCTL_PGMR_IFACE) which allows - * a patch manager daemon to read and write device parameters. This - * ioctl available through /dev/sequencer also. Avoid using it since it's - * extremely hardware dependent. In addition access trough /dev/sequencer - * may confuse the patch manager daemon. - */ - -struct patmgr_info { - unsigned int key; /* Don't worry. Reserved for communication - between the patch manager and the driver. */ -#define PM_K_EVENT 1 /* Event from the /dev/sequencer driver */ -#define PM_K_COMMAND 2 /* Request from a application */ -#define PM_K_RESPONSE 3 /* From patmgr to application */ -#define PM_ERROR 4 /* Error returned by the patmgr */ - int device; - int command; - -/* - * Commands 0x000 to 0xfff reserved for patch manager programs - */ -#define PM_GET_DEVTYPE 1 /* Returns type of the patch mgr interface of dev */ -#define PMTYPE_FM2 1 /* 2 OP fm */ -#define PMTYPE_FM4 2 /* Mixed 4 or 2 op FM (OPL-3) */ -#define PMTYPE_WAVE 3 /* Wave table synthesizer (GUS) */ -#define PM_GET_NRPGM 2 /* Returns max # of midi programs in parm1 */ -#define PM_GET_PGMMAP 3 /* Returns map of loaded midi programs in data8 */ -#define PM_GET_PGM_PATCHES 4 /* Return list of patches of a program (parm1) */ -#define PM_GET_PATCH 5 /* Return patch header of patch parm1 */ -#define PM_SET_PATCH 6 /* Set patch header of patch parm1 */ -#define PM_READ_PATCH 7 /* Read patch (wave) data */ -#define PM_WRITE_PATCH 8 /* Write patch (wave) data */ - -/* - * Commands 0x1000 to 0xffff are for communication between the patch manager - * and the client - */ -#define _PM_LOAD_PATCH 0x100 - -/* - * Commands above 0xffff reserved for device specific use - */ - - int parm1; - int parm2; - int parm3; - - union { - unsigned char data8[4000]; - unsigned short data16[2000]; - unsigned int data32[1000]; - struct patch_info patch; - } data; - }; - -/* - * When a patch manager daemon is present, it will be informed by the - * driver when something important happens. For example when the - * /dev/sequencer is opened or closed. A record with key == PM_K_EVENT is - * returned. The command field contains the event type: - */ -#define PM_E_OPENED 1 /* /dev/sequencer opened */ -#define PM_E_CLOSED 2 /* /dev/sequencer closed */ -#define PM_E_PATCH_RESET 3 /* SNDCTL_RESETSAMPLES called */ -#define PM_E_PATCH_LOADED 4 /* A patch has been loaded by appl */ - /* * /dev/sequencer input events. * @@ -469,8 +444,8 @@ struct patmgr_info { * Set the key field of the structure to FM_PATCH. The device field is used to * route the patch to the corresponding device. * - * For Gravis UltraSound use struct patch_info. Initialize the key field - * to GUS_PATCH. + * For wave table use struct patch_info. Initialize the key field + * to WAVE_PATCH. */ #define SEQ_PRIVATE 0xfe /* Low level HW dependent events (8 bytes) */ #define SEQ_EXTENDED 0xff /* Extended events (8 bytes) OBSOLETE */ @@ -503,7 +478,9 @@ struct synth_info { /* Read only */ #define FM_TYPE_OPL3 0x01 #define MIDI_TYPE_MPU401 0x401 -#define SAMPLE_TYPE_GUS 0x10 +#define SAMPLE_TYPE_BASIC 0x10 +#define SAMPLE_TYPE_GUS SAMPLE_TYPE_BASIC +#define SAMPLE_TYPE_WAVEFRONT 0x11 int perc_mode; /* No longer supported */ int nr_voices; @@ -540,30 +517,30 @@ typedef struct { unsigned char data[30]; } mpu_command_rec; -#define SNDCTL_MIDI_PRETIME _IOWR('m', 0, int) -#define SNDCTL_MIDI_MPUMODE _IOWR('m', 1, int) -#define SNDCTL_MIDI_MPUCMD _IOWR('m', 2, mpu_command_rec) +#define SNDCTL_MIDI_PRETIME _SIOWR('m', 0, int) +#define SNDCTL_MIDI_MPUMODE _SIOWR('m', 1, int) +#define SNDCTL_MIDI_MPUCMD _SIOWR('m', 2, mpu_command_rec) /******************************************** * IOCTL commands for /dev/dsp and /dev/audio */ -#define SNDCTL_DSP_RESET _IO ('P', 0) -#define SNDCTL_DSP_SYNC _IO ('P', 1) -#define SNDCTL_DSP_SPEED _IOWR('P', 2, int) -#define SNDCTL_DSP_STEREO _IOWR('P', 3, int) -#define SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) +#define SNDCTL_DSP_RESET _SIO ('P', 0) +#define SNDCTL_DSP_SYNC _SIO ('P', 1) +#define SNDCTL_DSP_SPEED _SIOWR('P', 2, int) +#define SNDCTL_DSP_STEREO _SIOWR('P', 3, int) +#define SNDCTL_DSP_GETBLKSIZE _SIOWR('P', 4, int) #define SNDCTL_DSP_SAMPLESIZE SNDCTL_DSP_SETFMT -#define SNDCTL_DSP_CHANNELS _IOWR('P', 6, int) +#define SNDCTL_DSP_CHANNELS _SIOWR('P', 6, int) #define SOUND_PCM_WRITE_CHANNELS SNDCTL_DSP_CHANNELS -#define SOUND_PCM_WRITE_FILTER _IOWR('P', 7, int) -#define SNDCTL_DSP_POST _IO ('P', 8) -#define SNDCTL_DSP_SUBDIVIDE _IOWR('P', 9, int) -#define SNDCTL_DSP_SETFRAGMENT _IOWR('P',10, int) +#define SOUND_PCM_WRITE_FILTER _SIOWR('P', 7, int) +#define SNDCTL_DSP_POST _SIO ('P', 8) +#define SNDCTL_DSP_SUBDIVIDE _SIOWR('P', 9, int) +#define SNDCTL_DSP_SETFRAGMENT _SIOWR('P',10, int) /* Audio data formats (Note! U8=8 and S16_LE=16 for compatibility) */ -#define SNDCTL_DSP_GETFMTS _IOR ('P',11, int) /* Returns a mask */ -#define SNDCTL_DSP_SETFMT _IOWR('P',5, int) /* Selects ONE fmt*/ +#define SNDCTL_DSP_GETFMTS _SIOR ('P',11, int) /* Returns a mask */ +#define SNDCTL_DSP_SETFMT _SIOWR('P',5, int) /* Selects ONE fmt*/ # define AFMT_QUERY 0x00000000 /* Return current fmt */ # define AFMT_MU_LAW 0x00000001 # define AFMT_A_LAW 0x00000002 @@ -588,10 +565,10 @@ typedef struct audio_buf_info { /* Note! 'bytes' could be more than fragments*fragsize */ } audio_buf_info; -#define SNDCTL_DSP_GETOSPACE _IOR ('P',12, audio_buf_info) -#define SNDCTL_DSP_GETISPACE _IOR ('P',13, audio_buf_info) -#define SNDCTL_DSP_NONBLOCK _IO ('P',14) -#define SNDCTL_DSP_GETCAPS _IOR ('P',15, int) +#define SNDCTL_DSP_GETOSPACE _SIOR ('P',12, audio_buf_info) +#define SNDCTL_DSP_GETISPACE _SIOR ('P',13, audio_buf_info) +#define SNDCTL_DSP_NONBLOCK _SIO ('P',14) +#define SNDCTL_DSP_GETCAPS _SIOR ('P',15, int) # define DSP_CAP_REVISION 0x000000ff /* Bits for revision level (0 to 255) */ # define DSP_CAP_DUPLEX 0x00000100 /* Full duplex record/playback */ # define DSP_CAP_REALTIME 0x00000200 /* Real time capability */ @@ -605,8 +582,8 @@ typedef struct audio_buf_info { # define DSP_CAP_TRIGGER 0x00001000 /* Supports SETTRIGGER */ # define DSP_CAP_MMAP 0x00002000 /* Supports mmap() */ -#define SNDCTL_DSP_GETTRIGGER _IOR ('P',16, int) -#define SNDCTL_DSP_SETTRIGGER _IOW ('P',16, int) +#define SNDCTL_DSP_GETTRIGGER _SIOR ('P',16, int) +#define SNDCTL_DSP_SETTRIGGER _SIOW ('P',16, int) # define PCM_ENABLE_INPUT 0x00000001 # define PCM_ENABLE_OUTPUT 0x00000002 @@ -616,22 +593,39 @@ typedef struct count_info { int ptr; /* Current DMA pointer value */ } count_info; -#define SNDCTL_DSP_GETIPTR _IOR ('P',17, count_info) -#define SNDCTL_DSP_GETOPTR _IOR ('P',18, count_info) +#define SNDCTL_DSP_GETIPTR _SIOR ('P',17, count_info) +#define SNDCTL_DSP_GETOPTR _SIOR ('P',18, count_info) typedef struct buffmem_desc { unsigned *buffer; int size; } buffmem_desc; -#define SNDCTL_DSP_MAPINBUF _IOR ('P', 19, buffmem_desc) -#define SNDCTL_DSP_MAPOUTBUF _IOR ('P', 20, buffmem_desc) -#define SNDCTL_DSP_SETSYNCRO _IO ('P', 21) -#define SNDCTL_DSP_SETDUPLEX _IO ('P', 22) +#define SNDCTL_DSP_MAPINBUF _SIOR ('P', 19, buffmem_desc) +#define SNDCTL_DSP_MAPOUTBUF _SIOR ('P', 20, buffmem_desc) +#define SNDCTL_DSP_SETSYNCRO _SIO ('P', 21) +#define SNDCTL_DSP_SETDUPLEX _SIO ('P', 22) +#define SNDCTL_DSP_GETODELAY _SIOR ('P', 23, int) + +/* + * Application's profile defines the way how playback underrun situations should be handled. + * + * APF_NORMAL (the default) and APF_NETWORK make the driver to cleanup the + * playback buffer whenever an underrun occurs. This consumes some time + * preven's looping the existing buffer. + * APF_CPUINTENS is intended to be set by CPU intensive applications which + * are likely to run out of time occasionally. In this mode the buffer cleanup is + * disabled which saves CPU time but also let's the previous buffer content to + * be played during the "pause" after the underrun. + */ +#define SNDCTL_DSP_PROFILE _SIOW ('P', 23, int) +#define APF_NORMAL 0 /* Normal applications */ +#define APF_NETWORK 1 /* Underruns probably caused by an "external" delay */ +#define APF_CPUINTENS 2 /* Underruns probably caused by "overheating" the CPU */ -#define SOUND_PCM_READ_RATE _IOR ('P', 2, int) -#define SOUND_PCM_READ_CHANNELS _IOR ('P', 6, int) -#define SOUND_PCM_READ_BITS _IOR ('P', 5, int) -#define SOUND_PCM_READ_FILTER _IOR ('P', 7, int) +#define SOUND_PCM_READ_RATE _SIOR ('P', 2, int) +#define SOUND_PCM_READ_CHANNELS _SIOR ('P', 6, int) +#define SOUND_PCM_READ_BITS _SIOR ('P', 5, int) +#define SOUND_PCM_READ_FILTER _SIOR ('P', 7, int) /* Some alias names */ #define SOUND_PCM_WRITE_BITS SNDCTL_DSP_SETFMT @@ -685,16 +679,16 @@ typedef struct copr_msg { unsigned char data[4000]; } copr_msg; -#define SNDCTL_COPR_RESET _IO ('C', 0) -#define SNDCTL_COPR_LOAD _IOWR('C', 1, copr_buffer) -#define SNDCTL_COPR_RDATA _IOWR('C', 2, copr_debug_buf) -#define SNDCTL_COPR_RCODE _IOWR('C', 3, copr_debug_buf) -#define SNDCTL_COPR_WDATA _IOW ('C', 4, copr_debug_buf) -#define SNDCTL_COPR_WCODE _IOW ('C', 5, copr_debug_buf) -#define SNDCTL_COPR_RUN _IOWR('C', 6, copr_debug_buf) -#define SNDCTL_COPR_HALT _IOWR('C', 7, copr_debug_buf) -#define SNDCTL_COPR_SENDMSG _IOWR('C', 8, copr_msg) -#define SNDCTL_COPR_RCVMSG _IOR ('C', 9, copr_msg) +#define SNDCTL_COPR_RESET _SIO ('C', 0) +#define SNDCTL_COPR_LOAD _SIOWR('C', 1, copr_buffer) +#define SNDCTL_COPR_RDATA _SIOWR('C', 2, copr_debug_buf) +#define SNDCTL_COPR_RCODE _SIOWR('C', 3, copr_debug_buf) +#define SNDCTL_COPR_WDATA _SIOW ('C', 4, copr_debug_buf) +#define SNDCTL_COPR_WCODE _SIOW ('C', 5, copr_debug_buf) +#define SNDCTL_COPR_RUN _SIOWR('C', 6, copr_debug_buf) +#define SNDCTL_COPR_HALT _SIOWR('C', 7, copr_debug_buf) +#define SNDCTL_COPR_SENDMSG _SIOWR('C', 8, copr_msg) +#define SNDCTL_COPR_RCVMSG _SIOR ('C', 9, copr_msg) /********************************************* * IOCTL commands for /dev/mixer @@ -709,7 +703,7 @@ typedef struct copr_msg { * the devices supported by the particular mixer. */ -#define SOUND_MIXER_NRDEVICES 17 +#define SOUND_MIXER_NRDEVICES 25 #define SOUND_MIXER_VOLUME 0 #define SOUND_MIXER_BASS 1 #define SOUND_MIXER_TREBLE 2 @@ -733,6 +727,14 @@ typedef struct copr_msg { #define SOUND_MIXER_LINE1 14 /* Input source 1 (aux1) */ #define SOUND_MIXER_LINE2 15 /* Input source 2 (aux2) */ #define SOUND_MIXER_LINE3 16 /* Input source 3 (line) */ +#define SOUND_MIXER_DIGITAL1 17 /* Digital (input) 1 */ +#define SOUND_MIXER_DIGITAL2 18 /* Digital (input) 2 */ +#define SOUND_MIXER_DIGITAL3 19 /* Digital (input) 3 */ +#define SOUND_MIXER_PHONEIN 20 /* Phone input */ +#define SOUND_MIXER_PHONEOUT 21 /* Phone output */ +#define SOUND_MIXER_VIDEO 22 /* Video/TV (audio) in */ +#define SOUND_MIXER_RADIO 23 /* Radio in */ +#define SOUND_MIXER_MONITOR 24 /* Monitor (usually mic) volume */ /* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */ /* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */ @@ -740,24 +742,26 @@ typedef struct copr_msg { #define SOUND_ONOFF_MAX 30 /* Note! Number 31 cannot be used since the sign bit is reserved */ - +#define SOUND_MIXER_NONE 31 /* * The following unsupported macros are no longer functional. * Use SOUND_MIXER_PRIVATE# macros in future. */ -#define SOUND_MIXER_ENHANCE 31 -#define SOUND_MIXER_MUTE 31 -#define SOUND_MIXER_LOUD 31 +#define SOUND_MIXER_ENHANCE SOUND_MIXER_NONE +#define SOUND_MIXER_MUTE SOUND_MIXER_NONE +#define SOUND_MIXER_LOUD SOUND_MIXER_NONE #define SOUND_DEVICE_LABELS {"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \ "Mic ", "CD ", "Mix ", "Pcm2 ", "Rec ", "IGain", "OGain", \ - "Line1", "Line2", "Line3"} + "Line1", "Line2", "Line3", "Digital1", "Digital2", "Digital3", \ + "PhoneIn", "PhoneOut", "Video", "Radio", "Monitor"} #define SOUND_DEVICE_NAMES {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \ "mic", "cd", "mix", "pcm2", "rec", "igain", "ogain", \ - "line1", "line2", "line3"} + "line1", "line2", "line3", "dig1", "dig2", "dig3", \ + "phin", "phout", "video", "radio", "monitor"} /* Device bitmask identifiers */ @@ -787,13 +791,21 @@ typedef struct copr_msg { #define SOUND_MASK_LINE1 (1 << SOUND_MIXER_LINE1) #define SOUND_MASK_LINE2 (1 << SOUND_MIXER_LINE2) #define SOUND_MASK_LINE3 (1 << SOUND_MIXER_LINE3) +#define SOUND_MASK_DIGITAL1 (1 << SOUND_MIXER_DIGITAL1) +#define SOUND_MASK_DIGITAL2 (1 << SOUND_MIXER_DIGITAL2) +#define SOUND_MASK_DIGITAL3 (1 << SOUND_MIXER_DIGITAL3) +#define SOUND_MASK_PHONEIN (1 << SOUND_MIXER_PHONEIN) +#define SOUND_MASK_PHONEOUT (1 << SOUND_MIXER_PHONEOUT) +#define SOUND_MASK_RADIO (1 << SOUND_MIXER_RADIO) +#define SOUND_MASK_VIDEO (1 << SOUND_MIXER_VIDEO) +#define SOUND_MASK_MONITOR (1 << SOUND_MIXER_MONITOR) /* Obsolete macros */ #define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE) #define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE) #define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD) -#define MIXER_READ(dev) _IOR('M', dev, int) +#define MIXER_READ(dev) _SIOR('M', dev, int) #define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME) #define SOUND_MIXER_READ_BASS MIXER_READ(SOUND_MIXER_BASS) #define SOUND_MIXER_READ_TREBLE MIXER_READ(SOUND_MIXER_TREBLE) @@ -823,7 +835,7 @@ typedef struct copr_msg { #define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS) #define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS) -#define MIXER_WRITE(dev) _IOWR('M', dev, int) +#define MIXER_WRITE(dev) _SIOWR('M', dev, int) #define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME) #define SOUND_MIXER_WRITE_BASS MIXER_WRITE(SOUND_MIXER_BASS) #define SOUND_MIXER_WRITE_TREBLE MIXER_WRITE(SOUND_MIXER_TREBLE) @@ -853,9 +865,18 @@ typedef struct mixer_info { char id[16]; char name[32]; + int modify_counter; + int fillers[10]; } mixer_info; -#define SOUND_MIXER_INFO _IOR ('M', 101, mixer_info) +typedef struct _old_mixer_info /* Obsolete */ +{ + char id[16]; + char name[32]; +} _old_mixer_info; + +#define SOUND_MIXER_INFO _SIOR ('M', 101, mixer_info) +#define SOUND_OLD_MIXER_INFO _SIOR ('M', 101, _old_mixer_info) /* * A mechanism for accessing "proprietary" mixer features. This method @@ -865,17 +886,42 @@ typedef struct mixer_info */ typedef unsigned char mixer_record[128]; -#define SOUND_MIXER_ACCESS _IOWR('M', 102, mixer_record) +#define SOUND_MIXER_ACCESS _SIOWR('M', 102, mixer_record) /* * The SOUND_MIXER_PRIVATE# commands can be redefined by low level drivers. * These features can be used when accessing device specific features. */ -#define SOUND_MIXER_PRIVATE1 _IOWR('M', 111, int) -#define SOUND_MIXER_PRIVATE2 _IOWR('M', 112, int) -#define SOUND_MIXER_PRIVATE3 _IOWR('M', 113, int) -#define SOUND_MIXER_PRIVATE4 _IOWR('M', 114, int) -#define SOUND_MIXER_PRIVATE5 _IOWR('M', 115, int) +#define SOUND_MIXER_PRIVATE1 _SIOWR('M', 111, int) +#define SOUND_MIXER_PRIVATE2 _SIOWR('M', 112, int) +#define SOUND_MIXER_PRIVATE3 _SIOWR('M', 113, int) +#define SOUND_MIXER_PRIVATE4 _SIOWR('M', 114, int) +#define SOUND_MIXER_PRIVATE5 _SIOWR('M', 115, int) + +/* + * SOUND_MIXER_GETLEVELS and SOUND_MIXER_SETLEVELS calls can be used + * for querying current mixer settings from the driver and for loading + * default volume settings _prior_ activating the mixer (loading + * doesn't affect current state of the mixer hardware). These calls + * are for internal use only. + */ + +typedef struct mixer_vol_table { + int num; /* Index to volume table */ + char name[32]; + int levels[32]; +} mixer_vol_table; + +#define SOUND_MIXER_GETLEVELS _SIOWR('M', 116, mixer_vol_table) +#define SOUND_MIXER_SETLEVELS _SIOWR('M', 117, mixer_vol_table) + +/* + * An ioctl for identifying the driver version. It will return value + * of the SOUND_VERSION macro used when compiling the driver. + * This call was introduced in OSS version 3.6 and it will not work + * with earlier versions (returns EINVAL). + */ +#define OSS_GETVERSION _SIOR ('M', 118, int) /* * Level 2 event types for /dev/sequencer @@ -950,10 +996,45 @@ typedef unsigned char mixer_record[128]; * * These macros define the API which should be used when possible. */ +#define SEQ_DECLAREBUF() SEQ_USE_EXTBUF() -#ifndef USE_SIMPLE_MACROS void seqbuf_dump(void); /* This function must be provided by programs */ +extern int OSS_init(int seqfd, int buflen); +extern void OSS_seqbuf_dump(int fd, unsigned char *buf, int buflen); +extern void OSS_seq_advbuf(int len, int fd, unsigned char *buf, int buflen); +extern void OSS_seq_needbuf(int len, int fd, unsigned char *buf, int buflen); +extern void OSS_patch_caching(int dev, int chn, int patch, + int fd, unsigned char *buf, int buflen); +extern void OSS_drum_caching(int dev, int chn, int patch, + int fd, unsigned char *buf, int buflen); +extern void OSS_write_patch(int fd, unsigned char *buf, int len); +extern int OSS_write_patch2(int fd, unsigned char *buf, int len); + +#define SEQ_PM_DEFINES int __foo_bar___ +#ifdef OSSLIB +# define SEQ_USE_EXTBUF() \ + extern unsigned char *_seqbuf; \ + extern int _seqbuflen;extern int _seqbufptr +# define SEQ_DEFINEBUF(len) SEQ_USE_EXTBUF();static int _requested_seqbuflen=len +# define _SEQ_ADVBUF(len) OSS_seq_advbuf(len, seqfd, _seqbuf, _seqbuflen) +# define _SEQ_NEEDBUF(len) OSS_seq_needbuf(len, seqfd, _seqbuf, _seqbuflen) +# define SEQ_DUMPBUF() OSS_seqbuf_dump(seqfd, _seqbuf, _seqbuflen) + +# define SEQ_LOAD_GMINSTR(dev, instr) \ + OSS_patch_caching(dev, -1, instr, seqfd, _seqbuf, _seqbuflen) +# define SEQ_LOAD_GMDRUM(dev, drum) \ + OSS_drum_caching(dev, -1, drum, seqfd, _seqbuf, _seqbuflen) +#else /* !OSSLIB */ + +# define SEQ_LOAD_GMINSTR(dev, instr) +# define SEQ_LOAD_GMDRUM(dev, drum) + +# define SEQ_USE_EXTBUF() \ + extern unsigned char _seqbuf[]; \ + extern int _seqbuflen;extern int _seqbufptr + +#ifndef USE_SIMPLE_MACROS /* Sample seqbuf_dump() implementation: * * SEQ_DEFINEBUF (2048); -- Defines a buffer for 2048 bytes @@ -974,9 +1055,6 @@ void seqbuf_dump(void); /* This function must be provided by programs */ */ #define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len;int _seqbufptr = 0 -#define SEQ_USE_EXTBUF() extern unsigned char _seqbuf[]; extern int _seqbuflen;extern int _seqbufptr -#define SEQ_DECLAREBUF() SEQ_USE_EXTBUF() -#define SEQ_PM_DEFINES struct patmgr_info _pm_info #define _SEQ_NEEDBUF(len) if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump() #define _SEQ_ADVBUF(len) _seqbufptr += len #define SEQ_DUMPBUF seqbuf_dump @@ -997,15 +1075,7 @@ void seqbuf_dump(void); /* This function must be provided by programs */ */ #define _SEQ_NEEDBUF(len) /* empty */ #endif - -#define PM_LOAD_PATCH(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \ - _pm_info.device=dev, _pm_info.data.data8[0]=pgm, \ - _pm_info.parm1 = bank, _pm_info.parm2 = 1, \ - ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info)) -#define PM_LOAD_PATCHES(dev, bank, pgm) (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \ - _pm_info.device=dev, memcpy(_pm_info.data.data8, pgm, 128), \ - _pm_info.parm1 = bank, _pm_info.parm2 = 128, \ - ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info)) +#endif /* !OSSLIB */ #define SEQ_VOLUME_MODE(dev, mode) {_SEQ_NEEDBUF(8);\ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\ @@ -1073,18 +1143,30 @@ void seqbuf_dump(void); /* This function must be provided by programs */ * cause fatal problems with some other devices (such as MPU401). */ #define SEQ_SYSEX(dev, buf, len) \ - {int i, l=(len); if (l>6)l=6;\ + {int ii, ll=(len); \ + unsigned char *bufp=buf;\ + if (ll>6)ll=6;\ _SEQ_NEEDBUF(8);\ _seqbuf[_seqbufptr] = EV_SYSEX;\ - for(i=0;iSampleResolution&2) + + +/* + + Because most/all of the sample data we pass in via pointers has + never been copied (just mmap-ed into user space straight from the + disk), it would be nice to allow handling of multi-channel sample + data without forcing user-level extraction of the relevant bytes. + + So, we need a way of specifying which channel to use (the WaveFront + only handles mono samples in a given slot), and the only way to do + this without using some struct other than wavefront_sample as the + interface is the awful hack of using the unused bits in a + wavefront_sample: + + Val Meaning + --- ------- + 0 no channel selection (use channel 1, sample is MONO) + 1 use first channel, and skip one + 2 use second channel, and skip one + 3 use third channel, and skip two + 4 use fourth channel, skip three + 5 use fifth channel, skip four + 6 use six channel, skip five + + + This can handle up to 4 channels, and anyone downloading >4 channels + of sample data just to select one of them needs to find some tools + like sox ... + + NOTE: values 0, 1 and 2 correspond to WF_CH_* above. This is + important. + +*/ + +#define WF_SET_CHANNEL(samp,chn) \ + (samp)->Unused1 = chn & 0x1; \ + (samp)->Unused2 = chn & 0x2; \ + (samp)->Unused3 = chn & 0x4 + +#define WF_GET_CHANNEL(samp) \ + (((samp)->Unused3 << 2)|((samp)->Unused2<<1)|(samp)->Unused1) + +typedef struct wf_sample { + struct wf_sample_offset sampleStartOffset; + struct wf_sample_offset loopStartOffset; + struct wf_sample_offset loopEndOffset; + struct wf_sample_offset sampleEndOffset; + INT16 FrequencyBias; + UCHAR8 SampleResolution:2; /* sample_format */ + UCHAR8 Unused1:1; + UCHAR8 Loop:1; + UCHAR8 Bidirectional:1; + UCHAR8 Unused2:1; + UCHAR8 Reverse:1; + UCHAR8 Unused3:1; +} wavefront_sample; + +typedef struct wf_multisample { + INT16 NumberOfSamples; /* log2 of the number of samples */ + INT16 SampleNumber[NUM_MIDIKEYS]; +} wavefront_multisample; + +typedef struct wf_alias { + INT16 OriginalSample __attribute__ ((packed)); + + struct wf_sample_offset sampleStartOffset __attribute__ ((packed)); + struct wf_sample_offset loopStartOffset __attribute__ ((packed)); + struct wf_sample_offset sampleEndOffset __attribute__ ((packed)); + struct wf_sample_offset loopEndOffset __attribute__ ((packed)); + + INT16 FrequencyBias __attribute__ ((packed)); + + UCHAR8 SampleResolution:2 __attribute__ ((packed)); + UCHAR8 Unused1:1 __attribute__ ((packed)); + UCHAR8 Loop:1 __attribute__ ((packed)); + UCHAR8 Bidirectional:1 __attribute__ ((packed)); + UCHAR8 Unused2:1 __attribute__ ((packed)); + UCHAR8 Reverse:1 __attribute__ ((packed)); + UCHAR8 Unused3:1 __attribute__ ((packed)); + + /* This structure is meant to be padded only to 16 bits on their + original. Of course, whoever wrote their documentation didn't + realize that sizeof(struct) can be >= + sum(sizeof(struct-fields)) and so thought that giving a C level + description of the structs used in WavePatch files was + sufficient. I suppose it was, as long as you remember the + standard 16->32 bit issues. + */ + + UCHAR8 sixteen_bit_padding __attribute__ ((packed)); +} wavefront_alias; + +typedef struct wf_drum { + UCHAR8 PatchNumber; + UCHAR8 MixLevel:7; + UCHAR8 Unmute:1; + UCHAR8 Group:4; + UCHAR8 Unused1:4; + UCHAR8 PanModSource:2; + UCHAR8 PanModulated:1; + UCHAR8 PanAmount:4; + UCHAR8 Unused2:1; +} wavefront_drum; + +typedef struct wf_drumkit { + struct wf_drum drum[NUM_MIDIKEYS]; +} wavefront_drumkit; + +typedef struct wf_channel_programs { + UCHAR8 Program[NUM_MIDICHANNELS]; +} wavefront_channel_programs; + +/* How to get MIDI channel status from the data returned by + a WFC_GET_CHANNEL_STATUS command (a struct wf_channel_programs) +*/ + +#define WF_CHANNEL_STATUS(ch,wcp) (wcp)[(ch/7)] & (1<<((ch)%7)) + +typedef union wf_any { + wavefront_sample s; + wavefront_multisample ms; + wavefront_alias a; + wavefront_program pr; + wavefront_patch p; + wavefront_drum d; +} wavefront_any; + +/* Hannu Savolainen hoped that his "patch_info" struct in soundcard.h + might work for other wavetable-based patch-loading situations. + Alas, his fears were correct. The WaveFront doesn't even come with + just "patches", but several different kinds of structures that + control the process of generating sound. + */ + +typedef struct wf_patch_info { + + INT16 key; /* Use WAVEFRONT_PATCH here */ + UINT16 devno; /* fill in when sending */ + UCHAR8 subkey; /* WF_ST_{SAMPLE,ALIAS,etc.} */ + UINT16 number; /* patch/sample/prog number */ + + UINT32 size; /* size of any data included in + one of the fields in `hdrptr', or + as `dataptr'. + + NOTE: for actual samples, this is + the size of the *SELECTED CHANNEL* + even if more data is actually available. + + So, a stereo sample (2 channels) of + 6000 bytes total has `size' = 3000. + + See the macros and comments for + WF_{GET,SET}_CHANNEL above. + + */ + wavefront_any *hdrptr; /* user-space ptr to hdr bytes */ + UINT16 *dataptr; /* actual sample data */ + + wavefront_any hdr; /* kernel-space copy of hdr bytes */ +} wavefront_patch_info; + +/* The maximum number of bytes we will ever move to or from user space + in response to a WFC_* command. This obviously doesn't cover + actual sample data. +*/ + +#define WF_MAX_READ sizeof(wavefront_multisample) +#define WF_MAX_WRITE sizeof(wavefront_multisample) + +/* + This allows us to execute any WF command except the download/upload + ones, which are handled differently due to copyin/copyout issues as + well as data-nybbling to/from the card. + */ + +typedef struct wavefront_control { + int devno; /* from /dev/sequencer interface */ + int cmd; /* WFC_* */ + char status; /* return status to user-space */ + unsigned char rbuf[WF_MAX_READ]; /* bytes read from card */ + unsigned char wbuf[WF_MAX_WRITE]; /* bytes written to card */ +} wavefront_control; + +/* Modulator table */ + +#define WF_MOD_LFO1 0 +#define WF_MOD_LFO2 1 +#define WF_MOD_ENV1 2 +#define WF_MOD_ENV2 3 +#define WF_MOD_KEYBOARD 4 +#define WF_MOD_LOGKEY 5 +#define WF_MOD_VELOCITY 6 +#define WF_MOD_LOGVEL 7 +#define WF_MOD_RANDOM 8 +#define WF_MOD_PRESSURE 9 +#define WF_MOD_MOD_WHEEL 10 +#define WF_MOD_1 WF_MOD_MOD_WHEEL +#define WF_MOD_BREATH 11 +#define WF_MOD_2 WF_MOD_BREATH +#define WF_MOD_FOOT 12 +#define WF_MOD_4 WF_MOD_FOOT +#define WF_MOD_VOLUME 13 +#define WF_MOD_7 WF_MOD_VOLUME +#define WF_MOD_PAN 14 +#define WF_MOD_10 WF_MOD_PAN +#define WF_MOD_EXPR 15 +#define WF_MOD_11 WF_MOD_EXPR + +/* FX-related material */ + +typedef struct wf_fx_info { + int request; /* see list below */ + int data[4]; /* we don't need much */ +} wavefront_fx_info; + +/* support for each of these will be forthcoming once I or someone + else has figured out which of the addresses on page 6 and page 7 of + the YSS225 control each parameter. Incidentally, these come from + the Windows driver interface, but again, Turtle Beach didn't + document the API to use them. +*/ + +#define WFFX_SETOUTGAIN 0 +#define WFFX_SETSTEREOOUTGAIN 1 +#define WFFX_SETREVERBIN1GAIN 2 +#define WFFX_SETREVERBIN2GAIN 3 +#define WFFX_SETREVERBIN3GAIN 4 +#define WFFX_SETCHORUSINPORT 5 +#define WFFX_SETREVERBIN1PORT 6 +#define WFFX_SETREVERBIN2PORT 7 +#define WFFX_SETREVERBIN3PORT 8 +#define WFFX_SETEFFECTPORT 9 +#define WFFX_SETAUXPORT 10 +#define WFFX_SETREVERBTYPE 11 +#define WFFX_SETREVERBDELAY 12 +#define WFFX_SETCHORUSLFO 13 +#define WFFX_SETCHORUSPMD 14 +#define WFFX_SETCHORUSAMD 15 +#define WFFX_SETEFFECT 16 +#define WFFX_SETBASEALL 17 +#define WFFX_SETREVERBALL 18 +#define WFFX_SETCHORUSALL 20 +#define WFFX_SETREVERBDEF 22 +#define WFFX_SETCHORUSDEF 23 +#define WFFX_DELAYSETINGAIN 24 +#define WFFX_DELAYSETFBGAIN 25 +#define WFFX_DELAYSETFBLPF 26 +#define WFFX_DELAYSETGAIN 27 +#define WFFX_DELAYSETTIME 28 +#define WFFX_DELAYSETFBTIME 29 +#define WFFX_DELAYSETALL 30 +#define WFFX_DELAYSETDEF 32 +#define WFFX_SDELAYSETINGAIN 33 +#define WFFX_SDELAYSETFBGAIN 34 +#define WFFX_SDELAYSETFBLPF 35 +#define WFFX_SDELAYSETGAIN 36 +#define WFFX_SDELAYSETTIME 37 +#define WFFX_SDELAYSETFBTIME 38 +#define WFFX_SDELAYSETALL 39 +#define WFFX_SDELAYSETDEF 41 +#define WFFX_DEQSETINGAIN 42 +#define WFFX_DEQSETFILTER 43 +#define WFFX_DEQSETALL 44 +#define WFFX_DEQSETDEF 46 +#define WFFX_MUTE 47 +#define WFFX_FLANGESETBALANCE 48 +#define WFFX_FLANGESETDELAY 49 +#define WFFX_FLANGESETDWFFX_TH 50 +#define WFFX_FLANGESETFBGAIN 51 +#define WFFX_FLANGESETINGAIN 52 +#define WFFX_FLANGESETLFO 53 +#define WFFX_FLANGESETALL 54 +#define WFFX_FLANGESETDEF 56 +#define WFFX_PITCHSETSHIFT 57 +#define WFFX_PITCHSETBALANCE 58 +#define WFFX_PITCHSETALL 59 +#define WFFX_PITCHSETDEF 61 +#define WFFX_SRSSETINGAIN 62 +#define WFFX_SRSSETSPACE 63 +#define WFFX_SRSSETCENTER 64 +#define WFFX_SRSSETGAIN 65 +#define WFFX_SRSSETMODE 66 +#define WFFX_SRSSETDEF 68 + +/* Allow direct user-space control over FX memory/coefficient data. + In theory this could be used to download the FX microprogram, + but it would be a little slower, and involve some weird code. + */ + +#define WFFX_MEMSET 69 + +#endif __wavefront_h__ diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 0789abba8862..ad6fa4f25e83 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -373,6 +373,9 @@ struct symbol_table symbol_table = { X(proc_dir_inode_operations), #endif + /* Modular sound */ + X(sys_open), + X(sys_read), /******************************************************** * Do not add anything below this line, * as the stacked modules depend on this! diff --git a/scripts/Makefile b/scripts/Makefile index 69be842bdf8e..3c09dfe0870d 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -10,16 +10,17 @@ ksymoops: # We allow the Makefile in drivers/sound to decide when to rebuild its # files, rather than trying to second-guess it like we did before. # -soundscript: - make -C ${TOPDIR}/drivers/sound mkscript - @echo - +#soundscript: +# make -C ${TOPDIR}/drivers/sound mkscript +# @echo +# # There is probably a better way to decide when to rebuild kconfig.tk; this # one won't catch every last change to the various Config.in files. However, # the reliance on ${TOPDIR}/Makefile makes sure we at least rebuild when the # kernel version number changes. # -kconfig.tk: soundscript ${TOPDIR}/Makefile ${TOPDIR}/arch/${ARCH}/config.in \ +# v-Soundscript +kconfig.tk: ${TOPDIR}/Makefile ${TOPDIR}/arch/${ARCH}/config.in \ tkparse ${HEADER} ${TAIL} ./tkparse < ../arch/${ARCH}/config.in > kconfig.tmp @if [ -f /usr/local/bin/wish ]; then \ -- 2.39.5