diff options
author | peter <peter@FreeBSD.org> | 1999-01-01 08:18:13 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1999-01-01 08:18:13 +0000 |
commit | d713c6aca4942e88e9afb66a012c176a3b3c93dd (patch) | |
tree | 145a2f9aaed47d75ef5882d97d90e8835c70db2c /sys/i386/isa/sound | |
parent | cfef427a396353878f1fdad225686030324bda8e (diff) | |
download | FreeBSD-src-d713c6aca4942e88e9afb66a012c176a3b3c93dd.zip FreeBSD-src-d713c6aca4942e88e9afb66a012c176a3b3c93dd.tar.gz |
Part 2 of pcvt/voxware revival. I hope I have not clobbered any other
deltas, but it is possible since I had a few merge conflicts over the last
few days while this has been sitting ready to go.
(Part 1 was committed to the config files, but cvs aborted grrr..)
Approved by: core
Diffstat (limited to 'sys/i386/isa/sound')
80 files changed, 37053 insertions, 0 deletions
diff --git a/sys/i386/isa/sound/CHANGELOG b/sys/i386/isa/sound/CHANGELOG new file mode 100644 index 0000000..04ff604 --- /dev/null +++ b/sys/i386/isa/sound/CHANGELOG @@ -0,0 +1,200 @@ +Changelog for version 3.5-alpha7 +-------------------------------- + +Since 3.5-alpha6 +- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified +configure to handle this. +- Removed initialization messages from the +modularized version. They can be enabled by using init_trace=1 in +the insmod command line (insmod sound init_trace=1). +- More AIX stuff. +- Added support for syncronizing dsp/audio devices with /dev/sequencer. +- mmap() support for dsp/audio devices. + +Since 3.5-alpha5 +- AIX port. +- Changed some xxx_PATCH macros in soundcard.h to work with + big endian machines. + +Since 3.5-alpha4 +- Removed the 'setfx' stuff from the version distributed with kernel + sources. + +Since 3.5-alpha3 +- Moved stuff from the 'setfx' program to the AudioTriX Pro driver. + +Since 3.5-alpha2 +- Modifications to makefile and configure.c. Unnecessary sources + are no longer compiled. Newly created local.h is also copied to + /etc/soundconf. "make oldconfig" reads /etc/soundconf and produces + new local.h which is compatible with current version of the driver. +- Some fixes to the SB16 support. +- Fixed random protection fault in gus_wave.c + +Since 3.5-alpha1 +- Modified to work with Linux-1.3.33 and leater +- Some minor changes + +Since 3.0.2 +- Support for CS4232 based PnP cards (AcerMagic S23 etc). +- Full duplex support for some CS4231, CS4232 and AD1845 based cards +(GUA MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards +having a codec mentioned above). +- Almost fully rewritten loadable modules support. +- Fixed some bugs. +- Huge amount of testing (more testing is still required). +- mmap() support (works with some cards). Requires much more testing. +- Sample/patch/program loading for TB Maui/Tropez. No initialization +since TB doesn't allow me to release that code. +- Using CS4231 compatible codecs as timer for /dev/music. + +Since 3.0.1 +- Added allocation of I/O ports, DMA channels and interrupts +to the initialization code. This may break modules support since +the driver may not free some resources on unload. Should be fixed soon. + +Since 3.0 +- Some important bug fixes. +- select() for /dev/dsp and /dev/audio (Linux only). +(To use select() with read, you have to call read() to start +the recording. Calling write() kills recording immediately so +use select() carefully when you are writing a half duplex app. +Full duplex mode is not implemented yet.) Select works also with +/dev/sequencer and /dev/music. Maybe with /dev/midi## too. + +Since 3.0-beta2 +- Minor fixes. +- Added Readme.cards + +Since 3.0-beta1 +- Minor fixes to the modules support. +- Eliminated call to sb_free_irq() in ad1848.c +- Rewritten MAD16&Mozart support (not tested with MAD16 Pro). +- Fix to DMA initialization of PSS cards. +- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.) +- Fixed some bugs in the PSS driver which caused I/O errors with + the MSS mode (/dev/dsp). + +Since 3.0-950506 +- Recording with GUS MAX fixed. It works when the driver is configured + to use two DMA channels with GUS MAX (16 bit ones recommended). + +Since 3.0-94xxxx +- Too many changes + +Since 3.0-940818 +- Fixes for Linux 1.1.4x. +- Disables Disney Sound System with SG NX Pro 16 (less noise). + +Since 2.90-2 +- Fixes to soundcard.h +- Non blocking mode to /dev/sequencer +- Experimental detection code for Ensoniq Soundscape. + +Since 2.90 +- Minor and major bug fixes + +Since pre-3.0-940712 +- GUS MAX support +- Partially working MSS/WSS support (could work with some cards). +- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs + (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and + GUS MAX, but it doesn't work yet. +Since pre-3.0-940426 +- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc). +This codec chip is used in various soundcards. This version is developed +for the 16 bit daughtercard of GUS. It should work with other cards also +if the following requirements are met: + - The I/O, IRQ and DMA settings are jumper selectable or + the card is initialized by booting DOS before booting Linux (etc.). + - You add the IO, IRQ and DMA settings manually to the local.h. + (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that + the base address bust be the base address of the codec chip not the + card itself. For the GUS16 these are the same but most MSS compatible + cards have the codec located at card_base+4. +- Some minor changes + +Since 2.5 (******* MAJOR REWRITE ***********) + +This version is based on v2.3. I have tried to maintain two versions +together so that this one should have the same features than v2.5. +Something may still be missing. If you notice such things, please let me +know. + +The Readme.v30 contains more details. + +- /dev/midi## devices. +- /dev/sequencer2 + +Since 2.5-beta2 +- Some fine tuning to the GUS v3.7 mixer code. +- Fixed speed limits for the plain SB (1.0 to 2.0). + +Since 2.5-beta +- Fixed OPL-3 detection with SB. Caused problems with PAS16. +- GUS v3.7 mixer support. + +Since 2.4 +- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h). +- Fixed truncated sound on /dev/dsp when the device is closed. +- Linear volume mode for GUS +- Pitch bends larger than +/- 2 octaves. +- MIDI recording for SB and SB Pro. (Untested). +- Some other fixes. +- SB16 MIDI and DSP drivers only initialized if SB16 actually installed. +- Implemented better detection for OPL-3. This should be useful if you + have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3. +- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested). + +Since 2.3b +- Fixed bug which made it impossible to make long recordings to disk. + Recording was not restarted after a buffer overflow situation. +- Limited mixer support for GUS. +- Numerous improvements to the GUS driver by Andrew Robinson. Including + some click removal etc. + +Since 2.3 +- Fixed some minor bugs in the SB16 driver. + +Since 2.2b +- Full SB16 DSP support. 8/16 bit, mono/stereo +- The SCO and FreeBSD versions should be in sync now. There are some + problems with SB16 and GUS in the freebsd versions. + The DMA buffer allocation of the SCO version has been polished but + there could still be some problems. At least it hogs memory. + The DMA channel + configuration method used in the sco/System is a hack. +- Support for the MPU emulation of the SB16. +- Some big arrays are now allocated boot time. This makes the bss segment + smaller which makes it possible to use the full driver with + NetBSD. These arrays are not allocated if no suitable soundcard is available. +- Fixed a bug in the compute_and_set_volume in gus_wave.c +- Fixed the too fast mono playback problem of SB Pro and PAS16. + +Since 2.2 +- Stereo recording for SB Pro. Somehow it was missing and nobody + had noticed it earlier. +- Minor polishing. +- Interpreting of boot time arguments (sound=) for Linux. +- Breakup of sb_dsp.c. Parts of the code has been moved to + sb_mixer.c and sb_midi.c + +Since 2.1 +- Preliminary support for SB16. + - The SB16 mixer is supported in its native mode. + - Digitized voice capability up to 44.1 kHz/8 bit/mono + (16 bit and stereo support coming in the next release). +- Fixed some bugs in the digitized voice driver for PAS16. +- Proper initialization of the SB emulation of latest PAS16 models. + +- Significantly improved /dev/dsp and /dev/audio support. + - Now supports half duplex mode. It's now possible to record and + playback without closing and reopening the device. + - It's possible to use smaller buffers than earlier. There is a new + ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4. + This call instructs the driver to use smaller buffers. The default + buffer size (0.5 to 1.0 seconds) is divided by n. Should be called + immediately after opening the device. + +Since 2.0 +Just cosmetic changes. diff --git a/sys/i386/isa/sound/COPYING b/sys/i386/isa/sound/COPYING new file mode 100644 index 0000000..d1509c5 --- /dev/null +++ b/sys/i386/isa/sound/COPYING @@ -0,0 +1,25 @@ +/* + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ diff --git a/sys/i386/isa/sound/README b/sys/i386/isa/sound/README new file mode 100644 index 0000000..1c31ac6 --- /dev/null +++ b/sys/i386/isa/sound/README @@ -0,0 +1,86 @@ +VoxWare v2.90 release notes +-------------------------- + + + This version includes some hidden features which + are described in the file experimental.txt + Some of these features are not enabled by default. Look at + experimental.txt for more info. + + I just decided to release this version with some + incompletely implemented features disabled since + there are some new features required by a popular + application. In addition there is also support + for the GUS MAX and the 16 bit sampling option of GUS. + + The MSS/WSS support works now. At least with SG NX Pro 16. + +********* IMPORTANT ***************************************** +Linux 1.0 or later is required to by this driver version. + +Don't distribute binaries which use /dev/sequencer and are +compiled with the soundcard.h of this version. They will +not work with version 2.x of the driver. +************************************************************* + + +You will need the snd-util-2.5.tar.gz and snd-data-0.1.tar.Z +packages to use this driver. They should be in the same +ftp site or BBS from where you got this driver. For +example at nic.funet.fi:pub/OS/Linux/*. + +If you are looking for the installation instructions, please +look at linux/Readme. + +Compatibility with the earlier versions +--------------------------------------- + +This version is backward compatible with the version 2.X. All programs +compiled with sys/soundcard.h of v2.X should work without problems. +PROGRAMS COMPILED WITH THE sys/soundcard.h OF THIS VERSION WILL NOT +WORK WITH v2.X DRIVER. BE CAREFUL WHEN DISTRIBUTING BINARIES COMPILED +FOR THIS VERSION. + +Contributors +------------ + +This driver contains code by several contributors. In addition several other +persons have given useful suggestions. The following is a list of major +contributors. (I could have forgotten some names.) + + Craig Metz 1/2 of the PAS16 Mixer and PCM support + Rob Hooft Volume computation algorithm for the FM synth. + Mika Liljeberg uLaw encoding and decoding routines + Greg Lee Volume computation algorithm for the GUS and + lot's of valuable suggestions. + Andy Warner ISC port + Jim Lowe FreeBSD port + Anders Baekgaard Bughunting and valuable suggestions. + Joerg Schubert SB16 DSP support. + Andrew Robinson Improvements to the GUS driver + Megens SA MIDI recording for SB and SB Pro. + Mikael Nordqvist Linear volume support for GUS. + Mikael Nordqvist Linear volume support for GUS. + Ian Hartas SVR4.2 port + Markus Aroharju and + Risto Kankkunen Major contributions to the mixer support + of GUS v3.7. + Hunyue Yau Mixer support for SG NX Pro. + Marc Hoffman PSS support. + +Regards, + +Hannu Savolainen +hannu@voxware.pp.fi + +Snail mail: Hannu Savolainen + Hiekkalaiturintie 3 A 8 + 00980 Helsinki + Finland +FAX: +358 0 341 6272 (answers if I have my machine (mgetty) on). + +NOTE! I probably don't answer to Snail mail or FAX messages. Sending answer + to each of them is simply too expensive and time consuming. However I + try to reply every email message I get (within a week). If you don't + get response, please check how your address is written in the message + header. I can't answer if I don't have a valid reply address. diff --git a/sys/i386/isa/sound/README.FREEBSD b/sys/i386/isa/sound/README.FREEBSD new file mode 100644 index 0000000..9979d12 --- /dev/null +++ b/sys/i386/isa/sound/README.FREEBSD @@ -0,0 +1,165 @@ +GUSPNP15 8/6//97 +ftp://rah.star-gate.com/pub/guspnp.tar.gz + +This sound driver is for FreeBSD 3.0-current . + + +Major enhancement we now support mapping the dma buffer to user +space for write operations only. This features is useful for +games like xquake . So far the mmap features seems to work +with the GUS PnP Pro and with the SB16. + +For xquake you will need: + +1. a few mods to the linux loadable module so just download it and + replace yours: +cd /sys/i386/ +tar -xzf linux_ioctl.tar.gz +cd /usr/src/lkm/linux +make +make install +modunload -i 0 (if you have the linux up and running) +linux +ftp://rah.star-gate.com/linux_ioctl.tar.gz + +2. Install the linux lib 2.4 package: +pkg_add linux_lib-2.4.tgz + +ftp://ftp.freebsd.org/pub/FreeBSD/packages-current/emulators/linux_lib-2.4.tgz + +If you don't have xquake you can get it from: + +3. ftp://ftp.freebsd.org/pub/idgames/idstuff/unsup/intel_linux_quake101.tgz + + +A simple test program mmap_test.c is provided in the sound driver directory. +So copy it to your home directory or favorite place and compile it. +cc -o mmap_test mmap_test.c + +record a sample session: +cat /dev/dsp >smpl + +now run mmap_test . You will hear a loud pop thats a test program +then the sample stream will loop . + + +Phew, now back to sound driver land 8) + + +This is a minor sound driver release for the GUS PnP Pro , GUS MAX, +AudioTrix Pro , SB16 and SB16 PnP. There is also an experimental +AWE sub model. + +Included Luigi's clean up work . Tnks! + +Randall Hopper's SB16 speed setting fix . Tnks! + +Fixed signal handling throught out the sound driver. The problem +first surfaced with the SB16 not generating an interrupt on the +last dma request and SB afficionados quickly pointed out the +bug in the driver. I had to put back the auto dma feature for +xquake :( + + +NOTE YOU DON'T NEED THE PNP DRIVER FOR THE GUS PNP SINCE THE DRIVER +HAS BUILTIN PNP SUPPORT FOR THE GUS. + +My P133 Bios supports PnP. To first figured out what my SB16 PnP +was being configured to I booted win95 and checked out what Win95 +ended up with . Alternatively, if you want to you can also down +load Sujal Patel's PnP driver: + + ftp://rah.start-gate.com/pub/FreeBSD-ISA_PnP_June8.tar.gz + +follow the instructions on how to configure your PnP soundcard or +ISA device. +Lugi also has Sujal's PnP driver . + + +--- + + http://www.iet.unipi.it/~luigi/pnp.c + http://www.iet.unipi.it/~luigi/pnp.h + + the two main files. Follow Sujal Patel's instructions + for installing pnp support. + + http://www.iet.unipi.it/~luigi/pnpinfo.tgz +--- + +This is my kernel configuration for my SB16 PnP: + +controller snd0 +device sb0 at isa? port 0x220 irq 10 conflicts drq 3 +device sbxvi0 at isa? port? irq? drq 5 conflicts +device opl0 at isa? port 0x388 conflicts +device sbmidi0 at isa? port 0x300 irq? conflicts + +---- + + +The difference between a GUS PnP and a GUS PnP PRO is that the Pro +comes with 512kb. I went out an got a 1mb 60ns 30pin simm and installed +it on my GUS PnP. I own a GUS PnP and a GUS PnP PRO. + + +To unpack: +cd / +mv /sys/i386/isa/sound /sys/i386/isa/sound.old +cd /sys/i386/isa/ +tar -xzf guspnp15.tar.gz + + +cd /sys/i386/conf +Edit the kernel config file in /sys/i386/conf + +IF YOU HAVE A GUS MAX or GUS add the option NOGUSPNP to the config file + OPTIONS NOGUSPNP + THEN follow the instructions for NONPNP CONFIGURATION + + +PNP CONFIGURATION +To configure your guspnp PRO if you have a motherboard which supports +PnP: + + controller snd0 + device gus0 at isa? + +NONPNP CONFIGURATION +To configure your guspnp PRO if your motherboard does NOT support +PnP: + +device gus0 at isa? port 0x220 irq 11 drq 1 flags 0x3 + + +the gus pnp is fully software configurable and the above configuration +is setup for full duplex audio. The dma channel settings are: + +drq 1 --- DMA channel for playback + +flags 0x3 --- DMAN channel for recording + +In the event that you have to configure a GUS PnP manually or a GUS MAX: + + Available IRQs for the GUS are: + 3, 5, 7, 9, 11, 12, 15 + + Available DMA channels for the GUS are: + 1, 3, 5, 6, 7 + + +config <kernel-file> +cd /sys/compile/<kernel-file> +make +make install + +THERE IS NO NEED TO BOOT TO DOS TO CONFIGURE YOUR GUS PNP. +THE DRIVER HAS BUILTIN SUPPORT FOR PNP WHICH I GOT +FROM THE GRAVIS DRIVER DEVELOPMENT KIT + +Many thanks to Brian Litzinger <brian@MediaCity.com> for +porting the sound driver 3.5 to 2.2-current. Well, thats a while +ago hence the reference to 2.2. + + Have fun, + Amancio diff --git a/sys/i386/isa/sound/Readme b/sys/i386/isa/sound/Readme new file mode 100644 index 0000000..2594143 --- /dev/null +++ b/sys/i386/isa/sound/Readme @@ -0,0 +1,248 @@ + +VoxWare v3.5-alpha5 release notes +--------------------------------- + +IMPORTANT! This version of the driver is compatible only with Linux versions + 1.3.33 and later. It may work with earlier ones as a loadable + module but... + + Also this is an ALPHA test version which has not been tested + with all cards. At least AEDSP16 support will not work. PAS16 + and PSS supports have not been tested. /dev/dsp and /dev/audio + playback with standard GUS sounds scrambled. 16 bit mode of + SB16 doesn't work. + +Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux ftp +sites). It contains much more information than this file. + +***************************************************************** +* NEW! VoxWare home page is http://personal.eunet.fi/pp/voxware * +* The file Readme.cards contains card specific instructions * +* about configuring various cards. * +***************************************************************** + +There are some programming information (little bit old) in the +Hacker's Guide +(ftp://nic.funet.fi/pub/OS/Linux/ALPHA/sound/snd-sdk-doc-0.1.ps.gz). +Believe me: The file is really there. The directory is just hidden and +you have to cd into it before the file is visible. Note: This directory +was accidently removed some time ago but it's now back. + +I have got many patches from various persons during last year. Some of +them are still on my mailbox and they should be included in versions +after v3.0 (I will not add aditional features before v3.0 is ready). + + ==================================================== +- THIS VERSION ____REQUIRES____ Linux 1.3.33 OR LATER. + ==================================================== + +- THIS VERSION MAY NOT WORK WITH Linux VERSIONS RELEASED + AFTER end of Nov 1995. If this version doesn't compile with + your kernel version, please use the sound driver version + included in your kernel. + +You will need the snd-util-3.0.tar.gz and snd-data-0.1.tar.Z +packages to use this driver. They should be in the same +ftp site or BBS from where you got this driver. For +example at nic.funet.fi:pub/OS/Linux/*. + +If you are looking for the installation instructions, please +look at linux/Readme. + +Supported soundcards +-------------------- + +Gravis Ultrasound (GUS) +GUS MAX +GUS with the 16 bit sampling daughtercard +PAS16 +Windows Sound System compatible soundcards +ECHO-PSS (cards based on the PSS architecture by Analog Devices. + Including Orchid SW32, Cardinal DSP16 among others). + (NOTE! WSS mode may not work (DMA channel setup problem)). +MediaTriX AudioTriX Pro (OPL4 and the optional effect daughtercard + require special initialization. There is a program (setfx) in + the snd-util-3.0.tar.gz package which does it). +Ensoniq SoundScape (works but needs some improvements) +MV Jazz16 based soundcards (ProSonic, 3D etc). +SoundMan Wave (recording may not work, mixer support is limited) +Mozart (OAK OTI-601 interface chip) based soundcards. +MAD16 (an interface chip by OPTi) based soundcards (TB Tropez ???). +(NOTE! The MAD16 looks similar to the Mozart chip. It could be a good +idea to configure MAD16 cards as Mozart ones. The MAD16 driver doesn't set +up MPU401 which the Mozart one does. +CS4232 based cards such as AcerMagic S23. + + +In addition all Sound Blaster models and clones (up to AWE32) work if +you want to use them. + +The Emu synthesizer chip of AWE32 is not and will not be supported. The same is +true with the ASP chip also. Creative Technology will not release detailed +information about them so it's not possible to support them. + +If you want to get support for AWE32 or ASP, please contact Creative Labs. +Ask _politely_ if they are going to support Linux. Maybe they change +their policy if there is enough demand. + +=========================================================================== +If your card is compatible with SB, MPU401 or Windows Sound System, it +may work with the driver even if it's not listed in the above list. In this +case it may require initialization using DOS. Just start DOS and cold +boot to Linux (etc.) by hitting ctrl-alt-del. +=========================================================================== + +Compatibility with the earlier versions +--------------------------------------- + +There have been some changes in soundcard.h after v2.5 of the driver +(v2.90 is compatible with this one). Binaries compiled with this version +of soundcard.h will not work with v2.0 and earlier. + +Contributors +------------ + +This driver contains code by several contributors. In addition several other +persons have given usefull suggestions. The following is a list of major +contributors. (I could have forgotten some names.) + + Craig Metz 1/2 of the PAS16 Mixer and PCM support + Rob Hooft Volume computation algorithm for the FM synth. + Mika Liljeberg uLaw encoding and decoding routines + Andy Fingerhut New ulaw conversion tables (ulaw.h) + Jeff Tranter Linux SOUND HOWTO document + Greg Lee Volume computation algorithm for the GUS and + lot's of valuable suggestions. + Andy Warner ISC port + Jim Lowe, + Amancio Hasty Jr FreeBSD/NetBSD port + Anders Baekgaard Bughunting and valuable suggestions. + Joerg Schubert SB16 DSP support. + Andrew Robinson Improvements to the GUS driver + Megens SA MIDI recording for SB and SB Pro. + Mikael Nordqvist Linear volume support for GUS and + nonblocking /dev/sequencer. + Ian Hartas SVR4.2 port + Markus Aroharju and + Risto Kankkunen Major contributions to the mixer support + of GUS v3.7. + Hunyue Yau Mixer support for SG NX Pro. + Marc Hoffman PSS support. + Rainer Vranken Initialization for Jazz16 (ProSonic, MV3D, SM Wave). + Peter Trattler Initial version of loadable module support for Linux. + JRA Gibson 16 bit mode for Jazz16 + Davor Jadrijevic MAD16 support + Gregor Hoffleit Mozart support + Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support + +There are propably many other names missing. If you have sent me some +patches and your name is not in the above list, please inform me. + +Sponsors etc. +------------- + +The following companies have greatly helped development of this driver +in form of a free copy of their product: + +Novell, Inc. UnixWare personal edition + SDK +The Santa Cruz Operation, Inc. A SCO OpenServer + SDK +Ensoniq Corp, a SoundScape card and extensive amount of assistance +MediaTriX Peripherals Inc, a AudioTriX Pro card + SDK +Acer, Inc. a pair of AcerMagic S23 cards. + +In addition the following companies have provided me sufficial amount +of technical information at least some of their products (free or $$$): + +Advanced Gravis Computer Technology Ltd. +Media Vision Inc. +Analog Devices Inc. +Logitech Inc. +Aztech Labs Inc. +Crystal Semiconductor Corporation, +Integrated Circuit Systems Inc. +OAK Technology +OPTi +Ad Lib Inc. ($$) +Music Quest Inc. ($$) +Creative Labs ($$$) + +If you have some problems +========================= + +Read the sound HOWTO (sunsite.unc.edu:/pub/Linux/docs/...?). +Also look at the home page (http://personal.eunet.fi/pp/voxware). 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 +good default configuration to use. Please try to use same I/O, DMA and IRQ +values for the soundcard 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. + + +In general the easiest way to diagnoze problems is to do "cat /dev/sndstat". + +If you get an error message, there are some problems with the driver setup: + + - "No such file or directory" tells that the device files for + the sound driver are missing. Use the script at the end of + linux/drivers/sound/Readme.linux to create them. + + - "No such device" telss that the sound driver is not in the kernel. + You have to reconfigure and recompile the kernel to have the sound + driver. Compiling the driver doesn't help alone. You have to boot + with the newly compiled one before the driver becomes active. + The Linux-HOWTO should help in this step. + +The following errors are likely with /dev/dsp and /dev/audio. + + - "No such device or address". This error message should not happen + with /dev/sndstat but it's possible with the other sound devices. + This error indicates that there are no suitable hardware for the + device file or the sound driver has been compiled without support for + this particular device. For example /dev/audio and /dev/dsp will not + work if "digitized voice support" was not enabled during "make config". + + - "Device or resource busy". Propably the IRQ (or DMA) channel + required by the soundcard 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. + + - "Invalid argument". The application is calling ioctl() + with impossible parameters. Check that the application is + for sound driver version 2.X or later. + +In general the printout of of /dev/sndstat should tell what is the problem. +It's possible that there are bugs in the sound driver but 99% of the problems +reported to me are caused by somehow incorrect setup during "make config". + +For owners of TI TM4000M notebooks +---------------------------------- + +There appears to be some kind of conflict between the sound support +(MV Jazz), mouse port and VoxWare. You could try to configure kernel +with the C&T 82C710 mouse port support disabled. + +Hannu + +Regards, + +Hannu Savolainen +hannu@voxware.pp.fi +(or Hannu.Savolainen@cctap.carel.fi in case the above bounces) + +Snail mail: Hannu Savolainen + Hiekkalaiturintie 3 A 8 + 00980 Helsinki + Finland + +NOTE! I propably don't answer to Snail mail or FAX messages. Sending answer + to each of them is simply too expensive and time consuming. However I + try to reply every email message I get (within a week). If you don't + get response, please check how your address is written in the message + header. I can't answer if I don't have a valid reply address. + +VoxWare home page is http://personal.eunet.fi/pp/voxware diff --git a/sys/i386/isa/sound/Readme.aedsp16 b/sys/i386/isa/sound/Readme.aedsp16 new file mode 100644 index 0000000..b205a9d --- /dev/null +++ b/sys/i386/isa/sound/Readme.aedsp16 @@ -0,0 +1,6 @@ +Informations 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 +informations. + + Riccardo diff --git a/sys/i386/isa/sound/Readme.cards b/sys/i386/isa/sound/Readme.cards new file mode 100644 index 0000000..fe17aa06 --- /dev/null +++ b/sys/i386/isa/sound/Readme.cards @@ -0,0 +1,845 @@ +Configuring VoxWare 3.0 (for Linux) with some most common soundcards +==================================================================== + +NOTE! This document may contain some error. Please inform me + if you find any mistakes. + +Read this before trying to configure the driver +----------------------------------------------- + +There are currently many cards that work with VoxWare. Some of the cards +have native support while the others work since they emulate some other +cards (usually SB, MSS/WSS and/or MPU401). The following cards have native +support in VoxWare. Detailed instructions for configuring these cards +will be given later in this document. + +Pro Audio Spectrum 16 (PAS16) and compatibles: + Pro Audio Spectrum 16 + Pro Audio Studio 16 + Logitech Sound Man 16 + NOTE! The original Pro Audio Spectrum as well as the PAS+ are not + and will not be supported by VoxWare. + +Media Vision Jazz16 based cards + Pro Sonic 16 + Logitech SoundMan Wave + (Other Jazz based cards should work but I don't have any reports + about them). + +Sound Blasters + SB 1.0 to 2.0 + SB Pro + SB 16 + NOTE! The ASP chip and the EMU synth of the AWE32 is not supported + since their manufacturer doesn't release information about + the card. However both the AB16ASP and the AWE32 work with + VoxWare just like a SB16. Also see the comment about some + unsupported cards at the end of this file. + SB16 compatible cards by other manufacturers than Creative. + You have been fooled since there are no SB16 compatible + cards in the market (July95). 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 could also + be a proprietary one like MV Jazz16. + +Gravis Ultrasound (GUS) + GUS + GUS + the 16 bit option + GUS MAX + GUS ACE (No MIDI port and audio recording) + +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 + don't enable MPU401 mode blindly. Many cards having native support + in VoxWare have their own MPU401 driver. Enabling the standard one + will cause a conflict with these cards. So look if your card is + in the list of supported cards before enabling MPU401. + +Windows Sound System (MSS/WSS) + Even Microsoft has discontinued their own Sound System card, they + managed to make 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 + chip. 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). + + Having a AD1848, CS4248 or CS4231 codec chip on the card is a good + sign. Even if the card is not MSS compatible, it could be easy to write + support for it to VoxWare. Note also that most MSS compatible cards + require special boot time initialization which may not be present + in VoxWare. Also some MSS compatible cards have native support in + VoxWare. Enabling the MSS support with these cards is likely to + 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 VoxWare in case you need it. + +Yamaha FM synthesizers (OPL2, OPL3 and OPL4) + Most soundcards 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 + voices than the OPL2. The OPL4 is a new chip which has a OPL3 and + a wave table synthesizer packed on the same chip. VoxWare supports + just the OPL3 mode directly. Most cards having a OPL4 (like + SM Wave and AudioTriX Pro) support the OPL4 mode using MPU401 + emulation. Writing a native OPL4 support to VoxWare is difficult + since Yamaha doesn't give information about their sample ROM chip. + + Enable the generic OPL2/OPL3 FM synthesizer support if your + card has a FM chip made by Yamaha. Don't enable it if your card + has a software (TRS) based FM emulator. + +PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC) + Analog Devices and Echo Speech have together defined a soundcard + architecture based on the above chips. The DSP chip is used + for emulation of SB Pro, FM and General MIDI/MT32. + + There are several cards based on this architecture. The most known + ones are Orchid SW32 and Cardinal DSP16. + + VoxWare supports downloading DSP algorithms to these cards. + +MediaTriX AudioTriX Pro + The ATP card is built around a CS4231 codec and a 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 + OTTO synthesizer chip used in their professional MIDI synthesizers. + Several companies (including Ensoniq, Reveal and Spea) are selling + cards based on this architecture. + +MAD16 and Mozart based cards + The Mozart (OAK OTI-601) and MAD16 Pro (OPTi 82C929) interface + chips are used in many different soundcards, including some + cards by Reveal and Turtle Beach (Tropez). Purpose of these + chips is to connect other audio components to the PC bus. The + interface chip performs address decoding for the other chips. + +Audio Excell DSP16 + Support for this card is made by Riccardo Faccetti + (riccardo@cdc8g5.cdc.polimi.it). See aedsp16.c for more info. + +Crystal CS4232 based cards such as AcerMagic S23 + CS4232 is a PnP multimedia chip which contains a CS3231A codec, + SB and MPU401 emulations. There is support for OPL3 too. + (Unfortunately the MPU401 mode doesn't work). + +Turtle Beach Maui and Tropez + VoxWare supports sample, parch and program loading commands + described in the Maui/Tropez User's manual. There is no initialization + code for Maui so it must be initialized using DOS. Audio side of Tropez + is based on the MAD16 chip (see above). + +Jumpers and software configuration +---------------------------------- + +Some of the earliest soundcards were jumper configurable. You have to +configure VoxWare to configure VoxWare 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). +Some cards made by Aztech have an EEPROM which contains the +config info. These cards behave much like hardware jumpered cards. + +Most cards have jumper for the base I/O address but other parameters +are software configurable. Sometimes there are few other jumpers too. + +Latest cards are fully software configurable or they are PnP ISA +compatible. There are no jumpers on the board. + +VoxWare handles software configurable cards automaticly. Just configure +the driver to use I/O, IRQ and DMA settings which are known to work. +You could usually use the same values than with DOS and/or Windows. +Using different settings is possible but not recommended since it may cause +some trouble (for example when warm booting from an OS to another or +when installing new hardware to the machine). + +VoxWare sets the soft configurable parameters of the card automaticly +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. + +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 +are lucky, all of them could be supported by VoxWare. 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 +cards and the ones which could be supported later. + +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 +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 VoxWare is configured to use the same settings, everything should +work OK. + + +Configuring VoxWare (with Linux) +================================ + +VoxWare sound driver is currently a part of Linux kernel distribution. The +driver files are located in directory /usr/src/linux/drivers/sound. + +**************************************************************************** +* VoxWare MUST BE CONFIGURED AND COMPILED WITH THE KERNEL. TRYING * +* TO COMPILE IT ALONE WILL _NOT_ WORK. * +* * +* ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH * +* THE KERNEL SOURCE PACKAGE YOU ARE USING. SOME ALPHA AND BETA TEST * +* VERSIONS CAN BE INSTALLED FROM A SEPARATELY DISTRIBUTED PACKAGE * +* BUT CHECK THAT THE PACKAGE IS NOT MUCH OLDER (OR NEWER) THAN THE * +* KERNEL YOU ARE USING. IT'S POSSIBLE THAT THE KERNEL/DRIVER * +* INTERFACE CHANGES BETWEEN KERNEL RELEASES WHICH MAY CAUSE SOME * +* INCOMPATIBILITY PROBLEMS. * +* * +* IN CASE YOU INSTALL A SEPARATELY DISTRIBUTED SOUND DRIVER VERSION, * +* BE SURE TO REMOVE OR RENAME THE OLD SOUND DRIVER DIRECTORY BEFORE * +* INSTALLING THE NEW ONE. LEAVING OLD FILES TO THE SOUND DRIVER * +* DIRECTORY _WILL_ CAUSE PROBLEMS WHEN THE DRIVER IS USED OR * +* COMPILED. * +**************************************************************************** + +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. + +After configuring the kernel and sound driver, run "make dep" and compile +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 +"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. + +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 +(n/y) means 'n'. To use the default value, just hit ENTER. But be careful +since using the default _doesn't_ guarantee anything. + +Note also that all questions may not be asked. The configuration program +may disable some questions dependig on the earlier choices. It may also +select some options automaticly as well. + + "ProAudioSpectrum 16 support", + - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, + ProAudio Studio 16 or Logitech SoundMan 16 (be sure that + you read the above list correctly). Don't answer 'y' if you + have some other card made by Media Vision or Logitech since they + are not PAS16 compatible. + "SoundBlaster support", + - Answer 'y' if you have an original SB card made by Creative Labs + or a full 100% hardware compatible clone (like Thunderboard or + SM Games). If your card was in the list of supported cards (above), + please look at the card specific instructions later in this file + before answering this question. For an unknown card you may answer + 'y' if the card claims to be SB compatible. + + Don't enable SB if you have a MAD16 or Mozart compatible card. + + "Generic OPL2/OPL3 FM synthesizer support", + - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + Answering 'y' is usually a safe and recommended choice. However some + cards may have software (TSR) FM emulation. Enabling FM support + with these cards may cause trouble. However I don't currently know + such cards. + "Gravis Ultrasound support", + - Answer 'y' if you have GUS or GUS MAX. Answer 'n' if you don't + have GUS since the GUS driver consumes much memory. + Currently I don't have experiences with the GUS ACE so I don't + 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 + have their own driver for MPU401. Enabling the MPU401 option with + these cards wil 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. + It's safe to answer 'y' if you have a true MPU401 MIDI interface + 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. + "PSS (ECHO-ADI2111) support", + - 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)", + - Answer 'y' if you have installed the 16 bit sampling daughtercard + to your GUS. Answer 'n' if you have GUS MAX. Enabling this option + disables GUS MAX support. + "GUS MAX support", + - Answer 'y' only if you have a GUS MAX. + "Microsoft Sound System support", + - Again think carefully before answering 'y' to this question. It's + safe to answer 'y' in case you have the original Windows Sound + System card made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). + Also you may answer 'y' in case your card was not listed earlier + in this file. For cards having native support in VoxWare, consult + the card specific instructions later in this file. Some drivers + have their own MSS support and enabling this option wil cause a + conflict. + "Ensoniq Soundscape support", + - Answer 'y' if you have a soundcard based on the Ensoniq SoundScape + chipset. Suach cards are being manufactured at least by Ensoniq, + Spea and Reveal (note that Reveal makes other cards also). + "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 or 82C929) 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 (latest ones). + "SoundBlaster Pro support", + - Enable this option if your card is SB Pro or SB16. Enable it + also with any SB Pro clones. Answering 'n' saves some amount of + memory but 'y' is the safe alterative. + "SoundBlaster 16 support", + - Enable if you have a SB16 (including the AWE32). + "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 + also affects any MPU401 and/or General MIDI compatible devices. + "FM synthesizer (YM3812/OPL-3) support", + - Answer 'y' here. + "/dev/sequencer support", + - Answering 'n' disables /dev/sequencer and /dev/music. + +Entering the I/O, IRQ and DMA config parameters +----------------------------------------------- + +After the above questions the configuration program prompts for the +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. + +Instructions for answering these questions are given in the next section. + + +Card specific information +========================= + +This section gives additional instructions about configuring some cards. +Please refer manual of your card for valid I/O, IRQ and DMA numbers. Using +the same settings with DOS/Windows and VoxWare is recommended. Using +different values could cause some problems when switching between +different operating systems. + +SoundBlasters (the original ones by Creative) +--------------------------------------------- + +It's possible to configure these cards to use different I/O, IRQ and +DMA settings. Since the available 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 +using different values with DOS and Linux is likely to cause troubles. The +DOS driver is not able to reset the card properly after warm boot from Linux +if Linux has used different IRQ or DMA values. + +The original (steam) Sound Blaster (versions 1.x and 2.x) use always +DMA1. There is no way to change it. + +The SB16 needs two DMA channels. A 8 bit one (1 or 3) is required for +8 bit operation and a 16 bit one (5, 6 or 7) for the 16 bit mode. In theory +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. + +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). + +SB Clones +--------- + +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. +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 +instructions for your card later in this file. + +For other not fully SB clones yoy may try initialization using DOS in +the following way: + + - Boot DOS so that the card specific driver gets run. + - Hit ctrl-alt-del (or use loadlin) to boot Linux. Don't + switch off power or press the reset button. + - If you use the same I/O, IRQ and DMA settings in Linux, the + card should work. + +If your card is both SB and MSS compatible, I recommend using the MSS mode. +Most cards of this kind are not able to work in the SB and the MSS mode +simultaneously. Using the MSS mode provides 16 bit recording and playback. + +ProAudioSpectrum 16 and compatibles +----------------------------------- + +There are nothing special with these cards. Just don't enable any +other cards in case you don't have them also. The PAS16 has +a SB mode so the driver config program will prompt for the SB settings +do. Use I/O 0x220 and DMA1 for the SB mode. Ensure that you assign different +IRQ numbers for the SB and PAS16 modes. + +With PAS16 you can use two audio device files at the same time. /dev/dsp (and +/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and +/dev/audio1) is connected to the SB emulation (8 bit mono only). + +Gravis Ultrasound +----------------- + +There are many different revisions of the Ultrasound card (GUS). The +earliest ones (pre 3.7) don't have a hardware mixer. With these cards +the driver uses a software emulation for synth and pcm playbacks. It's +also possible to switch some of the inputs (line in, mic) off by setting +mixer volume of the channel level below 10%. For recording you have +to select the channel as a recording source and to use volume above 10%. + +GUS 3.7 has a hardware mixer. + +GUS MAX and the 16 bit sampling daughtercard have a CS4231 codec chip which +also contains a mixer. + +Configuring GUS is simple. Just enable the GUS support and GUS MAX or +the 16 bit daughtercard if you have them. Note that enabling the daughter +card disables GUS MAX driver. + +With just the standard GUS enabled the configuration program prompts +for the I/O, IRQ and DMA numbers for the card. Use the same values than +with DOS. + +With the daughter card option enabled you will be prompted for the I/O, +IRQ and DMA numbers for the daughter card. You have to use different I/O +and DMA values than for the standard GUS. The daughter card permits +simultaneous recording and playback. Use /dev/dsp (the daughtercard) for +recording and /dev/dsp1 (GUS GF1) for playback. + +GUS MAX uses the same I/O address and IRQ settings than the original GUS +(GUS MAX = GUS + a CS4231 codec). In addition an extra DMA channel may be used. +Using two DMA channels permits simultaneous playback using two devices +(dev/dsp0 and /dev/dsp1). The second DMA channel is required for +full duplex audio. +To enable the second DMA channels, give a valid DMA channel when the config +program asks for the GUS MAX DMA (entering -1 disables the second DMA). +Using 16 bit DMA channels (5,6 or 7) is recommended. + +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. + + + +MPU401 and Windows Sound System +------------------------------- + +Again. Don't enable these options in case your card is listed +somewhere else in this file. + +Configuring these cards is obvious (or it should be). With MSS +you should propably enable the OPL3 synth also since +most MSS compatible cards have it. However check that this is true +before enabling OPL3. + +VoxWare supports more than one MPU401 compatible cards at the same time +but the config program asks config info for just the first of them. +Adding the second or third MPU interfaces must be done manually by +editing sound/local.h (after running the config program). Add defines for +MPU2_BASE & MPU2_IRQ (and MPU3_BASE & MPU3_IRQ) to the file. + +CAUTION! + +The default I/O base of Adaptec AHA-1542 SCSI controller is 0x330 which +is also the default of the MPU401 driver. Don't configure the sound driver to +use 0x330 as the MPU401 base if you have a AHA1542. The kernel will not boot +if you make this mistake. + +PSS +--- + +Even the PSS cards are compatible with SB, MSS and MPU401, you must not +enable these options when configuring the driver. The configuration +program handles these options itself. (You may use the SB, MPU and MSS options +together with PSS if you have another card on the system). + +The PSS driver enables MSS and MPU401 modes of the card. SB is not enabled +since it doesn't work concurrently with MSS. The driver loads also a +DSP algorithm which is used to for the general MIDI emulation. The +algorithm file (.ld) is read by the config program and written to a +file included when the pss.c is compiled. For this reason the config +program asks if you want to download the file. Use the genmidi.ld file +distributed with the DOS/Windows drivers of the card (don't use the mt32.ld). +With some cards the file is called 'synth.ld'. You must have access to +the file when configuring the driver. The easiest way is to mount the DOS +partition containing the file with Linux. + +It's possible to load your own DSP algorithms and run them with the card. +Look at the directory sound/pss_test for more info (in the VoxWare-3.0.tar.gz) +package. + +AudioTriX Pro +------------- + +You have to enable the OPL3 and SB (not SB Pro or SB16) drivers in addition +to the native AudioTriX driver. Don't enable MSS or MPU drivers. + +Configuring ATP is little bit tricky since it uses so many I/O, IRQ and +DMA numbers. Using the same values than with DOS/Win is a good idea. Don't +attemp to use the same IRQ or DMA channels twice. + +The SB mode of ATP is implemented so the the ATP driver just enables SB +in the proper address. The SB driver handles the rest. Yoy have to configure +both the SB driver and the SB mode of ATP to use the same IRQ, DMA and I/O +settings. + +Also the ATP has a microcontroller for the General MIDI emulation (OPL4). +For this reason the driver asks for the name of a file containing the +microcode (TRXPRO.HEX). This file is usually located in the directory +where the DOS drivers were installed. You must have access to this file +when configuring the driver. + +If you have the effects daughtercard, it must be initialized by running +the setfx program of snd-util-3.0.tar.gz package. This step is not required +when using the (future) binary distribution version of the driver. + +Ensoniq SoundScape +------------------ + +The SoundScape driver handles initialization of MSS and MPU supports +itself so you don't need to enable other drivers than SoundScape +(enable also the /dev/dsp, /dev/sequencer and MIDI supports). + +SoundScape driver uses the MSS compatible codec of the card. It's important +to note that /dev/dsp0 (/dev/dsp is linked to /dev/dsp0 by default) +doesn't work with SoundScape (yet). The 'ssinit' program needs /dev/dsp0 so +that's the reason why it's there. It's possible that 'primary' pcm channel +becomes supported later. Currently the card's firmware doesn't contain +support for it. + +With 3.0 of VoxWare you have to change your system to use /dev/dsp1 by default +so execute: cd /dev;rm dsp;ln -s dsp1 dsp after you have installed VoxWare +3.0 (first time). + +The configuration program asks two DMA channels and two interrupts. One IRQ +and one DMA is used by the MSS codec. The second IRQ is required for the +MPU401 mode (you have to use different IRQs for both purposes). +The second DMA channel is required for initialization of the microcontroller. +You have to use separate DMA channels. + +The SoundScape card has a Motorola microcontroller which must initialized +_after_ boot (the driver doesn't initialize it during boot). +The initialization is done by running the 'ssinit' program which is +distributed in the snd-util-3.0.tar.gz package. You have to edit two +defines in the ssinit.c and then compile the program. You may run ssinit +manually (after each boot) or add it to /etc/rc.d/rc.local. + +The ssinit program needs the microcode file that comes with the DOS/Windows +driver of the card. You will need to use version 1.30.00 or later +of the microcode file (sndscape.co0 or sndscape.co1 depending on +your card model). THE OLD sndscape.cod WILL NOT WORK. IT WILL HANG YOUR +MACHINE. The only way to get the new microcode file is to download +and install the DOS/Windows driver from ftp://ftp.ensoniq.com/pub. + +Then you have to select the proper microcode file to use: soundscape.co0 +is the right one for most cards and sndscape.co1 is for few (older) cards +made by Reveal and/or Spea. The driver has capability to detect the card +version during boot. Look at the boot log messages in /var/adm/messages +and locate the sound driver initialization message for the SoundScape +card. If the driver displays string <Ensoniq Soundscape (old)>, you have +an old card and you will need to use sndscape.co1. For other cards use +soundscape.co0. + +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). + +Running ssinit with a wrong version of the sndscape.co? file is not +dangerous as long as you don't try to use a file called sndscape.cod. +If you have initialized the card using a wrong microcode file (sounds +are terrible), just modify ssinit.c to use another microcode file and try +again. It's possible to use an earlier version of sndscape.co[01] but it +may sound wierd. + +Btw, The driver may complain something about "sscapeintr()" after +running ssinit. You should just ignore these messages. + +MAD16 (Pro) and Mozart +---------------------- + +You need to enable just the MAD16 /Mozart support when configuring +the driver. _Don't_ enable SB, MPU401 or MSS. However you will need the +/dev/audio, /dev/sequencer and MIDI supports. + +Mozart and OPTi 82C928 (the original MAD16) chips don't support +MPU401 mode so enter just 0 when the configuration program asks the +MPU/MIDI I/O base. The MAD16 Pro (OPTi 82C929) has MPU401 mode. + +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. + +There are some other OPTi chips which may be used in soundcards such as +82C930 and MAC32. These chips are not supported by VoxWare yet. Please +contact me if you have a soundcard which uses these chips. + +Some MAD16 based cards may cause feedback, whistle or terrible noise if the +line3 mixer channel is turned too high. + +If you have a MAD16 card which have an OPL4 (FM + Wave table) synthesizer +chip (_not_ an OPL3), you have to apped line containing #define MAD16_OPL4 +to the file linux/dirvers/sound/local.h (after running make config). + +MV Jazz (ProSonic) +------------------ + +The Jazz16 driver is just a hack made to the SB Pro driver. However it works +fairly well. You have to enable SB, SB Pro (_not_ SB16) and MPU401 supports +when configuring the driver. The configuration program asks later if you +want support for MV Jazz16 based cards (after asking SB base address). Answer +'y' here and the driver asks the second (16 bit) DMA channel. + +The Jazz16 driver uses the MPU401 driver in a way which will cause +problems if you have another MPU401 compatible card. In this case you must +give address of the Jazz16 based MPU401 interface when the config +program prompts for the MPU401 information. Then look at the MPU401 +spesific section for instructions about configuring more than one MPU401 cards. + +Logitech Soundman Wave +---------------------- + +Read the above MV Jazz spesific instructions first. + +The Logitech SoundMan Wave (don't confuse 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 +you have a SM Wave immediately after asking the second DMA channel of jazz16. +If you answer 'y', the config program will ask name of the file containing +code to be loaded to the microcontroller. The file is usually called +MIDI0001.BIN and it's located in the DOS/Windows driver directory. The file +may also be called as TSUNAMI.BIN or something else (older cards?). + +The OPL4 synth will be inaccessible without loading the microcontroller code. +Also remember to enable MPU401 support if you want to use the OPL4 mode. + +NOTE! Don't answer 'y' when the driver asks about SM Games support + (the next question after the MIDI0001.BIN name). However + aneswering 'y' is not dangerous. + +Sound Galaxies +-------------- + +There are many different Sound Galaxy cards made by Aztech. The 8 bit +ones are fully SB or SB Pro compatible and there should be no problems +with them. + +The older 16 bit cards (SG Pro16, SG NX Pro16, Nova and Lyra) have +an EEPROM chip for storing the configuration data. There is a microcontroller +which initializes the card to match the EEPROM settigs when the machine +is powered on. These cards actually behave just like they have jumpers +for all of the settings. Configure VoxWare 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. + + +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. + +Diamond +------- + +The oldest (Sierra Aria based) soundcards 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 VoxWare. + +Audio Excel DSP16 +----------------- + +See comments in aedsp16.c. + + +PCMCIA cards +------------ + +Sorry, can't help. Some cards may work and some don't. + +TI TM4000M notebooks +-------------------- + +These computers have a built in sound support based on the Jazz chipset. +Look at the instructions for MV Jazz (above). It's also important to note +that there is something wrong with the mouse port and sound at least on +some TM models. Don't enable the "C&T 82C710 mouse port support" when +configuring Linux. Having it enabled is likely to cause mysterious problems +and kernel failures when sound is used. + +Others? +------- + +Since there are so many different soundcards, 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)?). + +Cards not supported yet +======================= + +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 ctrl-alt-del to Linux. If Linux is configured +to use the sama I/O, IRQ and DMA numbers than DOS, the card could work. + +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 +the card is compatible with MSS, it's a better choise. Some cards +don't work in the SB and MSS modes at the same time. + +There are some cards which will be supported by VoxWare sooner or later +(currently at least cards based on the ESS chipset). Such cards are +so common that there is some idea in writing the driver. Check the +VoxWare home page (http://personal.eunet.fi/pp/voxware) for latest +information. + +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. +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 desicion to not support +obsolete cards. It's possible that someone else makes a separately +distributed driver (diffs) for the card. Version v4.0 will be much more +modular so making separately distributed drivers will be easier with it. +(The bad news is that v4.0 will not be available before late -96). + +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://personal.eunet.fi/pp/voxware). 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. + +Some companies don't give low level technical information about their +products to public or at least their require signing a NDA. + +I have also made decicion to not accept code based on reverse engineering +to VoxWare. There are three main reasons: First I don't want to break +relationships to sound card manufacturers. The second reason is that +maintaining and supporting a driver withoun any specs will be a pain. The +third reason is that why shoud we help such companies in selling their +products to Linux users when they don't want to sell to Linux users +at all? + +Unfortunately many of the leading soundcard manufacturers are not willing +to co-operate with Linux/Unix community. For example: Creative Technology +doesn't give information about the ASP chip and the Emu synth chip of AWE32 +and SB32. Turtle Beach don't give information about any of their +products. MediaVision requires NDA before they are willing to +give information about the Jazz16 chip (fortunately Logitech gave +the info about SM Wave). + +So at least the above three companies are out until they are willing to +release documentation about their products (the situation is the +same with many DOS based freeware/shareware games and utilities). If +you want to use Linux/Unix with their cards, please don't try to push +me. It's a better idea to contact the manufacturer and explain that +you want to use your card with Linux/Unix. You could also try to sell +your card to somebody else and then buy a card that is supported by VoxWare. + +However it's possible that things change and a driver gets written +for some of the banned cards. Please, don't send me messages asking if +there is any plans to write a driver for the cards mentioned above. I +will put any news to the VoxWare www home page (see below). + +There are some common audio chipsets that are supported yet. For example +the ESS chips and Sierra Aria. It's likely that these architectures +get some support in future but I can't make any promises. Just look +at the home page for latest info. + +Information about unsupported soundcards and chipsets is welcome as well +as free copies of soundcards, SDKs and operating systems. + +If you have any corrections and/or comments, please contact me. + +Hannu Savolainen +hannu@voxware.pp.fi +VoxWare www home page: http://personal.eunet.fi/pp/voxware + diff --git a/sys/i386/isa/sound/Readme.modules b/sys/i386/isa/sound/Readme.modules new file mode 100644 index 0000000..2dab125 --- /dev/null +++ b/sys/i386/isa/sound/Readme.modules @@ -0,0 +1,99 @@ +Building a loadable sound driver +================================ + +Loadable module support in version 3.5 of VoxWare 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 channe. +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 automaticly in case +you try to spesify 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 byffer just wastes 120k (or 250k) +of valuable physical RAM memory. + +The right buffer sice 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 doanloading the microcode + file (sndscape.co[012]) to the card. Using large buffers doesn't + increase performance in these cases. + +Configuring device parameters when loading the driver +----------------------------------------------------- + +The loadable version of the sound driver accepts now the same +sound= parameter that has been available in the LILO command line. +In this way it's possible to change I/O port, IRQ and DMA addresses +and to enable/disable various cards at load time. Normally +the driver uses the configuration parameters entered when compiling +and configuring the driver. +Look at Readme.linux for more info. + +NOTE! This method is not normally required. You should use it only when + you have to use different configuration than normally. The sound= + command line parameter is error phrone and not recommended. + +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 +init_trace=1 to the insmod command line. + +For example: + + insmod sound init_trace=1 diff --git a/sys/i386/isa/sound/Readme.v30 b/sys/i386/isa/sound/Readme.v30 new file mode 100644 index 0000000..2d358ca --- /dev/null +++ b/sys/i386/isa/sound/Readme.v30 @@ -0,0 +1,140 @@ +VoxWare v3.0 +------------ + +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 usefull for applications like MIDI sysex librarians. +There are (currently) no timing features so making music could be impossible. + +There are as many /dev/midi## devices as there are MIDI ports in the system. +The /dev/midi00 is connected to the first one, /dev/midi01 to the second etc. + +These devices work like tty devices in raw mode. Everything written to them is +sent out to the MIDI port. There is currently an extra delay of at most +1/100th of sec but it will be removed later. + +The reading algorithm is little bit more complicated. There are two different +cases: + +1) There is at least one byte in the input buffer. + +The read returns as many bytes as it can without waiting for more bytes. +For example when a process reads 100 bytes and there are 10 bytes in the +buffer, the read returns just 10 bytes. + +2) The input buffer is empty when the process calls read. + +The read waits for the first byte and then continues as in case 1. By +default it waits infinitely but there is an ioctl for setting a timeout +for this. The ioctl(fd, SNDCTL_MIDI_PRETIME, &time) changes the timeout. +The time is given in 1/10th of seconds (10 means one second). + +Other ioctl calls: + +ioctl(fd, SNDCTL_MIDI_MPUMODE, &mode) is available for full MPU-401 +compatible devices such as MPU-IPC-T, MQ PC Midi Card or MQX-32. +It's not available for the so called MPU UART ports of some soundcards +(PAS16, SB16 etc). By default the MIDI port is in UART mode after open. +If this ioctl is called with mode=1, the interface is put to the intelligent +(coprocessor) mode. NOTE! The MIDI port will be reset when this ioctl is called. +It could have some strange effects if not called immediately after open. This +vall returns EINVAL if the midi port doesn't support the MPU-401 intelligent +mode. + +ioctl(fd, SNDCTL_MIDI_MPUCMD, &cmdstruct) is valid only if the MIDI port +is put to the coprocessor mode using ioctl(SNDCTL_MIDI_MPUMODE). It's used to +send commands to a MPU-401 compatible MIDI cards. Please refer to the +MPU-401 Technical Reference Manual (or Music Quest Technical Reference +Manual) for descriptions of the commands. + +The argument of SNDCTL_MIDI_MPUCOMMAND is of type mpu_command_rec. It +has the following fields: + +typedef struct { + unsigned char cmd; + + char nr_args, nr_returns; + unsigned char data[30]; + } mpu_command_rec; + +where: + cmd Contains the command number. + nr_args Number of arguments of the command. + MUST BE INITIALIZED BEFORE CALL + nr_returns Number of bytes returned by the command. + MUST BE INITIALIZED BEFORE CALL + data Buffer for the command arguments and returned + data. + +Be extremely carefull with the nr_args and nr_returns fields. They +must match the command. An incorrect value will put the card and +the driver out of sync. Refer to the MPU-401/MQX-32M documentation for further +datails. + + + +/dev/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 automaticly (this is a responsibility/right of an application +with /dev/sequencer). The result is that a SEQ_START_NOTE() macro has +similar effects for a synth channel than on a MIDI port. This kind of +solution provides better device independence than the /dev/sequencer. The +drawback is that the new interface doesn't permit so low level access to the +device as the /dev/sequencer does. An application developer must choose between +these two interfaces. I think the old /dev/sequencer is better for applications +like module players while the new one is better for making generic sequencer +programs. + +- There are no separate MIDI devices with the /dev/sequencer2. The +ioctl(SNDCTL_SEQ_NRMIDIS) returns always zero. Instead the MIDI ports are +shown as synth devices. ioctl(SNDCTL_SEQ_NRSYNTHS) on /dev/sequencer2 will +return sum of internal synthesizers (GUS, OPL3) and MIDI ports in the systems. + +- The new interface is used much like the ordinary /dev/sequencer. The +event format is new so you have to use the API macros defined in the +sys/soundcard.h. The interface is will propably change before the final 3.0 +release but using the API macros should ensure compatibility in source level. +The new event format is not recognized by version 2.X so don't try to +distribute binaries compiled with soundcard.h of v3.X. + +- The basic API useage is similar to the current one. There are some new +macros but the older ones should work as earlier. The most important +incompatibility is that the /dev/sequencer2 driver allocates voices itself. +The other one is that the application must send SEQ_START_TIMER() as 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@voxware.pp.fi diff --git a/sys/i386/isa/sound/ad1848.c b/sys/i386/isa/sound/ad1848.c new file mode 100644 index 0000000..243ed78 --- /dev/null +++ b/sys/i386/isa/sound/ad1848.c @@ -0,0 +1,1858 @@ +/* + * sound/ad1848.c + * + * Modified by Luigi Rizzo (luigi@iet.unipi.it) + * + * The low level driver for the AD1848/CS4248 codec chip which is used for + * example in the MS Sound System. + * + * The CS4231 which is used in the GUS MAX and some other cards is upwards + * compatible with AD1848 and this driver is able to drive it. + * + * CS4231A and AD1845 are upward compatible with CS4231. However the new + * features of these chips are different. + * + * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU). CS4232A is + * an improved version of CS4232. + * + * CS4236 is also a PnP audio chip similar to the 4232 + * + * OPTi931 is another high-end 1848-type chip. It differs in the use + * of the high 16 registers and configuration stuff. Luckily, being a + * PnP device, we can avoid black magic to identify the chip and be + * sure of its identity. + * + * Copyright by Hannu Savolainen 1994, 1995 + * + * 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. + * + * Modified: Riccardo Facchetti 24 Mar 1995 - Added the Audio Excel DSP 16 + * initialization routine. + */ + +#define DEB(x) +#define DEB1(x) +#include <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_AD1848) + +#include <i386/isa/sound/ad1848_mixer.h> +#include <i386/isa/sound/iwdefs.h> + +#if defined(CONFIG_CS4232) +extern struct isa_driver cssdriver; +#else +extern struct isa_driver mssdriver; +#endif + +extern void IwaveStopDma(BYTE path); + +typedef struct { + int base; + int irq; + int dual_dma; /* 1, when two DMA channels allocated */ + u_char MCE_bit; + u_char saved_regs[16]; + + int speed; + u_char speed_bits; + int channels; + int audio_format; + u_char format_bits; + + u_long xfer_count; + int irq_mode; + int intr_active; + int opened; + char *chip_name; + int mode; +#define MD_1848 1 +#define MD_4231 2 +#define MD_4231A 3 +#define MD_4236 4 +#define MD_1845 5 +#define MD_MAXMODE 6 + + /* Mixer parameters */ + int recmask; + int supported_devices; + int supported_rec_devices; + u_short levels[32]; + int dev_no; + volatile u_long timer_ticks; + int timer_running; + int irq_ok; + sound_os_info *osp; +} ad1848_info; + +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}; + +static int timer_installed = -1; +static int mute_flag = 0; +static char mixer2codec[MAX_MIXER_DEV] = {0}; + +static int ad_format_mask[MD_MAXMODE /* devc->mode */ ] = +{ + /* 0 - none */ 0, + /* 1 - AD1848 */ 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, + */ + + /* 2 - CS4231 */ AFMT_U8 | AFMT_S16_LE | AFMT_U16_LE, + + /* + * AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | + * AFMT_IMA_ADPCM, + */ + + /* 3 - CS4231A */ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, + /* 4 - AD1845 */ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, + /* 5 - CS4236 */ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, +}; + +static ad1848_info dev_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, u_int cmd, ioctl_arg arg, int local); +static void ad1848_output_block(int dev, u_long buf, int count, int intrflag, int dma_restart); +static void ad1848_start_input(int dev, u_long buf, int count, int intrflag, int dma_restart); +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); + +/* + * AD_WAIT_INIT waits if we are initializing the board and we cannot modify + * its settings + */ +#define AD_WAIT_INIT(x) {int t=x; while(t>0 && inb(devc->base) == 0x80) t-- ; } + +short ipri_to_irq(u_short ipri); + +void +adintr(unit) +{ + static short unit_to_irq[4] = {9, -1, -1, -1}; + struct isa_device *dev; + + if (unit_to_irq[unit] > 0) + ad1848_interrupt(unit_to_irq[unit]); + else { +#if defined(CONFIG_CS4232) + dev = find_isadev(isa_devtab_null, &cssdriver, unit); +#else + dev = find_isadev(isa_devtab_null, &mssdriver, unit); +#endif + if (!dev) + printf("ad1848: Couldn't determine unit\n"); + else { + unit_to_irq[unit] = ipri_to_irq(dev->id_irq); + ad1848_interrupt(unit_to_irq[unit]); + } + } +} + +static int +ad_read(ad1848_info * devc, int reg) +{ + u_long flags; + int x; + + AD_WAIT_INIT(900000); + flags = splhigh(); + outb(io_Index_Addr(devc), (u_char) (reg & 0xff) | devc->MCE_bit); + x = inb(io_Indexed_Data(devc)); + splx(flags); + + return x; +} + +static void +ad_write(ad1848_info * devc, int reg, u_char data) +{ + u_long flags; + + AD_WAIT_INIT(90000); + + flags = splhigh(); + outb(io_Index_Addr(devc), (u_char) (reg & 0xff) | devc->MCE_bit); + outb(io_Indexed_Data(devc), (u_char) (data & 0xff)); + splx(flags); +} + +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. + */ + + AD_WAIT_INIT(100000); + if (inb(devc->base) & 0x80) + printf("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 = 20000; + while (timeout > 0 && ad_read(devc, 11) & 0x20) + timeout--; + if (ad_read(devc, 11) & 0x20) + printf("ad1848: Auto calibration timed out(3).\n"); +} + +static void +ad_mute(ad1848_info * devc) +{ + int i; + u_char prev; + + mute_flag = 1; + + /* + * 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); + } +} + +static void +ad_unmute(ad1848_info * devc) +{ + int i; + + mute_flag = 0; + /* + * 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) +{ + u_long flags; + + AD_WAIT_INIT(1000); + devc->MCE_bit = 0x40; + flags = splhigh(); + if ( ( inb(io_Index_Addr(devc)) & 0x40) == 0 ) + outb(io_Index_Addr(devc), devc->MCE_bit); + splx(flags); +} + +static void +ad_leave_MCE(ad1848_info * devc) +{ + u_long flags; + u_char prev; + + AD_WAIT_INIT(1000); + + flags = splhigh(); + + devc->MCE_bit = 0x00; + prev = inb(io_Index_Addr(devc)); + /* XXX the next call is redundant ? */ + outb(io_Index_Addr(devc), 0x00); /* Clear the MCE bit */ + + if ((prev & 0x40) == 0) { /* Not in MCE mode */ + splx(flags); + return; + } + outb(io_Index_Addr(devc), 0x00); /* Clear the MCE bit */ + wait_for_calibration(devc); + splx(flags); +} + + +static int +ad1848_set_recmask(ad1848_info * devc, int mask) +{ + u_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; +} + +static void +change_bits(u_char *regval, int dev, int chn, int newval) +{ + u_char mask; + int shift; + + if (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 */ + + *regval &= ~(mask << shift); /* Clear bits */ + *regval |= (newval & mask) << shift; /* Set new value */ +} + +static int +ad1848_mixer_get(ad1848_info * devc, int dev) +{ + if (!((1 << dev) & devc->supported_devices)) + return -(EINVAL); + + return devc->levels[dev]; +} + +#define CLMICI 0x00781601 +#define CRMICI 0x00791701 + +static int +ad1848_mixer_set(ad1848_info * devc, int dev, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + int retvol; + + int regoffs; + u_char val; + /* u_char clci, crmici, clmici, clici, crici; */ + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + if (mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */ + right = left; + + retvol = left | (right << 8); + + /* Scale volumes */ + left = mix_cvt[left]; + right = mix_cvt[right]; + + /* Scale it again */ + left = mix_cvt[left]; + right = mix_cvt[right]; + + if (dev > 31) + return -(EINVAL); + + if (!(devc->supported_devices & (1 << dev))) + return -(EINVAL); + + if (mix_devices[dev][LEFT_CHN].nbits == 0) + return -(EINVAL); + + devc->levels[dev] = retvol; + + /* + * Set the left channel + */ + /* IwaveCodecMode(CODEC_MODE3); Default codec mode */ + + 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; + + /* + * Set the right channel + */ + + if (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; + + return retvol; +} + +static void +ad1848_mixer_reset(ad1848_info * devc) +{ + int i; + + devc->recmask = 0; + if (devc->mode != MD_1848) + devc->supported_devices = MODE2_MIXER_DEVICES; + else + 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); +} + +static int +ad1848_mixer_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + ad1848_info *devc; + int codec_dev = mixer2codec[dev]; + + if (!codec_dev) + return -(ENXIO); + + codec_dev--; + + devc = (ad1848_info *) audio_devs[codec_dev]->devc; + + if (((cmd >> 8) & 0xff) == 'M') { + if (cmd & IOC_IN) + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + return *(int *) arg = ad1848_set_recmask(devc, (*(int *) arg)); + break; + + default: + return *(int *) arg = ad1848_mixer_set(devc, cmd & 0xff, (*(int *) arg)); + } + else + switch (cmd & 0xff) { /* Return parameters */ + + case SOUND_MIXER_RECSRC: + return *(int *) arg = devc->recmask; + break; + + case SOUND_MIXER_DEVMASK: + return *(int *) arg = devc->supported_devices; + break; + + case SOUND_MIXER_STEREODEVS: + return *(int *) arg = devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); + break; + + case SOUND_MIXER_RECMASK: + return *(int *) arg = devc->supported_rec_devices; + break; + + case SOUND_MIXER_CAPS: + return *(int *) arg = SOUND_CAP_EXCL_INPUT; + break; + + default: + return *(int *) arg = ad1848_mixer_get(devc, cmd & 0xff); + } + } else + return -(EINVAL); +} + +static struct audio_operations ad1848_pcm_operations[MAX_AUDIO_DEV] = +{ + { + "Generic AD1848 codec", + /* DMA_AUTOMODE | DMA_DUPLEX, */ + DMA_AUTOMODE, + AFMT_U8, /* Will be set later */ + NULL, + ad1848_open, + ad1848_close, + ad1848_output_block, + ad1848_start_input, + ad1848_ioctl, + ad1848_prepare_for_IO, + ad1848_prepare_for_IO, + ad1848_reset, + ad1848_halt, + NULL, + NULL, + ad1848_halt_input, + ad1848_halt_output, + ad1848_trigger + } +}; + +static struct mixer_operations ad1848_mixer_operations = +{ + "AD1848/CS4248/CS4231/CS4236", + ad1848_mixer_ioctl +}; + +static int +ad1848_open(int dev, int mode) +{ + ad1848_info *devc = NULL; + u_long flags; + int otherside = audio_devs[dev]->otherside; + + if (dev < 0 || dev >= num_audiodevs) + return -(ENXIO); + + if (otherside != -1) { + if (audio_devs[otherside]->busy) + return -(EBUSY); + } + if (audio_devs[dev]->busy) + return -(EBUSY); + + devc = (ad1848_info *) audio_devs[dev]->devc; + + flags = splhigh(); + if (audio_devs[dev]->busy) { + splx(flags); + return -(EBUSY); + } + devc->dual_dma = 0; + + if (audio_devs[dev]->flags & DMA_DUPLEX) { + devc->dual_dma = 1; + } + devc->intr_active = 0; + audio_devs[dev]->busy = 1; + devc->irq_mode = 0; + ad1848_trigger(dev, 0); + splx(flags); + /* + * Mute output until the playback really starts. This decreases + * clicking. + */ + ad_mute(devc); + + return 0; +} + +static void +ad1848_close(int dev) +{ + u_long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + int otherside = audio_devs[dev]->otherside; + + if (otherside != -1) { + if (audio_devs[otherside]->busy) + return; + } + DEB(printf("ad1848_close(void)\n")); + + flags = splhigh(); + + ad_mute(devc); + + ad_write(devc, 9, ad_read(devc, 9) & ~0x1); + outb(io_Status(devc), 0); /* Clear interrupt status */ + /* + * ad_write (devc, 15,0); ad_write (devc, 14,0); + */ + devc->irq_mode &= ~PCM_ENABLE_OUTPUT; + + devc->intr_active = 0; + ad1848_reset(dev); + + devc->opened = 0; + devc->irq_mode = 0; + audio_devs[dev]->busy = 0; + ad_unmute(devc); + splx(flags); +} + +static int +set_speed(ad1848_info * devc, int arg) +{ + /* + * The sampling speed is encoded in the least significant nible 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; + u_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 (devc->mode == MD_1845) { /* AD1845 has different timer than others */ + RANGE (arg, 4000, 50000) ; + + devc->speed = arg; + devc->speed_bits = speed_table[selected].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) { + printf("ad1848: Can't find speed???\n"); + selected = 3; + } + devc->speed = speed_table[selected].speed; + devc->speed_bits = speed_table[selected].bits; + return devc->speed; +} + +static int +set_channels(ad1848_info * devc, int arg) +{ + if (arg != 1 && arg != 2) + return devc->channels; + + devc->channels = arg; + return arg; +} + +static int +set_format(ad1848_info * devc, int arg) +{ + static struct format_tbl { + int format; + u_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 & ad_format_mask[devc->mode])) + 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 */ + return arg; + } + /* Still hanging here. Something must be terribly wrong */ + devc->format_bits = 0; + return devc->audio_format = AFMT_U8; +} + +/* XXX check what is arg, (int) or *(int *) lr970705 */ +static int +ad1848_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + switch (cmd) { + case SOUND_PCM_WRITE_RATE: + if (local) + return set_speed(devc, (int) arg); + return *(int *) arg = set_speed(devc, (*(int *) arg)); + + case SOUND_PCM_READ_RATE: + if (local) + return devc->speed; + return *(int *) arg = devc->speed; + + case SNDCTL_DSP_STEREO: + if (local) + return set_channels(devc, (int) arg + 1) - 1; + return *(int *) arg = set_channels(devc, (*(int *) arg) + 1) - 1; + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return set_channels(devc, (int) arg); + return *(int *) arg = set_channels(devc, (*(int *) arg)); + + case SOUND_PCM_READ_CHANNELS: + if (local) + return devc->channels; + return *(int *) arg = devc->channels; + + case SNDCTL_DSP_SAMPLESIZE: + if (local) + return set_format(devc, (int) arg); + return *(int *) arg = set_format(devc, (*(int *) arg)); + + case SOUND_PCM_READ_BITS: + if (local) + return devc->audio_format; + return *(int *) arg = devc->audio_format; + + + case FIOASYNC: + if (local) + return 1; + return *(int *) arg = 1; + + case FIONBIO: + if (local) + return 1; + return *(int *) arg = 1; + + + default:; + } + return -(EINVAL); +} + +static void +ad1848_output_block(int dev, u_long buf, int count, int intrflag, int dma_restart) +{ + u_long flags, cnt; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + 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 (mute_flag) + ad_unmute(devc); + + if ( devc->irq_mode & PCM_ENABLE_OUTPUT && + audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && + cnt == devc->xfer_count) { + devc->irq_mode |= PCM_ENABLE_OUTPUT; + devc->intr_active = 1; + + } + flags = splhigh(); + + if (dma_restart) { + + DMAbuf_start_dma(dev, buf, count, 1); + } + ad_write(devc, 15, (u_char) (cnt & 0xff)); + ad_write(devc, 14, (u_char) ((cnt >> 8) & 0xff)); + + devc->xfer_count = cnt; + devc->irq_mode |= PCM_ENABLE_OUTPUT; + devc->intr_active = 1; + splx(flags); +} + +static void +ad1848_start_input(int dev, u_long buf, int count, + int intrflag, int dma_restart) +{ + u_long flags, cnt; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + 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->irq_mode & PCM_ENABLE_INPUT && + audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && + cnt == devc->xfer_count) { + devc->irq_mode |= PCM_ENABLE_INPUT; + devc->intr_active = 1; + return; /* Auto DMA mode on. No need to react */ + } + flags = splhigh(); + + if (dma_restart) { + /* ad1848_halt (dev); */ + DMAbuf_start_dma(dev, buf, count, 0); + } + if (devc->mode == MD_1848 || !devc->dual_dma) {/* Single DMA chan. mode */ + ad_write(devc, 15, (u_char) (cnt & 0xff)); + ad_write(devc, 14, (u_char) ((cnt >> 8) & 0xff)); + } else { /* Dual DMA channel mode */ + ad_write(devc, 31, (u_char) (cnt & 0xff)); + ad_write(devc, 30, (u_char) ((cnt >> 8) & 0xff)); + } + + /* ad_write (devc, 9, ad_read (devc, 9) | 0x02); *//* Capture enable */ + ad_unmute(devc); + + devc->xfer_count = cnt; + devc->irq_mode |= PCM_ENABLE_INPUT; + devc->intr_active = 1; + splx(flags); +} + +static int +ad1848_prepare_for_IO(int dev, int bsize, int bcount) +{ + u_char fs, old_fs; + u_long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + if (devc->irq_mode) + return 0; + + fs = devc->speed_bits | (devc->format_bits << 5); + + if (devc->channels > 1) + fs |= 0x10; + old_fs = fs; + + flags = splhigh(); + + if (devc->mode == MD_1845) { /* Use alternate speed select regs */ + fs &= 0xf0; /* Mask off the rate select bits */ + + ad_write(devc, 22, (devc->speed >> 8) & 0xff); /* Speed MSB */ + ad_write(devc, 23, devc->speed & 0xff); /* Speed LSB */ + } + + ad_enter_MCE(devc); /* Enables changes to the format select reg */ + + ad_write(devc, 8, fs); + + /* + * Write to I8 starts resyncronization. Wait until it completes. + */ + AD_WAIT_INIT(10000); + + /* + * If mode == 2 (CS4231), set I28 also. It's the capture format + * register. + */ + if (devc->mode != MD_1848) { + ad_write(devc, 28, fs); + + /* + * Write to I28 starts resyncronization. Wait until it completes. + */ + AD_WAIT_INIT(10000); + } + + ad_write(devc, 9, ad_read(devc, 9) & ~0x08); + + ad_leave_MCE(devc); + + splx(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; +} + +static void +ad1848_reset(int dev) +{ + ad1848_halt(dev); +} + +static void +ad1848_halt(int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + u_long flags; + int timeout; + + flags = splhigh(); + + ad_mute(devc); + + ad_write(devc, 9, ad_read(devc, 9) & ~0x03); /* Stop DMA */ + + ad_write(devc, 14, 0); /* Clear DMA counter */ + ad_write(devc, 15, 0); /* Clear DMA counter */ + + if (devc->mode != MD_1848) { + ad_write(devc, 30, 0); /* Clear DMA counter */ + ad_write(devc, 31, 0); /* Clear DMA counter */ + } + + for (timeout = 0; timeout < 1000 && !(inb(io_Status(devc)) & 0x01); + timeout++); /* Wait for interrupt */ + + outb(io_Status(devc), 0); /* Clear interrupt status */ + + devc->irq_mode = 0; + + /* DMAbuf_reset_dma (dev); */ + splx(flags); +} + +static void +ad1848_halt_input(int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + u_long flags; + u_char playing; + if (devc->mode == MD_1848) { + ad1848_halt(dev); + return; + } + playing = ad_read(devc, 9); + if (!(playing & 0x2)) + return; + + flags = splhigh(); + + ad_mute(devc); + ad_write(devc, 9, playing & ~0x02); /* Stop capture */ + + outb(io_Status(devc), 0); /* Clear interrupt status */ + outb(io_Status(devc), 0); /* Clear interrupt status */ + + devc->irq_mode &= ~PCM_ENABLE_INPUT; + + splx(flags); +} + +static void +ad1848_halt_output(int dev) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + u_long flags; + u_char playing; + + playing = ad_read(devc, 9); + if (!(playing & 0x1)) { + devc->irq_mode &= ~PCM_ENABLE_OUTPUT; + return; + } + /* IwaveStopDma(PLAYBACK); */ + if (devc->mode == MD_1848) { + ad1848_halt(dev); + return; + } + flags = splhigh(); + /* ad_mute (devc); */ + + ad_write(devc, 9, playing & ~0x1); + outb(io_Status(devc), 0); /* Clear interrupt status */ + /* + * ad_write (devc, 15,0); ad_write (devc, 14,0); + */ + devc->irq_mode &= ~PCM_ENABLE_OUTPUT; + + splx(flags); +} + +static void +ad1848_trigger(int dev, int state) +{ + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + u_long flags; + u_char tmp; + + flags = splhigh(); + state &= devc->irq_mode; + + tmp = ad_read(devc, 9) & ~0x03; + if (state & PCM_ENABLE_INPUT) + tmp |= 0x02; + if (state & PCM_ENABLE_OUTPUT) { + tmp |= 0x01; + } + ad_write(devc, 9, tmp); + + splx(flags); +} + + +int +ad1848_detect(int io_base, int *ad_flags, sound_os_info * osp) +{ + static int last_probe_addr=0, last_result=0; /* to avoid multiple probes*/ + int i; + ad1848_info *devc = &dev_info[nr_ad1848_devs]; + u_char tmp, tmp1, tmp2 ; + + DDB(printf("ad1848_detect(%x)\n", io_base)); + if (io_base == last_probe_addr) + return last_result; + else { + last_result = 0; /* default value for detect */ + last_probe_addr = io_base ; + } + + if (ad_flags) + *ad_flags = 0; + + if (nr_ad1848_devs >= MAX_AUDIO_DEV) { + DDB(printf("ad1848 detect error - step 0\n")); + 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->mode = MD_1848; /* AD1848 or CS4248 */ + devc->osp = osp; + + /* + * 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(printf("ad1848_detect() - step A\n")); + + if ((inb(devc->base) & 0x80) != 0x00) { /* Not a AD1848 */ + DDB(printf("ad1848 detect error - step A," + " inb(base) = 0x%02x, want 0XXX.XXXX\n", + inb(devc->base))); + return 0; + } + /* + * 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(printf("ad1848_detect() - step B, test indirect register\n")); + + ad_write(devc, 0, 0xaa); + ad_write(devc, 1, 0x45);/* 0x55 with bit 0x10 clear */ + tmp1 = ad_read(devc, 0) ; + tmp2 = ad_read(devc, 1) ; + if ( tmp1 != 0xaa || tmp2 != 0x45) { + DDB(printf("ad1848 detect error - step B (0x%02x/0x%02x) want 0xaa/0x45\n", tmp1, tmp2)); + return 0; + } + DDB(printf("ad1848_detect() - step C\n")); + ad_write(devc, 0, 0x45); + ad_write(devc, 1, 0xaa); + tmp1 = ad_read(devc, 0) ; + tmp2 = ad_read(devc, 1) ; + + if (tmp1 != 0x45 || tmp2 != 0xaa) { + DDB(printf("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(printf("ad1848_detect() - step D, last 4 bits of I12 readonly\n")); + tmp = ad_read(devc, 12); + ad_write(devc, 12, (~tmp) & 0x0f); + tmp1 = ad_read(devc, 12); + + if ((tmp & 0x0f) != (tmp1 & 0x0f)) { + DDB(printf("ad1848 detect error - step D, I12 (0x%02x was 0x%02x)\n", + tmp1, tmp)); + return 0; + } + + /* + * NOTE! Last 4 bits of the reg I12 tell the chip revision. + * 0x01=RevB + * 0x0A=RevC. also CS4231/CS4231A and OPTi931 + */ + + + /* + * 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. + */ + + DDB(printf("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(printf("ad1848 detect warning - step F(I%d/0x%02x/0x%02x)\n", + i, tmp1, tmp2)); + /* + * note - this seems to fail on the 4232 on I11. So we just break + * rather than fail. + */ + break ; /* return 0; */ + } + /* + * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit + * (0x40). The bit 0x80 is always 1 in CS4248 and CS4231. + * + * On the OPTi931, however, I12 is readonly and only contains the + * chip revision ID (as in the CS4231A). The upper bits return 0. + */ + + DDB(printf("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 ((tmp1 & 0xf0) == 0x00) { + printf("this should be an OPTi931\n"); + } else if ((tmp1 & 0xc0) == 0xC0) { + /* + * The 4231 has bit7=1 always, and bit6 we just set to 1. + * We want to check that this is really a CS4231 + * Verify that setting I0 doesn't change I16. + */ + DDB(printf("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(printf("ad1848 detect error - step H(%x)\n", tmp1)); + return 0; + } + /* + * Verify that some bits of I25 are read only. + */ + + DDB(printf("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->mode = 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(printf("ad1848_detect() - step I\n")); + id = ad_read(devc, 25) & 0xe7; + /* + * b7-b5 = version number; + * 100 : all CS4231 + * 101 : CS4231A + * + * b2-b0 = chip id; + */ + switch (id) { + + case 0xa0: + devc->chip_name = "CS4231A"; + devc->mode = MD_4231A; + break; + + case 0xa2: + devc->chip_name = "CS4232"; + devc->mode = MD_4231A; + break; + + case 0xb2: + /* strange: the 4231 data sheet says b4-b3 are XX + * so this should be the same as 0xa2 + */ + devc->chip_name = "CS4232A"; + devc->mode = MD_4231A; + 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. + */ + + tmp = ad_read(devc, 23); + + ad_write(devc, 23, ~tmp); + if (ad_read(devc, 23) != tmp) { /* AD1845 ? */ + devc->chip_name = "AD1845"; + devc->mode = MD_1845; + } + ad_write(devc, 23, tmp); /* Restore */ + break; + + case 0x83: /* CS4236 */ + case 0x03: /* Mutant CS4236 on Intel PR440fx board */ + devc->chip_name = "CS4236"; + devc->mode = MD_4236; + break; + + default: /* Assume CS4231 */ + printf("unknown id 0x%02x, assuming CS4231\n", id); + devc->mode = MD_4231; + + } + } + ad_write(devc, 25, tmp1); /* Restore bits */ + + DDB(printf("ad1848_detect() - step K\n")); + } + } + DDB(printf("ad1848_detect() - step L\n")); + + if (ad_flags) { + if (devc->mode != MD_1848) + *ad_flags |= AD_F_CS4231; + } + DDB(printf("ad1848_detect() - Detected OK\n")); + return (last_result = 1); +} + +void +ad1848_init(char *name, int io_base, int irq, + int dma_playback, int dma_capture, int share_dma, sound_os_info * 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). + */ + + /* + * Initial values for the indirect registers of CS4248/AD1848. + */ + static int init_values[] = { + 0xa8, /* MIXOUTL: src:mic, +20dB, gain +12dB */ + 0xa8, /* MIXOUTR: src:mic, +20dB, gain +12dB */ + 0x08, /* CDL Input: mute, +6dB */ + 0x08, /* CDR Input: mute, +6dB */ + 0x08, /* FML Input: mute, +6dB */ + 0x08, /* FMR Input: mute, +6dB */ + 0x80, /* DAC-L Input: enable, 0dB */ + 0x80, /* DAC-R Input: enable, 0dB */ + /* 0xa8, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, */ + 0x00, /* 8bit, lin, uns, mono, 8KHz */ + 0x0c, /* dma-cap, dma-pb, autocal, single dma, disable cap/pb */ + 0x02, /* int enable */ + 0x00, /* clear error status */ + 0x8a, /* rev. id (low bytes readonly) */ + 0x00, + 0x00, /* playback upper base count */ + 0x00, /* playback lower base count */ + + /* Positions 16 to 31 just for CS4231 and newer devices */ + /* I16-I17: alt. feature enable on the 4231, but AUXL Input + * on the OPTi931 (where the features are set elsewhere + */ + 0x81, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + int i, my_dev; + + ad1848_info *devc = &dev_info[nr_ad1848_devs]; + + if (!ad1848_detect(io_base, NULL, osp)) + return; + + devc->irq = (irq > 0) ? irq : 0; + devc->opened = 0; + devc->timer_ticks = 0; + devc->osp = osp; + + if (nr_ad1848_devs != 0) { + bcopy((char *) &ad1848_pcm_operations[0], + (char *) &ad1848_pcm_operations[nr_ad1848_devs], + sizeof(struct audio_operations)); + } + 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 */ + + if (devc->mode > MD_1848) { + if (dma_capture == dma_playback || + dma_capture == -1 || dma_playback == -1) { + ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ + ad1848_pcm_operations[nr_ad1848_devs].flags &= ~DMA_DUPLEX; + } else { + ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */ + ad1848_pcm_operations[nr_ad1848_devs].flags |= DMA_DUPLEX; + } + + ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ + for (i = 16; i < 32; i++) + ad_write(devc, i, init_values[i]); + + if (devc->mode == MD_4231A) { + /* Enable full * calibration */ + ad_write(devc, 9, init_values[9] | 0x18); + } + + if (devc->mode == MD_1845) { + /* Alternate freq select enabled */ + ad_write(devc, 27, init_values[27] | 0x08); + } + } else { + ad1848_pcm_operations[nr_ad1848_devs].flags &= ~DMA_DUPLEX; + ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ + } + + outb(io_Status(devc), 0); /* Clear pending interrupts */ + + if (name != NULL && name[0] != 0) + snprintf(ad1848_pcm_operations[nr_ad1848_devs].name, + sizeof(ad1848_pcm_operations[nr_ad1848_devs].name), + "%s (%s)", name, devc->chip_name); + else + snprintf(ad1848_pcm_operations[nr_ad1848_devs].name, + sizeof(ad1848_pcm_operations[nr_ad1848_devs].name), + "Generic audio codec (%s)", devc->chip_name); + + conf_printf2(ad1848_pcm_operations[nr_ad1848_devs].name, + devc->base, devc->irq, dma_playback, dma_capture); + + + /* ad1848_pcm_operations[nr_ad1848_devs].flags |= DMA_AUTOMODE ; */ + + if (num_audiodevs < MAX_AUDIO_DEV) { + audio_devs[my_dev = num_audiodevs++] = + &ad1848_pcm_operations[nr_ad1848_devs]; + if (irq > 0) { + audio_devs[my_dev]->devc = devc; + irq2dev[irq] = my_dev; + if (snd_set_irq_handler(devc->irq, ad1848_interrupt, devc->osp)<0) { + printf("ad1848: IRQ in use\n"); + } +#ifdef NO_IRQ_TEST + if (devc->mode != MD_1848) { + int x; + u_char tmp = ad_read(devc, 16); + + devc->timer_ticks = 0; + + ad_write(devc, 21, 0x00); /* Timer msb */ + ad_write(devc, 20, 0x10); /* Timer lsb */ + + 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) + printf("[IRQ conflict???]"); + else + devc->irq_ok = 1; + + } else + devc->irq_ok = 1; /* Couldn't test. assume it's OK */ +#else + devc->irq_ok = 1; +#endif + } else if (irq < 0) + irq2dev[-irq] = devc->dev_no = my_dev; + + audio_devs[my_dev]->otherside = -1 ; + audio_devs[my_dev]->flags |= DMA_AUTOMODE; + audio_devs[my_dev]->dmachan1 = dma_playback; + audio_devs[my_dev]->dmachan2 = dma_capture; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; + audio_devs[my_dev]->devc = devc; + audio_devs[my_dev]->format_mask = ad_format_mask[devc->mode]; + nr_ad1848_devs++; + +#ifdef CONFIG_SEQUENCER + if (devc->mode != MD_1848 && devc->irq_ok) + ad1848_tmr_install(my_dev); +#endif + + /* + * Toggle the MCE bit. It completes the initialization phase. + */ + + ad_enter_MCE(devc); /* In case the bit was off */ + ad_leave_MCE(devc); + + if (num_mixers < MAX_MIXER_DEV) { + mixer2codec[num_mixers] = my_dev + 1; + audio_devs[my_dev]->mixer_dev = num_mixers; + mixer_devs[num_mixers++] = &ad1848_mixer_operations; + ad1848_mixer_reset(devc); + } + } else + printf("AD1848: Too many PCM devices available\n"); +} + +void +ad1848_interrupt(int irq) +{ + u_char status; + ad1848_info *devc; + int dev; + + if (irq < 0 || irq > 15) + dev = -1; + else + dev = irq2dev[irq]; + + if (dev < 0 || dev >= num_audiodevs) { + for (irq = 0; irq < 17; irq++) + if (irq2dev[irq] != -1) + break; + + if (irq > 15) { + printf("ad1848.c: Bogus interrupt %d\n", irq); + return; + } + dev = irq2dev[irq]; + } + devc = (ad1848_info *) audio_devs[dev]->devc; + + status = inb(io_Status(devc)); + + if (status & 0x01) { /* we have an interrupt */ + int alt_stat = 0xff ; + + if (devc->mode != MD_1848) { + /* + * high-end devices have full-duplex dma and timer. + * the exact reason for the interrupt is in reg. I24. + * For old devices, we fake the interrupt bits, and + * determine the real reason basing on the device mode. + */ + alt_stat = ad_read(devc, 24); + if (alt_stat & 0x40) { /* Timer interrupt */ + devc->timer_ticks++; +#ifdef CONFIG_SEQUENCER + if (timer_installed == dev && devc->timer_running) + sound_timer_interrupt(); +#endif + } + } + + outb(io_Status(devc), 0); /* Clear interrupt status */ + + if (audio_devs[dev]->busy) { + + if (devc->irq_mode & PCM_ENABLE_OUTPUT && alt_stat & 0x10) + DMAbuf_outputintr(dev, 1); + + if (devc->irq_mode & PCM_ENABLE_INPUT && alt_stat & 0x20) + DMAbuf_inputintr(dev); + } + } +} + +/* + * Some extra code for the MS Sound System + */ + +#ifdef amancio +void +check_opl3(int base, struct address_info * hw_config) +{ + + if (!opl3_detect(base, hw_config->osp)) + return; + + opl3_init(0, base, hw_config->osp); +} +#endif + +/* + * this is the probe routine. Note, it is not necessary to + * go through this for PnP devices, since they are already + * indentified precisely using their PnP id. + * + */ + +int +probe_mss(struct address_info * hw_config) +{ + u_char tmp; + + DDB(printf("Entered probe_mss(io 0x%x, type %d)\n", + hw_config->io_base, hw_config->card_subtype)); + + if (hw_config->card_subtype == 1) { /* Has no IRQ/DMA registers */ + /* check_opl3(0x388, hw_config); */ + goto probe_ms_end; + } + +#if defined(CONFIG_AEDSP16) && defined(AEDSP16_MSS) + /* + * Initialize Audio Excel DSP 16 to MSS: before any operation we must + * enable MSS I/O ports. + */ + InitAEDSP16_MSS(hw_config); +#endif + + /* + * 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. + */ + + if ((tmp = inb(hw_config->io_base + 3)) == 0xff) { /* Bus float */ + DDB(printf("I/O address inactive (%x), force type 1\n", tmp)); + hw_config->card_subtype = 1 ; + goto probe_ms_end; + } + + if ((tmp & 0x3f) != 0x04 && + (tmp & 0x3f) != 0x0f && + (tmp & 0x3f) != 0x00) { + DDB(printf("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) { + printf("MSS: Bad IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) { + printf("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) { + printf("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) { + printf("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); + return 0; + } +probe_ms_end: + return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); +} + +void +attach_mss(struct address_info * hw_config) +{ + +#if 0 + /* + * XXX do we really need to detect it again ? - lr970712 + */ + if (!ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp)) + return ; +#endif + + if (hw_config->card_subtype == 1) { /* Has no IRQ/DMA registers */ + ad1848_init("MS Sound System1", hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0, hw_config->osp); + } else { + /* + * Set the IRQ and DMA addresses. + */ +#ifdef PC98 + static char interrupt_bits[13] = { + -1, -1, -1, 0x08, -1, 0x10, -1, -1, -1, -1, 0x18, -1, 0x20 + }; +#else + static char interrupt_bits[12] = { + -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 + }; +#endif + 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; + char bits = interrupt_bits[hw_config->irq]; + + if (bits == -1) + return ; + + outb(config_port, bits | 0x40); + if ((inb(version_port) & 0x40) == 0) + printf("[IRQ Conflict?]"); + + /* Write IRQ+DMA setup */ + outb(config_port, bits | dma_bits[hw_config->dma]); + + ad1848_init("MS Sound System0", hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0, hw_config->osp); + } + return ; +} + +/* + * WSS compatible PnP codec support. + * XXX I doubt it works now - lr970712 + */ + +int +probe_pnp_ad1848(struct address_info * hw_config) +{ + return ad1848_detect(hw_config->io_base, NULL, hw_config->osp); +} + +void +attach_pnp_ad1848(struct address_info * hw_config) +{ + + ad1848_init(hw_config->name, hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma2, 0, hw_config->osp); +} + +#ifdef CONFIG_SEQUENCER +/* + * Timer stuff (for /dev/music). + */ + +static u_int current_interval = 0; + +static u_int +ad1848_tmr_start(int dev, u_int usecs) +{ + u_long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + u_long xtal_nsecs; /* nanoseconds per xtal oscillaror tick */ + u_long divider; + + flags = splhigh(); + + /* + * 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->mode == 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; + + if (divider < 100) /* Don't allow shorter intervals than about 1ms */ + divider = 100; + + 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; + splx(flags); + + return current_interval = (divider * xtal_nsecs + 500) / 1000; +} + +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. + */ + + ad1848_tmr_start(dev, current_interval); + sound_timer_syncinterval(current_interval); +} + +static void +ad1848_tmr_disable(int dev) +{ + u_long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + flags = splhigh(); + ad_write(devc, 16, ad_read(devc, 16) & ~0x40); + devc->timer_running = 0; + splx(flags); +} + +static void +ad1848_tmr_restart(int dev) +{ + u_long flags; + ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; + + if (current_interval == 0) + return; + + flags = splhigh(); + ad_write(devc, 16, ad_read(devc, 16) | 0x40); + devc->timer_running = 1; + splx(flags); +} + +static struct sound_lowlev_timer ad1848_tmr = { + 0, + ad1848_tmr_start, + ad1848_tmr_disable, + ad1848_tmr_restart +}; + +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 +#endif diff --git a/sys/i386/isa/sound/ad1848_mixer.h b/sys/i386/isa/sound/ad1848_mixer.h new file mode 100644 index 0000000..7a1d323 --- /dev/null +++ b/sys/i386/isa/sound/ad1848_mixer.h @@ -0,0 +1,142 @@ +/* + * sound/ad1848_mixer.h + * + * Definitions for the mixer of AD1848 and compatible codecs. + * + * Copyright by Hannu Savolainen 1994 + * + * 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. + */ +/* + * The AD1848 codec has generic input lines called Line, Aux1 and Aux2. + * Soundcard 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 + * the second is just a generic one (line1, line2 and line3). + * (Actually this is not a mapping but rather some kind of interleaving + * solution). + */ +#define GUSMAX_MIXER +#ifdef GUSMAX_MIXER +#define MODE1_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD|SOUND_MASK_IMIX) + +#define MODE1_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_MIC | \ + SOUND_MASK_CD | \ + SOUND_MASK_IGAIN | \ + SOUND_MASK_PCM|SOUND_MASK_IMIX) + +#define MODE2_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_SPEAKER | \ + SOUND_MASK_IGAIN | \ + SOUND_MASK_PCM | SOUND_MASK_IMIX) +#else /* Generic mapping */ +#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \ + SOUND_MASK_LINE1|SOUND_MASK_IMIX) + +#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \ + SOUND_MASK_LINE2 | \ + SOUND_MASK_IGAIN | \ + SOUND_MASK_PCM | SOUND_MASK_IMIX) + +#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | SOUND_MASK_MIC | \ + SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \ + SOUND_MASK_IGAIN | \ + SOUND_MASK_PCM | SOUND_MASK_IMIX) +#endif + +struct mixer_def { + unsigned int regno: 7; + unsigned int polarity:1; /* 0=normal, 1=reversed */ + unsigned int bitpos:4; + unsigned int nbits:4; +}; + +static char mix_cvt[101] = { + 0, 0,3,7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, + 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, + 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, + 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, + 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, + 100 +}; + +typedef struct mixer_def mixer_ent; + +/* + * 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 + * 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] = { /* As used in GUS MAX */ +MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0), +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) +}; + +static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] = +{ + 0x5a5a, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x4b4b, /* FM */ + 0x4040, /* PCM */ + 0x4b4b, /* PC Speaker */ + /* 0x2020, Ext Line */ + 0x0000, /* Ext Line */ + 0x4040, /* Mic */ + 0x4b4b, /* CD */ + 0x0000, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x4b4b, /* Recording level */ + 0x2525, /* Input gain */ + 0x0000, /* Output gain */ + /* 0x4040, Line1 */ + 0x0000, /* Line1 */ + 0x0000, /* Line2 */ + 0x1515, /* Line3 (usually line in)*/ +}; + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + diff --git a/sys/i386/isa/sound/adlib_card.c b/sys/i386/isa/sound/adlib_card.c new file mode 100644 index 0000000..41039b9 --- /dev/null +++ b/sys/i386/isa/sound/adlib_card.c @@ -0,0 +1,46 @@ +/* + * sound/adlib_card.c + * + * Detection routine for the AdLib card. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_YM3812) + +void +attach_adlib_card(struct address_info * hw_config) +{ + opl3_init(hw_config->io_base, hw_config->osp); +} + +int +probe_adlib(struct address_info * hw_config) +{ + return opl3_detect(hw_config->io_base, hw_config->osp); +} + +#endif diff --git a/sys/i386/isa/sound/aedsp16.c b/sys/i386/isa/sound/aedsp16.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/sys/i386/isa/sound/aedsp16.c diff --git a/sys/i386/isa/sound/alaw.h b/sys/i386/isa/sound/alaw.h new file mode 100644 index 0000000..336bf6b --- /dev/null +++ b/sys/i386/isa/sound/alaw.h @@ -0,0 +1,73 @@ +static unsigned char alaw_linear[] = { + 45, 214, 122, 133, 0, 255, 107, 149, + 86, 171, 126, 129, 0, 255, 117, 138, + 13, 246, 120, 135, 0, 255, 99, 157, + 70, 187, 124, 131, 0, 255, 113, 142, + 61, 198, 123, 132, 0, 255, 111, 145, + 94, 163, 127, 128, 0, 255, 119, 136, + 29, 230, 121, 134, 0, 255, 103, 153, + 78, 179, 125, 130, 0, 255, 115, 140, + 37, 222, 122, 133, 0, 255, 105, 151, + 82, 175, 126, 129, 0, 255, 116, 139, + 5, 254, 120, 135, 0, 255, 97, 159, + 66, 191, 124, 131, 0, 255, 112, 143, + 53, 206, 123, 132, 0, 255, 109, 147, + 90, 167, 127, 128, 0, 255, 118, 137, + 21, 238, 121, 134, 0, 255, 101, 155, + 74, 183, 125, 130, 0, 255, 114, 141, + 49, 210, 123, 133, 0, 255, 108, 148, + 88, 169, 127, 129, 0, 255, 118, 138, + 17, 242, 121, 135, 0, 255, 100, 156, + 72, 185, 125, 131, 0, 255, 114, 142, + 64, 194, 124, 132, 0, 255, 112, 144, + 96, 161, 128, 128, 1, 255, 120, 136, + 33, 226, 122, 134, 0, 255, 104, 152, + 80, 177, 126, 130, 0, 255, 116, 140, + 41, 218, 122, 133, 0, 255, 106, 150, + 84, 173, 126, 129, 0, 255, 117, 139, + 9, 250, 120, 135, 0, 255, 98, 158, + 68, 189, 124, 131, 0, 255, 113, 143, + 57, 202, 123, 132, 0, 255, 110, 146, + 92, 165, 127, 128, 0, 255, 119, 137, + 25, 234, 121, 134, 0, 255, 102, 154, + 76, 181, 125, 130, 0, 255, 115, 141, + +}; + +#ifndef LINEAR_ALAW_NOT_WANTED +static unsigned char linear_alaw[] = { + + 252, 172, 172, 172, 172, 80, 80, 80, + 80, 208, 208, 208, 208, 16, 16, 16, + 16, 144, 144, 144, 144, 112, 112, 112, + 112, 240, 240, 240, 240, 48, 48, 48, + 48, 176, 176, 176, 176, 64, 64, 64, + 64, 192, 192, 192, 192, 0, 0, 0, + 0, 128, 128, 128, 128, 96, 96, 96, + 96, 224, 224, 224, 224, 32, 32, 32, + 160, 160, 88, 88, 216, 216, 24, 24, + 152, 152, 120, 120, 248, 248, 56, 56, + 184, 184, 72, 72, 200, 200, 8, 8, + 136, 136, 104, 104, 232, 232, 40, 40, + 168, 86, 214, 22, 150, 118, 246, 54, + 182, 70, 198, 6, 134, 102, 230, 38, + 166, 222, 158, 254, 190, 206, 142, 238, + 210, 242, 194, 226, 218, 250, 202, 234, + 235, 203, 251, 219, 227, 195, 243, 211, + 175, 239, 143, 207, 191, 255, 159, 223, + 167, 39, 231, 103, 135, 7, 199, 71, + 183, 55, 247, 119, 151, 23, 215, 87, + 87, 169, 169, 41, 41, 233, 233, 105, + 105, 137, 137, 9, 9, 201, 201, 73, + 73, 185, 185, 57, 57, 249, 249, 121, + 121, 153, 153, 25, 25, 217, 217, 89, + 89, 89, 161, 161, 161, 161, 33, 33, + 33, 33, 225, 225, 225, 225, 97, 97, + 97, 97, 129, 129, 129, 129, 1, 1, + 1, 1, 193, 193, 193, 193, 65, 65, + 65, 65, 177, 177, 177, 177, 49, 49, + 49, 49, 241, 241, 241, 241, 113, 113, + 113, 113, 145, 145, 145, 145, 17, 17, + 17, 17, 209, 209, 209, 209, 81, 253, +}; +#endif /* !LINEAR_ALAW_NOT_WANTED */ diff --git a/sys/i386/isa/sound/audio.c b/sys/i386/isa/sound/audio.c new file mode 100644 index 0000000..31f952e --- /dev/null +++ b/sys/i386/isa/sound/audio.c @@ -0,0 +1,467 @@ +/* + * sound/audio.c + * + * Device file manager for /dev/audio + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + +#ifdef CONFIG_AUDIO + +#include <i386/isa/sound/ulaw.h> +#include <i386/isa/sound/coproc.h> + +#define ON 1 +#define OFF 0 +int +DMAbuf_poll(int dev, struct fileinfo * file, int events, select_table * wait); + +int +audio_poll(int dev, struct fileinfo * file, int events, select_table * wait); + +static int wr_buff_no[MAX_AUDIO_DEV]; + /* != -1, if there is a incomplete output block in the queue. */ +static int wr_buff_size[MAX_AUDIO_DEV], wr_buff_ptr[MAX_AUDIO_DEV]; + +static int audio_mode[MAX_AUDIO_DEV]; +static int dev_nblock[MAX_AUDIO_DEV]; /* 1 if in noblocking mode */ + +#define AM_NONE 0 +#define AM_WRITE 1 +#define AM_READ 2 + +static char *wr_dma_buf[MAX_AUDIO_DEV]; +static int audio_format[MAX_AUDIO_DEV]; +static int local_conversion[MAX_AUDIO_DEV]; + +#if defined(NO_INLINE_ASM) || !defined(__i386__) +static void +translate_bytes(const u_char *table, u_char *buff, int n); + +#else +extern __inline void +translate_bytes(const void *table, void *buff, int n); +#endif + +static int +set_format(int dev, int fmt) +{ + if (fmt != AFMT_QUERY) { + + local_conversion[dev] = 0; + + if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ + if (fmt == AFMT_MU_LAW) { + fmt = AFMT_U8; + local_conversion[dev] = AFMT_MU_LAW; + } else + fmt = AFMT_U8; /* This is always supported */ + + audio_format[dev] = DMAbuf_ioctl(dev, SNDCTL_DSP_SETFMT, + (ioctl_arg) fmt, 1); + } + if (local_conversion[dev]) /* This shadows the HW format */ + return local_conversion[dev]; + + return audio_format[dev]; +} + +int +audio_open(int dev, struct fileinfo * file) +{ + int ret; + int bits; + int dev_type = dev & 0x0f; + int mode = file->mode & O_ACCMODE; + + dev = dev >> 4; + + bits = (dev_type == SND_DEV_DSP16) ? 16 : 8 ; + + if ((ret = DMAbuf_open(dev, mode)) < 0) + return ret; + + if (audio_devs[dev]->coproc) + if ((ret = audio_devs[dev]->coproc-> + open(audio_devs[dev]->coproc->devc, COPR_PCM)) < 0) { + audio_release(dev, file); + printf("Sound: Can't access coprocessor device\n"); + + return ret; + } + local_conversion[dev] = 0; + + if (DMAbuf_ioctl(dev, SNDCTL_DSP_SETFMT, (ioctl_arg) bits, 1) != bits) { + audio_release(dev, file); + return -(ENXIO); + } + + set_format(dev, (dev_type == SND_DEV_AUDIO) ? AFMT_MU_LAW : bits ) ; + + wr_buff_no[dev] = -1; + audio_mode[dev] = AM_NONE; + wr_buff_size[dev] = wr_buff_ptr[dev] = 0; + dev_nblock[dev] = 0; + + return ret; +} + +void +audio_release(int dev, struct fileinfo * file) +{ + int mode; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + if (wr_buff_no[dev] >= 0) { + DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev]); + wr_buff_no[dev] = -1; + } + if (audio_devs[dev]->coproc) + audio_devs[dev]->coproc->close(audio_devs[dev]->coproc->devc,COPR_PCM); + DMAbuf_release(dev, mode); + audio_devs[dev]->dmap_out->mapping_flags &= ~DMA_MAP_MAPPED ; + +} + +#if defined(NO_INLINE_ASM) || !defined(__i386__) +static void +translate_bytes(const u_char *table, u_char *buff, int n) +{ + u_long i; + + if (n <= 0) + return; + + for (i = 0; i < n; ++i) + buff[i] = table[buff[i]]; +} + +#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"(table), "c"(n), "D"(buff), "S"(buff) + :"bx", "cx", "di", "si", "ax"); + } +} + +#endif + +int +audio_write(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) +{ + int c, p, l; + int err; + + dev = dev >> 4; + + p = 0; + c = count; + if ((audio_mode[dev] & AM_READ) && + !(audio_devs[dev]->flags & DMA_DUPLEX)) { /* Direction change */ + wr_buff_no[dev] = -1; + } + if (audio_devs[dev]->flags & DMA_DUPLEX) + audio_mode[dev] |= AM_WRITE; + else + audio_mode[dev] = AM_WRITE; + + if (!count) { /* Flush output */ + if (wr_buff_no[dev] >= 0) { + DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev]); + wr_buff_no[dev] = -1; + } + return 0; + } + while (c) { /* Perform output blocking */ + if (wr_buff_no[dev] < 0) { + /* There is no incomplete buffers */ + if ((wr_buff_no[dev] = DMAbuf_getwrbuffer(dev, + &wr_dma_buf[dev], &wr_buff_size[dev], + dev_nblock[dev])) < 0) { + /* Handle nonblocking mode */ + if (dev_nblock[dev] && wr_buff_no[dev] == -(EAGAIN)) + return p; /* No more space. Return # of accepted bytes */ + return wr_buff_no[dev]; + } + wr_buff_ptr[dev] = 0; + } + l = c; + if (l > (wr_buff_size[dev] - wr_buff_ptr[dev])) + l = (wr_buff_size[dev] - wr_buff_ptr[dev]); + + if (!audio_devs[dev]->copy_from_user) { /* No device specific + * copy routine */ + + if (uiomove(&wr_dma_buf[dev][wr_buff_ptr[dev]], l, buf)) { + printf("sb: Bad copyin()!\n"); + }; + } else + audio_devs[dev]->copy_from_user(dev, + wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l); + + + /* + * Insert local processing here + */ + + if (local_conversion[dev] == AFMT_MU_LAW) { + translate_bytes(ulaw_dsp, + (u_char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l); + } + c -= l; + p += l; + wr_buff_ptr[dev] += l; + + if (wr_buff_ptr[dev] >= wr_buff_size[dev]) { + if ((err = DMAbuf_start_output(dev, wr_buff_no[dev], + wr_buff_ptr[dev])) < 0) { + return err; + } + wr_buff_no[dev] = -1; + } + } + return count; +} + +int +audio_read(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) +{ + int c, p, l; + char *dmabuf; + int buff_no; + + dev = dev >> 4; + p = 0; + c = count; + if ((audio_mode[dev] & AM_WRITE) && + !(audio_devs[dev]->flags & DMA_DUPLEX)) { + if (wr_buff_no[dev] >= 0) { + DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev]); + + if (!(audio_devs[dev]->flags & DMA_DUPLEX)) + wr_buff_no[dev] = -1; + } + } + if (audio_devs[dev]->flags & DMA_DUPLEX) + audio_mode[dev] |= AM_READ; + else + audio_mode[dev] = AM_READ; + + while (c) { + if ((buff_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, + dev_nblock[dev])) < 0) { + /* + * Nonblocking mode handling. Return current # of bytes + */ + + if (dev_nblock[dev] && buff_no == -(EAGAIN)) + return p; + + return buff_no; + } + if (l > c) + l = c; + + /* + * Insert any local processing here. + */ + + if (local_conversion[dev] == AFMT_MU_LAW) { + translate_bytes(dsp_ulaw, (u_char *) dmabuf, l); + } + if (uiomove(dmabuf, l, buf)) { + printf("sb: Bad copyout()!\n"); + }; + + DMAbuf_rmchars(dev, buff_no, l); + + p += l; + c -= l; + } + return count - c; +} + +int +audio_ioctl(int dev, struct fileinfo * file, u_int cmd, ioctl_arg 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 + printf("/dev/dsp%d: No coprocessor for this device\n", dev); + + return -(ENXIO); + } else + switch (cmd) { + + case SNDCTL_DSP_SYNC: + if (wr_buff_no[dev] >= 0) { + DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev]); + wr_buff_no[dev] = -1; + } + return DMAbuf_ioctl(dev, cmd, arg, 0); + break; + + case SNDCTL_DSP_POST: + if (wr_buff_no[dev] >= 0) { + DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev]); + wr_buff_no[dev] = -1; + } + return 0; + break; + + case SNDCTL_DSP_RESET: + wr_buff_no[dev] = -1; + audio_mode[dev] = AM_NONE; + return DMAbuf_ioctl(dev, cmd, arg, 0); + break; + + case SNDCTL_DSP_GETFMTS: + return *(int *) arg = audio_devs[dev]->format_mask; + break; + + case SNDCTL_DSP_SETFMT: + return *(int *) arg = set_format(dev, (*(int *) arg)); + + case SNDCTL_DSP_GETISPACE: + if ((audio_mode[dev] & AM_WRITE) && + !(audio_devs[dev]->flags & DMA_DUPLEX)) + return -(EBUSY); + + else { + audio_buf_info info; + int err = DMAbuf_ioctl(dev, cmd, (ioctl_arg) & info, 1); + + if (err < 0) + return err; + + bcopy((char *) &info, &(((char *) arg)[0]), sizeof(info)); + return 0; + } + + case SNDCTL_DSP_GETOSPACE: + if ((audio_mode[dev] & AM_READ) && + !(audio_devs[dev]->flags & DMA_DUPLEX)) + return -(EBUSY); + else { + audio_buf_info info; + int err = DMAbuf_ioctl(dev, cmd, (ioctl_arg) & info, 1); + + if (err < 0) + return err; + + if (wr_buff_no[dev] != -1) + info.bytes += wr_buff_size[dev] - wr_buff_ptr[dev]; + + bcopy((char *) &info, &(((char *) arg)[0]), sizeof(info)); + return 0; + } + + case SNDCTL_DSP_NONBLOCK: + dev_nblock[dev] = 1; + return 0; + break; + + case SNDCTL_DSP_GETCAPS: + { + int info = 1; /* Revision level of this ioctl() */ + + if (audio_devs[dev]->flags & DMA_DUPLEX) + info |= DSP_CAP_DUPLEX; + + if (audio_devs[dev]->coproc) + info |= DSP_CAP_COPROC; + + if (audio_devs[dev]->local_qlen) /* Dev. has hidden buffs */ + info |= DSP_CAP_BATCH; + + if (audio_devs[dev]->trigger) /* Supports SETTRIGGER */ + info |= DSP_CAP_TRIGGER; + + info |= DSP_CAP_MMAP; + bcopy((char *) &info, &(((char *) arg)[0]), sizeof(info)); + return 0; + } + break; + + + case FIOASYNC: + return *(int *) arg = 1; + + case FIONBIO: + return *(int *) arg = 1; + + default: + return DMAbuf_ioctl(dev, cmd, arg, 0); + } +} + +#ifdef ALLOW_POLL +/* + * XXX should we use spltty() in the select calls ? - lr970714 + * + */ + +int +audio_poll(int dev, struct fileinfo * file, int events, select_table * wait) +{ + dev = dev >> 4; + + if (events & (POLLIN | POLLRDNORM)) { + if ((audio_mode[dev] & AM_WRITE) && + !(audio_devs[dev]->flags & DMA_DUPLEX)) + return 0; /* Not recording */ + + + return (DMAbuf_poll(dev, file, events, wait)); + } + + if (events & (POLLOUT | POLLWRNORM)) { + if ((audio_mode[dev] & AM_READ) && + !(audio_devs[dev]->flags & DMA_DUPLEX)) + return 0; /* Wrong direction */ + + if (wr_buff_no[dev] != -1) + return 1; /* There is space in the current buffer */ + + return ( DMAbuf_poll(dev, file, events, wait) ); + + } + return 0; +} +#endif /* ALLOW_POLL */ + +#endif diff --git a/sys/i386/isa/sound/coproc.h b/sys/i386/isa/sound/coproc.h new file mode 100644 index 0000000..f902382 --- /dev/null +++ b/sys/i386/isa/sound/coproc.h @@ -0,0 +1,12 @@ +/* + * Definitions for various on board processors on the soundcards. For + * example DSP processors. + */ + +/* + * Coprocessor access types + */ +#define COPR_CUSTOM 0x0001 /* Custom applications */ +#define COPR_MIDI 0x0002 /* MIDI (MPU-401) emulation */ +#define COPR_PCM 0x0004 /* Digitized voice applications */ +#define COPR_SYNTH 0x0008 /* Music synthesis */ diff --git a/sys/i386/isa/sound/cs4232.c b/sys/i386/isa/sound/cs4232.c new file mode 100644 index 0000000..c95842a --- /dev/null +++ b/sys/i386/isa/sound/cs4232.c @@ -0,0 +1,204 @@ +/* + * sound/cs4232.c + * + * The low level driver for Crystal CS4232 based cards. The CS4232 is a PnP + * compatible chip which contains a CS4231A codec, SB emulation, a MPU401 + * compatible MIDI port, joystick and synthesizer and IDE CD-ROM interfaces. + * This is just a temporary driver until full PnP support gets inplemented. + * Just the WSS codec, FM synth and the MIDI ports are supported. Other + * interfaces are left uninitialized. + * + * Copyright by Hannu Savolainen 1995 + * + * 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_CS4232) + +#define KEY_PORT 0x279 /* Same as LPT1 status port */ +#define CSN_NUM 0x99 /* Just a random number */ + +#define CS_OUT(a) outb( KEY_PORT, a) +#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; + +int +probe_cs4232_mpu(struct address_info * hw_config) +{ + /* + * Just write down the config values. + */ + + mpu_base = hw_config->io_base; + mpu_irq = hw_config->irq; + return 0; +} + +void +attach_cs4232_mpu(struct address_info * hw_config) +{ +} + +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 +}; + +int +probe_cs4232(struct address_info * hw_config) +{ + int i; + int base = hw_config->io_base, irq = hw_config->irq; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + + /* + * Verify that the I/O port range is free. + */ + + if (0) { + printf("cs4232.c: I/O port 0x%03x not free\n", base); + return 0; + } + /* + * 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. + */ + + /* + * 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]); + + /* + * Now set the CSN (Card Select Number). + */ + + CS_OUT2(0x06, CSN_NUM); + + /* + * Ensure that there is no other codec using the same address. + */ + + CS_OUT2(0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */ + CS_OUT2(0x33, 0x00); /* Inactivate logical dev 0 */ + + /* + * 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); /* WSSbase */ + + if (0) /* Not free */ + CS_OUT3(0x48, 0x00, 0x00) /* FMbase off */ + else + CS_OUT3(0x48, 0x03, 0x88); /* FMbase 0x388 */ + + CS_OUT3(0x42, 0x00, 0x00); /* SBbase 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 */ + + /* + * Initialize logical device 3 (MPU) + */ + +#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && 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); /* MPUbase */ + CS_OUT2(0x22, mpu_irq); /* MPU IRQ */ + CS_OUT2(0x33, 0x01); /* Activate logical dev 3 */ + } +#endif + + /* + * Finally activate the chip + */ + CS_OUT(0x79); + + /* + * Then try to detect the codec part of the chip + */ + + return ad1848_detect(hw_config->io_base, NULL, hw_config->osp); +} + +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.card_subtype = 0; + hw_config2.osp = hw_config->osp; + + if (probe_mpu401(&hw_config2)) { + mpu_detected = 1; + attach_mpu401(&hw_config2); + } else { + mpu_base = mpu_irq = 0; + } + } +#endif +} + +#endif diff --git a/sys/i386/isa/sound/dev_table.c b/sys/i386/isa/sound/dev_table.c new file mode 100644 index 0000000..3061170 --- /dev/null +++ b/sys/i386/isa/sound/dev_table.c @@ -0,0 +1,303 @@ +/* + * sound/dev_table.c + * + * Device call tables. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +#define _DEV_TABLE_C_ +#include <i386/isa/sound/sound_config.h> + +#if NSND > 0 + +int sound_started = 0; + +int sndtable_get_cardcount(void); +int snd_find_driver(int type); +static void sndtable_init(void); +int sndtable_probe(int unit, struct address_info * hw_config); +int sndtable_init_card(int unit, struct address_info * hw_config); +static int sndtable_identify_card(char *name); +static void sound_chconf(int card_type, int ioaddr, int irq, int dma); +static void start_services(void); +static void start_cards(void); +struct address_info *sound_getconf(int card_type); + +int +snd_find_driver(int type) +{ + int i, n = num_sound_drivers; + + for (i = 0; i < n; i++) + if (sound_drivers[i].card_type == type) + return i; + + return -1; +} + +static void +start_services() +{ + int soundcards_installed; + + if (!(soundcards_installed = sndtable_get_cardcount())) + return ; /* No cards detected */ + +#ifdef CONFIG_AUDIO + if (num_audiodevs) /* Audio devices present */ + DMAbuf_init(); +#endif + +#ifdef CONFIG_MIDI + if (num_midis) + /* MIDIbuf_init(0) */; +#endif + +#ifdef CONFIG_SEQUENCER + if (num_midis + num_synths) + sequencer_init(); +#endif +} + +static void +start_cards() +{ + int drv, i, n = num_sound_cards; + struct card_info *ci = snd_installed_cards ; + + sound_started = 1; + if (trace_init) + printf("Sound initialization started\n"); + + /* + * 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 && ci->card_type; ci++, i++) + if (ci->enabled) { + if ((drv = snd_find_driver(ci->card_type)) == -1) { + ci->enabled = 0; /* Mark as not detected */ + continue; + } + ci->config.card_subtype = sound_drivers[drv].card_subtype; + + if (sound_drivers[drv].probe(&(ci->config))) + sound_drivers[drv].attach(&(ci->config)); + else + ci->enabled = 0; /* Mark as not detected */ + } + if (trace_init) + printf("Sound initialization complete\n"); +} + +static void +sndtable_init() +{ + start_cards(); +} + +/* + * sndtable_probe probes a specific device. unit is the voxware unit number. + */ + +int +sndtable_probe(int unit, struct address_info * hw_config) +{ + int i, sel = -1, n = num_sound_cards; + struct card_info *ci = snd_installed_cards ; + + DDB(printf("-- sndtable_probe(%d)\n", unit)); + + + /* + * for some reason unit 0 always succeeds ? + */ + if (!unit) + return TRUE; + + sound_started = 1; + + for (i=0; i<n && sel== -1 && ci->card_type; ci++, i++) + if ( (ci->enabled) && (ci->card_type == unit) ) { + /* DDB(printf("-- found card %d\n", i) ); */ + sel = i; /* and break */ + } + + /* + * not found. Creates a new entry in the table for this unit. + */ + if (sel == -1 && num_sound_cards < max_sound_cards) { + int i; + + i = sel = (num_sound_cards++); + DDB(printf("-- installing card %d\n", i) ); + + ci = &snd_installed_cards[sel] ; + ci->card_type = unit; + ci->enabled = 1; + } + /* DDB(printf("-- installed card %d\n", sel) ); */ + if (sel != -1) { + int drv; + + ci->config.io_base = hw_config->io_base; + ci->config.irq = hw_config->irq; + ci->config.dma = hw_config->dma; + ci->config.dma2 = hw_config->dma2; + ci->config.name = hw_config->name; + ci->config.always_detect = hw_config->always_detect; + ci->config.card_subtype = hw_config->card_subtype; + ci->config.osp = hw_config->osp; + + if ((drv = snd_find_driver(ci->card_type)) == -1) { + ci->enabled = 0; + DDB(printf("Failed to find driver\n")); + return FALSE; + } + DDB(printf("-- Driver name '%s' probe 0x%08x\n", + sound_drivers[drv].name, sound_drivers[drv].probe)); + + hw_config->card_subtype = + ci->config.card_subtype = sound_drivers[drv].card_subtype; + + if (sound_drivers[drv].probe(hw_config)) { + DDB(printf("-- Hardware probed OK\n")); + return TRUE; + } + DDB(printf("-- Failed to find hardware\n")); + ci->enabled = 0; /* mark as not detected */ + return FALSE; + } + return FALSE; +} + +int +sndtable_init_card(int unit, struct address_info * hw_config) +{ + int i, n = num_sound_cards; + struct card_info *ci = snd_installed_cards ; + + DDB(printf("sndtable_init_card(%d) entered\n", unit)); + + if (!unit) { + sndtable_init() ; + return TRUE; + } + for (i = 0; i < n && ci->card_type; ci++, i++) + if (ci->card_type == unit) { + int drv; + + ci->config.io_base = hw_config->io_base; + ci->config.irq = hw_config->irq; + ci->config.dma = hw_config->dma; + ci->config.dma2 = hw_config->dma2; + ci->config.name = hw_config->name; + ci->config.always_detect = hw_config->always_detect; + ci->config.card_subtype = hw_config->card_subtype; + ci->config.osp = hw_config->osp; + + if ((drv = snd_find_driver(ci->card_type)) == -1) + ci->enabled = 0; /* Mark not fnd */ + else { + DDB(printf("Located card - calling attach routine\n")); + sound_drivers[drv].attach(hw_config) ; + DDB(printf("attach routine finished\n")); + } + start_services(); + return TRUE; + } + DDB(printf("sndtable_init_card: No card defined with type=%d, num cards: %d\n", + unit, num_sound_cards)); + return FALSE; +} + +int +sndtable_get_cardcount(void) +{ + return num_audiodevs + num_mixers + num_synths + num_midis; +} + +static int +sndtable_identify_card(char *name) +{ + int i, n = num_sound_drivers; + + if (name == NULL) + return 0; + + 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; +} + +static void +sound_chconf(int card_type, int ioaddr, int irq, int dma) +{ + int j, ptr = -1, n = num_sound_cards; + + for (j = 0; j < n && ptr == -1 && snd_installed_cards[j].card_type; j++) + if (snd_installed_cards[j].card_type == card_type && + !snd_installed_cards[j].enabled) /* Not already found */ + ptr = j; + + if (ptr != -1) { + snd_installed_cards[ptr].enabled = 1; + if (ioaddr) + snd_installed_cards[ptr].config.io_base = ioaddr; + if (irq) + snd_installed_cards[ptr].config.irq = irq; + if (dma) + snd_installed_cards[ptr].config.dma = dma; + snd_installed_cards[ptr].config.dma2 = -1; + } +} + + +struct address_info * +sound_getconf(int card_type) +{ + int j, ptr = -1, n = num_sound_cards; + + 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; + + if (ptr == -1) + return (struct address_info *) NULL; + + return &snd_installed_cards[ptr].config; +} + +#endif diff --git a/sys/i386/isa/sound/dev_table.h b/sys/i386/isa/sound/dev_table.h new file mode 100644 index 0000000..e8332e9 --- /dev/null +++ b/sys/i386/isa/sound/dev_table.h @@ -0,0 +1,594 @@ +/* + * dev_table.h + * + * Global definitions for device call tables + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +#ifndef _DEV_TABLE_H_ +#define _DEV_TABLE_H_ + +/* + * NOTE! NOTE! NOTE! NOTE! + * + * If you modify this file, please check the dev_table.c also. + * + * NOTE! NOTE! NOTE! NOTE! + */ + +extern int sound_started; + +struct driver_info { + char *driver_id; + int card_subtype; /* Driver specific. Usually 0 */ + int card_type; /* From soundcard.h */ + char *name; + void (*attach) (struct address_info * hw_config); + int (*probe) (struct address_info * hw_config); +}; + +struct card_info { + int card_type; /* Link (search key) to the driver list */ + struct address_info config; + int enabled; +}; + +typedef struct pnp_sounddev { + int id; + void (*setup) (void *dev); + char *driver_name; +} pnp_sounddev; + +/* + * Device specific parameters (used only by dmabuf.c) + */ +#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR) + +#define DMODE_NONE 0 +#define DMODE_OUTPUT 1 +#define DMODE_INPUT 2 + +struct dma_buffparms { + int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */ + + char *raw_buf; /* Pointers to raw buffers */ + u_long raw_buf_phys; + + /* + * Device state tables + */ + + u_long flags; +#define DMA_BUSY 0x00000001 +#define DMA_RESTART 0x00000002 +#define DMA_ACTIVE 0x00000004 +#define DMA_STARTED 0x00000008 +#define DMA_ALLOC_DONE 0x00000020 + + int open_mode; + + /* + * Queue parameters. + */ + int qlen; + int qhead; + int qtail; + + int nbufs; + int counts[MAX_SUB_BUFFERS]; + int subdivision; + + int fragment_size; + int max_fragments; + + int bytes_in_use; + + int underrun_count; + int byte_counter; + + int mapping_flags; +#define DMA_MAP_MAPPED 0x00000001 + char neutral_byte; + int dma_chan; +}; + +/* + * Structure for use with various microcontrollers and DSP processors in the + * recent soundcards. + */ +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, u_int cmd, ioctl_arg arg, int local); + void (*reset) (void *devc); + + void *devc; /* Driver specific info */ +} coproc_operations; + +struct audio_operations { + char name[32]; + int flags; +#define NOTHING_SPECIAL 0 +#define NEEDS_RESTART 1 +#define DMA_AUTOMODE 2 +#define DMA_DUPLEX 4 + int format_mask; /* Bitmask for supported audio formats */ + void *devc; /* Driver specific info */ + 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); + void (*start_input) (int dev, unsigned long buf, + int count, int intrflag, int dma_restart); + int (*ioctl) (int dev, u_int cmd, ioctl_arg arg, int local); + int (*prepare_for_input) (int dev, int bufsize, int nbufs); + int (*prepare_for_output) (int dev, int bufsize, int nbufs); + void (*reset) (int dev); + void (*halt_xfer) (int dev); + int (*local_qlen) (int dev); + void (*copy_from_user) (int dev, char *localbuf, int localoffs, + snd_rw_buf * userbuf, int useroffs, int len); + void (*halt_input) (int dev); + void (*halt_output) (int dev); + void (*trigger) (int dev, int bits); + long buffsize; + int dmachan1, dmachan2; + struct dma_buffparms *dmap_in, *dmap_out; + struct coproc_operations *coproc; + int mixer_dev; + int enable_bits; + int open_mode; + int go; + int otherside; + int busy; +}; + +struct mixer_operations { + char name[32]; + int (*ioctl) (int dev, unsigned int cmd, ioctl_arg arg); +}; + +struct synth_operations { + struct synth_info *info; + int midi_dev; + int synth_type; + int synth_subtype; + + int (*open) (int dev, int mode); + void (*close) (int dev); + int (*ioctl) (int dev, unsigned int cmd, ioctl_arg arg); + int (*kill_note) (int dev, int voice, int note, int velocity); + int (*start_note) (int dev, int voice, int note, int velocity); + int (*set_instr) (int dev, int voice, int instr); + void (*reset) (int dev); + void (*hw_control) (int dev, unsigned char *event); + int (*load_patch) (int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag); + void (*aftertouch) (int dev, int voice, int pressure); + void (*controller) (int dev, int voice, int ctrl_num, int value); + void (*panning) (int dev, int voice, int value); + 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); + int (*send_sysex) (int dev, unsigned char *bytes, int len); + + struct voice_alloc_info alloc; + struct channel_info chn_info[16]; +}; + +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; +#define MST_INIT 0 +#define MST_DATA 1 +#define MST_SYSEX 2 + int m_state; + int m_left; +}; + +struct midi_operations { + struct midi_info info; + struct synth_operations *converter; + struct midi_input_info in_info; + int (*open) (int dev, int mode, + void (*inputintr) (int dev, unsigned char data), + void (*outputintr) (int dev) ); + void (*close) (int dev); + int (*ioctl) (int dev, unsigned int cmd, ioctl_arg arg); + int (*putc) (int dev, unsigned char data); + int (*start_read) (int dev); + int (*end_read) (int dev); + void (*kick) (int dev); + int (*command) (int dev, unsigned char *data); + int (*buffer_status) (int dev); + int (*prefix_cmd) (int dev, unsigned char status); + struct coproc_operations *coproc; +}; + +struct sound_lowlev_timer { + int dev; + u_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_info info; + int priority; + int devlink; + int (*open) (int dev, int mode); + void (*close) (int dev); + int (*event) (int dev, unsigned char *ev); + u_long (*get_time) (int dev); + int (*ioctl) (int dev, unsigned int cmd, ioctl_arg arg); + void (*arm_timer) (int dev, long time); +}; + +#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; +#else +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. + * + * remember, each entry contains: + + char *driver_id; + int card_subtype; (Driver specific. Usually 0) + int card_type; (From soundcard.h) + char *name; + void (*attach) (struct address_info * hw_config); + int (*probe) (struct address_info * hw_config); + * + */ + +struct driver_info sound_drivers[] = { + +#ifdef CONFIG_PSS + {"PSSECHO", 0, SNDCARD_PSS, "Echo Personal Sound System PSS (ESC614)", + attach_pss, probe_pss}, + {"PSSMPU", 0, SNDCARD_PSS_MPU, "PSS-MPU", + attach_pss_mpu, probe_pss_mpu}, + {"PSSMSS", 0, SNDCARD_PSS_MSS, "PSS-MSS", + attach_pss_mss, probe_pss_mss}, +#endif + +#ifdef CONFIG_MSS + /* XXX changed type from 0 to 1 -lr 970705 */ + {"MSS", 1, SNDCARD_MSS, "MS Sound System", + attach_mss, probe_mss}, + /* MSS without IRQ/DMA config registers (for DEC Alphas) */ + {"PCXBJ", 1, SNDCARD_PSEUDO_MSS, "MS Sound System (AXP)", + attach_mss, probe_mss}, +#endif + +#ifdef CONFIG_MAD16 + {"MAD16", 0, SNDCARD_MAD16, "MAD16/Mozart (MSS)", + attach_mad16, probe_mad16}, + {"MAD16MPU", 0, SNDCARD_MAD16_MPU, "MAD16/Mozart (MPU)", + attach_mad16_mpu, probe_mad16_mpu}, +#endif + +#ifdef CONFIG_CS4232 + {"CS4232", 0, SNDCARD_CS4232, "CS4232", + attach_cs4232, probe_cs4232}, + {"CS4232MPU", 0, SNDCARD_CS4232_MPU, "CS4232 MIDI", + attach_cs4232_mpu, probe_cs4232_mpu}, +#endif + +#ifdef CONFIG_YM3812 + {"OPL3", 0, SNDCARD_ADLIB, "OPL-2/OPL-3 FM", + attach_adlib_card, probe_adlib}, +#endif + +#ifdef CONFIG_PAS + {"PAS16", 0, SNDCARD_PAS, "ProAudioSpectrum", + attach_pas_card, probe_pas}, +#endif + +#if defined(CONFIG_MPU401) && defined(CONFIG_MIDI) + {"MPU401", 0, SNDCARD_MPU401, "Roland MPU-401", + attach_mpu401, probe_mpu401}, +#endif + +#if defined(CONFIG_MAUI) + {"MAUI", 0, SNDCARD_MAUI, "TB Maui", + attach_maui, probe_maui}, +#endif + +#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) + {"MIDI6850", 0, SNDCARD_UART6850, "6860 UART Midi", + attach_uart6850, probe_uart6850}, +#endif + +#ifdef CONFIG_SB + {"SBLAST", 0, SNDCARD_SB, "SoundBlaster", + attach_sb_card, probe_sb}, +#endif + +#if defined(CONFIG_SB) && defined(CONFIG_SB16) +#ifdef CONFIG_AUDIO + {"SB16", 0, SNDCARD_SB16, "SoundBlaster16", + sb16_dsp_init, sb16_dsp_detect}, +#endif +#ifdef CONFIG_AWE32 + {"AWE32", 0, SNDCARD_AWE32, "AWE32 Synth", + attach_awe, probe_awe}, +#endif +#ifdef CONFIG_MIDI + {"SB16MIDI", 0, SNDCARD_SB16MIDI, "SB16 MIDI", + attach_sb16midi, probe_sb16midi}, +#endif +#endif + +#ifdef CONFIG_GUS16 + {"GUS16", 0, SNDCARD_GUS16, "Ultrasound 16-bit opt.", + attach_gus_db16, probe_gus_db16}, +#endif + +#ifdef CONFIG_GUS + {"GUS", 0, SNDCARD_GUS, "Gravis Ultrasound", + attach_gus_card, probe_gus}, +#endif + +#ifdef CONFIG_SSCAPE + {"SSCAPE", 0, SNDCARD_SSCAPE, "Ensoniq Soundscape", + attach_sscape, probe_sscape}, + {"SSCAPEMSS", 0, SNDCARD_SSCAPE_MSS, "MS Sound System (SoundScape)", + attach_ss_mss, probe_ss_mss}, +#endif + +#if NTRIX > 0 + {"TRXPRO", 0, SNDCARD_TRXPRO, "MediaTriX AudioTriX Pro", + attach_trix_wss, probe_trix_wss}, + {"TRXPROSB", 0, SNDCARD_TRXPRO_SB, "AudioTriX (SB mode)", + attach_trix_sb, probe_trix_sb}, + {"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTriX MIDI", + attach_trix_mpu, probe_trix_mpu}, +#endif + +#ifdef CONFIG_PNP + {"AD1848", 0, 500, "PnP MSS", + attach_pnp_ad1848, probe_pnp_ad1848}, +#endif + + {NULL, 0, 0, "*?*", NULL, NULL} +}; + +int num_sound_drivers = +sizeof(sound_drivers) / sizeof(struct driver_info); +int max_sound_drivers = +sizeof(sound_drivers) / sizeof(struct driver_info); + +#define FULL_SOUND + +#ifndef FULL_SOUND +/* + * List of devices actually configured in the system. + * + * Note! The detection order is significant. Don't change it. + * + * remember, the list contains + * + * int card_type; (Link (search key) to the driver list) + * struct address_info config; + * io_base, irq, dma, dma2, + * always_detect, char *name, struct... *osp + * int enabled; + * void *for_driver_use; + * + */ + +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 +#endif /* config PSS */ + +#if NTRIX > 0 + {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 +#endif /* NTRIX > 0 */ + +#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_MSS_DMA, -1}, SND_DEFAULT_ENABLE}, +#endif + +#ifdef CONFIG_MAD16 + {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 +#endif /* CONFIG_MAD16 */ + +#ifdef CONFIG_CS4232 +#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}, +#endif + +#ifdef CONFIG_MSS +#ifdef PSEUDO_MSS + {SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA, -1}, SND_DEFAULT_ENABLE}, +#else + {SNDCARD_PSEUDO_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 +#endif /* CONFIG_MSS */ + +#ifdef CONFIG_PAS + {SNDCARD_PAS, {PAS_BASE, PAS_IRQ, PAS_DMA, -1}, SND_DEFAULT_ENABLE}, +#endif + +#ifdef CONFIG_SB +#ifndef SBC_DMA +#define SBC_DMA 1 +#endif + {SNDCARD_SB, {SBC_BASE, SBC_IRQ, SBC_DMA, -1}, SND_DEFAULT_ENABLE}, +#endif + +#if defined(CONFIG_MAUI) + {SNDCARD_MAUI, {MAUI_BASE, 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}, +#ifdef MPU2_BASE + {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}, +#endif +#endif + +#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) + {SNDCARD_UART6850, {U6850_BASE, U6850_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +#endif + +#if defined(CONFIG_SB) && defined(CONFIG_SB16) +#ifdef CONFIG_AUDIO + {SNDCARD_SB16, {SBC_BASE, SBC_IRQ, SB16_DMA, -1}, SND_DEFAULT_ENABLE}, +#endif +#ifdef CONFIG_MIDI + {SNDCARD_SB16MIDI, {SB16MIDI_BASE, SBC_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +#endif +#ifdef CONFIG_AWE32 + {SNDCARD_AWE32,{AWE32_BASE, 0, 0, -1}, SND_DEFAULT_ENABLE}, +#endif +#endif + +#ifdef CONFIG_GUS +#ifdef CONFIG_GUS16 + {SNDCARD_GUS16, {GUS16_BASE, GUS16_IRQ, GUS16_DMA, -1}, SND_DEFAULT_ENABLE}, +#endif + {SNDCARD_GUS, {GUS_BASE, GUS_IRQ, GUS_DMA, GUS_DMA2}, SND_DEFAULT_ENABLE}, +#endif + +#ifdef CONFIG_YM3812 + {SNDCARD_ADLIB, {FM_MONO, 0, 0, -1}, SND_DEFAULT_ENABLE}, +#endif + /* Define some expansion space */ + {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); + +#else +int num_sound_cards = 0; +struct card_info snd_installed_cards[20] = {{0}}; +int max_sound_cards = 20; +#endif + +#ifdef MODULE +int trace_init = 0; +#else +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 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; + +void sndtable_init(void); +int sndtable_get_cardcount(void); +struct address_info *sound_getconf(int card_type); +void sound_chconf(int card_type, int ioaddr, int irq, int dma); +int snd_find_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 soud_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); +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); + +#endif /* _DEV_TABLE_C_ */ +#endif /* _DEV_TABLE_H_ */ diff --git a/sys/i386/isa/sound/dmabuf.c b/sys/i386/isa/sound/dmabuf.c new file mode 100644 index 0000000..76494ad --- /dev/null +++ b/sys/i386/isa/sound/dmabuf.c @@ -0,0 +1,1601 @@ +/* + * sound/dmabuf.c + * + * The DMA buffer manager for digitized voice applications + * + * Copyright by Hannu Savolainen 1993, 1994, 1995 + * + * 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 <i386/isa/sound/sound_config.h> + +#include <machine/md_var.h> + +#if defined(CONFIG_AUDIO) || defined(CONFIG_GUS) +#ifdef ALLOW_POLL + +int +DMAbuf_poll(int dev, struct fileinfo * file, int events, select_table * wait); +#endif; + +static void +reorganize_buffers(int dev, struct dma_buffparms * dmap); + +static int *in_sleeper[MAX_AUDIO_DEV] = {NULL}; +static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] = {{0}}; +static int *out_sleeper[MAX_AUDIO_DEV] = {NULL}; +static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] = {{0}}; + +static int ndmaps = 0; + +#define MAX_DMAP (MAX_AUDIO_DEV*2) + +static struct dma_buffparms dmaps[MAX_DMAP] = {{0}}; +/* + * Primitive way to allocate such a large array. Needs dynamic run-time + * alloction. + */ + +static int space_in_queue(int dev); + +static void dma_reset_output(int dev); +static void dma_reset_input(int dev); + +static void +reorganize_buffers(int dev, struct dma_buffparms * dmap) +{ + /* + * This routine breaks the physical device buffers to logical ones. + */ + + struct audio_operations *dsp_dev = audio_devs[dev]; + u_int sr, nc; + int bsz, sz, n, i; + + if (dmap->fragment_size == 0) { + /* Compute the fragment size using the default algorithm */ + + sr = dsp_dev->ioctl(dev, SOUND_PCM_READ_RATE, 0, 1); + nc = dsp_dev->ioctl(dev, SOUND_PCM_READ_CHANNELS, 0, 1); + sz = dsp_dev->ioctl(dev, SOUND_PCM_READ_BITS, 0, 1); + + if (sz == 8) + dmap->neutral_byte = 254; + else + dmap->neutral_byte = 0x00; + + if (sr < 1 || nc < 1 || sz < 1) { + printf("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", + dev, sr, nc, sz); + sr = DSP_DEFAULT_SPEED; + nc = 1; + sz = 8; + } + sz = sr * nc * sz; + + sz /= 8; /* #bits -> #bytes */ + + /* + * Compute a buffer size for time not exeeding 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 */ + + if (dmap->subdivision == 0) /* Not already set */ + dmap->subdivision = 1; /* Init to default value */ + else + bsz /= dmap->subdivision; + + if (bsz < 16) + bsz = 16; /* Just a sanity check */ + + dmap->fragment_size = bsz; + } else { + /* + * The process has specified the buffer sice with + * SNDCTL_DSP_SETFRAGMENT or the buffer sice 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; + + for (i = 0; i < dmap->nbufs; i++) { + dmap->counts[i] = 0; + } + + if (dmap->raw_buf) + fillw (dmap->neutral_byte, dmap->raw_buf, + dmap->bytes_in_use/2); + + dmap->flags |= DMA_ALLOC_DONE; + +} + +static void +dma_init_buffers(int dev, struct dma_buffparms * dmap) +{ + if (dmap == audio_devs[dev]->dmap_out) { + out_sleep_flag[dev].aborting = 0; + out_sleep_flag[dev].mode = WK_NONE; + } else { + in_sleep_flag[dev].aborting = 0; + in_sleep_flag[dev].mode = 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 = 0x00; +} + +static int +open_dmap(int dev, int mode, struct dma_buffparms * dmap, int chan) +{ + if (dmap->flags & DMA_BUSY) + return -(EBUSY); + +#ifdef RUNTIME_DMA_ALLOC + { + int err; + + if ((err = sound_alloc_dmap(dev, dmap, chan)) < 0) + return err; + } +#endif + + if (dmap->raw_buf == NULL) + return -(ENOSPC); /* Memory allocation failed during boot */ + + if (0) { + printf("Unable to grab(2) DMA%d for the audio driver\n", chan); + return -(EBUSY); + } + 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; + isa_dma_acquire(chan); + dmap->dma_chan = chan; + dma_init_buffers(dev, dmap); + + return 0; +} + +static void +close_dmap(int dev, struct dma_buffparms * dmap, int chan) +{ + if (dmap->flags & DMA_BUSY) + dmap->dma_mode = DMODE_NONE; + dmap->flags &= ~DMA_BUSY; + isa_dma_release(dmap->dma_chan); +#ifdef RUNTIME_DMA_ALLOC + sound_free_dmap(dev, dmap); +#endif +} + +int +DMAbuf_open(int dev, int mode) +{ + int retval; + struct dma_buffparms *dmap_in = NULL; + struct dma_buffparms *dmap_out = NULL; + + if (dev >= num_audiodevs) { + printf("PCM device %d not installed.\n", dev); + return -(ENXIO); + } + if (!audio_devs[dev]) { + printf("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]->open(dev, mode)) < 0) + return retval; + + 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]->close(dev); + return retval; + } + audio_devs[dev]->enable_bits = mode; + + if (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]->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].aborting = 0; + in_sleep_flag[dev].mode = WK_NONE; + + out_sleep_flag[dev].aborting = 0; + out_sleep_flag[dev].mode = WK_NONE; + + audio_devs[dev]->ioctl(dev, SOUND_PCM_WRITE_BITS, (ioctl_arg) 8, 1); + audio_devs[dev]->ioctl(dev, SOUND_PCM_WRITE_CHANNELS, (ioctl_arg) 1, 1); + audio_devs[dev]->ioctl(dev, SOUND_PCM_WRITE_RATE, (ioctl_arg) DSP_DEFAULT_SPEED, 1); + + return 0; +} + +static void +dma_reset(int dev) +{ + u_long flags; + + flags = splhigh(); + audio_devs[dev]->reset(dev); + splx(flags); + + dma_reset_output(dev); + + if (audio_devs[dev]->flags & DMA_DUPLEX) + dma_reset_input(dev); +} + +static void +dma_reset_output(int dev) +{ + u_long flags; + + flags = splhigh(); + if (!(audio_devs[dev]->flags & DMA_DUPLEX) || + !audio_devs[dev]->halt_output) + audio_devs[dev]->reset(dev); + else + audio_devs[dev]->halt_output(dev); + splx(flags); + + dma_init_buffers(dev, audio_devs[dev]->dmap_out); + reorganize_buffers(dev, audio_devs[dev]->dmap_out); +} + +static void +dma_reset_input(int dev) +{ + u_long flags; + + flags = splhigh(); + if (!(audio_devs[dev]->flags & DMA_DUPLEX) || + !audio_devs[dev]->halt_input) + audio_devs[dev]->reset(dev); + else + audio_devs[dev]->halt_input(dev); + splx(flags); + + dma_init_buffers(dev, audio_devs[dev]->dmap_in); + reorganize_buffers(dev, audio_devs[dev]->dmap_in); +} + +static int +dma_sync(int dev) +{ + u_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) { + flags = splhigh(); + + out_sleep_flag[dev].aborting = 0; +#ifdef ALLOW_BUFFER_MAPPING + if(audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED && + audio_devs[dev]->dmap_out->qlen) { + splx(flags); + + return audio_devs[dev]->dmap_out->qlen; + } + +#endif + while (!PROCESS_ABORTING (out_sleep_flag[dev]) + && audio_devs[dev]->dmap_out->qlen){ + int chn; + + out_sleeper[dev] = &chn; + DO_SLEEP1(chn, out_sleep_flag[dev], 10 * hz); + if (TIMED_OUT (out_sleep_flag[dev]) ) { + + splx(flags); + + return audio_devs[dev]->dmap_out->qlen; + + } + } + + + splx(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. + */ + + flags = splhigh(); + if (audio_devs[dev]->local_qlen) { /* Device has hidden buffers */ + while (!(PROCESS_ABORTING (out_sleep_flag[dev])) + && audio_devs[dev]->local_qlen(dev)) { + int chn; + out_sleeper[dev] = &chn; + DO_SLEEP(chn, out_sleep_flag[dev], 10 * hz); + + } + } + splx(flags); + } + return audio_devs[dev]->dmap_out->qlen; +} + +int +DMAbuf_release(int dev, int mode) +{ + u_long flags; + + if (!((out_sleep_flag[dev].aborting)) + && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)) { + dma_sync(dev); + } + flags = splhigh(); + + audio_devs[dev]->close(dev); + + close_dmap(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); + + if (audio_devs[dev]->flags & DMA_DUPLEX) + close_dmap(dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); + audio_devs[dev]->open_mode = 0; + + splx(flags); + + return 0; +} + +static int +activate_recording(int dev, struct dma_buffparms * dmap) +{ + if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT)) + return 0; + + if (dmap->flags & DMA_RESTART) { + dma_reset_input(dev); + dmap->flags &= ~DMA_RESTART; + } + 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); + + if (!dmap->dma_mode) { + int err; + + if ((err = audio_devs[dev]->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]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } + return 0; +} + +int +DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) +{ + u_long flags; + int err = EIO; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + + flags = splhigh(); +#ifdef ALLOW_BUFFER_MAPPING + if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) { + printf("Sound: Can't read from mmapped device (1)\n"); + return -(EINVAL); + } else +#endif + if (!dmap->qlen) { + int timeout; + + if ((err = activate_recording(dev, dmap)) < 0) { + splx(flags); + return err; + } + /* Wait for the next block */ + + if (dontblock) { + splx(flags); + return -(EAGAIN); + } + if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) & + audio_devs[dev]->go) { + splx(flags); + return -(EAGAIN); + } + if (!audio_devs[dev]->go) + timeout = 0; + else + timeout = 2 * hz; + + { + int chn; + + in_sleeper[dev] = &chn; + DO_SLEEP(chn, in_sleep_flag[dev], timeout); + + }; + /* XXX note -- nobody seems to set the mode to WK_TIMEOUT - lr */ + if ((in_sleep_flag[dev].mode & WK_TIMEOUT)) { + /* XXX hey, we are in splhigh here ? lr 970705 */ + printf("Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); + err = EIO; + audio_devs[dev]->reset(dev); + in_sleep_flag[dev].aborting = 1; + } else + err = EINTR; + } + splx(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; +} + +int +DMAbuf_rmchars(int dev, int buff_no, int c) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + + int p = dmap->counts[dmap->qhead] + c; + +#ifdef ALLOW_BUFFER_MAPPING + if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) { + printf("Sound: Can't read from mmapped device (2)\n"); + return -(EINVAL); + } else +#endif + if (p >= dmap->fragment_size) { /* This buffer is completely empty */ + dmap->counts[dmap->qhead] = 0; + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) + printf("\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; +} + +static int +dma_subdivide(int dev, struct dma_buffparms * dmap, ioctl_arg arg, int fact) +{ + if (fact == 0) { + fact = dmap->subdivision; + if (fact == 0) + fact = 1; + return *(int *) arg = fact; + } + if (dmap->subdivision != 0 || dmap->fragment_size)/* Loo 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 *(int *) arg = fact; +} + +static int +dma_set_fragment(int dev, struct dma_buffparms * dmap, ioctl_arg arg, int fact) +{ + int bytes, count; + + if (fact == 0) + return -(EIO); + + if (dmap->subdivision != 0 || dmap->fragment_size)/* Loo late to change */ + return -(EINVAL); + + bytes = fact & 0xffff; + count = (fact >> 16) & 0xffff; + + if (count == 0) + count = MAX_SUB_BUFFERS; + +#if amancio + if (bytes < 4 || bytes > 17) /* <16 || > 128k */ + return -(EINVAL); +#endif + + if (count < 2) + return -(EINVAL); + +#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 */ + return *(int *) arg = bytes | (count << 16); +} + +static int +get_buffer_pointer(int dev, int chan, struct dma_buffparms * dmap) +{ + int pos; + u_long flags; + + flags = splhigh(); + + if (!(dmap->flags & DMA_ACTIVE)) + pos = 0; + else + { + pos = isa_dmastatus(chan); + } + + splx(flags); + + pos = dmap->bytes_in_use - pos ; + if (audio_devs[dev]->flags & DMA_AUTOMODE) + return pos; + else + { + pos = dmap->fragment_size - pos; + if (pos < 0) + return 0; + return pos; + } + + +} + +int +DMAbuf_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) +{ + struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out; + struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in; + + switch (cmd) { + + + 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); + + return *(int *) arg = dmap_out->fragment_size; + break; + + case SNDCTL_DSP_SETBLKSIZE: + { + int size = (*(int *) arg); + + if (!(dmap_out->flags & DMA_ALLOC_DONE) && size) { + if ((size >> 16) > 0 ) + dmap_out->fragment_size = size >> 16; + else { + dmap_out->fragment_size = size; + } + dmap_out->max_fragments = 8888; + + size &= 0xffff; + + if (audio_devs[dev]->flags & DMA_DUPLEX) { + dmap_in->fragment_size = size; + dmap_in->max_fragments = 8888; + } + return 0; + + } else + return -(EINVAL); /* Too late to change */ + + } + break; + + case SNDCTL_DSP_SUBDIVIDE: + { + int fact = (*(int *) arg); + int ret; + + ret = dma_subdivide(dev, dmap_out, arg, fact); + if (ret < 0) + return ret; + + if (audio_devs[dev]->flags & DMA_DUPLEX) + ret = dma_subdivide(dev, dmap_in, arg, fact); + + return ret; + } + break; + + case SNDCTL_DSP_SETFRAGMENT: + { + int fact = (*(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) + 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]->flags & DMA_DUPLEX) + dmap = dmap_in; + +#ifdef ALLOW_BUFFER_MAPPING + if (dmap->mapping_flags & DMA_MAP_MAPPED) + return -(EINVAL); +#endif + + if (!(dmap->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap); + + 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]->local_qlen) { + int tmp = audio_devs[dev]->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]; + } + return 0; + + case SNDCTL_DSP_SETTRIGGER: + { + u_long flags; + + int bits = (*(int *) arg) & audio_devs[dev]->open_mode; + int changed; + + if (audio_devs[dev]->trigger == NULL) + return -(EINVAL); + + if (!(audio_devs[dev]->flags & DMA_DUPLEX)) + if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT)) { + printf("Sound: Device doesn't have full duplex capability\n"); + return -(EINVAL); + } + flags = splhigh(); + changed = audio_devs[dev]->enable_bits ^ bits; + + if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) { + if (!(dmap_in->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap_in); + activate_recording(dev, dmap_in); + } +#ifdef ALLOW_BUFFER_MAPPING + if ((changed & bits) & PCM_ENABLE_OUTPUT && + dmap_out->mapping_flags & DMA_MAP_MAPPED && + audio_devs[dev]->go) { + if (!(dmap_out->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap_out); + + audio_devs[dev]->prepare_for_output (dev, + dmap_out->fragment_size, dmap_out->nbufs); + + dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; + DMAbuf_start_output(dev, 0, dmap_out->fragment_size); + dmap_out->dma_mode = DMODE_OUTPUT; + } +#endif + + audio_devs[dev]->enable_bits = bits; + if (changed && audio_devs[dev]->trigger) + audio_devs[dev]->trigger(dev, bits * audio_devs[dev]->go); + splx(flags); + } + case SNDCTL_DSP_GETTRIGGER: + return *(int *) arg = audio_devs[dev]->enable_bits; + break; + + case SNDCTL_DSP_SETSYNCRO: + + if (!audio_devs[dev]->trigger) + return -(EINVAL); + + audio_devs[dev]->trigger(dev, 0); + audio_devs[dev]->go = 0; + return 0; + break; + + case SNDCTL_DSP_GETIPTR: + { + count_info info; + u_long flags; + + flags = splhigh(); + 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; + + bcopy((char *) &info, &(((char *) arg)[0]), sizeof(info)); + +#ifdef ALLOW_BUFFER_MAPPING + if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) + audio_devs[dev]->dmap_in->qlen = 0; /* Ack interrupts */ +#endif + splx(flags); + return 0; + } + break; + + case SNDCTL_DSP_GETOPTR: + { + count_info info; + u_long flags; + + flags = splhigh(); + 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; + bcopy((char *) &info, &(((char *) arg)[0]), sizeof(info)); + +#ifdef ALLOW_BUFFER_MAPPING + if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED) + audio_devs[dev]->dmap_out->qlen = 0; /* Ack interrupts */ +#endif + splx(flags); + return 0; + } + break; + + default: + return audio_devs[dev]->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(u_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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } +} + +static int +space_in_queue(int dev) +{ + 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]->local_qlen) { + tmp = audio_devs[dev]->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) +{ + u_long flags; + int abort, err = EIO; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + +#ifdef ALLOW_BUFFER_MAPPING + if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED) { + printf("Sound: Can't write to mmapped device (3)\n"); + return -(EINVAL); + } +#endif + + 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; + + if (!(dmap->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap); + + if (!dmap->dma_mode) { + int err; + + dmap->dma_mode = DMODE_OUTPUT; + if ((err = audio_devs[dev]->prepare_for_output(dev, + dmap->fragment_size, dmap->nbufs)) < 0) + return err; + } + flags = splhigh(); + + abort = 0; + while (!space_in_queue(dev) && !abort) { + int timeout; + + if (dontblock) { + splx(flags); + return -(EAGAIN); + } + if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT) && + audio_devs[dev]->go) { + splx(flags); + return -(EAGAIN); + } + /* + * Wait for free space + */ + if (!audio_devs[dev]->go) + timeout = 0; + else + timeout = 2 * hz; + + { + int chn; + + out_sleep_flag[dev].mode = WK_SLEEP; + out_sleeper[dev] = &chn; + DO_SLEEP2(chn, out_sleep_flag[dev], timeout); + + if ((out_sleep_flag[dev].mode & WK_TIMEOUT)) { + printf("Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); + err = EIO; + abort = 1; + out_sleep_flag[dev].aborting = 1; + audio_devs[dev]->reset(dev); + } else if ((out_sleep_flag[dev].aborting) || + CURSIG(curproc)) { + err = EINTR; + abort = 1; + } + } + } + splx(flags); + + 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; +} + +int +DMAbuf_start_output(int dev, int buff_no, int l) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + /* + * Bypass buffering if using mmaped access + */ + +#ifdef ALLOW_BUFFER_MAPPING + 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 +#else + if (dmap != NULL) +#endif + { + + if (buff_no != dmap->qtail) + printf("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail); + + dmap->qlen++; + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) + printf("\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) && + ((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; + audio_devs[dev]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } + return 0; +} + +int +DMAbuf_start_dma(int dev, u_long physaddr, int count, int dma_mode) +{ + int chan; + struct dma_buffparms *dmap; + + if (dma_mode == 1) { + chan = audio_devs[dev]->dmachan1; + dmap = audio_devs[dev]->dmap_out; + + } else { + chan = audio_devs[dev]->dmachan2; + dmap = audio_devs[dev]->dmap_in; + } + + /* + * The count must be one less than the actual size. This is handled + * by set_dma_addr() + */ + +#ifndef PSEUDO_DMA_AUTOINIT + if (audio_devs[dev]->flags & DMA_AUTOMODE) { + /* Auto restart mode. Transfer the whole buffer */ + isa_dmastart(B_RAW | ((dma_mode == 0) ? B_READ : B_WRITE), + (caddr_t) (void *) (uintptr_t) dmap->raw_buf_phys, + dmap->bytes_in_use, chan); + + } else +#endif + { + isa_dmastart((dma_mode == 0) ? B_READ : B_WRITE, + (caddr_t) (void *) (uintptr_t) physaddr, count, chan); + } + return count; +} + +void +DMAbuf_init() +{ + int dev; + + /* + * NOTE! This routine could be called several times. + * XXX is it ok to make it run only the first time ? -- lr970710 + */ + + 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++]; + } +} + +void +DMAbuf_outputintr(int dev, int event_type) +{ + /* + * 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 its local buffer. + */ + + u_long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + dmap->byte_counter += dmap->counts[dmap->qhead]; +#ifdef OS_DMA_INTR + sound_dma_intr(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); +#endif +#ifdef ALLOW_BUFFER_MAPPING + 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]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } +#ifdef PSEUDO_DMA_AUTOINIT + else { + DMAbuf_start_dma(dev, dmap->raw_buf_phys + + dmap->qhead * dmap->fragment_size, + dmap->counts[dmap->qhead], 1); + } +#endif + dmap->flags |= DMA_ACTIVE; + + } else +#endif + if (event_type != 2) { + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) { + printf("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n", + dev, dmap->qlen, dmap->nbufs); + return; + } + isa_dmadone(0, 0, 0, audio_devs[dev]->dmachan1); + + dmap->qlen--; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + dmap->flags &= ~DMA_ACTIVE; + if (dmap->qlen) { + /* if (!(audio_devs[dev]->flags & NEEDS_RESTART)) */ + { + audio_devs[dev]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } + +#ifdef PSEUDO_DMA_AUTOINIT + /* else */ + { + DMAbuf_start_dma(dev, dmap->raw_buf_phys + + dmap->qhead * dmap->fragment_size, + dmap->counts[dmap->qhead], 1); + } +#endif + dmap->flags |= DMA_ACTIVE; + } else if (event_type == 1) { + dmap->underrun_count++; + if ((audio_devs[dev]->flags & DMA_DUPLEX) && + audio_devs[dev]->halt_output) + audio_devs[dev]->halt_output(dev); + else + audio_devs[dev]->halt_xfer(dev); + + if ((audio_devs[dev]->flags & DMA_AUTOMODE) && + audio_devs[dev]->flags & NEEDS_RESTART) + dmap->flags |= DMA_RESTART; + else + dmap->flags &= ~DMA_RESTART; + } + } /* event_type != 2 */ + flags = splhigh(); + + if ((out_sleep_flag[dev].mode & WK_SLEEP)) { + out_sleep_flag[dev].mode = WK_WAKEUP; + wakeup(out_sleeper[dev]); + } + + if(selinfo[dev].si_pid) { + selwakeup(&selinfo[dev]); + } + + splx(flags); +} + +void +DMAbuf_inputintr(int dev) +{ + u_long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + + dmap->byte_counter += dmap->fragment_size; + +#ifdef OS_DMA_INTR + sound_dma_intr(dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); +#endif + isa_dmadone(0, 0, 0, audio_devs[dev]->dmachan2); + +#ifdef ALLOW_BUFFER_MAPPING + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + dmap->qlen++; + + if (!(audio_devs[dev]->flags & NEEDS_RESTART)) { + audio_devs[dev]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } +#ifdef PSEUDO_DMA_AUTOINIT + else { + DMAbuf_start_dma(dev, dmap->raw_buf_phys + + dmap->qtail * dmap->fragment_size, + dmap->counts[dmap->qtail], 0); + } +#endif + + dmap->flags |= DMA_ACTIVE; + } else +#endif + if (dmap->qlen == (dmap->nbufs - 1)) { + /* printf ("Sound: Recording overrun\n"); */ + dmap->underrun_count++; + if ((audio_devs[dev]->flags & DMA_DUPLEX) && + audio_devs[dev]->halt_input) + audio_devs[dev]->halt_input(dev); + else + audio_devs[dev]->halt_xfer(dev); + + dmap->flags &= ~DMA_ACTIVE; + if (audio_devs[dev]->flags & DMA_AUTOMODE) + dmap->flags |= DMA_RESTART; + else + dmap->flags &= ~DMA_RESTART; + } else { + dmap->qlen++; + if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) + printf("\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]->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]->trigger) + audio_devs[dev]->trigger(dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } +#ifdef PSEUDO_DMA_AUTOINIT + /* else */ + { + DMAbuf_start_dma(dev, dmap->raw_buf_phys + + dmap->qtail * dmap->fragment_size, + dmap->counts[dmap->qtail], 0); + } +#endif + + dmap->flags |= DMA_ACTIVE; + } + + flags = splhigh(); + if ((in_sleep_flag[dev].mode & WK_SLEEP)) { + in_sleep_flag[dev].mode = WK_WAKEUP; + wakeup(in_sleeper[dev]); + } + if (selinfo[dev].si_pid) + selwakeup(&selinfo[dev]); + splx(flags); +} + +int +DMAbuf_open_dma(int dev) +{ + int err; + u_long flags; + flags = splhigh(); + + if ((err = open_dmap(dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, + audio_devs[dev]->dmachan1)) < 0) { + splx(flags); + 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; + /* reorganize_buffers (dev, audio_devs[dev]->dmap_out); */ + + if (audio_devs[dev]->flags & DMA_DUPLEX) { + if ((err = open_dmap(dev, OPEN_READWRITE, + audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2)) < 0) { + printf("Unable to grab DMA%d for the audio driver\n", + audio_devs[dev]->dmachan2); + close_dmap(dev, audio_devs[dev]->dmap_out, + audio_devs[dev]->dmachan1); + splx(flags); + return -(EBUSY); + } + dma_init_buffers(dev, audio_devs[dev]->dmap_in); + /* audio_devs[dev]->dmap_in->flags |= DMA_ALLOC_DONE; */ + audio_devs[dev]->dmap_in->fragment_size = audio_devs[dev]->buffsize; + /* reorganize_buffers (dev, audio_devs[dev]->dmap_in); */ + } else { + audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out; + audio_devs[dev]->dmachan2 = audio_devs[dev]->dmachan1; + } + + splx(flags); + return 0; +} + +void +DMAbuf_close_dma(int dev) +{ + DMAbuf_reset_dma(dev); + close_dmap(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); + + if (audio_devs[dev]->flags & DMA_DUPLEX) + close_dmap(dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); + +} + +void +DMAbuf_reset_dma(int dev) +{ +} + +#ifdef ALLOW_POLL + +int +DMAbuf_poll(int dev, struct fileinfo * file, int events, select_table * wait) +{ + struct dma_buffparms *dmap; + u_long flags; + int revents = 0; + + dmap = audio_devs[dev]->dmap_in; + + if (events & (POLLIN | POLLRDNORM)) { + 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) { + u_long flags; + + flags = splhigh(); + + activate_recording(dev, dmap); + splx(flags); + + } + return 0; + } + if (!dmap->qlen) { + flags = splhigh(); + + selrecord(wait, &selinfo[dev]); + + splx(flags); + + return 0; + } else + revents |= events & (POLLIN | POLLRDNORM); + + } + + if (events & (POLLOUT | POLLWRNORM)) { + + dmap = audio_devs[dev]->dmap_out; + if (dmap->dma_mode == DMODE_INPUT) + return 0; + + if (dmap->dma_mode == DMODE_NONE) + return ( events & (POLLOUT | POLLWRNORM)); + + if (dmap->mapping_flags & DMA_MAP_MAPPED) { + + if(dmap->qlen) + return 1; + flags = splhigh(); + selrecord(wait, &selinfo[dev]); + + splx(flags); + + return 0; + + } + if (!space_in_queue(dev)) { + flags = splhigh(); + selrecord(wait, &selinfo[dev]); + splx(flags); + + } else + revents |= events & (POLLOUT | POLLWRNORM); + + + } + + return (revents); +} + + +#ifdef amancio +int +DMAbuf_select(int dev, struct fileinfo * file, int sel_type, select_table * wait) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + struct dma_buffparms *dmapin = audio_devs[dev]->dmap_in; + u_long flags; + + switch (sel_type) { + case FREAD: + if (dmapin->dma_mode != DMODE_INPUT) + return 0; + + if (!dmap->qlen) { + flags = splhigh(); + selrecord(wait, &selinfo[dev]); + splx(flags); + + return 0; + } + return 1; + break; + + case FWRITE: + if (dmap->dma_mode == DMODE_INPUT) + return 0; + + if (dmap->dma_mode == DMODE_NONE) + return 1; + + if (!space_in_queue(dev)) { + flags = splhigh(); + + selrecord(wait, &selinfo[dev]); + splx(flags); + + return 0; + } + return 1; + break; + + } + + return 0; +} + +#endif /* ALLOW_POLL */ +#endif + +#else /* CONFIG_AUDIO */ +/* + * Stub versions if audio services not included + */ + +int +DMAbuf_open(int dev, int mode) +{ + return -(ENXIO); +} + +int +DMAbuf_release(int dev, int mode) +{ + return 0; +} + +int +DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock) +{ + return -(EIO); +} + +int +DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) +{ + return -(EIO); +} + +int +DMAbuf_rmchars(int dev, int buff_no, int c) +{ + return -(EIO); +} + +int +DMAbuf_start_output(int dev, int buff_no, int l) +{ + return -(EIO); +} + +int +DMAbuf_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) +{ + return -(EIO); +} + +void +DMAbuf_init() +{ +} + +int +DMAbuf_start_dma(int dev, u_long physaddr, int count, int dma_mode) +{ + return -(EIO); +} + +int +DMAbuf_open_dma(int dev) +{ + return -(ENXIO); +} + +void +DMAbuf_close_dma(int dev) +{ + return; +} + +void +DMAbuf_reset_dma(int dev) +{ + return; +} + +void +DMAbuf_inputintr(int dev) +{ + return; +} + +void +DMAbuf_outputintr(int dev, int underrun_flag) +{ + return; +} +#endif /* CONFIG_AUDIO */ diff --git a/sys/i386/isa/sound/finetune.h b/sys/i386/isa/sound/finetune.h new file mode 100644 index 0000000..b86a0eb --- /dev/null +++ b/sys/i386/isa/sound/finetune.h @@ -0,0 +1,49 @@ +#ifdef SEQUENCER_C +/* + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + + unsigned short finetune_table[128] = + { +/* 0 */ 9439, 9447, 9456, 9464, 9473, 9481, 9490, 9499, +/* 8 */ 9507, 9516, 9524, 9533, 9542, 9550, 9559, 9567, +/* 16 */ 9576, 9585, 9593, 9602, 9611, 9619, 9628, 9637, +/* 24 */ 9645, 9654, 9663, 9672, 9680, 9689, 9698, 9707, +/* 32 */ 9715, 9724, 9733, 9742, 9750, 9759, 9768, 9777, +/* 40 */ 9786, 9795, 9803, 9812, 9821, 9830, 9839, 9848, +/* 48 */ 9857, 9866, 9874, 9883, 9892, 9901, 9910, 9919, +/* 56 */ 9928, 9937, 9946, 9955, 9964, 9973, 9982, 9991, +/* 64 */ 10000, 10009, 10018, 10027, 10036, 10045, 10054, 10063, +/* 72 */ 10072, 10082, 10091, 10100, 10109, 10118, 10127, 10136, +/* 80 */ 10145, 10155, 10164, 10173, 10182, 10191, 10201, 10210, +/* 88 */ 10219, 10228, 10237, 10247, 10256, 10265, 10274, 10284, +/* 96 */ 10293, 10302, 10312, 10321, 10330, 10340, 10349, 10358, +/* 104 */ 10368, 10377, 10386, 10396, 10405, 10415, 10424, 10433, +/* 112 */ 10443, 10452, 10462, 10471, 10481, 10490, 10499, 10509, +/* 120 */ 10518, 10528, 10537, 10547, 10556, 10566, 10576, 10585 + }; +#else + extern unsigned short finetune_table[128]; +#endif diff --git a/sys/i386/isa/sound/gus_card.c b/sys/i386/isa/sound/gus_card.c new file mode 100644 index 0000000..7dac1bd --- /dev/null +++ b/sys/i386/isa/sound/gus_card.c @@ -0,0 +1,188 @@ +/* + * sound/gus_card.c + * + * Detection routine for the Gravis Ultrasound. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_GUS) + +#include <i386/isa/sound/gus_hw.h> +#include <i386/isa/sound/iwdefs.h> + +int gus_base, gus_irq, gus_dma; +extern int gus_wave_volume; +extern int gus_pcm_volume; +extern int have_gus_max; +extern int gus_timer_enabled; + +static sound_os_info *gus_osp; + +#ifndef NOGUSPNP +int IwaveOpen(char voices, char mode, struct address_info * hw); +#endif + +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, hw_config->osp); + + if (gus_wave_detect(hw_config->io_base)) { + /* Try first the default */ + gus_wave_init(hw_config); + + /* 0x10c-> is MAX */ + + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + if (0) + printf("gus_card.c: Can't allocate DMA channel2\n"); +#ifdef CONFIG_MIDI + gus_midi_init(); +#endif + return ; + } +#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 */ + && (gus_wave_detect(io_addr)) ) { + hw_config->io_base = io_addr; + + printf(" WARNING! GUS found at %x, config was %x ", + io_addr, hw_config->io_base); + gus_wave_init(hw_config); + /* 0x10c-> is MAX */ + if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) + if (0) + printf("gus_card.c: Can't allocate DMA channel2\n"); +#ifdef CONFIG_MIDI + gus_midi_init(); +#endif + return ; + } +#endif +} + +int +probe_gus(struct address_info * hw_config) +{ + int io_addr; + + gus_osp = hw_config->osp; +#ifndef NOGUSPNP + IwaveOpen((char) 32, (char) GUS_MODE, hw_config); +#endif + if (gus_wave_detect(hw_config->io_base)) + return 1; + printf("oops I didnt find gus \n"); +#undef EXCLUDE_GUS_IODETECT +#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 */ + && (gus_wave_detect(io_addr)) ) { + hw_config->io_base = io_addr; + return 1; + } +#endif + + return 0; +} + +void +gusintr(int irq) +{ + u_char src; + +#ifdef CONFIG_GUSMAX + if (have_gus_max) + ad1848_interrupt(irq); +#endif + + for (;;) { + if (!(src = inb(u_IrqStatus))) + return; + + if (src & DMA_TC_IRQ) + guswave_dma_irq(); +#ifdef CONFIG_MIDI + if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ)) + gus_midi_interrupt(0); +#endif + 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 */ +#else + gus_write8(0x45, 0); /* Stop timers */ +#endif + } + if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ)) + gus_voice_irq(); + } +} + +#endif + +/* + * Some extra code for the 16 bit sampling option + */ +#if defined(CONFIG_GUS16) + +int +probe_gus_db16(struct address_info * hw_config) +{ + return ad1848_detect(hw_config->io_base, NULL, hw_config->osp); +} + +void +attach_gus_db16(struct address_info * hw_config) +{ + gus_pcm_volume = 100; + gus_wave_volume = 90; + + ad1848_init("GUS 16 bit sampling", hw_config->io_base, + hw_config->irq, + hw_config->dma, + hw_config->dma, 0, + hw_config->osp); +} + +#endif diff --git a/sys/i386/isa/sound/gus_hw.h b/sys/i386/isa/sound/gus_hw.h new file mode 100644 index 0000000..f97a0b8 --- /dev/null +++ b/sys/i386/isa/sound/gus_hw.h @@ -0,0 +1,50 @@ + +/* + * I/O addresses + */ + +#define u_Base (gus_base + 0x000) +#define u_Mixer u_Base +#define u_Status (gus_base + 0x006) +#define u_TimerControl (gus_base + 0x008) +#define u_TimerData (gus_base + 0x009) +#define u_IRQDMAControl (gus_base + 0x00b) +#define u_MidiControl (gus_base + 0x100) +#define MIDI_RESET 0x03 +#define MIDI_ENABLE_XMIT 0x20 +#define MIDI_ENABLE_RCV 0x80 +#define u_MidiStatus u_MidiControl +#define MIDI_RCV_FULL 0x01 +#define MIDI_XMIT_EMPTY 0x02 +#define MIDI_FRAME_ERR 0x10 +#define MIDI_OVERRUN 0x20 +#define MIDI_IRQ_PEND 0x80 +#define u_MidiData (gus_base + 0x101) +#define u_Voice (gus_base + 0x102) +#define u_Command (gus_base + 0x103) +#define u_DataLo (gus_base + 0x104) +#define u_DataHi (gus_base + 0x105) +#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */ +#define u_MixSelect (gus_base + 0x506) /* registers. */ +#define u_IrqStatus u_Status +# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */ +# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */ +# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */ +# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */ +# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */ +# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */ +# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */ + +#define ICS2101 1 +# define ICS_MIXDEVS 6 +# define DEV_MIC 0 +# define DEV_LINE 1 +# define DEV_CD 2 +# define DEV_GF1 3 +# define DEV_UNUSED 4 +# define DEV_VOL 5 + +# define CHN_LEFT 0 +# define CHN_RIGHT 1 +#define CS4231 2 +#define u_DRAMIO (gus_base + 0x107) diff --git a/sys/i386/isa/sound/gus_linearvol.h b/sys/i386/isa/sound/gus_linearvol.h new file mode 100644 index 0000000..7ad0c30 --- /dev/null +++ b/sys/i386/isa/sound/gus_linearvol.h @@ -0,0 +1,18 @@ +static unsigned short gus_linearvol[128] = { + 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0, + 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0, + 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70, + 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0, + 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38, + 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78, + 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8, + 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8, + 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c, + 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c, + 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c, + 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c, + 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c, + 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc, + 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc, + 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc +}; diff --git a/sys/i386/isa/sound/gus_midi.c b/sys/i386/isa/sound/gus_midi.c new file mode 100644 index 0000000..0dfc7c5 --- /dev/null +++ b/sys/i386/isa/sound/gus_midi.c @@ -0,0 +1,274 @@ +/* + * sound/gus2_midi.c + * + * The low level driver for the GUS Midi Interface. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_GUS) && defined(CONFIG_MIDI) +#include <i386/isa/sound/gus_hw.h> + +static int midi_busy = 0, input_opened = 0; +static int my_dev; +static int output_used = 0; +static volatile unsigned char gus_midi_control; + +static void (*midi_input_intr) (int dev, unsigned char data); + +static unsigned char tmp_queue[256]; +static volatile int qlen; +static volatile unsigned char qhead, qtail; +extern int gus_base, gus_irq, gus_dma; +extern sound_os_info *gus_osp; + +#define GUS_MIDI_STATUS() inb( u_MidiStatus) + +static int +gus_midi_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + + if (midi_busy) { + printf("GUS: Midi busy\n"); + return -(EBUSY); + } + outb(u_MidiControl, MIDI_RESET); + gus_delay(); + + gus_midi_control = 0; + input_opened = 0; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) { + gus_midi_control |= MIDI_ENABLE_RCV; + input_opened = 1; + } + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) { + gus_midi_control |= MIDI_ENABLE_XMIT; + } + outb(u_MidiControl, gus_midi_control); /* Enable */ + + midi_busy = 1; + qlen = qhead = qtail = output_used = 0; + midi_input_intr = input; + + return 0; +} + +static int +dump_to_midi(unsigned char midi_byte) +{ + unsigned long flags; + int ok = 0; + + output_used = 1; + + flags = splhigh(); + + if (GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY) { + ok = 1; + outb(u_MidiData, midi_byte); + } else { + /* + * Enable Midi xmit interrupts (again) + */ + gus_midi_control |= MIDI_ENABLE_XMIT; + outb(u_MidiControl, gus_midi_control); + } + + splx(flags); + return ok; +} + +static void +gus_midi_close(int dev) +{ + /* + * Reset FIFO pointers, disable intrs + */ + + outb(u_MidiControl, MIDI_RESET); + midi_busy = 0; +} + +static int +gus_midi_out(int dev, unsigned char midi_byte) +{ + + unsigned long flags; + + /* + * Drain the local queue first + */ + + flags = splhigh(); + + while (qlen && dump_to_midi(tmp_queue[qhead])) { + qlen--; + qhead++; + } + + splx(flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi(midi_byte)) + return 1; /* OK */ + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* Local queue full */ + + flags = splhigh(); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + splx(flags); + + return 1; +} + +static int +gus_midi_start_read(int dev) +{ + return 0; +} + +static int +gus_midi_end_read(int dev) +{ + return 0; +} + +static int +gus_midi_ioctl(int dev, unsigned cmd, ioctl_arg arg) +{ + return -(EINVAL); +} + +static void +gus_midi_kick(int dev) +{ +} + +static int +gus_midi_buffer_status(int dev) +{ + unsigned long flags; + + if (!output_used) + return 0; + + flags = splhigh(); + + if (qlen && dump_to_midi(tmp_queue[qhead])) { + qlen--; + qhead++; + } + splx(flags); + + return (qlen > 0) | !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY); +} + +#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include <i386/isa/sound/midi_synth.h> + +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, /* command */ + gus_midi_buffer_status, + NULL +}; + +void +gus_midi_init() +{ + if (num_midis >= MAX_MIDI_DEV) { + printf("Sound: Too many midi devices detected\n"); + return; + } + outb(u_MidiControl, MIDI_RESET); + + std_midi_synth.midi_dev = my_dev = num_midis; + midi_devs[num_midis++] = &gus_midi_operations; + return; +} + +void +gus_midi_interrupt(int dummy) +{ + unsigned char stat, data; + unsigned long flags; + + flags = splhigh(); + + stat = GUS_MIDI_STATUS(); + + 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(u_MidiControl, gus_midi_control); + } + } + splx(flags); +} + +#endif diff --git a/sys/i386/isa/sound/gus_vol.c b/sys/i386/isa/sound/gus_vol.c new file mode 100644 index 0000000..fc66618 --- /dev/null +++ b/sys/i386/isa/sound/gus_vol.c @@ -0,0 +1,138 @@ +/* + * gus_vol.c - Compute volume for GUS. + * + * Greg Lee 1993. + */ +#include <i386/isa/sound/sound_config.h> + +#ifdef CONFIG_GUS +#include <i386/isa/sound/gus_linearvol.h> + +#define GUS_VOLUME gus_wave_volume + + +extern int gus_wave_volume; +unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev); +unsigned short gus_linear_vol(int vol, int mainvol); + +/* + * Calculate gus volume from note velocity, main volume, expression, and + * intrinsic patch volume given in patch library. Expression is multiplied + * in, so it emphasizes differences in note velocity, while main volume is + * added in -- I don't know whether this is right, but it seems reasonable to + * me. (In the previous stage, main volume controller messages were changed + * to expression controller messages, if they were found to be used for + * dynamic volume adjustments, so here, main volume can be assumed to be + * constant throughout a song.) + * + * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so + * we can give a big boost to very weak voices like nylon guitar and the + * basses. The normal value is 64. Strings are assigned lower values. + */ +unsigned short +gus_adagio_vol(int vel, int mainv, int xpn, int voicev) +{ + int i, m, n, x; + + /* + * A voice volume of 64 is considered neutral, so adjust the main + * volume if something other than this neutral value was assigned in + * the patch library. + */ + x = 256 + 6 * (voicev - 64); + + /* + * Boost expression by voice volume above neutral. + */ + if (voicev > 65) + xpn += voicev - 64; + xpn += (voicev - 64) / 2; + + /* + * Combine multiplicative and level components. + */ + x = vel * xpn * 6 + (voicev / 4) * x; + +#ifdef GUS_VOLUME + /* + * Further adjustment by installation-specific master volume control + * (default 60). + */ + x = (x * GUS_VOLUME * GUS_VOLUME) / 10000; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + /* + * Experimental support for the channel main volume + */ + + 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); +} + +/* + * Volume-values are interpreted as linear values. Volume is based on the + * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in) + * and the volume set by the mixer-device (default 60%). + */ + +unsigned short +gus_linear_vol(int vol, int mainvol) +{ + int mixer_mainvol; + + RANGE (vol, 0, 127) ; + +#ifdef GUS_VOLUME + mixer_mainvol = GUS_VOLUME; +#else + mixer_mainvol = 100; +#endif + +#ifdef GUS_USE_CHN_MAIN_VOLUME + RANGE (mainvol, 0, 127); +#else + mainvol = 127; +#endif + + return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100]; +} + +#endif diff --git a/sys/i386/isa/sound/gus_wave.c b/sys/i386/isa/sound/gus_wave.c new file mode 100644 index 0000000..54d36cb --- /dev/null +++ b/sys/i386/isa/sound/gus_wave.c @@ -0,0 +1,4879 @@ +/* + * sound/gus_wave.c + * + * Driver for the Gravis UltraSound wave table synth. + * + * Copyright by Hannu Savolainen 1993, 1994 + * + * 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 <stddef.h> + +#include <i386/isa/sound/sound_config.h> +#include <i386/isa/sound/ultrasound.h> +#include <i386/isa/sound/gus_hw.h> +#include <i386/isa/sound/iwdefs.h> +#include <machine/clock.h> + +/* PnP stuff */ +#define GUS_PNP_ID 0x100561e + +#define MAX_CARDS 8 +#define MAX_GUS_PNP 12 + + +/* Static ports */ +#define PADDRESS 0x279 +#define PWRITE_DATA 0xa79 +#define SET_CSN 0x06 +#define PSTATUS 0x05 + +/* PnP Registers. Write to ADDRESS and then use WRITE/READ_DATA */ +#define SET_RD_DATA 0x00 +#define SERIAL_ISOLATION 0x01 +#define WAKE 0x03 + +#if defined(CONFIG_GUS) + +static IWAVE iw; +#define ENTER_CRITICAL + +#define LEAVE_CRITICAL + +#define MAX_SAMPLE 150 +#define MAX_PATCH 256 + + +static u_int gus_pnp_found[MAX_GUS_PNP] = + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +struct voice_info { + u_long orig_freq; + u_long current_freq; + u_long mode; + int bender; + int bender_range; + int panning; + int midi_volume; + u_int initial_volume; + u_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; +#define VMODE_HALT 1 +#define VMODE_ENVELOPE 2 +#define VMODE_START_NOTE 3 + + int env_phase; + u_char env_rate[6]; + u_char env_offset[6]; + + /* + * 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; + +}; + +static struct voice_alloc_info *voice_alloc; + +extern int gus_base; +extern int gus_irq, gus_dma; +static int gus_dma2 = -1; +static int dual_dma_mode = 0; +static long gus_mem_size = 0; +static long free_mem_ptr = 0; +static int gus_no_dma = 0; +static int nr_voices = 0; +static int gus_devnum = 0; +static int volume_base, volume_scale, volume_method; +static int gus_recmask = SOUND_MASK_MIC; +static int recording_active = 0; +static int only_read_access = 0; +static int only_8_bits = 0; + +int gus_wave_volume = 60; +static int gus_pcm_volume = 80; +int have_gus_max = 0; +static int gus_line_vol = 100, gus_mic_vol = 0; +static u_char mix_image = 0x00; + +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 */ +#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */ +#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */ + +static int gus_sampling_speed; +static int gus_sampling_channels; +static int gus_sampling_bits; + +static int *dram_sleeper = NULL; +static volatile struct snd_wait dram_sleep_flag = +{0}; + +/* + * Variables and buffers for PCM output + */ +#define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /* Don't change */ + +static int pcm_bsize, pcm_nblk, pcm_banksize; +static int pcm_datasize[MAX_PCM_BUFFERS]; +static volatile int pcm_head, pcm_tail, pcm_qlen; +static volatile int pcm_active; +static volatile int dma_active; +static int pcm_opened = 0; +static int pcm_current_dev; +static int pcm_current_block; +static u_long pcm_current_buf; +static int pcm_current_count; +static int pcm_current_intrflag; + +extern sound_os_info *gus_osp; + +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 */ +}; + +static struct patch_info *samples; +static struct patch_info *dbg_samples; +static int dbg_samplep; + +static long sample_ptrs[MAX_SAMPLE + 1]; +static int sample_map[32]; +static int free_sample; +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 void gus_default_mixer_init(void); + +static int guswave_start_note2(int dev, int voice, int note_num, int volume); +static void gus_poke(long addr, u_char data); +static void compute_and_set_volume(int voice, int volume, int ramp_time); +extern u_short gus_adagio_vol(int vel, int mainv, int xpn, int voicev); +extern u_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 SEND(int d, int r); +static int get_serial(int rd_port, u_char *data); +static void send_Initiation_LFSR(void); +static int isolation_protocol(int rd_port); + + +#define INSTANT_RAMP -1 /* Instant change. No ramping */ +#define FAST_RAMP 0 /* Fastest possible ramp */ + + +/* Crystal Select */ +#define CODEC_XTAL2 0x01 /* 16.9344 crystal */ +#define CODEC_XTAL1 0x00 /* 24.576 crystal */ +/************************************************************************/ + +/************************************************************************/ +/* Definitions for CONFIG_1 register */ +#define CODEC_CFIG1I_DEFAULT 0x03 | 0x8 +#define CODEC_CAPTURE_PIO 0x80 /* Capture PIO enable */ +#define CODEC_PLAYBACK_PIO 0x40 /* Playback PIO enable */ +#define CODEC_AUTOCALIB 0x08 /* auto calibrate */ +#define CODEC_SINGLE_DMA 0x04 /* Use single DMA channel */ +#define CODEC_RE 0x02 /* Capture enable */ +#define CODEC_PE 0x01 /* playback enable */ +/************************************************************************/ + +/************************************************************************/ +/* Definitions for CONFIG_2 register */ +#define CODEC_CFIG2I_DEFAULT 0x81 +#define CODEC_OFVS 0x80 /* Output Full Scale Voltage */ +#define CODEC_TE 0x40 /* Timer Enable */ +#define CODEC_RSCD 0x20 /* Recors Sample Counter Disable */ +#define CODEC_PSCD 0x10 /* Playback Sample Counter Disable */ +#define CODEC_DAOF 0x01 /* D/A Ouput Force Enable */ +/************************************************************************/ + +/************************************************************************/ +/* Definitions for CONFIG_3 register */ +/* #define CODEC_CFIG3I_DEFAULT 0xe0 0x02 when synth DACs are working */ + +#define CODEC_CFIG3I_DEFAULT 0xc0 /* 0x02 when synth DACs are working */ +#define CODEC_RPIE 0x80 /* Record FIFO IRQ Enable */ +#define CODEC_PPIE 0x40 /* Playback FIFO IRQ Enable */ +#define CODEC_FT_MASK 0x30 /* FIFO Threshold Select */ +#define CODEC_PVFM 0x04 /* Playback Variable Frequency Mode */ +#define CODEC_SYNA 0x02 /* AUX1/Synth Signal Select */ +/************************************************************************/ + +/************************************************************************/ +/* Definitions for EXTERNAL_CONTROL register */ +#define CODEC_CEXTI_DEFAULT 0x00 +#define CODEC_IRQ_ENABLE 0x02 /* interrupt enable */ +#define CODEC_GPOUT1 0x80 /* external control #1 */ +#define CODEC_GPOUT0 0x40 /* external control #0 */ +/************************************************************************/ + +/************************************************************************/ +/* Definitions for MODE_SELECT_ID register */ +#define CODEC_MODE_DEFAULT 0x40 +#define CODEC_MODE_MASK 0x60 +#define CODEC_ID_BIT4 0x80 +#define CODEC_ID_BIT3_0 0x0F +/************************************************************************/ +#define CONFIG_1 0x09 +#define EXTERNAL_CONTROL 0x0a/* Pin control */ +#define STATUS_2 0x0b/* Test and initialization */ +#define MODE_SELECT_ID 0x0c/* Miscellaneaous information */ +#define LOOPBACK 0x0d/* Digital Mix */ +#define UPPER_PLAY_COUNT 0x0e/* Playback Upper Base Count */ +#define LOWER_PLAY_COUNT 0x0f/* Playback Lower Base Count */ +#define CONFIG_2 0x10 +#define CONFIG_3 0x11 + + +#define IWL_CODEC_OUT(reg, val) \ + { outb(iwl_codec_base, reg); outb(iwl_codec_data, val); } + +#define IWL_CODEC_IN(reg, val) \ + { outb(iwl_codec_base, reg); val = inb(iwl_codec_data); } + + +static u_char gus_look8(int reg); + +static void gus_write16(int reg, u_int data); + +static u_short gus_read16(int reg); + +static void gus_write_addr(int reg, u_long address, int is16bit); +static void IwaveLineLevel(char level, char index); +static void IwaveInputSource(BYTE index, BYTE source); +static void IwaveDelay(WORD count); +static void IwaveStopDma(BYTE path); +static void IwavePnpGetCfg(void); +static void IwavePnpDevice(BYTE dev); +static void IwavePnpSetCfg(void); +static void IwavePnpKey(void); +static BYTE IwavePnpIsol(PORT * pnpread); +static void IwaveCfgIOSpace(void); + +static void IwavePnpSerial(PORT pnprdp, BYTE csn, + BYTE * vendor, DWORD * serial); + + +static void IwavePnpPeek(PORT pnprdp, WORD bytes, BYTE * data); +static void IwavePnpEeprom(BYTE ctrl); +static void IwavePnpActivate(BYTE dev, BYTE bool); + +static void IwavePnpPower(BYTE mode); +static void IwavePnpWake(BYTE csn); +static PORT IwavePnpIOcheck(PORT base, BYTE no_ports); + +static BYTE IwavePnpGetCSN(DWORD VendorID, BYTE csn_max); +static BYTE IwavePnpPing(DWORD VendorID); +static WORD IwaveMemSize(void); +static BYTE IwaveMemPeek(ADDRESS addr); +static void IwaveMemPoke(ADDRESS addr, BYTE datum); +static void IwaveMemCfg(DWORD * lpbanks); +static void IwaveCodecIrq(BYTE mode); +static WORD IwaveRegPeek(DWORD reg_mnem); + +static void IwaveRegPoke(DWORD reg_mnem, WORD datum); +static void IwaveCodecMode(char mode); +static void IwaveLineMute(BYTE mute, BYTE inx); +static void Iwaveinitcodec(void); +int IwaveOpen(char voices, char mode, struct address_info * hw); + + +static void +reset_sample_memory(void) +{ + 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; + + gus_poke(0, 0); /* Put a silent sample to the beginning */ + gus_poke(1, 0); + free_mem_ptr = 2; + + free_sample = 0; + + for (i = 0; i < MAX_PATCH; i++) + patch_table[i] = -1; +} + +void +gus_delay(void) +{ + int i; + + for (i = 0; i < 7; i++) + inb(u_DRAMIO); +} + +static void +gus_poke(long addr, u_char data) +{ /* Writes a byte to the DRAM */ + u_long flags; + + flags = splhigh(); + outb(u_Command, 0x43); + outb(u_DataLo, addr & 0xff); + outb(u_DataHi, (addr >> 8) & 0xff); + + outb(u_Command, 0x44); + outb(u_DataHi, (addr >> 16) & 0xff); + outb(u_DRAMIO, data); + splx(flags); +} + +static u_char +gus_peek(long addr) +{ /* Reads a byte from the DRAM */ + u_long flags; + u_char tmp; + + flags = splhigh(); + outb(u_Command, 0x43); + outb(u_DataLo, addr & 0xff); + outb(u_DataHi, (addr >> 8) & 0xff); + + outb(u_Command, 0x44); + outb(u_DataHi, (addr >> 16) & 0xff); + tmp = inb(u_DRAMIO); + splx(flags); + + return tmp; +} + +void +gus_write8(int reg, u_int data) +{ /* Writes to an indirect register (8 bit) */ + u_long flags; + + flags = splhigh(); + outb(u_Command, reg); + outb(u_DataHi, (u_char) (data & 0xff)); + splx(flags); +} + +u_char +gus_read8(int reg) +{ /* Reads from an indirect register (8 bit). Offset 0x80. */ + u_long flags; + u_char val; + + flags = splhigh(); + outb(u_Command, reg | 0x80); + val = inb(u_DataHi); + splx(flags); + + return val; +} + +static u_char +gus_look8(int reg) +{ /* Reads from an indirect register (8 bit). No additional offset. */ + u_long flags; + u_char val; + + flags = splhigh(); + outb(u_Command, reg); + val = inb(u_DataHi); + splx(flags); + + return val; +} + +static void +gus_write16(int reg, u_int data) +{ /* Writes to an indirect register (16 bit) */ + u_long flags; + + flags = splhigh(); + + outb(u_Command, reg); + + outb(u_DataLo, (u_char) (data & 0xff)); + outb(u_DataHi, (u_char) ((data >> 8) & 0xff)); + + splx(flags); +} + +static u_short +gus_read16(int reg) +{ /* Reads from an indirect register (16 bit). Offset 0x80. */ + u_long flags; + u_char hi, lo; + + flags = splhigh(); + + outb(u_Command, reg | 0x80); + + lo = inb(u_DataLo); + hi = inb(u_DataHi); + + splx(flags); + + return ((hi << 8) & 0xff00) | lo; +} + +static void +gus_write_addr(int reg, u_long address, int is16bit) +{ /* Writes an 24 bit memory address */ + u_long hold_address; + u_long flags; + + flags = splhigh(); + if (is16bit) { + /* + * Special processing required for 16 bit patches + */ + + hold_address = address; + address = address >> 1; + address &= 0x0001ffffL; + address |= (hold_address & 0x000c0000L); + } + gus_write16(reg, (u_short) ((address >> 7) & 0xffff)); + gus_write16(reg + 1, (u_short) ((address << 9) & 0xffff)); + /* + * Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... + */ + gus_delay(); + gus_write16(reg, (u_short) ((address >> 7) & 0xffff)); + gus_write16(reg + 1, (u_short) ((address << 9) & 0xffff)); + splx(flags); +} + +static void +gus_select_voice(int voice) +{ + if (voice < 0 || voice > 31) + return; + + outb(u_Voice, voice); +} + +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; + + gus_write8(0x0e, (nvoices - 1) | 0xc0); +} + +static void +gus_voice_on(u_int mode) +{ + gus_write8(0x00, (u_char) (mode & 0xfc)); + gus_delay(); + gus_write8(0x00, (u_char) (mode & 0xfc)); +} + +static void +gus_voice_off(void) +{ + gus_write8(0x00, gus_read8(0x00) | 0x03); +} + +static void +gus_voice_mode(u_int m) +{ + u_char mode = (u_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)); +} + +static void +gus_voice_freq(u_long freq) +{ + u_long divisor = freq_div_table[nr_voices - 14]; + u_short fc; + + fc = (u_short) (((freq << 9) + (divisor >> 1)) / divisor); + fc = fc << 1; + + gus_write16(0x01, fc); +} + +static void +gus_voice_volume(u_int vol) +{ + gus_write8(0x0d, 0x03); /* Stop ramp before setting volume */ + gus_write16(0x09, (u_short) (vol << 4)); +} + +static void +gus_voice_balance(u_int balance) +{ + gus_write8(0x0c, (u_char) (balance & 0xff)); +} + +static void +gus_ramp_range(u_int low, u_int high) +{ + gus_write8(0x07, (u_char) ((low >> 4) & 0xff)); + gus_write8(0x08, (u_char) ((high >> 4) & 0xff)); +} + +static void +gus_ramp_rate(u_int scale, u_int rate) +{ + gus_write8(0x06, (u_char) (((scale & 0x03) << 6) | (rate & 0x3f))); +} + +static void +gus_rampon(u_int m) +{ + u_char mode = (u_char) (m & 0xff); + + gus_write8(0x0d, mode & 0xfc); + gus_delay(); + gus_write8(0x0d, mode & 0xfc); +} + +static void +gus_ramp_mode(u_int m) +{ + u_char mode = (u_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)); +} + +static void +gus_rampoff(void) +{ + gus_write8(0x0d, 0x03); +} + +static void +gus_set_voice_pos(int voice, long position) +{ + 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, + samples[sample_no].mode & WAVE_16_BITS); +} + +static void +gus_voice_init(int voice) +{ + u_long flags; + + flags = splhigh(); + gus_select_voice(voice); + gus_voice_volume(0); + gus_voice_off(); + gus_write_addr(0x0a, 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; + splx(flags); + +} + +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; +} + +static void +step_envelope(int voice) +{ + u_int vol, prev_vol, phase; + u_char rate; + long int flags; + + if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2) { + flags = splhigh(); + gus_select_voice(voice); + gus_rampoff(); + splx(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]; + + flags = splhigh(); + 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 */ + splx(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; + splx(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(); + splx(flags); + step_envelope(voice); +} + +static void +gus_voice_fade(int voice) +{ + int instr_no = sample_map[voice], is16bits; + long int flags; + + flags = splhigh(); + gus_select_voice(voice); + + if (instr_no < 0 || instr_no > MAX_SAMPLE) { + gus_write8(0x00, 0x03); /* Hard stop */ + voice_alloc->map[voice] = 0; + splx(flags); + return; + } + is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */ + + if (voices[voice].mode & WAVE_ENVELOPES) { + start_release(voice, 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); + 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; + splx(flags); +} + +static void +gus_reset(void) +{ + int i; + + gus_select_max_voices(24); + volume_base = 3071; + volume_scale = 4; + volume_method = VOL_METHOD_ADAGIO; + + for (i = 0; i < 32; i++) { + gus_voice_init(i); /* Turn voice off */ + gus_voice_init2(i); + } + + inb(u_Status); /* Touch the status register */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + + gus_read8(0x0f); /* Clear pending IRQs */ + +} + +static void +gus_initialize(void) +{ + u_long flags; + u_char dma_image, irq_image, tmp; + + static u_char gus_irq_map[16] = + {0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7}; + + static u_char gus_dma_map[8] = + {0, 1, 0, 2, 0, 3, 4, 5}; + + flags = splhigh(); + gus_write8(0x4c, 0); /* Reset GF1 */ + gus_delay(); + gus_delay(); + + gus_write8(0x4c, 1); /* Release Reset */ + gus_delay(); + gus_delay(); + + /* + * Clear all interrupts + */ + + gus_write8(0x41, 0); /* DMA control */ + gus_write8(0x45, 0); /* Timer control */ + gus_write8(0x49, 0); /* Sample control */ + + gus_select_max_voices(24); + + inb(u_Status); /* Touch the status register */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ + + gus_reset(); /* Resets all voices */ + + gus_look8(0x41); /* Clear any pending DMA IRQs */ + gus_look8(0x49); /* Clear any pending sample IRQs */ + gus_read8(0x0f); /* Clear pending IRQs */ + + gus_write8(0x4c, 7); /* Master reset | DAC enable | IRQ enable */ + + /* + * Set up for Digital ASIC + */ + + outb(gus_base + 0x0f, 0x05); + + mix_image |= 0x02; /* Disable line out (for a moment) */ + outb(u_Mixer, mix_image); + + outb(u_IRQDMAControl, 0x00); + + outb(gus_base + 0x0f, 0x00); + + /* + * 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. + */ + + irq_image = 0; + tmp = gus_irq_map[gus_irq]; + if (!tmp) + printf("Warning! GUS IRQ not selected\n"); + irq_image |= tmp; + irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */ + + 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) */ + + tmp = gus_dma_map[gus_dma]; + if (!tmp) + printf("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) + printf("Warning! GUS DMA not selected\n"); + + tmp = gus_dma_map[gus_dma2] << 3; + if (!tmp) { + printf("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 + */ + + /* + * Doing it first time + */ + + outb(u_Mixer, mix_image); /* Select DMA control */ + outb(u_IRQDMAControl, dma_image | 0x80); /* Set DMA address */ + + outb(u_Mixer, mix_image | 0x40); /* Select IRQ control */ + outb(u_IRQDMAControl, irq_image); /* Set IRQ address */ + + /* + * Doing it second time + */ + + outb(u_Mixer, mix_image); /* Select DMA control */ + outb(u_IRQDMAControl, dma_image); /* Set DMA address */ + + outb(u_Mixer, mix_image | 0x40); /* Select IRQ control */ + outb(u_IRQDMAControl, irq_image); /* Set IRQ address */ + + gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ + + mix_image &= ~0x02; /* Enable line out */ + mix_image |= 0x08; /* Enable IRQ */ + outb(u_Mixer, mix_image); /* Turn mixer channels on Note! Mic + * in is left off. */ + + gus_select_voice(0); /* This disables writes to IRQ/DMA reg */ + + gusintr(0); /* Serve pending interrupts */ + splx(flags); +} + +int +gus_wave_detect(int baseaddr) +{ + u_long i; + u_long loc; + gus_base = baseaddr; + + gus_write8(0x4c, 0); /* Reset GF1 */ + gus_delay(); + gus_delay(); + + gus_write8(0x4c, 1); /* Release Reset */ + gus_delay(); + gus_delay(); + + /* See if there is first block there.... */ + gus_poke(0L, 0xaa); + if (gus_peek(0L) != 0xaa) + return (0); + + /* Now zero it out so that I can check for mirroring .. */ + gus_poke(0L, 0x00); + for (i = 1L; i < 1024L; i++) { + 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; + } + gus_mem_size = i << 10; + return 1; +} + +static int +guswave_ioctl(int dev, + u_int cmd, ioctl_arg arg) +{ + + switch (cmd) { + case SNDCTL_SYNTH_INFO: + gus_info.nr_voices = nr_voices; + bcopy(&gus_info, &(((char *) arg)[0]), sizeof(gus_info)); + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + reset_sample_memory(); + return 0; + break; + + case SNDCTL_SEQ_PERCMODE: + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return gus_mem_size - free_mem_ptr - 32; + + default: + return -(EINVAL); + } +} + +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); + + 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; + + if (sample_no < 0) { + printf("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 */ + printf("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) +{ + u_long flags; + + flags = splhigh(); + /* voice_alloc->map[voice] = 0xffff; */ + if (voices[voice].volume_irq_mode == VMODE_START_NOTE) { + voices[voice].kill_pending = 1; + splx(flags); + } else { + splx(flags); + gus_voice_fade(voice); + } + + splx(flags); + return 0; +} + +static void +guswave_aftertouch(int dev, int voice, int pressure) +{ +} + +static void +guswave_panning(int dev, int voice, int value) +{ + if (voice >= 0 || voice < 32) + voices[voice].panning = value; +} + +static void +guswave_volume_method(int dev, int mode) +{ + if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO) + volume_method = mode; +} + +static void +compute_volume(int voice, int 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; + + 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); + } + + if (voices[voice].initial_volume > 4030) + voices[voice].initial_volume = 4030; +} + +static void +compute_and_set_volume(int voice, int volume, int ramp_time) +{ + int curr, target, rate; + u_long flags; + + compute_volume(voice, volume); + voices[voice].current_volume = voices[voice].initial_volume; + + flags = splhigh(); + /* + * CAUTION! Interrupts disabled. Enable them before returning + */ + + gus_select_voice(voice); + + curr = gus_read16(0x09) >> 4; + target = voices[voice].initial_volume; + + if (ramp_time == INSTANT_RAMP) { + gus_rampoff(); + gus_voice_volume(target); + splx(flags); + return; + } + if (ramp_time == FAST_RAMP) + rate = 63; + else + rate = 16; + gus_ramp_rate(0, rate); + + if ((target - curr) / 64 == 0) { /* Close enough to target. */ + gus_rampoff(); + gus_voice_volume(target); + splx(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 */ + } + splx(flags); +} + +static void +dynamic_volume_change(int voice) +{ + u_char status; + u_long flags; + + flags = splhigh(); + gus_select_voice(voice); + status = gus_read8(0x00); /* Get voice status */ + splx(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. + */ + + flags = splhigh(); + gus_select_voice(voice); + status = gus_read8(0x0d); /* Ramping status */ + splx(flags); + + if (status & 0x03) { /* Sustain phase? */ + compute_and_set_volume(voice, voices[voice].midi_volume, 1); + return; + } + 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) +{ + u_long flags; + u_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; + + flags = splhigh(); + gus_select_voice(voice); + gus_voice_freq(freq); + splx(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; + } +} + +static int +guswave_start_note2(int dev, int voice, int note_num, int volume) +{ + int sample, best_sample, best_delta, delta_freq; + int is16bits, samplep, patch, pan; + u_long note_freq, base_note, freq, flags; + u_char mode = 0; + + if (voice < 0 || voice > 31) { + printf("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]) == -1) + 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 && sample == -1) { + dbg_samples = samples; + dbg_samplep = samplep; + + 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; /* Follow link */ + } + if (sample == -1) + sample = best_sample; + + if (sample == -1) { + printf("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)) + printf("GUS: Sample address error\n"); + } + /* + * CAUTION! Interrupts disabled. Don't return before enabling + */ + + flags = splhigh(); + gus_select_voice(voice); + gus_voice_off(); + gus_rampoff(); + + splx(flags); + + if (voices[voice].mode & WAVE_ENVELOPES) { + compute_volume(voice, volume); + init_envelope(voice); + } else { + compute_and_set_volume(voice, volume, 0); + } + + flags = splhigh(); + 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, is16bits); /* start=end */ + else + gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending, + 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, is16bits); + mode |= 0x40; + } + gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start, + is16bits); /* Loop start location */ + gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end, + 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], + is16bits); /* Loop start location */ + gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1, + is16bits); /* Loop end location */ + } + gus_voice_freq(freq); + gus_voice_balance(pan); + gus_voice_on(mode); + splx(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) +{ + long int flags; + int mode; + int ret_val = 0; + + flags = splhigh(); + 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) { + splx(flags); /* Run temporarily with interrupts + * enabled */ + guswave_set_instr(voices[voice].dev_pending, voice, + voices[voice].sample_pending); + voices[voice].sample_pending = -1; + flags = splhigh(); + gus_select_voice(voice); /* Reselect the voice + * (just to be sure) */ + } + if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < 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 */ + } + } + splx(flags); + return ret_val; +} + +static void +guswave_reset(int dev) +{ + int i; + + for (i = 0; i < 32; i++) { + gus_voice_init(i); + gus_voice_init2(i); + } +} + +static int +guswave_open(int dev, int mode) +{ + int err; + int otherside = audio_devs[dev]->otherside; + + if (otherside != -1) { + if (audio_devs[otherside]->busy) + return -(EBUSY); + } + if (audio_devs[dev]->busy) + return -(EBUSY); + + gus_initialize(); + voice_alloc->timestamp = 0; + + if ((err = DMAbuf_open_dma(gus_devnum)) < 0) { + printf("GUS: Loading saples without DMA\n"); + gus_no_dma = 1; /* Upload samples using PIO */ + } else + gus_no_dma = 0; + + dram_sleep_flag.aborting = 0; + dram_sleep_flag.mode = WK_NONE; + active_device = GUS_DEV_WAVE; + + audio_devs[dev]->busy = 1; + gus_reset(); + + return 0; +} + +static void +guswave_close(int dev) +{ + int otherside = audio_devs[dev]->otherside; + + if (otherside != -1) { + if (audio_devs[otherside]->busy) + return; + } + audio_devs[dev]->busy = 0; + + active_device = 0; + gus_reset(); + + if (!gus_no_dma) + DMAbuf_close_dma(gus_devnum); +} + +static int +guswave_load_patch(int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info patch; + int instr; + long sizeof_patch; + + u_long blk_size, blk_end, left, src_offs, target; + + sizeof_patch = offsetof(struct patch_info, data); /* Header size */ + + if (format != GUS_PATCH) { + printf("GUS Error: Invalid patch format (key) 0x%x\n", format); + return -(EINVAL); + } + if (count < sizeof_patch) { + printf("GUS Error: Patch header too short\n"); + return -(EINVAL); + } + count -= sizeof_patch; + + if (free_sample >= MAX_SAMPLE) { + printf("GUS: Sample table full\n"); + return -(ENOSPC); + } + /* + * Copy the header from user space but ignore the first bytes which + * have been transferred already. + */ + + if (uiomove(&((char *) &patch)[offs], sizeof_patch - offs, addr)) { + printf("audio: Bad copyin()!\n"); + }; + + instr = patch.instr_no; + + if (instr < 0 || instr > MAX_PATCH) { + printf("GUS: Invalid patch number %d\n", instr); + return -(EINVAL); + } + if (count < patch.len) { + printf("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) { + printf("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) { + printf("GUS: Invalid loop start\n"); + return -(EINVAL); + } + if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len) { + printf("GUS: Invalid loop end\n"); + return -(EINVAL); + } + } + free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */ + +#define GUS_BANK_SIZE (256*1024) + + if (patch.mode & WAVE_16_BITS) { + /* + * 16 bit samples must fit one 256k bank. + */ + if (patch.len >= GUS_BANK_SIZE) { + printf("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)) { + u_long tmp_mem = /* Aligning 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); + + sample_ptrs[free_sample] = free_mem_ptr; + + /* + * Tremolo is not possible with envelopes + */ + + if (patch.mode & WAVE_ENVELOPES) + patch.mode &= ~WAVE_TREMOLO; + + bcopy(&patch, (char *) &samples[free_sample], 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_size = audio_devs[gus_devnum]->buffsize; */ + blk_size = audio_devs[gus_devnum]->dmap_out->bytes_in_use; + if (blk_size > left) + blk_size = left; + + /* + * DMA cannot cross 256k bank boundaries. Check for that. + */ + blk_end = target + blk_size; + + if ((target >> 18) != (blk_end >> 18)) { /* Split the block */ + blk_end &= ~(256 * 1024 - 1); + blk_size = blk_end - target; + } + if (gus_no_dma) { + /* + * For some reason the DMA is not possible. We have + * to use PIO. + */ + long i; + u_char data; + + for (i = 0; i < blk_size; i++) { + uiomove((char *) &(data), 1, addr); + if (patch.mode & WAVE_UNSIGNED) + if (!(patch.mode & WAVE_16_BITS) || (i & 0x01)) + data ^= 0x80; /* Convert to signed */ + gus_poke(target + i, data); + } + } else { + u_long address, hold_address; + u_char dma_command; + u_long flags; + + /* + * OK, move now. First in and then out. + */ + + if (uiomove(audio_devs[gus_devnum]->dmap_out->raw_buf, blk_size, addr)) { + printf("audio: Bad copyin()!\n"); + }; + + flags = splhigh(); + /******** 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_size, 1); + + /* + * Set the DRAM address for the wave data + */ + + address = target; + + 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; + + + { + int chn; + + dram_sleep_flag.mode = WK_SLEEP; + dram_sleeper = &chn; + DO_SLEEP(chn, dram_sleep_flag, hz); + + }; + if ((dram_sleep_flag.mode & WK_TIMEOUT)) + printf("GUS: DMA Transfer timed out\n"); + splx(flags); + } + + /* + * Now the next part + */ + + left -= blk_size; + src_offs += blk_size; + target += blk_size; + + 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, u_char *event) +{ + int voice, cmd; + u_short p1, p2; + u_long plong, flags; + + cmd = event[2]; + voice = event[3]; + p1 = *(u_short *) &event[4]; + p2 = *(u_short *) &event[6]; + plong = *(u_long *) &event[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: + flags = splhigh(); + gus_select_voice(voice); + gus_select_max_voices(p1); + splx(flags); + break; + + case _GUS_VOICESAMPLE: + guswave_set_instr(dev, voice, p1); + break; + + case _GUS_VOICEON: + flags = splhigh(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_on(p1); + splx(flags); + break; + + case _GUS_VOICEOFF: + flags = splhigh(); + gus_select_voice(voice); + gus_voice_off(); + splx(flags); + break; + + case _GUS_VOICEFADE: + gus_voice_fade(voice); + break; + + case _GUS_VOICEMODE: + flags = splhigh(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_voice_mode(p1); + splx(flags); + break; + + case _GUS_VOICEBALA: + flags = splhigh(); + gus_select_voice(voice); + gus_voice_balance(p1); + splx(flags); + break; + + case _GUS_VOICEFREQ: + flags = splhigh(); + gus_select_voice(voice); + gus_voice_freq(plong); + splx(flags); + break; + + case _GUS_VOICEVOL: + flags = splhigh(); + gus_select_voice(voice); + gus_voice_volume(p1); + splx(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 */ + flags = splhigh(); + gus_select_voice(voice); + gus_ramp_range(p1, p2); + splx(flags); + break; + + case _GUS_RAMPRATE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NJET-NJET */ + flags = splhigh(); + gus_select_voice(voice); + gus_ramp_rate(p1, p2); + splx(flags); + break; + + case _GUS_RAMPMODE: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NO-NO */ + flags = splhigh(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_ramp_mode(p1); + splx(flags); + break; + + case _GUS_RAMPON: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* EI-EI */ + flags = splhigh(); + gus_select_voice(voice); + p1 &= ~0x20; /* Don't allow interrupts */ + gus_rampon(p1); + splx(flags); + break; + + case _GUS_RAMPOFF: + if (voices[voice].mode & WAVE_ENVELOPES) + break; /* NEJ-NEJ */ + flags = splhigh(); + gus_select_voice(voice); + gus_rampoff(); + splx(flags); + break; + + case _GUS_VOLUME_SCALE: + volume_base = p1; + volume_scale = p2; + break; + + case _GUS_VOICE_POS: + flags = splhigh(); + gus_select_voice(voice); + gus_set_voice_pos(voice, plong); + splx(flags); + break; + + default:; + } +} + +static int +gus_sampling_set_speed(int speed) +{ + + if (speed <= 0) + speed = gus_sampling_speed; + + RANGE(speed, 4000, 44100); + gus_sampling_speed = speed; + + if (only_read_access) { + /* Compute nearest valid recording speed and return it */ + + speed = (9878400 / (gus_sampling_speed + 2)) / 16; + speed = (9878400 / (speed * 16)) - 2; + } + return speed; +} + +static int +gus_sampling_set_channels(int channels) +{ + if (!channels) + return gus_sampling_channels; + RANGE(channels, 1, 2); + gus_sampling_channels = channels; + return channels; +} + +static int +gus_sampling_set_bits(int bits) +{ + if (!bits) + return gus_sampling_bits; + + if (bits != 8 && bits != 16) + bits = 8; + + if (only_8_bits) + bits = 8; + + gus_sampling_bits = bits; + return bits; +} + +static int +gus_sampling_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) +{ + switch (cmd) { + case SOUND_PCM_WRITE_RATE: + if (local) + return gus_sampling_set_speed((int) arg); + return *(int *) arg = gus_sampling_set_speed((*(int *) arg)); + break; + + case SOUND_PCM_READ_RATE: + if (local) + return gus_sampling_speed; + return *(int *) arg = gus_sampling_speed; + break; + + case SNDCTL_DSP_STEREO: + if (local) + return gus_sampling_set_channels((int) arg + 1) - 1; + return *(int *) arg = gus_sampling_set_channels((*(int *) arg) + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return gus_sampling_set_channels((int) arg); + return *(int *) arg = gus_sampling_set_channels((*(int *) arg)); + break; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return gus_sampling_channels; + return *(int *) arg = gus_sampling_channels; + break; + + case SNDCTL_DSP_SETFMT: + if (local) + return gus_sampling_set_bits((int) arg); + return *(int *) arg = gus_sampling_set_bits((*(int *) arg)); + break; + + case SOUND_PCM_READ_BITS: + if (local) + return gus_sampling_bits; + return *(int *) arg = gus_sampling_bits; + + case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */ + return *(int *) arg = -(EINVAL); + break; + + case SOUND_PCM_READ_FILTER: + return *(int *) arg = -(EINVAL); + break; + + } + return -(EINVAL); +} + +static void +gus_sampling_reset(int dev) +{ + if (recording_active) { + gus_write8(0x49, 0x00); /* Halt recording */ + set_input_volumes(); + } +} + +static int +gus_sampling_open(int dev, int mode) +{ + + int otherside = audio_devs[dev]->otherside; + if (otherside != -1) { + if (audio_devs[otherside]->busy) + return -(EBUSY); + } + if (audio_devs[dev]->busy) + return -(EBUSY); + + + gus_initialize(); + + active_device = 0; + + gus_reset(); + reset_sample_memory(); + gus_select_max_voices(14); + + pcm_active = 0; + dma_active = 0; + pcm_opened = 1; + audio_devs[dev]->busy = 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; + + return 0; +} + +static void +gus_sampling_close(int dev) +{ + int otherside = audio_devs[dev]->otherside; + audio_devs[dev]->busy = 0; + + if (otherside != -1) { + if (audio_devs[otherside]->busy) + return; + } + gus_reset(); + + pcm_opened = 0; + active_device = 0; + + if (recording_active) { + gus_write8(0x49, 0x00); /* Halt recording */ + set_input_volumes(); + } + recording_active = 0; +} + +static void +gus_sampling_update_volume(void) +{ + u_long flags; + int voice; + + if (pcm_active && pcm_opened) + for (voice = 0; voice < gus_sampling_channels; voice++) { + flags = splhigh(); + gus_select_voice(voice); + gus_rampoff(); + gus_voice_volume(1530 + (25 * gus_pcm_volume)); + gus_ramp_range(65, 1530 + (25 * gus_pcm_volume)); + splx(flags); + } +} + +static void +play_next_pcm_block(void) +{ + u_long flags; + int speed = gus_sampling_speed; + int this_one, is16bits, chn; + u_long dram_loc; + u_char mode[2], ramp_mode[2]; + + if (!pcm_qlen) + return; + + this_one = pcm_head; + + for (chn = 0; chn < gus_sampling_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_sampling_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 */ + } + + flags = splhigh(); + gus_select_voice(chn); + gus_voice_freq(speed); + + if (gus_sampling_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, dram_loc, is16bits); /* Starting position */ + gus_write_addr(0x02, chn * pcm_banksize, is16bits); /* Loop start */ + + if (chn != 0) + gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1, + is16bits); /* Loop end location */ + } + if (chn == 0) + gus_write_addr(0x04, dram_loc + pcm_datasize[this_one] - 1, + 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], + is16bits); /* Loop end location */ + mode[chn] &= ~0x08; /* Disable looping */ + } + } + splx(flags); + } + + for (chn = 0; chn < gus_sampling_channels; chn++) { + flags = splhigh(); + gus_select_voice(chn); + gus_write8(0x0d, ramp_mode[chn]); + gus_voice_on(mode[chn]); + splx(flags); + } + + pcm_active = 1; +} + +static void +gus_transfer_output_block(int dev, u_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; + u_long flags; + u_char dma_command; + u_long address, hold_address; + + flags = splhigh(); + + count = total_count / gus_sampling_channels; + + if (chn == 0) { + if (pcm_qlen >= pcm_nblk) + printf("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, 1); + + 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_sampling_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); /* Kickstart */ + + if (chn == (gus_sampling_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)) { + play_next_pcm_block(); + } + } else { + /* + * Left channel data. The right channel is transferred after + * DMA interrupt + */ + active_device = GUS_DEV_PCM_CONTINUE; + } + + splx(flags); +} + +static void +gus_sampling_output_block(int dev, u_long buf, int total_count, + int intrflag, int restart_dma) +{ + 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); +} + +static void +gus_sampling_start_input(int dev, u_long buf, int count, + int intrflag, int restart_dma) +{ + u_long flags; + u_char mode; + + flags = splhigh(); + + DMAbuf_start_dma(dev, buf, count, 0); + + mode = 0xa0; /* DMA IRQ enabled, invert MSB */ + + if (audio_devs[dev]->dmachan2 > 3) + mode |= 0x04; /* 16 bit DMA channel */ + if (gus_sampling_channels > 1) + mode |= 0x02; /* Stereo */ + mode |= 0x01; /* DMA enable */ + + gus_write8(0x49, mode); + + splx(flags); +} + +static int +gus_sampling_prepare_for_input(int dev, int bsize, int bcount) +{ + u_int rate; + + rate = (9878400 / (gus_sampling_speed + 2)) / 16; + + gus_write8(0x48, rate & 0xff); /* Set sampling rate */ + + if (gus_sampling_bits != 8) { + printf("GUS Error: 16 bit recording not supported\n"); + return -(EINVAL); + } + return 0; +} + +static int +gus_sampling_prepare_for_output(int dev, int bsize, int bcount) +{ + int i; + + long mem_ptr, mem_size; + + mem_ptr = 0; + mem_size = gus_mem_size / gus_sampling_channels; + + if (mem_size > (256 * 1024)) + mem_size = 256 * 1024; + + pcm_bsize = bsize / gus_sampling_channels; + pcm_head = pcm_tail = pcm_qlen = 0; + + pcm_nblk = MAX_PCM_BUFFERS; + if ((pcm_bsize * pcm_nblk) > mem_size) + pcm_nblk = mem_size / pcm_bsize; + + for (i = 0; i < pcm_nblk; i++) + pcm_datasize[i] = 0; + + pcm_banksize = pcm_nblk * pcm_bsize; + + if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024)) + pcm_nblk--; + + return 0; +} + +static int +gus_local_qlen(int dev) +{ + return pcm_qlen; +} + +static void +gus_copy_from_user(int dev, char *localbuf, int localoffs, + snd_rw_buf * userbuf, int useroffs, int len) +{ + if (gus_sampling_channels == 1) { + + if (uiomove(&localbuf[localoffs], len, userbuf)) { + printf("audio: Bad copyin()!\n"); + }; + } else if (gus_sampling_bits == 8) { + int in_left = useroffs; + int in_right = useroffs + 1; + char *out_left, *out_right; + int i; + + len /= 2; + localoffs /= 2; + out_left = &localbuf[localoffs]; + out_right = out_left + pcm_bsize; + + for (i = 0; i < len; i++) { + uiomove((char *) &(*out_left++), 1, userbuf); + in_left += 2; + uiomove((char *) &(*out_right++), 1, userbuf); + in_right += 2; + } + } else { + int in_left = useroffs; + int in_right = useroffs + 2; + short *out_left, *out_right; + int i; + + len /= 4; + localoffs /= 2; + + out_left = (short *) &localbuf[localoffs]; + out_right = out_left + (pcm_bsize / 2); + + for (i = 0; i < len; i++) { + uiomove((char *) &(*out_left++), 2, userbuf); + in_left += 2; + uiomove((char *) &(*out_right++), 2, userbuf); + in_right += 2; + } + } +} + +static struct audio_operations gus_sampling_operations = +{ + "Gravis UltraSound", + NEEDS_RESTART, + AFMT_U8 | AFMT_S16_LE, + NULL, + gus_sampling_open, + gus_sampling_close, + gus_sampling_output_block, + gus_sampling_start_input, + gus_sampling_ioctl, + gus_sampling_prepare_for_input, + gus_sampling_prepare_for_output, + gus_sampling_reset, + gus_sampling_reset, + gus_local_qlen, + gus_copy_from_user +}; + +static void +guswave_setup_voice(int dev, int voice, int chn) +{ + struct channel_info *info = + &synth_devs[dev]->chn_info[chn]; + + 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) / 128; + voices[voice].panning = + (info->controllers[CTL_PAN] * 2) - 128; + voices[voice].bender = info->bender_value; +} + +static void +guswave_bender(int dev, int voice, int value) +{ + int freq; + u_long flags; + + voices[voice].bender = value - 8192; + freq = compute_finetune(voices[voice].orig_freq, value - 8192, + voices[voice].bender_range); + voices[voice].current_freq = freq; + + flags = splhigh(); + gus_select_voice(voice); + gus_voice_freq(freq); + splx(flags); +} + +static int +guswave_patchmgr(int dev, struct patmgr_info * rec) +{ + int i, n; + + switch (rec->command) { + case PM_GET_DEVTYPE: + rec->parm1 = PMTYPE_WAVE; + return 0; + break; + + 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++) { + int ptr = patch_table[i]; + + rec->data.data8[i] = 0; + + while (ptr >= 0 && ptr < free_sample) { + rec->data.data8[i]++; + ptr = samples[ptr].key; /* Follow link */ + } + } + return 0; + break; + + case PM_GET_PGM_PATCHES: + { + int ptr = patch_table[rec->parm1]; + + n = 0; + + while (ptr >= 0 && ptr < free_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); + + bcopy((char *) &samples[ptr], rec->data.data8, sizeof(struct patch_info)); + + pat = (struct patch_info *) rec->data.data8; + + pat->key = GUS_PATCH; /* Restore patch type */ + rec->parm1 = sample_ptrs[ptr]; /* DRAM location */ + rec->parm2 = sizeof(struct patch_info); + } + return 0; + break; + + case PM_SET_PATCH: + { + int ptr = rec->parm1; + struct patch_info *pat; + + if (ptr < 0 || ptr >= free_sample) + return -(EINVAL); + + pat = (struct patch_info *) rec->data.data8; + + if (pat->len > samples[ptr].len) /* Cannot expand sample */ + return -(EINVAL); + + pat->key = samples[ptr].key; /* Ensure the link is + * correct */ + + bcopy(rec->data.data8, (char *) &samples[ptr], sizeof(struct patch_info)); + + pat->key = GUS_PATCH; + } + return 0; + break; + + 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; + + if (sample < 0 || sample >= free_sample) + return -(EINVAL); + + if (offs < 0 || offs >= samples[sample].len) + return -(EINVAL); /* Invalid offset */ + + n = samples[sample].len - offs; /* Num of bytes left */ + + if (l > n) + l = n; + + if (l > sizeof(rec->data.data8)) + l = sizeof(rec->data.data8); + + if (l <= 0) + return -(EINVAL); /* Was there a bug? */ + + offs += sample_ptrs[sample]; /* Begin offsess + + * offset to DRAM */ + + for (n = 0; n < l; n++) + rec->data.data8[n] = gus_peek(offs++); + rec->parm1 = n; /* Nr of bytes copied */ + } + return 0; + break; + + 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 */ + + if (l > n) + l = n; + + if (l > sizeof(rec->data.data8)) + l = sizeof(rec->data.data8); + + if (l <= 0) + return -(EINVAL); /* Was there a bug? */ + + offs += sample_ptrs[sample]; /* Begin offsess + + * offset to DRAM */ + + for (n = 0; n < l; n++) + gus_poke(offs++, rec->data.data8[n]); + rec->parm1 = n; /* Nr of bytes copied */ + } + return 0; + break; + + default: + return -(EINVAL); + } +} + +static int +guswave_alloc(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; + 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; + return p; + } + p = (p + 1) % alloc->max_voice; + } + + if (best >= 0) + p = best; + + 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 +}; + +static void +set_input_volumes(void) +{ + u_long flags; + u_char mask = 0xff & ~0x06; /* Just line out enabled */ + + if (have_gus_max) /* Don't disturb GUS MAX */ + return; + + flags = splhigh(); + + /* + * 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; + + 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(u_Mixer, mix_image); + + splx(flags); +} + +int +gus_default_mixer_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH|SOUND_MASK_PCM) + + if (((cmd >> 8) & 0xff) == 'M') { + if (cmd & IOC_IN) + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + gus_recmask = (*(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 *(int *) arg = gus_recmask; + break; + + case SOUND_MIXER_MIC: + { + int vol = (*(int *) arg) & 0xff; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_mic_vol = vol; + set_input_volumes(); + return *(int *) arg = vol | (vol << 8); + } + break; + + case SOUND_MIXER_LINE: + { + int vol = (*(int *) arg) & 0xff; + + if (vol < 0) + vol = 0; + if (vol > 100) + vol = 100; + gus_line_vol = vol; + set_input_volumes(); + return *(int *) arg = vol | (vol << 8); + } + break; + + case SOUND_MIXER_PCM: + gus_pcm_volume = (*(int *) arg) & 0xff; + RANGE (gus_pcm_volume, 0, 100); + gus_sampling_update_volume(); + return *(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8); + break; + + case SOUND_MIXER_SYNTH: + { + int voice; + + gus_wave_volume = (*(int *) arg) & 0xff; + + RANGE (gus_wave_volume , 0, 100); + + if (active_device == GUS_DEV_WAVE) + for (voice = 0; voice < nr_voices; voice++) + dynamic_volume_change(voice); /* Apply the new vol */ + + 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 *(int *) arg = gus_recmask; + break; + + case SOUND_MIXER_DEVMASK: + return *(int *) arg = MIX_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + return *(int *) arg = 0; + break; + + case SOUND_MIXER_RECMASK: + return *(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + + case SOUND_MIXER_CAPS: + return *(int *) arg = 0; + break; + + case SOUND_MIXER_MIC: + return *(int *) arg = gus_mic_vol | (gus_mic_vol << 8); + break; + + case SOUND_MIXER_LINE: + return *(int *) arg = gus_line_vol | (gus_line_vol << 8); + break; + + case SOUND_MIXER_PCM: + return *(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8); + break; + + case SOUND_MIXER_SYNTH: + return *(int *) arg = gus_wave_volume | (gus_wave_volume << 8); + break; + + default: + return -(EINVAL); + } +} else + return -(EINVAL); +} + +static struct mixer_operations gus_mixer_operations = {"Gravis Ultrasound", gus_default_mixer_ioctl}; + +static void +gus_default_mixer_init() +{ +if (num_mixers < MAX_MIXER_DEV) /* Don't install if there is another + * mixer */ + mixer_devs[num_mixers++] = &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(u_Mixer, mix_image); +} +} + +/* start of pnp code */ + +static void +SEND(int d, int r) +{ +outb(PADDRESS, d); +outb(PWRITE_DATA, r); +} + + + + +/* + * Get the device's serial number. Returns 1 if the serial is valid. + */ +static int +get_serial(int rd_port, u_char *data) +{ + int i, bit, valid = 0, sum = 0x6a; + + bzero(data, sizeof(char) * 9); + + for (i = 0; i < 72; i++) { + bit = inb((rd_port << 2) | 0x3) == 0x55; + DELAY(250); /* Delay 250 usec */ + + /* Can't Short Circuit the next evaluation, so 'and' is last */ + bit = (inb((rd_port << 2) | 0x3) == 0xaa) && bit; + DELAY(250); /* Delay 250 usec */ + + valid = valid || bit; + + if (i < 64) + sum = (sum >> 1) | + (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff); + + data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0); + } + valid = valid && (data[8] == sum); + + return valid; +} + +static void +send_Initiation_LFSR() +{ + int cur, i; + + /* Reset the LSFR */ + outb(PADDRESS, 0); + outb(PADDRESS, 0); + + cur = 0x6a; + outb(PADDRESS, cur); + + for (i = 1; i < 32; i++) { + cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff); + outb(PADDRESS, cur); + } +} + + + +static int +isolation_protocol(int rd_port) +{ + int csn; + u_char data[9]; + + send_Initiation_LFSR(); + + /* Reset CSN for All Cards */ + SEND(0x02, 0x04); + + for (csn = 1; (csn < MAX_CARDS); csn++) { + /* Wake up cards without a CSN */ + + SEND(WAKE, 0); + SEND(SET_RD_DATA, rd_port); + outb(PADDRESS, SERIAL_ISOLATION); + DELAY(1000); /* Delay 1 msec */ + if (get_serial(rd_port, data)) { + printf("Board Vendor ID: %c%c%c%02x%02x", + ((data[0] & 0x7c) >> 2) + 64, + (((data[0] & 0x03) << 3) | ((data[1] & 0xe0) >> 5)) + 64, + (data[1] & 0x1f) + 64, data[2], data[3]); + printf(" Board Serial Number: %08x\n", *(int *) &(data[4])); + + SEND(SET_CSN, csn); /* Move this out of this + * function XXX */ + outb(PADDRESS, PSTATUS); + + + return rd_port; + } else + break; + } + + return 0; +} + + + +static void +IwaveDelay(WORD count) +{ + WORD cur_cnt = 0, last_cnt; + BYTE reg, portb; + + count = 1193 * count; /* convert number of ms to counter */ + last_cnt = count; + portb = inb(0x61) & 0xFC; + outb(0x61, portb); /* disable counter */ + outb(0x43, 0xB0); /* load LSB first then MSB */ + outb(0x42, (BYTE) count); + outb(0x42, (BYTE) (count >> 8)); + outb(0x61, (BYTE) (portb | 0x01)); /* enable counter */ + while (cur_cnt <= count) { + outb(0x43, 0x80); /* latch counter */ + reg = inb(0x42);/* read latched value */ + cur_cnt = (((WORD) inb(0x42)) << 8) | reg; + if (cur_cnt > last_cnt) + break; + last_cnt = cur_cnt; + } + outb(0x61, portb); /* disable counter */ +} + +/* + * ######################################################################## + * + * FUNCTION: IwaveStopDma + * + * PROFILE: This function stops an active DMA transfer to or from the record or + * playback FIFOs. Set the "path" variable to either PLAYBACK or RECORD. + * ######################################################################## + */ + +static void +IwaveStopDma(BYTE path) +{ + BYTE reg; + + ENTER_CRITICAL; + reg = inb(iw.pcodar) & 0xE0; + outb(iw.pcodar, reg | _CFIG1I); /* select CFIG1I */ + outb(iw.cdatap, (BYTE) (inb(iw.cdatap) & ~path)); /* disable playback path */ + LEAVE_CRITICAL; +} + +/* + * ######################################################################## + * + * FUNCTION : IwaveInputSource + * + * PROFILE: This function allows the calling program to select among any of + * several possible sources to the ADC's. The possible input sources and + * their corresponding symbolic constants are: - Line (LINE_IN) - Aux1 + * (AUX1_IN) - Microphone (MIC_IN) - Mixer (MIX_IN) + * + * Set the first argument to either LEFT_SOURCE or RIGHT_SOURCE. Always use the + * symbolic contants for the arguments. + * + * ######################################################################## + */ +static void +IwaveInputSource(BYTE index, BYTE source) +{ + BYTE reg; + + ENTER_CRITICAL; + reg = inb(iw.pcodar) & 0xE0; + outb(iw.pcodar, reg | index); /* select register CLICI or CRICI */ + reg = inb(iw.cdatap) & ~MIX_IN; + source &= MIX_IN; + outb(iw.cdatap, (BYTE) (reg | source)); + LEAVE_CRITICAL; +} +static void +IwavePnpGetCfg(void) +{ + WORD val; + + + ENTER_CRITICAL; + IwavePnpDevice(AUDIO); + outb(_PIDXR, 0x60); /* select P2X0HI */ + val = ((WORD) inb(iw.pnprdp)) << 8; /* get P2XR[9:8] */ + outb(_PIDXR, 0x61); /* select P2XRLI */ + iw.p2xr = val + (WORD) inb(iw.pnprdp); /* get P2XR[7:4] */ + + outb(_PIDXR, 0x62); /* select P3X0HI */ + val = ((WORD) inb(iw.pnprdp)) << 8; /* get P3XR[9:8] */ + outb(_PIDXR, 0x63); /* select P3X0LI */ + iw.p3xr = val + (WORD) inb(iw.pnprdp); /* get P3XR[7:3] */ + + outb(_PIDXR, 0x64); /* select PHCAI */ + val = ((WORD) inb(iw.pnprdp)) << 8; /* get PCODAR[9:8] */ + outb(_PIDXR, 0x65); /* select PLCAI */ + iw.pcodar = val + (WORD) inb(iw.pnprdp); /* get PCODAR[7:2] */ + + outb(_PIDXR, 0x70); /* select PUI1SI */ + iw.synth_irq = (WORD) (inb(iw.pnprdp) & 0x0F); /* Synth IRQ number */ + + outb(_PIDXR, 0x72); /* select PUI2SI */ + iw.midi_irq = (WORD) (inb(iw.pnprdp) & 0x0F); /* MIDI IRQ number */ + + outb(_PIDXR, 0x74); /* select PUD1SI */ + iw.dma1_chan = inb(iw.pnprdp) & 0x07; /* DMA1 chan (LMC/Codec Rec) */ + + outb(_PIDXR, 0x75); /* select PUD2SI */ + iw.dma2_chan = inb(iw.pnprdp) & 0x07; /* DMA2 chan (codec play) */ + + + IwavePnpDevice(EXT); /* select external device */ + outb(_PIDXR, 0x60); /* select PRAHI */ + val = ((WORD) inb(iw.pnprdp)) << 8; /* get PCDRAR[9:8] */ + outb(_PIDXR, 0x61); /* select PRALI */ + iw.pcdrar = val + (WORD) inb(iw.pnprdp); /* get PCDRAR[7:4] */ + outb(_PIDXR, 0x62); /* select PATAHI */ + val = ((WORD) inb(iw.pnprdp)) << 8; /* get PATAAR[9:8] */ + outb(_PIDXR, 0x63); /* select PATALI */ + iw.pataar = val + (WORD) inb(iw.pnprdp); /* get PATAAR[7:1] */ + + outb(_PIDXR, 0x70); /* select PRISI */ + iw.ext_irq = (WORD) (inb(iw.pnprdp) & 0x0F); /* Ext Dev IRQ number */ + + outb(_PIDXR, 0x74); /* select PRDSI */ + iw.ext_chan = inb(iw.pnprdp) & 0x07; /* Ext Dev DMA channel */ + + IwavePnpDevice(MPU401); /* Select MPU401 Device */ + outb(_PIDXR, 0x60); /* select P401HI */ + val = ((WORD) inb(iw.pnprdp)) << 8; /* get P401AR[9:8] */ + outb(_PIDXR, 0x61); /* select P401LI */ + iw.p401ar = val + (WORD) inb(iw.pnprdp); /* get P401AR[7:1] */ + + outb(_PIDXR, 0x70); /* select PMISI */ + iw.mpu_irq = (WORD) (inb(iw.pnprdp) & 0x0F); /* MPU401 Dev IRQ number */ + + IwavePnpDevice(GAME); /* Select GAME logical Device */ + outb(_PIDXR, 0x60); /* select P201HI */ + val = ((WORD) inb(iw.pnprdp)) << 8; /* get P201AR[9:8] */ + outb(_PIDXR, 0x61); /* select P201LI */ + iw.p201ar = val + (WORD) inb(iw.pnprdp); /* get P201AR[7:6] */ + + IwavePnpDevice(EMULATION); /* Select SB and ADLIB Device */ + outb(_PIDXR, 0x60); /* select P388HI */ + val = ((WORD) inb(iw.pnprdp)) << 8; /* get P388AR[9:8] */ + outb(_PIDXR, 0x61); /* select P388LI */ + iw.p388ar = val + inb(iw.pnprdp); /* get P388AR[7:6] */ + outb(_PIDXR, 0x70); /* select PSBISI */ + iw.emul_irq = (WORD) (inb(iw.pnprdp) & 0x0F); /* emulation Dev IRQ + * number */ + LEAVE_CRITICAL; +} + +static void +IwavePnpSetCfg(void) +{ + ENTER_CRITICAL; + IwavePnpDevice(AUDIO); /* select audio device */ + outb(_PIDXR, 0x60); /* select P2X0HI */ + outb(_PNPWRP, (BYTE) (iw.p2xr >> 8)); /* set P2XR[9:8] */ + outb(_PIDXR, 0x61); /* select P2X0LI */ + outb(_PNPWRP, (BYTE) iw.p2xr); /* set P2XR[7:4] */ + /* P2XR[3:0]=0 */ + outb(_PIDXR, 0x62); /* select P3X0HI */ + outb(_PNPWRP, (BYTE) (iw.p3xr >> 8)); /* set P3XR[9:8] */ + outb(_PIDXR, 0x63); /* select P3X0LI */ + outb(_PNPWRP, (BYTE) (iw.p3xr)); /* set P3XR[7:3] */ + /* P3XR[2:0]=0 */ + outb(_PIDXR, 0x64); /* select PHCAI */ + outb(_PNPWRP, (BYTE) (iw.pcodar >> 8)); /* set PCODAR[9:8] */ + outb(_PIDXR, 0x65); /* select PLCAI */ + outb(_PNPWRP, (BYTE) iw.pcodar); /* set PCODAR[7:2] */ + + outb(_PIDXR, 0x70); /* select PUI1SI */ + outb(_PNPWRP, (BYTE) (iw.synth_irq & 0x0F)); /* Synth IRQ number */ + outb(_PIDXR, 0x72); /* select PUI2SI */ + outb(_PNPWRP, (BYTE) (iw.midi_irq & 0x0F)); /* MIDI IRQ number */ + + outb(_PIDXR, 0x74); /* select PUD1SI */ + outb(_PNPWRP, (BYTE) (iw.dma1_chan & 0x07)); /* DMA channel 1 */ + outb(_PIDXR, 0x75); /* select PUD2SI */ + outb(_PNPWRP, (BYTE) (iw.dma2_chan & 0x07)); /* DMA channel 2 */ + + IwavePnpDevice(EXT); + outb(_PIDXR, 0x60); /* select PRAHI */ + outb(_PNPWRP, (BYTE) (iw.pcdrar >> 8)); /* set PCDRAR[9:8] */ + outb(_PIDXR, 0x61); /* select PRALI */ + outb(_PNPWRP, (BYTE) iw.pcdrar); /* set PCDRAR[7:3] */ + /* PCDRAR[2:0]=0 */ + outb(_PIDXR, 0x62); /* select PATAHI */ + outb(_PNPWRP, (BYTE) (iw.pataar >> 8)); /* set PATAAR[9:8] */ + outb(_PIDXR, 0x63); /* select PATALI */ + outb(_PNPWRP, (BYTE) iw.pataar); /* set PATAAR[7:1] */ + /* PATAAR[0]=0 */ + outb(_PIDXR, 0x70); /* select PRISI */ + outb(_PNPWRP, (BYTE) (iw.ext_irq & 0x0F)); /* Ext Dev IRQ number */ + outb(_PIDXR, 0x74); /* select PRDSI */ + outb(_PNPWRP, (BYTE) (iw.ext_chan & 0x07)); /* Ext Dev DMA channel */ + + IwavePnpDevice(GAME); + outb(_PIDXR, 0x60); /* select P201HI */ + outb(_PNPWRP, (BYTE) (iw.p201ar >> 8)); /* set P201RAR[9:8] */ + outb(_PIDXR, 0x61); /* select P201LI */ + outb(_PNPWRP, (BYTE) iw.p201ar); /* set P201AR[7:6] */ + + IwavePnpDevice(EMULATION); + outb(_PIDXR, 0x60); /* select P388HI */ + outb(_PNPWRP, (BYTE) (iw.p388ar >> 8)); /* set P388AR[9:8] */ + outb(_PIDXR, 0x61); /* select P388LI */ + outb(_PNPWRP, (BYTE) iw.p388ar); /* set P388AR[7:6] */ + + outb(_PIDXR, 0x70); /* select PSBISI */ + outb(_PNPWRP, (BYTE) (iw.emul_irq & 0x0F)); /* emulation IRQ number */ + + IwavePnpDevice(MPU401); + outb(_PIDXR, 0x60); /* select P401HI */ + outb(_PNPWRP, (BYTE) (iw.p401ar >> 8)); /* set P401AR[9:8] */ + outb(_PIDXR, 0x61); /* select P401LI */ + outb(_PNPWRP, (BYTE) iw.p401ar); /* set P401AR[7:1] */ + + outb(_PIDXR, 0x70); /* select PMISI */ + outb(_PNPWRP, (BYTE) (iw.mpu_irq & 0x0F)); /* MPU emulation IRQ + * number */ + LEAVE_CRITICAL; +} + +static void +IwaveCfgIOSpace(void) +{ + ENTER_CRITICAL; + IwavePnpDevice(AUDIO); /* select audio device */ + outb(_PIDXR, 0x60); /* select P2X0HI */ + outb(_PNPWRP, (BYTE) (iw.p2xr >> 8)); /* set P2XR[9:8] */ + outb(_PIDXR, 0x61); /* select P2X0LI */ + outb(_PNPWRP, (BYTE) iw.p2xr); /* set P2XR[7:4] */ + /* P2XR[3:0]=0 */ + outb(_PIDXR, 0x62); /* select P3X0HI */ + outb(_PNPWRP, (BYTE) (iw.p3xr >> 8)); /* set P3XR[9:8] */ + outb(_PIDXR, 0x63); /* select P3X0LI */ + outb(_PNPWRP, (BYTE) (iw.p3xr)); /* set P3XR[7:3] */ + /* P3XR[2:0]=0 */ + outb(_PIDXR, 0x64); /* select PHCAI */ + outb(_PNPWRP, (BYTE) (iw.pcodar >> 8)); /* set PCODAR[9:8] */ + outb(_PIDXR, 0x65); /* select PLCAI */ + outb(_PNPWRP, (BYTE) iw.pcodar); /* set PCODAR[7:2] */ + + IwavePnpDevice(EXT); + outb(_PIDXR, 0x60); /* select PRAHI */ + outb(_PNPWRP, (BYTE) (iw.pcdrar >> 8)); /* set PCDRAR[9:8] */ + outb(_PIDXR, 0x61); /* select PRALI */ + outb(_PNPWRP, (BYTE) iw.pcdrar); /* set PCDRAR[7:3] */ + /* PCDRAR[2:0]=0 */ + outb(_PIDXR, 0x62); /* select PATAHI */ + outb(_PNPWRP, (BYTE) (iw.pataar >> 8)); /* set PATAAR[9:8] */ + outb(_PIDXR, 0x63); /* select PATALI */ + outb(_PNPWRP, (BYTE) iw.pataar); /* set PATAAR[7:1] */ + /* PATAAR[0]=0 */ + IwavePnpDevice(GAME); + outb(_PIDXR, 0x60); /* select P201HI */ + outb(_PNPWRP, (BYTE) (iw.p201ar >> 8)); /* set P201RAR[9:8] */ + outb(_PIDXR, 0x61); /* select P201LI */ + outb(_PNPWRP, (BYTE) iw.p201ar); /* set P201AR[7:6] */ + + IwavePnpDevice(EMULATION); + outb(_PIDXR, 0x60); /* select P388HI */ + outb(_PNPWRP, (BYTE) (iw.p388ar >> 8)); /* set P388AR[9:8] */ + outb(_PIDXR, 0x61); /* select P388LI */ + outb(_PNPWRP, (BYTE) iw.p388ar); /* set P388AR[7:6] */ + + IwavePnpDevice(MPU401); + outb(_PIDXR, 0x60); /* select P401HI */ + outb(_PNPWRP, (BYTE) (iw.p401ar >> 8)); /* set P401AR[9:8] */ + outb(_PIDXR, 0x61); /* select P401LI */ + outb(_PNPWRP, (BYTE) iw.p401ar); /* set P401AR[7:1] */ + LEAVE_CRITICAL; +} + + +/* ######################################################################## */ +/* FILE: iwpnp.c */ +/* */ +/* REMARKS: This file contains the definitions for the InterWave's DDK */ +/* functions dedicated to the configuration of the InterWave */ +/* PNP logic. */ +/* */ +/* UPDATE: 4/07/95 */ +/* ######################################################################## */ +/* */ +/* FUNCTION: IwavePnpKey */ +/* */ +/* PROFILE: This function issues the initiation key that places the PNP */ +/* logic into configuration mode. The PNP logic is quiescent at */ +/* power up and must be enabled by software. This function will */ +/* do 32 I/O writes to the PIDXR (0x0279). The function will */ +/* first reset the LFSR to its initial value by a sequence of two */ +/* write cycles of 0x00 to PIDXR before issuing the key. */ +/* */ +/* ######################################################################## */ +static void +IwavePnpKey(void) +{ + /* send_Initiation_LFSR(); */ + + BYTE code = 0x6A; + BYTE msb; + BYTE i; + + /* ############################################### */ + /* Reset Linear Feedback Shift Reg. */ + /* ############################################### */ + outb(0x279, 0x00); + outb(0x279, 0x00); + + outb(0x279, code); /* Initial value */ + + for (i = 1; i < 32; i++) { + msb = ((code & 0x01) ^ ((code & 0x02) >> 1)) << 7; + code = (code >> 1) | msb; + outb(0x279, code); + } + +} + +static BYTE +IwavePnpIsol(PORT * pnpread) +{ + int num_pnp_devs; + int rd_port = 0; + printf("Checking for GUS Plug-n-Play ...\n"); + + /* Try various READ_DATA ports from 0x203-0x3ff */ + for (rd_port = 0x80; (rd_port < 0xff); rd_port += 0x10) { + if (0) + printf("Trying Read_Port at %x\n", + (rd_port << 2) | 0x3); + + num_pnp_devs = isolation_protocol(rd_port); + if (num_pnp_devs) { + *pnpread = rd_port << 2 | 0x3; + break; + } + } + if (!num_pnp_devs) { + printf("No Plug-n-Play devices were found\n"); + return 0; + } + return 1; +} + +/* ######################################################################## */ +/* */ +/* FUNCTION: IwavePnpSerial */ +/* */ +/* PROFILE: This function reads the first nine bytes of the data from */ +/* the serial EEPROM and returns the Vendor ID and the serial */ +/* number. First, it resets the EEPROM control logic by */ +/* issuing a WAKE[CSN] command. The function will return an */ +/* ASCII string for the vendor ID into the char array pointed */ +/* to by "vendor" in the VVVNNNN format. The serial number */ +/* is placed in the 32-bit variable pointed to by "serial". */ +/* Note that the 9th byte is read but not used as it is invalid */ +/* when the serial identifier is read via PRESDI. */ +/* */ +/* This function assumes that the PNP state machine is not in */ +/* the "wait for key state". Otherwise, unpredictable results */ +/* will be obtained. */ +/* */ +/* ######################################################################## */ +static void +IwavePnpSerial(PORT pnprdp, + BYTE csn, + BYTE * vendor, + DWORD * serial) +{ + BYTE presdi, digit, i; + + *serial = 0L; + + /* ####################################### */ + /* Reset Serial EEPROM logic */ + /* ####################################### */ + IwavePnpWake(csn); /* Wake card up */ + + for (i = 1; i <= 4; i++) { + IwavePnpPeek(pnprdp, 1, &presdi); + + switch (i) { + case 1: + *(vendor++) = ((presdi & 0x7C) >> 2) | 0x40; /* 1st char */ + *vendor = (presdi & 0x03) << 3; /* isolate bits[4:3] of + * 2nd char */ + break; + case 2: + *vendor = ((presdi & 0xE0) >> 5) | (*vendor); + *(vendor++) = (*vendor) | 0x40; /* 2nd char */ + *vendor = (presdi & 0x1F) | 0x40; /* 3rd char */ + break; + case 3: + case 4: + digit = (presdi & 0xF0) >> 4; + if (digit <= 0x09) + *(++vendor) = digit + 0x30; /* ASCII of digit */ + else + *(++vendor) = (digit & 0x07) + 0x3F; + digit = presdi & 0x0F; + if (digit <= 0x09) + *(++vendor) = digit + 0x30; + else + *(++vendor) = (digit & 0x07) + 0x3F; + break; + } + } + *(++vendor) = '\0'; + IwavePnpPeek(pnprdp, 4, (BYTE *) serial); + IwavePnpPeek(pnprdp, 1, NULL); /* discard checksum */ +} +/* ######################################################################## */ +/* */ +/* FUNCTION: IwavePnpPeek */ +/* */ +/* PROFILE: This function will return the number of specified bytes of */ +/* resource data from the serial EEPROM. The function will NOT */ +/* reset the serial EEPROM logic to allow reading the entire */ +/* EEPROM by issuing repeated calls. The caller must supply a */ +/* pointer to where the data are to be stored. */ +/* It is assumed that the InterWave is not in either "sleep" */ +/* or "wait for key" states. Note that on the first call, if */ +/* the caller means to read from the beggining of data the */ +/* serial EEPROM logic must be reset. For this, the caller */ +/* should issue a WAKE[CSN] command */ +/* */ +/* ######################################################################## */ +static void +IwavePnpPeek(PORT pnprdp, WORD bytes, BYTE * data) +{ + WORD i; + BYTE datum; + + for (i = 1; i <= bytes; i++) { + outb(_PIDXR, 0x05); /* select PRESSI */ + + while (TRUE) { /* wait til new data byte is ready */ + if (inb(pnprdp) & PNP_DATA_RDY) + break; /* new resource byte ready */ + } + outb(_PIDXR, 0x04); /* select PRESDI */ + datum = inb(pnprdp); /* read resource byte */ + if (data != NULL) + *(data++) = datum; /* store it */ + } +} +/* ######################################################################## */ +/* */ +/* FUNCTION: IwavePnpEeprom */ +/* */ +/* PROFILE: This function allows the caller to control the serial */ +/* EEPROM directly while the audio device is inactive. To */ +/* de-activate the audio device issue the call */ +/* IwavePnpActivate(AUDIO,OFF). */ +/* */ +/* ######################################################################## */ +static void +IwavePnpEeprom(BYTE ctrl) +{ + ENTER_CRITICAL; + outb(_PIDXR, 0xF1); /* select PSECI */ + outb(_PNPWRP, ctrl); /* write PSECI */ + LEAVE_CRITICAL; +} +/* ######################################################################## */ +/* */ +/* FUNCTION: IwavePnpActivate */ +/* */ +/* PROFILE: This function will activate or de-activate the audio device */ +/* or the external device on the InterWave. Set the "dev" arg */ +/* to AUDIO for the audio device or EXT for the external device. */ +/* Set "bool" to ON or OFF to turn the device on or off the ISA */ +/* bus. Notice that for a logical device to work, it must be */ +/* activated. */ +/* */ +/* ######################################################################## */ +static void +IwavePnpActivate(BYTE dev, BYTE bool) +{ + IwavePnpDevice(dev); /* select audio device */ + ENTER_CRITICAL; + outb(_PIDXR, ACTIVATE_DEV); /* select Activate Register */ + outb(_PNPWRP, bool); /* write register */ + LEAVE_CRITICAL; + +} +/* ######################################################################## */ +/* */ +/* FUNCTION: IwavePnpDevice */ +/* */ +/* PROFILE: This function allows the caller to select between five */ +/* logical devices available on the InterWave.It is assumed */ +/* that the PNP state machine is in configuration mode. */ +/* */ +/* ######################################################################## */ +static void +IwavePnpDevice(BYTE dev) +{ + ENTER_CRITICAL; + outb(_PIDXR, _PLDNI); /* select PLDNI */ + outb(_PNPWRP, dev); /* write PLDNI */ + LEAVE_CRITICAL; +} +/* ######################################################################## */ +/* */ +/* FUNCTION: IwavePnpPower */ +/* */ +/* PROFILE: This function allows the caller to disable major sections of */ +/* the InterWave to prevent them from consuming power and */ +/* loading the ISA bus. */ +/* */ +/* It is assumed that the PNP state machine is in configuration */ +/* mode. */ +/* */ +/* ######################################################################## */ +static void +IwavePnpPower(BYTE mode) +{ + ENTER_CRITICAL; + outb(_PIDXR, _PPWRI); /* select PPWRI */ + outb(_PNPWRP, mode); /* write PPWRI */ + LEAVE_CRITICAL; +} +/* ######################################################################## */ +/* */ +/* FUNCTION: IwavePnpWake */ +/* */ +/* PROFILE: This function issues a WAKE[CSN] command to the InterWave. If */ +/* the CSN matches the PNP state machine will enter the */ +/* configuration state. Otherwise it will enter the sleep mode. */ +/* */ +/* It is assumed that the PNP state machine is not in the */ +/* "wait for key" state. */ +/* */ +/* ######################################################################## */ +static void +IwavePnpWake(BYTE csn) +{ + ENTER_CRITICAL; + outb(_PIDXR, _PWAKEI); /* select PWAKEI */ + outb(_PNPWRP, csn); /* write csn */ + LEAVE_CRITICAL; +} +/* ######################################################################## */ +/* */ +/* FUNCTION: IwavePnpIOcheck */ +/* */ +/* PROFILE: This function allows the caller to perform a conflict check */ +/* on an I/O port to be used by a logical device. The function */ +/* receives the base address of the I/O range as well as the */ +/* number of ports in the range and then performs the I/O check */ +/* protocol. It returns the address of the port if a conflict */ +/* is detected or IO_CHK if no conflict is detected. */ +/* */ +/* This function assumes that the logical device has been de- */ +/* activated and that the PNP state machine is in config mode. */ +/* */ +/* ######################################################################## */ +static PORT +IwavePnpIOcheck(PORT base, BYTE no_ports) +{ + BYTE i; + PORT portid; + + outb(_PIDXR, RANGE_IOCHK); /* select IO range check reg */ + + for (i = 0; i < no_ports; i++) { + portid = base + i; /* port to check */ + outb(_PNPWRP, 0x02); /* must drive 0xAA onto bus */ + if (inb(portid) != 0xAA) + return (portid); /* IO conflict detected */ + + outb(_PNPWRP, 0x03); /* must drive 0x55 onto bus */ + if (inb(portid) != 0x55) + return (portid); /* IO conflict detected */ + } + return (IO_OK); +} +/* ######################################################################## */ +/* */ +/* FUNCTION: IwavePnpGetCSN */ +/* */ +/* PROFILE: This function allows the caller to detect an InterWave based */ +/* adapter board and will return its asigned CSN so that an */ +/* an application can access its PnP interface and determine the */ +/* borad's current configuration. In conducting its search for */ +/* the InterWave IC, the function will use the first 32 bits of */ +/* the Serial Identifier called the vendor ID in the PnP ISA */ +/* spec. The last 4 bits in the Vendor ID represent a revision */ +/* number for the particular product and this function gives the */ +/* caller the option of taking this revision number into account */ +/* or not in the search. If the function fails to find the */ +/* InterWave IC it will return FALSE. */ +/* */ +/* ######################################################################## */ +static BYTE +IwavePnpGetCSN(DWORD VendorID, BYTE csn_max) +{ + BYTE csn; + DWORD vendor; + + IwavePnpKey(); /* Key to access PnP Interface */ + VendorID &= (0xFFFFFFF0); /* reset 4 least significant bits */ + + for (csn = 1; csn <= csn_max; csn++) { + IwavePnpWake(csn); /* Select card */ + IwavePnpPeek(iw.pnprdp, 4, (BYTE *) & vendor); /* get vendor ID */ + vendor &= (0xFFFFFFF0); + if (vendor == VendorID) { /* If IDs match, InterWave is + * found */ + outb(_PIDXR, 0x02); /* Place all cards in + * wait-for-key state */ + outb(0x0A79, 0x02); + return (csn); + } + } + outb(_PIDXR, 0x02); /* Place all cards in wait-for-key state */ + outb(0x0A79, 0x02); + return (FALSE); /* InterWave IC not found */ +} +/* ######################################################################## */ +/* */ +/* + * FUNCTION: IwavePnpPing + */ +/* */ +/* PROFILE: This function allows the caller to detect an InterWave based */ +/* adapter board and will return its asigned CSN so that an */ +/* an application can access its PnP interface and determine the */ +/* borad's current configuration. In conducting its search for */ +/* the InterWave IC, the function will use the first 32 bits of */ +/* the Serial Identifier called the vendor ID in the PnP ISA */ +/* spec. The last 4 bits in the Vendor ID represent a revision */ +/* number for the particular product and will not be included */ +/* in the search. The function will return the Vendor ID and the */ +/* calling application should check the revision bits to make */ +/* sure they are compatible with the board. */ +/* */ +/* ######################################################################## */ +static BYTE +IwavePnpPing(DWORD VendorID) +{ + BYTE csn; + + VendorID &= (0xFFFFFFF0); /* reset 4 least significant bits */ + IwavePnpKey(); /* Key to access PnP Interface */ + while (iw.pnprdp <= 0x23F) { + for (csn = 1; csn <= 10; csn++) { + IwavePnpWake(csn); /* Select card */ + IwavePnpPeek(iw.pnprdp, 4, (BYTE *) & iw.vendor); /* get vendor ID */ + + + if (((iw.vendor) & 0xFFFFFFF0) == VendorID) { /* If IDs match, + * InterWave is found */ + + outb(_PIDXR, 0x02); /* Place all cards in + * wait-for-key state */ + outb(0x0A79, 0x02); + return (csn); + } + } + iw.pnprdp += 0x04; + } + outb(_PIDXR, 0x02); /* Place all cards in wait-for-key state */ + outb(0x0A79, 0x02); + return (FALSE); /* InterWave IC not found */ +} + +/* end of pnp code */ + +static WORD +IwaveMemSize(void) +{ + BYTE datum = 0x55; + ADDRESS local = 0L; + + outb(iw.igidxr, _LMCI); + outb(iw.i8dp, inb(iw.i8dp) & 0xFD); /* DRAM I/O cycles selected */ + + while (TRUE) { + IwaveMemPoke(local, datum); + IwaveMemPoke(local + 1L, datum + 1); + if (IwaveMemPeek(local) != datum || IwaveMemPeek(local + 1L) != (datum + 1) || IwaveMemPeek(0L) != 0x55) + break; + local += RAM_STEP; + datum++; + } + return ((WORD) (local >> 10)); +} + +static BYTE +IwaveMemPeek(ADDRESS addr) +{ + PORT p3xr; + + p3xr = iw.p3xr; + + + outb(iw.igidxr, 0x43); /* Select LMALI */ + outw(iw.i16dp, (WORD) addr); /* Lower 16 bits of LM */ + outb(iw.igidxr, 0x44); /* Select LMAHI */ + outb(iw.i8dp, (BYTE) (addr >> 16)); /* Upper 8 bits of LM */ + return (inb(iw.lmbdr)); /* return byte from LMBDR */ +} + + +static void +IwaveMemPoke(ADDRESS addr, BYTE datum) +{ + PORT p3xr; + p3xr = iw.p3xr; + + + outb(iw.igidxr, 0x43); /* Select LMALI */ + outw(iw.i16dp, (WORD) addr); /* Lower 16 bits of LM */ + outb(iw.igidxr, 0x44); /* Select LMAHI */ + outb(iw.i8dp, (BYTE) (addr >> 16)); /* Upper 8 bits of LM */ + outb(iw.lmbdr, datum); /* Write byte to LMBDR */ +} + +/* ######################################################################## */ +/* */ +/* FUNCTION: IwaveMemCfg */ +/* */ +/* PROFILE : This function determines the amount of DRAM from its */ +/* configuration accross all banks. It sets the configuration */ +/* into register LMCFI and stores the total amount of DRAM */ +/* into iw.size_mem (Kbytes). */ +/* */ +/* The function first places the IC in enhanced mode to allow */ +/* full access to all DRAM locations. Then it selects full */ +/* addressing span (LMCFI[3:0]=0x0C). Finally, it determines */ +/* the amount of DRAM in each bank and from this the actual */ +/* configuration. */ +/* */ +/* Note that if a configuration other than one indicated in */ +/* the manual is implemented, this function will select */ +/* full addressing span (LMCFI[3:0]=0xC). */ +/* */ +/* ######################################################################## */ +static void +IwaveMemCfg(DWORD * lpbanks) +{ + DWORD bank[4] = {0L, 0L, 0L, 0L}; + DWORD addr = 0L, base = 0L, cnt = 0L; + BYTE i, reg, ram = FALSE; + WORD lmcfi; + /* */ + ENTER_CRITICAL; + outb(iw.igidxr, 0x99); + reg = inb(iw.i8dp); /* image of sgmi */ + outb(iw.igidxr, 0x19); + outb(iw.i8dp, (BYTE) (reg | 0x01)); /* enable enhaced mode */ + outb(iw.igidxr, _LMCFI);/* select LM Conf Reg */ + lmcfi = inw(iw.i16dp) & 0xFFF0; + outw(iw.i16dp, lmcfi | 0x000C); /* max addr span */ + /* */ + /* Clear every RAM_STEPth location */ + /* */ + while (addr < RAM_MAX) { + IwaveMemPoke(addr, 0x00); + addr += RAM_STEP; + } + /* */ + /* Determine amount of RAM in each bank */ + /* */ + for (i = 0; i < 4; i++) { + IwaveMemPoke(base, 0xAA); /* mark start of bank */ + IwaveMemPoke(base + 1L, 0x55); + if ((IwaveMemPeek(base) == 0xAA) && (IwaveMemPeek(base + 1L) == 0x55)) + ram = TRUE; + if (ram) { + while (cnt < BANK_MAX) { + bank[i] += RAM_STEP; + cnt += RAM_STEP; + addr = base + cnt; + if (IwaveMemPeek(addr) == 0xAA) + break; + } + } + if (lpbanks != NULL) { + *lpbanks = bank[i]; + lpbanks++; + } + bank[i] = bank[i] >> 10; + base += BANK_MAX; + cnt = 0L; + ram = FALSE; + } + /* */ + iw.flags &= ~DRAM_HOLES; + outb(iw.igidxr, _LMCFI); + if (bank[0] == 256 && bank[1] == 0 && bank[2] == 0 && bank[3] == 0) + outw(iw.i16dp, lmcfi); + else if (bank[0] == 256 && bank[1] == 256 && bank[2] == 0 && bank[3] == 0) + outw(iw.i16dp, lmcfi | 0x01); + else if (bank[0] == 256 && bank[1] == 256 && bank[2] == 256 && bank[3] == 256) + outw(iw.i16dp, lmcfi | 0x02); + else if (bank[0] == 256 && bank[1] == 1024 && bank[2] == 0 && bank[3] == 0) + outw(iw.i16dp, lmcfi | 0x03); + else if (bank[0] == 256 && bank[1] == 1024 && bank[2] == 1024 && bank[3] == 1024) + outw(iw.i16dp, lmcfi | 0x04); + else if (bank[0] == 256 && bank[1] == 256 && bank[2] == 1024 && bank[3] == 0) + outw(iw.i16dp, lmcfi | 0x05); + else if (bank[0] == 256 && bank[1] == 256 && bank[2] == 1024 && bank[3] == 1024) + outw(iw.i16dp, lmcfi | 0x06); + else if (bank[0] == 1024 && bank[1] == 0 && bank[2] == 0 && bank[3] == 0) + outw(iw.i16dp, lmcfi | 0x07); + else if (bank[0] == 1024 && bank[1] == 1024 && bank[2] == 0 && bank[3] == 0) + outw(iw.i16dp, lmcfi | 0x08); + else if (bank[0] == 1024 && bank[1] == 1024 && bank[2] == 1024 && bank[3] == 1024) + outw(iw.i16dp, lmcfi | 0x09); + else if (bank[0] == 4096 && bank[1] == 0 && bank[2] == 0 && bank[3] == 0) + outw(iw.i16dp, lmcfi | 0x0A); + else if (bank[0] == 4096 && bank[1] == 4096 && bank[2] == 0 && bank[3] == 0) + outw(iw.i16dp, lmcfi | 0x0B); + else /* Flag the non-contiguous config of memory */ + iw.flags |= DRAM_HOLES; + /* */ + outb(iw.igidxr, 0x19); /* restore sgmi */ + outb(iw.i8dp, reg); + LEAVE_CRITICAL; +} + + +/* ######################################################################## */ +/**/ +/* FUNCTION: IwaveCodecIrq */ +/**/ +/* PROFILE: This function disables or enables the Codec Interrupts. To */ +/* enable interrupts set CEXTI[2] high thus causing all interrupt */ +/* sources (CSR3I[6:4]) to pass onto the IRQ pin. To disable */ +/* interrupts set CEXTI[1]=0. To enable Code IRQs issue this call: */ +/**/ +/* IwaveCodecIrq(CODEC_IRQ_ENABLE). To disable IRQs issue the call */ +/**/ +/* IwaveCodeIrq(~CODEC_IRQ_ENABLE). */ +/**/ +/* ######################################################################## */ +static void +IwaveCodecIrq(BYTE mode) +{ + BYTE reg; + + ENTER_CRITICAL; + reg = inb(iw.pcodar) & 0xE0; + outb(iw.pcodar, reg | _CSR3I); /* select CSR3I */ + outb(iw.cdatap, 0x00); /* clear all interrupts */ + outb(iw.pcodar + 0x02, 0x00); /* clear CSR1R */ + outb(iw.pcodar, reg | _CEXTI); /* select CEXTI */ + reg = inb(iw.cdatap); + if (mode == CODEC_IRQ_ENABLE) /* enable Codec Irqs */ + outb(iw.cdatap, (BYTE) (reg | CODEC_IRQ_ENABLE)); + else /* disable Codec Irqs */ + outb(iw.cdatap, (BYTE) (reg & ~CODEC_IRQ_ENABLE)); + LEAVE_CRITICAL; +} + + +/* ######################################################################### */ +/**/ +/* FUNCTION: IwaveRegPeek */ +/**/ +/* PROFILE : This function returns the value stored in any readable */ +/* InterWave register. It takes as input a pointer to a */ +/* structure containing the addresses of the relocatable I/O */ +/* space as well as a register mnemonic. To correctly use this */ +/* function, the programmer must use the mnemonics defined in */ +/* "iwdefs.h". These mnemonics contain coded information used */ +/* by the function to properly access the desired register. */ +/**/ +/* An attempt to read from a write-only register will return */ +/* meaningless data. */ +/**/ +/* ######################################################################### */ +static WORD +IwaveRegPeek(DWORD reg_mnem) +{ + BYTE index, val; + WORD reg_id, offset; + + offset = (WORD) ((BYTE) reg_mnem); + reg_id = (WORD) (reg_mnem >> 16); + index = (BYTE) (reg_mnem >> 8); + + /* ################################################### */ + /* Logic to read registers in P2XR block & GMCR */ + /* ################################################### */ + + if (reg_id >= 0x0001 && reg_id <= 0x001A) { /* UMCR to GMCR */ + if (reg_id <= 0x000E) /* UMCR to USRR */ + return ((WORD) inb(iw.p2xr + offset)); + + if (reg_id == 0x0019) + return ((WORD) inb(iw.p201ar)); + + else { /* GUS Hidden registers or GMCR */ + BYTE iveri; + + outb(iw.igidxr, 0x5B); /* select IVERI */ + iveri = inb(iw.i8dp); /* read IVERI */ + outb(iw.i8dp, (BYTE) (iveri | 0x09)); /* set IVERI[3,0] */ + if (reg_id == 0x001A) { /* GMCR */ + val = inb(iw.p3xr); + outb(iw.i8dp, iveri); /* restore IVERI */ + return ((WORD) val); + } + val = inb(iw.p2xr + 0x0F); /* read URCR */ + val = (val & 0xF8) | index; /* value for URCR[2:0] */ + outb(iw.p2xr + 0x0F, val); /* set URCR[2:0] */ + + if (reg_mnem == UDCI || reg_mnem == UICI) { + val = inb(iw.p2xr); + if (reg_mnem == UDCI) + outb(iw.p2xr, (BYTE) (val & 0xBF)); + else + outb(iw.p2xr, (BYTE) (val | 0x40)); + } + val = inb(iw.p2xr + 0x0B); + outb(iw.igidxr, 0x5B); /* select IVERI */ + outb(iw.i8dp, iveri); /* restore IVERI */ + return ((WORD) val); /* read register */ + } + } + /* ################################################### */ + /* Logic to read registers in P3XR block */ + /* ################################################### */ + + if (reg_id >= 0x001B && reg_id <= 0x005C) { /* GMSR to LMBDR */ + if (reg_id == 0x005C) /* LMBDR */ + return ((WORD) inb(iw.lmbdr)); + + if (reg_id >= 0x001B && reg_id <= 0x0021) /* GMSR to I8DP */ + if (offset == 0x04) + return (inw(iw.i16dp)); + else + return ((WORD) inb(iw.p3xr + offset)); + else { /* indexed registers */ + + if (reg_id <= 0x003F) + index |= 0x80; /* adjust for reading */ + + outb(iw.igidxr, index); /* select register */ + + if (offset == 0x04) + return (inw(iw.i16dp)); + else + return ((WORD) inb(iw.i8dp)); + } + } + /* #################################################### */ + /* Logic to read registers in PCODAR block */ + /* #################################################### */ + + if (reg_id >= 0x005D && reg_id <= 0x0081) { /* CIDXR to CLRCTI */ + if (reg_id <= 0x0061) + return ((WORD) inb(iw.pcodar + offset)); /* CRDR */ + + else { /* indexed registers */ + BYTE cidxr; + + cidxr = inb(iw.pcodar); + cidxr = (cidxr & 0xE0) + index; + outb(iw.pcodar, cidxr); /* select register */ + return ((WORD) inb(iw.cdatap)); + } + } + /* ##################################################### */ + /* Logic to read the PnP registers */ + /* ##################################################### */ + if (reg_id >= 0x0082 && reg_id <= 0x00B7) { /* PCSNBR to PMITI */ + if (reg_id == 0x0085) + return ((WORD) inb(iw.pnprdp)); + + if (reg_id < 0x0085) + return ((WORD) inb((WORD) reg_mnem)); + + else { /* indexed registers */ + if (reg_id >= 0x008E && reg_id <= 0x00B7) { + outb(0x0279, 0x07); /* select PLDNI */ + outb(0xA79, (BYTE) offset); /* select logical dev */ + } + outb(0x0279, index); /* select the register */ + return ((WORD) inb(iw.pnprdp)); + } + } + return 0; +} +/* ######################################################################### */ +/**/ +/* FUNCTION: IwaveRegPoke */ +/**/ +/* PROFILE : This function writes a value to any writable */ +/* InterWave register. It takes as input a pointer to a */ +/* structure containing the addresses of the relocatable I/O */ +/* space as well as a register mnemonic. To correctly use this */ +/* function, the programmer must use the mnemonics defined in */ +/* "iwdefs.h". These mnemonics contain coded information used */ +/* by the function to properly access the desired register. */ +/**/ +/* This function does not guard against writing to read-only */ +/* registers. It is the programmer's responsibility to ensure */ +/* that the writes are to valid registers. */ +/**/ +/* ######################################################################### */ +static void +IwaveRegPoke(DWORD reg_mnem, WORD datum) +{ + BYTE index; + BYTE val; + WORD reg_id; + WORD offset; + + offset = (WORD) ((BYTE) reg_mnem); + reg_id = (WORD) (reg_mnem >> 16); + index = (BYTE) (reg_mnem >> 8); + + + /* ####################################################### */ + /* Logic to write to registers in P2XR block */ + /* ####################################################### */ + if (reg_id >= 0x0001 && reg_id <= 0x0019) { /* UMCR to GGCR */ + if (reg_id <= 0x000E) { /* UMCR to USRR */ + outb(iw.p2xr + offset, (BYTE) datum); + return; + } + if (reg_id == 0x0019) { + outb(iw.p201ar, (BYTE) datum); + return; + } else { /* GUS Hidden registers */ + + BYTE iveri; + + outb(iw.igidxr, 0x5B); /* select IVERI */ + iveri = inb(iw.i8dp); /* read IVERI */ + outb(iw.i8dp, (BYTE) (iveri | 0x09)); /* set IVERI[3,0] */ + val = inb(iw.p2xr + 0x0F); /* read URCR */ + val = (val & 0xF8) | index; /* value for URCR[2:0] */ + outb(iw.p2xr + 0x0F, val); /* set URCR[2:0] */ + + if (reg_mnem == UDCI || reg_mnem == UICI) { + val = inb(iw.p2xr); /* read UMCR */ + if (reg_mnem == UDCI) + outb(iw.p2xr, (BYTE) (val & 0xBF)); /* set UMCR[6]=0 */ + else + outb(iw.p2xr, (BYTE) (val | 0x40)); /* set UMCR[6]=1 */ + } + outb(iw.p2xr + 0x0B, (BYTE) datum); /* write register */ + outb(iw.igidxr, 0x5B); /* select IVERI */ + outb(iw.i8dp, iveri); /* restore IVERI */ + return; + } + } + /* ############################################################# */ + /* Logic to write to registers in P3XR block */ + /* ############################################################# */ + + if (reg_id >= 0x001A && reg_id <= 0x005C) { /* GMCR to LMBDR */ + + if (reg_id == 0x005C) { /* LMBDR */ + outb(iw.lmbdr, (BYTE) datum); + return; + } + if (reg_id == 0x001B) /* GMSR */ + return; + + if (reg_id >= 0x001A && reg_id <= 0x0021) /* GMCR to I8DP */ + if (offset == 0x04) + outw(iw.i16dp, datum); + else + outb(iw.p3xr + offset, (BYTE) datum); + else { /* indexed registers */ + outb(iw.igidxr, index); /* select register */ + + if (offset == 0x04) + outw(iw.i16dp, datum); + else + outb(iw.i8dp, (BYTE) datum); + } + } + /* /################################################### */ + /* Logic to write to registers in PCODAR block */ + /* ################################################### */ + + if (reg_id >= 0x005C && reg_id <= 0x0081) { /* CIDXR to CLRCTI */ + if (reg_id <= 0x0061) + outb(iw.pcodar + offset, (BYTE) datum); + + else { /* one of the indexed registers */ + BYTE cidxr; + + cidxr = inb(iw.pcodar); + cidxr = (cidxr & 0xE0) + index; + outb(iw.pcodar, cidxr); /* select register */ + outb(iw.cdatap, (BYTE) datum); + } + } + /* ###################################################### */ + /* Logic to write to the PnP registers */ + /* ###################################################### */ + if (reg_id >= 0x0082 && reg_id <= 0x00B7) { + if (reg_id == 0x0085) { + outb(iw.pnprdp, (BYTE) datum); + return; + } + if (reg_id < 0x0085) + outb((WORD) reg_mnem, (BYTE) datum); + + else { /* one of the indexed registers */ + if (reg_id >= 0x008E && reg_id <= 0x00B7) { + outb(0x0279, 0x07); /* select PLDNI */ + outb(0xA79, (BYTE) offset); /* select logical dev */ + } + outb(0x0279, index); /* select the register */ + outb(0xA79, (BYTE) datum); + } + } +} + + +static void +IwaveLineLevel(char level, char index) +{ + char reg; + + level &= 0x1F; + + ENTER_CRITICAL; + reg = inb(iw.pcodar) & 0xE0; + outb(iw.pcodar, reg | index); /* select register */ + outb(iw.cdatap, (BYTE) ((inb(iw.cdatap) & 0x80) | level)); /* set level */ + LEAVE_CRITICAL; +} + +static void +IwaveCodecMode(char mode) +{ + char reg; + + ENTER_CRITICAL; + reg = inb(iw.pcodar) & 0xE0; + outb(iw.pcodar, reg | _CMODEI); /* select CMODEI */ + outb(iw.cdatap, mode); + LEAVE_CRITICAL; + iw.cmode = mode; +} + +static void +IwaveLineMute(BYTE mute, BYTE inx) +{ + BYTE reg; + + ENTER_CRITICAL; + reg = inb(iw.pcodar) & 0xE0; + outb(iw.pcodar, reg | inx); /* select register */ + if (mute == ON) + outb(iw.cdatap, (BYTE) (inb(iw.cdatap) | 0x80)); /* mute */ + else + outb(iw.cdatap, (BYTE) (inb(iw.cdatap) & 0x7F)); /* unmute */ + LEAVE_CRITICAL; +} + +static void +Iwaveinitcodec() +{ + + u_short iwl_codec_base = iw.pcodar; + u_short iwl_codec_data = iw.pcodar + 1; + u_short foo; + + + + /* + * Set the CEXTI register foo = CODEC_CEXTI_DEFAULT; + * IWL_CODEC_OUT(EXTERNAL_CONTROL, foo); + */ + /* + * Disable Interrupts iwl_codec_disable_irqs(); + */ + + /* Set the CODEC to Operate in Mode 3 */ + IWL_CODEC_OUT(MODE_SELECT_ID, 0x6C); + foo = inb(iwl_codec_data); + + /* Set the configuration registers to their default values */ + foo = CODEC_CFIG1I_DEFAULT; + IWL_CODEC_OUT(CONFIG_1 | CODEC_MCE, foo); + outb(iwl_codec_base, CONFIG_1); + foo = CODEC_CFIG2I_DEFAULT; + IWL_CODEC_OUT(CONFIG_2, foo); + + foo = CODEC_CFIG3I_DEFAULT; + IWL_CODEC_OUT(CONFIG_3, foo); + +} + + + +int +IwaveOpen(char voices, char mode, struct address_info * hw) +{ + + u_long flags; + u_char tmp; + + flags = splhigh(); + + iw.pnprdp = 0; + if (IwavePnpIsol(&iw.pnprdp)) { + + iw.vendor = GUS_PNP_ID; + + iw.csn = IwavePnpPing(iw.vendor); + + IwavePnpKey(); + + IwavePnpWake(iw.csn); + + IwavePnpGetCfg(); + IwavePnpKey(); + + IwavePnpWake(iw.csn); + } + if (hw->irq > 0) { + /* I see the user wants to set the GUS PnP */ + /* Okay lets do it */ + iw.csn = 1; + iw.p2xr = hw->io_base; + iw.p3xr = hw->io_base + 0x100; + iw.pcodar = hw->io_base + 0x10c; + + iw.synth_irq = hw->irq; + + iw.midi_irq = hw->irq; + + iw.dma1_chan = hw->dma; + + if (hw->dma2 == -1) { + iw.dma2_chan = hw->dma; + } else { + iw.dma2_chan = hw->dma2; + } + + + } else { + + /* tell the os what we are doing 8) */ + hw->io_base = iw.p2xr; + hw->irq = iw.synth_irq; + /* + * iw.dma1_chan = 1; iw.dma2_chan = 3 ; + */ + hw->dma = iw.dma1_chan; + hw->dma2 = iw.dma2_chan; + + + } + + if (iw.csn > 0 && iw.csn < MAX_GUS_PNP) { + gus_pnp_found[iw.csn] = hw->io_base; + + } + iw.cdatap = iw.pcodar + 1; + iw.csr1r = iw.pcodar + 2; + iw.cxdr = iw.pcodar + 3;/* CPDR or CRDR */ + iw.gmxr = iw.p3xr; + iw.gmxdr = iw.p3xr + 1; /* GMTDR or GMRDR */ + iw.svsr = iw.p3xr + 2; + iw.igidxr = iw.p3xr + 3; + iw.i16dp = iw.p3xr + 4; + iw.i8dp = iw.p3xr + 5; + iw.lmbdr = iw.p3xr + 7; + iw.voices = voices; + + if (iw.pnprdp > 0 && iw.csn > 0) { + IwavePnpSetCfg(); + IwavePnpActivate(AUDIO, ON); + IwavePnpActivate(EXT, ON); + } + /* IwavePnpActivate(EMULATION,ON); */ + + + /* reset */ + outb(iw.igidxr, _URSTI);/* Pull reset */ + outb(iw.i8dp, 0x00); + DELAY(1000 * 30); + + outb(iw.i8dp, 0x01); /* Release reset */ + DELAY(1000 * 30); + + /* end of reset */ + + + IwaveMemCfg(NULL); + + + tmp = IwaveRegPeek(IDECI); + + IwaveRegPoke(IDECI, tmp | 0x18); + + IwaveCodecMode(CODEC_MODE2); /* Default codec mode */ + IwaveRegPoke(ICMPTI, 0); + + outb(iw.igidxr, 0x99); + tmp = inb(iw.i8dp); + outb(iw.igidxr, 0x19); + outb(iw.i8dp, tmp); + + + + IwaveCodecIrq(~CODEC_IRQ_ENABLE); + + Iwaveinitcodec(); + + outb(iw.p2xr, 0x0c); /* Disable line in, mic and line out */ + + IwaveRegPoke(CLCI, 0x3f << 2); + + IwaveLineLevel(0, _CLOAI); + IwaveLineLevel(0, _CROAI); + + IwaveLineMute(OFF, _CLOAI); + IwaveLineMute(OFF, _CROAI); + + IwaveLineLevel(0, _CLLICI); + IwaveLineLevel(0, _CRLICI); + IwaveLineMute(OFF, _CLLICI); + IwaveLineMute(OFF, _CRLICI); + + IwaveLineLevel(0, _CLDACI); + IwaveLineLevel(0, _CRDACI); + IwaveLineMute(ON, _CLDACI); + IwaveLineMute(ON, _CRDACI); + + IwaveLineLevel(0, _CLLICI); + IwaveLineLevel(0, _CRLICI); + IwaveLineMute(ON, _CLLICI); + IwaveLineMute(ON, _CRLICI); + + + IwaveInputSource(LEFT_SOURCE, MIC_IN); + IwaveInputSource(RIGHT_SOURCE, MIC_IN); + + outb(iw.pcodar, 0x9 | 0x40); + outb(iw.cdatap, 0); + IwaveCodecIrq(CODEC_IRQ_ENABLE); + outb(iw.pcodar, _CFIG3I | 0x20); + + + outb(iw.cdatap, 0xC2); /* Enable Mode 3 IRQs & Synth */ + + outb(iw.igidxr, _URSTI); + outb(iw.i8dp, GF1_SET | GF1_OUT_ENABLE | GF1_IRQ_ENABLE); + DELAY(1000 * 30); + iw.size_mem = IwaveMemSize(); /* Bytes of RAM in this mode */ + outb(iw.p2xr, 0xc); /* enable output */ + IwaveRegPoke(CLCI, 0x3f << 2); + + IwaveCodecIrq(CODEC_IRQ_ENABLE); + splx(flags); + + DELAY(1000 * 100); + IwaveRegPoke(CPDFI, 0); + + return (TRUE); +} + + +void +gus_wave_init(struct address_info * hw_config) +{ + u_long flags; + u_char val, gus_pnp_seen = 0; + char *model_num = "2.4"; + int gus_type = 0x24; /* 2.4 */ + int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2; + int otherside = -1, i; + + if (irq < 0 || irq > 15) { + printf("ERROR! Invalid IRQ#%d. GUS Disabled", irq); + return; + } + if (dma < 0 || dma > 7) { + printf("ERROR! Invalid DMA#%d. GUS Disabled", dma); + return; + } + for (i = 0; i < MAX_GUS_PNP; i++) { + if (gus_pnp_found[i] != 0 && gus_pnp_found[i] == hw_config->io_base) + gus_pnp_seen = 1; + } +#ifdef NOGUSPNP + gus_pnp_seen = 0; +#endif + + gus_irq = irq; + gus_dma = dma; + gus_dma2 = dma2; + + 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. + */ + + flags = splhigh(); + outb(gus_base + 0x0f, 0x20); + val = inb(gus_base + 0x0f); + splx(flags); + + if (val != 0xff && (val & 0x06)) { /* Should be 0x02?? */ + /* + * It has the digital ASIC so the card is at least v3.4. Next + * try to detect the true model. + */ + + 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 (gus_pnp_seen) + val = 66; + + 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; + } else { + if (gus_pnp_seen) + model_num = "PNP"; + else + model_num = "MAX"; + + gus_type = 0x40; + mixer_type = CS4231; +#ifdef CONFIG_GUSMAX + { + u_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(gus_base + 0x106, max_config); /* UltraMax control */ + } + + if (ad1848_detect(gus_base + 0x10c, NULL, hw_config->osp)) { + + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + have_gus_max = 1; + if (gus_pnp_seen) { + + ad1848_init("GUS PNP", gus_base + 0x10c, + -irq, + gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1, /* Share DMA channels with GF1 */ + hw_config->osp); + + + } else { + ad1848_init("GUS MAX", gus_base + 0x10c, + -irq, + gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1, /* Share DMA channels with GF1 */ + hw_config->osp); + } + otherside = num_audiodevs - 1; + + } else + printf("[Where's the CS4231?]"); +#else + printf("\n\n\nGUS MAX support was not compiled in!!!\n\n\n\n"); +#endif + } + } 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 (gus_pnp_seen) { + snprintf(gus_info.name, sizeof(gus_info.name), + "Gravis %s (%dk)", model_num, (int) gus_mem_size / 1024); + } else { + snprintf(gus_info.name, sizeof(gus_info.name), + "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); + } + conf_printf(gus_info.name, hw_config); + + if (num_synths >= MAX_SYNTH_DEV) + printf("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 + } + samples = (struct patch_info *) malloc((MAX_SAMPLE + 1) * sizeof(*samples), M_DEVBUF, M_NOWAIT); + if (!samples) + panic("SOUND: Cannot allocate memory\n"); + + reset_sample_memory(); + + gus_initialize(); + + if (num_audiodevs < MAX_AUDIO_DEV) { + audio_devs[gus_devnum = num_audiodevs++] = &gus_sampling_operations; + audio_devs[gus_devnum]->otherside = otherside; + audio_devs[gus_devnum]->dmachan1 = dma; + audio_devs[gus_devnum]->dmachan2 = dma2; + audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE; + if (otherside != -1) { + /* + * glue logic to prevent people from opening the gus + * max via the gf1 and the cs4231 side . Only the gf1 + * or the cs4231 are allowed to be open + */ + + audio_devs[otherside]->otherside = gus_devnum; + } + if (dma2 != dma && dma2 != -1) + audio_devs[gus_devnum]->flags |= DMA_DUPLEX; + } else + printf("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; + ics2101_mixer_init(); + return; + + case CS4231: + /* Initialized elsewhere (ad1848.c) */ + default: + gus_default_mixer_init(); + return; + } +} + +static void +do_loop_irq(int voice) +{ + u_char tmp; + int mode, parm; + u_long flags; + + flags = splhigh(); + gus_select_voice(voice); + + tmp = gus_read8(0x00); + tmp &= ~0x20; /* Disable wave IRQ for this_one voice */ + gus_write8(0x00, tmp); + + 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; + + switch (mode) { + + case LMODE_FINISH: /* Final loop finished, shoot volume down */ + + 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_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:; + } + splx(flags); +} + +static void +do_volume_irq(int voice) +{ + u_char tmp; + int mode, parm; + u_long flags; + + flags = splhigh(); + + 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 */ + splx(flags); + gus_voice_init(voice); + break; + + case VMODE_ENVELOPE: + gus_rampoff(); + splx(flags); + step_envelope(voice); + break; + + case VMODE_START_NOTE: + splx(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:; + } +} + +void +gus_voice_irq(void) +{ + u_long wave_ignore = 0, volume_ignore = 0; + u_long voice_bit; + + u_char src, voice; + + while (1) { + src = gus_read8(0x0f); /* Get source info */ + voice = src & 0x1f; + src &= 0xc0; + + 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) +{ + u_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.mode & WK_SLEEP)) { + dram_sleep_flag.mode = WK_WAKEUP; + wakeup(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; + DMAbuf_outputintr(gus_devnum, flag); + } + break; + + default:; + } + + status = gus_look8(0x49); /* Get Sampling IRQ Status */ + if (status & 0x40) { /* Sampling Irq pending */ + DMAbuf_inputintr(gus_devnum); + } +} + +#ifdef CONFIG_SEQUENCER +/* + * Timer stuff + */ + +static volatile int select_addr, data_addr; +static volatile int curr_timer = 0; + +void +gus_timer_command(u_int addr, u_int val) +{ + int i; + + outb(select_addr, (u_char) (addr & 0xff)); + + for (i = 0; i < 2; i++) + inb(select_addr); + + outb(data_addr, (u_char) (val & 0xff)); + + for (i = 0; i < 2; i++) + inb(select_addr); +} + +static void +arm_timer(int timer, u_int interval) +{ + 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 */ + } + + gus_timer_enabled = 0; +} + +static u_int +gus_tmr_start(int dev, u_int usecs_per_tick) +{ + 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); + + return divisor * resolution; +} + +static void +gus_tmr_disable(int dev) +{ + gus_write8(0x45, 0); /* Disable both timers */ + gus_timer_enabled = 0; +} + +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 */ +} + +static struct sound_lowlev_timer gus_tmr = +{ + 0, + gus_tmr_start, + gus_tmr_disable, + gus_tmr_restart +}; + +static void +gus_tmr_install(int io_base) +{ + select_addr = io_base; + data_addr = io_base + 1; + + sound_timer_init(&gus_tmr, "GUS"); +} +#endif +#endif diff --git a/sys/i386/isa/sound/hex2hex.h b/sys/i386/isa/sound/hex2hex.h new file mode 100644 index 0000000..5c90917 --- /dev/null +++ b/sys/i386/isa/sound/hex2hex.h @@ -0,0 +1,97 @@ +/* + * This file is a part of configure.c + * + * hex2hex reads an input file in Intel HEX format and produces + * an (unsigned char) array which contains the bytes and writes it to the + * output file using C syntax + */ + +#define MAX_SIZE (256*1024) +#define ABANDON(why) { \ + fprintf(stderr, "%s: " why "\n", source); \ + fclose(inf);fclose(outf);return 0; \ + } + +int hex2hex(char *source, char *target, char *varline) +{ + FILE *inf, *outf; + + int i,l, c; + 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=0; + + while ((c=getc(inf))!=EOF) + { + if (c == ':') /* Sync with beginning of line */ + { + int n, check; + unsigned char sum; + int addr; + int linetype; + + if (fscanf(inf, "%02x", &n) != 1) + ABANDON("File format error"); + sum = n; + + if (fscanf(inf, "%04x", &addr) != 1) + ABANDON("File format error"); + sum += addr/256; + sum += addr%256; + + if (fscanf(inf, "%02x", &linetype) != 1) + ABANDON("File format error"); + sum += linetype; + + if (linetype != 0) + continue; + + for (i=0;i<n;i++) + { + if (fscanf(inf, "%02x", &c) != 1) + ABANDON("File format error"); + if (addr >= MAX_SIZE) + ABANDON("File too large"); + buf[addr++] = c; + if (addr > l) + l = addr; + sum += c; + } + + if (fscanf(inf, "%02x", &check) != 1) + ABANDON("File format error"); + + sum = ~sum + 1; + if (check != sum) + ABANDON("Line checksum error"); + } + } + + fprintf(outf, "/*\n *\t Computer generated file. Do not edit.\n */\n"); + fprintf(outf, "static unsigned char %s[] = {\n", varline); + + for (i=0;i<l;i++) + { + if (i) fprintf(outf, ","); + if (i && !(i % 16)) fprintf(outf, "\n"); + fprintf(outf, "0x%02x", buf[i]); + } + + fprintf(outf, "\n};\n\n"); + fclose(inf); + fclose(outf); + return 1; +} diff --git a/sys/i386/isa/sound/ics2101.c b/sys/i386/isa/sound/ics2101.c new file mode 100644 index 0000000..4df8bde --- /dev/null +++ b/sys/i386/isa/sound/ics2101.c @@ -0,0 +1,233 @@ +/* + * sound/ics2101.c + * + * Driver for the ICS2101 mixer of GUS v3.7. + * + * Copyright by Hannu Savolainen 1994 + * + * 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_GUS) + +#include <i386/isa/sound/ultrasound.h> +#include <i386/isa/sound/gus_hw.h> + +#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \ + SOUND_MASK_SYNTH| \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +extern sound_os_info *gus_osp; +extern int gus_base; +static int volumes[ICS_MIXDEVS]; +static int left_fix[ICS_MIXDEVS] = {1, 1, 1, 2, 1, 2}; +static int right_fix[ICS_MIXDEVS] = {2, 2, 2, 1, 2, 1}; + +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; + + RANGE(vol, 0, 100); + vol = (31 * vol + 50) / 100; + e = 0; + if (vol) { + while (vol < 16) { + vol <<= 1; + e--; + } + vol -= 16; + e += 7; + } + return ((e << 4) + 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; + } + + flags = splhigh(); + outb(u_MixSelect, ctrl_addr); + outb(u_MixData, selector[dev]); + outb(u_MixSelect, attn_addr); + outb(u_MixData, (unsigned char) vol); + splx(flags); +} + +static int +set_volumes(int dev, int vol) +{ + int left = vol & 0x00ff; + int right = (vol >> 8) & 0x00ff; + + RANGE (left, 0, 100); + RANGE (right, 0, 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, ioctl_arg arg) +{ + if (((cmd >> 8) & 0xff) == 'M') { + if (cmd & IOC_IN) + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl(dev, cmd, arg); + break; + + case SOUND_MIXER_MIC: + return *(int *) arg = set_volumes(DEV_MIC, (*(int *) arg)); + break; + + case SOUND_MIXER_CD: + return *(int *) arg = set_volumes(DEV_CD, (*(int *) arg)); + break; + + case SOUND_MIXER_LINE: + return *(int *) arg = set_volumes(DEV_LINE, (*(int *) arg)); + break; + + case SOUND_MIXER_SYNTH: + return *(int *) arg = set_volumes(DEV_GF1, (*(int *) arg)); + break; + + case SOUND_MIXER_VOLUME: + return *(int *) arg = set_volumes(DEV_VOL, (*(int *) arg)); + break; + + default: + return -(EINVAL); + } + else + switch (cmd & 0xff) { /* Return parameters */ + + case SOUND_MIXER_RECSRC: + return gus_default_mixer_ioctl(dev, cmd, arg); + break; + + case SOUND_MIXER_DEVMASK: + return *(int *) arg = MIX_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + return *(int *) arg = SOUND_MASK_LINE | SOUND_MASK_CD | + SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; + break; + + case SOUND_MIXER_RECMASK: + return *(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE; + break; + + case SOUND_MIXER_CAPS: + return *(int *) arg = 0; + break; + + case SOUND_MIXER_MIC: + return *(int *) arg = volumes[DEV_MIC]; + break; + + case SOUND_MIXER_LINE: + return *(int *) arg = volumes[DEV_LINE]; + break; + + case SOUND_MIXER_CD: + return *(int *) arg = volumes[DEV_CD]; + break; + + case SOUND_MIXER_VOLUME: + return *(int *) arg = volumes[DEV_VOL]; + break; + + case SOUND_MIXER_SYNTH: + return *(int *) arg = volumes[DEV_GF1]; + break; + + default: + return -(EINVAL); + } + } + return -(EINVAL); +} + +static struct mixer_operations ics2101_mixer_operations = +{ + "ICS2101 Multimedia Mixer", + ics2101_mixer_ioctl +}; + +void +ics2101_mixer_init() +{ + int i; + + if (num_mixers < MAX_MIXER_DEV) { + mixer_devs[num_mixers++] = &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); + } +} + +#endif diff --git a/sys/i386/isa/sound/iwdefs.h b/sys/i386/isa/sound/iwdefs.h new file mode 100644 index 0000000..60f50fc --- /dev/null +++ b/sys/i386/isa/sound/iwdefs.h @@ -0,0 +1,712 @@ +/*#######################################################################*/ +/* FILE : iwdefs.h*/ +/**/ +/* REMARKS: This file contains all defines used by DDK functions. It*/ +/* should be included by all applications and should be*/ +/* referenced by programmers to make their code easy to read*/ +/* and understand.*/ +/**/ +/* UPDATE: 3/21/95*/ +/* 7/10/95 --- added #def DRAM_HOLES*/ +/*#######################################################################*/ +#ifndef IWDEFS +#define IWDEFS +/*#######################################################################*/ +/**/ +/* Macros for use in loading Synth Addr Regs*/ +/**/ +/*#######################################################################*/ +#define ADDR_HIGH(x) (short)(x>>7) +#define ADDR_LOW(x) (short)(x<<9) +/*#######################################################################*/ +/**/ +/* Defines for DMA Controllers*/ +/**/ +/*#######################################################################*/ +/* DMA Controler #1 (8-bit controller) */ +#define DMA1_STAT 0x08 /* read status register */ +#define DMA1_WCMD 0x08 /* write command register */ +#define DMA1_WREQ 0x09 /* write request register */ +#define DMA1_SNGL 0x0A /* write single bit register */ +#define DMA1_MODE 0x0B /* write mode register */ +#define DMA1_CLRFF 0x0C /* clear byte ptr flip/flop */ +#define DMA1_MCLR 0x0D /* master clear register */ +#define DMA1_CLRM 0x0E /* clear mask register */ +#define DMA1_WRTALL 0x0F /* write all mask register */ + +/* DMA Controler #2 (16-bit controller) */ +#define DMA2_STAT 0xD0 /* read status register */ +#define DMA2_WCMD 0xD0 /* write command register */ +#define DMA2_WREQ 0xD2 /* write request register */ +#define DMA2_SNGL 0xD4 /* write single bit register */ +#define DMA2_MODE 0xD6 /* write mode register */ +#define DMA2_CLRFF 0xD8 /* clear byte ptr flip/flop */ +#define DMA2_MCLR 0xDA /* master clear register */ +#define DMA2_CLRM 0xDC /* clear mask register */ +#define DMA2_WRTALL 0xDE /* write all mask register */ + +#define DMA0_ADDR 0x00 /* chan 0 base adddress */ +#define DMA0_CNT 0x01 /* chan 0 base count */ +#define DMA1_ADDR 0x02 /* chan 1 base adddress */ +#define DMA1_CNT 0x03 /* chan 1 base count */ +#define DMA2_ADDR 0x04 /* chan 2 base adddress */ +#define DMA2_CNT 0x05 /* chan 2 base count */ +#define DMA3_ADDR 0x06 /* chan 3 base adddress */ +#define DMA3_CNT 0x07 /* chan 3 base count */ +#define DMA4_ADDR 0xC0 /* chan 4 base adddress */ +#define DMA4_CNT 0xC2 /* chan 4 base count */ +#define DMA5_ADDR 0xC4 /* chan 5 base adddress */ +#define DMA5_CNT 0xC6 /* chan 5 base count */ +#define DMA6_ADDR 0xC8 /* chan 6 base adddress */ +#define DMA6_CNT 0xCA /* chan 6 base count */ +#define DMA7_ADDR 0xCC /* chan 7 base adddress */ +#define DMA7_CNT 0xCE /* chan 7 base count */ + +#define DMA0_PAGE 0x87 /* chan 0 page register (refresh)*/ +#define DMA1_PAGE 0x83 /* chan 1 page register */ +#define DMA2_PAGE 0x81 /* chan 2 page register */ +#define DMA3_PAGE 0x82 /* chan 3 page register */ +#define DMA4_PAGE 0x8F /* chan 4 page register (unusable)*/ +#define DMA5_PAGE 0x8B /* chan 5 page register */ +#define DMA6_PAGE 0x89 /* chan 6 page register */ +#define DMA7_PAGE 0x8A /* chan 7 page register */ +/*#######################################################################*/ +/**/ +/* Defines for register UISR (Interrupt Status)*/ +/**/ +/*#######################################################################*/ +#define MIDI_TX_IRQ 0x01 +#define MIDI_RX_IRQ 0x02 +#define ALIB_TIMER1_IRQ 0x04 +#define ALIB_TIMER2_IRQ 0x08 +#define _UASBCI 0x45 /* UASBCI index */ +#define SAMPLE_CONTROL 0x49 /* Not used by IW */ +#define SET_VOICES 0x0E +#define WAVETABLE_IRQ 0x20 +#define ENVELOPE_IRQ 0x40 +#define DMA_TC_IRQ 0x80 +/*#######################################################################*/ +/**/ +/* Synthesizer-related defines*/ +/**/ +/*#######################################################################*/ +#define GEN_INDEX 0x03 /* IGIDX offset into p3xr */ +#define VOICE_SELECT 0x02 /* SVSR offset into p3xr */ +#define VOICE_IRQS 0x8F /* SVII index (read) */ +#define _URSTI 0x4C /* URSTI index */ +#define GF1_SET 0x01 /* URSTI[0] */ +#define GF1_OUT_ENABLE 0x02 /* URSTI[1] */ +#define GF1_IRQ_ENABLE 0x04 /* URSTI[2] */ +#define GF1_RESET 0xFE /* URSTI[0]=0 */ +#define VOICE_VOLUME_IRQ 0x04 /* SVII[2] */ +#define VOICE_WAVE_IRQ 0x08 /* SVII[3] */ +#define VC_IRQ_ENABLE 0x20 /* SACI[5] or SVCI[5]*/ +#define VOICE_NUMBER 0x1F /* Mask for SVII[4:0] */ +#define VC_IRQ_PENDING 0x80 /* SACI[7] or SVCI[7] */ +#define VC_DIRECT 0x40 /* SACI[6] or SVCI[6]*/ +#define VC_DATA_WIDTH 0x04 /* SACI[2] */ +#define VOICE_STOP 0x02 /* SACI[1] */ +#define VOICE_STOPPED 0x01 /* SACI[0] */ +#define VOLUME_STOP 0x02 /* SVCI[1] */ +#define VOLUME_STOPPED 0x01 /* SVCI[0] */ +#define VC_ROLLOVER 0x04 /* SVCI[2] */ +#define VC_LOOP_ENABLE 0x08 /* SVCI[3] or SACI[3]*/ +#define VC_BI_LOOP 0x10 /* SVCI[4] or SACI[4]*/ +#define VOICE_OFFSET 0x20 /* SMSI[5] */ +#define VOLUME_RATE0 0x00 /* SVRI[7:6]=(0,0) */ +#define VOLUME_RATE1 0x40 /* SVRI[7:6]=(0,1) */ +#define VOLUME_RATE2 0x80 /* SVRI[7:6]=(1,0) */ +#define VOLUME_RATE3 0xC0 /* SVRI[7:6]=(1,1) */ +/*#######################################################################*/ +/**/ +/* Power-Mode Control Defines*/ +/**/ +/*#######################################################################*/ +#define SHUT_DOWN 0x7E /* shuts InterWave down */ +#define POWER_UP 0xFE /* enables all modules */ +#define CODEC_PWR_UP 0x81 /* enables Codec Analog Ckts */ +#define CODEC_PWR_DOWN 0x01 /* disables Codec Analog Ckts */ +#define CODEC_REC_UP 0x82 /* Enables Record Path */ +#define CODEC_REC_DOWN 0x02 /* Disables Record Path */ +#define CODEC_PLAY_UP 0x84 /* Enables Playback Path */ +#define CODEC_PLAY_DOWN 0x04 /* Disables Playback Path */ +#define CODEC_IRQ_ENABLE 0x02 /* CEXTI[2] */ +#define CODEC_TIMER_IRQ 0x40 /* CSR3I[6] */ +#define CODEC_REC_IRQ 0x20 /* CSR3I[5] */ +#define CODEC_PLAY_IRQ 0x10 /* CSR3I[4] */ +#define CODEC_INT 0x01 /* CSR1R[0] */ +#define MONO_INPUT 0x80 /* CMONOI[7] */ +#define MONO_OUTPUT 0x40 /* CMONOI[6] */ +#define MIDI_UP 0x88 /* Enables MIDI ports */ +#define MIDI_DOWN 0x08 /* Disables MIDI ports */ +#define SYNTH_UP 0x90 /* Enables Synthesizer */ +#define SYNTH_DOWN 0x10 /* Disables Synthesizer */ +#define LMC_UP 0xA0 /* Enables LM Module */ +#define LMC_DOWN 0x20 /* Disbales LM Module */ +#define XTAL24_UP 0xC0 /* Enables 24MHz Osc */ +#define XTAL24_DOWN 0x40 /* Disables 24MHz Osc */ +#define _PPWRI 0xF2 /* PPWRI index */ +#define PLAY 0x0F +#define REC 0x1F +#define LEFT_AUX1_INPUT 0x02 +#define RIGHT_AUX1_INPUT 0x03 +#define LEFT_AUX2_INPUT 0x04 +#define RIGHT_AUX2_INPUT 0x05 +#define LEFT_LINE_IN 0x12 +#define RIGHT_LINE_IN 0x13 +#define LEFT_LINE_OUT 0x19 +#define RIGHT_LINE_OUT 0x1B +#define LEFT_SOURCE 0x00 +#define RIGHT_SOURCE 0x01 +#define LINE_IN 0x00 +#define AUX1_IN 0x40 +#define MIC_IN 0x80 +#define MIX_IN 0xC0 +#define LEFT_DAC 0x06 +#define RIGHT_DAC 0x07 +#define LEFT_MIC_IN 0x16 +#define RIGHT_MIC_IN 0x17 +#define _CUPCTI 0x0E +#define _CLPCTI 0x0F +#define _CURCTI 0x1E +#define _CLRCTI 0x1F +#define _CLAX1I 0x02 +#define _CRAX1I 0x03 +#define _CLAX2I 0x04 +#define _CRAX2I 0x05 +#define _CLLICI 0x12 +#define _CRLICI 0x13 +#define _CLOAI 0x19 +#define _CROAI 0x1B +#define _CLICI 0x00 +#define _CRICI 0x01 +#define _CLDACI 0x06 +#define _CRDACI 0x07 +#define _CPVFI 0x1D +/*#######################################################################*/ +/**/ +/* Defines for DMA transfer related operations*/ +/**/ +/*#######################################################################*/ +#define MAX_DMA 0x07 +#define DMA_DECREMENT 0x20 +#define AUTO_INIT 0x10 +#define DMA_READ 0x01 +#define DMA_WRITE 0x02 +#define AUTO_READ 0x03 +#define AUTO_WRITE 0x04 +#define IDMA_INV 0x0400 +#define IDMA_WIDTH_16 0x0100 +/*#######################################################################*/ +/**/ +/* Bits for dma flags within a DMA structure.*/ +/**/ +/*#######################################################################*/ +#define DMA_USED 0x0001 + +#define DMA_SPLIT 0x0004 /* DMA Controller Page Crossover*/ +#define CODEC_DMA 0x0008 /* Indicates a Codec DMA*/ +#define DMA_WAIT 0x0020 /* Wait for DMA xfer to complete*/ +#define DMA_DOWN 0x0040 /* DMA xfer from PC to InterWave*/ +#define DRAM_HOLES 0x8000 /* Indicates Non-contiguous RAM configuration*/ +#define DMA_UP 0xFFBF /* DMA xfer from InterWave to PC */ +/*#######################################################################*/ +/**/ +/* Bits for DMA Control Register (LDMACI)*/ +/**/ +/*#######################################################################*/ +#define _LDMACI 0x41 /* Index */ +#define DMA_INV 0x80 +#define DMA_IRQ_ENABLE 0x20 +#define DMA_IRQ_PENDING 0x40 /* on reads of LDMACI[6] */ +#define DMA_DATA_16 0x40 /* on writes to LDMACI[6] */ +#define DMA_WIDTH_16 0x04 /* 1=16-bit, 0=8-bit (DMA channel) */ +#define DMA_RATE 0x18 /* 00=fastest,...,11=slowest */ +#define DMA_UPLOAD 0x02 /* From LM to PC */ +#define DMA_ENABLE 0x01 +/*#######################################################################*/ +/**/ +/* DMA Transfer Rates*/ +/**/ +/*#######################################################################*/ +#define DMA_R0 0xE7 /* Fastest (use ANDing to set) */ +#define DMA_R1 0x08 +#define DMA_R2 0x10 +#define DMA_R3 0x18 /* Slowest */ +/*#######################################################################*/ +/**/ +/* Interrupt Controller Defines*/ +/**/ +/*#######################################################################*/ +#define IW_HANDLERS_ON 0x80 /* Flag for when IVT is modified */ +#define EOI 0x20 +#define OCR1 0x20 /* 8259-1 Operation Control Reg. */ +#define IMR1 0x21 /* 8259-1 Interrupt Mask Reg. */ +#define OCR2 0xA0 /* 8259-2 Operation Control Reg. */ +#define IMR2 0xA1 /* 8259-2 Interrupt Mask Reg. */ +#define IRQ0_UNMASK 0xFE /* Mask to clear bit 0 in IMR */ +#define IRQ1_UNMASK 0xFD +#define IRQ2_UNMASK 0xFB +#define IRQ3_UNMASK 0xF7 +#define IRQ4_UNMASK 0xEF +#define IRQ5_UNMASK 0xDF +#define IRQ6_UNMASK 0xBF +#define IRQ7_UNMASK 0x7F +#define IRQ8_UNMASK 0xFE /* Mask to clear bit 0 in IMR */ +#define IRQ9_UNMASK 0xFD +#define IRQ10_UNMASK 0xFB +#define IRQ11_UNMASK 0xF7 +#define IRQ12_UNMASK 0xEF +#define IRQ13_UNMASK 0xDF +#define IRQ14_UNMASK 0xBF +#define IRQ15_UNMASK 0x7F +#define IRQ0_EOI 0x60 /* Spec EOI for IRQ0 */ +#define IRQ1_EOI 0x61 +#define IRQ2_EOI 0x62 +#define IRQ3_EOI 0x63 +#define IRQ4_EOI 0x64 +#define IRQ5_EOI 0x65 +#define IRQ6_EOI 0x66 +#define IRQ7_EOI 0x67 +#define IRQ8_EOI 0x60 /* Spec EOI for IRQ8 */ +#define IRQ9_EOI 0x61 +#define IRQ10_EOI 0x62 +#define IRQ11_EOI 0x63 +#define IRQ12_EOI 0x64 +#define IRQ13_EOI 0x65 +#define IRQ14_EOI 0x66 +#define IRQ15_EOI 0x67 +/*#######################################################################*/ +/**/ +/* Generic defines*/ +/**/ +/*#######################################################################*/ +/**/ +#define MEMBANK0 0L /* Addr of Memory Bank 0*/ +#define MEMBANK1 4194304L /* Addr of Memory Bank 1*/ +#define MEMBANK2 8388608L /* Addr of Memory Bank 2*/ +#define MEMBANK3 12582912L /* Addr of Memory Bank 3*/ +#define IRQ_UNAVAIL 0x0000 +#define IRQ_AVAIL 0x0001 +#define IRQ_USED 0x0002 +#define MAX_IRQ 16 +#define NEXT_OFFSET 0L +#define PREV_OFFSET 4L +#define SIZE_OFFSET 8L +#define MEM_HEADER_SIZE 12L +#define GF1_POOL (usigned long)(256L*1024L) +#define GUS_MODE 0x00 /* SGMI[0]=0*/ +#define ENH_MODE 0x01 /* SGMI[0]=1*/ +#define ENABLE_LFOS 0x02 /* SGMI[1]*/ +#define NO_WAVETABLE 0x04 /* SGMI[2]*/ +#define RAM_TEST 0x08 /* SGMI[3]*/ +#define TRUE 1 +#define FALSE 0 +#define ON 1 +#define OFF 0 +#define AUDIO 0 +#define EXT 1 +#define GAME 2 +#define EMULATION 3 +#define MPU401 4 +#define AUDIO_EXT 2 +#define ALLOC_FAILURE 0xFFFFFFFFL +#define MEM_EXHAUSTED 0xFFFFFFFFL +#define RAM_MAX 16777216L +#define RAM_STEP 65536L +#define BANK_MAX 4194304L +#define ILLEGAL_SIZE -1 +#define MEM_INIT 1 +#define NO_NEXT 0xFFFFFFFFL +#define NO_PREV NO_NEXT +#define DMA_BAD_ADDR -1 +#define DMA_ON -1 +#define DMA_OK 1 +#define MIDI_TX_IRQ 0x01 +#define MIDI_RX_IRQ 0x02 +#define ALIB_TIMER1_IRQ 0x04 +#define ALIB_TIMER2_IRQ 0x08 +#define WAVETABLE_IRQ 0x20 +#define ENVELOPE_IRQ 0x40 +#define DMA_TC_IRQ 0x80 +#define DMA_SET_MASK 0x04 +#define PNP_DATA_RDY 1 /* PRESSI[0] */ +#define IWAVE_ABSENT 2 +#define IWAVE_OPEN 4 +#define IWAVE_OK 5 +#define BAD_VOICES -1 +#define PNP_ABSENT 0xFF /* No PNP cards in system */ +#define DPMI_INT 0x31 +#define _PCCCI 0x02 +#define _PCSNI 0x06 +#define _PIDXR 0x279 +#define _PNPWRP 0xA79 +#define _LDSALI 0x42 +#define _LDSAHI 0x50 +#define _LMALI 0x43 +#define _LMAHI 0x44 +#define _LMCFI 0x52 +#define _LMCI 0x53 +#define _LDIBI 0x58 +#define _LDICI 0x57 +#define _LMSBAI 0x51 +#define _SVCI_RD 0x8D +#define _SVCI_WR 0x0D +#define _SACI_RD 0x80 +#define _SACI_WR 0x00 +#define _SALI_RD 0x8B +#define _SALI_WR 0x0B +#define _SAHI_RD 0x8A +#define _SAHI_WR 0x0A +#define _SASHI_RD 0x82 +#define _SASHI_WR 0x02 +#define _SASLI_RD 0x83 +#define _SASLI_WR 0x03 +#define _SAEHI_RD 0x84 +#define _SAEHI_WR 0x04 +#define _SAELI_RD 0x85 +#define _SAELI_WR 0x05 +#define _SVRI_RD 0x86 +#define _SVRI_WR 0x06 +#define _SVSI_RD 0x87 +#define _SVSI_WR 0x07 +#define _SVEI_RD 0x88 +#define _SVEI_WR 0x08 +#define _SVLI_RD 0x89 +#define _SVLI_WR 0x09 +#define _SROI_RD 0x8C +#define _SROI_WR 0x0C +#define _SLOI_RD 0x93 +#define _SLOI_WR 0x13 +#define _SMSI_RD 0x95 +#define _SMSI_WR 0x15 +#define _SGMI_RD 0x99 +#define _SGMI_WR 0x19 +#define _SFCI_RD 0x81 +#define _SFCI_WR 0x01 +#define _SUAI_RD 0x90 +#define _SUAI_WR 0x10 +#define _SVII 0x8F +#define _CMODEI 0x0C /* index for CMODEI */ +#define _CFIG3I 0x11 +#define _CFIG2I 0x10 +#define _CLTIMI 0x14 +#define _CUTIMI 0x15 +#define _CSR3I 0x18 /* Index to CSR3I (Interrupt Status) */ +#define _CEXTI 0x0A /* Index to External Control Register */ +#define _CFIG1I 0x09 /* Index to Codec Conf Reg 1 */ +#define _CSR2I 0x0B /* Index to Codec Stat Reg 2 */ +#define _CPDFI 0x08 /* Index to Play Data Format Reg */ +#define _CRDFI 0x1C /* Index to Rec Data Format Reg */ +#define _CLMICI 0x16 /* Index to Left Mic Input Ctrl Register */ +#define _CRMICI 0x17 /* Index to Right Mic Input Ctrl Register */ +#define _CLCI 0x0D /* Index to Loopback Ctrl Register */ +#define _IVERI 0x5B /* Index to register IVERI */ +#define CODEC_MODE1 0x00 +#define CODEC_MODE2 0x40 +#define CODEC_MODE3 0x6C /* Enhanced Mode */ +#define CODEC_STATUS1 0x01 +#define CODEC_STATUS2 0x0B /* Index to CSR2I */ +#define CODEC_STATUS3 0x18 /* Index to CSR3I */ +#define PLAYBACK 0x01 /* Enable playback path CFIG1I[0]=1*/ +#define RECORD 0x02 /* Enable Record path CFIG1I[1]=1*/ +#define TIMER_ENABLE 0x40 /* CFIG2I[6] */ +#define CODEC_MCE 0x40 /* CIDXR[6] */ +#define CALIB_IN_PROGRESS 0x20 /* CSR2I[5] */ +#define CODEC_INIT 0x80 /* CIDXR[7] */ +#define BIT16_BIG 0xC0 /* 16-bit signed, big endian */ +#define IMA_ADPCM 0xA0 /* IMA-compliant ADPCM */ +#define BIT8_ALAW 0x60 /* 8-bit A-law */ +#define BIT16_LITTLE 0x40 /* 16-bit signed, lillte endian */ +#define BIT8_ULAW 0x20 /* 8-bit u-law */ +#define BIT8_LINEAR 0x00 /* 8-bit unsigned */ +#define REC_DFORMAT 0x1C +#define PLAY_DFORMAT 0x08 +#define DMA_ACCESS 0x00 +#define PIO_ACCESS 0xC0 +#define DMA_SIMPLEX 0x04 +#define STEREO 0x10 /* CxDFI[4] */ +#define XTAL1 0x00 /* CxDFI[4]=0 selects 24.5Mhz XTAL */ +#define XTAL2 0x01 /* CxDFI[4]=1 selects 16.9Mhz XTAL */ +#define AUTOCALIB 0x08 /* CFIG1I[3] */ +#define ROM_IO 0x02 /* ROM I/O cycles - LMCI[1]=1 */ +#define DRAM_IO 0x4D /* DRAM I/O cycles - LMCI[1]=0 */ +#define AUTOI 0x01 /* LMCI[0]=1 */ +#define _PLDNI 0x07 +#define ACTIVATE_DEV 0x30 +#define _PWAKEI 0x03 /* Index for PWAKEI */ +#define _PISOCI 0x01 /* Index for PISOCI */ +#define _PSECI 0xF1 /* Index for PSECI */ +#define RANGE_IOCHK 0x31 /* PURCI or PRRCI Index */ +#define MIDI_RESET 0x03 +#define IO_OK 5 /* No IO conflict flag */ +#define IO_CONFLICT 6 /* IO Conflict detected */ +#define IO_0x55 0x01 +#define IO_0xAA 0xFE +/*#######################################################################*/ +/**/ +/* Defines for Sound Handlers in "iw".*/ +/**/ +/*#######################################################################*/ +#define PLAY_DMA_HANDLER 0x01 +#define REC_DMA_HANDLER 0x02 +#define MIDI_TX_HANDLER 0x03 +#define MIDI_RX_HANDLER 0x04 +#define TIMER1_HANDLER 0x05 +#define TIMER2_HANDLER 0x06 +#define WAVE_HANDLER 0x07 +#define VOLUME_HANDLER 0x08 +#define CODEC_TIMER_HANDLER 0x09 +#define CODEC_PLAY_HANDLER 0x0A +#define CODEC_REC_HANDLER 0x0B +#define AUX_HANDLER 0x0C +/*#######################################################################*/ +/**/ +/* Mapping for System Control Regs.*/ +/**/ +/*#######################################################################*/ +#define UMCR 0x00010000 /* Mix Control Reg.*/ +#define UISR 0x00020006 /* IRQ Stat Reg. (read) */ +#define U2X6R 0x00030006 /* SB 2X6 reg */ +#define UACWR 0x00040008 /* AdLib Command Write Reg */ +#define UASRR 0x00050008 /* AdLib Stat Read Reg */ +#define UADR 0x00060009 /* AdLib Data Register */ +#define UACRR 0x0007000A /* AdLib Cmd Read Reg */ +#define UASWR 0x0008000A /* AdLib Stat Write Reg */ +#define UHRDP 0x0009000B /* Hidden Reg Data Port */ +#define UI2XCR 0x000A000C /* SB IRQ 2xC Reg */ +#define U2XCR 0x000B000D /* SB 2xC Reg. (No IRQ) */ +#define U2XER 0x000C000E /* SB 2xE Reg. */ +#define URCR 0x000D000F /* Reg Control Register */ +#define USRR 0x000E000F /* Status Read Register */ +#define UDCI 0x000F000B /* DMA Channel Control Reg */ +#define UICI 0x0010000B /* Interrupt Ctrl Reg */ +#define UGP1I 0x0011010B /* GP Reg 1 (Back Door) */ +#define UGP2I 0x0012020B /* GP Reg 2 (Back Door) */ +#define UGPA1I 0x0013030B /* GP reg 1 Address */ +#define UGPA2I 0x0014040B /* GP reg 2 Address */ +#define UCLRII 0x0015050B /* Clear Interrupt Reg */ +#define UJMPI 0x0016060B /* Jumper Register */ +#define UGP1II 0x0017000B /* Gen. Purp Reg 1(Emulation) */ +#define UGP2II 0x0018000B /* Gen. Purp Reg 2(Emulation) */ +#define GGCR 0x00190201 /* Game Control Register */ +#define GMCR 0x001A0000 /* MIDI Control Register */ +#define GMSR 0x001B0000 /* MIDI Status Reg. */ +#define GMTDR 0x001C0001 /* MIDI xmit data reg */ +#define GMRDR 0x001D0001 /* MIDI rcv data reg */ +#define SVSR 0x001E0002 /* Synth Voice Select Reg */ +#define IGIDXR 0x001F0003 /* General Index Register */ +#define I16DP 0x00200004 /* General 16-bit Data Port */ +#define I8DP 0x00210005 /* General 8-bit Data Port */ +/*#######################################################################*/ +/**/ +/* Synth defines*/ +/**/ +/*#######################################################################*/ +#define SACI 0x00220005 /* Synth Addr Control */ +#define SFCI 0x00230104 /* Synth Freq Control */ +#define SASHI 0x00240204 /* Synth Addr Start High */ +#define SASLI 0x00250304 /* Synth Addr Start Low */ +#define SAEHI 0x00260404 /* Synth Addr End High */ +#define SAELI 0x00270504 /* Synth Addr End Low */ +#define SVRI 0x00280605 /* Synth Volume Rate */ +#define SVSI 0x00290705 /* Synth Volume Start */ +#define SVEI 0x002A0805 /* Synth Volume End */ +#define SVLI 0x002B0904 /* Synth Volume Level */ +#define SAHI 0x002C0A04 /* Synth Address High */ +#define SALI 0x002D0B04 /* Synth Address Low */ +#define SROI 0x002E0C04 /* Synth Right Offset */ +#define SVCI 0x002F0D05 /* Synth Volume Control */ +#define SAVI 0x00300E05 /* Synth Active Voices */ +#define SVII 0x00318F05 /* Synth Voice IRQ */ +#define SUAI 0x00321005 /* Synth Upper Addr */ +#define SEAHI 0x00331104 /* Synth Effect Addr High */ +#define SEALI 0x00341204 /* Synth Effect Addr Low */ +#define SLOI 0x00351304 /* Synth Left Offset */ +#define SEASI 0x00361405 /* Synth Effects Accum Sel */ +#define SMSI 0x00371505 /* Synth Mode Select */ +#define SEVI 0x00381604 /* Synth Effect Volume */ +#define SFLFOI 0x00391705 /* Synth Freq LFO */ +#define SVLFOI 0x003A1805 /* Synth Vol LFO */ +#define SGMI 0x003B1905 /* Synth Global Mode */ +#define SLFOBI 0x003C1A04 /* Synth LFO Base Address */ +#define SROFI 0x003D1B04 +#define SLOFI 0x003E1C04 +#define SEVFI 0x003F1D04 +#define SVIRI 0x00409F05 /* Synth Voice Read IRQ */ +#define LDMACI 0x00414105 /* DMA Control Reg. */ +#define LDSALI 0x00424204 /* LMC DMA Start Addr. Low Reg. */ +#define LMALI 0x00434304 /* LMC Addr Low (I/O) */ +#define LMAHI 0x00444405 /* LMC Addr High (I/O) */ +#define UASBCI 0x00454505 /* Adlib-SB Control */ +#define UAT1I 0x00464605 /* AdLib Timer 1 Count */ +#define UAT2I 0x00474705 /* AdLib Timer 2 Count */ +#define USCI 0x00484905 /* Sample Control Reg */ +#define GJTDI 0x00494B05 +#define URSTI 0x004A4C05 +#define LDSAHI 0x004B5005 +#define LMSBAI 0x004C5104 +#define LMCFI 0x004D5204 +#define LMCI 0x004E5305 +#define LMRFAI 0x004F5404 +#define LMPFAI 0x00505504 +#define LMSFI 0x00515604 +#define LDICI 0x00525704 +#define LDIBI 0x00535804 +#define ICMPTI 0x00545905 +#define IDECI 0x00555A05 +#define IVERI 0x00565B05 +#define IEMUAI 0x00575C05 +#define IEMUBI 0x00585D05 +#define GMRFAI 0x00595E05 +#define ITCI 0x005A5F05 +#define IEIRQI 0x005B6005 +#define LMBDR 0x005C0007 +/*##########################################################*/ +/* Mnemonics for Codec Registers*/ +/*##########################################################*/ +#define CIDXR 0x005D0000 +#define CDATAP 0x005E0001 +#define CSR1R 0x005F0002 +#define CPDR 0x00600003 +#define CRDR 0x00610003 +#define CLICI 0x00620001 +#define CRICI 0x00630101 +#define CLAX1I 0x00640201 +#define CRAX1I 0x00650301 +#define CLAX2I 0x00660401 +#define CRAX2I 0x00670501 +#define CLDACI 0x00680601 +#define CRDACI 0x00690701 +#define CPDFI 0x006A0801 +#define CFIG1I 0x006B0901 +#define CEXTI 0x006C0A01 +#define CSR2I 0x006D0B01 +#define CMODEI 0x006E0C01 +#define CLCI 0x006F0D01 +#define CUPCTI 0x00700E01 +#define CLPCTI 0x00710F01 +#define CFIG2I 0x00721001 +#define CFIG3I 0x00731101 +#define CLLICI 0x00741201 +#define CRLICI 0x00751301 +#define CLTIMI 0x00761401 +#define CUTIMI 0x00771501 +#define CLMICI 0x00781601 +#define CRMICI 0x00791701 +#define CSR3I 0x007A1801 +#define CLOAI 0x007B1901 +#define CMONOI 0x007C1A01 +#define CROAI 0x007D1B01 +#define CRDFI 0x007E1C01 +#define CPVFI 0x007F1D01 +#define CURCTI 0x00801E01 +#define CLRCTI 0x00811F01 +/*##########################################################*/ +/* Mnemonics for PnP Registers*/ +/*##########################################################*/ +#define PCSNBR 0x00820201 +#define PIDXR 0x00830279 +#define PNPWRP 0x00840A79 +#define PNPRDP 0x00850000 +#define PSRPAI 0x00860000 +#define PISOCI 0x00870100 +#define PCCCI 0x00880200 +#define PWAKEI 0x00890300 +#define PRESDI 0x008A0400 +#define PRESSI 0x008B0500 +#define PCSNI 0x008C0600 +#define PLDNI 0x008D0700 +#define PUACTI 0x008E3000 +#define PURCI 0x008F3100 +#define P2X0HI 0x00906000 +#define P2X0LI 0x00916100 +#define P3X0HI 0x00926200 +#define P3X0LI 0x00936300 +#define PHCAI 0x00946400 +#define PLCAI 0x00956500 +#define PUI1SI 0x00967000 +#define PUI1TI 0x00977100 +#define PUI2SI 0x00987200 +#define PUI2TI 0x00997300 +#define PUD1SI 0x009A7400 +#define PUD2SI 0x009B7500 +#define PSEENI 0x009CF000 +#define PSECI 0x009DF100 +#define PPWRI 0x009EF200 +#define PRACTI 0x009F3001 +#define PRRCI 0x00A03101 +#define PRAHI 0x00A16001 +#define PRALI 0x00A26101 +#define PATAHI 0x00A36201 +#define PATALI 0x00A46301 +#define PRISI 0x00A57001 +#define PRITI 0x00A67101 +#define PRDSI 0x00A77401 +#define PGACTI 0x00A83002 +#define PGRCI 0x00A93102 +#define P201HI 0x00AA6002 +#define P201LI 0x00AB6102 +#define PSACTI 0x00AC3003 +#define PSRCI 0x00AD3103 +#define P388HI 0x00AE6003 +#define P388LI 0x00AF6103 +#define PSBISI 0x00B07003 +#define PSBITI 0x00B17103 +#define PMACTI 0x00B23004 +#define PMRCI 0x00B33104 +#define P401HI 0x00B46004 +#define P401LI 0x00B56104 +#define PMISI 0x00B67004 +#define PMITI 0x00B77104 + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned short PORT; +typedef unsigned long DWORD; +typedef unsigned long ADDRESS; +typedef int BOOL; +typedef int FLAG; + +typedef struct + { + short flags; /* InterWave stat flags */ + PORT pcodar; /* Base Port for Codec */ + PORT pcdrar; /* Base Port for Ext Device */ + PORT p2xr; /* Compatibility Base Port */ + PORT p3xr; /* MIDI and Synth Base Port */ + PORT p401ar; /* Gen Purpose Reg. 1 address */ + PORT p201ar; /* Game Ctrl normally at 0x201 */ + PORT pataar; /* Base Address for ATAPI I/O Space */ + PORT p388ar; /* Base Port for AdLib. It should be 388h */ + PORT pnprdp; /* PNP read data port */ + PORT igidxr; /* Gen Index Reg at P3XR+0x03 */ + PORT i16dp; /* 16-bit data port at P3XR+0x04 */ + PORT i8dp; /* 8-bit data port at P3XR+0x05 */ + PORT svsr; /* Synth Voice Select at P3XR+0x02 */ + PORT cdatap; /* Codec Indexed Data Port at PCODAR+0x01 */ + PORT csr1r; /* Codec Stat Reg 1 at PCODAR+0x02 */ + PORT cxdr; /* Play or Record Data Reg at PCODAR+0x03 */ + PORT gmxr; /* GMCR or GMSR at P3XR+0x00 */ + PORT gmxdr; /* GMTDR or GMRDR at P3XR+0x01 */ + PORT lmbdr; /* LMBDR at P3XR+0x07 */ + BYTE csn; /* Card Select Number */ + BYTE cmode; /* Codec Operation Mode */ + int dma1_chan; /* DMA channel 1 (local DMA & codec rec) */ + int dma2_chan; /* DMA channel 2 (codec play) */ + int ext_chan; /* Ext Dev DMA channel */ + BYTE voices; /* Number of active voices */ + DWORD vendor; /* Vendor ID and Product Identifier */ + int synth_irq; /* Synth IRQ number */ + int midi_irq; /* MIDI IRQ number */ + int ext_irq; /* Ext Dev IRQ */ + int mpu_irq; /* MPU401 Dev IRQ */ + int emul_irq; /* Sound Blaster/AdLib Dev IRQ */ + ADDRESS free_mem; /* Address of First Free LM Block */ + DWORD reserved_mem; /* Amount of LM reserved by app. */ + BYTE smode; /* Synth Mode */ + WORD size_mem; /* Total LM in Kbytes */ + + } IWAVE; + +#endif diff --git a/sys/i386/isa/sound/local.h b/sys/i386/isa/sound/local.h new file mode 100644 index 0000000..3f02968 --- /dev/null +++ b/sys/i386/isa/sound/local.h @@ -0,0 +1,188 @@ +/* + * local.h + * + * This file was generated by configure. But then HAND-EDITED. It will + * probably disappear in future revisions once the configuration process + * will become more like that of standard bsd code. + * lr 970714 + * + */ + +/* build hex2hex /tmp/foo.x trix_boot.h trix_boot */ + +/* + * make everything conditioned on NSND>0 so as to detect errors + * because of missing "controller snd0" statement + */ +#define ALLOW_BUFFER_MAPPING 1 + +#include "snd.h" +#if NSND > 0 + +#include "opt_sound.h" + +#define CONFIGURE_SOUNDCARD + +#define CONFIG_SEQUENCER + +#include "gus.h" +#if NGUS != 0 && !defined(CONFIG_GUS) +#define CONFIG_GUS +#define CONFIG_GUSMAX +#endif + +#include "sscape.h" +#if NSSCAPE != 0 && !defined(CONFIG_SSCAPE) +#define CONFIG_SSCAPE +#endif + +#include "trix.h" +#if NTRIX > 0 +#define INCLUDE_TRIX_BOOT +#define CONFIG_TRIX /* can use NTRIX > 0 instead */ +#define CONFIG_YM3812 +#endif + +#if defined(CONFIG_GUSMAX) || ( NSSCAPE > 0 ) || ( NTRIX > 0 ) +#define CONFIG_AD1848 +#endif + +#if defined(CONFIG_SEQUENCER) && (NTRIX == 0) +#define CONFIG_MIDI +#endif + +#include "sb.h" +#if NSB > 0 +#define CONFIG_SB +#endif + +#include "mss.h" +#if NMSS != 0 +#define CONFIG_AD1848 +#define CONFIG_MSS +#undef CONFIG_CS4232 +#endif + +#include "css.h" +#if NCSS != 0 +#define CONFIG_AD1848 +#undef CONFIG_MSS +#define CONFIG_CS4232 +#endif + +#include "sbxvi.h" +#if NSBXVI != 0 && !defined(CONFIG_SB16) +#define CONFIG_SB16 +#define CONFIG_SBPRO /* FIXME: Also needs to be a sep option */ +#endif + +#include "sbmidi.h" +#if NSBMIDI != 0 && !defined(CONFIG_SB16MIDI) +#define CONFIG_SB16MIDI +#endif + +#include "awe.h" +#if NAWE != 0 && !defined(CONFIG_AWE32) +#define CONFIG_AWE32 +#endif + +#include "pas.h" +#if NPAS != 0 && !defined(CONFIG_PAS) +#define CONFIG_PAS +#endif + +#include "mpu.h" +#if NMPU != 0 && !defined(CONFIG_MPU401) +#define CONFIG_MPU401 +#endif + +#include "opl.h" +#if NOPL != 0 && !defined(CONFIG_YM3812) +#define CONFIG_YM3812 +#endif + +#define ALLOW_POLL + +/* #undef CONFIG_PAS */ +/* #undef CONFIG_ADLIB */ +/* #define CONFIG_GUS */ +/* #undef CONFIG_MPU401 */ +#undef CONFIG_UART6850 +#undef CONFIG_PSS +#undef CONFIG_GUS16 +/* #undef CONFIG_MSS */ +/* #undef CONFIG_SSCAPE */ +#undef CONFIG_MAD16 +/* #undef CONFIG_CS4232 */ +#undef CONFIG_MAUI +#undef CONFIG_PNP +/* #undef CONFIG_SBPRO */ +/* #undef CONFIG_SB16 */ +#undef CONFIG_AEDSP16 +#define CONFIG_AUDIO /* obvious ? */ + +#define CONFIG_MPU_EMU + +#ifdef PC98 +#define DSP_BUFFSIZE 61440 +#else +#define DSP_BUFFSIZE 32768*2 +#endif +/* #define SELECTED_SOUND_OPTIONS 0x0188090a */ + +#ifndef TRIX_SB_BASE +#define TRIX_SB_BASE 0x220 +#endif + +#ifndef TRIX_SB_IRQ +#define TRIX_SB_IRQ 7 +#endif + +#ifndef TRIX_SB_DMA +#define TRIX_SB_DMA 1 +#endif + +#ifndef TRIX_BASE +#define TRIX_BASE 0x530 +#endif + +#ifndef TRIX_IRQ +#define TRIX_IRQ 9 +#endif + +#ifndef TRIX_DMA +#define TRIX_DMA 3 +#endif + +#ifndef TRIX_DMA2 +#define TRIX_DMA2 1 +#endif + +#ifndef GUS_BASE +#define GUS_BASE 0x220 +#endif + +#ifndef GUS_IRQ +#define GUS_IRQ 12 +#endif + +#ifndef GUS_MIDI_IRQ +#define GUS_MIDI_IRQ GUS_IRQ +#endif + +#ifndef GUS_DMA +#define GUS_DMA 4 +#endif + +#ifndef GUS_DMA2 +#define GUS_DMA2 4 +#endif + +#define SOUND_CONFIG_DATE "Wed Aug 6 22:58:35 PDT 1997" +#define SOUND_CONFIG_BY "Amancio Hasty" +#define SOUND_CONFIG_HOST "rah" +#define SOUND_CONFIG_DOMAIN "star-gate.com" + +#else /* NSND = 0 */ +#undef CONFIGURE_SOUNDCARD +#endif diff --git a/sys/i386/isa/sound/mad16.c b/sys/i386/isa/sound/mad16.c new file mode 100644 index 0000000..0e2064a --- /dev/null +++ b/sys/i386/isa/sound/mad16.c @@ -0,0 +1,524 @@ +/* + * sound/mad16.c + * + * Initialization code for OPTi MAD16 compatible audio chips. Including + * + * OPTi 82C928 MAD16 (replaced by C929) OAK OTI-601D Mozart + * OPTi 82C929 MAD16 Pro + * + * These audio interface chips don't prduce sound themselves. They just connect + * some other components (OPL-[234] and a WSS compatible codec) to the PC bus + * and perform I/O, DMA and IRQ address decoding. There is also a UART for + * the MPU-401 mode (not 82C928/Mozart). The Mozart chip appears to be + * compatible with the 82C928 (can anybody confirm this?). + * + * NOTE! If you want to set CD-ROM address and/or joystick enable, define + * MAD16_CONF in local.h as combination of the following bits: + * + * 0x01 - joystick disabled + * + * CD-ROM type selection (select just one): 0x00 - none 0x02 - Sony 31A + * 0x04 - Mitsumi 0x06 - Panasonic (type "LaserMate", not + * "SoundBlaster") 0x08 - Secondary IDE (address 0x170) 0x0a - Primary + * IDE (address 0x1F0) + * + * For example Mitsumi with joystick disabled = 0x04|0x01 = 0x05 For example + * LaserMate (for use with sbpcd) plus joystick = 0x06 + * + * MAD16_CDSEL: This defaults to CD I/O 0x340, no IRQ and DMA3 (DMA5 with + * Mitsumi or IDE). If you like to change these, define MAD16_CDSEL with the + * following bits: + * + * 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, 0x14=IRQ10 and 0x18=IRQ11. + * + * CD-ROM DMA (Sony or Panasonic): 0x00=DMA3, 0x01=DMA2, 0x02=DMA1 or + * 0x03=disabled or 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. + * + * Copyright by Hannu Savolainen 1995 + * + * 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_MAD16) + +static int already_initialized = 0; + +#define C928 1 +#define MOZART 2 +#define C929 3 + +/* + * Registers + * + * The MAD16 occupies I/O ports 0xf8d to 0xf93 (fixed locations). All ports are + * inactive by default. They can be activated by writing 0xE2 or 0xE3 to the + * password register. The password is valid only until the next I/O read or + * write. + */ + +#define MC1_PORT 0xf8d /* SB address, CDROM interface type, joystick */ +#define MC2_PORT 0xf8e /* CDROM address, IRQ, DMA, plus OPL4 bit */ +#define MC3_PORT 0xf8f +#define PASSWD_REG 0xf8f +#define MC4_PORT 0xf90 +#define MC5_PORT 0xf91 +#define MC6_PORT 0xf92 +#define MC7_PORT 0xf93 + +static int board_type = C928; + +static sound_os_info *mad16_osp; + +#ifndef DDB +#define DDB(x) +#endif + +static unsigned char +mad_read(int port) +{ + unsigned long flags; + unsigned char tmp; + + flags = splhigh(); + + switch (board_type) { /* Output password */ + case C928: + case MOZART: + outb(PASSWD_REG, 0xE2); + break; + + case C929: + outb(PASSWD_REG, 0xE3); + break; + } + + tmp = inb(port); + splx(flags); + + return tmp; +} + +static void +mad_write(int port, int value) +{ + unsigned long flags; + + flags = splhigh(); + + switch (board_type) { /* Output password */ + case C928: + case MOZART: + outb(PASSWD_REG, 0xE2); + break; + + case C929: + outb(PASSWD_REG, 0xE3); + break; + } + + outb(port, (unsigned char) (value & 0xff)); + splx(flags); +} + +static int +detect_mad16(void) +{ + unsigned char tmp, tmp2; + + /* + * 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(printf("MC1_PORT returned 0xff\n")); + return 0; + } + /* + * Now check that the gate is closed on first I/O after writing the + * password. (This is how a MAD16 compatible card works). + */ + + if ((tmp2 = inb(MC1_PORT)) == tmp) { /* It didn't close */ + DDB(printf("MC1_PORT didn't close after read (0x%02x)\n", tmp2)); + return 0; + } + mad_write(MC1_PORT, tmp ^ 0x80); /* Togge a bit */ + + if ((tmp2 = mad_read(MC1_PORT)) != (tmp ^ 0x80)) { /* Compare the bit */ + mad_write(MC1_PORT, tmp); /* Restore */ + DDB(printf("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2)); + return 0; + } + mad_write(MC1_PORT, tmp); /* Restore */ + return 1; /* Bingo */ + +} + +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; + + int ad_flags = 0; + + if (already_initialized) + return 0; + + mad16_osp = hw_config->osp; + /* + * Check that all ports return 0xff (bus float) when no password is + * written to the password register. + */ + + DDB(printf("--- Detecting MAD16 / Mozart ---\n")); + + + /* + * Then try to detect with the old password + */ + board_type = C928; + + DDB(printf("Detect using password = 0xE2\n")); + + if (!detect_mad16()) { /* No luck. Try different model */ + board_type = C929; + + DDB(printf("Detect using password = 0xE3\n")); + + if (!detect_mad16()) + return 0; + + DDB(printf("mad16.c: 82C929 detected\n")); + } else { + unsigned char model; + + if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) { + DDB(printf("mad16.c: Mozart detected\n")); + board_type = MOZART; + } else { + DDB(printf("mad16.c: 82C928 detected???\n")); + board_type = C928; + } + } + + for (i = 0xf8d; i <= 0xf93; i++) + DDB(printf("port %03x = %03x\n", i, mad_read(i))); + + /* + * Set the WSS address + */ + + tmp = 0x80; /* Enable WSS, Disable SB */ + + for (i = 0; i < 5; i++) { + if (i > 3) { /* Not a valid port */ + printf("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; + } + } + + /* + * Set optional CD-ROM and joystick settings. + */ + +#ifdef MAD16_CONF + tmp |= ((MAD16_CONF) & 0x0f); /* CD-ROM and joystick bits */ +#endif + mad_write(MC1_PORT, tmp); + +#if defined(MAD16_CONF) && defined(MAD16_CDSEL) + tmp = MAD16_CDSEL; +#else + tmp = 0x03; +#endif + +#ifdef MAD16_OPL4 + tmp |= 0x20; /* Enable OPL4 access */ +#endif + + 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 (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); + } + + for (i = 0xf8d; i <= 0xf93; i++) + DDB(printf("port %03x after init = %03x\n", i, mad_read(i))); + + /* + * Verify the WSS parameters + */ + + if (0) { + printf("MSS: I/O port conflict\n"); + 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(printf("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) { + printf("MSS: Bad IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) { + printf("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) { + printf("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) { + printf("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); + return 0; + } + return 1; +} + +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; + + already_initialized = 1; + + if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) + return; + + /* + * Set the IRQ and DMA addresses. + */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == -1) + return; + + outb(config_port, bits | 0x40); + if ((inb(version_port) & 0x40) == 0) + printf("[IRQ Conflict?]"); + + /* + * Handle the capture DMA channel + */ + + if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma) { + if ((dma == 0 && dma2 == 1) || + (dma == 1 && dma2 == 0) || + (dma == 3 && dma2 == 0)) { + dma2_bit = 0x04; /* Enable capture DMA */ + } else { + printf("MAD16: Invalid capture DMA\n"); + dma2 = dma; + } + } else + dma2 = dma; + + outb(config_port, bits | dma_bits[dma] | dma2_bit); /* Write IRQ+DMA setup */ + + ad1848_init("MAD16 WSS", hw_config->io_base + 4, + hw_config->irq, + dma, + dma2, 0, + hw_config->osp); +} + +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 (mad_read(MC1_PORT) & 0x20) + hw_config->io_base = 0x240; + else + hw_config->io_base = 0x220; + + return mad16_sb_dsp_init(hw_config); +#else + return 0; +#endif + } +#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) + if (!already_initialized) + return; + + attach_mpu401(hw_config); +#endif +} + +int +probe_mad16_mpu(struct address_info * hw_config) +{ +#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && 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: + printf("mad16/Mozart: Invalid MIDI IRQ\n"); + return 0; + } + + mad_write(MC3_PORT, tmp | 0x04); + return mad16_sb_dsp_detect(hw_config); +#else + return 0; +#endif + } + tmp = 0x83; /* MPU-401 enable */ + + /* + * Set the MPU base bits + */ + + for (i = 0; i < 5; i++) { + if (i > 3) { /* Out of array bounds */ + printf("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; + } + } + + /* + * Set the MPU IRQ bits + */ + + for (i = 0; i < 5; i++) { + if (i > 3) { /* Out of array bounds */ + printf("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 */ + + return probe_mpu401(hw_config); +#else + return 0; +#endif +} + +/* That's all folks */ +#endif diff --git a/sys/i386/isa/sound/mad16.h b/sys/i386/isa/sound/mad16.h new file mode 100644 index 0000000..0370973 --- /dev/null +++ b/sys/i386/isa/sound/mad16.h @@ -0,0 +1,91 @@ + +/* + * Initialization code for OPTI MAD16 interface chip by + * Davor Jadrijevic <davor@emard.pub.hr> + * (Included by ad1848.c when MAD16 support is enabled) + * + * It looks like MAD16 is similar than the Mozart chip (OAK OTI-601). + * It could be even possible that these chips are exactly the same. Can + * anybody confirm this? + */ + +static void wr_a_mad16(int base, int v, int a) +{ + OUTB(a, base + 0xf); + OUTB(v, base + 0x11); +} + +static void wr_b_mad16(int base, int v, int a) +{ + OUTB(a, base + 0xf); + OUTB(v, base + 0xd); +} + +/* +static int rd_a_mad16(int base, int a) +{ + OUTB(a, base + 0xf); + return INB(base + 0x11); +} +*/ + +static int rd_b_mad16(int base, int a) +{ + OUTB(a, base + 0xf); + return INB(base + 0xd); +} + +/* +static int rd_0_mad16(int base, int a) +{ + OUTB(a, base + 0xf); + return INB(base + 0xf); +} + +static void wr_ad(int base, int v, int a) +{ + OUTB(a, base + 4); + OUTB(v, base + 5); +} + +static int rd_ad(int base, int a) +{ + OUTB(a, base + 4); + return INB(base + 5); +} +*/ + +static int mad16init(int adr) +{ + int j; + long i; + + static int ad1848_bases[] = +{ 0x220, -1, -1, 0x240, -1, -1, -1, -1, 0x530, 0xE80, 0xF40, 0x604, 0 }; + + int mad16_base = 0xf80, ad1848_base; + + + for(j = 0; (j < 16) && (ad1848_bases[j] != 0); j++) + if(adr == ad1848_bases[j]) + break; + + if( (ad1848_base = ad1848_bases[j]) < 0x530) + { + printk("Unknown MAD16 setting 0x%3X\n", adr); + return -1; + } + + /* printk("OPTi MAD16 WSS at 0x%3X\n", ad1848_base); */ + + rd_b_mad16(mad16_base, 0xe2); + wr_a_mad16(mad16_base, 0x1a, 0xe2); + wr_b_mad16(mad16_base, j * 16 + 1, 0xe2); + wr_a_mad16(mad16_base, 0x1a, 0xe2); + for( i = 0; i < 10000; i++) + if( (INB(ad1848_base+4) & 0x80) == 0 ) + break; + + return 0; +}; + diff --git a/sys/i386/isa/sound/mad16_sb_midi.c b/sys/i386/isa/sound/mad16_sb_midi.c new file mode 100644 index 0000000..d1b3ad0 --- /dev/null +++ b/sys/i386/isa/sound/mad16_sb_midi.c @@ -0,0 +1,285 @@ +/* + * sound/mad16_sb_midi.c + * + * The low level driver for MAD16 SoundBlaster-DS-chip-based MIDI. + * + * Copyright by Hannu Savolainen 1993, Aaron Ucko 1995 + * + * 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_MAD16) && defined(CONFIG_MIDI) + +#define sbc_base mad16_sb_base +#include <i386/isa/sound/sb_card.h> + +static int input_opened = 0; +static int my_dev; +static int mad16_sb_base = 0x220; +static int mad16_sb_irq = 0; +static int mad16_sb_dsp_ok = 0; +static sound_os_info *midi_osp; + +int mad16_sb_midi_mode = NORMAL_MIDI; +int mad16_sb_midi_busy = 0; + +int mad16_sb_duplex_midi = 0; +volatile int mad16_sb_intr_active = 0; + +void (*midi_input_intr) (int dev, unsigned char data); + +static void mad16_sb_midi_init(int model); + +static int +mad16_sb_dsp_command(unsigned char val) +{ + int i; + unsigned long limit; + + limit = get_time() + hz / 10; /* The timeout is 0.1 secods */ + + /* + * Note! the i<500000 is an emergency exit. The + * mad16_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 && get_time() < limit; i++) { + if ((inb(DSP_STATUS) & 0x80) == 0) { + outb(DSP_COMMAND, val); + return 1; + } + } + + printf("MAD16 (SBP mode): DSP Command(%x) Timeout.\n", val); + printf("IRQ conflict???\n"); + return 0; +} + +void +mad16_sbintr(int irq) +{ + int status; + + unsigned long flags; + unsigned char data; + + status = inb(DSP_DATA_AVAIL); /* Clear interrupt */ + + flags = splhigh(); + + data = inb(DSP_READ); + if (input_opened) + midi_input_intr(my_dev, data); + + splx(flags); +} + +static int +mad16_sb_reset_dsp(void) +{ + int loopc; + + outb(DSP_RESET, 1); + DELAY(10); + outb(DSP_RESET, 0); + DELAY(30); + + for (loopc = 0; loopc < 100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++) + DELAY(10); + /* Wait for data available status */ + + if (inb(DSP_READ) != 0xAA) + return 0; /* Sorry */ + + return 1; +} + +int +mad16_sb_dsp_detect(struct address_info * hw_config) +{ + mad16_sb_base = hw_config->io_base; + mad16_sb_irq = hw_config->irq; + midi_osp = hw_config->osp; + + if (mad16_sb_dsp_ok) + return 0; /* Already initialized */ + if (!mad16_sb_reset_dsp()) + return 0; + + return 1; /* Detected */ +} + +void +mad16_sb_dsp_init(struct address_info * hw_config) +/* + * this function now just verifies the reported version and calls + * mad16_sb_midi_init -- everything else is done elsewhere + */ +{ + + midi_osp = hw_config->osp; + if (snd_set_irq_handler(mad16_sb_irq, mad16_sbintr, midi_osp) < 0) { + printf("MAD16 SB MIDI: IRQ not free\n"); + return; + } + + conf_printf("MAD16 MIDI (SB mode)", hw_config); + mad16_sb_midi_init(2); + + mad16_sb_dsp_ok = 1; + return; +} + +static int +mad16_sb_midi_open(int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + + if (!mad16_sb_dsp_ok) { + printf("MAD16_SB Error: MIDI hardware not installed\n"); + return -(ENXIO); + } + if (mad16_sb_midi_busy) + return -(EBUSY); + + if (mode != OPEN_WRITE && !mad16_sb_duplex_midi) { + if (num_midis == 1) + printf("MAD16 (SBP mode): Midi input not currently supported\n"); + return -(EPERM); + } + mad16_sb_midi_mode = NORMAL_MIDI; + if (mode != OPEN_WRITE) { + if (mad16_sb_intr_active) + return -(EBUSY); + mad16_sb_midi_mode = UART_MIDI; + } + if (mad16_sb_midi_mode == UART_MIDI) { + mad16_sb_reset_dsp(); + + if (!mad16_sb_dsp_command(0x35)) + return -(EIO); /* Enter the UART mode */ + mad16_sb_intr_active = 1; + + input_opened = 1; + midi_input_intr = input; + } + mad16_sb_midi_busy = 1; + + return 0; +} + +static void +mad16_sb_midi_close(int dev) +{ + if (mad16_sb_midi_mode == UART_MIDI) { + mad16_sb_reset_dsp(); /* The only way to kill the UART mode */ + } + mad16_sb_intr_active = 0; + mad16_sb_midi_busy = 0; + input_opened = 0; +} + +static int +mad16_sb_midi_out(int dev, unsigned char midi_byte) +{ + unsigned long flags; + + if (mad16_sb_midi_mode == NORMAL_MIDI) { + flags = splhigh(); + if (mad16_sb_dsp_command(0x38)) + mad16_sb_dsp_command(midi_byte); + else + printf("MAD16_SB Error: Unable to send a MIDI byte\n"); + splx(flags); + } else + mad16_sb_dsp_command(midi_byte); /* UART write */ + + return 1; +} + +static int +mad16_sb_midi_start_read(int dev) +{ + if (mad16_sb_midi_mode != UART_MIDI) { + printf("MAD16 (SBP mode): MIDI input not implemented.\n"); + return -(EPERM); + } + return 0; +} + +static int +mad16_sb_midi_end_read(int dev) +{ + if (mad16_sb_midi_mode == UART_MIDI) { + mad16_sb_reset_dsp(); + mad16_sb_intr_active = 0; + } + return 0; +} + +static int +mad16_sb_midi_ioctl(int dev, unsigned cmd, ioctl_arg arg) +{ + return -(EPERM); +} + +#define MIDI_SYNTH_NAME "pseudo-SoundBlaster Midi" +#define MIDI_SYNTH_CAPS 0 +#include <i386/isa/sound/midi_synth.h> + +static struct midi_operations mad16_sb_midi_operations = +{ + {"MAD16 (SBP mode)", 0, 0, SNDCARD_MAD16}, + &std_midi_synth, + {0}, + mad16_sb_midi_open, + mad16_sb_midi_close, + mad16_sb_midi_ioctl, + mad16_sb_midi_out, + mad16_sb_midi_start_read, + mad16_sb_midi_end_read, + NULL, /* Kick */ + NULL, /* command */ + NULL, /* buffer_status */ + NULL +}; + +static void +mad16_sb_midi_init(int model) +{ + if (num_midis >= MAX_MIDI_DEV) { + printf("Sound: Too many midi devices detected\n"); + return; + } + std_midi_synth.midi_dev = num_midis; + my_dev = num_midis; + midi_devs[num_midis++] = &mad16_sb_midi_operations; +} + +#endif diff --git a/sys/i386/isa/sound/maui.c b/sys/i386/isa/sound/maui.c new file mode 100644 index 0000000..e92b48a --- /dev/null +++ b/sys/i386/isa/sound/maui.c @@ -0,0 +1,225 @@ +/* + * sound/maui.c + * + * The low level driver for Turtle Beach Maui and Tropez. + * + * Copyright by Hannu Savolainen 1995 + * + * 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. + * + */ + +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include <i386/isa/sound/sound_config.h> + + +#if defined(CONFIG_MAUI) + +static int maui_base = 0x330; + +static volatile int irq_ok = 0; +static sound_os_info *maui_osp; + +#define HOST_DATA_PORT (maui_base + 2) +#define HOST_STAT_PORT (maui_base + 3) +#define HOST_CTRL_PORT (maui_base + 3) + +#define STAT_TX_INTR 0x40 +#define STAT_TX_AVAIL 0x20 +#define STAT_TX_IENA 0x10 +#define STAT_RX_INTR 0x04 +#define STAT_RX_AVAIL 0x02 +#define STAT_RX_IENA 0x01 + +static int (*orig_load_patch) (int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag) = NULL; + +static int +maui_read(void) +{ + int timeout; + + for (timeout = 0; timeout < 1000000; timeout++) + if (inb(HOST_STAT_PORT) & STAT_RX_AVAIL) + return inb(HOST_DATA_PORT); + + printf("Maui: Receive timeout\n"); + + return -1; +} + +static int +maui_write(u_char data) +{ + int timeout; + + for (timeout = 0; timeout < 10000000; timeout++) { + if (inb(HOST_STAT_PORT) & STAT_TX_AVAIL) { + outb(HOST_DATA_PORT, data); + return 1; + } + } + + printf("Maui: Write timeout\n"); + + return 0; +} + +void +mauiintr(int irq) +{ + irq_ok = 1; +} + + +int +maui_load_patch(int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag) +{ + + struct sysex_info header; + u_long left, src_offs; + int hdr_size = (u_long) &header.data[0] - (u_long) &header; + int i; + + if (format == SYSEX_PATCH) /* Handled by midi_synth.c */ + return orig_load_patch(dev, format, addr, offs, count, pmgr_flag); + + if (format != MAUI_PATCH) { + printf("Maui: Unknown patch format\n"); + } + if (count < hdr_size) { + printf("Maui error: Patch header too short\n"); + return -(EINVAL); + } + count -= hdr_size; + + /* + * Copy the header from user space but ignore the first bytes which + * have been transferred already. + */ + + if (uiomove(&((char *) &header)[offs], hdr_size - offs, addr)) { + printf("sb: Bad copyin()!\n"); + }; + + if (count < header.len) { + printf("Maui warning: Host command record too short (%d<%d)\n", + count, (int) header.len); + header.len = count; + } + left = header.len; + src_offs = 0; + + for (i = 0; i < left; i++) { + u_char data; + uiomove((char *) &(data), 1, addr); + if (i == 0 && !(data & 0x80)) + return -(EINVAL); + + if (maui_write(data) == -1) + return -(EIO); + } + + if ((i = maui_read()) != 0x80) { + if (i != -1) + printf("Maui: Error status %02x\n", i); + + return -(EIO); + } + return 0; +} + +int +probe_maui(struct address_info * hw_config) +{ + int i; + int tmp1, tmp2; + + maui_base = hw_config->io_base; + maui_osp = hw_config->osp; + + if (snd_set_irq_handler(hw_config->irq, mauiintr, maui_osp) < 0) + return 0; + + if (!maui_write(0xCF)) {/* Report hardware version */ + /* snd_release_irq(hw_config->irq); */ + return 0; + } + if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) { + /* snd_release_irq(hw_config->irq); */ + return 0; + } + printf("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; + printf("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); + } + printf("Available DRAM %dk\n", tmp1 / 1024); + + for (i = 0; i < 1000; i++) + if (probe_mpu401(hw_config)) + break; + + return probe_mpu401(hw_config); +} + +void +attach_maui(struct address_info * hw_config) +{ + int this_dev = num_midis; + + conf_printf("Maui", hw_config); + + hw_config->irq *= -1; + attach_mpu401(hw_config); + + if (num_midis > this_dev) { /* The MPU401 driver installed itself */ + struct synth_operations *synth; + + /* + * Intercept patch loading calls so that they canbe handled + * by the Maui driver. + */ + + synth = midi_devs[this_dev]->converter; + + if (synth != NULL) { + orig_load_patch = synth->load_patch; + synth->load_patch = &maui_load_patch; + } else + printf("Maui: Can't install patch loader\n"); + } + return; +} + +#endif diff --git a/sys/i386/isa/sound/midi_ctrl.h b/sys/i386/isa/sound/midi_ctrl.h new file mode 100644 index 0000000..8b68c7d --- /dev/null +++ b/sys/i386/isa/sound/midi_ctrl.h @@ -0,0 +1,22 @@ +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,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 16 to 23 */ + 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 24 to 31 */ + + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */ + + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */ + + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 96 to 103 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */ +}; diff --git a/sys/i386/isa/sound/midi_synth.c b/sys/i386/isa/sound/midi_synth.c new file mode 100644 index 0000000..e63b24a --- /dev/null +++ b/sys/i386/isa/sound/midi_synth.c @@ -0,0 +1,675 @@ +/* + * sound/midi_synth.c + * + * High level midi sequencer manager for dumb MIDI interfaces. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include <stddef.h> + +#include <i386/isa/sound/sound_config.h> + +#if defined(CONFIGURE_SOUNDCARD) /* && defined(CONFIG_MIDI) */ + +#define _MIDI_SYNTH_C_ + +static int *sysex_sleeper = NULL; +static volatile struct snd_wait sysex_sleep_flag = {0}; + +#include <i386/isa/sound/midi_synth.h> + +static int midi2synth[MAX_MIDI_DEV]; +static int sysex_state[MAX_MIDI_DEV] = +{0}; +static unsigned char prev_out_status[MAX_MIDI_DEV]; + +#ifndef CONFIG_SEQUENCER +#define STORE(cmd) +#else +#define STORE(cmd) { \ + int len; \ + unsigned char obuf[8]; \ + cmd; \ + seq_input_event(obuf, len); \ +} +#endif + +#define _seqbuf obuf +#define _seqbufptr 0 +#define _SEQ_ADVBUF(x) len=x + +void +do_midi_msg(int synthno, unsigned char *msg, int mlen) +{ + switch (msg[0] & 0xf0) { + case 0x90: + if (msg[2] != 0) { + STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + } + msg[2] = 64; + + case 0x80: + STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + + case 0xA0: + STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + + case 0xB0: + STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f, msg[1], msg[2])); + break; + + case 0xC0: + STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1])); + break; + + case 0xD0: + STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1])); + break; + + case 0xE0: + STORE(SEQ_BENDER(synthno, msg[0] & 0x0f, + (msg[1] % 0x7f) | ((msg[2] & 0x7f) << 7))); + break; + + default: + /* + * printf ("MPU: Unknown midi channel message %02x\n", + * msg[0]); + */ + ; + } +} + +static void +midi_outc(int midi_dev, int data) +{ + 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 */ + return; /* Mission complete */ + } + /* + * Sorry! No space on buffers. + */ + printf("Midi send timed out\n"); +} + +static int +prefix_cmd(int midi_dev, unsigned char status) +{ + if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL) + return 1; + + return midi_devs[midi_dev]->prefix_cmd(midi_dev, status); +} + +static void +midi_synth_input(int orig_dev, unsigned char data) +{ + int dev; + struct midi_input_info *inc; + + static unsigned char len_tab[] = /* # of data bytes following a status */ + { + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ + }; + + if (orig_dev < 0 || orig_dev > num_midis) + 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: + printf("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, + (int) data); + inc->m_state = MST_INIT; + } +} + +static void +leave_sysex(int dev) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int timeout = 0; + + if (!sysex_state[dev]) + return; + + sysex_state[dev] = 0; + + while (!midi_devs[orig_dev]->putc(orig_dev, 0xf7) && timeout < 1000) + timeout++; + + sysex_state[dev] = 0; +} + +static void +midi_synth_output(int dev) +{ + /* + * Currently NOP + */ +} + +int +midi_synth_ioctl(int dev, unsigned int cmd, ioctl_arg arg) +{ + /* + * int orig_dev = synth_devs[dev]->midi_dev; + */ + + switch (cmd) { + + case SNDCTL_SYNTH_INFO: + bcopy(synth_devs[dev]->info, &(((char *) arg)[0]), sizeof(struct synth_info)); + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + break; + + default: + return -(EINVAL); + } +} + +int +midi_synth_kill_note(int dev, int channel, int note, int velocity) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, chn; + + if (note < 0 || note > 127) + return 0; + if (channel < 0 || channel > 15) + return 0; + RANGE (velocity, 0, 127 ) ; + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80)) { /* Use running status */ + if (!prefix_cmd(orig_dev, note)) + return 0; + + midi_outc(orig_dev, note); + + if (msg == 0x90)/* Running status = Note on */ + midi_outc(orig_dev, 0); /* Note on with velocity 0 == note off */ + else + midi_outc(orig_dev, velocity); + } else { + if (velocity == 64) { + if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /* Note on */ + midi_outc(orig_dev, note); + midi_outc(orig_dev, 0); /* Zero G */ + } else { + if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0x80 | (channel & 0x0f)); /* Note off */ + midi_outc(orig_dev, note); + midi_outc(orig_dev, velocity); + } + } + + return 0; +} + +int +midi_synth_set_instr(int dev, int channel, int instr_no) +{ + int orig_dev = synth_devs[dev]->midi_dev; + + if (instr_no < 0 || instr_no > 127) + return 0; + if (channel < 0 || channel > 15) + return 0; + + leave_sysex(dev); + + if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0xc0 | (channel & 0x0f)); /* Program change */ + midi_outc(orig_dev, instr_no); + + return 0; +} + +int +midi_synth_start_note(int dev, int channel, int note, int velocity) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, chn; + + if (note < 0 || note > 127) + return 0; + if (channel < 0 || channel > 15) + return 0; + RANGE (velocity, 0, 127 ); + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (chn == channel && msg == 0x90) { /* Use running status */ + if (!prefix_cmd(orig_dev, note)) + return 0; + midi_outc(orig_dev, note); + midi_outc(orig_dev, velocity); + } else { + if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f))) + return 0; + midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /* Note on */ + midi_outc(orig_dev, note); + midi_outc(orig_dev, velocity); + } + return 0; +} + +void +midi_synth_reset(int dev) +{ + + leave_sysex(dev); +} + +int +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; + + flags = splhigh(); + inc->m_busy = 0; + inc->m_state = MST_INIT; + inc->m_ptr = 0; + inc->m_left = 0; + inc->m_prev_status = 0x00; + splx(flags); + + sysex_sleep_flag.aborting = 0; + sysex_sleep_flag.mode = WK_NONE; + + return 1; +} + +void +midi_synth_close(int dev) +{ + int orig_dev = synth_devs[dev]->midi_dev; + + leave_sysex(dev); + + /* + * Shut up the synths by sending just single active sensing message. + */ + midi_devs[orig_dev]->putc(orig_dev, 0xfe); + + midi_devs[orig_dev]->close(orig_dev); +} + +void +midi_synth_hw_control(int dev, unsigned char *event) +{ +} + +int +midi_synth_load_patch(int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag) +{ + 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 = offsetof(struct sysex_info, data); + + leave_sysex(dev); + + if (!prefix_cmd(orig_dev, 0xf0)) + return 0; + + if (format != SYSEX_PATCH) { + printf("MIDI Error: Invalid patch format (key) 0x%x\n", format); + return -(EINVAL); + } + if (count < hdr_size) { + printf("MIDI Error: Patch header too short\n"); + return -(EINVAL); + } + count -= hdr_size; + + /* + * Copy the header from user space but ignore the first bytes which + * have been transferred already. + */ + + + if (uiomove(&((char *) &sysex)[offs], hdr_size - offs, addr)) { + printf("sb: Bad copyin()!\n"); + }; + + if (count < sysex.len) { + printf("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.aborting = 0; + sysex_sleep_flag.mode = WK_NONE; + + for (i = 0; i < left && !(sysex_sleep_flag.aborting); i++) { + unsigned char data; + + uiomove((char *) &(data), 1, addr); + + eox_seen = (i > 0 && data & 0x80); /* End of sysex */ + + if (eox_seen && data != 0xf7) + data = 0xf7; + + if (i == 0) { + if (data != 0xf0) { + printf("Error: Sysex start missing\n"); + return -(EINVAL); + } + } + while (!midi_devs[orig_dev]->putc(orig_dev, + (unsigned char) (data & 0xff)) && + !(sysex_sleep_flag.aborting)) { + int chn; + + + sysex_sleeper = &chn; + DO_SLEEP(chn, sysex_sleep_flag, 1); + + }; /* Wait for timeout */ + + if (!first_byte && data & 0x80) + 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_aftertouch(int dev, int channel, int pressure) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, chn; + + if (pressure < 0 || pressure > 127) + return; + if (channel < 0 || channel > 15) + return; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + chn = prev_out_status[orig_dev] & 0x0f; + + if (msg != 0xd0 || chn != channel) { /* Test for running status */ + if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f))) + return; + midi_outc(orig_dev, 0xd0 | (channel & 0x0f)); /* Channel pressure */ + } else if (!prefix_cmd(orig_dev, pressure)) + return; + + midi_outc(orig_dev, pressure); +} + +void +midi_synth_controller(int dev, int channel, int ctrl_num, int value) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int chn, msg; + + if (ctrl_num < 1 || ctrl_num > 127) + return; /* NOTE! Controller # 0 ignored */ + 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 +midi_synth_patchmgr(int dev, struct patmgr_info * rec) +{ + return -(EINVAL); +} + +void +midi_synth_bender(int dev, int channel, int value) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int msg, prev_chn; + + if (channel < 0 || channel > 15) + return; + + if (value < 0 || value > 16383) + return; + + leave_sysex(dev); + + msg = prev_out_status[orig_dev] & 0xf0; + prev_chn = prev_out_status[orig_dev] & 0x0f; + + if (msg != 0xd0 || prev_chn != channel) { /* Test for running status */ + if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f))) + return; + midi_outc(orig_dev, 0xe0 | (channel & 0x0f)); + } else if (!prefix_cmd(orig_dev, value & 0x7f)) + return; + + midi_outc(orig_dev, value & 0x7f); + midi_outc(orig_dev, (value >> 7) & 0x7f); +} + +void +midi_synth_setup_voice(int dev, int voice, int channel) +{ +} + +int +midi_synth_send_sysex(int dev, unsigned char *bytes, int len) +{ + int orig_dev = synth_devs[dev]->midi_dev; + int i; + + for (i = 0; i < len; i++) { + switch (bytes[i]) { + case 0xf0: /* Start sysex */ + if (!prefix_cmd(orig_dev, 0xf0)) + return 0; + sysex_state[dev] = 1; + break; + + case 0xf7: /* End sysex */ + if (!sysex_state[dev]) /* Orphan sysex end */ + return 0; + sysex_state[dev] = 0; + break; + + default: + if (!sysex_state[dev]) + return 0; + + if (bytes[i] & 0x80) { /* Error. Another message before sysex end */ + bytes[i] = 0xf7; /* Sysex end */ + sysex_state[dev] = 0; + } + } + + if (!midi_devs[orig_dev]->putc(orig_dev, bytes[i])) { + /* + * Hardware leve buffer is full. Abort the sysex message. + */ + + int timeout = 0; + + bytes[i] = 0xf7; + sysex_state[dev] = 0; + + while (!midi_devs[orig_dev]->putc(orig_dev, bytes[i]) && + timeout < 1000) + timeout++; + } + if (!sysex_state[dev]) + return 0; + } + + return 0; +} +#endif diff --git a/sys/i386/isa/sound/midi_synth.h b/sys/i386/isa/sound/midi_synth.h new file mode 100644 index 0000000..80e627b --- /dev/null +++ b/sys/i386/isa/sound/midi_synth.h @@ -0,0 +1,49 @@ +int midi_synth_ioctl (int dev, + unsigned int cmd, ioctl_arg arg); +int midi_synth_kill_note (int dev, int channel, int note, int velocity); +int midi_synth_set_instr (int dev, int channel, int instr_no); +int midi_synth_start_note (int dev, int channel, int note, int volume); +void midi_synth_reset (int dev); +int midi_synth_open (int dev, int mode); +void midi_synth_close (int dev); +void midi_synth_hw_control (int dev, unsigned char *event); +int midi_synth_load_patch (int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag); +void midi_synth_panning (int dev, int channel, int pressure); +void midi_synth_aftertouch (int dev, int channel, int pressure); +void midi_synth_controller (int dev, int channel, int ctrl_num, int value); +int midi_synth_patchmgr (int dev, struct patmgr_info *rec); +void midi_synth_bender (int dev, int chn, int value); +void midi_synth_setup_voice (int dev, int voice, int chn); +int midi_synth_send_sysex(int dev, unsigned char *bytes,int len); + +#ifndef _MIDI_SYNTH_C_ +static struct synth_info std_synth_info = +{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS}; + +static struct synth_operations std_midi_synth = +{ + &std_synth_info, + 0, + SYNTH_TYPE_MIDI, + 0, + midi_synth_open, + midi_synth_close, + midi_synth_ioctl, + midi_synth_kill_note, + midi_synth_start_note, + midi_synth_set_instr, + midi_synth_reset, + midi_synth_hw_control, + midi_synth_load_patch, + midi_synth_aftertouch, + midi_synth_controller, + midi_synth_panning, + NULL, + midi_synth_patchmgr, + midi_synth_bender, + NULL, /* alloc_voice */ + midi_synth_setup_voice, + midi_synth_send_sysex +}; +#endif diff --git a/sys/i386/isa/sound/midibuf.c b/sys/i386/isa/sound/midibuf.c new file mode 100644 index 0000000..af5ddc5 --- /dev/null +++ b/sys/i386/isa/sound/midibuf.c @@ -0,0 +1,429 @@ +/* + * sound/midibuf.c + * + * Device file manager for /dev/midi# + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + + +#if defined(CONFIG_MIDI) + +/* + * Don't make MAX_QUEUE_SIZE larger than 4000 + */ + +#define MAX_QUEUE_SIZE 4000 +int +MIDIbuf_poll (int dev, struct fileinfo *file, int events, select_table * wait); + +static void +drain_midi_queue(int dev); + +static int *midi_sleeper[MAX_MIDI_DEV] = {NULL}; +static volatile struct snd_wait midi_sleep_flag[MAX_MIDI_DEV] = { {0}}; +static int *input_sleeper[MAX_MIDI_DEV] = {NULL}; +static volatile struct snd_wait input_sleep_flag[MAX_MIDI_DEV] = { {0}}; + +struct midi_buf { + int len, head, tail; + u_char queue[MAX_QUEUE_SIZE]; +}; + +struct midi_parms { + 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_parms parms[MAX_MIDI_DEV]; + +static void midi_poll(void *dummy); + +static volatile int open_devs = 0; + +#define DATA_AVAIL(q) (q->len) +#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len) + +#define QUEUE_BYTE(q, data) \ + if (SPACE_AVAIL(q)) { \ + u_long flags; \ + flags = splhigh(); \ + q->queue[q->tail] = (data); \ + q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \ + splx(flags); \ + } + +#define REMOVE_BYTE(q, data) \ + if (DATA_AVAIL(q)) { \ + u_long flags; \ + flags = splhigh(); \ + data = q->queue[q->head]; \ + q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \ + splx(flags); \ + } + +static void +drain_midi_queue(int dev) +{ + + /* + * Give the Midi driver time to drain its output queues + */ + + if (midi_devs[dev]->buffer_status != NULL) + while (!(PROCESS_ABORTING (midi_sleep_flag[dev])) && + midi_devs[dev]->buffer_status(dev)) { + int chn; + + midi_sleeper[dev] = &chn; + DO_SLEEP(chn, midi_sleep_flag[dev], hz / 10); + + }; +} + +static void +midi_input_intr(int dev, u_char data) +{ + if (midi_in_buf[dev] == NULL) + return; + + if (data == 0xfe) /* Active sensing */ + return; /* Ignore */ + + if (SPACE_AVAIL(midi_in_buf[dev])) { + QUEUE_BYTE(midi_in_buf[dev], data); + if ((input_sleep_flag[dev].mode & WK_SLEEP)) { + input_sleep_flag[dev].mode = WK_WAKEUP; + wakeup(input_sleeper[dev]); + }; + } +} + +static void +midi_output_intr(int dev) +{ + /* + * Currently NOP + */ +} + +static void +midi_poll(void *dummy) +{ + u_long flags; + int dev; + + flags = splhigh(); + 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].mode & WK_SLEEP)) { + midi_sleep_flag[dev].mode = WK_WAKEUP; + wakeup(midi_sleeper[dev]); + }; + } + timeout( midi_poll, 0, 1);; /* Come back later */ + } + splx(flags); +} + +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) { + printf("Sound: FATAL ERROR: Too many midi interfaces\n"); + num_midis = MAX_MIDI_DEV; + } + if (dev < 0 || dev >= num_midis) { + printf("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 *) malloc(sizeof(struct midi_buf), M_TEMP, M_WAITOK); + + if (midi_in_buf[dev] == NULL) { + printf("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 *) malloc(sizeof(struct midi_buf), M_TEMP, M_WAITOK); + + if (midi_out_buf[dev] == NULL) { + printf("midi: Can't allocate buffer\n"); + midi_devs[dev]->close(dev); + free(midi_in_buf[dev], M_TEMP); + 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].aborting = 0; + midi_sleep_flag[dev].mode = WK_NONE; + }; + { + input_sleep_flag[dev].aborting = 0; + input_sleep_flag[dev].mode = WK_NONE; + }; + + if (open_devs < 2) { /* This was first open */ + { + }; + + timeout( midi_poll, 0, 1);; /* Start polling */ + } + return err; +} + +void +MIDIbuf_release(int dev, struct fileinfo * file) +{ + int mode; + u_long flags; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + if (dev < 0 || dev >= num_midis) + return; + + flags = splhigh(); + + /* + * Wait until the queue is empty + */ + + if (mode != OPEN_READ) { + midi_devs[dev]->putc(dev, 0xfe); /* Active sensing to + * shut the devices */ + + while (!(PROCESS_ABORTING (midi_sleep_flag[dev])) && + DATA_AVAIL(midi_out_buf[dev])) { + int chn; + midi_sleeper[dev] = &chn; + DO_SLEEP(chn, midi_sleep_flag[dev], 0); + + }; /* Sync */ + + drain_midi_queue(dev); /* Ensure the output queues are empty */ + } + splx(flags); + + midi_devs[dev]->close(dev); + + free(midi_in_buf[dev], M_TEMP); + free(midi_out_buf[dev], M_TEMP); + midi_in_buf[dev] = NULL; + midi_out_buf[dev] = NULL; + if (open_devs < 2) { + }; + open_devs--; +} + +int +MIDIbuf_write(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) +{ + u_long flags; + int c, n, i; + u_char tmp_data; + + dev = dev >> 4; + + if (!count) + return 0; + + flags = splhigh(); + + c = 0; + + while (c < count) { + n = SPACE_AVAIL(midi_out_buf[dev]); + + if (n == 0) { /* No space just now. We have to sleep */ + + { + int chn; + + midi_sleeper[dev] = &chn; + DO_SLEEP(chn, midi_sleep_flag[dev], 0); + }; + + if (PROCESS_ABORTING(midi_sleep_flag[dev])) { + splx(flags); + return -(EINTR); + } + n = SPACE_AVAIL(midi_out_buf[dev]); + } + if (n > (count - c)) + n = count - c; + + for (i = 0; i < n; i++) { + + if (uiomove((char *) &tmp_data, 1, buf)) { + printf("sb: Bad copyin()!\n"); + }; + QUEUE_BYTE(midi_out_buf[dev], tmp_data); + c++; + } + } + + splx(flags); + + return c; +} + + +int +MIDIbuf_read(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) +{ + int n, c = 0; + u_long flags; + u_char tmp_data; + + dev = dev >> 4; + + flags = splhigh(); + + if (!DATA_AVAIL(midi_in_buf[dev])) { /* No data yet, wait */ + + { + int chn; + + + input_sleeper[dev] = &chn; + DO_SLEEP(chn, input_sleep_flag[dev], + parms[dev].prech_timeout); + + }; + if (PROCESS_ABORTING(input_sleep_flag[dev])) + 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) { + REMOVE_BYTE(midi_in_buf[dev], tmp_data); + + if (uiomove((char *) &tmp_data, 1, buf)) { + printf("sb: Bad copyout()!\n"); + }; + c++; + } + } + splx(flags); + + return c; +} + +int +MIDIbuf_ioctl(int dev, struct fileinfo * file, u_int cmd, ioctl_arg 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 + printf("/dev/midi%d: No coprocessor for this device\n", dev); + + return -(ENXIO); + } else + switch (cmd) { + + case SNDCTL_MIDI_PRETIME: + val = (int) (*(int *) arg); + if (val < 0) + val = 0; + + val = (hz * val) / 10; + parms[dev].prech_timeout = val; + return *(int *) arg = val; + break; + + default: + return midi_devs[dev]->ioctl(dev, cmd, arg); + } +} + +#ifdef ALLOW_POLL +int +MIDIbuf_poll (int dev, struct fileinfo *file, int events, select_table * wait) +{ + int revents = 0; + + dev = dev >> 4; + + if (events & (POLLIN | POLLRDNORM)) + if (!DATA_AVAIL (midi_in_buf[dev])) + selrecord(wait, &selinfo[dev]); + else + revents |= events & (POLLIN | POLLRDNORM); + + if (events & (POLLOUT | POLLWRNORM)) + if (SPACE_AVAIL (midi_out_buf[dev])) + selrecord(wait, &selinfo[dev]); + else + revents |= events & (POLLOUT | POLLWRNORM); + + return revents; +} + +#endif /* ALLOW_SELECT */ + + + +#endif diff --git a/sys/i386/isa/sound/mmap_test.c b/sys/i386/isa/sound/mmap_test.c new file mode 100644 index 0000000..c6afae9 --- /dev/null +++ b/sys/i386/isa/sound/mmap_test.c @@ -0,0 +1,265 @@ +/* + * This is a simple program which demonstrates use of mmapped DMA buffer + * of the sound driver directly from application program. + * + * This sample program works (currently) only with Linux, FreeBSD and BSD/OS + * (FreeBSD and BSD/OS require OSS version 3.8-beta16 or later. + * + * Note! Don't use mmapped DMA buffers (direct audio) unless you have + * very good reasons to do it. Programs using this feature will not + * work with all soundcards. GUS (GF1) is one of them (GUS MAX works). + * + * This program requires version 3.5-beta7 or later of OSS + * (3.8-beta16 or later in FreeBSD and BSD/OS). + */ + +#ifndef lint +static const char rcsid[] = + "$Id: mmap_test.c,v 1.2 1998/01/16 07:20:34 charnier Exp $"; +#endif /* not lint */ + +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <machine/soundcard.h> +#include <sys/time.h> + +int +main() +{ + int fd, sz, fsz, tmp, nfrag; + int caps; + + int sd, sl=0, sp; + + unsigned char data[500000], *dp = data; + + caddr_t buf; + struct timeval tim; + + unsigned char *op; + + struct audio_buf_info info; + + int frag = 0xffff000c; /* Max # fragments of 2^13=8k bytes */ + + fd_set writeset; + + close(0); + if ((fd=open("/dev/dsp", O_RDWR, 0))==-1) + err(1, "/dev/dsp"); +/* + * Then setup sampling parameters. Just sampling rate in this case. + */ + + tmp = 8000; + ioctl(fd, SNDCTL_DSP_SPEED, &tmp); + +/* + * Load some test data. + */ + + sl = sp = 0; + if ((sd=open("smpl", O_RDONLY, 0))!=-1) + { + sl = read(sd, data, sizeof(data)); + printf("%d bytes read from file.\n", sl); + close(sd); + } + else warn("smpl"); + + if (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps)==-1) + { + warn("sorry but your sound driver is too old"); + err(1, "/dev/dsp"); + } + +/* + * Check that the device has capability to do this. Currently just + * CS4231 based cards will work. + * + * The application should also check for DSP_CAP_MMAP bit but this + * version of driver doesn't have it yet. + */ +/* ioctl(fd, SNDCTL_DSP_SETSYNCRO, 0); */ + +/* + * You need version 3.5-beta7 or later of the sound driver before next + * two lines compile. There is no point to modify this program to + * compile with older driver versions since they don't have working + * mmap() support. + */ + if (!(caps & DSP_CAP_TRIGGER) || + !(caps & DSP_CAP_MMAP)) + errx(1, "sorry but your soundcard can't do this"); + +/* + * Select the fragment size. This is propably important only when + * the program uses select(). Fragment size defines how often + * select call returns. + */ + + ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag); + +/* + * Compute total size of the buffer. It's important to use this value + * in mmap() call. + */ + + if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info)==-1) + err(1, "GETOSPACE"); + + sz = info.fragstotal * info.fragsize; + fsz = info.fragsize; +/* + * Call mmap(). + * + * IMPORTANT NOTE!!!!!!!!!!! + * + * Full duplex audio devices have separate input and output buffers. + * It is not possible to map both of them at the same mmap() call. The buffer + * is selected based on the prot argument in the following way: + * + * - PROT_READ (alone) selects the input buffer. + * - PROT_WRITE (alone) selects the output buffer. + * - PROT_WRITE|PROT_READ together select the output buffer. This combination + * is required in BSD to make the buffer accessible. With just PROT_WRITE + * every attempt to access the returned buffer will result in segmentation/bus + * error. PROT_READ|PROT_WRITE is also permitted in Linux with OSS version + * 3.8-beta16 and later (earlier versions don't accept it). + * + * Non duplex devices have just one buffer. When an application wants to do both + * input and output it's recommended that the device is closed and re-opened when + * switching between modes. PROT_READ|PROT_WRITE can be used to open the buffer + * for both input and output (with OSS 3.8-beta16 and later) but the result may be + * unpredictable. + */ + + if ((buf=mmap(NULL, sz, PROT_WRITE | PROT_READ, MAP_FILE|MAP_SHARED, fd, 0))==(caddr_t)-1) + err(1, "mmap (write)"); + printf("mmap (out) returned %08x\n", buf); + op=buf; + +/* + * op contains now a pointer to the DMA buffer + */ + +/* + * Then it's time to start the engine. The driver doesn't allow read() and/or + * write() when the buffer is mapped. So the only way to start operation is + * to togle device's enable bits. First set them off. Setting them on enables + * recording and/or playback. + */ + + tmp = 0; + ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp); + +/* + * It might be usefull to write some data to the buffer before starting. + */ + + tmp = PCM_ENABLE_OUTPUT; + ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp); + +/* + * The machine is up and running now. Use SNDCTL_DSP_GETOPTR to get the + * buffer status. + * + * NOTE! The driver empties each buffer fragmen after they have been + * played. This prevents looping sound if there are some performance problems + * in the application side. For similar reasons it recommended that the + * application uses some amout of play ahead. It can rewrite the unplayed + * data later if necessary. + */ + + nfrag = 0; + while (1) + { + struct count_info count; + int extra; + + FD_ZERO(&writeset); + FD_SET(fd, &writeset); + + tim.tv_sec = 10; + tim.tv_usec= 0; + + select(fd+1, &writeset, &writeset, NULL, NULL); +/* + * SNDCTL_DSP_GETOPTR (and GETIPTR as well) return three items. The + * bytes field returns number of bytes played since start. It can be used + * as a real time clock. + * + * The blocks field returns number of fragment transitions (interrupts) since + * previous GETOPTR call. It can be used as a method to detect underrun + * situations. + * + * The ptr field is the DMA pointer inside the buffer area (in bytes from + * the beginning of total buffer area). + */ + + if (ioctl(fd, SNDCTL_DSP_GETOPTR, &count)==-1) + err(1, "GETOPTR"); + if (count.ptr < 0 ) count.ptr = 0; + nfrag += count.blocks; + + +#ifdef VERBOSE + + printf("\rTotal: %09d, Fragment: %03d, Ptr: %06d", + count.bytes, nfrag, count.ptr); + fflush(stdout); +#endif + +/* + * Caution! This version doesn't check for bounds of the DMA + * memory area. It's possible that the returned pointer value is not aligned + * to fragment boundaries. It may be several samples behind the boundary + * in case there was extra delay between the actual hardware interrupt and + * the time when DSP_GETOPTR was called. + * + * Don't just call memcpy() with length set to 'fragment_size' without + * first checking that the transfer really fits to the buffer area. + * A mistake of just one byte causes seg fault. It may be easiest just + * to align the returned pointer value to fragment boundary before using it. + * + * It would be very good idea to write few extra samples to next fragment + * too. Otherwise several (uninitialized) samples from next fragment + * will get played before your program gets chance to initialize them. + * Take in count the fact thaat there are other processes batling about + * the same CPU. This effect is likely to be very annoying if fragment + * size is decreased too much. + */ + +/* + * Just a minor clarification to the above. The following line alings + * the pointer to fragment boundaries. Note! Don't trust that fragment + * size is always a power of 2. It may not be so in future. + */ + count.ptr = ((count.ptr+16)/fsz )*fsz; +#ifdef VERBOSE + printf(" memcpy(%6d, %4d)", (dp-data), fsz); + fflush(stdout); +#endif + +/* + * Set few bytes in the beginning of next fragment too. + */ + + if ((count.ptr+fsz+16) < sz) /* Last fragment? */ + extra = 16; + else + extra = 0; + memcpy(op+count.ptr, dp, (fsz+extra)); + dp += fsz; + if (dp > (data+sl-fsz)) + dp = data; + + } + + exit(0); +} diff --git a/sys/i386/isa/sound/mpu401.c b/sys/i386/isa/sound/mpu401.c new file mode 100644 index 0000000..0bd1412 --- /dev/null +++ b/sys/i386/isa/sound/mpu401.c @@ -0,0 +1,1631 @@ +/* + * sound/mpu401.c + * + * The low level driver for Roland MPU-401 compatible Midi cards. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + * Modified: Riccardo Facchetti 24 Mar 1995 - Added the Audio Excel DSP 16 + * initialization routine. + */ + +#define USE_SEQ_MACROS +#define USE_SIMPLE_MACROS + +#include <i386/isa/sound/sound_config.h> + +#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) +#include <i386/isa/sound/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; + +#endif + +struct mpu_config { + int base; /* I/O base */ + int irq; + int opened; /* Open mode */ + int devno; + int synthno; + int uart_mode; + int initialized; + int mode; +#define MODE_MIDI 1 +#define MODE_SYNTH 2 + u_char version, revision; + u_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; + +#define MBUF_MAX 10 +#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ + {printf("MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} + int m_busy; + u_char m_buf[MBUF_MAX]; + int m_ptr; + int m_state; + int m_left; + u_char last_status; + void (*inputintr) (int dev, u_char data); + int shared_irq; + sound_os_info *osp; +}; + +#define DATAPORT(base) (base) +#define COMDPORT(base) (base+1) +#define STATPORT(base) (base+1) + +#define mpu401_status(devc) inb( STATPORT(devc->base)) +#define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL)) +#define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY)) +#define write_command(devc, cmd) outb( COMDPORT(devc->base), cmd) +#define read_data(devc) inb( DATAPORT(devc->base)) + +#define write_data(devc, byte) outb( DATAPORT(devc->base), byte) + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define MPU_RESET 0xFF +#define UART_MODE_ON 0x3F + +static struct mpu_config dev_conf[MAX_MIDI_DEV] = { {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 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 struct synth_info mpu_synth_info_proto = +{"MPU-401 MIDI interface", 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, SYNTH_CAP_INPUT}; + +static struct synth_info mpu_synth_info[MAX_MIDI_DEV]; + +/* + * States for the input scanner + */ + +#define ST_INIT 0 /* Ready for timing byte or msg */ +#define ST_TIMED 1 /* Leading timing byte rcvd */ +#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */ + +#define ST_SYSMSG 100 /* System message (sysx etc). */ +#define ST_SYSEX 101 /* System exclusive msg */ +#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */ +#define ST_SONGSEL 103 /* Song select */ +#define ST_SONGPOS 104 /* Song position pointer */ + +static u_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 */ +}; + +#ifndef CONFIG_SEQUENCER +#define STORE(cmd) +#else +#define STORE(cmd) \ +{ \ + int len; \ + u_char obuf[8]; \ + cmd; \ + seq_input_event(obuf, len); \ +} +#endif + +#define _seqbuf obuf +#define _seqbufptr 0 +#define _SEQ_ADVBUF(x) len=x + +static int +mpu_input_scanner(struct mpu_config * devc, u_char midic) +{ + + switch (devc->m_state) { + case ST_INIT: + switch (midic) { + case 0xf8: + /* Timer overflow */ + break; + + case 0xfc: + printf("<all end>"); + 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: + printf("<Trk data rq #%d>", midic & 0x0f); + break; + + case 0xf9: + printf("<conductor rq>"); + break; + + case 0xff: + devc->m_state = ST_SYSMSG; + break; + + default: + if (midic <= 0xef) { + /* printf("mpu time: %d ", midic); */ + devc->m_state = ST_TIMED; + } else + printf("<MPU: Unknown event %02x> ", midic); + } + break; + + case ST_TIMED: + { + int msg = ((int) (midic & 0xf0) >> 4); + + devc->m_state = ST_DATABYTE; + + if (msg < 8) { /* Data byte */ + /* printf("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: + /* printf("NOP "); */ + break; + + case 0xf9: + /* printf("meas end "); */ + break; + + case 0xfc: + /* printf("data end "); */ + break; + + default: + printf("Unknown MPU mark %02x\n", midic); + } + } else { + devc->last_status = midic; + /* printf ("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: + printf("<SYX>"); + 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: + /* printf("tune_request\n"); */ + devc->m_state = ST_INIT; + /* XXX do we need a break here ? - lr 970710 */ + + /* + * 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: + /* printf("midi hard reset"); */ + devc->m_state = ST_INIT; + break; + + default: + printf("unknown MIDI sysmsg %0x\n", midic); + devc->m_state = ST_INIT; + } + break; + + case ST_MTC: + devc->m_state = ST_INIT; + printf("MTC frame %x02\n", midic); + break; + + case ST_SYSEX: + if (midic == 0xf7) { + printf("<EOX>"); + devc->m_state = ST_INIT; + } else + printf("%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: + printf("Bad state %d ", devc->m_state); + devc->m_state = ST_INIT; + } + + return 1; +} + +static void +mpu401_input_loop(struct mpu_config * devc) +{ + u_long flags; + int n, busy; + + flags = splhigh(); + busy = devc->m_busy; + devc->m_busy = 1; + splx(flags); + + if (busy) /* Already inside the scanner */ + return; + + n = 50; + + while (input_avail(devc) && n-- > 0) { + u_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); + } + + devc->m_busy = 0; +} + +void +mpuintr(int irq) +{ + struct mpu_config *devc; + int dev; + + /* + * FreeBSD (and some others) pass unit number to the interrupt + * handler. In this case we have to scan the table for first handler. + */ + + 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) { + printf("MPU-401: Bogus interrupt #%d?\n", origirq); + return; + } + 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, u_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) { + printf("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 */ + printf("MPU-401: Device not initialized properly\n"); + return -(EIO); + } + reset_mpu401(devc); + } + irq2dev[devc->irq] = dev; + + if (midi_devs[dev]->coproc) + if ((err = midi_devs[dev]->coproc-> + open(midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0) { + printf("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); + + devc->inputintr = input; + devc->opened = mode; + + return 0; +} + +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; + + devc->inputintr = NULL; + + 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, u_char midi_byte) +{ + int timeout; + u_long flags; + + struct mpu_config *devc; + + devc = &dev_conf[dev]; + + /* + * Sometimes it takes about 13000 loops before the output becomes + * ready (After reset). Normally it takes just about 10 loops. + */ + + for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--); + + flags = splhigh(); + if (!output_ready(devc)) { + printf("MPU-401: Send data timeout\n"); + splx(flags); + return 0; + } + write_data(devc, midi_byte); + splx(flags); + return 1; +} + +static int +mpu401_command(int dev, mpu_command_rec * cmd) +{ + int i, timeout, ok; + int ret = 0; + u_long flags; + struct mpu_config *devc; + + devc = &dev_conf[dev]; + + if (devc->uart_mode) { /* Not possible in UART mode */ + printf("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 30000 loops before the output becomes + * ready (After reset). Normally it takes just about 10 loops. + */ + + timeout = 30000; +retry: + if (timeout-- <= 0) { + printf("MPU-401: Command (0x%x) timeout\n", (int) cmd->cmd); + return -(EIO); + } + flags = splhigh(); + + if (!output_ready(devc)) { + splx(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 simplier method */ + if (read_data(devc) == MPU_ACK) + ok = 1; + } + + if (!ok) { + splx(flags); + /* printf ("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])) { + splx(flags); + printf("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 (!ok) { + splx(flags); + /* printf ("MPU: No response(%d) to command (0x%x)\n", + * i, (int) cmd->cmd); + */ + return -(EIO); + } + } + + splx(flags); + + return ret; +} + +static int +mpu_cmd(int dev, int cmd, int data) +{ + int ret; + + 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; + + if ((ret = mpu401_command(dev, &rec)) < 0) { + return ret; + } + return (u_char) rec.data[0]; +} + +static int +mpu401_prefix_cmd(int dev, u_char status) +{ + struct mpu_config *devc = &dev_conf[dev]; + + if (devc->uart_mode) + return 1; + + if (status < 0xf0) { + if (mpu_cmd(dev, 0xD0, 0) < 0) { + return 0; + } + return 1; + } + switch (status) { + case 0xF0: + if (mpu_cmd(dev, 0xDF, 0) < 0) { + return 0; + } + return 1; + break; + + default: + return 0; + } + +} + +static int +mpu401_start_read(int dev) +{ + return 0; +} + +static int +mpu401_end_read(int dev) +{ + return 0; +} + +static int +mpu401_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + struct mpu_config *devc; + + devc = &dev_conf[dev]; + + switch (cmd) { + case 1: + bcopy(&(((char *) arg)[0]), (char *) init_sequence, sizeof(init_sequence)); + return 0; + break; + + case SNDCTL_MIDI_MPUMODE: + if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */ + printf("MPU-401: Intelligent mode not supported by the HW\n"); + return -(EINVAL); + } + set_uart_mode(dev, devc, !(*(int *) arg)); + return 0; + break; + + case SNDCTL_MIDI_MPUCMD: + { + int ret; + mpu_command_rec rec; + + bcopy(&(((char *) arg)[0]), (char *) &rec, sizeof(rec)); + + if ((ret = mpu401_command(dev, &rec)) < 0) + return ret; + bcopy((char *) &rec, &(((char *) arg)[0]), sizeof(rec)); + return 0; + } + break; + + default: + return -(EINVAL); + } +} + +static void +mpu401_kick(int dev) +{ +} + +static int +mpu401_buffer_status(int dev) +{ + return 0; /* No data in buffers */ +} + +static int +mpu_synth_ioctl(int dev, + u_int cmd, ioctl_arg arg) +{ + int midi_dev; + struct mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis) + return -(ENXIO); + + devc = &dev_conf[midi_dev]; + + switch (cmd) { + + case SNDCTL_SYNTH_INFO: + bcopy(&mpu_synth_info[midi_dev], &(((char *) arg)[0]), sizeof(struct synth_info)); + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return 0x7fffffff; + break; + + default: + return -(EINVAL); + } +} + +static int +mpu_synth_open(int dev, int mode) +{ + int midi_dev, err; + struct mpu_config *devc; + + midi_dev = synth_devs[dev]->midi_dev; + + if (midi_dev < 0 || midi_dev > num_midis) { + return -(ENXIO); + } + 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. + */ + + if (!devc->initialized) { + if (mpu401_status(devc) == 0xff) { /* Bus float */ + printf("MPU-401: Device not initialized properly\n"); + return -(EIO); + } + reset_mpu401(devc); + } + if (devc->opened) { + printf("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) { + printf("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 */ + } + return 0; +} + +static void +mpu_synth_close(int dev) +{ + int midi_dev; + struct mpu_config *devc; + + 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->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; +} + +#define MIDI_SYNTH_NAME "MPU-401 UART Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include <i386/isa/sound/midi_synth.h> + +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 +}; + +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 +}; + +static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV]; + +static void +mpu401_chk_version(struct mpu_config * devc) +{ + int tmp; + + devc->version = devc->revision = 0; + + if ((tmp = mpu_cmd(num_midis, 0xAC, 0)) < 0) + return; + + if ((tmp & 0xf0) > 0x20)/* Why is it larger than 2.x ??? */ + return; + + devc->version = tmp; + + if ((tmp = mpu_cmd(num_midis, 0xAD, 0)) < 0) { + devc->version = 0; + return; + } + devc->revision = tmp; +} + +void +attach_mpu401(struct address_info * hw_config) +{ + u_long flags; + char revision_char; + + struct mpu_config *devc; + + if (num_midis >= MAX_MIDI_DEV) { + printf("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)) + return ; + + if (!devc->shared_irq) + if (snd_set_irq_handler(devc->irq, mpuintr, devc->osp) < 0) { + return ; + } + flags = splhigh(); + mpu401_chk_version(devc); + if (devc->version == 0) + mpu401_chk_version(devc); + splx(flags); + }; + + 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 *) malloc(sizeof(struct synth_operations), M_DEVBUF, M_NOWAIT); + + if (!mpu401_synth_operations[num_midis]) + panic("SOUND: Cannot allocate memory\n"); + + if (mpu401_synth_operations[num_midis] == NULL) { + printf("mpu401: Can't allocate memory\n"); + return ; + } + if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */ + bcopy((char *) &std_midi_synth, (char *) mpu401_synth_operations[num_midis], sizeof(struct synth_operations)); + } else { + bcopy((char *) &mpu401_synth_proto, (char *) mpu401_synth_operations[num_midis], sizeof(struct synth_operations)); + } + + bcopy((char *) &mpu401_midi_proto, (char *) &mpu401_midi_operations[num_midis], sizeof(struct midi_operations)); + + mpu401_midi_operations[num_midis].converter = + mpu401_synth_operations[num_midis]; + + bcopy((char *) &mpu_synth_info_proto, (char *) &mpu_synth_info[num_midis], 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' : ' '; + snprintf(mpu_synth_info[num_midis].name, + sizeof(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; + + snprintf(mpu_synth_info[num_midis].name, + sizeof(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]; + return ; +} + +static int +reset_mpu401(struct mpu_config * devc) +{ + u_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--) { + flags = splhigh(); + if ( (input_avail(devc)) && (read_data(devc) == MPU_ACK) ) + ok = 1; + splx(flags); + } + + } + + devc->m_state = ST_INIT; + devc->m_ptr = 0; + devc->m_left = 0; + devc->last_status = 0; + devc->uart_mode = 0; + + 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 && (mpu_cmd(dev, UART_MODE_ON, 0) < 0) ) { + printf("MPU%d: Can't enter UART mode\n", devc->devno); + devc->uart_mode = 0; + return; + } + devc->uart_mode = arg; +} + +int +probe_mpu401(struct address_info * hw_config) +{ + int ok = 0; + struct mpu_config tmp_devc; + + 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 defined(CONFIG_AEDSP16) && defined(AEDSP16_MPU401) + /* + * Initialize Audio Excel DSP 16 to MPU-401, before any operation. + */ + InitAEDSP16_MPU401(hw_config); +#endif + + if (hw_config->always_detect) + return 1; + + if (inb(hw_config->io_base + 1) == 0xff) { + DDB(printf("MPU401: Port %x looks dead.\n", hw_config->io_base)); + return 0; /* Just bus float? */ + } + ok = reset_mpu401(&tmp_devc); + + if (!ok) { + DDB(printf("MPU401: Reset failed on port %x\n", hw_config->io_base)); + } + return ok; +} + + +/* + * Timer stuff + */ + +#if defined(CONFIG_SEQUENCER) + +static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0; +static volatile int curr_tempo, curr_timebase, hw_timebase; +static int max_timebase = 8; /* 8*24=192 ppqn */ +static volatile u_long next_event_time; +static volatile u_long curr_ticks, curr_clocks; +static u_long prev_event_time; +static int metronome_mode; + +static u_long +clocks2ticks(u_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; +} + +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) { + printf("MPU: 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) +{ + u_long flags; + + flags = splhigh(); + next_event_time = 0xffffffff; + prev_event_time = 0; + curr_ticks = curr_clocks = 0; + splx(flags); +} + +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)) { + 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 */ + } +} + +static void +stop_metronome(int midi_dev) +{ + mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ +} + +static void +setup_metronome(int midi_dev) +{ + int numerator, denominator; + int clks_per_click, num_32nds_per_beat; + int beats_per_measure; + + numerator = ((u_int) metronome_mode >> 24) & 0xff; + denominator = ((u_int) metronome_mode >> 16) & 0xff; + clks_per_click = ((u_int) metronome_mode >> 8) & 0xff; + num_32nds_per_beat = (u_int) 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) +{ + 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) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + 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); + + mpu_cmd(midi_dev, 0xe7, 0x04); /* Send all clocks to host */ + mpu_cmd(midi_dev, 0x95, 0); /* Enable clock to host */ + + return 0; +} + +static void +mpu_timer_close(int dev) +{ + 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); +} + +static int +mpu_timer_event(int dev, u_char *event) +{ + u_char command = event[1]; + u_long parm = *(u_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) + printf("MPU: 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:; + } + + return TIMER_NOT_ARMED; +} + +static u_long +mpu_timer_get_time(int dev) +{ + if (!timer_open) + return 0; + + return curr_ticks; +} + +static int +mpu_timer_ioctl(int dev, u_int command, ioctl_arg arg) +{ + int midi_dev = sound_timer_devs[dev]->devlink; + + switch (command) { + case SNDCTL_TMR_SOURCE: + { + int parm = (int) (*(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 *(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) (*(int *) arg); + + if (val) + set_timebase(midi_dev, val); + + return *(int *) arg = curr_timebase; + } + break; + + case SNDCTL_TMR_TEMPO: + { + int val = (int) (*(int *) arg); + int ret; + + if (val) { + RANGE (val, 8 , 250 ); + if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0) { + printf("MPU: Can't set tempo to %d\n", (int) val); + return ret; + } + curr_tempo = val; + } + return *(int *) arg = curr_tempo; + } + break; + + case SNDCTL_SEQ_CTRLRATE: + if ((*(int *) arg) != 0) /* Can't change */ + return -(EINVAL); + + return *(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60; + break; + + case SNDCTL_TMR_METRONOME: + metronome_mode = (int) (*(int *) arg); + setup_metronome(midi_dev); + return 0; + break; + + default:; + } + + return -(EINVAL); +} + +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; +} + +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 +}; + +static void +mpu_timer_interrupt(void) +{ + + if (!timer_open) + return; + + if (!tmr_running) + return; + + curr_clocks++; + curr_ticks = clocks2ticks(curr_clocks); + + if (curr_ticks >= next_event_time) { + next_event_time = 0xffffffff; + sequencer_timer(0); + } +} + +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: + printf("<MIDI clk>"); + break; + + case TMR_START: + printf("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: + printf("Ext MIDI stop\n"); + if (timer_mode & TMR_EXTERNAL) { + tmr_running = 0; + stop_metronome(midi_dev); + STORE(SEQ_STOP_TIMER()); + } + break; + + case TMR_CONTINUE: + printf("Ext MIDI continue\n"); + if (timer_mode & TMR_EXTERNAL) { + tmr_running = 1; + setup_metronome(midi_dev); + STORE(SEQ_CONTINUE_TIMER()); + } + break; + + case TMR_SPP: + printf("Songpos: %d\n", parm); + if (timer_mode & TMR_EXTERNAL) { + STORE(SEQ_SONGPOS(parm)); + } + break; + } +} + +static void +mpu_timer_init(int midi_dev) +{ + struct mpu_config *devc; + int n; + + devc = &dev_conf[midi_dev]; + + if (timer_initialized) + return; /* There is already a similar timer */ + + timer_initialized = 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; + + 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 & 0x02) + timer_caps |= TMR_MODE_CLS; + + + if (devc->revision & 0x40) + max_timebase = 10; /* Has the 216 and 240 ppqn modes */ + } + timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps; +} + +#endif + +#endif diff --git a/sys/i386/isa/sound/opl3.c b/sys/i386/isa/sound/opl3.c new file mode 100644 index 0000000..1baeadd --- /dev/null +++ b/sys/i386/isa/sound/opl3.c @@ -0,0 +1,1132 @@ +/* + * sound/opl3.c + * + * A low level driver for Yamaha YM3812 and OPL-3 -chips + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +/* + * Major improvements to the FM handling 30AUG92 by Rob Hooft, + */ +/* + * hooft@chem.ruu.nl + */ +#include <i386/isa/sound/sound_config.h> + + +#if defined(CONFIG_YM3812) + +#include <i386/isa/sound/opl3.h> +#include <machine/clock.h> + +#define MAX_VOICE 18 +#define OFFS_4OP 11 + +struct voice_info { + u_char keyon_byte; + long bender; + long bender_range; + u_long orig_freq; + u_long current_freq; + int volume; + int mode; +}; + +typedef struct opl_devinfo { + 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 sbi_instrument i_map[SBFM_MAXINSTR]; + struct sbi_instrument *act_i[MAX_VOICE]; + + struct synth_info fm_info; + + int busy; + int model; + u_char cmask; + + int is_opl4; + sound_os_info *osp; +} + opl_devinfo; + +static struct opl_devinfo *devc = NULL; + + +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, u_int addr, u_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) +{ + /* NOP */ +} + +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; +} + +static int +opl3_ioctl(int dev, + u_int cmd, ioctl_arg arg) +{ + switch (cmd) { + + case SNDCTL_FM_LOAD_INSTR: + { + struct sbi_instrument ins; + + bcopy(&(((char *) arg)[0]), (char *) &ins, sizeof(ins)); + + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { + printf("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; + bcopy(&devc->fm_info, &(((char *) arg)[0]), 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); + } + +} + +int +opl3_detect(int ioaddr, sound_os_info * 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. + */ + + u_char stat1, stat2, signature; + int i; + + if (devc != NULL) + return 0; + + devc = (struct opl_devinfo *) malloc(sizeof(*devc), M_DEVBUF, M_NOWAIT); + if (!devc) + panic("SOUND: Cannot allocate memory\n"); + + if (devc == NULL) { + printf("OPL3: Can't allocate memory for device control structure\n"); + return 0; + } + devc->osp = osp; + + /* 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 */ + + + DELAY(150); /* Now we have to delay at least 80 usec */ + + 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 */ + } + /* + * There is a FM chicp in this address. Detect the type (OPL2 to + * OPL4) + */ + + if (signature == 0x06) {/* OPL2 */ + detected_model = 2; + } else if (signature == 0x00) { /* OPL3 or OPL4 */ + u_char tmp; + + detected_model = 3; + + /* + * Detect availability of OPL4 (_experimental_). Works + * propably 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 (!0) { /* OPL4 port is free */ /* XXX check here lr970711 */ + int tmp; + + outb(ioaddr - 8, 0x02); /* Select OPL4 ID register */ + DELAY(10); + tmp = inb(ioaddr - 7); /* Read it */ + DELAY(10); + + if (tmp == 0x20) { /* OPL4 should return 0x20 here */ + detected_model = 4; + + outb(ioaddr - 8, 0xF8); /* Select OPL4 FM mixer control */ + DELAY(10); + outb(ioaddr - 7, 0x1B); /* Write value */ + DELAY(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, TEST_REGISTER, ENABLE_WAVE_SELECT); + opl3_command(ioaddr, PERCUSSION_REGISTER, 0x00); /* Melodic mode. */ + + return 1; +} + +static int +opl3_kill_note(int dev, int voice, int note, int velocity) +{ + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return 0; + + devc->v_alloc->map[voice] = 0; + + map = &pv_map[devc->lv_map[voice]]; + + DEB(printf("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; + + return 0; +} + +#define HIHAT 0 +#define CYMBAL 1 +#define TOMTOM 2 +#define SNARE 3 +#define BDRUM 4 +#define UNDEFINED TOMTOM +#define DEFAULT TOMTOM + +static int +store_instr(int instr_no, struct sbi_instrument * instr) +{ + + if (instr->key !=FM_PATCH && (instr->key !=OPL3_PATCH || devc->model != 2)) + printf("FM warning: Invalid patch format field (key) 0x%x\n", + instr->key); + bcopy((char *) instr, (char *) &(devc->i_map[instr_no]), sizeof(*instr)); + + return 0; +} + +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; + + devc->act_i[voice] = &devc->i_map[instr_no]; + return 0; +} + +/* + * The next table looks magical, but it certainly is not. Its values have + * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception + * for i=0. This log-table converts a linear volume-scaling (0..127) to a + * logarithmic scaling as present in the FM-synthesizer chips. so : Volume + * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative + * volume -8 it was implemented as a table because it is only 128 bytes and + * it saves a lot of log() calculations. (RH) + */ +static 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(u_char *regbyte, int volume, int main_vol) +{ + int level = (~*regbyte & 0x3f); + + if (main_vol > 127) + main_vol = 127; + + volume = (volume * main_vol) / 127; + + if (level) + level += fm_volume_table[volume]; + + RANGE (level, 0, 0x3f ); + + *regbyte = (*regbyte & 0xc0) | (~level & 0x3f); +} + +static void +set_voice_volume(int voice, int volume, int main_vol) +{ + u_char vol1, vol2, vol3, vol4; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return; + + map = &pv_map[devc->lv_map[voice]]; + + instr = devc->act_i[voice]; + + if (!instr) + instr = &devc->i_map[0]; + + if (instr->channel < 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)) { + calc_vol(&vol1, volume, main_vol); + } + 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:; + } + + 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) +{ + u_char data, fpc; + int block, fnum, freq, voice_mode; + struct sbi_instrument *instr; + struct physical_voice_info *map; + + if (voice < 0 || voice >= devc->nr_voice) + return 0; + + map = &pv_map[devc->lv_map[voice]]; + + if (map->voice_mode == 0) + return 0; + + 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 */ + + if (map->voice_mode == 4) { + opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff); + opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff); + } + opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* Note off */ + + instr = devc->act_i[voice]; + + if (!instr) + instr = &devc->i_map[0]; + + if (instr->channel < 0) { + printf( "OPL3: Initializing voice %d with undefined instrument\n", + voice); + return 0; + } + if (map->voice_mode == 2 && instr->key == OPL3_PATCH) + return 0; /* Cannot play */ + + voice_mode = map->voice_mode; + + if (voice_mode == 4) { + int voice_shift; + + voice_shift = (map->ioaddr == 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); + } else + devc->cmask |= (1 << voice_shift); + + 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; /* 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); + } + 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; + + /* + * 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_to_fnum(freq, &block, &fnum); + + /* + * Play note + */ + + data = fnum & 0xff; /* Least significant bits of fnumber */ + opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); + + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); + 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; +} + +static void +freq_to_fnum(int freq, int *block, int *fnum) +{ + int f, octave; + + /* + * Converts the note frequency to block and fnum values for the FM + * chip + */ + /* + * First try to compute the block -value (octave) where the note + * belongs + */ + + f = freq; + + octave = 5; + + if (f == 0) + octave = 0; + else if (f < 261) { + while (f < 261) { + octave--; + f <<= 1; + } + } else if (f > 493) { + while (f > 493) { + octave++; + f >>= 1; + } + } + if (octave > 7) + octave = 7; + + *fnum = freq * (1 << (20 - octave)) / 49716; + *block = octave; +} + +static void +opl3_command(int io_addr, u_int addr, u_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(io_addr, (u_char) (addr & 0xff)); + + if (!devc->model != 2) + DELAY(10); + else + for (i = 0; i < 2; i++) + inb(io_addr); + + outb(io_addr + 1, (u_char) (val & 0xff)); + + if (devc->model != 2) + DELAY(30); + else + for (i = 0; i < 2; i++) + inb(io_addr); +} + +static void +opl3_reset(int dev) +{ + int i; + + for (i = 0; i < 18; i++) + devc->lv_map[i] = i; + + 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); + + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff); + + 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); + + opl3_command(pv_map[devc->lv_map[i]].ioaddr, + KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff); + } + opl3_kill_note(dev, 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) +{ + int i; + + if (devc->busy) + return -(EBUSY); + devc->busy = 1; + + 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; + } + + devc->cmask = 0x00; /* Just 2 OP mode */ + if (devc->model == 2) + opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); + return 0; +} + +static void +opl3_close(int dev) +{ + 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; + + opl3_reset(dev); +} + +static void +opl3_hw_control(int dev, u_char *event) +{ +} + +static int +opl3_load_patch(int dev, int format, snd_rw_buf * addr, + int offs, int count, int pmgr_flag) +{ + struct sbi_instrument ins; + + if (count < sizeof(ins)) { + printf("FM Error: Patch record too short\n"); + return -(EINVAL); + } + if (uiomove(&((char *) &ins)[offs], sizeof(ins) - offs, addr)) { + printf("sb: Bad copyin()!\n"); + }; + + if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { + printf("FM Error: Invalid instrument number %d\n", ins.channel); + return -(EINVAL); + } + ins.key = format; + + return store_instr(ins.channel, &ins); +} + +static void +opl3_panning(int dev, int voice, int pressure) +{ +} + +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);} + +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; + + map = &pv_map[devc->lv_map[voice]]; + + DEB(printf("Aftertouch %d\n", voice)); + + if (map->voice_mode == 0) + return; + + /* + * Adjust the amount of vibrato depending the pressure + */ + + instr = devc->act_i[voice]; + + if (!instr) + instr = &devc->i_map[0]; + + 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 { + 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) +{ + u_char data; + int block, fnum, freq; + struct physical_voice_info *map; + + map = &pv_map[devc->lv_map[voice]]; + + 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 */ + + freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range); + devc->voc[voice].current_freq = freq; + + freq_to_fnum(freq, &block, &fnum); + + data = fnum & 0xff; /* Least significant bits of fnumber */ + opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); + + data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); + /* KEYON|OCTAVE|MS bits of f-num */ + devc->voc[voice].keyon_byte = data; + opl3_command(map->ioaddr, KEYON_BLOCK + 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; + } +} + +static int +opl3_patchmgr(int dev, struct patmgr_info * rec) +{ + return -(EINVAL); +} + +static void +opl3_bender(int dev, int voice, int value) +{ + if (voice < 0 || voice >= devc->nr_voice) + return; + + bend_pitch(dev, voice, value - 8192); +} + +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) { + 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. + */ + + 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) +{ + struct channel_info *info = + &synth_devs[dev]->chn_info[chn]; + + opl3_set_instr(dev, voice, info->pgm_num); + + devc->voc[voice].bender = info->bender_value; + devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME]; +} + +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 +}; + +void +opl3_init(int ioaddr, sound_os_info * osp) +{ + int i; + + if (num_synths >= MAX_SYNTH_DEV) { + printf("OPL3 Error: Too many synthesizers\n"); + return ; + } + if (devc == NULL) { + printf("OPL3: Device control structure not initialized.\n"); + return ; + } + bzero((char *) devc, sizeof(*devc)); + devc->osp = osp; + + 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.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 < 18; i++) + pv_map[i].ioaddr = devc->left_io; + }; + + for (i = 0; i < SBFM_MAXINSTR; i++) + devc->i_map[i].channel = -1; + + return ; +} + +#endif diff --git a/sys/i386/isa/sound/opl3.h b/sys/i386/isa/sound/opl3.h new file mode 100644 index 0000000..afa8d16 --- /dev/null +++ b/sys/i386/isa/sound/opl3.h @@ -0,0 +1,261 @@ +/* + * opl3.h - Definitions of the OPL-3 registers + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +/* + * The OPL-3 mode is switched on by writing 0x01, to the offset 5 + * of the right side. + * + * Another special register at the right side is at offset 4. It contains + * a bit mask defining which voices are used as 4 OP voices. + * + * The percussive mode is implemented in the left side only. + * + * With the above exeptions the both sides can be operated independently. + * + * A 4 OP voice can be created by setting the corresponding + * bit at offset 4 of the right side. + * + * For example setting the rightmost bit (0x01) changes the + * first voice on the right side to the 4 OP mode. The fourth + * voice is made inaccessible. + * + * If a voice is set to the 2 OP mode, it works like 2 OP modes + * of the original YM3812 (AdLib). In addition the voice can + * be connected the left, right or both stereo channels. It can + * even be left unconnected. This works with 4 OP voices also. + * + * The stereo connection bits are located in the FEEDBACK_CONNECTION + * register of the voice (0xC0-0xC8). In 4 OP voices these bits are + * in the second half of the voice. + */ + +/* + * Register numbers for the global registers + */ + +#define TEST_REGISTER 0x01 +#define ENABLE_WAVE_SELECT 0x20 + +#define TIMER1_REGISTER 0x02 +#define TIMER2_REGISTER 0x03 +#define TIMER_CONTROL_REGISTER 0x04 /* Left side */ +#define IRQ_RESET 0x80 +#define TIMER1_MASK 0x40 +#define TIMER2_MASK 0x20 +#define TIMER1_START 0x01 +#define TIMER2_START 0x02 + +#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */ +#define RIGHT_4OP_0 0x01 +#define RIGHT_4OP_1 0x02 +#define RIGHT_4OP_2 0x04 +#define LEFT_4OP_0 0x08 +#define LEFT_4OP_1 0x10 +#define LEFT_4OP_2 0x20 + +#define OPL3_MODE_REGISTER 0x05 /* Right side */ +#define OPL3_ENABLE 0x01 +#define OPL4_ENABLE 0x02 + +#define KBD_SPLIT_REGISTER 0x08 /* Left side */ +#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ +#define KEYBOARD_SPLIT 0x40 + +#define PERCUSSION_REGISTER 0xbd /* Left side only */ +#define TREMOLO_DEPTH 0x80 +#define VIBRATO_DEPTH 0x40 +#define PERCUSSION_ENABLE 0x20 +#define BASSDRUM_ON 0x10 +#define SNAREDRUM_ON 0x08 +#define TOMTOM_ON 0x04 +#define CYMBAL_ON 0x02 +#define HIHAT_ON 0x01 + +/* + * Offsets to the register banks for operators. To get the + * register number just add the operator offset to the bank offset + * + * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) + */ +#define AM_VIB 0x20 +#define TREMOLO_ON 0x80 +#define VIBRATO_ON 0x40 +#define SUSTAIN_ON 0x20 +#define KSR 0x10 /* Key scaling rate */ +#define MULTIPLE_MASK 0x0f /* Frequency multiplier */ + + /* + * KSL/Total level (0x40 to 0x55) + */ +#define KSL_LEVEL 0x40 +#define KSL_MASK 0xc0 /* Envelope scaling bits */ +#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ + +/* + * Attack / Decay rate (0x60 to 0x75) + */ +#define ATTACK_DECAY 0x60 +#define ATTACK_MASK 0xf0 +#define DECAY_MASK 0x0f + +/* + * Sustain level / Release rate (0x80 to 0x95) + */ +#define SUSTAIN_RELEASE 0x80 +#define SUSTAIN_MASK 0xf0 +#define RELEASE_MASK 0x0f + +/* + * Wave select (0xE0 to 0xF5) + */ +#define WAVE_SELECT 0xe0 + +/* + * Offsets to the register banks for voices. Just add to the + * voice number to get the register number. + * + * F-Number low bits (0xA0 to 0xA8). + */ +#define FNUM_LOW 0xa0 + +/* + * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) + */ +#define KEYON_BLOCK 0xb0 +#define KEYON_BIT 0x20 +#define BLOCKNUM_MASK 0x1c +#define FNUM_HIGH_MASK 0x03 + +/* + * Feedback / Connection (0xc0 to 0xc8) + * + * These registers have two new bits when the OPL-3 mode + * is selected. These bits controls connecting the voice + * to the stereo channels. For 4 OP voices this bit is + * defined in the second half of the voice (add 3 to the + * register offset). + * + * For 4 OP voices the connection bit is used in the + * both halfs (gives 4 ways to connect the operators). + */ +#define FEEDBACK_CONNECTION 0xc0 +#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ +#define CONNECTION_BIT 0x01 +/* + * In the 4 OP mode there is four possible configurations how the + * operators can be connected together (in 2 OP modes there is just + * AM or FM). The 4 OP connection mode is defined by the rightmost + * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halfs. + * + * First half Second half Mode + * + * +---+ + * v | + * 0 0 >+-1-+--2--3--4--> + * + * + * + * +---+ + * | | + * 0 1 >+-1-+--2-+ + * |-> + * >--3----4-+ + * + * +---+ + * | | + * 1 0 >+-1-+-----+ + * |-> + * >--2--3--4-+ + * + * +---+ + * | | + * 1 1 >+-1-+--+ + * | + * >--2--3-+-> + * | + * >--4----+ + */ +#define STEREO_BITS 0x30 /* OPL-3 only */ +#define VOICE_TO_LEFT 0x10 +#define VOICE_TO_RIGHT 0x20 + +/* + * Definition table for the physical voices + */ + +struct physical_voice_info { + unsigned char voice_num; + unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */ + unsigned short ioaddr; /* I/O port (left or right side) */ + unsigned char op[4]; /* Operator offsets */ + }; + +/* + * There is 18 possible 2 OP voices + * (9 in the left and 9 in the right). + * The first OP is the modulator and 2nd is the carrier. + * + * The first three voices in the both sides may be connected + * with another voice to a 4 OP voice. For example voice 0 + * can be connected with voice 3. The operators of voice 3 are + * used as operators 3 and 4 of the new 4 OP voice. + * In this case the 2 OP voice number 0 is the 'first half' and + * voice 3 is the second. + */ + +#define USE_LEFT 0 +#define USE_RIGHT 1 + +static struct physical_voice_info pv_map[18] = +{ +/* No Mode Side OP1 OP2 OP3 OP4 */ +/* --------------------------------------------------- */ + { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}}, + { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}}, + { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}}, + + { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}}, + { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}}, + { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}}, + + { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */ + { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */ + { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */ + + { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}}, + { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}}, + { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}}, + + { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}}, + { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}}, + { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}}, + + { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}}, + { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}}, + { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}} +}; diff --git a/sys/i386/isa/sound/os.h b/sys/i386/isa/sound/os.h new file mode 100644 index 0000000..cea247c --- /dev/null +++ b/sys/i386/isa/sound/os.h @@ -0,0 +1,99 @@ +/* + * os.h -- only included by sound_config.h right after local.h + * + */ + +#ifndef _OS_H_ +#define _OS_H_ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/fcntl.h> +#include <sys/filio.h> +#include <sys/uio.h> +#include <sys/kernel.h> +#include <sys/poll.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/signalvar.h> + +#include <machine/soundcard.h> + +#include <i386/isa/isa_device.h> + +#undef DELAY +#define DELAY(x) tenmicrosec(x) +typedef struct uio snd_rw_buf; + +struct snd_wait { + int mode; + int aborting; +}; + + +unsigned long get_time(void); + +#endif /* _OS_H_ */ + +typedef caddr_t ioctl_arg; + +typedef struct sound_os_info { + int unit; +} sound_os_info; + + +/* + * The following macro calls tsleep. It should be implemented such that + * the process is resumed if it receives a signal. + * The q parameter is a wait_queue defined with DEFINE_WAIT_QUEUE(), + * and the second is a workarea parameter. The third is a timeout + * in ticks. Zero means no timeout. + */ +#define DO_SLEEP(q, f, time_limit) \ + { \ + int flag; \ + f.mode = WK_SLEEP; \ + flag=tsleep(&q, (PRIBIO-5)|PCATCH, "sndint", time_limit); \ + f.mode &= ~WK_SLEEP; \ + if (flag == EWOULDBLOCK) { \ + f.mode |= WK_TIMEOUT; \ + f.aborting = 0; \ + } else \ + f.aborting = flag; \ + } + +#define DO_SLEEP1(q, f, time_limit) \ + { \ + int flag; \ + f.mode = WK_SLEEP; \ + flag=tsleep(&q, (PRIBIO-5)|PCATCH, "snd1", time_limit); \ + f.mode &= ~WK_SLEEP; \ + if (flag == EWOULDBLOCK) { \ + f.mode |= WK_TIMEOUT; \ + f.aborting = 0; \ + } else \ + f.aborting = flag; \ + } + +#define DO_SLEEP2(q, f, time_limit) \ + { \ + int flag; \ + f.mode = WK_SLEEP; \ + flag=tsleep(&q, (PRIBIO-5)|PCATCH, "snd2", time_limit); \ + f.mode &= ~WK_SLEEP; \ + if (flag == EWOULDBLOCK) { \ + f.mode |= WK_TIMEOUT; \ + f.aborting = 0; \ + } else \ + f.aborting = flag; \ + } + +#define PROCESS_ABORTING( f) (f.aborting || CURSIG(curproc)) +#define TIMED_OUT( f) (f.mode & WK_TIMEOUT) + +#ifdef ALLOW_POLL +typedef struct proc select_table; +extern struct selinfo selinfo[]; +#endif diff --git a/sys/i386/isa/sound/pas2_card.c b/sys/i386/isa/sound/pas2_card.c new file mode 100644 index 0000000..dc78afa --- /dev/null +++ b/sys/i386/isa/sound/pas2_card.c @@ -0,0 +1,361 @@ +/* + * sound/pas2_card.c + * + * Detection routine for the Pro Audio Spectrum cards. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_PAS) +#define _PAS2_CARD_C_ + +#define DEFINE_TRANSLATIONS +#include <i386/isa/sound/pas_hw.h> + +/* + * The Address Translation code is used to convert I/O register addresses to + * be relative to the given base -register + */ + +int translat_code; +static int pas_intr_mask = 0; +static int pas_irq = 0; + +static sound_os_info *pas_osp; + +char pas_model; +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(u_char data, int ioaddr); + +u_char +pas_read(int ioaddr) +{ + return inb(ioaddr ^ translat_code); +} + +void +pas_write(u_char data, int ioaddr) +{ + outb(ioaddr ^ translat_code, data); +} + +static void +pas2_msg(char *foo) +{ + printf(" PAS2: %s.\n", foo); +} + +/******************* Begin of the Interrupt Handler ********************/ + +void +pasintr(int irq) +{ + int status; + + status = pas_read(INTERRUPT_STATUS); + pas_write(status, INTERRUPT_STATUS); /* Clear interrupt */ + + if (status & I_S_PCM_SAMPLE_BUFFER_IRQ) { +#ifdef CONFIG_AUDIO + pas_pcm_interrupt(status, 1); +#endif + status &= ~I_S_PCM_SAMPLE_BUFFER_IRQ; + } + if (status & I_S_MIDI_IRQ) { +#ifdef CONFIG_MIDI + pas_midi_interrupt(); +#endif + status &= ~I_S_MIDI_IRQ; + } +} + +int +pas_set_intr(int mask) +{ + if (!mask) + return 0; + + pas_intr_mask |= mask; + + pas_write(pas_intr_mask, INTERRUPT_MASK); + return 0; +} + +int +pas_remove_intr(int mask) +{ + if (!mask) + return 0; + + pas_intr_mask &= ~mask; + pas_write(pas_intr_mask, INTERRUPT_MASK); + + return 0; +} + +/******************* End of the Interrupt handler **********************/ + +/******************* Begin of the Initialization Code ******************/ + +static int +config_pas_hw(struct address_info * hw_config) +{ + char ok = 1; + u_int int_ptrs; /* scsi/sound interrupt pointers */ + + pas_irq = hw_config->irq; + + pas_write(0x00, INTERRUPT_MASK); + + pas_write(0x36, SAMPLE_COUNTER_CONTROL); /* Local timer control * + * register */ + + pas_write(0x36, SAMPLE_RATE_TIMER); /* Sample rate timer (16 bit) */ + pas_write(0, SAMPLE_RATE_TIMER); + + pas_write(0x74, SAMPLE_COUNTER_CONTROL); /* Local timer control * + * register */ + + pas_write(0x74, SAMPLE_BUFFER_COUNTER); /* Sample count register (16 * + * bit) */ + pas_write(0, SAMPLE_BUFFER_COUNTER); + + pas_write(F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER | F_F_MIXER_UNMUTE | 1, FILTER_FREQUENCY); + pas_write(P_C_PCM_DMA_ENABLE | P_C_PCM_MONO | P_C_PCM_DAC_MODE | P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, PCM_CONTROL); + pas_write(S_M_PCM_RESET | S_M_FM_RESET | S_M_SB_RESET | S_M_MIXER_RESET /* | S_M_OPL3_DUAL_MONO */ , SERIAL_MIXER); + + pas_write(I_C_1_BOOT_RESET_ENABLE +#ifdef PAS_JOYSTICK_ENABLE + | I_C_1_JOYSTICK_ENABLE +#endif + ,IO_CONFIGURATION_1); + + if (pas_irq < 0 || pas_irq > 15) { + printf("PAS2: Invalid IRQ %d", pas_irq); + ok = 0; + } else { + int_ptrs = pas_read(IO_CONFIGURATION_3); + int_ptrs |= I_C_3_PCM_IRQ_translate[pas_irq] & 0xf; + pas_write(int_ptrs, IO_CONFIGURATION_3); + if (!I_C_3_PCM_IRQ_translate[pas_irq]) { + printf("PAS2: Invalid IRQ %d", pas_irq); + ok = 0; + } else { + if (snd_set_irq_handler(pas_irq, pasintr, hw_config->osp) < 0) + ok = 0; + } + } + + if (hw_config->dma < 0 || hw_config->dma > 7) { + printf("PAS2: Invalid DMA selection %d", hw_config->dma); + ok = 0; + } else { + pas_write(I_C_2_PCM_DMA_translate[hw_config->dma], IO_CONFIGURATION_2); + if (!I_C_2_PCM_DMA_translate[hw_config->dma]) { + printf("PAS2: Invalid DMA selection %d", hw_config->dma); + ok = 0; + } else { + if (0) { + printf("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(0xa8, 0x05); + outb(0xa9, 0x60); +#endif + +#ifdef BROKEN_BUS_CLOCK + pas_write(S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1); +#else + /* + * pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1); + */ + pas_write(S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1); +#endif + pas_write(0x18, SYSTEM_CONFIGURATION_3); /* ??? */ + + pas_write(F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); /* Sets mute off and * + * selects filter rate * + * of 17.897 kHz */ + pas_write(8, PRESCALE_DIVIDER); + + mix_write(P_M_MV508_ADDRESS | 5, PARALLEL_MIXER); + mix_write(5, PARALLEL_MIXER); + +#if defined(CONFIG_SB_EMULATION) && defined(CONFIG_SB) + + { + struct address_info *sb_config; + + if ((sb_config = sound_getconf(SNDCARD_SB))) { + u_char irq_dma; + + /* + * Turn on Sound Blaster compatibility + */ + /* + * bit 1 = SB emulation + */ + /* + * bit 0 = MPU401 emulation (CDPC only :-( ) + */ + pas_write(0x02, COMPATIBILITY_ENABLE); + + /* + * "Emulation address" + */ + pas_write((sb_config->io_base >> 4) & 0x0f, EMULATION_ADDRESS); + + if (!E_C_SB_DMA_translate[sb_config->dma]) + printf("\n\nPAS16 Warning: Invalid SB DMA %d\n\n", + sb_config->dma); + + if (!E_C_SB_IRQ_translate[sb_config->irq]) + printf("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n", + sb_config->irq); + + irq_dma = E_C_SB_DMA_translate[sb_config->dma] | + E_C_SB_IRQ_translate[sb_config->irq]; + + pas_write(irq_dma, EMULATION_CONFIGURATION); + } + } +#else + pas_write(0x00, COMPATIBILITY_ENABLE); +#endif + + if (!ok) + pas2_msg("Driver not enabled"); + + return ok; +} + +static int +detect_pas_hw(struct address_info * hw_config) +{ + u_char board_id, foo; + + /* + * WARNING: Setting an option like W:1 or so that disables warm boot + * reset of the card will screw up this detect code something fierce. + * Adding code to handle this means possibly interfering with other + * cards on the bus if you have something on base port 0x388. SO be + * forewarned. + */ + + outb(MASTER_DECODE, 0xBC); /* Talk to first board */ + outb(MASTER_DECODE, hw_config->io_base >> 2); /* Set base address */ + translat_code = PAS_DEFAULT_BASE ^ hw_config->io_base; + pas_write(1, WAIT_STATE); /* One wait-state */ + + board_id = pas_read(INTERRUPT_MASK); + + if (board_id == 0xff) + return 0; + + /* + * We probably have a PAS-series board, now check for a PAS2-series + * board by trying to change the board revision bits. PAS2-series + * hardware won't let you do this - the bits are read-only. + */ + + foo = board_id ^ 0xe0; + + pas_write(foo, INTERRUPT_MASK); + foo = inb(INTERRUPT_MASK); + pas_write(board_id, INTERRUPT_MASK); + + if (board_id != foo) /* Not a PAS2 */ + return 0; + + pas_model = pas_read(CHIP_REV); + + return pas_model; +} + +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)) { + + if ((pas_model = pas_read(CHIP_REV))) { + char temp[100]; + + snprintf(temp, sizeof(temp), + "%s rev %d", pas_model_names[(int) pas_model], + pas_read(BOARD_REV_ID)); + conf_printf(temp, hw_config); + } + if (config_pas_hw(hw_config)) { + +#ifdef CONFIG_AUDIO + pas_pcm_init(hw_config); +#endif + +#if defined(CONFIG_SB_EMULATION) && defined(CONFIG_SB) + + sb_dsp_disable_midi(); /* The SB emulation don't + * support * midi */ +#endif + + +#ifdef CONFIG_MIDI + pas_midi_init(); +#endif + pas_init_mixer(); + } + } +} + +int +probe_pas(struct address_info * hw_config) +{ + pas_osp = hw_config->osp; + return detect_pas_hw(hw_config); +} + +#endif diff --git a/sys/i386/isa/sound/pas2_midi.c b/sys/i386/isa/sound/pas2_midi.c new file mode 100644 index 0000000..006b7d4c --- /dev/null +++ b/sys/i386/isa/sound/pas2_midi.c @@ -0,0 +1,285 @@ +/* + * sound/pas2_midi.c + * + * The low level driver for the PAS Midi Interface. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_PAS) && defined(CONFIG_MIDI) +#include <i386/isa/sound/pas_hw.h> + +static int midi_busy = 0, input_opened = 0; +static int my_dev; +static volatile int ofifo_bytes = 0; + +static u_char tmp_queue[256]; +static volatile int qlen; +static volatile u_char qhead, qtail; + +static void (*midi_input_intr) (int dev, u_char data); + +static int +pas_midi_open(int dev, int mode, + void (*input) (int dev, u_char data), + void (*output) (int dev) +) +{ + int err; + u_long flags; + u_char ctrl; + + + if (midi_busy) { + printf("PAS2: Midi busy\n"); + return -(EBUSY); + } + /* + * Reset input and output FIFO pointers + */ + pas_write(M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, + MIDI_CONTROL); + + flags = splhigh(); + + if ((err = pas_set_intr(I_M_MIDI_IRQ_ENABLE)) < 0) + return err; + + /* + * Enable input available and output FIFO empty interrupts + */ + + ctrl = 0; + input_opened = 0; + midi_input_intr = input; + + if (mode == OPEN_READ || mode == OPEN_READWRITE) { + ctrl |= M_C_ENA_INPUT_IRQ; /* Enable input */ + input_opened = 1; + } + if (mode == OPEN_WRITE || mode == OPEN_READWRITE) { + ctrl |= M_C_ENA_OUTPUT_IRQ | /* Enable output */ + M_C_ENA_OUTPUT_HALF_IRQ; + } + pas_write(ctrl, + MIDI_CONTROL); + + /* + * Acknowledge any pending interrupts + */ + + pas_write(0xff, MIDI_STATUS); + ofifo_bytes = 0; + + splx(flags); + + midi_busy = 1; + qlen = qhead = qtail = 0; + return 0; +} + +static void +pas_midi_close(int dev) +{ + + /* + * Reset FIFO pointers, disable intrs + */ + pas_write(M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL); + + pas_remove_intr(I_M_MIDI_IRQ_ENABLE); + midi_busy = 0; +} + +static int +dump_to_midi(u_char midi_byte) +{ + int fifo_space, x; + + fifo_space = ((x = pas_read(MIDI_FIFO_STATUS)) >> 4) & 0x0f; + + if (fifo_space == 15 || (fifo_space < 2 && ofifo_bytes > 13)) { /* Fifo full */ + return 0; /* Upper layer will call again */ + } + ofifo_bytes++; + + pas_write(midi_byte, MIDI_DATA); + + return 1; +} + +static int +pas_midi_out(int dev, u_char midi_byte) +{ + + u_long flags; + + /* + * Drain the local queue first + */ + + flags = splhigh(); + + while (qlen && dump_to_midi(tmp_queue[qhead])) { + qlen--; + qhead++; + } + + splx(flags); + + /* + * Output the byte if the local queue is empty. + */ + + if (!qlen) + if (dump_to_midi(midi_byte)) + return 1; /* OK */ + + /* + * Put to the local queue + */ + + if (qlen >= 256) + return 0; /* Local queue full */ + + flags = splhigh(); + + tmp_queue[qtail] = midi_byte; + qlen++; + qtail++; + + splx(flags); + + return 1; +} + +static int +pas_midi_start_read(int dev) +{ + return 0; +} + +static int +pas_midi_end_read(int dev) +{ + return 0; +} + +static int +pas_midi_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + return -(EINVAL); +} + +static void +pas_midi_kick(int dev) +{ + ofifo_bytes = 0; +} + +static int +pas_buffer_status(int dev) +{ + return qlen; +} + +#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include <i386/isa/sound/midi_synth.h> + +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 +}; + +void +pas_midi_init() +{ + if (num_midis >= MAX_MIDI_DEV) { + printf("Sound: Too many midi devices detected\n"); + return; + } + std_midi_synth.midi_dev = my_dev = num_midis; + midi_devs[num_midis++] = &pas_midi_operations; + return; +} + +void +pas_midi_interrupt(void) +{ + u_char stat; + int i, incount; + u_long flags; + + stat = pas_read(MIDI_STATUS); + + if (stat & M_S_INPUT_AVAIL) { /* Input byte available */ + incount = pas_read(MIDI_FIFO_STATUS) & 0x0f; /* Input FIFO count */ + if (!incount) + incount = 16; + + for (i = 0; i < incount; i++) + if (input_opened) { + midi_input_intr(my_dev, pas_read(MIDI_DATA)); + } else + pas_read(MIDI_DATA); /* Flush */ + } + if (stat & (M_S_OUTPUT_EMPTY | M_S_OUTPUT_HALF_EMPTY)) { + if (!(stat & M_S_OUTPUT_EMPTY)) { + ofifo_bytes = 8; + } else { + ofifo_bytes = 0; + } + + flags = splhigh(); + + while (qlen && dump_to_midi(tmp_queue[qhead])) { + qlen--; + qhead++; + } + + splx(flags); + } + if (stat & M_S_OUTPUT_OVERRUN) { + printf("MIDI output overrun %x,%x,%d \n", pas_read(MIDI_FIFO_STATUS), stat, ofifo_bytes); + ofifo_bytes = 100; + } + pas_write(stat, MIDI_STATUS); /* Acknowledge interrupts */ +} + +#endif diff --git a/sys/i386/isa/sound/pas2_mixer.c b/sys/i386/isa/sound/pas2_mixer.c new file mode 100644 index 0000000..305ef45 --- /dev/null +++ b/sys/i386/isa/sound/pas2_mixer.c @@ -0,0 +1,322 @@ +/* + * sound/pas2_mixer.c + * + * Mixer routines for the Pro Audio Spectrum cards. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +#define _PAS2_MIXER_C_ + +#include <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_PAS) + +#include <i386/isa/sound/pas_hw.h> + +#define TRACE(what) /* (what) */ + +extern int translat_code; +extern char pas_model; +extern sound_os_info *pas_osp; + +static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */ +static int mode_control = 0; + +#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_ALTPCM) + +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \ + SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \ + SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD) + +static u_short levels[SOUND_MIXER_NRDEVICES] = +{ + 0x3232, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x5050, /* FM */ + 0x4b4b, /* PCM */ + 0x3232, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x4b4b, /* Mic */ + 0x4b4b, /* CD */ + 0x6464, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x6464 /* Recording level */ +}; + +void +mix_write(u_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 == PAS_16D) { + outw((ioaddr ^ translat_code) - 1, data | (data << 8)); + outb(0, 0x80); + } else + pas_write(data, ioaddr); +} + +static int +mixer_output(int right_vol, int left_vol, int div, int bits, + int mixer) +{ /* Input or output mixer */ + int left = left_vol * div / 100; + int right = right_vol * div / 100; + + + if (bits & P_M_MV508_MIXER) { /* Select input or output mixer */ + left |= mixer; + right |= mixer; + } + if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE) { /* Bass and treble are + * mono devices */ + mix_write(P_M_MV508_ADDRESS | bits, PARALLEL_MIXER); + mix_write(left, PARALLEL_MIXER); + right_vol = left_vol; + } else { + mix_write(P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER); + mix_write(left, PARALLEL_MIXER); + mix_write(P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER); + mix_write(right, PARALLEL_MIXER); + } + + return (left_vol | (right_vol << 8)); +} + +static void +set_mode(int new_mode) +{ + mix_write(P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER); + mix_write(new_mode, PARALLEL_MIXER); + + mode_control = new_mode; +} + +static int +pas_mixer_set(int whichDev, u_int level) +{ + int left, right, devmask, changed, i, mixer = 0; + + TRACE(printf("static int pas_mixer_set(int whichDev = %d, u_int level = %X)\n", whichDev, level)); + + left = level & 0x7f; + right = (level & 0x7f00) >> 8; + + if (whichDev < SOUND_MIXER_NRDEVICES) + if ((1 << whichDev) & rec_devices) + mixer = P_M_MV508_INPUTMIX; + else + mixer = P_M_MV508_OUTPUTMIX; + + switch (whichDev) { + case SOUND_MIXER_VOLUME: /* Master volume (0-63) */ + levels[whichDev] = mixer_output(right, left, 63, P_M_MV508_MASTER_A, 0); + break; + + /* + * Note! Bass and Treble are mono devices. Will use just the + * left channel. + */ + case SOUND_MIXER_BASS: /* Bass (0-12) */ + levels[whichDev] = mixer_output(right, left, 12, P_M_MV508_BASS, 0); + break; + case SOUND_MIXER_TREBLE: /* Treble (0-12) */ + levels[whichDev] = mixer_output(right, left, 12, P_M_MV508_TREBLE, 0); + break; + + case SOUND_MIXER_SYNTH:/* Internal synthesizer (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer); + break; + case SOUND_MIXER_PCM: /* PAS PCM (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer); + break; + case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer); + break; + case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer); + break; + case SOUND_MIXER_LINE: /* External line (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer); + break; + case SOUND_MIXER_CD: /* CD (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer); + break; + case SOUND_MIXER_MIC: /* External microphone (0-31) */ + levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer); + break; + case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer + * only) */ + levels[whichDev] = mixer_output(right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER, + P_M_MV508_OUTPUTMIX); + break; + case SOUND_MIXER_RECLEV: /* Recording level (0-15) */ + levels[whichDev] = mixer_output(right, left, 15, P_M_MV508_MASTER_B, 0); + break; + + case SOUND_MIXER_MUTE: + return 0; + break; + + case SOUND_MIXER_ENHANCE: + i = 0; + level &= 0x7f; + if (level) + i = (level / 20) - 1; + + mode_control &= ~P_M_MV508_ENHANCE_BITS; + mode_control |= P_M_MV508_ENHANCE_BITS; + set_mode(mode_control); + + if (i) + i = (i + 1) * 20; + return i; + break; + + case SOUND_MIXER_LOUD: + mode_control &= ~P_M_MV508_LOUDNESS; + if (level) + mode_control |= P_M_MV508_LOUDNESS; + set_mode(mode_control); + return !!level; /* 0 or 1 */ + break; + + case SOUND_MIXER_RECSRC: + devmask = level & POSSIBLE_RECORDING_DEVICES; + + changed = devmask ^ rec_devices; + rec_devices = devmask; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (changed & (1 << i)) { + pas_mixer_set(i, levels[i]); + } + return rec_devices; + break; + + default: + return -(EINVAL); + } + + return (levels[whichDev]); +} + +/*****/ + +static void +pas_mixer_reset(void) +{ + int foo; + + TRACE(printf("pas2_mixer.c: void pas_mixer_reset(void)\n")); + + for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) + pas_mixer_set(foo, levels[foo]); + + set_mode(P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40); +} + +static int +pas_mixer_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + TRACE(printf("pas2_mixer.c: int pas_mixer_ioctl(u_int cmd = %X, u_int arg = %X)\n", cmd, arg)); + + if (((cmd >> 8) & 0xff) == 'M') { + if (cmd & IOC_IN) + return *(int *) arg = pas_mixer_set(cmd & 0xff, (*(int *) arg)); + else { /* Read parameters */ + + switch (cmd & 0xff) { + + case SOUND_MIXER_RECSRC: + return *(int *) arg = rec_devices; + break; + + case SOUND_MIXER_STEREODEVS: + return *(int *) arg = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE); + break; + + case SOUND_MIXER_DEVMASK: + return *(int *) arg = SUPPORTED_MIXER_DEVICES; + break; + + case SOUND_MIXER_RECMASK: + return *(int *) arg = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES; + break; + + case SOUND_MIXER_CAPS: + return *(int *) arg = 0; /* No special + * capabilities */ + break; + + case SOUND_MIXER_MUTE: + return *(int *) arg = 0; /* No mute yet */ + break; + + case SOUND_MIXER_ENHANCE: + if (!(mode_control & P_M_MV508_ENHANCE_BITS)) + return *(int *) arg = 0; + return *(int *) arg = ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20; + break; + + case SOUND_MIXER_LOUD: + if (mode_control & P_M_MV508_LOUDNESS) + return *(int *) arg = 1; + return *(int *) arg = 0; + break; + + default: + return *(int *) arg = levels[cmd & 0xff]; + } + } + } + return -(EINVAL); +} + +static struct mixer_operations pas_mixer_operations = +{ + "Pro Audio Spectrum 16", + pas_mixer_ioctl +}; + +int +pas_init_mixer(void) +{ + pas_mixer_reset(); + + if (num_mixers < MAX_MIXER_DEV) + mixer_devs[num_mixers++] = &pas_mixer_operations; + return 1; +} + +#endif diff --git a/sys/i386/isa/sound/pas2_pcm.c b/sys/i386/isa/sound/pas2_pcm.c new file mode 100644 index 0000000..f2fc95c --- /dev/null +++ b/sys/i386/isa/sound/pas2_pcm.c @@ -0,0 +1,449 @@ +#define _PAS2_PCM_C_ +/* + * sound/pas2_pcm.c + * + * The low level driver for the Pro Audio Spectrum ADC/DAC. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_PAS) && defined(CONFIG_AUDIO) +#include <i386/isa/sound/pas_hw.h> + + +#define TRACE(WHAT) /* * * (WHAT) */ + +#define PAS_PCM_INTRBITS (0x08) +/* + * Sample buffer timer interrupt enable + */ + +#define PCM_NON 0 +#define PCM_DAC 1 +#define PCM_ADC 2 + +static u_long pcm_speed = 0; /* sampling rate */ +static u_char pcm_channels = 1; /* channels (1 or 2) */ +static u_char pcm_bits = 8; /* bits/sample (8 or 16) */ +static u_char pcm_filter = 0; /* filter FLAG */ +static u_char pcm_mode = PCM_NON; +static u_long pcm_count = 0; +static u_short pcm_bitsok = 8; /* mask of OK bits */ +static int my_devnum = 0; +static int open_mode = 0; + +static int +pcm_set_speed(int arg) +{ + int foo, tmp; + u_long flags; + + if (arg > 44100) + arg = 44100; + if (arg < 5000) + arg = 5000; + + foo = (1193180 + (arg / 2)) / arg; + arg = 1193180 / foo; + + if (pcm_channels & 2) + foo = foo >> 1; + + pcm_speed = arg; + + tmp = pas_read(FILTER_FREQUENCY); + + /* + * Set anti-aliasing filters according to sample rate. You reall + * *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/. + */ +#if !defined NO_AUTO_FILTER_SET + tmp &= 0xe0; + if (pcm_speed >= 2 * 17897) + tmp |= 0x21; + else if (pcm_speed >= 2 * 15909) + tmp |= 0x22; + else if (pcm_speed >= 2 * 11931) + tmp |= 0x29; + else if (pcm_speed >= 2 * 8948) + tmp |= 0x31; + else if (pcm_speed >= 2 * 5965) + tmp |= 0x39; + else if (pcm_speed >= 2 * 2982) + tmp |= 0x24; + pcm_filter = tmp; +#endif + + flags = splhigh(); + + pas_write(tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY); + pas_write(S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); + pas_write(foo & 0xff, SAMPLE_RATE_TIMER); + pas_write((foo >> 8) & 0xff, SAMPLE_RATE_TIMER); + pas_write(tmp, FILTER_FREQUENCY); + + splx(flags); + + return pcm_speed; +} + +static int +pcm_set_channels(int arg) +{ + + if ((arg != 1) && (arg != 2)) + return pcm_channels; + + if (arg != pcm_channels) { + pas_write(pas_read(PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL); + + pcm_channels = arg; + pcm_set_speed(pcm_speed); /* The speed must be + * reinitialized */ + } + return pcm_channels; +} + +static int +pcm_set_bits(int arg) +{ + if ((arg & pcm_bitsok) != arg) + return pcm_bits; + + if (arg != pcm_bits) { + pas_write(pas_read(SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2); + + pcm_bits = arg; + } + return pcm_bits; +} + +static int +pas_pcm_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) +{ + TRACE(printf("pas2_pcm.c: static int pas_pcm_ioctl(u_int cmd = %X, u_int arg = %X)\n", cmd, arg)); + + switch (cmd) { + case SOUND_PCM_WRITE_RATE: + if (local) + return pcm_set_speed((int) arg); + return *(int *) arg = pcm_set_speed((*(int *) arg)); + break; + + case SOUND_PCM_READ_RATE: + if (local) + return pcm_speed; + return *(int *) arg = pcm_speed; + break; + + case SNDCTL_DSP_STEREO: + if (local) + return pcm_set_channels((int) arg + 1) - 1; + return *(int *) arg = pcm_set_channels((*(int *) arg) + 1) - 1; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return pcm_set_channels((int) arg); + return *(int *) arg = pcm_set_channels((*(int *) arg)); + break; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return pcm_channels; + return *(int *) arg = pcm_channels; + break; + + case SNDCTL_DSP_SETFMT: + if (local) + return pcm_set_bits((int) arg); + return *(int *) arg = pcm_set_bits((*(int *) arg)); + break; + + case SOUND_PCM_READ_BITS: + if (local) + return pcm_bits; + return *(int *) arg = pcm_bits; + + case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */ + if ((*(int *) arg) > 1) + return -(EINVAL); + pcm_filter = (*(int *) arg); + break; + + case SOUND_PCM_READ_FILTER: + return *(int *) arg = pcm_filter; + break; + + default: + return -(EINVAL); + } + + return -(EINVAL); +} + +static void +pas_pcm_reset(int dev) +{ + TRACE(printf("pas2_pcm.c: static void pas_pcm_reset(void)\n")); + + pas_write(pas_read(PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL); +} + +static int +pas_pcm_open(int dev, int mode) +{ + int err; + + TRACE(printf("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode)); + + if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0) + return err; + + + pcm_count = 0; + open_mode = mode; + + return 0; +} + +static void +pas_pcm_close(int dev) +{ + u_long flags; + + TRACE(printf("pas2_pcm.c: static void pas_pcm_close(void)\n")); + + flags = splhigh(); + + pas_pcm_reset(dev); + pas_remove_intr(PAS_PCM_INTRBITS); + pcm_mode = PCM_NON; + + open_mode = 0; + + splx(flags); +} + +static void +pas_pcm_output_block(int dev, u_long buf, int count, + int intrflag, int restart_dma) +{ + u_long flags, cnt; + + TRACE(printf("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count)); + + cnt = count; + if (audio_devs[dev]->dmachan1 > 3) + cnt >>= 1; + + if (audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + cnt == pcm_count) + return; /* Auto mode on. No need to react */ + + flags = splhigh(); + + pas_write(pas_read(PCM_CONTROL) & ~P_C_PCM_ENABLE, + PCM_CONTROL); + + if (restart_dma) + DMAbuf_start_dma(dev, buf, count, 1); + + if (audio_devs[dev]->dmachan1 > 3) + count >>= 1; + + if (count != pcm_count) { + pas_write(pas_read(FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + pas_write(S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); + pas_write(count & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write(pas_read(FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + + pcm_count = count; + } + pas_write(pas_read(FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY); +#ifdef NO_TRIGGER + pas_write(pas_read(PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL); +#endif + + pcm_mode = PCM_DAC; + + splx(flags); +} + +static void +pas_pcm_start_input(int dev, u_long buf, int count, + int intrflag, int restart_dma) +{ + u_long flags; + int cnt; + + TRACE(printf("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count)); + + cnt = count; + if (audio_devs[dev]->dmachan1 > 3) + cnt >>= 1; + + if (audio_devs[my_devnum]->flags & DMA_AUTOMODE && + intrflag && + cnt == pcm_count) + return; /* Auto mode on. No need to react */ + + flags = splhigh(); + + if (restart_dma) + DMAbuf_start_dma(dev, buf, count, 0); + + if (audio_devs[dev]->dmachan1 > 3) + count >>= 1; + + if (count != pcm_count) { + pas_write(pas_read(FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + pas_write(S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); + pas_write(count & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER); + pas_write(pas_read(FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); + + pcm_count = count; + } + pas_write(pas_read(FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY); +#ifdef NO_TRIGGER + pas_write((pas_read(PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL); +#endif + + pcm_mode = PCM_ADC; + + splx(flags); +} +#ifndef NO_TRIGGER +static void +pas_audio_trigger (int dev, int state) +{ + unsigned long flags; + + flags = splhigh(); + + 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); + + splx(flags); +} +#endif + +static int +pas_pcm_prepare_for_input(int dev, int bsize, int bcount) +{ + return 0; +} +static int +pas_pcm_prepare_for_output(int dev, int bsize, int bcount) +{ + return 0; +} + +static struct audio_operations pas_pcm_operations = +{ + "Pro Audio Spectrum", + DMA_AUTOMODE, + AFMT_U8 | AFMT_S16_LE, + NULL, + pas_pcm_open, + pas_pcm_close, + pas_pcm_output_block, + pas_pcm_start_input, + pas_pcm_ioctl, + pas_pcm_prepare_for_input, + pas_pcm_prepare_for_output, + pas_pcm_reset, + pas_pcm_reset, + NULL, + NULL, + NULL, + NULL, + pas_audio_trigger +}; + +void +pas_pcm_init(struct address_info * hw_config) +{ + pcm_bitsok = 8; + if (pas_read(OPERATION_MODE_1) & O_M_1_PCM_TYPE) + pcm_bitsok |= 16; + + pcm_set_speed(DSP_DEFAULT_SPEED); + + if (num_audiodevs < MAX_AUDIO_DEV) { + audio_devs[my_devnum = num_audiodevs++] = &pas_pcm_operations; + audio_devs[my_devnum]->dmachan1 = hw_config->dma; + audio_devs[my_devnum]->buffsize = DSP_BUFFSIZE; + } else + printf("PAS2: Too many PCM devices available\n"); + + return; +} + +void +pas_pcm_interrupt(u_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[my_devnum]->flags & DMA_AUTOMODE)) { + pas_write(pas_read(PCM_CONTROL) & ~P_C_PCM_ENABLE, + PCM_CONTROL); + } + switch (pcm_mode) { + + case PCM_DAC: + DMAbuf_outputintr(my_devnum, 1); + break; + + case PCM_ADC: + DMAbuf_inputintr(my_devnum); + break; + + default: + printf("PAS: Unexpected PCM interrupt\n"); + } + } +} + +#endif diff --git a/sys/i386/isa/sound/pas_defs.h b/sys/i386/isa/sound/pas_defs.h new file mode 100644 index 0000000..c4af2d4 --- /dev/null +++ b/sys/i386/isa/sound/pas_defs.h @@ -0,0 +1,250 @@ +/* */ +/* Port addresses and bit fields for the Media Vision Pro AudioSpectrum second generation sound cards. */ +/* */ +/* Feel free to use this header file in any application you create that has support for the Media Vision */ +/* Pro AudioSpectrum second generation sound cards. Other uses prohibited without prior permission. */ +/* */ +/* - cmetz@thor.tjhsst.edu */ +/* */ +/* Notes: */ +/* */ +/* * All of these ports go into the MVD101 multimedia controller chip, which then signals the other chips to do */ +/* the actual work. Many ports like the FM ones functionally attach directly to the destination chip though */ +/* they don't actually have a direct connection. */ +/* */ +/* * The PAS2 series cards have an MVD101 multimedia controller chip, the original PAS cards don't. The original */ +/* PAS cards are pretty defunct now, so no attempt is made here to support them. */ +/* */ +/* * The PAS2 series cards are all really different at the hardware level, though the MVD101 hides some of the */ +/* incompatibilities, there still are differences that need to be accounted for. */ +/* */ +/* Card CD-ROM interface PCM chip Mixer chip FM chip */ +/* PAS Plus Sony proprietary (Crystal?) 8-bit DAC National OPL3 */ +/* PAS 16 Zilog SCSI MVA416 16-bit Codec MVA508 OPL3 */ +/* CDPC Sony proprietary Sony 16-bit Codec National OPL3 */ +/* Fusion CD 16 Sony proprietary MVA416 16-bit Codec MVA508 OPL3 */ +/* Fusion CD Sony proprietary (Crystal?) 8-bit DAC National OPL3 */ +/* */ +#define PAS_DEFAULT_BASE 0x388 + +/* Symbolic Name Value R W Subsystem Description */ +#define SPEAKER_CONTROL 0x61 /* W PC speaker Control register */ +#define SPEAKER_CONTROL_GHOST 0x738B /* R W PC speaker Control ghost register */ +#define SPEAKER_TIMER_CONTROL 0x43 /* W PC speaker Timer control register */ +#define SPEAKER_TIMER_CONTROL_GHOST 0x778B /* R W PC speaker Timer control register ghost */ +#define SPEAKER_TIMER_DATA 0x42 /* W PC speaker Timer data register */ +#define SPEAKER_TIMER_DATA_GHOST 0x138A /* R W PC speaker Timer data register ghost */ + +#define WARM_BOOT 0x41 /* W Control Used to detect system warm boot */ +#define WARM_BOOT_GHOST 0x7789 /* ? W Control Use to get the card to fake warm boot */ +#define MASTER_DECODE 0x9A01 /* W Control Address >> 2 of card base address */ +#define PRESCALE_DIVIDER 0xBF8A /* R W PCM Ration between Codec clock and master clock */ +#define WAIT_STATE 0xBF88 /* R W Control Four-bit bus wait-state count (~140ns ea.) */ +#define BOARD_REV_ID 0x2789 /* R Control Extended Board Revision ID */ + +#define CHIP_REV 0xFF88 /* R 0=PAS, 1=PAS+, 2=CDPC, 3=PAS16C, 4=PAS16D */ + +#define SYSTEM_CONFIGURATION_1 0x8388 /* R W Control */ + #define S_C_1_PCS_ENABLE 0x01 /* R W PC speaker 1=enable, 0=disable PC speaker emulation */ + #define S_C_1_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=14.31818Mhz/12, 0=28.224Mhz master clock */ + #define S_C_1_FM_EMULATE_CLOCK 0x04 /* R W FM 1=use 28.224Mhz/2, 0=use 14.31818Mhz clock */ + #define S_C_1_PCS_STEREO 0x10 /* R W PC speaker 1=enable PC speaker stereo effect, 0=disable */ + #define S_C_1_PCS_REALSOUND 0x20 /* R W PC speaker 1=enable RealSound enhancement, 0=disable */ + #define S_C_1_FORCE_EXT_RESET 0x40 /* R W Control Force external reset */ + #define S_C_1_FORCE_INT_RESET 0x80 /* R W Control Force internal reset */ +#define SYSTEM_CONFIGURATION_2 0x8389 /* R W Control */ + #define S_C_2_PCM_OVERSAMPLING 0x03 /* R W PCM 00=0x, 01=2x, 10=4x, 11=reserved */ + #define S_C_2_PCM_16_BIT 0x04 /* R W PCM 1=16-bit, 0=8-bit samples */ +#define SYSTEM_CONFIGURATION_3 0x838A /* R W Control */ + #define S_C_3_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=use 1.008Mhz clock for PCM, 0=don't */ +#define SYSTEM_CONFIGURATION_4 0x838B /* R W Control CD-ROM interface controls */ + +#define IO_CONFIGURATION_1 0xF388 /* R W Control */ + #define I_C_1_BOOT_RESET_ENABLE 0x80 /* R W Control 1=reset board on warm boot, 0=don't */ + #define I_C_1_JOYSTICK_ENABLE 0x40 /* R W Control 1=enable joystick port, 0=don't */ +#define IO_CONFIGURATION_2 0xF389 /* R W Control */ + #define I_C_2_PCM_DMA_DISABLED 0x00 /* R W PCM PCM DMA disabled */ +#define IO_CONFIGURATION_3 0xF38A /* R W Control */ + #define I_C_3_PCM_IRQ_DISABLED 0x00 /* R W PCM PCM IRQ disabled */ + +#define COMPATIBILITY_ENABLE 0xF788 /* R W Control */ + #define C_E_MPU401_ENABLE 0x01 /* R W MIDI 1=enable, 0=disable MPU401 MIDI emulation */ + #define C_E_SB_ENABLE 0x02 /* R W PCM 1=enable, 0=disable Sound Blaster emulation */ + #define C_E_SB_ACTIVE 0x04 /* R PCM "Sound Blaster Interrupt active" */ + #define C_E_MPU401_ACTIVE 0x08 /* R MIDI "MPU UART mode active" */ + #define C_E_PCM_COMPRESSION 0x10 /* R W PCM 1=enable, 0=disabled compression */ +#define EMULATION_ADDRESS 0xF789 /* R W Control */ + #define E_A_SB_BASE 0x0f /* R W PCM bits A4-A7 for SB base port */ + #define E_A_MPU401_BASE 0xf0 /* R W MIDI bits A4-A7 for MPU401 base port */ +#define EMULATION_CONFIGURATION 0xFB8A /* R W ***** Only valid on newer PAS2 cards (?) ***** */ + #define E_C_MPU401_IRQ 0x07 /* R W MIDI MPU401 emulation IRQ */ + #define E_C_SB_IRQ 0x38 /* R W PCM SB emulation IRQ */ + #define E_C_SB_DMA 0xC0 /* R W PCM SB emulation DMA */ + +#define OPERATION_MODE_1 0xEF8B /* R Control */ + #define O_M_1_CDROM_TYPE 0x03 /* R CD-ROM 3=SCSI, 2=Sony, 0=no CD-ROM interface */ + #define O_M_1_FM_TYPE 0x04 /* R FM 1=sterero, 0=mono FM chip */ + #define O_M_1_PCM_TYPE 0x08 /* R PCM 1=16-bit Codec, 0=8-bit DAC */ +#define OPERATION_MODE_2 0xFF8B /* R Control */ + #define O_M_2_PCS_ENABLED 0x02 /* R PC speaker PC speaker emulation 1=enabled, 0=disabled */ + #define O_M_2_BUS_TIMING 0x10 /* R Control 1=AT bus timing, 0=XT bus timing */ + #define O_M_2_BOARD_REVISION 0xe0 /* R Control Board revision */ + +#define INTERRUPT_MASK 0x0B8B /* R W Control */ + #define I_M_FM_LEFT_IRQ_ENABLE 0x01 /* R W FM Enable FM left interrupt */ + #define I_M_FM_RIGHT_IRQ_ENABLE 0x02 /* R W FM Enable FM right interrupt */ + #define I_M_PCM_RATE_IRQ_ENABLE 0x04 /* R W PCM Enable Sample Rate interrupt */ + #define I_M_PCM_BUFFER_IRQ_ENABLE 0x08 /* R W PCM Enable Sample Buffer interrupt */ + #define I_M_MIDI_IRQ_ENABLE 0x10 /* R W MIDI Enable MIDI interrupt */ + #define I_M_BOARD_REV 0xE0 /* R Control Board revision */ + +#define INTERRUPT_STATUS 0x0B89 /* R W Control */ + #define I_S_FM_LEFT_IRQ 0x01 /* R W FM Left FM Interrupt Pending */ + #define I_S_FM_RIGHT_IRQ 0x02 /* R W FM Right FM Interrupt Pending */ + #define I_S_PCM_SAMPLE_RATE_IRQ 0x04 /* R W PCM Sample Rate Interrupt Pending */ + #define I_S_PCM_SAMPLE_BUFFER_IRQ 0x08 /* R W PCM Sample Buffer Interrupt Pending */ + #define I_S_MIDI_IRQ 0x10 /* R W MIDI MIDI Interrupt Pending */ + #define I_S_PCM_CHANNEL 0x20 /* R W PCM 1=right, 0=left */ + #define I_S_RESET_ACTIVE 0x40 /* R W Control Reset is active (Timed pulse not finished) */ + #define I_S_PCM_CLIPPING 0x80 /* R W PCM Clipping has occurred */ + +#define FILTER_FREQUENCY 0x0B8A /* R W Control */ + #define F_F_FILTER_DISABLED 0x00 /* R W Mixer No filter */ +#if 0 + struct { /* R W Mixer Filter translation */ + unsigned int freq:24; + unsigned int value:8; + } F_F_FILTER_translate[] = + { { 73500, 0x01 }, /* 73500Hz - divide by 16 */ + { 65333, 0x02 }, /* 65333Hz - divide by 18 */ + { 49000, 0x09 }, /* 49000Hz - divide by 24 */ + { 36750, 0x11 }, /* 36750Hz - divide by 32 */ + { 24500, 0x19 }, /* 24500Hz - divide by 48 */ + { 18375, 0x07 }, /* 18375Hz - divide by 64 */ + { 12783, 0x0f }, /* 12783Hz - divide by 92 */ + { 12250, 0x04 }, /* 12250Hz - divide by 96 */ + { 9188, 0x17 }, /* 9188Hz - divide by 128 */ + { 6125, 0x1f }, /* 6125Hz - divide by 192 */ + }; +#endif + #define F_F_MIXER_UNMUTE 0x20 /* R W Mixer 1=disable, 0=enable board mute */ + #define F_F_PCM_RATE_COUNTER 0x40 /* R W PCM 1=enable, 0=disable sample rate counter */ + #define F_F_PCM_BUFFER_COUNTER 0x80 /* R W PCM 1=enable, 0=disable sample buffer counter */ + +#define PAS_NONE 0 +#define PAS_PLUS 1 +#define PAS_CDPC 2 +#define PAS_16 3 +#define PAS_16D 4 + +#ifdef DEFINE_TRANSLATIONS +static unsigned char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */ + { 4, 1, 2, 3, 0, 5, 6, 7 }; +static unsigned char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */ + { 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 }; +#ifdef unused +static unsigned char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */ + { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x05, 0x06, 0x07 }; +#endif +static unsigned char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */ + { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 }; +static unsigned char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */ + { 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 }; +#ifdef unused +static unsigned char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */ + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 2, 3 }; +#endif +#endif + +#define PARALLEL_MIXER 0x078B /* W Mixer Documented for MVD101 as FM Mono Right decode?? */ + #define P_M_MV508_ADDRESS 0x80 /* W Mixer MVD508 Address/mixer select */ + #define P_M_MV508_DATA 0x00 + #define P_M_MV508_LEFT 0x20 /* W Mixer MVD508 Left channel select */ + #define P_M_MV508_RIGHT 0x40 /* W Mixer MVD508 Right channel select */ + #define P_M_MV508_BOTH 0x00 /* W Mixer MVD508 Both channel select */ + #define P_M_MV508_MIXER 0x10 /* W Mixer MVD508 Select a mixer (rather than a volume) */ + #define P_M_MV508_VOLUME 0x00 + + #define P_M_MV508_INPUTMIX 0x20 /* W Mixer MVD508 Select mixer A */ + #define P_M_MV508_OUTPUTMIX 0x00 /* W Mixer MVD508 Select mixer B */ + + #define P_M_MV508_MASTER_A 0x01 /* W Mixer MVD508 Master volume control A (output) */ + #define P_M_MV508_MASTER_B 0x02 /* W Mixer MVD508 Master volume control B (DSP input) */ + #define P_M_MV508_BASS 0x03 /* W Mixer MVD508 Bass control */ + #define P_M_MV508_TREBLE 0x04 /* W Mixer MVD508 Treble control */ + #define P_M_MV508_MODE 0x05 /* W Mixer MVD508 Master mode control */ + + #define P_M_MV508_LOUDNESS 0x04 /* W Mixer MVD508 Mode control - Loudness filter */ + #define P_M_MV508_ENHANCE_BITS 0x03 + #define P_M_MV508_ENHANCE_NONE 0x00 /* W Mixer MVD508 Mode control - No stereo enhancement */ + #define P_M_MV508_ENHANCE_40 0x01 /* W Mixer MVD508 Mode control - 40% stereo enhancement */ + #define P_M_MV508_ENHANCE_60 0x02 /* W Mixer MVD508 Mode control - 60% stereo enhancement */ + #define P_M_MV508_ENHANCE_80 0x03 /* W Mixer MVD508 Mode control - 80% stereo enhancement */ + + #define P_M_MV508_FM 0x00 /* W Mixer MVD508 Channel 0 - FM */ + #define P_M_MV508_IMIXER 0x01 /* W Mixer MVD508 Channel 1 - Input mixer (rec monitor) */ + #define P_M_MV508_LINE 0x02 /* W Mixer MVD508 Channel 2 - Line in */ + #define P_M_MV508_CDROM 0x03 /* W Mixer MVD508 Channel 3 - CD-ROM */ + #define P_M_MV508_MIC 0x04 /* W Mixer MVD508 Channel 4 - Microphone */ + #define P_M_MV508_PCM 0x05 /* W Mixer MVD508 Channel 5 - PCM */ + #define P_M_MV508_SPEAKER 0x06 /* W Mixer MVD508 Channel 6 - PC Speaker */ + #define P_M_MV508_SB 0x07 /* W Mixer MVD508 Channel 7 - SB DSP */ + +#define SERIAL_MIXER 0xB88 /* R W Control Serial mixer control (used other ways) */ + #define S_M_PCM_RESET 0x01 /* R W PCM Codec/DSP reset */ + #define S_M_FM_RESET 0x02 /* R W FM FM chip reset */ + #define S_M_SB_RESET 0x04 /* R W PCM SB emulation chip reset */ + #define S_M_MIXER_RESET 0x10 /* R W Mixer Mixer chip reset */ + #define S_M_INTEGRATOR_ENABLE 0x40 /* R W Speaker Enable PC speaker integrator (FORCE RealSound) */ + #define S_M_OPL3_DUAL_MONO 0x80 /* R W FM Set the OPL-3 to dual mono mode */ + +#define PCM_CONTROL 0xF8A /* R W PCM PCM Control Register */ + #define P_C_MIXER_CROSS_FIELD 0x0f + #define P_C_MIXER_CROSS_R_TO_R 0x01 /* R W Mixer Connect Right to Right */ + #define P_C_MIXER_CROSS_L_TO_R 0x02 /* R W Mixer Connect Left to Right */ + #define P_C_MIXER_CROSS_R_TO_L 0x04 /* R W Mixer Connect Right to Left */ + #define P_C_MIXER_CROSS_L_TO_L 0x08 /* R W Mixer Connect Left to Left */ + #define P_C_PCM_DAC_MODE 0x10 /* R W PCM Playback (DAC) mode */ + #define P_C_PCM_ADC_MODE 0x00 /* R W PCM Record (ADC) mode */ + #define P_C_PCM_MONO 0x20 /* R W PCM Mono mode */ + #define P_C_PCM_STEREO 0x00 /* R W PCM Stereo mode */ + #define P_C_PCM_ENABLE 0x40 /* R W PCM Enable PCM engine */ + #define P_C_PCM_DMA_ENABLE 0x80 /* R W PCM Enable DRQ */ + +#define SAMPLE_COUNTER_CONTROL 0x138B /* R W PCM Sample counter control register */ + #define S_C_C_SQUARE_WAVE 0x04 /* R W PCM Square wave generator (use for sample rate) */ + #define S_C_C_RATE 0x06 /* R W PCM Rate generator (use for sample buffer count) */ + #define S_C_C_LSB_THEN_MSB 0x30 /* R W PCM Change all 16 bits, LSB first, then MSB */ + + /* MVD101 and SDK documentations have S_C_C_SAMPLE_RATE and S_C_C_SAMPLE_BUFFER transposed. Only one works :-) */ + #define S_C_C_SAMPLE_RATE 0x00 /* R W PCM Select sample rate timer */ + #define S_C_C_SAMPLE_BUFFER 0x40 /* R W PCM Select sample buffer counter */ + + #define S_C_C_PC_SPEAKER 0x80 /* R W PCM Select PC speaker counter */ + +#define SAMPLE_RATE_TIMER 0x1388 /* W PCM Sample rate timer register (PCM wait interval) */ +#define SAMPLE_BUFFER_COUNTER 0x1389 /* R W PCM Sample buffer counter (DMA buffer size) */ + +#define MIDI_CONTROL 0x178b /* R W MIDI Midi control register */ + #define M_C_ENA_TSTAMP_IRQ 0x01 /* R W MIDI Enable Time Stamp Interrupts */ + #define M_C_ENA_TME_COMP_IRQ 0x02 /* R W MIDI Enable time compare interrupts */ + #define M_C_ENA_INPUT_IRQ 0x04 /* R W MIDI Enable input FIFO interrupts */ + #define M_C_ENA_OUTPUT_IRQ 0x08 /* R W MIDI Enable output FIFO interrupts */ + #define M_C_ENA_OUTPUT_HALF_IRQ 0x10 /* R W MIDI Enable output FIFO half full interrupts */ + #define M_C_RESET_INPUT_FIFO 0x20 /* R W MIDI Reset input FIFO pointer */ + #define M_C_RESET_OUTPUT_FIFO 0x40 /* R W MIDI Reset output FIFO pointer */ + #define M_C_ENA_THRU_MODE 0x80 /* R W MIDI Echo input to output (THRU) */ + +#define MIDI_STATUS 0x1B88 /* R W MIDI Midi (interrupt) status register */ + #define M_S_TIMESTAMP 0x01 /* R W MIDI Midi time stamp interrupt occurred */ + #define M_S_COMPARE 0x02 /* R W MIDI Midi compare time interrupt occurred */ + #define M_S_INPUT_AVAIL 0x04 /* R W MIDI Midi input data available interrupt occurred */ + #define M_S_OUTPUT_EMPTY 0x08 /* R W MIDI Midi output FIFO empty interrupt occurred */ + #define M_S_OUTPUT_HALF_EMPTY 0x10 /* R W MIDI Midi output FIFO half empty interrupt occurred */ + #define M_S_INPUT_OVERRUN 0x20 /* R W MIDI Midi input overrun error occurred */ + #define M_S_OUTPUT_OVERRUN 0x40 /* R W MIDI Midi output overrun error occurred */ + #define M_S_FRAMING_ERROR 0x80 /* R W MIDI Midi input framing error occurred */ + +#define MIDI_FIFO_STATUS 0x1B89 /* R W MIDI Midi fifo status */ +#define MIDI_DATA 0x178A /* R W MIDI Midi data register */ +#define MIDI_INPUT_AVAILABLE 0x0f /* RW MIDI */ diff --git a/sys/i386/isa/sound/pas_hw.h b/sys/i386/isa/sound/pas_hw.h new file mode 100644 index 0000000..5f82328 --- /dev/null +++ b/sys/i386/isa/sound/pas_hw.h @@ -0,0 +1,236 @@ +/* */ +/* Port addresses and bit fields for the Media Vision Pro AudioSpectrum second generation sound cards. */ +/* */ +/* Feel free to use this header file in any application you create that has support for the Media Vision */ +/* Pro AudioSpectrum second generation sound cards. Other uses prohibited without prior permission. */ +/* */ +/* - cmetz@thor.tjhsst.edu */ +/* */ +/* Notes: */ +/* */ +/* * All of these ports go into the MVD101 multimedia controller chip, which then signals the other chips to do */ +/* the actual work. Many ports like the FM ones functionally attach directly to the destination chip though */ +/* they don't actually have a direct connection. */ +/* */ +/* * The PAS2 series cards have an MVD101 multimedia controller chip, the original PAS cards don't. The original */ +/* PAS cards are pretty defunct now, so no attempt is made here to support them. */ +/* */ +/* * The PAS2 series cards are all really different at the hardware level, though the MVD101 hides some of the */ +/* incompatibilities, there still are differences that need to be accounted for. */ +/* */ +/* Card CD-ROM interface PCM chip Mixer chip FM chip */ +/* PAS Plus Sony proprietary (Crystal?) 8-bit DAC National OPL3 */ +/* PAS 16 Zilog SCSI MVA416 16-bit Codec MVA508 OPL3 */ +/* CDPC Sony proprietary Sony 16-bit Codec National OPL3 */ +/* Fusion CD 16 Sony proprietary MVA416 16-bit Codec MVA508 OPL3 */ +/* Fusion CD Sony proprietary (Crystal?) 8-bit DAC National OPL3 */ +/* */ +#define PAS_DEFAULT_BASE 0x388 + +/* Symbolic Name Value R W Subsystem Description */ +#define SPEAKER_CONTROL 0x61 /* W PC speaker Control register */ +#define SPEAKER_CONTROL_GHOST 0x738B /* R W PC speaker Control ghost register */ +#define SPEAKER_TIMER_CONTROL 0x43 /* W PC speaker Timer control register */ +#define SPEAKER_TIMER_CONTROL_GHOST 0x778B /* R W PC speaker Timer control register ghost */ +#define SPEAKER_TIMER_DATA 0x42 /* W PC speaker Timer data register */ +#define SPEAKER_TIMER_DATA_GHOST 0x138A /* R W PC speaker Timer data register ghost */ + +#define WARM_BOOT 0x41 /* W Control Used to detect system warm boot */ +#define WARM_BOOT_GHOST 0x7789 /* ? W Control Use to get the card to fake warm boot */ +#define MASTER_DECODE 0x9A01 /* W Control Address >> 2 of card base address */ +#define PRESCALE_DIVIDER 0xBF8A /* R W PCM Ration between Codec clock and master clock */ +#define WAIT_STATE 0xBF88 /* R W Control Four-bit bus wait-state count (~140ns ea.) */ +#define BOARD_REV_ID 0x2789 /* R Control Extended Board Revision ID */ + +#define CHIP_REV 0xFF88 /* R 0=PAS, 1=PAS+, 2=CDPC, 3=PAS16C, 4=PAS16D */ + +#define SYSTEM_CONFIGURATION_1 0x8388 /* R W Control */ +# define S_C_1_PCS_ENABLE 0x01 /* R W PC speaker 1=enable, 0=disable PC speaker emulation */ +# define S_C_1_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=14.31818Mhz/12, 0=28.224Mhz master clock */ +# define S_C_1_FM_EMULATE_CLOCK 0x04 /* R W FM 1=use 28.224Mhz/2, 0=use 14.31818Mhz clock */ +# define S_C_1_PCS_STEREO 0x10 /* R W PC speaker 1=enable PC speaker stereo effect, 0=disable */ +# define S_C_1_PCS_REALSOUND 0x20 /* R W PC speaker 1=enable RealSound enhancement, 0=disable */ +# define S_C_1_FORCE_EXT_RESET 0x40 /* R W Control Force external reset */ +# define S_C_1_FORCE_INT_RESET 0x80 /* R W Control Force internal reset */ +#define SYSTEM_CONFIGURATION_2 0x8389 /* R W Control */ +# define S_C_2_PCM_OVERSAMPLING 0x03 /* R W PCM 00=0x, 01=2x, 10=4x, 11=reserved */ +# define S_C_2_PCM_16_BIT 0x04 /* R W PCM 1=16-bit, 0=8-bit samples */ +#define SYSTEM_CONFIGURATION_3 0x838A /* R W Control */ +# define S_C_3_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=use 1.008Mhz clock for PCM, 0=don't */ +#define SYSTEM_CONFIGURATION_4 0x838B /* R W Control CD-ROM interface controls */ + +#define IO_CONFIGURATION_1 0xF388 /* R W Control */ +# define I_C_1_BOOT_RESET_ENABLE 0x80 /* R W Control 1=reset board on warm boot, 0=don't */ +# define I_C_1_JOYSTICK_ENABLE 0x40 /* R W Control 1=enable joystick port, 0=don't */ +#define IO_CONFIGURATION_2 0xF389 /* R W Control */ +# define I_C_2_PCM_DMA_DISABLED 0x00 /* R W PCM PCM DMA disabled */ +#define IO_CONFIGURATION_3 0xF38A /* R W Control */ +# define I_C_3_PCM_IRQ_DISABLED 0x00 /* R W PCM PCM IRQ disabled */ + +#define COMPATIBILITY_ENABLE 0xF788 /* R W Control */ +# define C_E_MPU401_ENABLE 0x01 /* R W MIDI 1=enable, 0=disable MPU401 MIDI emulation */ +# define C_E_SB_ENABLE 0x02 /* R W PCM 1=enable, 0=disable Sound Blaster emulation */ +# define C_E_SB_ACTIVE 0x04 /* R PCM "Sound Blaster Interrupt active" */ +# define C_E_MPU401_ACTIVE 0x08 /* R MIDI "MPU UART mode active" */ +# define C_E_PCM_COMPRESSION 0x10 /* R W PCM 1=enable, 0=disabled compression */ +#define EMULATION_ADDRESS 0xF789 /* R W Control */ +# define E_A_SB_BASE 0x0f /* R W PCM bits A4-A7 for SB base port */ +# define E_A_MPU401_BASE 0xf0 /* R W MIDI bits A4-A7 for MPU401 base port */ +#define EMULATION_CONFIGURATION 0xFB8A /* R W ***** Only valid on newer PAS2 cards (?) ***** */ +# define E_C_MPU401_IRQ 0x07 /* R W MIDI MPU401 emulation IRQ */ +# define E_C_SB_IRQ 0x38 /* R W PCM SB emulation IRQ */ +# define E_C_SB_DMA 0xC0 /* R W PCM SB emulation DMA */ + +#define OPERATION_MODE_1 0xEF8B /* R Control */ +# define O_M_1_CDROM_TYPE 0x03 /* R CD-ROM 3=SCSI, 2=Sony, 0=no CD-ROM interface */ +# define O_M_1_FM_TYPE 0x04 /* R FM 1=sterero, 0=mono FM chip */ +# define O_M_1_PCM_TYPE 0x08 /* R PCM 1=16-bit Codec, 0=8-bit DAC */ +#define OPERATION_MODE_2 0xFF8B /* R Control */ +# define O_M_2_PCS_ENABLED 0x02 /* R PC speaker PC speaker emulation 1=enabled, 0=disabled */ +# define O_M_2_BUS_TIMING 0x10 /* R Control 1=AT bus timing, 0=XT bus timing */ +# define O_M_2_BOARD_REVISION 0xe0 /* R Control Board revision */ + +#define INTERRUPT_MASK 0x0B8B /* R W Control */ +# define I_M_FM_LEFT_IRQ_ENABLE 0x01 /* R W FM Enable FM left interrupt */ +# define I_M_FM_RIGHT_IRQ_ENABLE 0x02 /* R W FM Enable FM right interrupt */ +# define I_M_PCM_RATE_IRQ_ENABLE 0x04 /* R W PCM Enable Sample Rate interrupt */ +# define I_M_PCM_BUFFER_IRQ_ENABLE 0x08 /* R W PCM Enable Sample Buffer interrupt */ +# define I_M_MIDI_IRQ_ENABLE 0x10 /* R W MIDI Enable MIDI interrupt */ +# define I_M_BOARD_REV 0xE0 /* R Control Board revision */ + +#define INTERRUPT_STATUS 0x0B89 /* R W Control */ +# define I_S_FM_LEFT_IRQ 0x01 /* R W FM Left FM Interrupt Pending */ +# define I_S_FM_RIGHT_IRQ 0x02 /* R W FM Right FM Interrupt Pending */ +# define I_S_PCM_SAMPLE_RATE_IRQ 0x04 /* R W PCM Sample Rate Interrupt Pending */ +# define I_S_PCM_SAMPLE_BUFFER_IRQ 0x08 /* R W PCM Sample Buffer Interrupt Pending */ +# define I_S_MIDI_IRQ 0x10 /* R W MIDI MIDI Interrupt Pending */ +# define I_S_PCM_CHANNEL 0x20 /* R W PCM 1=right, 0=left */ +# define I_S_RESET_ACTIVE 0x40 /* R W Control Reset is active (Timed pulse not finished) */ +# define I_S_PCM_CLIPPING 0x80 /* R W PCM Clipping has occurred */ + +#define FILTER_FREQUENCY 0x0B8A /* R W Control */ +# define F_F_FILTER_DISABLED 0x00 /* R W Mixer No filter */ +# define F_F_MIXER_UNMUTE 0x20 /* R W Mixer 1=disable, 0=enable board mute */ +# define F_F_PCM_RATE_COUNTER 0x40 /* R W PCM 1=enable, 0=disable sample rate counter */ +# define F_F_PCM_BUFFER_COUNTER 0x80 /* R W PCM 1=enable, 0=disable sample buffer counter */ + +#define PAS_NONE 0 +#define PAS_PLUS 1 +#define PAS_CDPC 2 +#define PAS_16 3 +#define PAS_16D 4 + +#ifdef DEFINE_TRANSLATIONS + unsigned char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */ + { 4, 1, 2, 3, 0, 5, 6, 7 }; + unsigned char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */ + { 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 }; + unsigned char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */ + { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x05, 0x06, 0x07 }; + unsigned char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */ + { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 }; + unsigned char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */ + { 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 }; + unsigned char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */ + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 2, 3 }; +#else + extern unsigned char I_C_2_PCM_DMA_translate[]; /* R W PCM PCM DMA channel value translations */ + extern unsigned char I_C_3_PCM_IRQ_translate[]; /* R W PCM PCM IRQ level value translation */ + extern unsigned char E_C_MPU401_IRQ_translate[]; /* R W MIDI MPU401 emulation IRQ value translation */ + extern unsigned char E_C_SB_IRQ_translate[]; /* R W PCM SB emulation IRQ translate */ + extern unsigned char E_C_SB_DMA_translate[]; /* R W PCM SB emulation DMA translate */ + extern unsigned char O_M_1_to_card[]; /* R W Control Translate (OM1 & 0x0f) to card type */ +#endif + +#define PARALLEL_MIXER 0x078B /* W Mixer Documented for MVD101 as FM Mono Right decode?? */ +# define P_M_MV508_ADDRESS 0x80 /* W Mixer MVD508 Address/mixer select */ +# define P_M_MV508_DATA 0x00 +# define P_M_MV508_LEFT 0x20 /* W Mixer MVD508 Left channel select */ +# define P_M_MV508_RIGHT 0x40 /* W Mixer MVD508 Right channel select */ +# define P_M_MV508_BOTH 0x00 /* W Mixer MVD508 Both channel select */ +# define P_M_MV508_MIXER 0x10 /* W Mixer MVD508 Select a mixer (rather than a volume) */ +# define P_M_MV508_VOLUME 0x00 + +# define P_M_MV508_INPUTMIX 0x20 /* W Mixer MVD508 Select mixer A */ +# define P_M_MV508_OUTPUTMIX 0x00 /* W Mixer MVD508 Select mixer B */ + +# define P_M_MV508_MASTER_A 0x01 /* W Mixer MVD508 Master volume control A (output) */ +# define P_M_MV508_MASTER_B 0x02 /* W Mixer MVD508 Master volume control B (DSP input) */ +# define P_M_MV508_BASS 0x03 /* W Mixer MVD508 Bass control */ +# define P_M_MV508_TREBLE 0x04 /* W Mixer MVD508 Treble control */ +# define P_M_MV508_MODE 0x05 /* W Mixer MVD508 Master mode control */ + +# define P_M_MV508_LOUDNESS 0x04 /* W Mixer MVD508 Mode control - Loudness filter */ +# define P_M_MV508_ENHANCE_BITS 0x03 +# define P_M_MV508_ENHANCE_NONE 0x00 /* W Mixer MVD508 Mode control - No stereo enhancement */ +# define P_M_MV508_ENHANCE_40 0x01 /* W Mixer MVD508 Mode control - 40% stereo enhancement */ +# define P_M_MV508_ENHANCE_60 0x02 /* W Mixer MVD508 Mode control - 60% stereo enhancement */ +# define P_M_MV508_ENHANCE_80 0x03 /* W Mixer MVD508 Mode control - 80% stereo enhancement */ + +# define P_M_MV508_FM 0x00 /* W Mixer MVD508 Channel 0 - FM */ +# define P_M_MV508_IMIXER 0x01 /* W Mixer MVD508 Channel 1 - Input mixer (rec monitor) */ +# define P_M_MV508_LINE 0x02 /* W Mixer MVD508 Channel 2 - Line in */ +# define P_M_MV508_CDROM 0x03 /* W Mixer MVD508 Channel 3 - CD-ROM */ +# define P_M_MV508_MIC 0x04 /* W Mixer MVD508 Channel 4 - Microphone */ +# define P_M_MV508_PCM 0x05 /* W Mixer MVD508 Channel 5 - PCM */ +# define P_M_MV508_SPEAKER 0x06 /* W Mixer MVD508 Channel 6 - PC Speaker */ +# define P_M_MV508_SB 0x07 /* W Mixer MVD508 Channel 7 - SB DSP */ + +#define SERIAL_MIXER 0xB88 /* R W Control Serial mixer control (used other ways) */ +# define S_M_PCM_RESET 0x01 /* R W PCM Codec/DSP reset */ +# define S_M_FM_RESET 0x02 /* R W FM FM chip reset */ +# define S_M_SB_RESET 0x04 /* R W PCM SB emulation chip reset */ +# define S_M_MIXER_RESET 0x10 /* R W Mixer Mixer chip reset */ +# define S_M_INTEGRATOR_ENABLE 0x40 /* R W Speaker Enable PC speaker integrator (FORCE RealSound) */ +# define S_M_OPL3_DUAL_MONO 0x80 /* R W FM Set the OPL-3 to dual mono mode */ + +#define PCM_CONTROL 0xF8A /* R W PCM PCM Control Register */ +# define P_C_MIXER_CROSS_FIELD 0x0f +# define P_C_MIXER_CROSS_R_TO_R 0x01 /* R W Mixer Connect Right to Right */ +# define P_C_MIXER_CROSS_L_TO_R 0x02 /* R W Mixer Connect Left to Right */ +# define P_C_MIXER_CROSS_R_TO_L 0x04 /* R W Mixer Connect Right to Left */ +# define P_C_MIXER_CROSS_L_TO_L 0x08 /* R W Mixer Connect Left to Left */ +# define P_C_PCM_DAC_MODE 0x10 /* R W PCM Playback (DAC) mode */ +# define P_C_PCM_ADC_MODE 0x00 /* R W PCM Record (ADC) mode */ +# define P_C_PCM_MONO 0x20 /* R W PCM Mono mode */ +# define P_C_PCM_STEREO 0x00 /* R W PCM Stereo mode */ +# define P_C_PCM_ENABLE 0x40 /* R W PCM Enable PCM engine */ +# define P_C_PCM_DMA_ENABLE 0x80 /* R W PCM Enable DRQ */ + +#define SAMPLE_COUNTER_CONTROL 0x138B /* R W PCM Sample counter control register */ +# define S_C_C_SQUARE_WAVE 0x04 /* R W PCM Square wave generator (use for sample rate) */ +# define S_C_C_RATE 0x06 /* R W PCM Rate generator (use for sample buffer count) */ +# define S_C_C_LSB_THEN_MSB 0x30 /* R W PCM Change all 16 bits, LSB first, then MSB */ + + /* MVD101 and SDK documentations have S_C_C_SAMPLE_RATE and S_C_C_SAMPLE_BUFFER transposed. Only one works :-) */ +# define S_C_C_SAMPLE_RATE 0x00 /* R W PCM Select sample rate timer */ +# define S_C_C_SAMPLE_BUFFER 0x40 /* R W PCM Select sample buffer counter */ + +# define S_C_C_PC_SPEAKER 0x80 /* R W PCM Select PC speaker counter */ + +#define SAMPLE_RATE_TIMER 0x1388 /* W PCM Sample rate timer register (PCM wait interval) */ +#define SAMPLE_BUFFER_COUNTER 0x1389 /* R W PCM Sample buffer counter (DMA buffer size) */ + +#define MIDI_CONTROL 0x178b /* R W MIDI Midi control register */ +# define M_C_ENA_TSTAMP_IRQ 0x01 /* R W MIDI Enable Time Stamp Interrupts */ +# define M_C_ENA_TME_COMP_IRQ 0x02 /* R W MIDI Enable time compare interrupts */ +# define M_C_ENA_INPUT_IRQ 0x04 /* R W MIDI Enable input FIFO interrupts */ +# define M_C_ENA_OUTPUT_IRQ 0x08 /* R W MIDI Enable output FIFO interrupts */ +# define M_C_ENA_OUTPUT_HALF_IRQ 0x10 /* R W MIDI Enable output FIFO half full interrupts */ +# define M_C_RESET_INPUT_FIFO 0x20 /* R W MIDI Reset input FIFO pointer */ +# define M_C_RESET_OUTPUT_FIFO 0x40 /* R W MIDI Reset output FIFO pointer */ +# define M_C_ENA_THRU_MODE 0x80 /* R W MIDI Echo input to output (THRU) */ + +#define MIDI_STATUS 0x1B88 /* R W MIDI Midi (interrupt) status register */ +# define M_S_TIMESTAMP 0x01 /* R W MIDI Midi time stamp interrupt occurred */ +# define M_S_COMPARE 0x02 /* R W MIDI Midi compare time interrupt occurred */ +# define M_S_INPUT_AVAIL 0x04 /* R W MIDI Midi input data available interrupt occurred */ +# define M_S_OUTPUT_EMPTY 0x08 /* R W MIDI Midi output FIFO empty interrupt occurred */ +# define M_S_OUTPUT_HALF_EMPTY 0x10 /* R W MIDI Midi output FIFO half empty interrupt occurred */ +# define M_S_INPUT_OVERRUN 0x20 /* R W MIDI Midi input overrun error occurred */ +# define M_S_OUTPUT_OVERRUN 0x40 /* R W MIDI Midi output overrun error occurred */ +# define M_S_FRAMING_ERROR 0x80 /* R W MIDI Midi input framing error occurred */ + +#define MIDI_FIFO_STATUS 0x1B89 /* R W MIDI Midi fifo status */ +#define MIDI_DATA 0x178A /* R W MIDI Midi data register */ +#define MIDI_INPUT_AVAILABLE 0x0f /* RW MIDI */ diff --git a/sys/i386/isa/sound/patmgr.c b/sys/i386/isa/sound/patmgr.c new file mode 100644 index 0000000..752b2e9 --- /dev/null +++ b/sys/i386/isa/sound/patmgr.c @@ -0,0 +1,267 @@ +/* + * sound/patmgr.c + * + * The patch maneger interface for the /dev/sequencer + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +#define PATMGR_C +#include <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_SEQUENCER) + +static int *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 int *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].aborting = 0; + server_wait_flag[dev].mode = 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.mode & WK_SLEEP)) { + appl_wait_flag.mode = WK_WAKEUP; + wakeup(appl_proc); + }; + } + pmgr_opened[dev] = 0; +} + +int +pmgr_read(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) +{ + u_long flags; + int ok = 0; + + if (count != sizeof(struct patmgr_info)) { + printf("PATMGR%d: Invalid read count\n", dev); + return -(EIO); + } + while (!ok && !(server_wait_flag[dev].aborting)) { + flags = splhigh(); + + while (!(mbox[dev] && msg_direction[dev] == A_TO_S) && + !(server_wait_flag[dev].aborting)) { + + int chn; + server_procs[dev] = &chn; + DO_SLEEP(chn, server_wait_flag[dev], 0); + + } + + if (mbox[dev] && msg_direction[dev] == A_TO_S) { + + if (uiomove((char *) mbox[dev], count, buf)) { + printf("sb: Bad copyout()!\n"); + }; + msg_direction[dev] = 0; + ok = 1; + } + splx(flags); + + } + + if (!ok) + return -(EINTR); + return count; +} + +int +pmgr_write(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) +{ + u_long flags; + + if (count < 4) { + printf("PATMGR%d: Write count < 4\n", dev); + return -(EIO); + } + if (uiomove((char *) mbox[dev], 4, buf)) { + printf("sb: Bad copyin()!\n"); + }; + + if (*(u_char *) mbox[dev] == SEQ_FULLSIZE) { + int tmp_dev; + + tmp_dev = ((u_short *) mbox[dev])[2]; + if (tmp_dev != dev) + return -(ENXIO); + + return synth_devs[dev]->load_patch(dev, *(u_short *) mbox[dev], + buf, 4, count, 1); + } + if (count != sizeof(struct patmgr_info)) { + printf("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. + */ + + flags = splhigh(); + + if (mbox[dev] && !msg_direction[dev]) { + + if (uiomove(&((char *) mbox[dev])[4], count - 4, buf)) { + printf("sb: Bad copyin()!\n"); + }; + msg_direction[dev] = S_TO_A; + + if ((appl_wait_flag.mode & WK_SLEEP)) { + appl_wait_flag.mode = WK_WAKEUP; + wakeup(appl_proc); + } + } + splx(flags); + + return count; +} + +int +pmgr_access(int dev, struct patmgr_info * rec) +{ + u_long flags; + int err = 0; + + flags = splhigh(); + + if (mbox[dev]) + printf(" PATMGR: Server %d mbox full. Why?\n", dev); + else { + int chn; + + rec->key = PM_K_COMMAND; + mbox[dev] = rec; + msg_direction[dev] = A_TO_S; + + if ((server_wait_flag[dev].mode & WK_SLEEP)) { + server_wait_flag[dev].mode = WK_WAKEUP; + wakeup(server_procs[dev]); + } + + + appl_proc = &chn; + DO_SLEEP(chn, appl_wait_flag, 0); + + 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; + } + + splx(flags); + + return err; +} + +int +pmgr_inform(int dev, int event, u_long p1, u_long p2, u_long p3, u_long p4) +{ + u_long flags; + int err = 0; + + struct patmgr_info *tmp_mbox; + + if (!pmgr_opened[dev]) + return 0; + + tmp_mbox = (struct patmgr_info *) malloc(sizeof(struct patmgr_info), M_TEMP, M_WAITOK); + + if (tmp_mbox == NULL) { + printf("pmgr: Couldn't allocate memory for a message\n"); + return 0; + } + flags = splhigh(); + + if (mbox[dev]) + printf(" PATMGR: Server %d mbox full. Why?\n", dev); + else { + int chn; + + 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].mode & WK_SLEEP)) { + server_wait_flag[dev].mode = WK_WAKEUP; + wakeup(server_procs[dev]); + } + + + appl_proc = &chn; + DO_SLEEP(chn, appl_wait_flag, 0); + + mbox[dev] = NULL; + msg_direction[dev] = 0; + } + + splx(flags); + free(tmp_mbox, M_TEMP); + + return err; +} + +#endif diff --git a/sys/i386/isa/sound/pcm86.c b/sys/i386/isa/sound/pcm86.c new file mode 100644 index 0000000..9c16d9a --- /dev/null +++ b/sys/i386/isa/sound/pcm86.c @@ -0,0 +1,2194 @@ +/* + * PC-9801-86 PCM driver for FreeBSD(98). + * + * Copyright (c) 1995 NAGAO Tadaaki (ABTK) + * All rights reserved. + * + * 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 AND 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. + * + * $Id: pcm86.c,v 1.5 1998/12/04 22:54:50 archie Exp $ + */ + +/* + * !! NOTE !! : + * This file DOES NOT belong to the VoxWare distribution though it works + * as part of the VoxWare drivers. It is FreeBSD(98) original. + * -- Nagao (nagao@cs.titech.ac.jp) + */ + + +#include <i386/isa/sound/sound_config.h> + +#ifdef CONFIGURE_SOUNDCARD + +#if !defined(EXCLUDE_PCM86) && !defined(EXCLUDE_AUDIO) + + +/* + * Constants + */ + +#define YES 1 +#define NO 0 + +#define IMODE_NONE 0 +#define IMODE_INPUT 1 +#define IMODE_OUTPUT 2 + +/* PC-9801-86 specific constants */ +#define PCM86_IOBASE 0xa460 /* PCM I/O ports */ +#define PCM86_FIFOSIZE 32768 /* There is a 32kB FIFO buffer on 86-board */ + +/* XXX -- These values should be chosen appropriately. */ +#define PCM86_INTRSIZE_OUT 1024 +#define PCM86_INTRSIZE_IN (PCM86_FIFOSIZE / 2 - 128) +#define DEFAULT_VOLUME 15 /* 0(min) - 15(max) */ + + +/* + * Switches for debugging and experiments + */ + +/* #define PCM86_DEBUG */ + +#ifdef PCM86_DEBUG +# ifdef DEB +# undef DEB +# endif +# define DEB(x) x +#endif + + +/* + * Private variables and types + */ + +typedef unsigned char pcm_data; + +enum board_type { + NO_SUPPORTED_BOARD = 0, + PC980186_FAMILY = 1, + PC980173_FAMILY = 2 +}; + +static char *board_name[] = { + /* Each must be of the length less than 32 bytes. */ + "No supported board", + "PC-9801-86 soundboard", + "PC-9801-73 soundboard" +}; + +/* Current status of the driver */ +static struct { + int iobase; + int irq; + enum board_type board_type; + int opened; + int format; + int bytes; + int chipspeedno; + int chipspeed; + int speed; + int stereo; + int volume; + int intr_busy; + int intr_size; + int intr_mode; + int intr_last; + int intr_trailer; + pcm_data * pdma_buf; + int pdma_count; + int pdma_chunkcount; + int acc; + int last_l; + int last_r; +} pcm_s; + +static struct { + pcm_data buff[4]; + int size; +} tmpbuf; + +static int my_dev = 0; +static char pcm_initialized = NO; + +/* 86-board supports only the following rates. */ +static int rates_tbl[8] = { +#ifndef WAVEMASTER_FREQ + 44100, 33075, 22050, 16538, 11025, 8269, 5513, 4134 +#else + /* + * It is said that Q-Vision's WaveMaster of some earlier lot(s?) has + * sampling rates incompatible with PC-9801-86. + * But I'm not sure whether the following rates are correct, especially + * 4000Hz. + */ + 44100, 33075, 22050, 16000, 11025, 8000, 5510, 4000 +#endif +}; + +/* u-law to linear translation table */ +static pcm_data ulaw2linear[256] = { + 130, 134, 138, 142, 146, 150, 154, 158, + 162, 166, 170, 174, 178, 182, 186, 190, + 193, 195, 197, 199, 201, 203, 205, 207, + 209, 211, 213, 215, 217, 219, 221, 223, + 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, + 240, 241, 241, 242, 242, 243, 243, 244, + 244, 245, 245, 246, 246, 247, 247, 248, + 248, 248, 249, 249, 249, 249, 250, 250, + 250, 250, 251, 251, 251, 251, 252, 252, + 252, 252, 252, 252, 253, 253, 253, 253, + 253, 253, 253, 253, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 125, 121, 117, 113, 109, 105, 101, 97, + 93, 89, 85, 81, 77, 73, 69, 65, + 62, 60, 58, 56, 54, 52, 50, 48, + 46, 44, 42, 40, 38, 36, 34, 32, + 30, 29, 28, 27, 26, 25, 24, 23, + 22, 21, 20, 19, 18, 17, 16, 15, + 15, 14, 14, 13, 13, 12, 12, 11, + 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 6, 6, 5, 5, + 5, 5, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 2, 2, 2, 2, + 2, 2, 2, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* linear to u-law translation table */ +static pcm_data linear2ulaw[256] = { + 255, 231, 219, 211, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 175, 174, 173, 172, 171, 170, 169, 168, + 167, 166, 165, 164, 163, 162, 161, 160, + 159, 159, 158, 158, 157, 157, 156, 156, + 155, 155, 154, 154, 153, 153, 152, 152, + 151, 151, 150, 150, 149, 149, 148, 148, + 147, 147, 146, 146, 145, 145, 144, 144, + 143, 143, 143, 143, 142, 142, 142, 142, + 141, 141, 141, 141, 140, 140, 140, 140, + 139, 139, 139, 139, 138, 138, 138, 138, + 137, 137, 137, 137, 136, 136, 136, 136, + 135, 135, 135, 135, 134, 134, 134, 134, + 133, 133, 133, 133, 132, 132, 132, 132, + 131, 131, 131, 131, 130, 130, 130, 130, + 129, 129, 129, 129, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 1, 1, 1, + 1, 2, 2, 2, 2, 3, 3, 3, + 3, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 9, 9, 9, + 9, 10, 10, 10, 10, 11, 11, 11, + 11, 12, 12, 12, 12, 13, 13, 13, + 13, 14, 14, 14, 14, 15, 15, 15, + 15, 16, 16, 17, 17, 18, 18, 19, + 19, 20, 20, 21, 21, 22, 22, 23, + 23, 24, 24, 25, 25, 26, 26, 27, + 27, 28, 28, 29, 29, 30, 30, 31, + 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 50, 52, 54, 56, 58, 60, + 62, 65, 69, 73, 77, 83, 91, 103 +}; + + +/* + * Prototypes + */ + +static int pcm86_detect(struct address_info *); + +static int pcm86_open(int, int); +static void pcm86_close(int); +static void pcm86_output_block(int, unsigned long, int, int, int); +static void pcm86_start_input(int, unsigned long, int, int, int); +static int pcm86_ioctl(int, unsigned int, unsigned int, int); +static int pcm86_prepare_for_input(int, int, int); +static int pcm86_prepare_for_output(int, int, int); +static void pcm86_reset(int); +static void pcm86_halt_xfer(int); + +static void dsp73_send_command(unsigned char); +static void dsp73_send_data(unsigned char); +static void dsp73_init(void); +static int set_format(int); +static int set_speed(int); +static int set_stereo(int); +static void set_volume(int); +static void fifo_start(int); +static void fifo_stop(void); +static void fifo_reset(void); +static void fifo_output_block(void); +static int fifo_send(pcm_data *, int); +static void fifo_sendtrailer(int); +static void fifo_send_stereo(pcm_data *, int); +static void fifo_send_monoral(pcm_data *, int); +static void fifo_send_stereo_ulaw(pcm_data *, int); +static void fifo_send_stereo_8(pcm_data *, int, int); +static void fifo_send_stereo_16le(pcm_data *, int, int); +static void fifo_send_stereo_16be(pcm_data *, int, int); +static void fifo_send_mono_ulaw(pcm_data *, int); +static void fifo_send_mono_8(pcm_data *, int, int); +static void fifo_send_mono_16le(pcm_data *, int, int); +static void fifo_send_mono_16be(pcm_data *, int, int); +static void fifo_input_block(void); +static void fifo_recv(pcm_data *, int); +static void fifo_recv_stereo(pcm_data *, int); +static void fifo_recv_monoral(pcm_data *, int); +static void fifo_recv_stereo_ulaw(pcm_data *, int); +static void fifo_recv_stereo_8(pcm_data *, int, int); +static void fifo_recv_stereo_16le(pcm_data *, int, int); +static void fifo_recv_stereo_16be(pcm_data *, int, int); +static void fifo_recv_mono_ulaw(pcm_data *, int); +static void fifo_recv_mono_8(pcm_data *, int, int); +static void fifo_recv_mono_16le(pcm_data *, int, int); +static void fifo_recv_mono_16be(pcm_data *, int, int); +static void pcm_stop(void); +static void pcm_init(void); + + +/* + * Identity + */ + +static struct audio_operations pcm86_operations = +{ + "PC-9801-86 SoundBoard", /* filled in properly by auto configuration */ + NOTHING_SPECIAL, + ( AFMT_MU_LAW | + AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | + AFMT_S8 | AFMT_U16_LE | AFMT_U16_BE ), + NULL, + pcm86_open, + pcm86_close, + pcm86_output_block, + pcm86_start_input, + pcm86_ioctl, + pcm86_prepare_for_input, + pcm86_prepare_for_output, + pcm86_reset, + pcm86_halt_xfer, + NULL, + NULL +}; + + +/* + * Codes for internal use + */ + +static void +dsp73_send_command(unsigned char command) +{ + /* wait for RDY */ + while ((inb(pcm_s.iobase + 2) & 0x48) != 8); + + /* command mode */ + outb(pcm_s.iobase + 2, (inb(pcm_s.iobase + 2) & 0x20) | 3); + + /* wait for RDY */ + while ((inb(pcm_s.iobase + 2) & 0x48) != 8); + + /* send command */ + outb(pcm_s.iobase + 4, command); +} + + +static void +dsp73_send_data(unsigned char data) +{ + /* wait for RDY */ + while ((inb(pcm_s.iobase + 2) & 0x48) != 8); + + /* data mode */ + outb(pcm_s.iobase + 2, (inb(pcm_s.iobase + 2) & 0x20) | 0x83); + + /* wait for RDY */ + while ((inb(pcm_s.iobase + 2) & 0x48) != 8); + + /* send command */ + outb(pcm_s.iobase + 4, data); +} + + +static void +dsp73_init(void) +{ + const unsigned char dspinst[15] = { + 0x00, 0x00, 0x27, + 0x3f, 0xe0, 0x01, + 0x00, 0x00, 0x27, + 0x36, 0x5a, 0x0d, + 0x3e, 0x60, 0x04 + }; + unsigned char t; + int i; + + /* reset DSP */ + t = inb(pcm_s.iobase + 2); + outb(pcm_s.iobase + 2, (t & 0x80) | 0x23); + + /* mute on */ + dsp73_send_command(0x04); + dsp73_send_data(0x6f); + dsp73_send_data(0x3c); + + /* write DSP instructions */ + dsp73_send_command(0x01); + dsp73_send_data(0x00); + for (i = 0; i < 16; i++) + dsp73_send_data(dspinst[i]); + + /* mute off */ + dsp73_send_command(0x04); + dsp73_send_data(0x6f); + dsp73_send_data(0x30); + + /* wait for RDY */ + while ((inb(pcm_s.iobase + 2) & 0x48) != 8); + + outb(pcm_s.iobase + 2, 3); +} + + +static int +set_format(int format) +{ + switch (format) { + case AFMT_MU_LAW: + case AFMT_S8: + case AFMT_U8: + pcm_s.format = format; + pcm_s.bytes = 1; /* 8bit */ + break; + case AFMT_S16_LE: + case AFMT_U16_LE: + case AFMT_S16_BE: + case AFMT_U16_BE: + pcm_s.format = format; + pcm_s.bytes = 2; /* 16bit */ + break; + case AFMT_QUERY: + break; + default: + return -1; + } + + return pcm_s.format; +} + + +static int +set_speed(int speed) +{ + int i; + + if (speed < 4000) /* Minimum 4000Hz */ + speed = 4000; + if (speed > 44100) /* Maximum 44100Hz */ + speed = 44100; + for (i = 7; i >= 0; i--) { + if (speed <= rates_tbl[i]) { + pcm_s.chipspeedno = i; + pcm_s.chipspeed = rates_tbl[i]; + break; + } + } + pcm_s.speed = speed; + + return speed; +} + + +static int +set_stereo(int stereo) +{ + pcm_s.stereo = stereo ? YES : NO; + + return pcm_s.stereo; +} + + +static void +set_volume(int volume) +{ + if (volume < 0) + volume = 0; + if (volume > 15) + volume = 15; + pcm_s.volume = volume; + + outb(pcm_s.iobase + 6, 0xaf - volume); /* D/A -> LINE OUT */ + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); + outb(pcm_s.iobase + 6, 0x20); /* FM -> A/D */ + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); + outb(pcm_s.iobase + 6, 0x60); /* LINE IN -> A/D */ + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); +} + + +static void +fifo_start(int mode) +{ + unsigned char tmp; + + /* Set frame length & panpot(LR). */ + tmp = inb(pcm_s.iobase + 10) & 0x88; + outb(pcm_s.iobase + 10, tmp | ((pcm_s.bytes == 1) ? 0x72 : 0x32)); + + tmp = pcm_s.chipspeedno; + if (mode == IMODE_INPUT) + tmp |= 0x40; + + /* Reset intr. flag. */ + outb(pcm_s.iobase + 8, tmp); + outb(pcm_s.iobase + 8, tmp | 0x10); + + /* Enable FIFO intr. */ + outb(pcm_s.iobase + 8, tmp | 0x30); + + /* Set intr. interval. */ + outb(pcm_s.iobase + 10, pcm_s.intr_size / 128 - 1); + + /* Start intr. */ + outb(pcm_s.iobase + 8, tmp | 0xb0); +} + + +static void +fifo_stop(void) +{ + unsigned char tmp; + + /* Reset intr. flag, and disable FIFO intr. */ + tmp = inb(pcm_s.iobase + 8) & 0x0f; + outb(pcm_s.iobase + 8, tmp); +} + + +static void +fifo_reset(void) +{ + unsigned char tmp; + + /* Reset FIFO. */ + tmp = inb(pcm_s.iobase + 8) & 0x77; + outb(pcm_s.iobase + 8, tmp | 0x8); + outb(pcm_s.iobase + 8, tmp); +} + + +static void +fifo_output_block(void) +{ + int chunksize, count; + + if (pcm_s.pdma_chunkcount) { + /* Update chunksize and then send the next chunk to FIFO. */ + chunksize = pcm_s.pdma_count / pcm_s.pdma_chunkcount--; + count = fifo_send(pcm_s.pdma_buf, chunksize); + } else { + /* ??? something wrong... */ + printk("pcm0: chunkcount overrun\n"); + chunksize = count = 0; + } + + if (((audio_devs[my_dev]->dmap->qlen < 2) && (pcm_s.pdma_chunkcount == 0)) + || (count < pcm_s.intr_size)) { + /* The sent chunk seems to be the last one. */ + fifo_sendtrailer(pcm_s.intr_size); + pcm_s.intr_last = YES; + } + + pcm_s.pdma_buf += chunksize; + pcm_s.pdma_count -= chunksize; +} + + +static int +fifo_send(pcm_data *buf, int count) +{ + int i, length, r, cnt, rslt; + pcm_data *p; + + /* Calculate the length of PCM frames. */ + cnt = count + tmpbuf.size; + length = pcm_s.bytes << pcm_s.stereo; + r = cnt % length; + cnt -= r; + + if (cnt > 0) { + if (pcm_s.stereo) + fifo_send_stereo(buf, cnt); + else + fifo_send_monoral(buf, cnt); + /* Carry over extra data which doesn't seem to be a full PCM frame. */ + p = (pcm_data *)buf + count - r; + for (i = 0; i < r; i++) + tmpbuf.buff[i] = *p++; + } else { + /* Carry over extra data which doesn't seem to be a full PCM frame. */ + p = (pcm_data *)buf; + for (i = tmpbuf.size; i < r; i++) + tmpbuf.buff[i] = *p++; + } + tmpbuf.size = r; + + rslt = ((cnt / length) * pcm_s.chipspeed / pcm_s.speed) * pcm_s.bytes * 2; +#ifdef PCM86_DEBUG + printk("fifo_send(): %d bytes sent\n", rslt); +#endif + return rslt; +} + + +static void +fifo_sendtrailer(int count) +{ + /* Send trailing zeros to the FIFO buffer. */ + int i; + + for (i = 0; i < count; i++) + outb(pcm_s.iobase + 12, 0); + pcm_s.intr_trailer = YES; + +#ifdef PCM86_DEBUG + printk("fifo_sendtrailer(): %d bytes sent\n", count); +#endif +} + + +static void +fifo_send_stereo(pcm_data *buf, int count) +{ + /* Convert format and sampling speed. */ + switch (pcm_s.format) { + case AFMT_MU_LAW: + fifo_send_stereo_ulaw(buf, count); + break; + case AFMT_S8: + fifo_send_stereo_8(buf, count, NO); + break; + case AFMT_U8: + fifo_send_stereo_8(buf, count, YES); + break; + case AFMT_S16_LE: + fifo_send_stereo_16le(buf, count, NO); + break; + case AFMT_U16_LE: + fifo_send_stereo_16le(buf, count, YES); + break; + case AFMT_S16_BE: + fifo_send_stereo_16be(buf, count, NO); + break; + case AFMT_U16_BE: + fifo_send_stereo_16be(buf, count, YES); + break; + } +} + + +static void +fifo_send_monoral(pcm_data *buf, int count) +{ + /* Convert format and sampling speed. */ + switch (pcm_s.format) { + case AFMT_MU_LAW: + fifo_send_mono_ulaw(buf, count); + break; + case AFMT_S8: + fifo_send_mono_8(buf, count, NO); + break; + case AFMT_U8: + fifo_send_mono_8(buf, count, YES); + break; + case AFMT_S16_LE: + fifo_send_mono_16le(buf, count, NO); + break; + case AFMT_U16_LE: + fifo_send_mono_16le(buf, count, YES); + break; + case AFMT_S16_BE: + fifo_send_mono_16be(buf, count, NO); + break; + case AFMT_U16_BE: + fifo_send_mono_16be(buf, count, YES); + break; + } +} + + +static void +fifo_send_stereo_ulaw(pcm_data *buf, int count) +{ + int i; + signed char dl, dl0, dl1, dr, dr0, dr1; + pcm_data t[2]; + + if (tmpbuf.size > 0) + t[0] = ulaw2linear[tmpbuf.buff[0]]; + else + t[0] = ulaw2linear[*buf++]; + t[1] = ulaw2linear[*buf++]; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[0]); + outb(pcm_s.iobase + 12, t[1]); + count -= 2; + for (i = 0; i < count; i++) + outb(pcm_s.iobase + 12, ulaw2linear[*buf++]); + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = t[0]; + dr1 = t[1]; + i = 0; + count /= 2; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + dl0 = dl1; + dr0 = dr1; + if (i < count) { + dl1 = ulaw2linear[*buf++]; + dr1 = ulaw2linear[*buf++]; + } else + dl1 = dr1 = 0; + } + dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.chipspeed; + dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, dl); + outb(pcm_s.iobase + 12, dr); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_send_stereo_8(pcm_data *buf, int count, int uflag) +{ + int i; + signed char dl, dl0, dl1, dr, dr0, dr1, zlev; + pcm_data t[2]; + + zlev = uflag ? -128 : 0; + + if (tmpbuf.size > 0) + t[0] = tmpbuf.buff[0] + zlev; + else + t[0] = *buf++ + zlev; + t[1] = *buf++ + zlev; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[0]); + outb(pcm_s.iobase + 12, t[1]); + count -= 2; + for (i = 0; i < count; i++) + outb(pcm_s.iobase + 12, *buf++ + zlev); + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = t[0]; + dr1 = t[1]; + i = 0; + count /= 2; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + dl0 = dl1; + dr0 = dr1; + if (i < count) { + dl1 = *buf++ + zlev; + dr1 = *buf++ + zlev; + } else + dl1 = dr1 = 0; + } + dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.chipspeed; + dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, dl); + outb(pcm_s.iobase + 12, dr); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_send_stereo_16le(pcm_data *buf, int count, int uflag) +{ + int i; + short dl, dl0, dl1, dr, dr0, dr1, zlev; + pcm_data t[4]; + + zlev = uflag ? -128 : 0; + + for (i = 0; i < 4; i++) + t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[1] + zlev); + outb(pcm_s.iobase + 12, t[0]); + outb(pcm_s.iobase + 12, t[3] + zlev); + outb(pcm_s.iobase + 12, t[2]); + count = count / 2 - 2; + for (i = 0; i < count; i++) { + outb(pcm_s.iobase + 12, *(buf + 1) + zlev); + outb(pcm_s.iobase + 12, *buf); + buf += 2; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = t[0] + ((t[1] + zlev) << 8); + dr1 = t[2] + ((t[3] + zlev) << 8); + i = 0; + count /= 4; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + dl0 = dl1; + dr0 = dr1; + if (i < count) { + dl1 = *buf + ((*(buf + 1) + zlev) << 8); + buf += 2; + dr1 = *buf + ((*(buf + 1) + zlev) << 8); + buf += 2; + } else + dl1 = dr1 = 0; + } + dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.chipspeed; + dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, (dl >> 8) & 0xff); + outb(pcm_s.iobase + 12, dl & 0xff); + outb(pcm_s.iobase + 12, (dr >> 8) & 0xff); + outb(pcm_s.iobase + 12, dr & 0xff); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_send_stereo_16be(pcm_data *buf, int count, int uflag) +{ + int i; + short dl, dl0, dl1, dr, dr0, dr1, zlev; + pcm_data t[4]; + + zlev = uflag ? -128 : 0; + + for (i = 0; i < 4; i++) + t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[0] + zlev); + outb(pcm_s.iobase + 12, t[1]); + outb(pcm_s.iobase + 12, t[2] + zlev); + outb(pcm_s.iobase + 12, t[3]); + count = count / 2 - 2; + for (i = 0; i < count; i++) { + outb(pcm_s.iobase + 12, *buf + zlev); + outb(pcm_s.iobase + 12, *(buf + 1)); + buf += 2; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = ((t[0] + zlev) << 8) + t[1]; + dr1 = ((t[2] + zlev) << 8) + t[3]; + i = 0; + count /= 4; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + dl0 = dl1; + dr0 = dr1; + if (i < count) { + dl1 = ((*buf + zlev) << 8) + *(buf + 1); + buf += 2; + dr1 = ((*buf + zlev) << 8) + *(buf + 1); + buf += 2; + } else + dl1 = dr1 = 0; + } + dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.chipspeed; + dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, (dl >> 8) & 0xff); + outb(pcm_s.iobase + 12, dl & 0xff); + outb(pcm_s.iobase + 12, (dr >> 8) & 0xff); + outb(pcm_s.iobase + 12, dr & 0xff); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_send_mono_ulaw(pcm_data *buf, int count) +{ + int i; + signed char d, d0, d1; + + if (pcm_s.speed == pcm_s.chipspeed) + /* No reason to convert the pcm speed. */ + for (i = 0; i < count; i++) { + d = ulaw2linear[*buf++]; + outb(pcm_s.iobase + 12, d); + outb(pcm_s.iobase + 12, d); + } + else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = ulaw2linear[*buf++]; + i = 0; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + d0 = d1; + d1 = (i < count) ? ulaw2linear[*buf++] : 0; + } + d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, d); + outb(pcm_s.iobase + 12, d); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_send_mono_8(pcm_data *buf, int count, int uflag) +{ + int i; + signed char d, d0, d1, zlev; + + zlev = uflag ? -128 : 0; + + if (pcm_s.speed == pcm_s.chipspeed) + /* No reason to convert the pcm speed. */ + for (i = 0; i < count; i++) { + d = *buf++ + zlev; + outb(pcm_s.iobase + 12, d); + outb(pcm_s.iobase + 12, d); + } + else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = *buf++ + zlev; + i = 0; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + d0 = d1; + d1 = (i < count) ? *buf++ + zlev : 0; + } + d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, d); + outb(pcm_s.iobase + 12, d); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_send_mono_16le(pcm_data *buf, int count, int uflag) +{ + int i; + short d, d0, d1, zlev; + pcm_data t[2]; + + zlev = uflag ? -128 : 0; + + for (i = 0; i < 2; i++) + t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[1] + zlev); + outb(pcm_s.iobase + 12, t[0]); + outb(pcm_s.iobase + 12, t[1] + zlev); + outb(pcm_s.iobase + 12, t[0]); + count = count / 2 - 1; + for (i = 0; i < count; i++) { + outb(pcm_s.iobase + 12, *(buf + 1) + zlev); + outb(pcm_s.iobase + 12, *buf); + outb(pcm_s.iobase + 12, *(buf + 1) + zlev); + outb(pcm_s.iobase + 12, *buf); + buf += 2; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = t[0] + ((t[1] + zlev) << 8); + i = 0; + count /= 2; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + d0 = d1; + if (i < count) { + d1 = *buf + ((*(buf + 1) + zlev) << 8); + buf += 2; + } else + d1 = 0; + } + d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, (d >> 8) & 0xff); + outb(pcm_s.iobase + 12, d & 0xff); + outb(pcm_s.iobase + 12, (d >> 8) & 0xff); + outb(pcm_s.iobase + 12, d & 0xff); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_send_mono_16be(pcm_data *buf, int count, int uflag) +{ + int i; + short d, d0, d1, zlev; + pcm_data t[2]; + + zlev = uflag ? -128 : 0; + + for (i = 0; i < 2; i++) + t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[0] + zlev); + outb(pcm_s.iobase + 12, t[1]); + outb(pcm_s.iobase + 12, t[0] + zlev); + outb(pcm_s.iobase + 12, t[1]); + count = count / 2 - 1; + for (i = 0; i < count; i++) { + outb(pcm_s.iobase + 12, *buf + zlev); + outb(pcm_s.iobase + 12, *(buf + 1)); + outb(pcm_s.iobase + 12, *buf + zlev); + outb(pcm_s.iobase + 12, *(buf + 1)); + buf += 2; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = ((t[0] + zlev) << 8) + t[1]; + i = 0; + count /= 2; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + d0 = d1; + if (i < count) { + d1 = ((*buf + zlev) << 8) + *(buf + 1); + buf += 2; + } else + d1 = 0; + } + d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.chipspeed; +/* outb(pcm_s.iobase + 12, d & 0xff); + outb(pcm_s.iobase + 12, (d >> 8) & 0xff); + outb(pcm_s.iobase + 12, d & 0xff); + outb(pcm_s.iobase + 12, (d >> 8) & 0xff); */ + outb(pcm_s.iobase + 12, (d >> 8) & 0xff); + outb(pcm_s.iobase + 12, d & 0xff); + outb(pcm_s.iobase + 12, (d >> 8) & 0xff); + outb(pcm_s.iobase + 12, d & 0xff); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_input_block(void) +{ + int chunksize; + + if (pcm_s.pdma_chunkcount) { + /* Update chunksize and then receive the next chunk from FIFO. */ + chunksize = pcm_s.pdma_count / pcm_s.pdma_chunkcount--; + fifo_recv(pcm_s.pdma_buf, chunksize); + pcm_s.pdma_buf += chunksize; + pcm_s.pdma_count -= chunksize; + } else + /* ??? something wrong... */ + printk("pcm0: chunkcount overrun\n"); +} + + +static void +fifo_recv(pcm_data *buf, int count) +{ + int i; + + if (count > tmpbuf.size) { + for (i = 0; i < tmpbuf.size; i++) + *buf++ = tmpbuf.buff[i]; + count -= tmpbuf.size; + tmpbuf.size = 0; + if (pcm_s.stereo) + fifo_recv_stereo(buf, count); + else + fifo_recv_monoral(buf, count); + } else { + for (i = 0; i < count; i++) + *buf++ = tmpbuf.buff[i]; + for (i = 0; i < tmpbuf.size - count; i++) + tmpbuf.buff[i] = tmpbuf.buff[i + count]; + tmpbuf.size -= count; + } + +#ifdef PCM86_DEBUG + printk("fifo_recv(): %d bytes received\n", + ((count / (pcm_s.bytes << pcm_s.stereo)) * pcm_s.chipspeed + / pcm_s.speed) * pcm_s.bytes * 2); +#endif +} + + +static void +fifo_recv_stereo(pcm_data *buf, int count) +{ + /* Convert format and sampling speed. */ + switch (pcm_s.format) { + case AFMT_MU_LAW: + fifo_recv_stereo_ulaw(buf, count); + break; + case AFMT_S8: + fifo_recv_stereo_8(buf, count, NO); + break; + case AFMT_U8: + fifo_recv_stereo_8(buf, count, YES); + break; + case AFMT_S16_LE: + fifo_recv_stereo_16le(buf, count, NO); + break; + case AFMT_U16_LE: + fifo_recv_stereo_16le(buf, count, YES); + break; + case AFMT_S16_BE: + fifo_recv_stereo_16be(buf, count, NO); + break; + case AFMT_U16_BE: + fifo_recv_stereo_16be(buf, count, YES); + break; + } +} + + +static void +fifo_recv_monoral(pcm_data *buf, int count) +{ + /* Convert format and sampling speed. */ + switch (pcm_s.format) { + case AFMT_MU_LAW: + fifo_recv_mono_ulaw(buf, count); + break; + case AFMT_S8: + fifo_recv_mono_8(buf, count, NO); + break; + case AFMT_U8: + fifo_recv_mono_8(buf, count, YES); + break; + case AFMT_S16_LE: + fifo_recv_mono_16le(buf, count, NO); + break; + case AFMT_U16_LE: + fifo_recv_mono_16le(buf, count, YES); + break; + case AFMT_S16_BE: + fifo_recv_mono_16be(buf, count, NO); + break; + case AFMT_U16_BE: + fifo_recv_mono_16be(buf, count, YES); + break; + } +} + + +static void +fifo_recv_stereo_ulaw(pcm_data *buf, int count) +{ + int i, cnt; + signed char dl, dl0, dl1, dr, dr0, dr1; + + cnt = count / 2; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + *buf++ = linear2ulaw[inb(pcm_s.iobase + 12)]; + *buf++ = linear2ulaw[inb(pcm_s.iobase + 12)]; + } + if (count % 2) { + *buf++ = linear2ulaw[inb(pcm_s.iobase + 12)]; + tmpbuf.buff[0] = linear2ulaw[inb(pcm_s.iobase + 12)]; + tmpbuf.size = 1; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = linear2ulaw[dl & 0xff]; + *buf++ = linear2ulaw[dr & 0xff]; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 2) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = linear2ulaw[dl & 0xff]; + tmpbuf.buff[0] = linear2ulaw[dr & 0xff]; + tmpbuf.size = 1; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_recv_stereo_8(pcm_data *buf, int count, int uflag) +{ + int i, cnt; + signed char dl, dl0, dl1, dr, dr0, dr1, zlev; + + zlev = uflag ? -128 : 0; + + cnt = count / 2; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + *buf++ = inb(pcm_s.iobase + 12) + zlev; + *buf++ = inb(pcm_s.iobase + 12) + zlev; + } + if (count % 2) { + *buf++ = inb(pcm_s.iobase + 12) + zlev; + tmpbuf.buff[0] = inb(pcm_s.iobase + 12) + zlev; + tmpbuf.size = 1; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = dl + zlev; + *buf++ = dr + zlev; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 2) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = dl + zlev; + tmpbuf.buff[0] = dr + zlev; + tmpbuf.size = 1; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_recv_stereo_16le(pcm_data *buf, int count, int uflag) +{ + int i, cnt; + short dl, dl0, dl1, dr, dr0, dr1, zlev; + pcm_data t[4]; + + zlev = uflag ? -128 : 0; + + cnt = count / 4; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + *(buf + 1) = inb(pcm_s.iobase + 12) + zlev; + *buf = inb(pcm_s.iobase + 12); + *(buf + 3) = inb(pcm_s.iobase + 12) + zlev; + *(buf + 2) = inb(pcm_s.iobase + 12); + buf += 4; + } + if (count % 4) { + t[1] = inb(pcm_s.iobase + 12) + zlev; + t[0] = inb(pcm_s.iobase + 12); + t[3] = inb(pcm_s.iobase + 12) + zlev; + t[2] = inb(pcm_s.iobase + 12); + tmpbuf.size = 0; + for (i = 0; i < count % 4; i++) + *buf++ = t[i]; + for (i = count % 4; i < 4; i++) + tmpbuf.buff[tmpbuf.size++] = t[i]; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = dl & 0xff; + *buf++ = ((dl >> 8) & 0xff) + zlev; + *buf++ = dr & 0xff; + *buf++ = ((dr >> 8) & 0xff) + zlev; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 4) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + t[0] = dl & 0xff; + t[1] = ((dl >> 8) & 0xff) + zlev; + t[2] = dr & 0xff; + t[3] = ((dr >> 8) & 0xff) + zlev; + tmpbuf.size = 0; + for (i = 0; i < count % 4; i++) + *buf++ = t[i]; + for (i = count % 4; i < 4; i++) + tmpbuf.buff[tmpbuf.size++] = t[i]; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_recv_stereo_16be(pcm_data *buf, int count, int uflag) +{ + int i, cnt; + short dl, dl0, dl1, dr, dr0, dr1, zlev; + pcm_data t[4]; + + zlev = uflag ? -128 : 0; + + cnt = count / 4; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + *buf++ = inb(pcm_s.iobase + 12) + zlev; + *buf++ = inb(pcm_s.iobase + 12); + *buf++ = inb(pcm_s.iobase + 12) + zlev; + *buf++ = inb(pcm_s.iobase + 12); + } + if (count % 4) { + t[0] = inb(pcm_s.iobase + 12) + zlev; + t[1] = inb(pcm_s.iobase + 12); + t[2] = inb(pcm_s.iobase + 12) + zlev; + t[3] = inb(pcm_s.iobase + 12); + tmpbuf.size = 0; + for (i = 0; i < count % 4; i++) + *buf++ = t[i]; + for (i = count % 4; i < 4; i++) + tmpbuf.buff[tmpbuf.size++] = t[i]; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = ((dl >> 8) & 0xff) + zlev; + *buf++ = dl & 0xff; + *buf++ = ((dr >> 8) & 0xff) + zlev; + *buf++ = dr & 0xff; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 4) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + t[0] = ((dl >> 8) & 0xff) + zlev; + t[1] = dl & 0xff; + t[2] = ((dr >> 8) & 0xff) + zlev; + t[3] = dr & 0xff; + tmpbuf.size = 0; + for (i = 0; i < count % 4; i++) + *buf++ = t[i]; + for (i = count % 4; i < 4; i++) + tmpbuf.buff[tmpbuf.size++] = t[i]; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_recv_mono_ulaw(pcm_data *buf, int count) +{ + int i; + signed char d, d0, d1; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < count; i++) { + d = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + *buf++ = linear2ulaw[d & 0xff]; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + for (i = 0; i < count; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + d1 = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = linear2ulaw[d & 0xff]; + pcm_s.acc += pcm_s.chipspeed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_recv_mono_8(pcm_data *buf, int count, int uflag) +{ + int i; + signed char d, d0, d1, zlev; + + zlev = uflag ? -128 : 0; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < count; i++) { + d = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + *buf++ = d + zlev; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + for (i = 0; i < count; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + d1 = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = d + zlev; + pcm_s.acc += pcm_s.chipspeed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_recv_mono_16le(pcm_data *buf, int count, int uflag) +{ + int i, cnt; + short d, d0, d1, el, er, zlev; + + zlev = uflag ? -128 : 0; + + cnt = count / 2; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d = (el + er) >> 1; + *buf++ = d & 0xff; + *buf++ = ((d >> 8) & 0xff) + zlev; + } + if (count % 2) { + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d = (el + er) >> 1; + *buf++ = d & 0xff; + tmpbuf.buff[0] = ((d >> 8) & 0xff) + zlev; + tmpbuf.size = 1; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = d & 0xff; + *buf++ = ((d >> 8) & 0xff) + zlev; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 2) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = d & 0xff; + tmpbuf.buff[0] = ((d >> 8) & 0xff) + zlev; + tmpbuf.size = 1; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_recv_mono_16be(pcm_data *buf, int count, int uflag) +{ + int i, cnt; + short d, d0, d1, el, er, zlev; + + zlev = uflag ? -128 : 0; + + cnt = count / 2; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d = (el + er) >> 1; + *buf++ = ((d >> 8) & 0xff) + zlev; + *buf++ = d & 0xff; + } + if (count % 2) { + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d = (el + er) >> 1; + *buf++ = ((d >> 8) & 0xff) + zlev; + tmpbuf.buff[0] = d & 0xff; + tmpbuf.size = 1; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = ((d >> 8) & 0xff) + zlev; + *buf++ = d & 0xff; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 2) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = ((d >> 8) & 0xff) + zlev; + tmpbuf.buff[0] = d & 0xff; + tmpbuf.size = 1; + } + + pcm_s.last_l = d0; + } +} + + +static void +pcm_stop(void) +{ + fifo_stop(); /* stop FIFO */ + fifo_reset(); /* reset FIFO buffer */ + + /* Reset driver's status. */ + pcm_s.intr_busy = NO; + pcm_s.intr_last = NO; + pcm_s.intr_trailer = NO; + pcm_s.acc = 0; + pcm_s.last_l = 0; + pcm_s.last_r = 0; + + DEB(printk("pcm_stop\n")); +} + + +static void +pcm_init(void) +{ + /* Initialize registers on the board. */ + pcm_stop(); + if (pcm_s.board_type == PC980173_FAMILY) + dsp73_init(); + + /* Set default volume. */ + set_volume(DEFAULT_VOLUME); + + /* Initialize driver's status. */ + pcm_s.opened = NO; + pcm_initialized = YES; +} + + +/* + * Codes for global use + */ + +int +probe_pcm86(struct address_info *hw_config) +{ + return pcm86_detect(hw_config); +} + + +long +attach_pcm86(long mem_start, struct address_info *hw_config) +{ + if (pcm_s.board_type == NO_SUPPORTED_BOARD) + return mem_start; + + /* Initialize the board. */ + pcm_init(); + + printk("pcm0: <%s>", pcm86_operations.name); + + if (num_audiodevs < MAX_AUDIO_DEV) { + my_dev = num_audiodevs++; + audio_devs[my_dev] = &pcm86_operations; + audio_devs[my_dev]->buffcount = DSP_BUFFCOUNT; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; +#ifdef PCM86_DEBUG + printk("\nbuffsize = %d", DSP_BUFFSIZE); +#endif + } else + printk("pcm0: Too many PCM devices available"); + + return mem_start; +} + + +static int +pcm86_detect(struct address_info *hw_config) +{ + int opna_iobase = 0x188, irq = 12, i; + unsigned char tmp; + + if (hw_config->io_base == -1) { + printf("pcm0: iobase not specified. Assume default port(0x%x)\n", + PCM86_IOBASE); + hw_config->io_base = PCM86_IOBASE; + } + pcm_s.iobase = hw_config->io_base; + + /* auto configuration */ + tmp = inb(pcm_s.iobase) & 0xfc; + switch ((tmp & 0xf0) >> 4) { + case 2: + opna_iobase = 0x188; + pcm_s.board_type = PC980173_FAMILY; + break; + case 3: + opna_iobase = 0x288; + pcm_s.board_type = PC980173_FAMILY; + break; + case 4: + opna_iobase = 0x188; + pcm_s.board_type = PC980186_FAMILY; + break; + case 5: + opna_iobase = 0x288; + pcm_s.board_type = PC980186_FAMILY; + break; + default: + pcm_s.board_type = NO_SUPPORTED_BOARD; + return NO; + } + + /* Enable OPNA(YM2608) facilities. */ + outb(pcm_s.iobase, tmp | 0x01); + + /* Wait for OPNA to be ready. */ + i = 100000; /* Some large value */ + while((inb(opna_iobase) & 0x80) && (i-- > 0)); + + /* Make IOA/IOB port ready (IOA:input, IOB:output) */ + outb(opna_iobase, 0x07); + outb(0x5f, 0); /* Because OPNA ports are comparatively slow(?), */ + outb(0x5f, 0); /* we'd better wait a moment. */ + outb(0x5f, 0); + outb(0x5f, 0); + tmp = inb(opna_iobase + 2) & 0x3f; + outb(opna_iobase + 2, tmp | 0x80); + + /* Wait for OPNA to be ready. */ + i = 100000; /* Some large value */ + while((inb(opna_iobase) & 0x80) && (i-- > 0)); + + /* Get irq number from IOA port. */ + outb(opna_iobase, 0x0e); + outb(0x5f, 0); + outb(0x5f, 0); + outb(0x5f, 0); + outb(0x5f, 0); + tmp = inb(opna_iobase + 2) & 0xc0; + switch (tmp >> 6) { + case 0: /* INT0 (IRQ3)*/ + irq = 3; + break; + case 1: /* INT6 (IRQ13)*/ + irq = 13; + break; + case 2: /* INT4 (IRQ10)*/ + irq = 10; + break; + case 3: /* INT5 (IRQ12)*/ + irq = 12; + break; + default: /* error */ + return NO; + } + + /* Wait for OPNA to be ready. */ + i = 100000; /* Some large value */ + while((inb(opna_iobase) & 0x80) && (i-- > 0)); + + /* Reset OPNA timer register. */ + outb(opna_iobase, 0x27); + outb(0x5f, 0); + outb(0x5f, 0); + outb(0x5f, 0); + outb(0x5f, 0); + outb(opna_iobase + 2, 0x30); + + /* Ok. Detection finished. */ + snprintf(pcm86_operations.name, sizeof(pcm86_operations.name), + "%s", board_name[pcm_s.board_type]); + pcm_initialized = NO; + pcm_s.irq = irq; + + if ((hw_config->irq > 0) && (hw_config->irq != irq)) + printf("pcm0: change irq %d -> %d\n", hw_config->irq, irq); + hw_config->irq = irq; + + return YES; +} + + +static int +pcm86_open(int dev, int mode) +{ + int err; + + if (!pcm_initialized) + return RET_ERROR(ENXIO); + + if (pcm_s.intr_busy || pcm_s.opened) + return RET_ERROR(EBUSY); + + if ((err = snd_set_irq_handler(pcm_s.irq, pcmintr, "PC-9801-73/86")) < 0) + return err; + + pcm_stop(); + + tmpbuf.size = 0; + pcm_s.intr_mode = IMODE_NONE; + pcm_s.opened = YES; + + return 0; +} + + +static void +pcm86_close(int dev) +{ + snd_release_irq(pcm_s.irq); + + pcm_s.opened = NO; +} + + +static void +pcm86_output_block(int dev, unsigned long buf, int count, int intrflag, + int dma_restart) +{ + unsigned long flags, cnt; + int maxchunksize; + +#ifdef PCM86_DEBUG + printk("pcm86_output_block():"); + if (audio_devs[dev]->dmap->flags & DMA_BUSY) + printk(" DMA_BUSY"); + if (audio_devs[dev]->dmap->flags & DMA_RESTART) + printk(" DMA_RESTART"); + if (audio_devs[dev]->dmap->flags & DMA_ACTIVE) + printk(" DMA_ACTIVE"); + if (audio_devs[dev]->dmap->flags & DMA_STARTED) + printk(" DMA_STARTED"); + if (audio_devs[dev]->dmap->flags & DMA_ALLOC_DONE) + printk(" DMA_ALLOC_DONE"); + printk("\n"); +#endif + +#if 0 + DISABLE_INTR(flags); +#endif + +#ifdef PCM86_DEBUG + printk("pcm86_output_block(): count = %d, intrsize= %d\n", + count, pcm_s.intr_size); +#endif + + pcm_s.pdma_buf = (pcm_data *)buf; + pcm_s.pdma_count = count; + pcm_s.pdma_chunkcount = 1; + maxchunksize = (((PCM86_FIFOSIZE - pcm_s.intr_size * 2) + / (pcm_s.bytes * 2)) * pcm_s.speed + / pcm_s.chipspeed) * (pcm_s.bytes << pcm_s.stereo); + if (count > maxchunksize) + pcm_s.pdma_chunkcount = 2 * count / maxchunksize; + /* + * Let chunksize = (float)count / (float)pcm_s.pdma_chunkcount. + * Data of size chunksize is sent to the FIFO buffer on the 86-board + * on every occuring of interrupt. + * By assuming that pcm_s.intr_size < PCM86_FIFOSIZE / 2, we can conclude + * that the FIFO buffer never overflows from the following lemma. + * + * Lemma: + * maxchunksize / 2 <= chunksize <= maxchunksize. + * (Though pcm_s.pdma_chunkcount is obtained through the flooring + * function, this inequality holds.) + * Proof) Omitted. + */ + + fifo_output_block(); + + pcm_s.intr_last = NO; + pcm_s.intr_mode = IMODE_OUTPUT; + if (!pcm_s.intr_busy) + fifo_start(IMODE_OUTPUT); + pcm_s.intr_busy = YES; + +#if 0 + RESTORE_INTR(flags); +#endif +} + + +static void +pcm86_start_input(int dev, unsigned long buf, int count, int intrflag, + int dma_restart) +{ + unsigned long flags, cnt; + int maxchunksize; + +#ifdef PCM86_DEBUG + printk("pcm86_start_input():"); + if (audio_devs[dev]->dmap->flags & DMA_BUSY) + printk(" DMA_BUSY"); + if (audio_devs[dev]->dmap->flags & DMA_RESTART) + printk(" DMA_RESTART"); + if (audio_devs[dev]->dmap->flags & DMA_ACTIVE) + printk(" DMA_ACTIVE"); + if (audio_devs[dev]->dmap->flags & DMA_STARTED) + printk(" DMA_STARTED"); + if (audio_devs[dev]->dmap->flags & DMA_ALLOC_DONE) + printk(" DMA_ALLOC_DONE"); + printk("\n"); +#endif + +#if 0 + DISABLE_INTR(flags); +#endif + + pcm_s.intr_size = PCM86_INTRSIZE_IN; + +#ifdef PCM86_DEBUG + printk("pcm86_start_input(): count = %d, intrsize= %d\n", + count, pcm_s.intr_size); +#endif + + pcm_s.pdma_buf = (pcm_data *)buf; + pcm_s.pdma_count = count; + pcm_s.pdma_chunkcount = 1; + maxchunksize = ((pcm_s.intr_size / (pcm_s.bytes * 2)) * pcm_s.speed + / pcm_s.chipspeed) * (pcm_s.bytes << pcm_s.stereo); + if (count > maxchunksize) + pcm_s.pdma_chunkcount = 2 * count / maxchunksize; + + pcm_s.intr_mode = IMODE_INPUT; + if (!pcm_s.intr_busy) + fifo_start(IMODE_INPUT); + pcm_s.intr_busy = YES; + +#if 0 + RESTORE_INTR(flags); +#endif +} + + +static int +pcm86_ioctl(int dev, unsigned int cmd, unsigned int arg, int local) +{ + switch (cmd) { + case SOUND_PCM_WRITE_RATE: + if (local) + return set_speed(arg); + return IOCTL_OUT(arg, set_speed(IOCTL_IN(arg))); + + case SOUND_PCM_READ_RATE: + if (local) + return pcm_s.speed; + return IOCTL_OUT(arg, pcm_s.speed); + + case SNDCTL_DSP_STEREO: + if (local) + return set_stereo(arg); + return IOCTL_OUT(arg, set_stereo(IOCTL_IN(arg))); + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return set_stereo(arg - 1) + 1; + return IOCTL_OUT(arg, set_stereo(IOCTL_IN(arg) - 1) + 1); + + case SOUND_PCM_READ_CHANNELS: + if (local) + return pcm_s.stereo + 1; + return IOCTL_OUT(arg, pcm_s.stereo + 1); + + case SNDCTL_DSP_SETFMT: + if (local) + return set_format(arg); + return IOCTL_OUT(arg, set_format(IOCTL_IN(arg))); + + case SOUND_PCM_READ_BITS: + if (local) + return pcm_s.bytes * 8; + return IOCTL_OUT(arg, pcm_s.bytes * 8); + } + + /* Invalid ioctl request */ + return RET_ERROR(EINVAL); +} + + +static int +pcm86_prepare_for_input(int dev, int bufsize, int nbufs) +{ + pcm_s.intr_size = PCM86_INTRSIZE_IN; + pcm_s.intr_mode = IMODE_NONE; + pcm_s.acc = 0; + pcm_s.last_l = 0; + pcm_s.last_r = 0; + + DEB(printk("pcm86_prepare_for_input\n")); + + return 0; +} + + +static int +pcm86_prepare_for_output(int dev, int bufsize, int nbufs) +{ + pcm_s.intr_size = PCM86_INTRSIZE_OUT; + pcm_s.intr_mode = IMODE_NONE; + pcm_s.acc = 0; + pcm_s.last_l = 0; + pcm_s.last_r = 0; + + DEB(printk("pcm86_prepare_for_output\n")); + + return 0; +} + + +static void +pcm86_reset(int dev) +{ + pcm_stop(); +} + + +static void +pcm86_halt_xfer(int dev) +{ + pcm_stop(); + + DEB(printk("pcm86_halt_xfer\n")); +} + + +void +pcmintr(int unit) +{ + unsigned char tmp; + + if ((inb(pcm_s.iobase + 8) & 0x10) == 0) + return; /* not FIFO intr. */ + + switch(pcm_s.intr_mode) { + case IMODE_OUTPUT: + if (pcm_s.intr_trailer) { + DEB(printk("pcmintr(): fifo_reset\n")); + fifo_reset(); + pcm_s.intr_trailer = NO; + pcm_s.intr_busy = NO; + } + if (pcm_s.pdma_count > 0) + fifo_output_block(); + else + DMAbuf_outputintr(my_dev, 1); + /* Reset intr. flag. */ + tmp = inb(pcm_s.iobase + 8); + outb(pcm_s.iobase + 8, tmp & ~0x10); + outb(pcm_s.iobase + 8, tmp | 0x10); + break; + + case IMODE_INPUT: + fifo_input_block(); + if (pcm_s.pdma_count == 0) + DMAbuf_inputintr(my_dev); + /* Reset intr. flag. */ + tmp = inb(pcm_s.iobase + 8); + outb(pcm_s.iobase + 8, tmp & ~0x10); + outb(pcm_s.iobase + 8, tmp | 0x10); + break; + + default: + pcm_stop(); + printk("pcm0: unexpected interrupt\n"); + } +} + + +#endif /* EXCLUDE_PCM86, EXCLUDE_AUDIO */ + +#endif /* CONFIGURE_SOUNDCARD */ diff --git a/sys/i386/isa/sound/pss.c b/sys/i386/isa/sound/pss.c new file mode 100644 index 0000000..a6a5b65 --- /dev/null +++ b/sys/i386/isa/sound/pss.c @@ -0,0 +1,693 @@ +/* + * sound/pss.c + * + * The low level driver for the Personal Sound System (ECHO ESC614). + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + +#if defined(CONFIG_PSS) && defined(CONFIG_AUDIO) + +/* + * PSS registers. + */ +#define REG(x) (devc->base+x) +#define PSS_DATA 0 +#define PSS_STATUS 2 +#define PSS_CONTROL 2 +#define PSS_ID 4 +#define PSS_IRQACK 4 +#define PSS_PIO 0x1a + +/* + * Config registers + */ +#define CONF_PSS 0x10 +#define CONF_WSS 0x12 +#define CONF_SB 0x13 +#define CONF_CDROM 0x16 +#define CONF_MIDI 0x18 + +/* + * Status bits. + */ +#define PSS_FLAG3 0x0800 +#define PSS_FLAG2 0x0400 +#define PSS_FLAG1 0x1000 +#define PSS_FLAG0 0x0800 +#define PSS_WRITE_EMPTY 0x8000 +#define PSS_READ_FULL 0x4000 + +#include "coproc.h" + +#ifdef PSS_HAVE_LD +#include "synth-ld.h" +#else +static int pss_synthLen = 0; +static u_char pss_synth[1] = +{0}; + +#endif + +typedef struct pss_config { + int base; + int irq; + int dma; + sound_os_info *osp; +} pss_config; + +static pss_config pss_data; +static pss_config *devc = &pss_data; + +static int pss_initialized = 0; +static int nonstandard_microcode = 0; + +int +probe_pss(struct address_info * hw_config) +{ + u_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; + + /* these are the possible addresses */ + if (devc->base != 0x220 && devc->base != 0x240 && + devc->base != 0x230 && devc->base != 0x250) + return 0; + + /* these are the possible irqs */ + if (irq != 3 && irq != 5 && irq != 7 && irq != 9 && + irq != 10 && irq != 11 && irq != 12) + return 0; + + /* and these are the possible dmas */ + if (dma != 5 && dma != 6 && dma != 7) + return 0; + + id = inb(REG(PSS_ID)); + + /* XXX the following test cannot possibly succeed! - lr970714 */ + if ((id >> 8) != 'E') { + /* + * printf ("No PSS signature detected at 0x%x (0x%x)\n", + * devc->base, id); + */ + return 0; + } + return 1; +} + +static int +set_irq(pss_config * devc, int dev, int irq) +{ + static u_short irq_bits[16] = + { + 0x0000, 0x0000, 0x0000, 0x0008, + 0x0000, 0x0010, 0x0000, 0x0018, + 0x0000, 0x0020, 0x0028, 0x0030, + 0x0038, 0x0000, 0x0000, 0x0000 + }; + + u_short tmp, bits; + + if (irq < 0 || irq > 15) + return 0; + + tmp = inb(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */ + + if ((bits = irq_bits[irq]) == 0 && irq != 0) { + printf("PSS: Invalid IRQ %d\n", irq); + return 0; + } + outw(REG(dev), tmp | bits); + return 1; +} + +static int +set_io_base(pss_config * devc, int dev, int base) +{ + u_short tmp = inb(REG(dev)) & 0x003f; + u_short bits = (base & 0x0ffc) << 4; + + outw(REG(dev), bits | tmp); + + return 1; +} + +static int +set_dma(pss_config * devc, int dev, int dma) +{ + static u_short dma_bits[8] = + { + 0x0001, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0006, 0x0007 + }; + + u_short tmp, bits; + + if (dma < 0 || dma > 7) + return 0; + + tmp = inb(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */ + + if ((bits = dma_bits[dma]) == 0 && dma != 4) { + printf("PSS: Invalid DMA %d\n", dma); + return 0; + } + outw(REG(dev), tmp | bits); + return 1; +} + +static int +pss_reset_dsp(pss_config * devc) +{ + u_long i, limit = get_time() + 10; + + outw(REG(PSS_CONTROL), 0x2000); + + for (i = 0; i < 32768 && get_time() < limit; i++) + inb(REG(PSS_CONTROL)); + + outw(REG(PSS_CONTROL), 0x0000); + + return 1; +} + +static int +pss_put_dspword(pss_config * devc, u_short word) +{ + int i, val; + + for (i = 0; i < 327680; i++) { + val = inb(REG(PSS_STATUS)); + if (val & PSS_WRITE_EMPTY) { + outw(REG(PSS_DATA), word); + return 1; + } + } + return 0; +} + +static int +pss_get_dspword(pss_config * devc, u_short *word) +{ + int i, val; + + for (i = 0; i < 327680; i++) { + val = inb(REG(PSS_STATUS)); + if (val & PSS_READ_FULL) { + *word = inb(REG(PSS_DATA)); + return 1; + } + } + + return 0; +} + +static int +pss_download_boot(pss_config * devc, u_char *block, int size, int flags) +{ + int i, limit, val, count; + + if (flags & CPF_FIRST) { + /* _____ Warn DSP software that a boot is coming */ + outw(REG(PSS_DATA), 0x00fe); + + limit = get_time() + 10; + + for (i = 0; i < 32768 && get_time() < limit; i++) + if (inb(REG(PSS_DATA)) == 0x5500) + break; + + outw(REG(PSS_DATA), *block++); + + pss_reset_dsp(devc); + } + count = 1; + while (1) { + int j; + + for (j = 0; j < 327670; j++) { + /* _____ Wait for BG to appear */ + if (inb(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 { + printf("\nPSS: DownLoad timeout problems, byte %d=%d\n", + count, size); + return 0; + } + } + /* _____ Send the next byte */ + outw(REG(PSS_DATA), *block++); + count++; + } + + if (flags & CPF_LAST) { + /* _____ Why */ + outw(REG(PSS_DATA), 0); + + limit = get_time() + 10; + for (i = 0; i < 32768 && get_time() < limit; i++) + val = inb(REG(PSS_STATUS)); + + limit = get_time() + 10; + for (i = 0; i < 32768 && get_time() < limit; i++) { + val = inb(REG(PSS_STATUS)); + if (val & 0x4000) + break; + } + + /* now read the version */ + for (i = 0; i < 32000; i++) { + val = inb(REG(PSS_STATUS)); + if (val & PSS_READ_FULL) + break; + } + if (i == 32000) + return 0; + + val = inb(REG(PSS_DATA)); + /* + * printf("<PSS: microcode version %d.%d loaded>", val/16, + * val % 16); + */ + } + return 1; +} + +void +attach_pss(struct address_info * hw_config) +{ + u_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 = inb(REG(PSS_ID)) & 0x00ff; + + /* + * Disable all emulations. Will be enabled later (if required). + */ + outw(REG(CONF_PSS), 0x0000); + outw(REG(CONF_WSS), 0x0000); + outw(REG(CONF_SB), 0x0000); + outw(REG(CONF_MIDI), 0x0000); + outw(REG(CONF_CDROM), 0x0000); + +#if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES + if (0) { + printf("pss.c: Can't allocate DMA channel\n"); + return; + } + if (!set_irq(devc, CONF_PSS, devc->irq)) { + printf("PSS: IRQ error\n"); + return; + } + if (!set_dma(devc, CONF_PSS, devc->dma)) { + printf("PSS: DRQ error\n"); + return; + } +#endif + + pss_initialized = 1; + snprintf(tmp, sizeof(tmp), "ECHO-PSS Rev. %d", id); + conf_printf(tmp, hw_config); + + return; +} + +int +probe_pss_mpu(struct address_info * hw_config) +{ + int timeout; + + if (!pss_initialized) + return 0; + + if (0) { + printf("PSS: MPU I/O port conflict\n"); + return 0; + } + if (!set_io_base(devc, CONF_MIDI, hw_config->io_base)) { + printf("PSS: MIDI base error.\n"); + return 0; + } + if (!set_irq(devc, CONF_MIDI, hw_config->irq)) { + printf("PSS: MIDI IRQ error.\n"); + return 0; + } + if (!pss_synthLen) { + printf("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)) { + printf("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 +} + +static int +pss_coproc_open(void *dev_info, int sub_device) +{ + switch (sub_device) { + case COPR_MIDI: + + if (pss_synthLen == 0) { + printf("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)) { + printf("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) +{ + return; +} + +static void +pss_coproc_reset(void *dev_info) +{ + if (pss_synthLen) + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) { + printf("PSS: Unable to load MIDI synth microcode to DSP.\n"); + } + nonstandard_microcode = 0; +} + +static int +download_boot_block(void *dev_info, copr_buffer * buf) +{ + if (buf->len <= 0 || buf->len > sizeof(buf->data)) + return -(EINVAL); + + if (!pss_download_boot(devc, buf->data, buf->len, buf->flags)) { + printf("PSS: Unable to load microcode block to DSP.\n"); + return -(EIO); + } + nonstandard_microcode = 1; /* The MIDI microcode has been + * overwritten */ + + return 0; +} + +static int +pss_coproc_ioctl(void *dev_info, u_int cmd, ioctl_arg arg, int local) +{ + /* printf("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 *) malloc(sizeof(copr_buffer), M_TEMP, M_WAITOK); + if (buf == NULL) + return -(ENOSPC); + + bcopy(&(((char *) arg)[0]), (char *) buf, sizeof(*buf)); + err = download_boot_block(dev_info, buf); + free(buf, M_TEMP); + return err; + } + break; + + case SNDCTL_COPR_RDATA: + { + copr_debug_buf buf; + u_long flags; + u_short tmp; + + bcopy(&(((char *) arg)[0]), (char *) &buf, sizeof(buf)); + + flags = splhigh(); + if (!pss_put_dspword(devc, 0x00d0)) { + splx(flags); + return -(EIO); + } + if (!pss_put_dspword(devc, (u_short) (buf.parm1 & 0xffff))) { + splx(flags); + return -(EIO); + } + if (!pss_get_dspword(devc, &tmp)) { + splx(flags); + return -(EIO); + } + buf.parm1 = tmp; + splx(flags); + + bcopy(&buf, &(((char *) arg)[0]), sizeof(buf)); + return 0; + } + break; + + case SNDCTL_COPR_WDATA: + { + copr_debug_buf buf; + u_long flags; + u_short tmp; + + bcopy(&(((char *) arg)[0]), (char *) &buf, sizeof(buf)); + + flags = splhigh(); + if (!pss_put_dspword(devc, 0x00d1)) { + splx(flags); + return -(EIO); + } + if (!pss_put_dspword(devc, (u_short) (buf.parm1 & 0xffff))) { + splx(flags); + return -(EIO); + } + tmp = (u_int) buf.parm2 & 0xffff; + if (!pss_put_dspword(devc, tmp)) { + splx(flags); + return -(EIO); + } + splx(flags); + return 0; + } + break; + + case SNDCTL_COPR_WCODE: + { + copr_debug_buf buf; + u_long flags; + u_short tmp; + + bcopy(&(((char *) arg)[0]), (char *) &buf, sizeof(buf)); + + flags = splhigh(); + if (!pss_put_dspword(devc, 0x00d3)) { + splx(flags); + return -(EIO); + } + if (!pss_put_dspword(devc, (u_short) (buf.parm1 & 0xffff))) { + splx(flags); + return -(EIO); + } + tmp = ((u_int) buf.parm2 >> 8) & 0xffff; + if (!pss_put_dspword(devc, tmp)) { + splx(flags); + return -(EIO); + } + tmp = (u_int) buf.parm2 & 0x00ff; + if (!pss_put_dspword(devc, tmp)) { + splx(flags); + return -(EIO); + } + splx(flags); + return 0; + } + break; + + case SNDCTL_COPR_RCODE: + { + copr_debug_buf buf; + u_long flags; + u_short tmp; + + bcopy(&(((char *) arg)[0]), (char *) &buf, sizeof(buf)); + + flags = splhigh(); + if (!pss_put_dspword(devc, 0x00d2)) { + splx(flags); + return -(EIO); + } + if (!pss_put_dspword(devc, (u_short) (buf.parm1 & 0xffff))) { + splx(flags); + return -(EIO); + } + if (!pss_get_dspword(devc, &tmp)) { /* Read msb */ + splx(flags); + return -(EIO); + } + buf.parm1 = tmp << 8; + + if (!pss_get_dspword(devc, &tmp)) { /* Read lsb */ + splx(flags); + return -(EIO); + } + buf.parm1 |= tmp & 0x00ff; + + splx(flags); + + bcopy(&buf, &(((char *) arg)[0]), sizeof(buf)); + return 0; + } + break; + + 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 +}; + +void +attach_pss_mpu(struct address_info * hw_config) +{ + int prev_devs; + +#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) + 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; +#endif +} + +int +probe_pss_mss(struct address_info * hw_config) +{ + int timeout; + + if (!pss_initialized) + return 0; + + if (0) { + printf("PSS: WSS I/O port conflict\n"); + return 0; + } + if (!set_io_base(devc, CONF_WSS, hw_config->io_base)) { + printf("PSS: WSS base error.\n"); + return 0; + } + if (!set_irq(devc, CONF_WSS, hw_config->irq)) { + printf("PSS: WSS IRQ error.\n"); + return 0; + } + if (!set_dma(devc, CONF_WSS, hw_config->dma)) { + printf("PSS: WSS DRQ error\n"); + return 0; + } + /* + * For some reason the card returns 0xff in the WSS status register + * immediately after boot. Propably 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++); + + return probe_mss(hw_config); +} + +void +attach_pss_mss(struct address_info * hw_config) +{ + int prev_devs; + long ret; + + prev_devs = num_audiodevs; + attach_mss(hw_config); + + /* Check if The MSS driver installed itself */ + if (num_audiodevs == (prev_devs + 1)) + audio_devs[prev_devs]->coproc = &pss_coproc_operations; +} + +#endif diff --git a/sys/i386/isa/sound/pss.h b/sys/i386/isa/sound/pss.h new file mode 100644 index 0000000..e020af6 --- /dev/null +++ b/sys/i386/isa/sound/pss.h @@ -0,0 +1,371 @@ +/****************************************************************************** + + def.h + + Version 1.3 11/2/93 + + Copyright (c) 1993 Analog Devices Inc. All rights reserved + +******************************************************************************/ +/* Port offsets from base port for Sound Blaster DSP */ +#define DSP_PORT_CMSD0 0x00 /* C/MS music voice 1-6 data port, write only */ +#define DSP_PORT_CMSR0 0x01 /* C/MS music voice 1-6 register port, write only */ +#define DSP_PORT_CMSD1 0x02 /* C/MS music voice 7-12 data port, write only */ +#define DSP_PORT_CMSR1 0x03 /* C/MS music voice 7-12 register port, write only */ + +#define DSP_PORT_STATUS 0x04 /* DSP Status bits, read only */ +#define DSP_PORT_CONTROL 0x04 /* DSP Control bits, write only */ +#define DSP_PORT_DATA_LSB 0x05 /* Read or write LSB of 16 bit data */ + + +#define DSP_PORT_RESET 0x06 /* DSP Reset, write only */ +#define DSP_PORT_07h 0x07 /* reserved port */ + +#define DSP_PORT_FMD0 0x08 /* FM music data/status port, read/write */ +#define DSP_PORT_FMR0 0x09 /* FM music data/status port, write only */ + +#define DSP_PORT_RDDATA 0x0A /* DSP Read data, read only reading signals DSP */ +#define DSP_PORT_0Bh 0x0B /* reserved port */ +#define DSP_PORT_WRDATA 0x0C /* DSP Write data or command, write */ +#define DSP_PORT_WRBUSY 0x0C /* DSP Write buffer status (bit 7), read */ +#define DSP_PORT_0Dh 0x0D /* reserved port */ +#define DSP_PORT_DATAAVAIL 0x0E /* DSP Data available status (bit 7), read only */ +#define DSP_PORT_INTERFACE 0x0E /* Sets DMA Channel and Interrupt, write only */ +#define DSP_PORT_0Fh 0x0F /* reserved port (used on Pro cards) */ + +#define ADDR_MASK 0x003f + +#define INT_MASK 0xffc7 +#define INT_3_BITS 0x0008 +#define INT_5_BITS 0x0010 +#define INT_7_BITS 0x0018 +#define INT_9_BITS 0x0020 +#define INT_10_BITS 0x0028 +#define INT_11_BITS 0x0030 +#define INT_12_BITS 0x0038 + +#define GAME_BIT 0x0400 +#define GAME_BIT_MASK 0xfbff + +#define INT_TEST_BIT 0x0200 +#define INT_TEST_PASS 0x0100 +#define INT_TEST_BIT_MASK 0xFDFF + +#define DMA_MASK 0xfff8 +#define DMA_0_BITS 0x0001 +#define DMA_1_BITS 0x0002 +#define DMA_3_BITS 0x0003 +#define DMA_5_BITS 0x0004 +#define DMA_6_BITS 0x0005 +#define DMA_7_BITS 0x0006 + +#define DMA_TEST_BIT 0x0080 +#define DMA_TEST_PASS 0x0040 +#define DMA_TEST_BIT_MASK 0xFF7F + + +/* Echo DSP Flags */ + +#define DSP_FLAG3 0x10 +#define DSP_FLAG2 0x08 +#define DSP_FLAG1 0x80 +#define DSP_FLAG0 0x40 + +#define PSS_CONFIG 0x10 +#define PSS_WSS_CONFIG 0x12 +#define SB_CONFIG 0x14 +#define MIDI_CONFIG 0x18 +#define CD_CONFIG 0x16 +#define UART_CONFIG 0x1a + +#define PSS_DATA 0x00 +#define PSS_STATUS 0x02 +#define PSS_CONTROL 0x02 +#define PSS_ID_VERS 0x04 + +#define PSS_FLAG3 0x0800 +#define PSS_FLAG2 0x0400 +#define PSS_FLAG1 0x1000 +#define PSS_FLAG0 0x0800 + +/*_____ WSS defines */ +#define WSS_BASE_ADDRESS 0x530 +#define WSS_CONFIG 0x0 +#define WSS_VERSION 0x03 +#define WSS_SP0 0x04 +#define WSS_SP1 0x05 +#define WSS_SP2 0x06 +#define WSS_SP3 0x07 + +/*_____ SoundPort register addresses */ + +#define SP_LIN_SOURCE_CTRL 0x00 +#define SP_RIN_SOURCE_CTRL 0x01 +#define SP_LIN_GAIN_CTRL 0x10 +#define SP_RIN_GAIN_CTRL 0x11 +#define SP_LAUX1_CTRL 0x02 +#define SP_RAUX1_CTRL 0x03 +#define SP_LAUX2_CTRL 0x04 +#define SP_RAUX2_CTRL 0x05 +#define SP_LOUT_CTRL 0x06 +#define SP_ROUT_CTRL 0x07 +#define SP_CLK_FORMAT 0x48 +#define SP_INT_CONF 0x09 +#define SP_INT_CONF_MCE 0x49 +#define SP_PIN_CTRL 0x0a +#define SP_TEST_INIT 0x0b +#define SP_MISC_CTRL 0x0c +#define SP_MIX_CTRL 0x0d +#define SP_DMA_UCNT 0x0e +#define SP_DMA_LCNT 0x0f + +/*_____ Gain constants */ + +#define GAIN_0 0x00 +#define GAIN_1_5 0x01 +#define GAIN_3 0x02 +#define GAIN_4_5 0x03 +#define GAIN_6 0x04 +#define GAIN_7_5 0x05 +#define GAIN_9 0x06 +#define GAIN_10_5 0x07 +#define GAIN_12 0x08 +#define GAIN_13_5 0x09 +#define GAIN_15 0x0a +#define GAIN_16_5 0x0b +#define GAIN_18 0x0c +#define GAIN_19_5 0x0d +#define GAIN_21 0x0e +#define GAIN_22_5 0x0f +#define MUTE 0XFFFF + +/*_____ Attenuation constants */ + +#define ATTEN_0 0x00 +#define ATTEN_1_5 0x01 +#define ATTEN_3 0x02 +#define ATTEN_4_5 0x03 +#define ATTEN_6 0x04 +#define ATTEN_7_5 0x05 +#define ATTEN_9 0x06 +#define ATTEN_10_5 0x07 +#define ATTEN_12 0x08 +#define ATTEN_13_5 0x09 +#define ATTEN_15 0x0a +#define ATTEN_16_5 0x0b +#define ATTEN_18 0x0c +#define ATTEN_19_5 0x0d +#define ATTEN_21 0x0e +#define ATTEN_22_5 0x0f + + +#define PSS_WRITE_EMPTY 0x8000 + +#define CD_POL_MASK 0xFFBF +#define CD_POL_BIT 0x0040 + + + +/****************************************************************************** + + host.h + + Version 1.2 9/27/93 + + Copyright (c) 1993 Analog Devices Inc. All rights reserved + +******************************************************************************/ +#define SB_WRITE_FULL 0x80 +#define SB_READ_FULL 0x80 +#define SB_WRITE_STATUS 0x0C +#define SB_READ_STATUS 0x0E +#define SB_READ_DATA 0x0A +#define SB_WRITE_DATA 0x0C + +#define PSS_DATA_REG 0x00 +#define PSS_STATUS_REG 0x02 +#define PSS_WRITE_EMPTY 0x8000 +#define PSS_READ_FULL 0x4000 + +/*_____ 1848 Sound Port bit defines */ + +#define SP_IN_INIT 0x80 +#define MODE_CHANGE_ENABLE 0x40 +#define MODE_CHANGE_MASK 0xbf +#define TRANSFER_DISABLE 0x20 +#define TRANSFER_DISABLE_MASK 0xdf +#define ADDRESS_MASK 0xf0 + +/*_____ Status bits */ +#define INTERRUPT_STATUS 0x01 +#define PLAYBACK_READY 0x02 +#define PLAYBACK_LEFT 0x04 +/*_____ pbright is not left */ +#define PLAYBACK_UPPER 0x08 +/*_____ bplower is not upper */ + +#define SAMPLE_OVERRUN 0x10 +#define SAMPLE_UNDERRUN 0x10 +#define CAPTURE_READY 0x20 +#define CAPTURE_LEFT 0x40 +/*_____ cpright is not left */ +#define CAPTURE_UPPER 0x08 +/*_____ cplower is not upper */ + +/*_____ Input & Output regs bits */ +#define LINE_INPUT 0x80 +#define AUX_INPUT 0x40 +#define MIC_INPUT 0x80 +#define MIXED_DAC_INPUT 0xC0 +#define INPUT_GAIN_MASK 0xf0 +#define INPUT_MIC_GAIN_ENABLE 0x20 +#define INPUT_MIC_GAIN_MASK 0xdf +#define INPUT_SOURCE_MASK 0x3f +#define AUX_INPUT_ATTEN_MASK 0xf0 +#define AUX_INPUT_MUTE 0x80 +#define AUX_INPUT_MUTE_MASK 0x7f +#define OUTPUT_MUTE 0x80 +#define OUTPUT_MUTE_MASK 0x7f +#define OUTPUT_ATTEN_MASK 0xc0 + +/*_____ Clock and Data format reg bits */ +#define CLOCK_SELECT_MASK 0xfe +#define CLOCK_XTAL2 0x01 +#define CLOCK_XTAL1 0x00 +#define CLOCK_FREQ_MASK 0xf1 +#define STEREO_MONO_MASK 0xef +#define STEREO 0x10 +#define AUDIO_MONO 0x00 +#define LINEAR_COMP_MASK 0xdf +#define LINEAR 0x00 +#define COMPANDED 0x20 +#define FORMAT_MASK 0xbf +#define PCM 0x00 +#define ULAW 0x00 +#define TWOS_COMP 0x40 +#define ALAW 0x40 + +/*_____ Interface Configuration reg bits */ +#define PLAYBACK_ENABLE 0x01 +#define PLAYBACK_ENABLE_MASK 0xfe +#define CAPTURE_ENABLE 0x02 +#define CAPTURE_ENABLE_MASK 0xfd +#define SINGLE_DMA 0x04 +#define SINGLE_DMA_MASK 0xfb +#define DUAL_DMA 0x00 +#define AUTO_CAL_ENABLE 0x08 +#define AUTO_CAL_DISABLE_MASK 0xf7 +#define PLAYBACK_PIO_ENABLE 0x40 +#define PLAYBACK_DMA_MASK 0xbf +#define CAPTURE_PIO_ENABLE 0x80 +#define CAPTURE_DMA_MASK 0x7f + +/*_____ Pin control bits */ +#define INTERRUPT_ENABLE 0x02 +#define INTERRUPT_MASK 0xfd + +/*_____ Test and init reg bits */ +#define OVERRANGE_LEFT_MASK 0xfc +#define OVERRANGE_RIGHT_MASK 0xf3 +#define DATA_REQUEST_STATUS 0x10 +#define AUTO_CAL_IN_PROG 0x20 +#define PLAYBACK_UNDERRUN 0x40 +#define CAPTURE_UNDERRUN 0x80 + +/*_____ Miscellaneous Control reg bits */ +#define ID_MASK 0xf0 + +/*_____ Digital Mix Control reg bits */ +#define DIGITAL_MIX1_MUTE_MASK 0xfe +#define MIX_ATTEN_MASK 0x03 + +/*_____ 1848 Sound Port reg defines */ + +#define SP_LEFT_INPUT_CONTROL 0x0 +#define SP_RIGHT_INPUT_CONTROL 0x1 +#define SP_LEFT_AUX1_CONTROL 0x2 +#define SP_RIGHT_AUX1_CONTROL 0x3 +#define SP_LEFT_AUX2_CONTROL 0x4 +#define SP_RIGHT_AUX2_CONTROL 0x5 +#define SP_LEFT_OUTPUT_CONTROL 0x6 +#define SP_RIGHT_OUTPUT_CONTROL 0x7 +#define SP_CLOCK_DATA_FORMAT 0x8 +#define SP_INTERFACE_CONFIG 0x9 +#define SP_PIN_CONTROL 0xA +#define SP_TEST_AND_INIT 0xB +#define SP_MISC_INFO 0xC +#define SP_DIGITAL_MIX 0xD +#define SP_UPPER_BASE_COUNT 0xE +#define SP_LOWER_BASE_COUNT 0xF + +#define HOST_SP_ADDR (0x534) +#define HOST_SP_DATA (0x535) + + +/****************************************************************************** + + phillips.h + + Version 1.2 9/27/93 + + Copyright (c) 1993 Analog Devices Inc. All rights reserved + +******************************************************************************/ +/*_____ Phillips control SW defines */ + +/*_____ Settings and ranges */ +#define VOLUME_MAX 6 +#define VOLUME_MIN (-64) +#define VOLUME_RANGE 70 +#define VOLUME_STEP 2 +#define BASS_MAX 15 +#define BASS_MIN (-12) +#define BASS_STEP 2 +#define BASS_RANGE 27 +#define TREBLE_MAX 12 +#define TREBLE_MIN (-12) +#define TREBLE_STEP 2 +#define TREBLE_RANGE 24 + +#define VOLUME_CONSTANT 252 +#define BASS_CONSTANT 246 +#define TREBLE_CONSTANT 246 + +/*_____ Software commands */ +#define SET_MASTER_COMMAND 0x0010 +#define MASTER_VOLUME_LEFT 0x0000 +#define MASTER_VOLUME_RIGHT 0x0100 +#define MASTER_BASS 0x0200 +#define MASTER_TREBLE 0x0300 +#define MASTER_SWITCH 0x0800 + +#define STEREO_MODE 0x00ce +#define PSEUDO_MODE 0x00d6 +#define SPATIAL_MODE 0x00de +#define MONO_MODE 0x00c6 + + +#define PSS_STEREO 0x00ce +#define PSS_PSEUDO 0x00d6 +#define PSS_SPATIAL 0x00de +#define PSS_MONO 0x00c6 + +#define PHILLIPS_VOL_MIN -64 +#define PHILLIPS_VOL_MAX 6 +#define PHILLIPS_VOL_DELTA 70 +#define PHILLIPS_VOL_INITIAL -20 +#define PHILLIPS_VOL_CONSTANT 252 +#define PHILLIPS_VOL_STEP 2 +#define PHILLIPS_BASS_MIN -12 +#define PHILLIPS_BASS_MAX 15 +#define PHILLIPS_BASS_DELTA 27 +#define PHILLIPS_BASS_INITIAL 0 +#define PHILLIPS_BASS_CONSTANT 246 +#define PHILLIPS_BASS_STEP 2 +#define PHILLIPS_TREBLE_MIN -12 +#define PHILLIPS_TREBLE_MAX 12 +#define PHILLIPS_TREBLE_DELTA 24 +#define PHILLIPS_TREBLE_INITIAL 0 +#define PHILLIPS_TREBLE_CONSTANT 246 +#define PHILLIPS_TREBLE_STEP 2 + diff --git a/sys/i386/isa/sound/sb16_dsp.c b/sys/i386/isa/sound/sb16_dsp.c new file mode 100644 index 0000000..e923176 --- /dev/null +++ b/sys/i386/isa/sound/sb16_dsp.c @@ -0,0 +1,535 @@ +/* + * sound/sb16_dsp.c + * + * The low level driver for the SoundBlaster DSP chip. + * + * (C) 1993 J. Schubert (jsb@sth.ruhr-uni-bochum.de) + * + * based on SB-driver by (C) Hannu Savolainen + * + * 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. + * + */ + +#define DEB(x) +#define DEB1(x) +#include <i386/isa/sound/sound_config.h> +#include "sb.h" +#include <i386/isa/sound/sb_mixer.h> +#include <i386/isa/sound/sbcard.h> + +#if defined(CONFIG_SB16) && (NSB > 0) && defined(CONFIG_AUDIO) && defined(CONFIG_SBPRO) + + +extern sound_os_info *sb_osp; +extern int sbc_base; + +static int sb16_dsp_ok = 0; +static int dsp_16bit = 0; +static int dsp_stereo = 0; +static int dsp_current_speed = 8000; +static int dsp_busy = 0; +static int dma16, dma8; + + +static int trigger_bits = 0; +static u_long dsp_count = 0; + +static int irq_mode = IMODE_NONE; +static int my_dev = 0; + +static volatile int intr_active = 0; + +static int sb16_dsp_open(int dev, int mode); +static void sb16_dsp_close(int dev); +static void sb16_dsp_output_block(int dev, u_long buf, int count, int intrflag, int dma_restart); +static void sb16_dsp_start_input(int dev, u_long buf, int count, int intrflag, int dma_restart); +static int sb16_dsp_ioctl(int dev, u_int cmd, ioctl_arg arg, int local); +static int sb16_dsp_prepare_for_input(int dev, int bsize, int bcount); +static int sb16_dsp_prepare_for_output(int dev, int bsize, int bcount); +static void sb16_dsp_reset(int dev); +static void sb16_dsp_halt(int dev); +static void sb16_dsp_trigger(int dev, int bits); +static int dsp_set_speed(int); +static int dsp_set_stereo(int); +static void dsp_cleanup(void); + +static struct audio_operations sb16_dsp_operations = +{ + "SoundBlaster 16", + DMA_AUTOMODE, + AFMT_U8 | AFMT_S16_LE, + NULL, + sb16_dsp_open, + sb16_dsp_close, + sb16_dsp_output_block, + sb16_dsp_start_input, + sb16_dsp_ioctl, + sb16_dsp_prepare_for_input, + sb16_dsp_prepare_for_output, + sb16_dsp_reset, + sb16_dsp_halt, + NULL, + NULL, + NULL, + NULL, + sb16_dsp_trigger +}; + +static int +sb_dsp_command01(u_char val) +{ + int i = 1 << 16; + + while (--i & (!inb(DSP_STATUS) & 0x80)); + if (!i) + printf("SB16 sb_dsp_command01 Timeout\n"); + return sb_dsp_command(val); +} + +static int +dsp_set_speed(int mode) +{ + DEB(printf("dsp_set_speed(%d)\n", mode)); + if (mode) { + RANGE (mode, 5000, 44100); + dsp_current_speed = mode; + } + return mode; +} + +static int +dsp_set_stereo(int mode) +{ + DEB(printf("dsp_set_stereo(%d)\n", mode)); + dsp_stereo = mode; + return mode; +} + +static int +dsp_set_bits(int arg) +{ + DEB(printf("dsp_set_bits(%d)\n", arg)); + + if (arg) + dsp_16bit = (arg == 16) ? 1 : 0 ; + return dsp_16bit ? 16 : 8; +} + +static int +sb16_dsp_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) +{ + switch (cmd) { + case SOUND_PCM_WRITE_RATE: + if (local) + return dsp_set_speed((int) arg); + return *(int *) arg = dsp_set_speed((*(int *) arg)); + + case SOUND_PCM_READ_RATE: + if (local) + return dsp_current_speed; + return *(int *) arg = dsp_current_speed; + + case SNDCTL_DSP_STEREO: + if (local) + return dsp_set_stereo((int) arg); + return *(int *) arg = dsp_set_stereo((*(int *) arg)); + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return dsp_set_stereo((int) arg - 1) + 1; + return *(int *) arg = dsp_set_stereo((*(int *) arg) - 1) + 1; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return dsp_stereo + 1; + return *(int *) arg = dsp_stereo + 1; + + case SNDCTL_DSP_SETFMT: + if (local) + return dsp_set_bits((int) arg); + return *(int *) arg = dsp_set_bits((*(int *) arg)); + + case SOUND_PCM_READ_BITS: + if (local) + return dsp_16bit ? 16 : 8; + return *(int *) arg = dsp_16bit ? 16 : 8; + + case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */ + if ((*(int *) arg) > 1) + return *(int *) arg = -(EINVAL); + + case FIOASYNC: + if (local) + return 1; + return *(int *) arg = 1; + + case FIONBIO: + if (local) + return 1; + return *(int *) arg = 1; + + default: + return -(EINVAL); + } + + return -(EINVAL); +} + +static int +sb16_dsp_open(int dev, int mode) +{ + DEB(printf("sb16_dsp_open()\n")); + + if (!sb16_dsp_ok) { + printf("SB16 Error: SoundBlaster board not installed\n"); + return -(ENXIO); + } + if (intr_active) + return -(EBUSY); + + sb_reset_dsp(); + + + irq_mode = IMODE_NONE; + dsp_busy = 1; + trigger_bits = 0; + + return 0; +} + +static void +sb16_dsp_close(int dev) +{ + u_long flags; + + DEB(printf("sb16_dsp_close()\n")); + sb_dsp_command01(0xd9); + sb_dsp_command01(0xd5); + + flags = splhigh(); + + audio_devs[dev]->dmachan1 = dma8; + + dsp_cleanup(); + dsp_busy = 0; + + + splx(flags); +} + +static void +sb16_dsp_output_block(int dev, u_long buf, int count, int intrflag, int dma_restart) +{ + u_long flags, cnt; + + + cnt = count; + if (dsp_16bit) + cnt >>= 1; + cnt--; + + if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt==dsp_count) { + irq_mode = IMODE_OUTPUT; + intr_active = 1; + return; /* Auto mode on. No need to react */ + } + flags = splhigh(); + + if (dma_restart) { + + sb16_dsp_halt(dev); + DMAbuf_start_dma(dev, buf, count, 1); + } + + + sb_dsp_command(0x41); + sb_dsp_command((u_char) ((dsp_current_speed >> 8) & 0xff)); + sb_dsp_command((u_char) (dsp_current_speed & 0xff)); + sb_dsp_command((u_char) (dsp_16bit ? 0xb6 : 0xc6)); + dsp_count = cnt; + sb_dsp_command((u_char) ((dsp_stereo ? 0x20 : 0) + + (dsp_16bit ? 0x10 : 0))); + sb_dsp_command((u_char) (cnt & 0xff)); + sb_dsp_command((u_char) (cnt >> 8)); + + irq_mode = IMODE_OUTPUT; + intr_active = 1; + splx(flags); +} + +static void +sb16_dsp_start_input(int dev, u_long buf, int count, int intrflag, int dma_restart) +{ + u_long flags, cnt; + + cnt = count; + if (dsp_16bit) + cnt >>= 1; + cnt--; + + if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == dsp_count) { + irq_mode = IMODE_INPUT; + intr_active = 1; + return; /* Auto mode on. No need to react */ + } + flags = splhigh(); + + if (dma_restart) { + sb_reset_dsp(); + DMAbuf_start_dma(dev, buf, count, 0); + } + sb_dsp_command(0x42); + sb_dsp_command((u_char) ((dsp_current_speed >> 8) & 0xff)); + sb_dsp_command((u_char) (dsp_current_speed & 0xff)); + sb_dsp_command((u_char) (dsp_16bit ? 0xbe : 0xce)); + dsp_count = cnt; + sb_dsp_command((u_char) ((dsp_stereo ? 0x20 : 0) + + (dsp_16bit ? 0x10 : 0))); + sb_dsp_command01((u_char) (cnt & 0xff)); + sb_dsp_command((u_char) (cnt >> 8)); + + irq_mode = IMODE_INPUT; + intr_active = 1; + splx(flags); +} + +static int +sb16_dsp_prepare_for_input(int dev, int bsize, int bcount) +{ + int fudge; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + + audio_devs[my_dev]->dmachan2 = dsp_16bit ? dma16 : dma8; + + + fudge = audio_devs[my_dev]->dmachan2 ; + + if (dmap->dma_chan != fudge ) { + isa_dma_release( dmap->dma_chan); + isa_dma_acquire(fudge); + dmap->dma_chan = fudge; + } + + dsp_count = 0; + dsp_cleanup(); + if (dsp_16bit) + sb_dsp_command(0xd5); /* Halt DMA until trigger() is called */ + else + sb_dsp_command(0xd0); /* Halt DMA until trigger() is called */ + + trigger_bits = 0; + return 0; +} + +static int +sb16_dsp_prepare_for_output(int dev, int bsize, int bcount) +{ + int fudge = dsp_16bit ? dma16 : dma8; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + if (dmap->dma_chan != fudge ) { + isa_dma_release( dmap->dma_chan); + isa_dma_acquire(fudge); + dmap->dma_chan = fudge; + } + + audio_devs[my_dev]->dmachan1 = fudge; + + dsp_count = 0; + dsp_cleanup(); + if (dsp_16bit) + sb_dsp_command(0xd5); /* Halt DMA until trigger() is called */ + else + sb_dsp_command(0xd0); /* Halt DMA until trigger() is called */ + + trigger_bits = 0; + return 0; +} + +static void +sb16_dsp_trigger(int dev, int bits) +{ + if (bits != 0) + bits = 1; + + if (bits == trigger_bits) /* No change */ + return; + + trigger_bits = bits; + + if (!bits) + sb_dsp_command(0xd0); /* Halt DMA */ + else if (bits & irq_mode) + sb_dsp_command(0xd4); /* Continue DMA */ +} + +static void +dsp_cleanup(void) +{ + irq_mode = IMODE_NONE; + intr_active = 0; +} + +static void +sb16_dsp_reset(int dev) +{ + u_long flags; + + flags = splhigh(); + + sb_reset_dsp(); + dsp_cleanup(); + + splx(flags); +} + +static void +sb16_dsp_halt(int dev) +{ + + if (dsp_16bit) { + sb_dsp_command01(0xd9); + sb_dsp_command01(0xd5); + } else { + sb_dsp_command01(0xda); + sb_dsp_command01(0xd0); + } + + +} + +static void +set_irq_hw(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: + printf("SB16_IRQ_LEVEL %d does not exist\n", level); + return; + } + sb_setmixer(IRQ_NR, ival); +} + +void +sb16_dsp_init(struct address_info * hw_config) +{ + if (sbc_major < 4) + return; /* Not a SB16 */ + + snprintf(sb16_dsp_operations.name, sizeof(sb16_dsp_operations.name), + "SoundBlaster 16 %d.%d", sbc_major, sbc_minor); + + conf_printf(sb16_dsp_operations.name, hw_config); + + if (num_audiodevs < MAX_AUDIO_DEV) { + audio_devs[my_dev = num_audiodevs++] = &sb16_dsp_operations; + audio_devs[my_dev]->dmachan1 = dma8; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; + + } else + printf("SB: Too many DSP devices available\n"); + sb16_dsp_ok = 1; + return; +} + +int +sb16_dsp_detect(struct address_info * hw_config) +{ + struct address_info *sb_config; + + if (sb16_dsp_ok) + return 1; /* Can't drive two cards */ + + if (!(sb_config = sound_getconf(SNDCARD_SB))) { + printf("SB16 Error: Plain SB not configured\n"); + return 0; + } + /* + * sb_setmixer(OPSW,0xf); if(sb_getmixer(OPSW)!=0xf) return 0; + */ + + if (!sb_reset_dsp()) + return 0; + + if (sbc_major < 4) /* Set by the plain SB driver */ + return 0; /* Not a SB16 */ + + if (hw_config->dma < 4) + if (hw_config->dma != sb_config->dma) { + printf("SB16 Error: Invalid DMA channel %d/%d\n", + sb_config->dma, hw_config->dma); + return 0; + } + dma16 = hw_config->dma; + dma8 = sb_config->dma; + /* hw_config->irq = 0; sb_config->irq; + hw_config->io_base = sb_config->io_base; + */ + set_irq_hw(sb_config->irq); + + sb_setmixer(DMA_NR, (1 << hw_config->dma) | (1 << sb_config->dma)); + + DEB(printf("SoundBlaster 16: IRQ %d DMA %d OK\n", + sb_config->irq, hw_config->dma)); + + /* + * dsp_showmessage(0xe3,99); + */ + sb16_dsp_ok = 1; + return 1; +} + +void +sb16_dsp_interrupt(int unused) +{ + int data; + + data = inb(DSP_DATA_AVL16); /* Interrupt acknowledge */ + + if (intr_active) + switch (irq_mode) { + case IMODE_OUTPUT: + DMAbuf_outputintr(my_dev, 1); + break; + + case IMODE_INPUT: + DMAbuf_inputintr(my_dev); + break; + + default: + printf("SoundBlaster: Unexpected interrupt\n"); + } +} +#endif diff --git a/sys/i386/isa/sound/sb16_midi.c b/sys/i386/isa/sound/sb16_midi.c new file mode 100644 index 0000000..e7b5084 --- /dev/null +++ b/sys/i386/isa/sound/sb16_midi.c @@ -0,0 +1,299 @@ +/* + * sound/sb16_midi.c + * + * The low level driver for the MPU-401 UART emulation of the SB16. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> +#include <i386/isa/sound/sbcard.h> + +#if defined(CONFIG_SB) && defined(CONFIG_SB16) && defined(CONFIG_MIDI) + +#include "sb.h" + +#define DATAPORT (sb16midi_base) +#define COMDPORT (sb16midi_base+1) +#define STATPORT (sb16midi_base+1) + +extern sound_os_info *sb_osp; + +#define sb16midi_status() inb( STATPORT) +#define input_avail() (!(sb16midi_status()&INPUT_AVAIL)) +#define output_ready() (!(sb16midi_status()&OUTPUT_READY)) +#define sb16midi_cmd(cmd) outb( COMDPORT, cmd) +#define sb16midi_read() inb( DATAPORT) +#define sb16midi_write(byte) outb( DATAPORT, byte) + +#define OUTPUT_READY 0x40 +#define INPUT_AVAIL 0x80 +#define MPU_ACK 0xFE +#define MPU_RESET 0xFF +#define UART_MODE_ON 0x3F + +static int sb16midi_opened = 0; +static int sb16midi_base = 0x330; +static int sb16midi_detected = 0; +static int my_dev; +extern int sbc_base; + +static int reset_sb16midi(void); +static void (*midi_input_intr) (int dev, u_char data); +static volatile u_char input_byte; + +static void +sb16midi_input_loop(void) +{ + while (input_avail()) { + u_char c = sb16midi_read(); + + if (c == MPU_ACK) + input_byte = c; + else if (sb16midi_opened & OPEN_READ && midi_input_intr) + midi_input_intr(my_dev, c); + } +} + +void +sb16midiintr(int unit) +{ + if (input_avail()) + sb16midi_input_loop(); +} + +static int +sb16midi_open(int dev, int mode, + void (*input) (int dev, u_char data), + void (*output) (int dev) +) +{ + if (sb16midi_opened) { + return -(EBUSY); + } + sb16midi_input_loop(); + + midi_input_intr = input; + sb16midi_opened = mode; + + return 0; +} + +static void +sb16midi_close(int dev) +{ + sb16midi_opened = 0; +} + +static int +sb16midi_out(int dev, u_char midi_byte) +{ + int timeout; + u_long flags; + + /* + * Test for input since pending input seems to block the output. + */ + + flags = splhigh(); + + if (input_avail()) + sb16midi_input_loop(); + + splx(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()) { + printf("MPU-401: Timeout\n"); + return 0; + } + sb16midi_write(midi_byte); + return 1; +} + +static int +sb16midi_start_read(int dev) +{ + return 0; +} + +static int +sb16midi_end_read(int dev) +{ + return 0; +} + +static int +sb16midi_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + return -(EINVAL); +} + +static void +sb16midi_kick(int dev) +{ +} + +static int +sb16midi_buffer_status(int dev) +{ + return 0; /* No data in buffers */ +} + +#define MIDI_SYNTH_NAME "SoundBlaster 16 Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include <i386/isa/sound/midi_synth.h> + +static struct midi_operations sb16midi_operations = +{ + {"SoundBlaster 16 Midi", 0, 0, SNDCARD_SB16MIDI}, + &std_midi_synth, + {0}, + sb16midi_open, + sb16midi_close, + sb16midi_ioctl, + sb16midi_out, + sb16midi_start_read, + sb16midi_end_read, + sb16midi_kick, + NULL, + sb16midi_buffer_status, + NULL +}; + + +void +attach_sb16midi(struct address_info * hw_config) +{ + int ok, timeout; + u_long flags; + + sb16midi_base = hw_config->io_base; + + if (!sb16midi_detected) + return; + + flags = splhigh(); + for (timeout = 30000; timeout < 0 && !output_ready(); timeout--); /* Wait */ + input_byte = 0; + sb16midi_cmd(UART_MODE_ON); + + ok = 0; + for (timeout = 50000; timeout > 0 && !ok; timeout--) + if (input_byte == MPU_ACK) + ok = 1; + else if (input_avail()) + if (sb16midi_read() == MPU_ACK) + ok = 1; + + splx(flags); + + if (num_midis >= MAX_MIDI_DEV) { + printf("Sound: Too many midi devices detected\n"); + return; + } + + conf_printf("SoundBlaster MPU-401", hw_config); + std_midi_synth.midi_dev = my_dev = num_midis; + midi_devs[num_midis++] = &sb16midi_operations; + return; +} + +static int +reset_sb16midi(void) +{ + int ok, timeout, n; + + /* + * Send the RESET command. Try again if no success at the first time. + */ + + if (inb(STATPORT) == 0xff) + return 0; + + ok = 0; + + /* flags = splhigh(); */ + + for (n = 0; n < 2 && !ok; n++) { + for (timeout = 30000; timeout < 0 && !output_ready(); timeout--); /* Wait */ + input_byte = 0; + sb16midi_cmd(MPU_RESET); /* Send MPU-401 RESET Command */ + + /* + * Wait at least 25 msec. This method is not accurate so + * let's make the loop bit longer. Cannot sleep since this is + * called during boot. + */ + + for (timeout = 50000; timeout > 0 && !ok; timeout--) + if (input_byte == MPU_ACK) /* Interrupt */ + ok = 1; + else if (input_avail()) + if (sb16midi_read() == MPU_ACK) + ok = 1; + + } + + sb16midi_opened = 0; + if (ok) + sb16midi_input_loop(); /* Flush input before enabling + * interrupts */ + + /* splx(flags); */ + + return ok; +} + + +int +probe_sb16midi(struct address_info * hw_config) +{ + int ok = 0; + struct address_info *sb_config; + + if (sbc_major < 4) + return 0; /* Not a SB16 */ + + if (!(sb_config = sound_getconf(SNDCARD_SB))) { + printf("SB16 Error: Plain SB not configured\n"); + return 0; + } + + sb16midi_base = hw_config->io_base; + + ok = reset_sb16midi(); + + sb16midi_detected = ok; + return ok; +} + +#endif + diff --git a/sys/i386/isa/sound/sb_card.c b/sys/i386/isa/sound/sb_card.c new file mode 100644 index 0000000..37da6b4 --- /dev/null +++ b/sys/i386/isa/sound/sb_card.c @@ -0,0 +1,67 @@ +/* + * sound/sb_card.c + * + * Detection routine for the SoundBlaster cards. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + * Modified: Riccardo Facchetti 24 Mar 1995 - Added the Audio Excel DSP 16 + * initialization routine. + * + * Major code cleanup - Luigi Rizzo (luigi@iet.unipi.it) 970711 + */ + +#include <i386/isa/sound/sound_config.h> + +#if NSB > 0 +#include <i386/isa/sound/sbcard.h> + +void +attach_sb_card(struct address_info * hw_config) +{ +#if defined(CONFIG_AUDIO) || defined(CONFIG_MIDI) + +#if 0 + /* why do a detect during the attach ? XXX */ + if (!sb_dsp_detect(hw_config)) + return ; +#endif + sb_dsp_init(hw_config); +#endif + + return ; +} + +int +probe_sb(struct address_info * hw_config) +{ + +#if defined(CONFIG_AEDSP16) && defined(AEDSP16_SBPRO) + /* + * Initialize Audio Excel DSP 16 to SBPRO. + */ + InitAEDSP16_SBPRO(hw_config); +#endif + return sb_dsp_detect(hw_config); +} +#endif diff --git a/sys/i386/isa/sound/sb_defs.h b/sys/i386/isa/sound/sb_defs.h new file mode 100644 index 0000000..e21c7c8 --- /dev/null +++ b/sys/i386/isa/sound/sb_defs.h @@ -0,0 +1,43 @@ +#ifdef PC98 +#define DSP_RESET (sbc_base + 0x600) +#define DSP_READ (sbc_base + 0xA00) +#define DSP_WRITE (sbc_base + 0xC00) +#define DSP_COMMAND (sbc_base + 0xC00) +#define DSP_STATUS (sbc_base + 0xC00) +#define DSP_DATA_AVAIL (sbc_base + 0xE00) +#define DSP_DATA_AVL16 (sbc_base + 0xF00) +#define MIXER_ADDR (sbc_base + 0x400) +#define MIXER_DATA (sbc_base + 0x500) +#define OPL3_LEFT (sbc_base + 0x000) +#define OPL3_RIGHT (sbc_base + 0x200) +#define OPL3_BOTH (sbc_base + 0x800) +#else +#define DSP_RESET (sbc_base + 0x6) +#define DSP_READ (sbc_base + 0xA) +#define DSP_WRITE (sbc_base + 0xC) +#define DSP_COMMAND (sbc_base + 0xC) +#define DSP_STATUS (sbc_base + 0xC) +#define DSP_DATA_AVAIL (sbc_base + 0xE) +#define DSP_DATA_AVL16 (sbc_base + 0xF) +#define MIXER_ADDR (sbc_base + 0x4) +#define MIXER_DATA (sbc_base + 0x5) +#define OPL3_LEFT (sbc_base + 0x0) +#define OPL3_RIGHT (sbc_base + 0x2) +#define OPL3_BOTH (sbc_base + 0x8) +#endif +/* DSP Commands */ + +#define DSP_CMD_SPKON 0xD1 +#define DSP_CMD_SPKOFF 0xD3 +#define DSP_CMD_DMAON 0xD0 +#define DSP_CMD_DMAOFF 0xD4 + +#define IMODE_NONE 0 +#define IMODE_OUTPUT 1 +#define IMODE_INPUT 2 +#define IMODE_INIT 3 +#define IMODE_MIDI 4 + +#define NORMAL_MIDI 0 +#define UART_MIDI 1 + diff --git a/sys/i386/isa/sound/sb_dsp.c b/sys/i386/isa/sound/sb_dsp.c new file mode 100644 index 0000000..2facd33 --- /dev/null +++ b/sys/i386/isa/sound/sb_dsp.c @@ -0,0 +1,1128 @@ +/* + * sound/sb_dsp.c + * + * The low level driver for the SoundBlaster DSP chip (SB1.0 to 2.1, SB Pro). + * + * Copyright by Hannu Savolainen 1994 + * + * 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. + * + * Modified: Hunyue Yau Jan 6 1994 Added code to support Sound Galaxy NX + * Pro + * + * JRA Gibson April 1995 Code added for MV ProSonic/Jazz 16 in 16 bit mode + */ + +#include <i386/isa/sound/sound_config.h> + +#if (NSB > 0) + +#ifdef SM_WAVE +#define JAZZ16 +#endif + +#include <i386/isa/sound/sbcard.h> +#include <i386/isa/sound/sb_mixer.h> +#include <machine/clock.h> + +#undef SB_TEST_IRQ + +/* + * XXX note -- only one sb-like device is supported until these + * variables are put in a struct sb_unit[] array + */ + + + +int sbc_base = 0; +static int sbc_irq = 0; +static int open_mode = 0; /* Read, write or both */ +int Jazz16_detected = 0; +int sb_no_recording = 0; +static int dsp_count = 0; +static int trigger_bits; + +/* + * 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. + */ + +int sb_dsp_ok = 0; /* Set to 1 after successful init */ +static int midi_disabled = 0; +int sb_dsp_highspeed = 0; +int sbc_major = 1, sbc_minor = 0; /* DSP version */ +static int dsp_stereo = 0; +static int dsp_current_speed = DSP_DEFAULT_SPEED; +static int sb16 = 0; + +int sb_midi_mode = NORMAL_MIDI; +int sb_midi_busy = 0; /* 1 if the process has output to * * + * MIDI */ +int sb_dsp_busy = 0; + +volatile int sb_irq_mode = IMODE_NONE; /* or IMODE_INPUT or IMODE_OUTPUT */ + +static int dma8 = 1; + +#ifdef JAZZ16 /* 16 bit support for JAZZ16 */ + +static int dsp_16bit = 0; +static int dma16 = 5; + +static int dsp_set_bits(int arg); +static int initialize_ProSonic16(void); +#endif /* end of 16 bit support for JAZZ16 */ + +int sb_duplex_midi = 0; +static int my_dev = 0; + +volatile int sb_intr_active = 0; + +static int dsp_speed(int); +static int dsp_set_stereo(int mode); +static void sb_dsp_reset(int dev); +sound_os_info *sb_osp = NULL; + +#if defined(CONFIG_MIDI) || defined(CONFIG_AUDIO) +static void dsp_speaker(char state); + +/* + * Common code for the midi and pcm functions + */ + +int +sb_dsp_command(u_char val) +{ + int i; + u_long limit; + + limit = get_time() + hz / 10; /* The timeout is 0.1 secods */ + + /* + * 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 && get_time() < limit; i++) { + if ((inb(DSP_STATUS) & 0x80) == 0) { + outb(DSP_COMMAND, val); + return 1; + } + } + + printf("SoundBlaster: DSP Command(0x%02x) timeout. IRQ conflict ?\n", val); + return 0; +} + +void +sbintr(int irq) +{ + int status; + +#ifdef CONFIG_SBPRO + if (sb16) { + u_char src = sb_getmixer(IRQ_STAT); /* Interrupt source register */ +#ifdef CONFIG_SB16 + if (src & 3) + sb16_dsp_interrupt(irq); +#ifdef CONFIG_MIDI + if (src & 4) + sb16midiintr(irq); /* SB MPU401 interrupt */ +#endif /* CONFIG_MIDI */ +#endif /* CONFIG_SB16 */ + if (!(src & 1)) + return; /* Not a DSP interupt */ + } +#endif /* CONFIG_SBPRO */ + + status = inb(DSP_DATA_AVAIL); /* Clear interrupt */ + + if (sb_intr_active) + switch (sb_irq_mode) { + case IMODE_OUTPUT: + sb_intr_active = 0; + DMAbuf_outputintr(my_dev, 1); + break; + + case IMODE_INPUT: + sb_intr_active = 0; + DMAbuf_inputintr(my_dev); + /* + * A complete buffer has been input. Let's start new one + */ + break; + + case IMODE_INIT: + sb_intr_active = 0; + break; + + case IMODE_MIDI: +#ifdef CONFIG_MIDI + sb_midi_interrupt(irq); +#endif + break; + + default: + printf("SoundBlaster: Unexpected interrupt\n"); + } +} + + +int +sb_reset_dsp(void) +{ + int loopc; + + outb(DSP_RESET, 1); + DELAY(10); + outb(DSP_RESET, 0); + DELAY(30); + + + for (loopc = 0; loopc < 100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++) + DELAY(10); + + if (inb(DSP_READ) != 0xAA) { + printf("sb_reset_dsp failed\n"); + return 0; /* Sorry */ + } + + return 1; +} + +#endif + +#ifdef CONFIG_AUDIO + +static void +dsp_speaker(char state) +{ + if (state) + sb_dsp_command(DSP_CMD_SPKON); + else + sb_dsp_command(DSP_CMD_SPKOFF); +} + +static int +dsp_speed(int speed) +{ + u_char tconst; + u_long flags; + int max_speed = 44100; + + if (speed < 4000) + speed = 4000; + + /* + * Older SB models don't support higher speeds than 22050. + */ + + if (sbc_major < 2 || (sbc_major == 2 && sbc_minor == 0)) + max_speed = 22050; + + /* + * SB models earlier than SB Pro have low limit for the input speed. + */ + if (open_mode != OPEN_WRITE) /* Recording is possible */ + if (sbc_major < 3) /* Limited input speed with these cards */ + if (sbc_major == 2 && sbc_minor > 0) + max_speed = 15000; + else + max_speed = 13000; + + if (speed > max_speed) + speed = max_speed; /* Invalid speed */ + + /* + * Logitech SoundMan Games and Jazz16 cards can support 44.1kHz + * stereo + */ +#if !defined (SM_GAMES) + /* + * Max. stereo speed is 22050 + */ + if (dsp_stereo && speed > 22050 && Jazz16_detected == 0) + speed = 22050; +#endif + + if ((speed > 22050) && sb_midi_busy) { + printf("SB Warning: High speed DSP not possible simultaneously with MIDI output\n"); + speed = 22050; + } + if (dsp_stereo) + speed *= 2; + + /* + * Now the speed should be valid + */ + + if (speed > 22050) { /* High speed mode */ + int tmp; + + tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8); + sb_dsp_highspeed = 1; + + flags = splhigh(); + if (sb_dsp_command(DSP_CMD_TCONST)) + sb_dsp_command(tconst); + splx(flags); + + tmp = 65536 - (tconst << 8); + speed = (256000000 + tmp / 2) / tmp; + } else { + int tmp; + + sb_dsp_highspeed = 0; + tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; + + flags = splhigh(); + if (sb_dsp_command(DSP_CMD_TCONST)) /* Set time constant */ + sb_dsp_command(tconst); + splx(flags); + + tmp = 256 - tconst; + speed = (1000000 + tmp / 2) / tmp; + } + + if (dsp_stereo) + speed /= 2; + + dsp_current_speed = speed; + return speed; +} + +static int +dsp_set_stereo(int mode) +{ + dsp_stereo = 0; + +#ifndef CONFIG_SBPRO + return 0; +#else + if (sbc_major < 3 || sb16) + return 0; /* Sorry no stereo */ + + if (mode && sb_midi_busy) { + printf("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n"); + return 0; + } + dsp_stereo = !!mode; + return dsp_stereo; +#endif +} + +static void +sb_dsp_output_block(int dev, u_long buf, int count, + int intrflag, int restart_dma) +{ + u_long flags; + + if (!sb_irq_mode) + dsp_speaker(ON); + + DMAbuf_start_dma(dev, buf, count, 1); + + sb_irq_mode = 0; + + if (audio_devs[dev]->dmachan1 > 3) + count >>= 1; + count--; + dsp_count = count; + + sb_irq_mode = IMODE_OUTPUT; + if (sb_dsp_highspeed) { + flags = splhigh(); + if (sb_dsp_command(DSP_CMD_HSSIZE)) { /* High speed size */ + sb_dsp_command((u_char) (dsp_count & 0xff)); + sb_dsp_command((u_char) ((dsp_count >> 8) & 0xff)); + sb_dsp_command(DSP_CMD_HSDAC); /* High speed 8 bit DAC */ + } else + printf("SB Error: Unable to start (high speed) DAC\n"); + splx(flags); + } else { + flags = splhigh(); + if (sb_dsp_command(DSP_CMD_DAC8)) { /* 8-bit DAC (DMA) */ + sb_dsp_command((u_char) (dsp_count & 0xff)); + sb_dsp_command((u_char) ((dsp_count >> 8) & 0xff)); + } else + printf("SB Error: Unable to start DAC\n"); + splx(flags); + } + sb_intr_active = 1; +} + +static void +sb_dsp_start_input(int dev, u_long buf, int count, int intrflag, + int restart_dma) +{ + u_long flags; + + if (sb_no_recording) { + printf("SB Error: This device doesn't support recording\n"); + return; + } + /* + * Start a DMA input to the buffer pointed by dmaqtail + */ + + if (!sb_irq_mode) + dsp_speaker(OFF); + + DMAbuf_start_dma(dev, buf, count, 0); + sb_irq_mode = 0; + + if (audio_devs[dev]->dmachan1 > 3) + count >>= 1; + count--; + dsp_count = count; + + sb_irq_mode = IMODE_INPUT; + if (sb_dsp_highspeed) { + flags = splhigh(); + if (sb_dsp_command(DSP_CMD_HSSIZE)) { /* High speed size */ + sb_dsp_command((u_char) (dsp_count & 0xff)); + sb_dsp_command((u_char) ((dsp_count >> 8) & 0xff)); + sb_dsp_command(DSP_CMD_HSADC); /* High speed 8 bit ADC */ + } else + printf("SB Error: Unable to start (high speed) ADC\n"); + splx(flags); + } else { + flags = splhigh(); + if (sb_dsp_command(DSP_CMD_ADC8)) { /* 8-bit ADC (DMA) */ + sb_dsp_command((u_char) (dsp_count & 0xff)); + sb_dsp_command((u_char) ((dsp_count >> 8) & 0xff)); + } else + printf("SB Error: Unable to start ADC\n"); + splx(flags); + } + + sb_intr_active = 1; +} + +static void +sb_dsp_trigger(int dev, int bits) +{ + if (bits == trigger_bits) + return; + + if (!bits) + sb_dsp_command(0xd0); /* Halt DMA */ + else if (bits & sb_irq_mode) + sb_dsp_command(0xd4); /* Continue DMA */ + + trigger_bits = bits; +} + +static void +dsp_cleanup(void) +{ + sb_intr_active = 0; +} + +static int +sb_dsp_prepare_for_input(int dev, int bsize, int bcount) +{ + int fudge = -1; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + + dsp_cleanup(); + dsp_speaker(OFF); + + if (sbc_major == 3) { /* SB Pro */ +#ifdef JAZZ16 + /* + * Select correct dma channel for 16/8 bit acccess + */ + audio_devs[my_dev]->dmachan1 = dsp_16bit ? dma16 : dma8; + if (dsp_stereo) + sb_dsp_command(dsp_16bit ? 0xac : 0xa8); + else + sb_dsp_command(dsp_16bit ? 0xa4 : 0xa0); +#else + /* + * 8 bit only cards use this + */ + if (dsp_stereo) + sb_dsp_command(0xa8); + else + sb_dsp_command(0xa0); +#endif + dsp_speed(dsp_current_speed); /* Speed must be recalculated + * if #channels * changes */ + } + + fudge = audio_devs[my_dev]->dmachan1; + if (dmap->dma_chan != fudge ) { + isa_dma_release( dmap->dma_chan); + isa_dma_acquire(fudge); + dmap->dma_chan = fudge; + } + + trigger_bits = 0; + sb_dsp_command(DSP_CMD_DMAHALT); /* Halt DMA */ + return 0; +} + +static int +sb_dsp_prepare_for_output(int dev, int bsize, int bcount) +{ + + int fudge; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + dsp_cleanup(); + dsp_speaker(ON); + +#ifdef CONFIG_SBPRO + if (sbc_major == 3) { /* SB Pro */ +#ifdef JAZZ16 + /* + * 16 bit specific instructions + */ + audio_devs[my_dev]->dmachan1 = dsp_16bit ? dma16 : dma8; + + if (Jazz16_detected != 2) /* SM Wave */ + sb_mixer_set_stereo(dsp_stereo); + if (dsp_stereo) + sb_dsp_command(dsp_16bit ? 0xac : 0xa8); + else + sb_dsp_command(dsp_16bit ? 0xa4 : 0xa0); +#else + sb_mixer_set_stereo(dsp_stereo); +#endif + dsp_speed(dsp_current_speed); /* Speed must be recalculated + * if #channels * changes */ + } +#endif + fudge = audio_devs[my_dev]->dmachan1; + + if (dmap->dma_chan != fudge ) { + isa_dma_release( dmap->dma_chan); + isa_dma_acquire(fudge); + dmap->dma_chan = fudge; + } + + trigger_bits = 0; + sb_dsp_command(DSP_CMD_DMAHALT); /* Halt DMA */ + return 0; +} + +static void +sb_dsp_halt_xfer(int dev) +{ +} + +static int +sb_dsp_open(int dev, int mode) +{ + if (!sb_dsp_ok) { + printf("SB Error: SoundBlaster board not installed\n"); + return -(ENXIO); + } + if (sb_no_recording && mode & OPEN_READ) { + printf("SB Warning: Recording not supported by this device\n"); + } + if (sb_intr_active || (sb_midi_busy && sb_midi_mode == UART_MIDI)) { + printf("SB: PCM not possible during MIDI input\n"); + return -(EBUSY); + } + /* + * Allocate 8 bit dma + */ +#ifdef JAZZ16 + audio_devs[my_dev]->dmachan1 = dma8; + /* + * Allocate 16 bit dma + */ + if (Jazz16_detected != 0) + if (dma16 != dma8) { + if (0) { + return -(EBUSY); + } + } +#endif + + sb_irq_mode = IMODE_NONE; + + sb_dsp_busy = 1; + open_mode = mode; + + + return 0; +} + +static void +sb_dsp_close(int dev) +{ +#ifdef JAZZ16 + /* + * Release 16 bit dma channel + */ + if (Jazz16_detected) { + audio_devs[my_dev]->dmachan1 = dma8; + + } +#endif + + dsp_cleanup(); + dsp_speaker(OFF); + sb_dsp_busy = 0; + sb_dsp_highspeed = 0; + open_mode = 0; + +} + +#ifdef JAZZ16 +/* + * Function dsp_set_bits() only required for 16 bit cards + */ +static int +dsp_set_bits(int arg) +{ + if (arg) + if (Jazz16_detected == 0) + dsp_16bit = 0; + else + switch (arg) { + case 8: + dsp_16bit = 0; + break; + case 16: + dsp_16bit = 1; + break; + default: + dsp_16bit = 0; + } + return dsp_16bit ? 16 : 8; +} + +#endif /* ifdef JAZZ16 */ + +static int +sb_dsp_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) +{ + switch (cmd) { + case SOUND_PCM_WRITE_RATE: + if (local) + return dsp_speed((int) arg); + return *(int *) arg = dsp_speed((*(int *) arg)); + break; + + case SOUND_PCM_READ_RATE: + if (local) + return dsp_current_speed; + return *(int *) arg = dsp_current_speed; + break; + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return dsp_set_stereo((int) arg - 1) + 1; + return *(int *) arg = dsp_set_stereo((*(int *) arg) - 1) + 1; + break; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return dsp_stereo + 1; + return *(int *) arg = dsp_stereo + 1; + break; + + case SNDCTL_DSP_STEREO: + if (local) + return dsp_set_stereo((int) arg); + return *(int *) arg = dsp_set_stereo((*(int *) arg)); + break; + +#ifdef JAZZ16 + /* + * Word size specific cases here. + * SNDCTL_DSP_SETFMT=SOUND_PCM_WRITE_BITS + */ + case SNDCTL_DSP_SETFMT: + if (local) + return dsp_set_bits((int) arg); + return *(int *) arg = dsp_set_bits((*(int *) arg)); + break; + + case SOUND_PCM_READ_BITS: + if (local) + return dsp_16bit ? 16 : 8; + return *(int *) arg = dsp_16bit ? 16 : 8; + break; +#else + case SOUND_PCM_WRITE_BITS: + case SOUND_PCM_READ_BITS: + if (local) + return 8; + return *(int *) (int) arg = 8; /* Only 8 bits/sample supported */ + break; +#endif /* ifdef JAZZ16 */ + + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -(EINVAL); + break; + + default:; + } + + return -(EINVAL); +} + +static void +sb_dsp_reset(int dev) +{ + u_long flags; + + flags = splhigh(); + + sb_reset_dsp(); + dsp_speed(dsp_current_speed); + dsp_cleanup(); + + splx(flags); +} + +#endif + + +#ifdef JAZZ16 + +/* + * Initialization of a Media Vision ProSonic 16 Soundcard. The function + * initializes a ProSonic 16 like PROS.EXE does for DOS. It sets the base + * address, the DMA-channels, interrupts and enables the joystickport. + * + * Also used by Jazz 16 (same card, different name) + * + * written 1994 by Rainer Vranken E-Mail: + * rvranken@polaris.informatik.uni-essen.de + */ + +u_int +get_sb_byte(void) +{ + int i; + + for (i = 1000; i; i--) + if (inb(DSP_DATA_AVAIL) & 0x80) { + return inb(DSP_READ); + } + return 0xffff; +} + +#ifdef SM_WAVE +/* + * Logitech Soundman Wave detection and initialization by Hannu Savolainen. + * + * There is a microcontroller (8031) in the SM Wave card for MIDI emulation. + * it's located at address MPU_BASE+4. MPU_BASE+7 is a SM Wave specific + * control register for MC reset, SCSI, OPL4 and DSP (future expansion) + * address decoding. Otherwise the SM Wave is just a ordinary MV Jazz16 based + * soundcard. + */ + +static void +smw_putmem(int base, int addr, u_char val) +{ + u_long flags; + + flags = splhigh(); + + outb(base + 1, addr & 0xff); /* Low address bits */ + outb(base + 2, addr >> 8); /* High address bits */ + outb(base, val); /* Data */ + + splx(flags); +} + +static u_char +smw_getmem(int base, int addr) +{ + u_long flags; + u_char val; + + flags = splhigh(); + + outb(base + 1, addr & 0xff); /* Low address bits */ + outb(base + 2, addr >> 8); /* High address bits */ + val = inb(base); /* Data */ + + splx(flags); + return val; +} + +#ifdef SMW_MIDI0001_INCLUDED +#include </sys/i386/isa/sound/smw-midi0001.h> +#else +u_char *smw_ucode = NULL; +int smw_ucodeLen = 0; + +#endif + +static int +initialize_smw(int mpu_base) +{ + + int mp_base = mpu_base + 4; /* Microcontroller base */ + int i; + u_char control; + + + /* + * Reset the microcontroller so that the RAM can be accessed + */ + + control = inb(mpu_base + 7); + outb(mpu_base + 7, control | 3); /* Set last two bits to 1 (?) */ + outb(mpu_base + 7, (control & 0xfe) | 2); /* xxxxxxx0 resets the mc */ + DELAY(3000); /* Wait at least 1ms */ + + outb(mpu_base + 7, control & 0xfc); /* xxxxxx00 enables RAM */ + + /* + * Detect microcontroller by probing the 8k RAM area + */ + smw_putmem(mp_base, 0, 0x00); + smw_putmem(mp_base, 1, 0xff); + DELAY(10); + + if (smw_getmem(mp_base, 0) != 0x00 || smw_getmem(mp_base, 1) != 0xff) { + printf("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n", + smw_getmem(mp_base, 0), smw_getmem(mp_base, 1)); + return 0; /* No RAM */ + } + /* + * There is RAM so assume it's really a SM Wave + */ + + if (smw_ucodeLen > 0) { + if (smw_ucodeLen != 8192) { + printf("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n"); + return 1; + } + /* + * Download microcode + */ + + for (i = 0; i < 8192; i++) + smw_putmem(mp_base, i, smw_ucode[i]); + + /* + * Verify microcode + */ + + for (i = 0; i < 8192; i++) + if (smw_getmem(mp_base, i) != smw_ucode[i]) { + printf("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 u_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 VoxWare 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(mpu_base + 7, control | 0x03); /* xxxxxx11 restarts */ + return 1; +} + +#endif + +static int +initialize_ProSonic16(void) +{ + int x; + static u_char int_translat[16] = + {0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6}, + dma_translat[8] = + {0, 1, 0, 2, 0, 3, 0, 4}; + + struct address_info *mpu_config; + + int mpu_base, mpu_irq; + + if ((mpu_config = sound_getconf(SNDCARD_MPU401))) { + mpu_base = mpu_config->io_base; + mpu_irq = mpu_config->irq; + } else { + mpu_base = mpu_irq = 0; + } + + outb(0x201, 0xAF); /* ProSonic/Jazz16 wakeup */ + DELAY(15000); /* wait at least 10 milliseconds */ + outb(0x201, 0x50); + outb(0x201, (sbc_base & 0x70) | ((mpu_base & 0x30) >> 4)); + + if (sb_reset_dsp()) { /* OK. We have at least a SB */ + + /* Check the version number of ProSonic (I guess) */ + + if (!sb_dsp_command(0xFA)) + return 1; + if (get_sb_byte() != 0x12) + return 1; + + if (sb_dsp_command(0xFB) && /* set DMA-channels and Interrupts */ + sb_dsp_command((dma_translat[JAZZ_DMA16]<<4)|dma_translat[dma8]) && + sb_dsp_command((int_translat[mpu_irq]<<4)|int_translat[sbc_irq])) { + Jazz16_detected = 1; + if (mpu_base == 0) + printf("Jazz16: No MPU401 devices configured - MIDI port not initialized\n"); + +#ifdef SM_WAVE + if (mpu_base != 0) + if (initialize_smw(mpu_base)) + Jazz16_detected = 2; +#endif + sb_dsp_disable_midi(); + } + return 1; /* There was at least a SB */ + } + return 0; /* No SB or ProSonic16 detected */ +} + +#endif /* ifdef JAZZ16 */ + +int +sb_dsp_detect(struct address_info * hw_config) +{ + sbc_base = hw_config->io_base; + sbc_irq = hw_config->irq; + sb_osp = hw_config->osp; + + + if (sb_dsp_ok) + return 0; /* Already initialized */ + dma8 = hw_config->dma; + +#ifdef JAZZ16 + dma16 = JAZZ_DMA16; + + if (!initialize_ProSonic16()) + return 0; +#else + if (!sb_reset_dsp()) + return 0; +#endif + + return 1; /* Detected */ +} + +#ifdef CONFIG_AUDIO +static struct audio_operations sb_dsp_operations = +{ + "SoundBlaster", + NOTHING_SPECIAL, + AFMT_U8, /* Just 8 bits. Poor old SB */ + NULL, + sb_dsp_open, + sb_dsp_close, + sb_dsp_output_block, + sb_dsp_start_input, + sb_dsp_ioctl, + sb_dsp_prepare_for_input, + sb_dsp_prepare_for_output, + sb_dsp_reset, + sb_dsp_halt_xfer, + NULL, /* local_qlen */ + NULL, /* copy_from_user */ + NULL, + NULL, + sb_dsp_trigger +}; + +#endif + +void +sb_dsp_init(struct address_info * hw_config) +{ + int i; + char *fmt = NULL ; + +#ifdef CONFIG_SBPRO + int mixer_type = 0; + +#endif + + sb_osp = hw_config->osp; + sbc_major = sbc_minor = 0; + sb_dsp_command(DSP_CMD_GETVER); /* Get version */ + + for (i = 10000; i; i--) { /* perhaps wait longer on a fast machine ? */ + if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ + if (sbc_major == 0) + sbc_major = inb(DSP_READ); + else { + sbc_minor = inb(DSP_READ); + break; + } + } else + DELAY(20); + } + + if (sbc_major == 0) { + printf("\n\nFailed to get SB version (%x) - possible I/O conflict\n\n", + inb(DSP_DATA_AVAIL)); + sbc_major = 1; + } + if (sbc_major == 2 || sbc_major == 3) + sb_duplex_midi = 1; + + if (sbc_major == 4) + sb16 = 1; + + if (sbc_major == 3 && sbc_minor == 1) { + int ess_major = 0, ess_minor = 0; + + /* + * Try to detect ESS chips. + */ + + sb_dsp_command(DSP_CMD_GETID); /* Return identification bytes. */ + + for (i = 1000; i; i--) { + if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */ + if (ess_major == 0) + ess_major = inb(DSP_READ); + else { + ess_minor = inb(DSP_READ); + break; + } + } + } + + if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) + printf("Hmm... Could this be an ESS488 based card (rev %d)\n", + ess_minor & 0x0f); + else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) + printf("Hmm... Could this be an ESS688 based card (rev %d)\n", + ess_minor & 0x0f); + } + if (snd_set_irq_handler(sbc_irq, sbintr, sb_osp) < 0) + printf("sb_dsp: Can't allocate IRQ\n");; + +#ifdef CONFIG_SBPRO + if (sbc_major >= 3) + mixer_type = sb_mixer_init(sbc_major); +#else + if (sbc_major >= 3) + printf("\nNOTE! SB Pro support required with your soundcard!\n"); +#endif + + +#ifdef CONFIG_AUDIO + if (sbc_major >= 3) { + if (Jazz16_detected) { + if (Jazz16_detected == 2) + fmt = "SoundMan Wave %d.%d"; + else + fmt = "MV Jazz16 %d.%d"; + sb_dsp_operations.format_mask |= AFMT_S16_LE; /* 16 bits */ + } else +#ifdef __SGNXPRO__ + if (mixer_type == 2) + fmt = "Sound Galaxy NX Pro %d.%d" ; + else +#endif /* __SGNXPRO__ */ + if (sbc_major == 4) + fmt = "SoundBlaster 16 %d.%d"; + else + fmt = "SoundBlaster Pro %d.%d"; + } else { + fmt = "SoundBlaster %d.%d" ; + } + + snprintf(sb_dsp_operations.name, sizeof(sb_dsp_operations.name), + fmt, sbc_major, sbc_minor); + conf_printf(sb_dsp_operations.name, hw_config); + +#if defined(CONFIG_SB16) && defined(CONFIG_SBPRO) + if (!sb16) /* There is a better driver for SB16 */ +#endif /* CONFIG_SB16 && CONFIG_SBPRO */ + if (num_audiodevs < MAX_AUDIO_DEV) { + audio_devs[my_dev = num_audiodevs++] = &sb_dsp_operations; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; + dma8 = audio_devs[my_dev]->dmachan1 = hw_config->dma; + audio_devs[my_dev]->dmachan2 = -1; +#ifdef JAZZ16 + /* + * Allocate 16 bit dma + */ + if (Jazz16_detected != 0) + if (dma16 != dma8) { + if (0) { + printf("Jazz16: Can't allocate 16 bit DMA channel\n"); + } + } +#endif /* JAZZ16 */ + } else + printf("SB: Too many DSP devices available\n"); +#else + conf_printf("SoundBlaster (configured without audio support)", hw_config); +#endif + +#ifdef CONFIG_MIDI + if (!midi_disabled && !sb16) { + /* + * Midi don't work in the SB emulation mode of PAS, + * SB16 has better midi interface + */ + sb_midi_init(sbc_major); + } +#endif /* CONFIG_MIDI */ + sb_dsp_ok = 1; +} + +void +sb_dsp_disable_midi(void) +{ + midi_disabled = 1; +} +#endif diff --git a/sys/i386/isa/sound/sb_midi.c b/sys/i386/isa/sound/sb_midi.c new file mode 100644 index 0000000..3d773be --- /dev/null +++ b/sys/i386/isa/sound/sb_midi.c @@ -0,0 +1,211 @@ +/* + * sound/sb_dsp.c + * + * The low level driver for the SoundBlaster DS chips. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + +#if (NSND > 0) && defined(CONFIG_SB) && defined(CONFIG_MIDI) + +#include <i386/isa/sound/sbcard.h> +#undef SB_TEST_IRQ + +/* + * The DSP channel can be used either for input or output. Variable + * 'sb_irq_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + +extern int sb_dsp_ok; /* Set to 1 atfer successful initialization */ +extern int sbc_base; + +extern int sb_midi_mode; +extern int sb_midi_busy; /* 1 if the process has output to MIDI */ +extern int sb_dsp_busy; +extern int sb_dsp_highspeed; + +extern volatile int sb_irq_mode; +extern int sb_duplex_midi; +extern int sb_intr_active; +static int input_opened = 0; +static int my_dev; + +extern sound_os_info *sb_osp; + +static void (*midi_input_intr) (int dev, u_char data); + +static int +sb_midi_open(int dev, int mode, void (*input) (int dev, u_char data), + void (*output) (int dev)) +{ + int ret; + + if (!sb_dsp_ok) { + printf("SB Error: MIDI hardware not installed\n"); + return -(ENXIO); + } + if (sb_midi_busy) + return -(EBUSY); + + if (mode != OPEN_WRITE && !sb_duplex_midi) { + if (num_midis == 1) + printf("SoundBlaster: Midi input not currently supported\n"); + return -(EPERM); + } + sb_midi_mode = NORMAL_MIDI; + if (mode != OPEN_WRITE) { + if (sb_dsp_busy || sb_intr_active) + return -(EBUSY); + sb_midi_mode = UART_MIDI; + } + if (sb_dsp_highspeed) { + printf("SB Error: Midi output not possible during stereo or high speed audio\n"); + return -(EBUSY); + } + if (sb_midi_mode == UART_MIDI) { + sb_irq_mode = IMODE_MIDI; + + sb_reset_dsp(); + + if (!sb_dsp_command(0x35)) + return -(EIO); /* Enter the UART mode */ + sb_intr_active = 1; + + input_opened = 1; + midi_input_intr = input; + } + sb_midi_busy = 1; + + return 0; +} + +static void +sb_midi_close(int dev) +{ + if (sb_midi_mode == UART_MIDI) { + sb_reset_dsp(); /* The only way to kill the UART mode */ + } + sb_intr_active = 0; + sb_midi_busy = 0; + input_opened = 0; +} + +static int +sb_midi_out(int dev, u_char midi_byte) +{ + u_long flags; + + if (sb_midi_mode == NORMAL_MIDI) { + flags = splhigh(); + if (sb_dsp_command(0x38)) + sb_dsp_command(midi_byte); + else + printf("SB Error: Unable to send a MIDI byte\n"); + splx(flags); + } else + sb_dsp_command(midi_byte); /* UART write */ + + return 1; +} + +static int +sb_midi_start_read(int dev) +{ + if (sb_midi_mode != UART_MIDI) { + printf("SoundBlaster: MIDI input not implemented.\n"); + return -(EPERM); + } + return 0; +} + +static int +sb_midi_end_read(int dev) +{ + if (sb_midi_mode == UART_MIDI) { + sb_reset_dsp(); + sb_intr_active = 0; + } + return 0; +} + +static int +sb_midi_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + return -(EPERM); +} + +void +sb_midi_interrupt(int dummy) +{ + u_long flags; + u_char data; + + flags = splhigh(); + + data = inb(DSP_READ); + if (input_opened) + midi_input_intr(my_dev, data); + + splx(flags); +} + +#define MIDI_SYNTH_NAME "SoundBlaster Midi" +#define MIDI_SYNTH_CAPS 0 +#include <i386/isa/sound/midi_synth.h> + +static struct midi_operations sb_midi_operations = +{ + {"SoundBlaster", 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, /* Kick */ + NULL, /* command */ + NULL, /* buffer_status */ + NULL +}; + +void +sb_midi_init(int model) +{ + if (num_midis >= MAX_MIDI_DEV) { + printf("Sound: Too many midi devices detected\n"); + return; + } + std_midi_synth.midi_dev = num_midis; + my_dev = num_midis; + midi_devs[num_midis++] = &sb_midi_operations; +} + +#endif diff --git a/sys/i386/isa/sound/sb_mixer.c b/sys/i386/isa/sound/sb_mixer.c new file mode 100644 index 0000000..14db176 --- /dev/null +++ b/sys/i386/isa/sound/sb_mixer.c @@ -0,0 +1,529 @@ +/* + * sound/sb_mixer.c + * + * The low level mixer driver for the SoundBlaster Pro and SB16 cards. + * + * Copyright by Hannu Savolainen 1994 + * + * 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. + * + * Modified: Hunyue Yau Jan 6 1994 Added code to support the Sound Galaxy + * NX Pro mixer. + * + */ + +#include <i386/isa/sound/sound_config.h> + +#if (NSB > 0) && defined(CONFIG_SBPRO) +#define __SB_MIXER_C__ + +#include <i386/isa/sound/sbcard.h> +#include <i386/isa/sound/sb_mixer.h> +#undef SB_TEST_IRQ + +extern int sbc_base; +extern int Jazz16_detected; +extern sound_os_info *sb_osp; + +static int mixer_initialized = 0; + +static int supported_rec_devices; +static int supported_devices; +static int recmask = 0; +static int mixer_model; +static int mixer_caps; +static mixer_tab *iomap; + +void +sb_setmixer(u_int port, u_int value) +{ + u_long flags; + + flags = splhigh(); /* XXX ouch... */ + outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + outb(MIXER_DATA, (u_char) (value & 0xff)); + DELAY(10); + splx(flags); +} + +int +sb_getmixer(u_int port) +{ + int val; + u_long flags; + + flags = splhigh(); + outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */ + DELAY(10); + val = inb(MIXER_DATA); + DELAY(10); + splx(flags); + + return val; +} + +void +sb_mixer_set_stereo(int mode) +{ + if (!mixer_initialized) + return; + + sb_setmixer(OUT_FILTER, ((sb_getmixer(OUT_FILTER) & ~STEREO_DAC) + | (mode ? STEREO_DAC : MONO_DAC))); +} + +/* + * Returns: + * 0 No mixer detected. + * 1 Only a plain Sound Blaster Pro style mixer detected. + * 2 The Sound Galaxy NX Pro mixer detected. + */ +static int +detect_mixer(void) +{ + +#ifdef __SGNXPRO__ + int oldbass, oldtreble; + extern int sbc_major; +#endif + int retcode = 1; + + /* + * 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(FM_VOL, 0xff); + sb_setmixer(VOC_VOL, 0x33); + + if (sb_getmixer(FM_VOL) != 0xff) + return 0; /* No match */ + if (sb_getmixer(VOC_VOL) != 0x33) + return 0; + +#ifdef __SGNXPRO__ + /* + * Attempt to detect the SG NX Pro by check for valid bass/treble + * registers. + */ + oldbass = sb_getmixer(BASS_LVL); + oldtreble = sb_getmixer(TREBLE_LVL); + + sb_setmixer(BASS_LVL, 0xaa); + sb_setmixer(TREBLE_LVL, 0x55); + + if ((sb_getmixer(BASS_LVL) != 0xaa) || + (sb_getmixer(TREBLE_LVL) != 0x55)) { + retcode = 1; /* 1 == Only SB Pro detected */ + } else + retcode = 2; /* 2 == SG NX Pro detected */ + /* + * Restore register in either case since SG NX Pro has EEPROM with + * 'preferred' values stored. + */ + sb_setmixer(BASS_LVL, oldbass); + sb_setmixer(TREBLE_LVL, oldtreble); + + /* + * If the SB version is 3.X (SB Pro), assume we have a SG NX Pro 16. + * In this case it's good idea to disable the Disney Sound Source + * compatibility mode. It's useless and just causes noise every time + * the LPT-port is accessed. + * + * Also place the card into WSS mode. + */ + if (sbc_major == 3) { + outb(sbc_base + 0x1c, 0x01); + outb(sbc_base + 0x1a, 0x00); + } +#endif + return retcode; +} + +static void +change_bits(u_char *regval, int dev, int chn, int newval) +{ + u_char mask; + int shift; + + mask = (1 << (*iomap)[dev][chn].nbits) - 1; + newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ + + shift = (*iomap)[dev][chn].bitoffs - (*iomap)[dev][LEFT_CHN].nbits + 1; + + *regval &= ~(mask << shift); /* Filter out the previous value */ + *regval |= (newval & mask) << shift; /* Set the new value */ +} + +static int +sb_mixer_get(int dev) +{ + if (!((1 << dev) & supported_devices)) + return -(EINVAL); + + return levels[dev]; +} + +#ifdef JAZZ16 +static char smw_mix_regs[] =/* Left mixer registers */ +{ + 0x0b, /* SOUND_MIXER_VOLUME */ + 0x0d, /* SOUND_MIXER_BASS */ + 0x0d, /* SOUND_MIXER_TREBLE */ + 0x05, /* SOUND_MIXER_SYNTH */ + 0x09, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x03, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x07, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00, /* SOUND_MIXER_RECLEV */ + 0x00, /* SOUND_MIXER_IGAIN */ + 0x00, /* SOUND_MIXER_OGAIN */ + 0x00, /* SOUND_MIXER_LINE1 */ + 0x00, /* SOUND_MIXER_LINE2 */ + 0x00 /* SOUND_MIXER_LINE3 */ +}; + +static void +smw_mixer_init(void) +{ + int i; + + sb_setmixer(0x00, 0x18);/* Mute unused (Telephone) line */ + sb_setmixer(0x10, 0x38);/* Config register 2 */ + + supported_devices = 0; + for (i = 0; i < sizeof(smw_mix_regs); i++) + if (smw_mix_regs[i] != 0) + supported_devices |= (1 << i); + + supported_rec_devices = supported_devices & + ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | + SOUND_MASK_VOLUME); +} + +static int +smw_mixer_set(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 (!(supported_devices & (1 << dev))) /* Not supported */ + return -(EINVAL); + + switch (dev) { + case SOUND_MIXER_VOLUME: + sb_setmixer(0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */ + sb_setmixer(0x0c, 96 - (96 * right / 100)); + break; + + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + levels[dev] = left | (right << 8); + + /* Set left bass and treble values */ + val = ((levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / 100) << 4; + val |= ((levels[SOUND_MIXER_BASS] & 0xff) * 16 / 100) & 0x0f; + sb_setmixer(0x0d, val); + + /* Set right bass and treble values */ + val = (((levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / 100) << 4; + val |= (((levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / 100) & 0x0f; + sb_setmixer(0x0e, val); + break; + + default: + reg = smw_mix_regs[dev]; + if (reg == 0) + return -(EINVAL); + sb_setmixer(reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */ + sb_setmixer(reg + 1, (24 - (24 * right / 100)) | 0x40); + } + + levels[dev] = left | (right << 8); + return left | (right << 8); +} + +#endif + +static int +sb_mixer_set(int dev, int value) +{ + int left = value & 0x000000ff; + int right = (value & 0x0000ff00) >> 8; + + int regoffs; + u_char val; + +#ifdef JAZZ16 + if (Jazz16_detected == 2) + return smw_mixer_set(dev, value); +#endif + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + if (dev > 31) + return -(EINVAL); + + if (!(supported_devices & (1 << dev))) /* Not supported */ + return -(EINVAL); + + regoffs = (*iomap)[dev][LEFT_CHN].regno; + + if (regoffs == 0) + return -(EINVAL); + + val = sb_getmixer(regoffs); + change_bits(&val, dev, LEFT_CHN, left); + + levels[dev] = left | (left << 8); + + if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */ + sb_setmixer(regoffs, val); /* Save the old one */ + regoffs = (*iomap)[dev][RIGHT_CHN].regno; + + if (regoffs == 0) + return left | (left << 8); /* Just left channel present */ + + val = sb_getmixer(regoffs); /* Read the new one */ + } + change_bits(&val, dev, RIGHT_CHN, right); + + sb_setmixer(regoffs, val); + + levels[dev] = left | (right << 8); + return left | (right << 8); +} + +static void +set_recsrc(int src) +{ + sb_setmixer(RECORD_SRC, (sb_getmixer(RECORD_SRC) & ~7) | (src & 0x7)); +} + +static int +set_recmask(int mask) +{ + int devmask, i; + u_char regimageL, regimageR; + + devmask = mask & supported_rec_devices; + + switch (mixer_model) { + case 3: + + if (devmask != SOUND_MASK_MIC && devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) { + /* + * More than one devices selected. Drop the previous + * selection + */ + devmask &= ~recmask; + } + if (devmask != SOUND_MASK_MIC && devmask != SOUND_MASK_LINE && + devmask != SOUND_MASK_CD) { + /* More than one devices selected. Default to mic */ + devmask = SOUND_MASK_MIC; + } + if (devmask ^ recmask) { /* Input source changed */ + switch (devmask) { + + case SOUND_MASK_MIC: + set_recsrc(SRC_MIC); + break; + + case SOUND_MASK_LINE: + set_recsrc(SRC_LINE); + break; + + case SOUND_MASK_CD: + set_recsrc(SRC_CD); + break; + + default: + set_recsrc(SRC_MIC); + } + } + break; + + case 4: + 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(SB16_IMASK_L, regimageL); + sb_setmixer(SB16_IMASK_R, regimageR); + break; + } + + recmask = devmask; + return recmask; +} + +static int +sb_mixer_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + if (((cmd >> 8) & 0xff) == 'M') { + if (cmd & IOC_IN) + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + return *(int *) arg = set_recmask((*(int *) arg)); + break; + + default: + return *(int *) arg = sb_mixer_set(cmd & 0xff, (*(int *) arg)); + } + else + switch (cmd & 0xff) { /* Return parameters */ + + case SOUND_MIXER_RECSRC: + return *(int *) arg = recmask; + break; + + case SOUND_MIXER_DEVMASK: + return *(int *) arg = supported_devices; + break; + + case SOUND_MIXER_STEREODEVS: + if (Jazz16_detected) + return *(int *) arg = supported_devices; + else + return *(int *) arg = supported_devices & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER); + break; + + case SOUND_MIXER_RECMASK: + return *(int *) arg = supported_rec_devices; + break; + + case SOUND_MIXER_CAPS: + return *(int *) arg = mixer_caps; + break; + + default: + return *(int *) arg = sb_mixer_get(cmd & 0xff); + } + } else + return -(EINVAL); +} + +static struct mixer_operations sb_mixer_operations = +{ + "SoundBlaster", + sb_mixer_ioctl +}; + +static void +sb_mixer_reset(void) +{ + int i; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + sb_mixer_set(i, levels[i]); + set_recmask(SOUND_MASK_MIC); +} + +/* + * Returns a code depending on whether a SG NX Pro was detected. 1 == Plain + * SB Pro 2 == SG NX Pro detected. 3 == SB16 + * + * Used to update message. + */ +int +sb_mixer_init(int major_model) +{ + int mixer_type = 0; + + sb_setmixer(0x00, 0); /* Reset mixer */ + + if (!(mixer_type = detect_mixer())) + return 0; /* No mixer. Why? */ + + mixer_initialized = 1; + mixer_model = major_model; + + switch (major_model) { + case 3: + mixer_caps = SOUND_CAP_EXCL_INPUT; + +#ifdef JAZZ16 + if (Jazz16_detected == 2) { /* SM Wave */ + supported_devices = 0; + supported_rec_devices = 0; + iomap = &sbpro_mix; + smw_mixer_init(); + mixer_type = 1; + } else +#endif +#ifdef __SGNXPRO__ + if (mixer_type == 2) { /* A SGNXPRO was detected */ + supported_devices = SGNXPRO_MIXER_DEVICES; + supported_rec_devices = SGNXPRO_RECORDING_DEVICES; + iomap = &sgnxpro_mix; + } else +#endif + { + supported_devices = SBPRO_MIXER_DEVICES; + supported_rec_devices = SBPRO_RECORDING_DEVICES; + iomap = &sbpro_mix; + mixer_type = 1; + } + break; + + case 4: + mixer_caps = 0; + supported_devices = SB16_MIXER_DEVICES; + supported_rec_devices = SB16_RECORDING_DEVICES; + iomap = &sb16_mix; + mixer_type = 3; + break; + + default: + printf("SB Warning: Unsupported mixer type\n"); + return 0; + } + + if (num_mixers < MAX_MIXER_DEV) + mixer_devs[num_mixers++] = &sb_mixer_operations; + sb_mixer_reset(); + return mixer_type; +} + +#endif diff --git a/sys/i386/isa/sound/sb_mixer.h b/sys/i386/isa/sound/sb_mixer.h new file mode 100644 index 0000000..155d6c7 --- /dev/null +++ b/sys/i386/isa/sound/sb_mixer.h @@ -0,0 +1,255 @@ +/* + * sound/sb_mixer.h + * + * Definitions for the SB Pro and SB16 mixers + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + * Modified: Hunyue Yau Jan 6 1994 Added defines for the Sound Galaxy NX Pro + * mixer. + * + */ + +#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) + +/* Same as SB Pro, unless I find otherwise */ +#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES + +#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_VOLUME) + +/* + * SG NX Pro has treble and bass settings on the mixer. The 'speaker' channel + * is the COVOX/DisneySoundSource emulation volume control on the mixer. It + * does NOT control speaker volume. Should have own mask eventually? + */ +#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \ + SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER ) + +#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD) + +#define SB16_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) + +/* + * Mixer registers + * + * NOTE! RECORD_SRC == IN_FILTER + */ + +/* + * Mixer registers of SB Pro + */ +#define VOC_VOL 0x04 +#define MIC_VOL 0x0A +#define MIC_MIX 0x0A +#define RECORD_SRC 0x0C +#define IN_FILTER 0x0C +#define OUT_FILTER 0x0E +#define MASTER_VOL 0x22 +#define FM_VOL 0x26 +#define CD_VOL 0x28 +#define LINE_VOL 0x2E +#define IRQ_NR 0x80 +#define DMA_NR 0x81 +#define IRQ_STAT 0x82 +#define OPSW 0x3c + +/* + * Additional registers on the SG NX Pro + */ +#define COVOX_VOL 0x42 +#define TREBLE_LVL 0x44 +#define BASS_LVL 0x46 + +#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */ +#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */ +#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */ +#define FILT_OFF (1 << 5) + +#define MONO_DAC 0x00 +#define STEREO_DAC 0x02 + +/* + * Mixer registers of SB16 + */ +#define SB16_IMASK_L 0x3d +#define SB16_IMASK_R 0x3e + +#define LEFT_CHN 0 +#define RIGHT_CHN 1 + +struct mixer_def { + unsigned int regno:8; + unsigned int bitoffs:4; + unsigned int nbits:4; +}; + + +typedef struct mixer_def mixer_tab[32][2]; +typedef struct mixer_def mixer_ent; + +#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 = { + MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), + MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), + MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), + MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), + MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), + MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0) +}; + +#ifdef __SGNXPRO__ +mixer_tab sgnxpro_mix = { + 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), + MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), + MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), + MIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), + MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), + MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0) +}; +#endif + +mixer_tab sb16_mix = { + MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5), + MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4), + MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4), + MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5), + MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5), + MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5), + MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0), + MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5), + MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 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 higher than with SB Pro. This + * improves the sound quality */ + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = +{ + 0x2020, /* Master Volume */ + 0x4b4b, /* Bass */ + 0x4b4b, /* Treble */ + 0x6464, /* FM */ + 0x6464, /* PCM */ + 0x4b4b, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x0000, /* Mic */ + 0x4b4b, /* CD */ + 0x4b4b, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x4b4b, /* Recording level */ + 0x4b4b, /* Input gain */ +0x4b4b}; /* Output gain */ + +#else /* If the user selected just plain SB Pro */ + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = +{ + 0x5a5a, /* Master Volume */ + 0x4b4b, /* Bass */ + 0x4b4b, /* Treble */ + 0x4b4b, /* FM */ + 0x4b4b, /* PCM */ + 0x4b4b, /* PC Speaker */ + 0x4b4b, /* Ext Line */ + 0x1010, /* Mic */ + 0x4b4b, /* CD */ + 0x4b4b, /* Recording monitor */ + 0x4b4b, /* SB PCM */ + 0x4b4b, /* Recording level */ + 0x4b4b, /* Input gain */ +0x4b4b}; /* Output gain */ +#endif /* SM_GAMES */ + +static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = +{ + 0x00, /* SOUND_MIXER_VOLUME */ + 0x00, /* SOUND_MIXER_BASS */ + 0x00, /* SOUND_MIXER_TREBLE */ + 0x40, /* SOUND_MIXER_SYNTH */ + 0x00, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x10, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x04, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00, /* SOUND_MIXER_RECLEV */ + 0x00, /* SOUND_MIXER_IGAIN */ + 0x00 /* SOUND_MIXER_OGAIN */ +}; + +static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] = +{ + 0x00, /* SOUND_MIXER_VOLUME */ + 0x00, /* SOUND_MIXER_BASS */ + 0x00, /* SOUND_MIXER_TREBLE */ + 0x20, /* SOUND_MIXER_SYNTH */ + 0x00, /* SOUND_MIXER_PCM */ + 0x00, /* SOUND_MIXER_SPEAKER */ + 0x08, /* SOUND_MIXER_LINE */ + 0x01, /* SOUND_MIXER_MIC */ + 0x02, /* SOUND_MIXER_CD */ + 0x00, /* SOUND_MIXER_IMIX */ + 0x00, /* SOUND_MIXER_ALTPCM */ + 0x00, /* SOUND_MIXER_RECLEV */ + 0x00, /* SOUND_MIXER_IGAIN */ + 0x00 /* SOUND_MIXER_OGAIN */ +}; + +/* + * Recording sources (SB Pro) + */ + +#define SRC_MIC 1 /* Select Microphone recording source */ +#define SRC_CD 3 /* Select CD recording source */ +#define SRC_LINE 7 /* Use Line-in for recording source */ + +#endif diff --git a/sys/i386/isa/sound/sbcard.h b/sys/i386/isa/sound/sbcard.h new file mode 100644 index 0000000..464128c --- /dev/null +++ b/sys/i386/isa/sound/sbcard.h @@ -0,0 +1,54 @@ +/* + * file: sbcard.h + */ + +extern int sbc_major, sbc_minor ; +/* + * sound blaster registers + */ + +#define DSP_RESET (sbc_base + 0x6) +#define DSP_READ (sbc_base + 0xA) +#define DSP_WRITE (sbc_base + 0xC) +#define DSP_COMMAND (sbc_base + 0xC) +#define DSP_STATUS (sbc_base + 0xC) +#define DSP_DATA_AVAIL (sbc_base + 0xE) +#define DSP_DATA_AVL16 (sbc_base + 0xF) +#define MIXER_ADDR (sbc_base + 0x4) +#define MIXER_DATA (sbc_base + 0x5) +#define OPL3_LEFT (sbc_base + 0x0) +#define OPL3_RIGHT (sbc_base + 0x2) +#define OPL3_BOTH (sbc_base + 0x8) + +/* + * DSP Commands. There are many, and in many cases they are used explicitly + */ + +#define DSP_CMD_SPKON 0xD1 +#define DSP_CMD_SPKOFF 0xD3 +#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */ +#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */ + +#define DSP_CMD_DMAHALT 0xD0 +#define DSP_CMD_TCONST 0x40 /* set time constant */ +#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */ +#define DSP_CMD_HSDAC 0x91 /* high speed dac */ +#define DSP_CMD_HSADC 0x99 /* high speed adc */ +#define DSP_CMD_DAC8 0x14 /* 8-bit dac (dma count) */ +#define DSP_CMD_ADC8 0x24 /* 8-bit adc (dma count) */ + +#define DSP_CMD_GETVER 0xE1 +#define DSP_CMD_GETID 0xE7 /* return id bytes */ + +#if 0 /*** unknown ***/ + +#endif + +#define IMODE_NONE 0 +#define IMODE_OUTPUT PCM_ENABLE_OUTPUT +#define IMODE_INPUT PCM_ENABLE_INPUT +#define IMODE_INIT 3 +#define IMODE_MIDI 4 + +#define NORMAL_MIDI 0 +#define UART_MIDI 1 diff --git a/sys/i386/isa/sound/sequencer.c b/sys/i386/isa/sound/sequencer.c new file mode 100644 index 0000000..4990774 --- /dev/null +++ b/sys/i386/isa/sound/sequencer.c @@ -0,0 +1,1822 @@ +/* + * sound/sequencer.c + * + * The sequencer personality manager. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +#define SEQUENCER_C +#include <i386/isa/sound/sound_config.h> + +#if NSND > 0 + +#ifdef CONFIG_SEQUENCER + +#include <i386/isa/sound/midi_ctrl.h> + +static void seq_drain_midi_queues(void); +int +sequencer_poll (int dev, struct fileinfo *file, int events, select_table * wait); +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 */ + +/* + * Local counts for number of synth and MIDI devices. These are initialized + * by the sequencer_open. + */ +static int max_mididev = 0; +static int max_synthdev = 0; + +/* + * The seq_mode gives the operating mode of the sequencer: 1 = level1 (the + * default) 2 = level2 (extended capabilites) + */ + +#define SEQ_1 1 +#define SEQ_2 2 +static int seq_mode = SEQ_1; + +static int *seq_sleeper = NULL; +static volatile struct snd_wait seq_sleep_flag = {0}; +static int *midi_sleeper = NULL; +static volatile struct snd_wait midi_sleep_flag = {0}; + +static int midi_opened[MAX_MIDI_DEV] = {0}; +static int midi_written[MAX_MIDI_DEV] = {0}; + +static u_long prev_input_time = 0; +static int prev_event_time; +static u_long seq_time = 0; + +#include <i386/isa/sound/tuning.h> + +#define EV_SZ 8 +#define IEV_SZ 8 +static u_char *queue = NULL; +static u_char *iqueue = NULL; + +static volatile int qhead = 0, qtail = 0, qlen = 0; +static volatile int iqhead = 0, iqtail = 0, iqlen = 0; +static volatile int seq_playing = 0; +static volatile int sequencer_busy = 0; +static int output_treshold; +static int pre_event_timeout; +static u_int synth_open_mask; + +static int seq_queue(u_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 struct callout_handle sequencertimeout_ch + = CALLOUT_HANDLE_INITIALIZER(&sequencertimeout_ch); + +#if MAX_SYNTH_DEV > 15 +#error Too many synthesizer devices enabled. +#endif + +/* + * sound_timer stuff -- originally in soundcard.c + * + * A negative value means a relative timeout in |count| ticks. + * A positive value is used for what ? + * + * In any case, this is only used in sequencer.c + */ + +static int timer_running = 0; + +void +request_sound_timer(int count) +{ + static int current = 0; + int tmp = count; + + if (count < 0) + sequencertimeout_ch = timeout(sequencer_timer, 0, -count); + else { + + if (count < current) + current = 0; /* Timer restarted */ + + count = count - current; + current = tmp; + if (!count) + count = 1; + sequencertimeout_ch = timeout(sequencer_timer, 0, count); + } + timer_running = 1; +} + +void +sound_stop_timer(void) +{ + if (timer_running) + untimeout( sequencer_timer, 0, sequencertimeout_ch); + timer_running = 0; +} + +int +sequencer_read(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) +{ + int c = count, p = 0; + int ev_len; + u_long flags; + + dev = dev >> 4; + + ev_len = seq_mode == SEQ_1 ? 4 : 8; + + if (dev) /* Patch manager device */ + return pmgr_read(dev - 1, file, buf, count); + + flags = splhigh(); + if (!iqlen) { + int chn; + + + midi_sleeper = &chn; + DO_SLEEP(chn, midi_sleep_flag, pre_event_timeout); + + if (!iqlen) { + splx(flags); + return 0; + } + } + while (iqlen && c >= ev_len) { + + if (uiomove((char *) &iqueue[iqhead * IEV_SZ], ev_len, buf)) + printf("sb: Bad copyout()!\n"); + p += ev_len; + c -= ev_len; + + iqhead = (iqhead + 1) % SEQ_MAX_QUEUE; + iqlen--; + } + splx(flags); + + return count - c; +} + +static void +sequencer_midi_output(int dev) +{ + /* + * Currently NOP + */ +} + +void +seq_copy_to_input(u_char *event, int len) +{ + u_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 */ + + flags = splhigh(); + bcopy(event, &iqueue[iqtail * IEV_SZ], len); + iqlen++; + iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; + + if ((midi_sleep_flag.mode & WK_SLEEP)) { + midi_sleep_flag.mode = WK_WAKEUP; + wakeup(midi_sleeper); + } + splx(flags); +} + +static void +sequencer_midi_input(int dev, u_char data) +{ + u_int tstamp; + u_char event[4]; + + if (data == 0xfe) /* Ignore active sensing */ + return; + + tstamp = get_time() - seq_time; + if (tstamp != prev_input_time) { + tstamp = (tstamp << 8) | SEQ_WAIT; + + seq_copy_to_input((u_char *) &tstamp, 4); + prev_input_time = tstamp; + } + event[0] = SEQ_MIDIPUTC; + event[1] = data; + event[2] = dev; + event[3] = 0; + + seq_copy_to_input(event, 4); +} + +void +seq_input_event(u_char *event, int len) +{ + u_long this_time; + + if (seq_mode == SEQ_2) + this_time = tmr->get_time(tmr_no); + else + this_time = get_time() - seq_time; + + if (this_time != prev_input_time) { + u_char tmp_event[8]; + + tmp_event[0] = EV_TIMING; + tmp_event[1] = TMR_WAIT_ABS; + tmp_event[2] = 0; + tmp_event[3] = 0; + *(u_long *) &tmp_event[4] = this_time; + + seq_copy_to_input(tmp_event, 8); + prev_input_time = this_time; + } + seq_copy_to_input(event, len); +} + +int +sequencer_write(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) +{ + u_char event[EV_SZ], ev_code; + int p = 0, c, ev_size; + int err; + int mode = file->mode & O_ACCMODE; + + dev = dev >> 4; + + DEB(printf("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; + + while (c >= 4) { + + if (uiomove((char *) event, 4, buf)) + printf("sb: Bad copyin()!\n"); + ev_code = event[0]; + + if (ev_code == SEQ_FULLSIZE) { + int err; + + dev = *(u_short *) &event[2]; + if (dev < 0 || dev >= max_synthdev) + return -(ENXIO); + + if (!(synth_open_mask & (1 << dev))) + return -(ENXIO); + + err = synth_devs[dev]->load_patch(dev, + *(short *) &event[0], buf, p + 4, c, 0); + if (err < 0) + return err; + + return err; + } + if (ev_code >= 128) { + if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) { + printf("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; + } + if (uiomove((char *) &event[4], 4, buf)) + printf("sb: Bad copyin()!\n"); + } else { + if (seq_mode == SEQ_2) { + printf("Sequencer: 4 byte event in level 2 mode\n"); + return -(EINVAL); + } + ev_size = 4; + } + + if (event[0] == SEQ_MIDIPUTC) { + + if (!midi_opened[event[2]]) { + int mode; + int dev = event[2]; + + if (dev >= max_mididev) { + printf("Sequencer Error: Nonexistent MIDI device %d\n",dev); + return -(ENXIO); + } + mode = file->mode & O_ACCMODE; + + if ((err = midi_devs[dev]->open(dev, mode, + sequencer_midi_input, sequencer_midi_output)) < 0) { + seq_reset(); + printf("Sequencer Error: Unable to open Midi #%d\n", dev); + return err; + } + midi_opened[dev] = 1; + } + } + if (!seq_queue(event, 0)) { + int processed = count - c; + + if (!seq_playing) + seq_startplay(); + + if (!processed && 0) + return -(EAGAIN); + else + return processed; + } + p += ev_size; + c -= ev_size; + } + + if (!seq_playing) + seq_startplay(); + + return count; /* This will "eat" chunks shorter than 4 bytes + * (if written alone) Should we really do that ? + */ +} + +static int +seq_queue(u_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.mode & WK_SLEEP)) { + /* + * Sleep until there is enough space on the queue + */ + + int chn; + + + seq_sleeper = &chn; + DO_SLEEP(chn, seq_sleep_flag, 0); + + } + if (qlen >= SEQ_MAX_QUEUE) + return 0; /* To be sure */ + bcopy(note, &queue[qtail * EV_SZ], EV_SZ); + + qtail = (qtail + 1) % SEQ_MAX_QUEUE; + qlen++; + + return 1; +} + +static int +extended_event(u_char *q) +{ + int dev = q[2]; + + if (dev < 0 || dev >= max_synthdev) + return -(ENXIO); + + if (!(synth_open_mask & (1 << dev))) + return -(ENXIO); + + 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; + + 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]); + 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) +{ + u_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) +{ + u_short key; + int voice; + + 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; +} + +static void +seq_chn_voice_event(u_char *event) +{ + u_char dev = event[1]; + u_char cmd = event[2]; + u_char chn = event[3]; + u_char note = event[4]; + u_char parm = event[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; + } + } + switch (cmd) { + 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); + note = 60; /* Middle C */ + + } + } + if (seq_mode == SEQ_2) + 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:; + } +} + +static void +seq_chn_common_event(u_char *event) +{ + u_char dev = event[1]; + u_char cmd = event[2]; + u_char chn = event[3]; + u_char p1 = event[4]; + + /* u_char p2 = event[5]; */ + u_short w14 = *(short *) &event[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) { + 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:; + } +} + +static int +seq_timing_event(u_char *event) +{ + u_char cmd = event[1]; + u_int parm = *(int *) &event[4]; + + if (seq_mode == SEQ_2) { + int ret; + + if ((ret = tmr->event(tmr_no, event)) == TIMER_ARMED) { + if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) { + u_long flags; + + flags = splhigh(); + if ((seq_sleep_flag.mode & WK_SLEEP)) { + seq_sleep_flag.mode = WK_WAKEUP; + wakeup(seq_sleeper); + } + splx(flags); + } + } + return ret; + } + switch (cmd) { + 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; + + seq_playing = 1; + time = parm; + prev_event_time = time; + + request_sound_timer(time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) { + u_long flags; + + flags = splhigh(); + if ((seq_sleep_flag.mode & WK_SLEEP)) { + seq_sleep_flag.mode = WK_WAKEUP; + wakeup(seq_sleeper); + } + splx(flags); + } + return TIMER_ARMED; + } + break; + + case TMR_START: + seq_time = get_time(); + 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, 8); + else { + parm = (parm << 8 | SEQ_ECHO); + seq_copy_to_input((u_char *) &parm, 4); + } + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static void +seq_local_event(u_char *event) +{ + u_char cmd = event[1]; + u_int parm = *((u_int *) &event[4]); + + switch (cmd) { + case LOCL_STARTAUDIO: +#ifdef CONFIG_AUDIO + DMAbuf_start_devices(parm); +#endif + break; + + default:; + } +} + +static void +seq_sysex_message(u_char *event) +{ + int dev = event[1]; + int i, l = 0; + u_char *buf = &event[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); +} + +static int +play_event(u_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 + */ + u_long *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 = (u_long *) q; /* Bytes 1 to 3 are + * containing the * delay in + * get_time() */ + *delay = (*delay >> 8) & 0xffffff; + + if (*delay > 0) { + long time; + + seq_playing = 1; + time = *delay; + prev_event_time = time; + + request_sound_timer(time); + + if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) { + u_long flags; + + flags = splhigh(); + if ((seq_sleep_flag.mode & WK_SLEEP)) { + seq_sleep_flag.mode = WK_WAKEUP; + wakeup(seq_sleeper); + } + splx(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 = get_time(); + prev_input_time = 0; + prev_event_time = 0; + break; + + case SEQ_MIDIPUTC: /* Put a midi character */ + if (midi_opened[q[2]]) { + int dev; + + dev = q[2]; + + 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; + } + 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:; + } + + return 0; +} + +static void +seq_startplay(void) +{ + u_long flags; + int this_one, action; + + while (qlen > 0) { + + flags = splhigh(); + qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; + qlen--; + splx(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; + } + } + + seq_playing = 0; + + if ((SEQ_MAX_QUEUE - qlen) >= output_treshold) { + u_long flags; + + flags = splhigh(); + if ((seq_sleep_flag.mode & WK_SLEEP)) { + seq_sleep_flag.mode = WK_WAKEUP; + wakeup(seq_sleeper); + } + splx(flags); + } +} + +static void +reset_controllers(int dev, u_char *controller, int update_dev) +{ + + int i; + + for (i = 0; i < 128; i++) + controller[i] = ctrl_def_values[i]; +} + +static void +setup_mode2(void) +{ + int dev; + + max_synthdev = num_synths; + + for (dev = 0; dev < num_midis; dev++) + if (midi_devs[dev]->converter != NULL) + synth_devs[max_synthdev++] = midi_devs[dev]->converter; + for (dev = 0; dev < max_synthdev; dev++) { + int chn; + + 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 */ + } + } + + max_mididev = 0; + seq_mode = SEQ_2; +} + +int +sequencer_open(int dev, struct fileinfo * file) +{ + int retval, mode, i; + int level, tmp; + u_long flags; + + level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1; + + dev = dev >> 4; + mode = file->mode & O_ACCMODE; + + DEB(printf("sequencer_open(dev=%d)\n", dev)); + + if (!sequencer_ok) { + printf("Soundcard: Sequencer not initialized\n"); + return -(ENXIO); + } + if (dev) { /* Patch manager device */ + int err; + + printf("Patch manager interface is currently broken. Sorry\n"); + return -(ENXIO); + + dev--; + + if (dev >= MAX_SYNTH_DEV) + return -(ENXIO); + if (pmgr_present[dev]) + return -(EBUSY); + if ((err = pmgr_open(dev)) < 0) + return err; + + pmgr_present[dev] = 1; + return err; + } + flags = splhigh(); + if (sequencer_busy) { + printf("Sequencer busy\n"); + splx(flags); + return -(EBUSY); + } + sequencer_busy = 1; + splx(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) { + printf("sequencer: No timer for level 2\n"); + sequencer_busy = 0; + return -(ENXIO); + } + setup_mode2(); + } + if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) + if (!max_mididev) { + printf("Sequencer: No Midi devices. Input not possible\n"); + sequencer_busy = 0; + return -(ENXIO); + } + if (!max_synthdev && !max_mididev) + 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) { + printf("Sequencer: Warning! Cannot open synth device #%d (%d)\n", + i, tmp); + if (synth_devs[i]->midi_dev) + printf("(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; + } + + seq_time = get_time(); + prev_input_time = 0; + prev_event_time = 0; + + 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.aborting = 0; + seq_sleep_flag.mode = WK_NONE; + midi_sleep_flag.aborting = 0; + midi_sleep_flag.mode = WK_NONE; + output_treshold = 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; +} + +static void +seq_drain_midi_queues(void) +{ + int i, n; + + /* + * Give the Midi drivers time to drain their output queues + */ + + n = 1; + + while (!(seq_sleep_flag.aborting) && n) { + 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) { + int chn; + + seq_sleeper = &chn; + DO_SLEEP(chn, seq_sleep_flag, hz / 10); + + } + } +} + +void +sequencer_release(int dev, struct fileinfo * file) +{ + int i; + int mode = file->mode & O_ACCMODE; + + dev = dev >> 4; + + DEB(printf("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 && !0) + while (!(seq_sleep_flag.aborting) && 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 */ + + 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 < num_synths; i++) + if (pmgr_present[i]) + pmgr_inform(i, PM_E_CLOSED, 0, 0, 0, 0); + + 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); + + sequencer_busy = 0; +} + +static int +seq_sync(void) +{ + u_long flags; + + if (qlen && !seq_playing && !(seq_sleep_flag.aborting)) + seq_startplay(); + + flags = splhigh(); + if (qlen && !(seq_sleep_flag.mode & WK_SLEEP)) { + int chn; + + seq_sleeper = &chn; + DO_SLEEP(chn, seq_sleep_flag, 0); + + } + splx(flags); + + return qlen; +} + +static void +midi_outc(int dev, u_char data) +{ + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int n; + u_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 */ + + flags = splhigh(); + while (n && !midi_devs[dev]->putc(dev, data)) { + int chn; + seq_sleeper = &chn; + DO_SLEEP(chn, seq_sleep_flag, 4); + + n--; + } + splx(flags); +} + +static void +seq_reset(void) +{ + /* + * NOTE! Calls sleep(). Don't call this from interrupt. + */ + + int i; + int chn; + u_long flags; + + sound_stop_timer(); + seq_time = get_time(); + prev_input_time = 0; + prev_event_time = 0; + + qlen = qhead = qtail = 0; + iqlen = iqhead = iqtail = 0; + + for (i = 0; i < max_synthdev; i++) + if (synth_open_mask & (1 << i)) + if (synth_devs[i]) + synth_devs[i]->reset(i); + + if (seq_mode == SEQ_2) { + for (chn = 0; chn < 16; chn++) + for (i = 0; i < max_synthdev; i++) + if ( (synth_open_mask & (1 << i)) && (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, (u_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; + + flags = splhigh(); + if ((seq_sleep_flag.mode & WK_SLEEP)) { + seq_sleep_flag.mode = WK_WAKEUP; + wakeup(seq_sleeper); + } + splx(flags); + +} + +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, + u_int cmd, ioctl_arg 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 = (*(int *) arg); + + if (pending_timer < 0 || pending_timer >= num_sound_timers) { + pending_timer = -1; + return -(EINVAL); + } + 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 && !(seq_sleep_flag.aborting)) + 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 = (*(int *) arg); + if (midi_dev >= max_mididev) + return -(ENXIO); + + if (!midi_opened[midi_dev]) { + 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 */ + return -(EIO); + + if (mode == OPEN_WRITE) + return 0; + return *(int *) arg = iqlen; + break; + + case SNDCTL_SEQ_GETOUTCOUNT: + + if (mode == OPEN_READ) + return 0; + 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 ((*(int *) arg) != 0) + return -(EINVAL); + + return *(int *) arg = hz; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + { + int err; + + dev = (*(int *) arg); + if (dev < 0 || dev >= num_synths) + return -(ENXIO); + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -(EBUSY); + 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 *(int *) arg = max_synthdev; + break; + + case SNDCTL_SEQ_NRMIDIS: + return *(int *) arg = max_mididev; + break; + + case SNDCTL_SYNTH_MEMAVL: + { + int dev = (*(int *) arg); + + if (dev < 0 || dev >= num_synths) + return -(ENXIO); + + if (!(synth_open_mask & (1 << dev)) && !orig_dev) + return -(EBUSY); + + return *(int *) arg = synth_devs[dev]->ioctl(dev, cmd, arg); + } + break; + + case SNDCTL_FM_4OP_ENABLE: + { + int dev = (*(int *) arg); + + if (dev < 0 || dev >= num_synths) + return -(ENXIO); + + if (!(synth_open_mask & (1 << dev))) + return -(ENXIO); + + synth_devs[dev]->ioctl(dev, cmd, arg); + return 0; + } + break; + + case SNDCTL_SYNTH_INFO: + { + struct synth_info inf; + int dev; + + bcopy(&(((char *) arg)[0]), (char *) &inf, 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; + u_long flags; + + bcopy(&(((char *) arg)[0]), (char *) &event, sizeof(event)); + + flags = splhigh(); + play_event(event.arr); + splx(flags); + + return 0; + } + break; + + case SNDCTL_MIDI_INFO: + { + struct midi_info inf; + int dev; + + bcopy(&(((char *) arg)[0]), (char *) &inf, sizeof(inf)); + dev = inf.device; + + if (dev < 0 || dev >= max_mididev) + return -(ENXIO); + + bcopy((char *) &(midi_devs[dev]->info), &(((char *) arg)[0]), sizeof(inf)); + return 0; + } + break; + + case SNDCTL_PMGR_IFACE: + { + struct patmgr_info *inf; + int dev, err; + + if ((inf = (struct patmgr_info *) malloc(sizeof(*inf), M_TEMP, M_WAITOK)) == NULL) { + printf("patmgr: Can't allocate memory for a message\n"); + return -(EIO); + } + bcopy(&(((char *) arg)[0]), (char *) inf, sizeof(*inf)); + dev = inf->device; + + if (dev < 0 || dev >= num_synths) { + free(inf, M_TEMP); + return -(ENXIO); + } + if (!synth_devs[dev]->pmgr_interface) { + free(inf, M_TEMP); + return -(ENXIO); + } + if ((err = synth_devs[dev]->pmgr_interface(dev, inf)) == -1) { + free(inf, M_TEMP); + return err; + } + bcopy((char *) inf, &(((char *) arg)[0]), sizeof(*inf)); + free(inf, M_TEMP); + return 0; + } + break; + + case SNDCTL_PMGR_ACCESS: + { + struct patmgr_info *inf; + int dev, err; + + if ((inf = (struct patmgr_info *) malloc(sizeof(*inf), M_TEMP, M_WAITOK)) == NULL) { + printf("patmgr: Can't allocate memory for a message\n"); + return -(EIO); + } + bcopy(&(((char *) arg)[0]), (char *) inf, sizeof(*inf)); + dev = inf->device; + + if (dev < 0 || dev >= num_synths) { + free(inf, M_TEMP); + return -(ENXIO); + } + if (!pmgr_present[dev]) { + free(inf, M_TEMP); + return -(ESRCH); + } + if ((err = pmgr_access(dev, inf)) < 0) { + free(inf, M_TEMP); + return err; + } + bcopy((char *) inf, &(((char *) arg)[0]), sizeof(*inf)); + free(inf, M_TEMP); + return 0; + } + break; + + case SNDCTL_SEQ_THRESHOLD: + { + int tmp = (*(int *) arg); + + if (dev)/* Patch manager */ + return -(EIO); + + if (tmp < 1) + tmp = 1; + if (tmp >= SEQ_MAX_QUEUE) + tmp = SEQ_MAX_QUEUE - 1; + output_treshold = tmp; + return 0; + } + break; + + case SNDCTL_MIDI_PRETIME: + { + int val = (*(int *) arg); + + if (val < 0) + val = 0; + + val = (hz * val) / 10; + pre_event_timeout = val; + 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); +} + +#ifdef ALLOW_POLL +int +sequencer_poll (int dev, struct fileinfo *file, int events, select_table * wait) +{ + unsigned long flags; + int revents = 0; + + dev = dev >> 4; + flags = splhigh(); + + + if (events & (POLLIN | POLLRDNORM)) + if (!iqlen) + selrecord(wait, &selinfo[dev]); + else { + revents |= events & (POLLIN | POLLRDNORM); + midi_sleep_flag.mode &= ~WK_SLEEP; + } + + if (events & (POLLOUT | POLLWRNORM)) + if (qlen >= SEQ_MAX_QUEUE) + selrecord(wait, &selinfo[dev]); + else { + revents |= events & (POLLOUT | POLLWRNORM); + seq_sleep_flag.mode &= ~WK_SLEEP; + } + + splx(flags); + + return (revents); +} + +#endif + + +void +sequencer_timer(void *dummy) +{ + seq_startplay(); +} + +int +note_to_freq(int note_num) +{ + + /* + * This routine converts a midi note to a frequency (multiplied by + * 1000) + */ + + int note, octave, note_freq; + int notes[] = + { + 261632, 277189, 293671, 311132, 329632, 349232, + 369998, 391998, 415306, 440000, 466162, 493880 + }; + +#define BASE_OCTAVE 5 + + octave = note_num / 12; + note = note_num % 12; + + note_freq = notes[note]; + + if (octave < BASE_OCTAVE) + note_freq >>= (BASE_OCTAVE - octave); + else if (octave > BASE_OCTAVE) + note_freq <<= (octave - BASE_OCTAVE); + + /* + * note_freq >>= 1; + */ + + return note_freq; +} + +u_long +compute_finetune(u_long base_freq, int bend, int range) +{ + u_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 */ +} + + +void +sequencer_init() +{ + + sequencer_ok = 1; + + queue = (u_char *) malloc(SEQ_MAX_QUEUE * EV_SZ, M_DEVBUF, M_NOWAIT); + if (!queue) + panic("SOUND: Cannot allocate memory\n"); + + iqueue = (u_char *) malloc(SEQ_MAX_QUEUE * IEV_SZ, M_DEVBUF, M_NOWAIT); + if (!iqueue) + panic("SOUND: Cannot allocate memory\n"); +} + +#endif +#endif diff --git a/sys/i386/isa/sound/sound.doc b/sys/i386/isa/sound/sound.doc new file mode 100644 index 0000000..2340d60 --- /dev/null +++ b/sys/i386/isa/sound/sound.doc @@ -0,0 +1,120 @@ +$Id: sound.doc,v 1.9 1998/10/22 15:37:35 bde Exp $ + +Instructions on using audio on a FreeBSD 2.1 (or 2.0-current) system. +See also /sys/i386/conf/LINT. + +To enable sound driver support, the controller sound code must be included +in your config file: + +# SB = SoundBlaster; PAS = ProAudioSpectrum; GUS = Gravis UltraSound +# Controls all sound devices +controller snd0 + +Uncomment one or more of these device entries, depending on what type of +sound card you have: + +# ProAudioSpectrum PCM and Midi - for PAS +#device pas0 at isa? port 0x388 irq 10 drq 6 + +# SoundBlaster DSP driver - for SB, SB Pro, SB16, PAS(emulating SB) +#device sb0 at isa? port 0x220 irq 7 drq 1 + +# SoundBlaster 16 DSP driver - for SB16 - requires sb0 device +#device sbxvi0 at isa? drq 5 + +# SoundBlaster 16 MIDI - for SB16 - requires sb0 device +#device sbmidi0 at isa? port 0x300 + +# Gravis UltraSound - for GUS, GUS16, GUSMAX +# For cards that use 2 DMA Channels: +# drq = Write DMA Channel, flags = Read DMA Channel +#device gus0 at isa? port 0x220 irq 11 drq 1 flags 0x3 + +# Gravis UltraSound 16 bit option - for GUS16 - requires gus0 +#device gusxvi0 at isa? port 0x530 irq 7 drq 3 + +# MS Sound System (AD1848 Based Boards) +#device mss0 at isa? port 0x530 irq 10 drq 1 + +# Yamaha OPL-2/OPL-3 FM - for SB, SB Pro, SB16, PAS +#device opl0 at isa? port 0x388 + +# MPU-401 - for MPU-401 standalone card +#device mpu0 at isa? port 0x330 irq 6 drq 0 + +# 6850 UART Midi +#device uart0 at isa? port 0x330 irq 5 + +You may add one or more of the following depending on what you do and don't +want compiled into your kernel. Note: Excluding things with EXCLUDE_... +is NOT recommended unless you really know what you're doing. + +#options EXCLUDE_AUDIO # NO digital audio support +#options EXCLUDE_SEQUENCER # NO sequencer support +#options EXCLUDE_MIDI # NO MIDI support whatsoever +#options EXCLUDE_SBPRO # EXCLUDE SB Pro support +#options EXCLUDE_SB_EMULATION # NO PAS SB emulation support +#options EXCLUDE_GUS_IODETECT # NO GUS io detection +#options EXCLUDE_PRO_MIDI # NO PAS MIDI support + +Other Options: + +#options SYMPHONY_PAS + Adds some code to make pas work with Symphony chipsets. Only use + this if your pas doesn't work and you have a Symphony chipset. + +#options BROKEN_BUS_CLOCK + Some systems with the OPTI chipset and a PAS will require you to + use this option. Symptoms are that you will hear a lot of clicking and + popping sounds, like a geiger counter, coming out of the PAS even when + it is not playing anything. + +#options MOZART_PORT + Adds support for Mozart (OAK OTI-601). (Part of the MSS driver) + +#options OPTI_MAD16_PORT + Adds support for the OPTI MAD16 Chip. (Part of the MSS driver) + If your soundcard has a chip labeled "OPTi 82C929" then try this. + +#options __SGNXPRO__ + Adds support for the SG NX Pro mixer. (Part of the SB driver) + +#options JAZZ16 + Adds support for the MV Jazz16 (ProSonic etc). (Part of the SB Driver) + +#options SM_WAVE + Adds support for the SoundMan Wave (Part of the SB Driver) + Note: You will need to do some work to get this to work. + See i386/isa/sound/configure.c + +#options SM_GAMES + Adds support for the Logitech SoundMan Games (Part of the SB Driver) + +#options PAS_JOYSTICK_ENABLE + Enables the gameport on the ProAudio Spectrum + +NOTE: The MPU-401 driver may or may not work, and is unfortunately +unverifiable since no one I know has one. If you can test this, +please let me know! Also note that you will have to change these +settings if your soundcard is set for a non-standard address or IRQ. +Please check your documentation (or verify with any provided DOS utilities +that may have come with your card) and set the IRQ or address fields +accordingly. + + +Also: You can configure more then one card on a single DMA using +the conflicts keyword in your configuration file. This is useful for boards +with more then one type of emulation. + + +Probing problems: Since the SB16 uses the same IRQ and addresses for +the different drivers, some of the snd drivers will not be probed because +the kernel thinks there is a conflict. This can be worked-around by +using the "conflicts" keyword on the sb16's device line. + + +For further information, contact multimedia@freebsd.org + + - Jordan Hubbard (jkh@freefall.cdrom.com) + - Steven Wallace (swallace@freefall.cdrom.com) + - Sujal Patel (smpatel@wam.umd.edu) diff --git a/sys/i386/isa/sound/sound_calls.h b/sys/i386/isa/sound/sound_calls.h new file mode 100644 index 0000000..cb00e30 --- /dev/null +++ b/sys/i386/isa/sound/sound_calls.h @@ -0,0 +1,288 @@ +/* + * DMA buffer calls + */ + +int DMAbuf_open(int dev, int mode); +int DMAbuf_release(int dev, int mode); +int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock); +int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock); +int DMAbuf_rmchars(int dev, int buff_no, int c); +int DMAbuf_start_output(int dev, int buff_no, int l); +int DMAbuf_ioctl(int dev, u_int cmd, ioctl_arg arg, int local); +void DMAbuf_init(void); +int DMAbuf_start_dma (int dev, u_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); +void DMAbuf_start_devices(u_int devmask); +#ifdef ALLOW_POLL +int DMAbuf_select(int dev, struct fileinfo *file, int sel_type, select_table * wait); +#endif + +/* + * System calls for /dev/dsp and /dev/audio + */ + +int audio_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int audio_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int audio_open (int dev, struct fileinfo *file); +void audio_release (int dev, struct fileinfo *file); +int audio_ioctl (int dev, struct fileinfo *file, + u_int cmd, ioctl_arg arg); +int audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +/* long audio_init (void); */ + +#ifdef ALLOW_SELECT +int audio_poll(int dev, struct fileinfo *file, int events, select_table * wait); +#endif + +/* + * System calls for the /dev/sequencer + */ + +int sequencer_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int sequencer_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int sequencer_open (int dev, struct fileinfo *file); +void sequencer_release (int dev, struct fileinfo *file); +int sequencer_ioctl (int dev, struct fileinfo *file, + u_int cmd, ioctl_arg arg); +int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +void sequencer_init (void); +void sequencer_timer(void *dummy); +int note_to_freq(int note_num); +u_long compute_finetune(u_long base_freq, int bend, int range); +void seq_input_event(u_char *event, int len); +void seq_copy_to_input (u_char *event, int len); + + +/* + * System calls for the /dev/midi + */ + +int MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int MIDIbuf_open (int dev, struct fileinfo *file); +void MIDIbuf_release (int dev, struct fileinfo *file); +int MIDIbuf_ioctl (int dev, struct fileinfo *file, + u_int cmd, ioctl_arg arg); +int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig); +void MIDIbuf_bytes_received(int dev, u_char *buf, int count); + +/* + * + * Misc calls from various sources + */ + +/* From soundcard.c */ +void soundcard_init(void); +void tenmicrosec(int); +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 int_lvl, void(*hndlr)(int), sound_os_info *osp); +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, snd_rw_buf *buf, int count); +int sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf *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, u_int cmd, ioctl_arg arg); + +/* From sb_dsp.c */ +int sb_dsp_detect (struct address_info *hw_config); +void sb_dsp_init (struct address_info *hw_config); +void sb_dsp_disable_midi(void); +int sb_dsp_command (u_char val); +ointhand2_t sbintr; +int sb_reset_dsp (void); + +/* From sb16_dsp.c */ +void sb16_dsp_interrupt (int irq); +void sb16_dsp_init(struct address_info *hw_config); +int sb16_dsp_detect(struct address_info *hw_config); + +/* From sb16_midi.c */ +void sb16midiintr (int unit); +void attach_sb16midi(struct address_info * hw_config); +int probe_sb16midi(struct address_info *hw_config); +void sb_midi_interrupt(int dummy); + +/* From sb_midi.c */ +void sb_midi_init(int model); + +/* From sb_mixer.c */ +void sb_setmixer (u_int port, u_int value); +int sb_getmixer (u_int port); +void sb_mixer_set_stereo(int mode); +int sb_mixer_init(int major_model); + +/* From opl3.c */ +int opl3_detect (int ioaddr, sound_os_info *osp); +void opl3_init(int ioaddr, sound_os_info *osp); + +/* From sb_card.c */ +void attach_sb_card(struct address_info *hw_config); +int probe_sb(struct address_info *hw_config); + +/* From awe_wave.c */ +void attach_awe(struct address_info *hw_config); +int probe_awe(struct address_info *hw_config); + +/* From adlib_card.c */ +void attach_adlib_card(struct address_info *hw_config); +int probe_adlib(struct address_info *hw_config); + +/* From pas_card.c */ +void attach_pas_card(struct address_info *hw_config); +int probe_pas(struct address_info *hw_config); +ointhand2_t pasintr; +int pas_set_intr(int mask); +int pas_remove_intr(int mask); +u_char pas_read(int ioaddr); +void pas_write(u_char data, int ioaddr); + +/* From pas_audio.c */ +void pas_pcm_interrupt(u_char status, int cause); +void pas_pcm_init(struct address_info *hw_config); + +/* From pas_mixer.c */ +int pas_init_mixer(void); + +/* From pas_midi.c */ +void pas_midi_init(void); +void pas_midi_interrupt(void); + +/* From gus_card.c */ +void attach_gus_card(struct address_info * hw_config); +int probe_gus(struct address_info *hw_config); +int gus_set_midi_irq(int num); +ointhand2_t gusintr; +void attach_gus_db16(struct address_info * hw_config); +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_voice_irq(void); +u_char gus_read8 (int reg); +void gus_write8(int reg, u_int data); +void guswave_dma_irq(void); +void gus_delay(void); +int gus_default_mixer_ioctl (int dev, u_int cmd, ioctl_arg arg); +void gus_timer_command (u_int addr, u_int val); + +/* From gus_midi.c */ +void gus_midi_init(void); +void gus_midi_interrupt(int dummy); + +/* From mpu401.c */ +void attach_mpu401(struct address_info * hw_config); +int probe_mpu401(struct address_info *hw_config); +void mpuintr(int irq); + +/* From uart6850.c */ +void attach_uart6850(struct address_info * hw_config); +ointhand2_t m6850intr; +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, snd_rw_buf * buf, int count); +int pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count); +int pmgr_access(int dev, struct patmgr_info *rec); +int pmgr_inform(int dev, int event, u_long parm1, u_long parm2, + u_long parm3, u_long parm4); + +/* From ics2101.c */ +void ics2101_mixer_init(void); + +/* From sound_timer.c */ +void sound_timer_interrupt(void); +void sound_timer_syncinterval(u_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, sound_os_info *osp); +ointhand2_t adintr; + +int ad1848_detect (int io_base, int *flags, sound_os_info *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 attach_mss(struct address_info * hw_config); +int probe_mss(struct address_info *hw_config); +void attach_pnp_ad1848(struct address_info * hw_config); +int probe_pnp_ad1848(struct address_info *hw_config); + +/* From pss.c */ +int probe_pss (struct address_info *hw_config); +void attach_pss (struct address_info *hw_config); +int probe_pss_mpu (struct address_info *hw_config); +void attach_pss_mpu (struct address_info *hw_config); +int probe_pss_mss (struct address_info *hw_config); +void attach_pss_mss (struct address_info *hw_config); + +/* From sscape.c */ +int probe_sscape (struct address_info *hw_config); +void attach_sscape (struct address_info *hw_config); +int probe_ss_mss(struct address_info *hw_config); +void attach_ss_mss(struct address_info * hw_config); +ointhand2_t sscapeintr; + +int pss_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count); +int pss_write (int dev, struct fileinfo *file, snd_rw_buf *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, + u_int cmd, ioctl_arg 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); +int InitAEDSP16_MPU401(struct address_info *hw_config); + +/* From midi_synth.c */ +void do_midi_msg (int synthno, u_char *msg, int mlen); + +/* From trix.c */ +void attach_trix_wss (struct address_info *hw_config); +int probe_trix_wss (struct address_info *hw_config); +void attach_trix_sb (struct address_info *hw_config); +int probe_trix_sb (struct address_info *hw_config); +void attach_trix_mpu (struct address_info *hw_config); +int probe_trix_mpu (struct address_info *hw_config); + +/* From mad16.c */ +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); +void mad16_sb_dsp_init (struct address_info *hw_config); + +/* From cs4232.c */ + +int probe_cs4232 (struct address_info *hw_config); +void attach_cs4232 (struct address_info *hw_config); +int probe_cs4232_mpu (struct address_info *hw_config); +void attach_cs4232_mpu (struct address_info *hw_config); + +/* From maui.c */ +void attach_maui(struct address_info * hw_config); +int probe_maui(struct address_info *hw_config); + +/* From sound_pnp.c */ +void sound_pnp_init(void); +void sound_pnp_disconnect(void); diff --git a/sys/i386/isa/sound/sound_config.h b/sys/i386/isa/sound/sound_config.h new file mode 100644 index 0000000..a802763 --- /dev/null +++ b/sys/i386/isa/sound/sound_config.h @@ -0,0 +1,205 @@ +/* sound_config.h + * + * A driver for Soundcards, misc configuration parameters. + * + * Copyright by Hannu Savolainen 1995 + * + * 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. + * + */ + +/* + * many variables should be reduced to a range. Here define a macro + */ + +#define RANGE(var, low, high) (var) = \ + ((var)<(low)?(low) : (var)>(high)?(high) : (var)) + +#undef CONFIGURE_SOUNDCARD +#undef DYNAMIC_BUFFER + +#include <i386/isa/sound/local.h> + +#define CONFIGURE_SOUNDCARD +#define DYNAMIC_BUFFER +#undef LOADABLE_SOUNDCARD + +#include <i386/isa/sound/os.h> +#include <i386/isa/sound/soundvers.h> + +#if defined(ISC) || defined(SCO) || defined(SVR42) +#define GENERIC_SYSV +#endif + +#ifndef SND_DEFAULT_ENABLE +#define SND_DEFAULT_ENABLE 1 +#endif + +#ifdef CONFIGURE_SOUNDCARD + +#ifndef MAX_REALTIME_FACTOR +#define MAX_REALTIME_FACTOR 4 +#endif + +/************* PCM DMA buffer sizes *******************/ + +/* If you are using high playback or recording speeds, the default buffersize + is too small. DSP_BUFFSIZE must be 64k or less. + + A rule of thumb is 64k for PAS16, 32k for PAS+, 16k for SB Pro and + 4k for SB. + + If you change the DSP_BUFFSIZE, don't modify this file. + Use the make config command instead. Seems to allow only 4K, 16K, + 32K, 64K. + */ + +#ifndef DSP_BUFFSIZE +#define DSP_BUFFSIZE (32760) +#endif + +#ifndef DSP_BUFFCOUNT +#define DSP_BUFFCOUNT 1 /* 1 is recommended. */ +#endif + +#define DMA_AUTOINIT 0x10 /* XXX never used */ + +#define FM_MONO 0x388 /* This is the I/O address used by AdLib */ + +#ifndef AWE32_BASE +#define AWE32_BASE 0x620 /* Default = 0x620-3, 0xA20-3, 0xE20-3 */ +#endif + +#ifndef PAS_BASE +#define PAS_BASE 0x388 +#endif + +#ifdef JAZZ16 +#ifndef JAZZ_DMA16 +#define JAZZ_DMA16 5 +#endif +#endif + +/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the + driver. (There is no need to alter this) */ +#define SEQ_MAX_QUEUE 1024 + +#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */ +/* 128 instruments for general MIDI setup and 16 unassigned */ + +/* + * Minor numbers for the sound driver. + * + * Unfortunately Creative called the codec chip of SB as a DSP. For this + * reason the /dev/dsp is reserved for digitized audio use. There is a + * device for true DSP processors but it will be called something else. + * In v3.0 it's /dev/sndproc but this could be a temporary solution. + */ + +#define SND_NDEVS 256 /* Number of supported devices */ + +#define SND_DEV_CTL 0 /* Control port /dev/mixer */ +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + synthesizer and MIDI output) */ +#define SND_DEV_MIDIN 2 /* Raw midi access */ +#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ +#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ +#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ +#define SND_DEV_STATUS 6 /* /dev/sndstat */ +/* #7 not in use now. Was in 2.4. Free for use after v3.0. */ +#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ +#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ +#define SND_DEV_PSS SND_DEV_SNDPROC + +#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_MIDI_DEV 6 +#define MAX_TIMER_DEV 3 + +struct fileinfo { + int mode; /* Open mode */ + int flags; + int dummy; /* Reference to file-flags. OS-dependent. */ +}; + +struct address_info { + int io_base; + int irq; + int dma; + int dma2; + int always_detect; /* 1=Trust me, it's there */ + char *name; + sound_os_info *osp; /* OS specific info */ + int card_subtype; /* Driver specific. Usually 0 */ +}; + +#define SYNTH_MAX_VOICES 32 + +struct voice_alloc_info { + int max_voice; + int used_voices; + int ptr; /* For device specific use */ + unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */ + int timestamp; + int alloc_times[SYNTH_MAX_VOICES]; +}; + +struct channel_info { + int pgm_num; + int bender_value; + unsigned char controllers[128]; +}; + +/* + * Process wakeup reasons + */ +#define WK_NONE 0x00 +#define WK_WAKEUP 0x01 +#define WK_TIMEOUT 0x02 +#define WK_SIGNAL 0x04 +#define WK_SLEEP 0x08 + +#define OPEN_READ PCM_ENABLE_INPUT +#define OPEN_WRITE PCM_ENABLE_OUTPUT +#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE) + +#include <i386/isa/sound/sound_calls.h> +#include <i386/isa/sound/dev_table.h> + +#ifndef DEB +#define DEB(x) +#endif +#ifndef DDB +/* #define DDB(x) x XXX */ +#define DDB(x) +#endif + +#define TIMER_ARMED 121234 +#define TIMER_NOT_ARMED 1 + +#endif diff --git a/sys/i386/isa/sound/sound_pnp.c b/sys/i386/isa/sound/sound_pnp.c new file mode 100644 index 0000000..d3d278b --- /dev/null +++ b/sys/i386/isa/sound/sound_pnp.c @@ -0,0 +1,186 @@ +/* + * sound/sound_pnp.c + * + * PnP soundcard support (Linux spesific) + * + * Copyright by Hannu Savolainen 1995 + * + * 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 <i386/isa/sound/sound_config.h> + +/* + * XXX check what to use in place of CONFIG_PNP + */ +#if (NSND > 0) && defined(CONFIG_PNP) + +#include <linux/pnp.h> + +static struct pnp_sounddev *pnp_devs[20] = { NULL }; + +static int max_pnpdevs = 20; +static int nr_pnpdevs = 0; +static int pnp_sig = 0; + +void +install_pnp_sounddrv(struct pnp_sounddev * drv) +{ + if (nr_pnpdevs < max_pnpdevs) { + pnp_devs[nr_pnpdevs++] = drv; + } else + printf("Sound: More than 20 PnP drivers defined\n"); +} + +void +cs4232_pnp(void *parm) +{ + struct pnp_dev *dev = (struct pnp_dev *) parm; + char *name; + + int portmask = 0x00, irqmask = 0x01, dmamask = 0x03; + int opl3_driver, wss_driver; + + printf("CS4232 driver waking up\n"); + + if (dev->card && dev->card->name) + name = dev->card->name; + else + name = "PnP WSS"; + + if ((wss_driver = sndtable_identify_card("AD1848"))) + portmask |= 0x01; /* MSS */ + else + printf("Sound: MSS/WSS device detected but no driver enabled\n"); + + if ((opl3_driver = sndtable_identify_card("OPL3"))) + portmask |= 0x02; /* OPL3 */ + else + printf("Sound: OPL3/4 device detected but no driver enabled\n"); + + printf("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); + + if (!portmask) /* No drivers available */ + return; + + if (!pnp_allocate_device(pnp_sig, dev, portmask, irqmask, dmamask, 0x00)) + printf("Device activation failed\n"); + else { + struct address_info hw_config; + int wss_base, opl3_base; + int irq; + int dma1, dma2; + + printf("Device activation OK\n"); + wss_base = pnp_get_port(dev, 0); + opl3_base = pnp_get_port(dev, 1); + irq = pnp_get_irq(dev, 0); + dma1 = pnp_get_dma(dev, 0); + dma2 = pnp_get_dma(dev, 1); + + printf("I/O0 %03x\n", wss_base); + printf("I/O1 %03x\n", opl3_base); + printf("IRQ %d\n", irq); + printf("DMA0 %d\n", dma1); + printf("DMA1 %d\n", dma2); + + if (opl3_base && opl3_driver) { + hw_config.io_base = opl3_base; + hw_config.irq = 0; + hw_config.dma = -1; + hw_config.dma2 = -1; + hw_config.always_detect = 0; + hw_config.name = ""; + hw_config.osp = NULL; + hw_config.card_subtype = 0; + + if (sndtable_probe(opl3_driver, &hw_config)) + sndtable_init_card(opl3_driver, &hw_config); + + } + if (wss_base && wss_driver) { + hw_config.io_base = wss_base; + hw_config.irq = irq; + hw_config.dma = dma1; + hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2; + hw_config.always_detect = 0; + hw_config.name = name; + hw_config.osp = NULL; + hw_config.card_subtype = 0; + + if (sndtable_probe(wss_driver, &hw_config)) + sndtable_init_card(wss_driver, &hw_config); + + } + } +} + +static int +pnp_activate(int id, struct pnp_dev * dev) +{ + int i; + + for (i = 0; i < nr_pnpdevs; i++) + if (pnp_devs[i]->id == id) { + + printf("PnP dev: %08x, %s\n", id, pnp_devid2asc(id)); + + pnp_devs[i]->setup((void *) dev); + return 1; + } + return 0; +} + +void +sound_pnp_init(void) +{ + static struct pnp_sounddev cs4232_dev = + {PNP_DEVID('C', 'S', 'C', 0x0000), cs4232_pnp, "CS4232"}; + + struct pnp_dev *dev; + + install_pnp_sounddrv(&cs4232_dev); + + dev = NULL; + + if ((pnp_sig = pnp_connect("sound")) == -1) { + printf("Sound: Can't connect to kernel PnP services.\n"); + return; + } + while ((dev = pnp_get_next_device(pnp_sig, dev)) != NULL) { + if (!pnp_activate(dev->key, dev)) { + /* Scan all compatible devices */ + + int i; + + for (i = 0; i < dev->ncompat; i++) + if (pnp_activate(dev->compat_keys[i], dev)) + break; + } + } +} + +void +sound_pnp_disconnect(void) +{ + pnp_disconnect(pnp_sig); +} +#endif diff --git a/sys/i386/isa/sound/sound_switch.c b/sys/i386/isa/sound/sound_switch.c new file mode 100644 index 0000000..763a10c --- /dev/null +++ b/sys/i386/isa/sound/sound_switch.c @@ -0,0 +1,499 @@ +/* + * sound/sound_switch.c + * + * The system call switch + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 <i386/isa/sound/sound_config.h> + +#if NSND > 0 + +#define SNDSTAT_BUF_SIZE 4000 + +/* + * /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 >= SNDSTAT_BUF_SIZE) + return 0; + + bcopy(s, &status_buf[status_len], l); + status_len += l; + + return 1; +} + +/* + * in principle, we never overflow the buffer. But... if radix=2 ... + * and if smaller... lr970711 + */ + +static int +put_status_int(u_int val, int radix) +{ + int l, v; + static char hx[] = "0123456789abcdef"; + char buf[33]; /* int is 32 bit+null, in base 2 */ + + if (radix < 2 || radix > 16) /* better than panic */ + return put_status("???"); + + 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 >= SNDSTAT_BUF_SIZE) + return 0; + + bcopy(&buf[10 - l], &status_buf[status_len], 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 SNDSTAT_BUF_SIZE bytes for the data. + * put_status handles this and returns 0 in case of failure. Since + * it never oveflows the buffer, we do not care to check. + */ + + int i; + + status_ptr = 0; + +#ifdef SOUND_UNAME_A + put_status("VoxWare Sound Driver:" SOUND_VERSION_STRING + " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY ",\n" + SOUND_UNAME_A ")\n"); +#else + put_status("VoxWare Sound Driver:" SOUND_VERSION_STRING + " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@" + SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")\n"); +#endif + + put_status("Config options: ") ; + /* put_status_int(SELECTED_SOUND_OPTIONS, 16) ; */ + put_status("\n\nInstalled drivers: \n") ; + + for (i = 0; i < num_sound_drivers; i++) + if (sound_drivers[i].card_type != 0) { + put_status("Type ") ; + put_status_int(sound_drivers[i].card_type, 10); + put_status(": ") ; + put_status(sound_drivers[i].name) ; + put_status("\n") ; + } + put_status("\n\nCard config: \n") ; + + 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) + put_status("(") ; + + if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) != -1) + put_status(sound_drivers[drv].name) ; + + put_status(" at 0x") ; + put_status_int(snd_installed_cards[i].config.io_base, 16); + + put_status(" irq ") ; + tmp = snd_installed_cards[i].config.irq; + if (tmp < 0) + tmp = -tmp; + put_status_int(tmp, 10) ; + + if (snd_installed_cards[i].config.dma != -1) { + put_status(" drq ") ; + put_status_int(snd_installed_cards[i].config.dma, 10) ; + if (snd_installed_cards[i].config.dma2 != -1) { + put_status(",") ; + put_status_int(snd_installed_cards[i].config.dma2, 10) ; + } + } + if (!snd_installed_cards[i].enabled) + put_status(")") ; + + put_status("\n") ; + } + if (!sound_started) { + put_status("\n\n***** Sound driver not started *****\n\n"); + return; + } +#ifndef CONFIG_AUDIO + put_status("\nAudio devices: NOT ENABLED IN CONFIG\n") ; +#else + put_status("\nAudio devices:\n") ; + + for (i = 0; i < num_audiodevs; i++) { + put_status_int(i, 10) ; + put_status(": ") ; + put_status(audio_devs[i]->name) ; + + if (audio_devs[i]->flags & DMA_DUPLEX) + put_status(" (DUPLEX)") ; + + put_status("\n") ; + } +#endif + +#ifndef CONFIG_SEQUENCER + put_status("\nSynth devices: NOT ENABLED IN CONFIG\n"); +#else + put_status("\nSynth devices:\n") ; + + for (i = 0; i < num_synths; i++) { + put_status_int(i, 10) ; + put_status(": ") ; + put_status(synth_devs[i]->info->name) ; + put_status("\n") ; + } +#endif + +#ifndef CONFIG_MIDI + put_status("\nMidi devices: NOT ENABLED IN CONFIG\n") ; +#else + put_status("\nMidi devices:\n") ; + + for (i = 0; i < num_midis; i++) { + put_status_int(i, 10) ; + put_status(": ") ; + put_status(midi_devs[i]->info.name) ; + put_status("\n") ; + } +#endif + + put_status("\nTimers:\n"); + + for (i = 0; i < num_sound_timers; i++) { + put_status_int(i, 10); + put_status(": "); + put_status(sound_timer_devs[i]->info.name); + put_status("\n"); + } + + put_status("\nMixers:\n"); + + for (i = 0; i < num_mixers; i++) { + put_status_int(i, 10); + put_status(": "); + put_status(mixer_devs[i]->name); + put_status("\n"); + } +} + +static int +read_status(snd_rw_buf * 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; + + + if (uiomove(&status_buf[status_ptr], l, buf)) { + printf("sb: Bad copyout()!\n"); + }; + status_ptr += l; + + return l; +} + +int +sound_read_sw(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) +{ + DEB(printf("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: + printf("Sound: Undefined minor device %d\n", dev); + } + + return -(EPERM); +} + +int +sound_write_sw(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) +{ + + DEB(printf("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 + + default: + return -(EPERM); + } + + return count; +} + +int +sound_open_sw(int dev, struct fileinfo * file) +{ + int retval; + + DEB(printf("sound_open_sw(dev=%d)\n", dev)); + + if ((dev >= SND_NDEVS) || (dev < 0)) { + printf("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 *) malloc(SNDSTAT_BUF_SIZE, M_TEMP, M_WAITOK)) == NULL) + return -(EIO); + status_len = status_ptr = 0; + init_status(); + break; + + case SND_DEV_CTL: + 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: + printf("Invalid minor device %d\n", dev); + return -(ENXIO); + } + + return 0; +} + +void +sound_release_sw(int dev, struct fileinfo * file) +{ + + DEB(printf("sound_release_sw(dev=%d)\n", dev)); + + switch (dev & 0x0f) { + case SND_DEV_STATUS: + if (status_buf) + free(status_buf, M_TEMP); + 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: + printf("Sound error: Releasing unknown device 0x%02x\n", dev); + } +} + +int +sound_ioctl_sw(int dev, struct fileinfo * file, u_int cmd, ioctl_arg arg) +{ + DEB(printf("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); + return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg); + break; +#endif + + default: + 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); + + 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 + + default: + return -(EPERM); + break; + } + + return -(EPERM); +} + +#endif diff --git a/sys/i386/isa/sound/sound_timer.c b/sys/i386/isa/sound/sound_timer.c new file mode 100644 index 0000000..cef7596 --- /dev/null +++ b/sys/i386/isa/sound/sound_timer.c @@ -0,0 +1,344 @@ +/* + * sound/sound_timer.c + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +#define SEQUENCER_C +#include <i386/isa/sound/sound_config.h> + +#if NSND > 0 + +#if defined(CONFIG_SEQUENCER) + +static volatile int initialized = 0, opened = 0, tmr_running = 0; +static volatile time_t tmr_offs, tmr_ctr; +static volatile u_long ticks_offs; +static volatile int curr_tempo, curr_timebase; +static volatile u_long curr_ticks; +static volatile u_long next_event_time; +static u_long prev_event_time; +static volatile u_long usecs_per_tmr; /* Length of the current interval */ + +static struct sound_lowlev_timer *tmr = NULL; + +static u_long +tmr2ticks(int tmr_value) +{ + /* + * Convert timer ticks to MIDI ticks + */ + + u_long tmp; + u_long scale; + + tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */ + + scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ + + return (tmp + (scale / 2)) / scale; +} + +static void +reprogram_timer(void) +{ + u_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); +} + +void +sound_timer_syncinterval(u_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; +} + +static void +tmr_reset(void) +{ + u_long flags; + + flags = splhigh(); + tmr_offs = 0; + ticks_offs = 0; + tmr_ctr = 0; + next_event_time = 0xffffffff; + prev_event_time = 0; + curr_ticks = 0; + splx(flags); +} + +static int +timer_open(int dev, int mode) +{ + if (opened) + return -(EBUSY); + + tmr_reset(); + curr_tempo = 60; + curr_timebase = hz; + opened = 1; + reprogram_timer(); + + return 0; +} + +static void +timer_close(int dev) +{ + opened = tmr_running = 0; + tmr->tmr_disable(tmr->dev); +} + +static int +timer_event(int dev, u_char *event) +{ + u_char cmd = event[1]; + u_long parm = *(int *) &event[4]; + + switch (cmd) { + case TMR_WAIT_REL: + parm += prev_event_time; + case TMR_WAIT_ABS: + if (parm > 0) { + long time; + + if (parm <= curr_ticks) /* It's the time */ + return TIMER_NOT_ARMED; + + time = parm; + next_event_time = prev_event_time = time; + + return TIMER_ARMED; + } + break; + + case TMR_START: + tmr_reset(); + tmr_running = 1; + reprogram_timer(); + break; + + case TMR_STOP: + tmr_running = 0; + break; + + case TMR_CONTINUE: + tmr_running = 1; + reprogram_timer(); + break; + + case TMR_TEMPO: + if (parm) { + if (parm < 8) + parm = 8; + if (parm > 250) + parm = 250; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = parm; + reprogram_timer(); + } + break; + + case TMR_ECHO: + seq_copy_to_input(event, 8); + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static u_long +timer_get_time(int dev) +{ + if (!opened) + return 0; + + return curr_ticks; +} + +static int +timer_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + switch (cmd) { + case SNDCTL_TMR_SOURCE: + 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 = (*(int *) arg); + + if (val) { + if (val < 1) + val = 1; + if (val > 1000) + val = 1000; + curr_timebase = val; + } + return *(int *) arg = curr_timebase; + } + break; + + case SNDCTL_TMR_TEMPO: + { + int val = (*(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 *(int *) arg = curr_tempo; + } + break; + + case SNDCTL_SEQ_CTRLRATE: + if ((*(int *) arg) != 0) /* Can't change */ + return -(EINVAL); + + return *(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60; + break; + + case SNDCTL_TMR_METRONOME: + /* NOP */ + break; + + default:; + } + + return -(EINVAL); +} + +static void +timer_arm(int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + + next_event_time = prev_event_time = time; + + return; +} + +static struct sound_timer_operations sound_timer = +{ + {"GUS 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) +{ + if (!opened) + return; + + tmr->tmr_restart(tmr->dev); + + if (!tmr_running) + return; + + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); + + if (curr_ticks >= next_event_time) { + next_event_time = 0xffffffff; + sequencer_timer(0); + } +} + +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; + + if (num_sound_timers >= MAX_TIMER_DEV) + n = 0; /* Overwrite the system timer */ + else + n = num_sound_timers++; + + snprintf(sound_timer.info.name, sizeof(sound_timer.info.name), "%s", name); + + sound_timer_devs[n] = &sound_timer; +} + +#endif +#endif diff --git a/sys/i386/isa/sound/soundcard.c b/sys/i386/isa/sound/soundcard.c new file mode 100644 index 0000000..3b6db0b --- /dev/null +++ b/sys/i386/isa/sound/soundcard.c @@ -0,0 +1,684 @@ +/* + * sound/386bsd/soundcard.c + * + * Soundcard driver for 386BSD. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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 "opt_devfs.h" + +#include <i386/isa/sound/sound_config.h> +#ifdef DEVFS +#include <sys/devfsext.h> +#endif /* DEVFS */ + +#if NSND > 0 /* from "snd.h" */ +#include "uart.h" + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <sys/mman.h> + +#include <i386/isa/isa_device.h> + + +/* +** Register definitions for DMA controller 1 (channels 0..3): +*/ +#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ +#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ +#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ +#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ + +/* +** Register definitions for DMA controller 2 (channels 4..7): +*/ +#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ +#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ +#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ +#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ + + +#define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;} + +static int soundcards_installed = 0; /* Number of installed soundcards */ +static int soundcard_configured = 0; + +static struct fileinfo files[SND_NDEVS]; +struct selinfo selinfo[SND_NDEVS >> 4]; + +int +MIDIbuf_poll (int dev, struct fileinfo *file, int events, select_table * wait); + +int +audio_poll(int dev, struct fileinfo * file, int events, select_table * wait); + +int +sequencer_poll (int dev, struct fileinfo *file, int events, select_table * wait); + +static int sndprobe __P((struct isa_device *)); +static int sndattach __P((struct isa_device *)); +static int sndmmap __P((dev_t dev, int offset, int nprot )); + +static d_open_t sndopen; +static d_close_t sndclose; +static d_ioctl_t sndioctl; +static d_read_t sndread; +static d_write_t sndwrite; +static d_poll_t sndpoll; + +static char driver_name[] = "snd"; + +#define CDEV_MAJOR 30 +static struct cdevsw snd_cdevsw = { + sndopen, sndclose, sndread, sndwrite, + sndioctl, nostop, noreset, nodevtotty, + sndpoll, sndmmap, nostrategy, driver_name, + NULL, -1, +}; + + + + +static void sound_mem_init(void); + +/* + * for each "device XXX" entry in the config file, we have + * a struct isa_driver which is linked into isa_devtab_null[] + * + * XXX It is a bit stupid to call the generic routine so many times and + * switch then to the specific one, but the alternative way would be + * to replicate some code in the probe/attach routines. + */ + +struct isa_driver opldriver = {sndprobe, sndattach, "opl"}; +struct isa_driver trixdriver = {sndprobe, sndattach, "trix"}; +struct isa_driver trixsbdriver = {sndprobe, sndattach, "trixsb"}; +struct isa_driver sbdriver = {sndprobe, sndattach, "sb"}; +struct isa_driver sbxvidriver = {sndprobe, sndattach, "sbxvi"}; +struct isa_driver sbmididriver = {sndprobe, sndattach, "sbmidi"}; +struct isa_driver awedriver = {sndprobe, sndattach, "awe"}; +struct isa_driver pasdriver = {sndprobe, sndattach, "pas"}; +struct isa_driver mpudriver = {sndprobe, sndattach, "mpu"}; +struct isa_driver gusdriver = {sndprobe, sndattach, "gus"}; +struct isa_driver gusxvidriver = {sndprobe, sndattach, "gusxvi"}; +struct isa_driver gusmaxdriver = {sndprobe, sndattach, "gusmax"}; +struct isa_driver uartdriver = {sndprobe, sndattach, "uart"}; +struct isa_driver mssdriver = {sndprobe, sndattach, "mss"}; +struct isa_driver cssdriver = {sndprobe, sndattach, "css"}; +struct isa_driver sscapedriver = {sndprobe, sndattach, "sscape"}; +struct isa_driver sscape_mssdriver = {sndprobe, sndattach, "sscape_mss"}; + +short ipri_to_irq(u_short ipri); + +static ointhand2_t sndintr; + +u_long +get_time(void) +{ + struct timeval timecopy; + + getmicrotime(&timecopy); + return timecopy.tv_usec / (1000000 / hz) + + (u_long) timecopy.tv_sec * hz; +} + +static int +sndmmap( dev_t dev, int offset, int nprot ) +{ + struct dma_buffparms * dmap; + + dev = minor(dev) >> 4; + if (dev > 0 ) return (-1); + + dmap = audio_devs[dev]->dmap_out; + + if (nprot & PROT_EXEC) + return( -1 ); + dmap->mapping_flags |= DMA_MAP_MAPPED ; + return( i386_btop(vtophys(dmap->raw_buf) + offset) ); +} + + +static int +sndread(dev_t dev, struct uio * buf, int flag) +{ + int count = buf->uio_resid; + + dev = minor(dev); + FIX_RETURN(sound_read_sw(dev, &files[dev], buf, count)); +} + + +static int +sndwrite(dev_t dev, struct uio * buf, int flag) +{ + int count = buf->uio_resid; + + dev = minor(dev); + FIX_RETURN(sound_write_sw(dev, &files[dev], buf, count)); +} + +static int +sndopen(dev_t dev, int flags, int mode, struct proc * p) +{ + int retval; + struct fileinfo tmp_file; + + dev = minor(dev); + if (!soundcard_configured && dev) { + printf("SoundCard Error: soundcard system has not been configured\n"); + return ENODEV ; + } + tmp_file.mode = 0; + + if (flags & FREAD && flags & FWRITE) + tmp_file.mode = OPEN_READWRITE; + else if (flags & FREAD) + tmp_file.mode = OPEN_READ; + else if (flags & FWRITE) + tmp_file.mode = OPEN_WRITE; + + selinfo[dev >> 4].si_pid = 0; + selinfo[dev >> 4].si_flags = 0; + if ((retval = sound_open_sw(dev, &tmp_file)) < 0) + FIX_RETURN(retval); + + bcopy((char *) &tmp_file, (char *) &files[dev], sizeof(tmp_file)); + + FIX_RETURN(retval); +} + + +static int +sndclose(dev_t dev, int flags, int mode, struct proc * p) +{ + dev = minor(dev); + sound_release_sw(dev, &files[dev]); + + return 0 ; +} + +static int +sndioctl(dev_t dev, u_long cmd, caddr_t arg, int mode, struct proc * p) +{ + dev = minor(dev); + FIX_RETURN(sound_ioctl_sw(dev, &files[dev], cmd, arg)); +} + +int +sndpoll(dev_t dev, int events, struct proc * p) +{ + dev = minor(dev); + dev = minor(dev); + + /* printf ("snd_select(dev=%d, rw=%d, pid=%d)\n", dev, rw, p->p_pid); */ +#ifdef ALLOW_POLL + switch (dev & 0x0f) { +#ifdef CONFIG_SEQUENCER + case SND_DEV_SEQ: + case SND_DEV_SEQ2: + return sequencer_poll(dev, &files[dev], events, p); + break; +#endif + +#ifdef CONFIG_MIDI + case SND_DEV_MIDIN: + return MIDIbuf_poll(dev, &files[dev], events, p); + break; +#endif + +#ifdef CONFIG_AUDIO + case SND_DEV_DSP: + case SND_DEV_DSP16: + case SND_DEV_AUDIO: + + return audio_poll(dev, &files[dev], events, p); + break; +#endif + + default: + return 0; + } + +#endif /* ALLOW_POLL */ + DEB(printf("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); + + return 0 ; +} + +/* XXX this should become ffs(ipri), perhaps -1 lr 970705 */ +short +ipri_to_irq(u_short ipri) +{ + /* + * Converts the ipri (bitmask) to the corresponding irq number + */ + int irq; + + for (irq = 0; irq < 16; irq++) + if (ipri == (1 << irq)) + return irq; + + return -1; /* Invalid argument */ +} + +static int +driver_to_voxunit(struct isa_driver * driver) +{ + /* + * converts a sound driver pointer into the equivalent VoxWare device + * unit number + */ + if (driver == &opldriver) + return (SNDCARD_ADLIB); + else if (driver == &sbdriver) + return (SNDCARD_SB); + else if (driver == &pasdriver) + return (SNDCARD_PAS); + else if (driver == &gusdriver) + return (SNDCARD_GUS); + else if (driver == &mpudriver) + return (SNDCARD_MPU401); + else if (driver == &sbxvidriver) + return (SNDCARD_SB16); + else if (driver == &sbmididriver) + return (SNDCARD_SB16MIDI); + else if(driver == &awedriver) + return(SNDCARD_AWE32); + else if (driver == &uartdriver) + return (SNDCARD_UART6850); + else if (driver == &gusdriver) + return (SNDCARD_GUS16); + else if (driver == &mssdriver) + return (SNDCARD_MSS); + else if (driver == &cssdriver) + return (SNDCARD_CS4232); + else if (driver == &sscapedriver) + return(SNDCARD_SSCAPE); + else if (driver == &sscape_mssdriver) + return(SNDCARD_SSCAPE_MSS); + else if (driver == &trixdriver) + return (SNDCARD_TRXPRO); + else if (driver == &trixsbdriver) + return (SNDCARD_TRXPRO_SB); + else + return (0); +} + +/* + * very dirty: tmp_osp is allocated in sndprobe, and used at the next + * call in sndattach + */ + +static sound_os_info *temp_osp; + +/* + * sndprobe is called for each isa_device. From here, a voxware unit + * number is determined, and the appropriate probe routine is selected. + * The parameters from the config line are passed to the hw_config struct. + */ + +static int +sndprobe(struct isa_device * dev) +{ + struct address_info hw_config; + int unit; + + temp_osp = (sound_os_info *)malloc(sizeof(sound_os_info), + M_DEVBUF, M_NOWAIT); + if (!temp_osp) + panic("SOUND: Cannot allocate memory\n"); + + /* + * get config info from the kernel config. These may be overridden + * by the local autoconfiguration routines though (e.g. pnp stuff). + */ + + hw_config.io_base = dev->id_iobase; + hw_config.irq = ipri_to_irq(dev->id_irq); + hw_config.dma = dev->id_drq; + + /* + * misuse the flags field for read dma. Note that, to use 0 as + * read dma channel, one of the high bits should be set. lr970705 XXX + */ + + if (dev->id_flags != 0) + hw_config.dma2 = dev->id_flags & 0x7; + else + hw_config.dma2 = -1; + + hw_config.always_detect = 0; + hw_config.name = NULL; + hw_config.card_subtype = 0; + + temp_osp->unit = dev->id_unit; + hw_config.osp = temp_osp; + unit = driver_to_voxunit(dev->id_driver); + + if (sndtable_probe(unit, &hw_config)) { + dev->id_iobase = hw_config.io_base; + dev->id_irq = hw_config.irq == -1 ? 0 : (1 << hw_config.irq); + dev->id_drq = hw_config.dma; + + if (hw_config.dma != hw_config.dma2 && ( hw_config.dma2 != -1)) + dev->id_flags = hw_config.dma2 | 0x100; /* XXX lr */ + else + dev->id_flags = 0; + return TRUE; + } + return 0; +} + +static int +sndattach(struct isa_device * dev) +{ + int unit; + static int midi_initialized = 0; + static int seq_initialized = 0; + struct address_info hw_config; + char *dname; + void *tmp; + + /* + * Associate interrupt handlers with devices. XXX this may be incomplete. + */ + dname = dev->id_driver->name; +#if defined(CONFIG_AD1848) + if (strcmp(dname, "css") == 0 || strcmp(dname, "gusxvi") == 0 || + strcmp(dname, "mss") == 0) + dev->id_ointr = adintr; +#endif +#ifdef CONFIG_GUS + if (strcmp(dname, "gus") == 0) + dev->id_ointr = gusintr; +#endif +#ifdef CONFIG_PAS + if (strcmp(dname, "pas") == 0) + dev->id_ointr = pasintr; +#endif +#if NSB > 0 && (defined(CONFIG_MIDI) || defined(CONFIG_AUDIO)) + if (strcmp(dname, "sb") == 0) + dev->id_ointr = sbintr; +#endif + if (strcmp(dname, "sscape_mss") == 0) + dev->id_ointr = sndintr; +#if NSSCAPE > 0 + if (strcmp(dname, "sscape") == 0 || strcmp(dname, "trix") == 0) + dev->id_ointr = sscapeintr; +#endif +#if NUART > 0 + if (strcmp(dname, "uart0") == 0) + dev->id_ointr = m6850intr; +#endif + + unit = driver_to_voxunit(dev->id_driver); + hw_config.io_base = dev->id_iobase; + hw_config.irq = ipri_to_irq(dev->id_irq); + hw_config.dma = dev->id_drq; + + /* misuse the flags field for read dma */ + if (dev->id_flags != 0) + hw_config.dma2 = dev->id_flags & 0x7; + else + hw_config.dma2 = -1; + + hw_config.card_subtype = 0; + hw_config.osp = temp_osp; + + if (!unit) + return FALSE; + + if (!(sndtable_init_card(unit, &hw_config))) { /* init card */ + printf(" <Driver not configured>"); + return FALSE; + } + /* + * Init the high level sound driver + */ + + if (!(soundcards_installed = sndtable_get_cardcount())) { + DDB(printf("No drivers actually installed\n")); + return FALSE; /* No cards detected */ + } + printf("\n"); + +#ifdef CONFIG_AUDIO + if (num_audiodevs) { /* Audio devices present */ + DMAbuf_init(); + sound_mem_init(); + } + soundcard_configured = 1; +#endif + + if (num_midis && !midi_initialized) + midi_initialized = 1; + + if ((num_midis + num_synths) && !seq_initialized) { + seq_initialized = 1; + sequencer_init(); + } + + { + dev_t dev; + + dev = makedev(CDEV_MAJOR, 0); + cdevsw_add(&dev, &snd_cdevsw, NULL); + } +#ifdef DEVFS +#define GID_SND GID_GAMES +#define UID_SND UID_ROOT +#define PERM_SND 0660 + /* + * make links to first successfully probed device, don't do it if + * duplicate creation of same node failed (ie. bad cookie returned) + */ + if (dev->id_driver == &opldriver){ + tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_SEQ, + DV_CHR, UID_SND, GID_SND, PERM_SND, + "sequencer%r", dev->id_unit); + if (tmp) devfs_makelink(tmp, "sequencer"); + } else if (dev->id_driver == &mpudriver || + dev->id_driver == &sbmididriver || + dev->id_driver == &uartdriver){ + tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_MIDIN, + DV_CHR, UID_SND, GID_SND, PERM_SND, + "midi%r", dev->id_unit); + if (tmp) devfs_makelink(tmp, "midi"); + } else { + tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_DSP, + DV_CHR, UID_SND, GID_SND, PERM_SND, + "dsp%r", dev->id_unit); + if (tmp) devfs_makelink(tmp, "dsp"); + tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_DSP16, + DV_CHR, UID_SND, GID_SND, PERM_SND, + "dspW%r", dev->id_unit); + if (tmp) devfs_makelink(tmp, "dspW"); + tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_AUDIO, + DV_CHR, UID_SND, GID_SND, PERM_SND, + "audio%r", dev->id_unit); + if (tmp) devfs_makelink(tmp, "audio"); + tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_CTL, + DV_CHR, UID_SND, GID_SND, PERM_SND, + "mixer%r", dev->id_unit); + if (tmp) devfs_makelink(tmp, "mixer"); + tmp = devfs_add_devswf(&snd_cdevsw, (dev->id_unit << 4) | SND_DEV_STATUS, + DV_CHR, UID_SND, GID_SND, PERM_SND, + "sndstat%r", dev->id_unit); + if (tmp) devfs_makelink(tmp, "sndstat"); + } +#endif /* DEVFS */ + return TRUE; +} + + +#ifdef CONFIG_AUDIO + +static void +alloc_dmap(int dev, int chan, struct dma_buffparms * dmap) +{ + char *tmpbuf; + int i; + + tmpbuf = contigmalloc(audio_devs[dev]->buffsize, M_DEVBUF, M_NOWAIT, + 0ul, 0xfffffful, 1ul, chan & 4 ? 0x20000ul : 0x10000ul); + if (tmpbuf == NULL) + printf("soundcard buffer alloc failed \n"); + + if (tmpbuf == NULL) { + printf("snd: Unable to allocate %d bytes of buffer\n", + 2 * (int) audio_devs[dev]->buffsize); + return; + } + dmap->raw_buf = tmpbuf; + /* + * Use virtual address as the physical address, since isa_dmastart + * performs the phys address computation. + */ + + dmap->raw_buf_phys = (uintptr_t) tmpbuf; + for (i = 0; i < audio_devs[dev]->buffsize; i++) *tmpbuf++ = 0x80; + +} + +static void +sound_mem_init(void) +{ + int dev; + static u_long dsp_init_mask = 0; + + for (dev = 0; dev < num_audiodevs; dev++) /* Enumerate devices */ + if (!(dsp_init_mask & (1 << dev))) /* Not already done */ + if (audio_devs[dev]->dmachan1 >= 0) { + dsp_init_mask |= (1 << dev); + audio_devs[dev]->buffsize = DSP_BUFFSIZE; + /* Now allocate the buffers */ + alloc_dmap(dev, audio_devs[dev]->dmachan1, + audio_devs[dev]->dmap_out); + if (audio_devs[dev]->flags & DMA_DUPLEX) + alloc_dmap(dev, audio_devs[dev]->dmachan2, + audio_devs[dev]->dmap_in); + } /* for dev */ +} + +#endif + + +int +snd_ioctl_return(int *addr, int value) +{ + if (value < 0) + return value; /* Error */ + suword(addr, value); + return 0; +} + +#define MAX_UNIT 50 +typedef void (*irq_proc_t) (int irq); +static irq_proc_t irq_proc[MAX_UNIT] = {NULL}; +static int irq_irq[MAX_UNIT] = {0}; + +int +snd_set_irq_handler(int int_lvl, void (*hndlr) (int), sound_os_info * osp) +{ + if (osp->unit >= MAX_UNIT) { + printf("Sound error: Unit number too high (%d)\n", osp->unit); + return 0; + } + irq_proc[osp->unit] = hndlr; + irq_irq[osp->unit] = int_lvl; + return 1; +} + +static void +sndintr(int unit) +{ + if ( (unit >= MAX_UNIT) || (irq_proc[unit] == NULL) ) + return; + + irq_proc[unit] (irq_irq[unit]); /* Call the installed handler */ +} + +void +conf_printf(char *name, struct address_info * hw_config) +{ + if (!trace_init) + return; + + printf("snd0: <%s> ", name); +#if 0 + if (hw_config->io_base != -1 ) + printf("at 0x%03x", hw_config->io_base); + + if (hw_config->irq != -1 ) + printf(" irq %d", hw_config->irq); + + if (hw_config->dma != -1 || hw_config->dma2 != -1) { + printf(" dma %d", hw_config->dma); + if (hw_config->dma2 != -1) + printf(",%d", hw_config->dma2); + } +#endif + +} + +void +conf_printf2(char *name, int base, int irq, int dma, int dma2) +{ + if (!trace_init) + return; + + printf("snd0: <%s> ", name); +#if 0 + if (hw_config->io_base != -1 ) + printf("at 0x%03x", hw_config->io_base); + + if (irq) + printf(" irq %d", irq); + + if (dma != -1 || dma2 != -1) { + printf(" dma %d", dma); + if (dma2 != -1) + printf(",%d", dma2); + } +#endif + +} + + +void tenmicrosec (int j) +{ + int i, k; + for (k = 0; k < j/10 ; k++) { + for (i = 0; i < 16; i++) + inb (0x80); + } +} + +#endif /* NSND > 0 */ + + + + diff --git a/sys/i386/isa/sound/soundvers.h b/sys/i386/isa/sound/soundvers.h new file mode 100644 index 0000000..66ca10d --- /dev/null +++ b/sys/i386/isa/sound/soundvers.h @@ -0,0 +1,2 @@ +#define SOUND_VERSION_STRING "3.5-alpha15-970902" +#define SOUND_INTERNAL_VERSION 0x030518 diff --git a/sys/i386/isa/sound/sscape.c b/sys/i386/isa/sound/sscape.c new file mode 100644 index 0000000..28ef044 --- /dev/null +++ b/sys/i386/isa/sound/sscape.c @@ -0,0 +1,1061 @@ +/* + * sound/sscape.c + * + * Low level driver for Ensoniq Soundscape + * + * Copyright by Hannu Savolainen 1994 + * + * 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 <i386/isa/sound/sound_config.h> + +#if NSSCAPE > 0 + +#include <i386/isa/sound/coproc.h> + +#define EXCLUDE_NATIVE_PCM + +/* + * 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 + +/* + * 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 + +/* + * DMA channel identifiers (A and B) + */ +#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_ACK 0x80 + +typedef struct sscape_info { + int base, irq, dma; + int ok; /* Properly detected */ + int failed; + int dma_allocated; + int my_audiodev; + int opened; + sound_os_info *osp; +} + + sscape_info; +static struct sscape_info dev_info = +{0}; +static struct sscape_info *devc = &dev_info; + +static int *sscape_sleeper = NULL; +static volatile struct snd_wait sscape_sleep_flag = +{0}; + +/* Some older cards have assigned interrupt bits differently than new ones */ +static char valid_interrupts_old[] = +{9, 7, 5, 15}; + +static char valid_interrupts_new[] = +{9, 5, 7, 10}; + +static char *valid_interrupts = valid_interrupts_new; + +#ifdef REVEAL_SPEA +static char old_hardware = 1; + +#else +static char old_hardware = 0; + +#endif + +static u_char +sscape_read(struct sscape_info * devc, int reg) +{ + u_long flags; + u_char val; + + flags = splhigh(); + outb(PORT(ODIE_ADDR), reg); + val = inb(PORT(ODIE_DATA)); + splx(flags); + return val; +} + +static void +sscape_write(struct sscape_info * devc, int reg, int data) +{ + u_long flags; + + flags = splhigh(); + outb(PORT(ODIE_ADDR), reg); + outb(PORT(ODIE_DATA), data); + splx(flags); +} + +static void +host_open(struct sscape_info * devc) +{ + outb(PORT(HOST_CTRL), 0x00); /* Put the board to the host mode */ +} + +static void +host_close(struct sscape_info * devc) +{ + outb(PORT(HOST_CTRL), 0x03); /* Put the board to the MIDI mode */ +} + +static int +host_write(struct sscape_info * devc, u_char *data, int count) +{ + u_long flags; + int i, timeout_val; + + flags = splhigh(); + + /* + * Send the command and data bytes + */ + + for (i = 0; i < count; i++) { + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + if (inb(PORT(HOST_CTRL)) & TX_READY) + break; + + if (timeout_val <= 0) { + splx(flags); + return 0; + } + outb(PORT(HOST_DATA), data[i]); + } + + + splx(flags); + + return 1; +} + +static int +host_read(struct sscape_info * devc) +{ + u_long flags; + int timeout_val; + u_char data; + + flags = splhigh(); + + /* + * Read a byte + */ + + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + if (inb(PORT(HOST_CTRL)) & RX_READY) + break; + + if (timeout_val <= 0) { + splx(flags); + return -1; + } + data = inb(PORT(HOST_DATA)); + + splx(flags); + + return data; +} + +static int +host_command1(struct sscape_info * devc, int cmd) +{ + u_char buf[10]; + + buf[0] = (u_char) (cmd & 0xff); + + return host_write(devc, buf, 1); +} + +static int +host_command2(struct sscape_info * devc, int cmd, int parm1) +{ + u_char buf[10]; + + buf[0] = (u_char) (cmd & 0xff); + buf[1] = (u_char) (parm1 & 0xff); + + return host_write(devc, buf, 2); +} + +static int +host_command3(struct sscape_info * devc, int cmd, int parm1, int parm2) +{ + u_char buf[10]; + + buf[0] = (u_char) (cmd & 0xff); + buf[1] = (u_char) (parm1 & 0xff); + buf[2] = (u_char) (parm2 & 0xff); + + return host_write(devc, buf, 3); +} + +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) { + printf("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) { + printf("SNDSCAPE: Setting control (%d) failed\n", ctrl); + } + host_close(devc); +} + +static int +get_board_type(struct sscape_info * devc) +{ + 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; +} + +void +sscapeintr(int irq) +{ + u_char bits, tmp; + + DEB(printf("sscapeintr(0x%02x)\n", (bits = sscape_read(devc, GA_INTSTAT_REG)))); + if ((sscape_sleep_flag.mode & WK_SLEEP)) { + sscape_sleep_flag.mode = WK_WAKEUP; + wakeup(sscape_sleeper); + } + if (bits & 0x02) { /* Host interface interrupt */ + printf("SSCAPE: Host interrupt, data=%02x\n", host_read(devc)); + } +#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) + if (bits & 0x01) { + static int debug = 0; + + mpuintr(irq); + if (debug++ > 10) { /* Temporary debugging hack */ + sscape_write(devc, GA_INTENA_REG, 0x00); /* Disable all interr. */ + } + } +#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, u_long buf, int blk_size, int mode) +{ + u_char temp; + + if (dma_chan != SSCAPE_DMA_A) { + printf("SSCAPE: Tried to use DMA channel != A. Why?\n"); + return; + } + DMAbuf_start_dma(devc->my_audiodev, buf, blk_size, mode); + + 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) +{ + /* + * 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; + } + + printf("SoundScape: The device is not in the MPU-401 mode\n"); + return 0; +} + +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.aborting = 0; + sscape_sleep_flag.mode = WK_NONE; + return 0; +} + +static void +sscape_coproc_close(void *dev_info, int sub_device) +{ + struct sscape_info *devc = dev_info; + u_long flags; + + flags = splhigh(); + if (devc->dma_allocated) { + sscape_write(devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */ +#ifdef CONFIG_NATIVE_PCM +#endif + devc->dma_allocated = 0; + } + sscape_sleep_flag.aborting = 0; + sscape_sleep_flag.mode = WK_NONE; + splx(flags); + + return; +} + +static void +sscape_coproc_reset(void *dev_info) +{ +} + +static int +sscape_download_boot(struct sscape_info * devc, u_char *block, int size, int flag) +{ + u_long flags; + u_char temp; + int done, timeout_val; + + if (flag & CPF_FIRST) { + /* + * First block. Have to allocate DMA and to reset the board + * before continuing. + */ + + flags = splhigh(); + if (devc->dma_allocated == 0) { + devc->dma_allocated = 1; + } + splx(flags); + + sscape_write(devc, GA_HMCTL_REG, + (temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f); /* Reset */ + + for (timeout_val = 10000; timeout_val > 0; timeout_val--) + sscape_read(devc, GA_HMCTL_REG); /* Delay */ + + /* 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 + */ + bcopy(block, audio_devs[devc->my_audiodev]->dmap_out->raw_buf, size); + + flags = splhigh(); + /******** INTERRUPTS DISABLED NOW ********/ + do_dma(devc, SSCAPE_DMA_A, + audio_devs[devc->my_audiodev]->dmap_out->raw_buf_phys, + size, 1); + + /* + * Wait until transfer completes. + */ + sscape_sleep_flag.aborting = 0; + sscape_sleep_flag.mode = WK_NONE; + done = 0; + timeout_val = 100; + while (!done && timeout_val-- > 0) { + int chn; + sscape_sleeper = &chn; + DO_SLEEP(chn, sscape_sleep_flag, 1); + + done = 1; + } + + splx(flags); + if (!done) + return 0; + + if (flag & CPF_LAST) { + /* + * Take the board out of reset + */ + outb(PORT(HOST_CTRL), 0x00); + outb(PORT(MIDI_CTRL), 0x00); + + 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 + */ + + flags = splhigh(); + done = 0; + timeout_val = 5 * hz; + while (!done && timeout_val-- > 0) { + int chn; + + sscape_sleeper = &chn; + DO_SLEEP(chn, sscape_sleep_flag, 1); + + if (inb(PORT(HOST_DATA)) == 0xff) /* OBP startup acknowledge */ + done = 1; + } + splx(flags); + if (!done) { + printf("SoundScape: The OBP didn't respond after code download\n"); + return 0; + } + flags = splhigh(); + done = 0; + timeout_val = 5 * hz; + while (!done && timeout_val-- > 0) { + int chn; + + sscape_sleeper = &chn; + DO_SLEEP(chn, sscape_sleep_flag, 1); + + if (inb(PORT(HOST_DATA)) == 0xfe) /* Host startup acknowledge */ + done = 1; + } + splx(flags); + if (!done) { + printf("SoundScape: OBP Initialization failed.\n"); + return 0; + } + printf("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); + +#ifdef SSCAPE_DEBUG3 + /* + * Temporary debugging aid. Print contents of the registers + * after downloading the code. + */ + { + int i; + + for (i = 0; i < 13; i++) + printf("I%d = %02x (new value)\n", i, sscape_read(devc, i)); + } +#endif + + } + return 1; +} + +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)) { + printf("SSCAPE: Unable to load microcode block to the OBP.\n"); + return -(EIO); + } + return 0; +} + +static int +sscape_coproc_ioctl(void *dev_info, u_int cmd, ioctl_arg arg, int local) +{ + + 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 *) malloc(sizeof(copr_buffer), M_TEMP, M_WAITOK); + if (buf == NULL) + return -(ENOSPC); + bcopy(&(((char *) arg)[0]), (char *) buf, sizeof(*buf)); + err = download_boot_block(dev_info, buf); + free(buf, M_TEMP); + return err; + } + break; + + 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 +}; + +static int +sscape_audio_open(int dev, int mode) +{ + u_long flags; + sscape_info *devc = (sscape_info *) audio_devs[dev]->devc; + + flags = splhigh(); + if (devc->opened) { + splx(flags); + return -(EBUSY); + } + devc->opened = 1; + splx(flags); +#ifdef SSCAPE_DEBUG4 + /* + * Temporary debugging aid. Print contents of the registers when the + * device is opened. + */ + { + int i; + + for (i = 0; i < 13; i++) + printf("I%d = %02x\n", i, sscape_read(devc, i)); + } +#endif + + return 0; +} + +static void +sscape_audio_close(int dev) +{ + u_long flags; + sscape_info *devc = (sscape_info *) audio_devs[dev]->devc; + + DEB(printf("sscape_audio_close(void)\n")); + + flags = splhigh(); + + devc->opened = 0; + + splx(flags); +} + +static int +set_speed(sscape_info * devc, int arg) +{ + return 8000; +} + +static int +set_channels(sscape_info * devc, int arg) +{ + return 1; +} + +static int +set_format(sscape_info * devc, int arg) +{ + return AFMT_U8; +} + +static int +sscape_audio_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) +{ + sscape_info *devc = (sscape_info *) audio_devs[dev]->devc; + + switch (cmd) { + case SOUND_PCM_WRITE_RATE: + if (local) + return set_speed(devc, (int) arg); + return *(int *) arg = set_speed(devc, (*(int *) arg)); + + case SOUND_PCM_READ_RATE: + if (local) + return 8000; + return *(int *) arg = 8000; + + case SNDCTL_DSP_STEREO: + if (local) + return set_channels(devc, (int) arg + 1) - 1; + return *(int *) arg = set_channels(devc, (*(int *) arg) + 1) - 1; + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return set_channels(devc, (int) arg); + return *(int *) arg = set_channels(devc, (*(int *) arg)); + + case SOUND_PCM_READ_CHANNELS: + if (local) + return 1; + return *(int *) arg = 1; + + case SNDCTL_DSP_SAMPLESIZE: + if (local) + return set_format(devc, (int) arg); + return *(int *) arg = set_format(devc, (*(int *) arg)); + + case SOUND_PCM_READ_BITS: + if (local) + return 8; + return *(int *) arg = 8; + + default:; + } + return -(EINVAL); +} + +static void +sscape_audio_output_block(int dev, u_long buf, int count, int intrflag, int dma_restart) +{ +} + +static void +sscape_audio_start_input(int dev, u_long buf, int count, int intrflag, int dma_restart) +{ +} + +static int +sscape_audio_prepare_for_input(int dev, int bsize, int bcount) +{ + return 0; +} + +static int +sscape_audio_prepare_for_output(int dev, int bsize, int bcount) +{ + return 0; +} + +static void +sscape_audio_halt(int dev) +{ +} + +static void +sscape_audio_reset(int dev) +{ + sscape_audio_halt(dev); +} + + +#if !defined(EXCLUDE_NATIVE_PCM) && defined(CONFIG_AUDIO) +static struct audio_operations sscape_audio_operations = +{ + "Not functional", + 0, + AFMT_U8 | AFMT_S16_LE, + NULL, + sscape_audio_open, + sscape_audio_close, + sscape_audio_output_block, + sscape_audio_start_input, + sscape_audio_ioctl, + sscape_audio_prepare_for_input, + sscape_audio_prepare_for_output, + sscape_audio_reset, + sscape_audio_halt, + NULL, + NULL +}; +#endif /* !defined(EXCLUDE_NATIVE_PCM) && defined(CONFIG_AUDIO) */ + +static int sscape_detected = 0; + +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 + */ +#define SSCAPE_REGS { \ +/* I0 */ 0x00, \ + 0xf0, /* Note! Ignored. Set always to 0xf0 */ \ + 0x20, /* Note! Ignored. Set always to 0x20 */ \ + 0x20, /* Note! Ignored. Set always to 0x20 */ \ + 0xf5, /* Ignored */ \ + 0x10, \ + 0x00, \ + 0x2e, /* I7 MEM config A. Likely to vary between models */ \ + 0x00, /* I8 MEM config B. Likely to vary between models */ \ +/* I9 */ 0x40 /* Ignored */ \ + } +#endif + + u_long flags; + static u_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)) { + printf("Invalid IRQ%d\n", hw_config->irq); + return; + } + flags = splhigh(); + + 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) | 0x00); + break; + + default: + sscape_write(devc, i, regs[i]); + } + + splx(flags); + +#ifdef SSCAPE_DEBUG2 + /* + * Temporary debugging aid. Print contents of the registers after + * changing them. + */ + { + int i; + + for (i = 0; i < 13; i++) + printf("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; + + prev_devs = num_midis; + attach_mpu401(hw_config); + + if (num_midis == (prev_devs + 1)) /* The MPU driver + * installed itself */ + midi_devs[prev_devs]->coproc = &sscape_coproc_operations; + } +#endif + +#ifndef EXCLUDE_NATIVE_PCM + /* Not supported yet */ + +#ifdef CONFIG_AUDIO + if (num_audiodevs < MAX_AUDIO_DEV) { + int my_dev; + + audio_devs[my_dev = num_audiodevs++] = &sscape_audio_operations; + audio_devs[my_dev]->dmachan1 = hw_config->dma; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; + audio_devs[my_dev]->devc = devc; + devc->my_audiodev = my_dev; + devc->opened = 0; + audio_devs[my_dev]->coproc = &sscape_coproc_operations; + if (snd_set_irq_handler(hw_config->irq, sscapeintr, devc->osp) < 0) + printf("Error: Can't allocate IRQ for SoundScape\n"); + sscape_write(devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */ + } else + printf("SoundScape: More than enough audio devices detected\n"); +#endif +#endif + devc->ok = 1; + devc->failed = 0; + return; +} + +int +probe_sscape(struct address_info * hw_config) +{ + u_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; + + if (sscape_detected != 0 && sscape_detected != hw_config->io_base) + return 0; + + /* + * First check that the address register of "ODIE" is there and that + * it has exactly 4 writeable bits. First 4 bits + */ + if ((save = inb(PORT(ODIE_ADDR))) & 0xf0) + return 0; + + outb(PORT(ODIE_ADDR), 0x00); + if (inb(PORT(ODIE_ADDR)) != 0x00) + return 0; + + outb(PORT(ODIE_ADDR), 0xff); + if (inb(PORT(ODIE_ADDR)) != 0x0f) + return 0; + + outb(PORT(ODIE_ADDR), save); + + /* + * 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) + return 0; + + if (sscape_read(devc, 1) & 0x0f) + return 0; + + if (sscape_read(devc, 5) & 0x0f) + return 0; + +#ifdef SSCAPE_DEBUG1 + /* + * Temporary debugging aid. Print contents of the registers before + * changing them. + */ + { + int i; + + for (i = 0; i < 13; i++) + printf("I%d = %02x (old value)\n", i, sscape_read(devc, i)); + } +#endif + + if (old_hardware) { /* Check that it's really an old Spea/Reveal card. */ + u_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; + } + if (0) { + printf("sscape.c: Can't allocate DMA channel\n"); + return 0; + } + sscape_detected = hw_config->io_base; + + return 1; +} + +int +probe_ss_mss(struct address_info * hw_config) +{ + int i, irq_bits = 0xff; + + if (devc->failed) + return 0; + + if (devc->ok == 0) { + printf("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) { + printf("SoundScape: Invalid MSS IRQ%d\n", hw_config->irq); + return 0; + } + return ad1848_detect(hw_config->io_base, NULL, hw_config->osp); +} + +void +attach_ss_mss(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; + +#ifndef CONFIG_NATIVE_PCM + int prev_devs = num_audiodevs; +#endif + + /* + * Setup the DMA polarity. + */ + sscape_write(devc, GA_DMACFG_REG, 0x50); + + /* + * Take the gate-arry off of the DMA channel. + */ + sscape_write(devc, GA_DMAB_REG, 0x20); + + /* + * Init the AD1848 (CD-ROM) config reg. + */ + + 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) + printf("SoundScape: Warning! 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); + +#ifndef CONFIG_NATIVE_PCM + /* Check if the AD1848 driver installed itself */ + if (num_audiodevs == (prev_devs + 1)) + audio_devs[prev_devs]->coproc = &sscape_coproc_operations; +#endif + + return; +} + +#endif diff --git a/sys/i386/isa/sound/sys_timer.c b/sys/i386/isa/sound/sys_timer.c new file mode 100644 index 0000000..a605dfc --- /dev/null +++ b/sys/i386/isa/sound/sys_timer.c @@ -0,0 +1,276 @@ +/* + * sound/sys_timer.c + * + * The default timer for the Level 2 sequencer interface. + * Uses the (100hz) timer of kernel. + * + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +#define SEQUENCER_C +#include <i386/isa/sound/sound_config.h> + +#if NSND > 0 + +#if defined(CONFIG_SEQUENCER) + +static volatile int opened = 0, tmr_running = 0; +static volatile time_t tmr_offs, tmr_ctr; +static volatile u_long ticks_offs; +static volatile int curr_tempo, curr_timebase; +static volatile u_long curr_ticks; +static volatile u_long next_event_time; +static u_long prev_event_time; + +static void poll_def_tmr(void *dummy); + +static u_long +tmr2ticks(int tmr_value) +{ + /* + * 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 * hz)) / (60 * hz); +} + +static void +poll_def_tmr(void *dummy) +{ + + if (opened) { + + timeout( poll_def_tmr, 0, 1);; + + if (tmr_running) { + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); + + if (curr_ticks >= next_event_time) { + next_event_time = 0xffffffff; + sequencer_timer(0); + } + } + } +} + +static void +tmr_reset(void) +{ + u_long flags; + + flags = splhigh(); + tmr_offs = 0; + ticks_offs = 0; + tmr_ctr = 0; + next_event_time = 0xffffffff; + prev_event_time = 0; + curr_ticks = 0; + splx(flags); +} + +static int +def_tmr_open(int dev, int mode) +{ + if (opened) + return -(EBUSY); + + tmr_reset(); + curr_tempo = 60; + curr_timebase = hz; + opened = 1; + + timeout( poll_def_tmr, 0, 1);; + + return 0; +} + +static void +def_tmr_close(int dev) +{ + opened = tmr_running = 0; +} + +static int +def_tmr_event(int dev, u_char *event) +{ + u_char cmd = event[1]; + u_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) { + RANGE (parm, 8, 360) ; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = parm; + } + break; + + case TMR_ECHO: + seq_copy_to_input(event, 8); + break; + + default:; + } + + return TIMER_NOT_ARMED; +} + +static u_long +def_tmr_get_time(int dev) +{ + if (!opened) + return 0; + + return curr_ticks; +} + +static int +def_tmr_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + switch (cmd) { + case SNDCTL_TMR_SOURCE: + 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 = (*(int *) arg); + + if (val) { + RANGE (val, 1, 1000) ; + curr_timebase = val; + } + return *(int *) arg = curr_timebase; + } + break; + + case SNDCTL_TMR_TEMPO: + { + int val = (*(int *) arg); + + if (val) { + RANGE (val, 8, 250) ; + tmr_offs = tmr_ctr; + ticks_offs += tmr2ticks(tmr_ctr); + tmr_ctr = 0; + curr_tempo = val; + } + return *(int *) arg = curr_tempo; + } + break; + + case SNDCTL_SEQ_CTRLRATE: + if ((*(int *) arg) != 0) /* Can't change */ + return -(EINVAL); + + return *(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60; + break; + + case SNDCTL_TMR_METRONOME: + /* NOP */ + break; + + default:; + } + + return -(EINVAL); +} + +static void +def_tmr_arm(int dev, long time) +{ + if (time < 0) + time = curr_ticks + 1; + else if (time <= curr_ticks) /* It's the time */ + return; + + next_event_time = prev_event_time = time; + + return; +} + +struct sound_timer_operations default_sound_timer = +{ + {"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 +#endif /* NSND > 0 */ diff --git a/sys/i386/isa/sound/trix.c b/sys/i386/isa/sound/trix.c new file mode 100644 index 0000000..818ad5f --- /dev/null +++ b/sys/i386/isa/sound/trix.c @@ -0,0 +1,368 @@ +/* + * sound/trix.c + * + * Low level driver for the MediaTriX AudioTriX Pro (MT-0002-PC Control Chip) + * + * Copyright by Hannu Savolainen 1995 + * + * 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 <i386/isa/sound/sound_config.h> + +#if NTRIX > 0 + +#ifdef INCLUDE_TRIX_BOOT +#include <i386/isa/sound/trix_boot.h> +#endif + +#if (NSB > 0) +extern int sb_no_recording; +#endif + +static int kilroy_was_here = 0; /* Don't detect twice */ +static int sb_initialized = 0; + +static sound_os_info *trix_osp = NULL; + +static u_char +trix_read(int addr) +{ + outb(0x390, (u_char) addr); /* MT-0002-PC ASIC address */ + return inb(0x391); /* MT-0002-PC ASIC data */ +} + +static void +trix_write(int addr, int data) +{ + outb(0x390, (u_char) addr); /* MT-0002-PC ASIC address */ + outb(0x391, (u_char) data); /* MT-0002-PC ASIC data */ +} + +static void +download_boot(int base) +{ +#ifdef INCLUDE_TRIX_BOOT + int i = 0, n = sizeof(trix_boot); + + trix_write(0xf8, 0x00); /* ??????? */ + outb(base + 6, 0x01); /* Clear the internal data pointer */ + outb(base + 6, 0x00); /* Restart */ + + /* + * Write the boot code to the RAM upload/download register. Each + * write increments the internal data pointer. + */ + outb(base + 6, 0x01); /* Clear the internal data pointer */ + outb(0x390, 0x1A); /* Select RAM download/upload port */ + + for (i = 0; i < n; i++) + outb(0x391, trix_boot[i]); + for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */ + outb(0x391, 0x00); + outb(base + 6, 0x00); /* Reset */ + outb(0x390, 0x50); /* ?????? */ +#endif + +} + +static int +trix_set_wss_port(struct address_info * hw_config) +{ + u_char addr_bits; + + if (0) { + printf("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(printf("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; +} + +/* + * Probe and attach routines for the Windows Sound System mode of AudioTriX + * Pro + */ + +int +probe_trix_wss(struct address_info * hw_config) +{ + /* + * 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 (0) { + printf("AudioTriX: MSS I/O port conflict\n"); + 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(printf("No MSS signature detected on port 0x%x\n", + hw_config->io_base)); + return 0; + } + if (hw_config->irq > 11) { + printf("AudioTriX: Bad WSS IRQ %d\n", hw_config->irq); + return 0; + } + if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) { + printf("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) { + printf("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) { + printf("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) { + printf("AudioTriX: Can't use IRQ%d with a 8 bit card\n", hw_config->irq); + return 0; + } + return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); +} + +void +attach_trix_wss(struct address_info * hw_config) +{ + static u_char interrupt_bits[12] = + {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20}; + char bits; + + static u_char dma_bits[4] = + {1, 2, 0, 3}; + + int config_port = hw_config->io_base + 0, + version_port = hw_config->io_base + 3; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + + trix_osp = hw_config->osp; + + if (!kilroy_was_here) { + DDB(printf("AudioTriX: Attach called but not probed yet???\n")); + return ; + } + /* + * Set the IRQ and DMA addresses. + */ + + bits = interrupt_bits[hw_config->irq]; + if (bits == -1) { + printf("AudioTriX: Bad IRQ (%d)\n", hw_config->irq); + return ; + } + outb(config_port, bits | 0x40); + if ((inb(version_port) & 0x40) == 0) + printf("[IRQ Conflict?]"); + + if (hw_config->dma2 == -1) { /* Single DMA mode */ + bits |= dma_bits[dma1]; + dma2 = dma1; + } else { + u_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(config_port, bits);/* Write IRQ+DMA setup */ + + ad1848_init("AudioTriX Pro", hw_config->io_base + 4, + hw_config->irq, + dma1, + dma2, + 0, + hw_config->osp); + return ; +} + +int +probe_trix_sb(struct address_info * hw_config) +{ + int tmp; + u_char conf; + static char irq_translate[] = {-1, -1, -1, 0, 1, 2, -1, 3}; + +#ifndef INCLUDE_TRIX_BOOT + return 0; /* No boot code -> no fun */ +#endif + if (!kilroy_was_here) + return 0; /* AudioTriX Pro has not been detected earlier */ + + if (sb_initialized) + 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; + + return 1; +} + +void +attach_trix_sb(struct address_info * hw_config) +{ +#if (NSB > 0) + sb_dsp_disable_midi(); + sb_no_recording = 1; +#endif + conf_printf("AudioTriX (SB)", hw_config); +} + +void +attach_trix_mpu(struct address_info * hw_config) +{ +#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) + attach_mpu401(hw_config); +#endif +} + +int +probe_trix_mpu(struct address_info * hw_config) +{ +#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) + u_char conf; + static char irq_bits[] = {-1, -1, -1, 1, 2, 3, -1, 4, -1, 5}; + + if (!kilroy_was_here) { + DDB(printf("Trix: WSS and SB modes must be initialized before MPU\n")); + return 0; /* AudioTriX Pro has not been detected earlier */ + } + if (!sb_initialized) { + DDB(printf("Trix: SB mode must be initialized before MPU\n")); + return 0; + } + if (mpu_initialized) { + DDB(printf("Trix: MPU mode already initialized\n")); + return 0; + } + if (hw_config->irq > 9) { + printf("AudioTriX: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + if (irq_bits[hw_config->irq] == -1) { + printf("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_mpu401(hw_config); +#else + return 0; +#endif +} + +#endif diff --git a/sys/i386/isa/sound/trix_boot.h b/sys/i386/isa/sound/trix_boot.h new file mode 100644 index 0000000..d6bd16a --- /dev/null +++ b/sys/i386/isa/sound/trix_boot.h @@ -0,0 +1,2488 @@ +/* + * Computer generated file. Do not edit. + */ +static unsigned char trix_boot[] = { +0x80,0x2e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x40,0xe1,0x00,0x00, +0x00,0x00,0x00,0x02,0x44,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x02,0x44,0xcb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x74,0x00,0xa2,0xe3,0x92,0xd3,0xa2,0xe4,0x92,0xd4,0x75,0x81,0x45,0x12,0x3d,0x2e, +0xd2,0xaa,0xd2,0xaf,0x12,0x04,0xb9,0x7e,0x97,0x7f,0x54,0x90,0x00,0x00,0x02,0x0e, +0x14,0x80,0xfe,0x12,0x00,0x5c,0x12,0x03,0xc7,0x02,0x00,0x74,0xd0,0x00,0xd0,0x01, +0xc0,0x02,0xc0,0x03,0xc0,0x01,0xc0,0x00,0x8b,0x82,0x8a,0x83,0xe0,0xcc,0xfa,0xa3, +0xe0,0xcd,0xfb,0x22,0xd0,0x82,0xd0,0x83,0xf5,0xf0,0xea,0xf0,0xa3,0xeb,0xf0,0xe5, +0xf0,0x22,0xeb,0x33,0xe4,0x95,0xe0,0xfa,0xed,0x33,0xe4,0x95,0xe0,0xfc,0xec,0x33, +0xea,0x6c,0x13,0xc0,0xe0,0x12,0x03,0xcf,0xd0,0xf0,0x60,0x0c,0x30,0xf6,0x02,0xaa, +0xf0,0xea,0x33,0xb3,0xe4,0xfa,0x33,0xfb,0x22,0x7a,0x00,0x7c,0x00,0x12,0x03,0xcf, +0x60,0x05,0xb3,0xe4,0xfa,0x33,0xfb,0x22,0xeb,0x33,0xe4,0x95,0xe0,0xfa,0xed,0x33, +0xe4,0x95,0xe0,0xfc,0xec,0x33,0xea,0x6c,0x13,0xc0,0xe0,0x12,0x03,0xcf,0xd0,0xe0, +0x30,0xe6,0x01,0xfa,0xe4,0xca,0x23,0x54,0x01,0xfb,0x22,0x7a,0x00,0x7c,0x00,0x12, +0x03,0xcf,0xe4,0xfa,0x33,0xfb,0x22,0xeb,0x33,0xe4,0x95,0xe0,0xfa,0xed,0x33,0xe4, +0x95,0xe0,0xfc,0xec,0x33,0xea,0x6c,0x13,0xc0,0xe0,0x12,0x03,0xcf,0xd0,0xe0,0x30, +0xe6,0x01,0xfa,0xe4,0xca,0xf4,0x23,0x54,0x01,0xfb,0x22,0x7a,0x00,0x7c,0x00,0x12, +0x03,0xcf,0xe4,0xfa,0xb3,0x33,0xfb,0x22,0x7a,0x00,0x7c,0x00,0x12,0x03,0xcf,0x0b, +0x60,0x04,0xe4,0xfa,0x33,0xfb,0xeb,0x22,0x7a,0x00,0x7c,0x00,0x12,0x03,0xcf,0x0b, +0x60,0x03,0xe4,0xfa,0xfb,0xeb,0x22,0x7a,0x00,0x7c,0x00,0x12,0x03,0xcf,0x60,0x04, +0xe4,0xfa,0x04,0xfb,0xeb,0x22,0xd0,0x83,0xd0,0x82,0xe4,0x93,0xf5,0xf0,0xa3,0xe4, +0x93,0xa3,0xc0,0x82,0xc0,0x83,0xb8,0x03,0x0a,0xc0,0x04,0xc0,0x05,0xc0,0x00,0x78, +0x04,0x80,0x08,0xc0,0x02,0xc0,0x03,0xc0,0x00,0x78,0x02,0xa6,0xf0,0x08,0xf6,0x12, +0x03,0xd8,0xd0,0x00,0xb8,0x03,0x05,0xd0,0x05,0xd0,0x04,0x22,0x8b,0x05,0x8a,0x04, +0xd0,0x03,0xd0,0x02,0x22,0xd0,0x83,0xd0,0x82,0x12,0x02,0x14,0x80,0x0e,0xd0,0x83, +0xd0,0x82,0x12,0x02,0x14,0xc3,0xe4,0x99,0xf9,0xe4,0x98,0xf8,0xc0,0x82,0xc0,0x83, +0x8a,0x83,0x8b,0x82,0xc0,0x82,0xc0,0x83,0x80,0x28,0x78,0x00,0x79,0x01,0x80,0x04, +0x78,0xff,0x79,0xff,0xd0,0x83,0xd0,0x82,0xe4,0x93,0xa3,0xc0,0x82,0xc0,0x83,0x8a, +0x83,0x8b,0x82,0xb4,0x01,0x05,0xe0,0x29,0xfb,0xf0,0x22,0xc0,0x82,0xc0,0x83,0xb4, +0x02,0x13,0xe0,0xfa,0xa3,0xe0,0x29,0xfb,0xea,0x38,0xfa,0xd0,0x83,0xd0,0x82,0xf0, +0xa3,0xeb,0xf0,0x4a,0x22,0x75,0xf0,0x04,0xc0,0x00,0x78,0x02,0xe0,0xf6,0x08,0xa3, +0xd5,0xf0,0xf9,0x29,0xfd,0xd0,0x00,0xec,0x38,0xfc,0xeb,0x38,0xfb,0xea,0x38,0xfa, +0xd0,0x83,0xd0,0x82,0x75,0xf0,0x04,0x78,0x02,0xe6,0xf0,0xa3,0x08,0xd5,0xf0,0xf9, +0x4c,0x4b,0x4a,0x22,0xe4,0x93,0xf8,0xa3,0xe4,0x93,0xf9,0xa3,0x22,0xd0,0x83,0xd0, +0x82,0x12,0x02,0x14,0x80,0x0e,0xd0,0x83,0xd0,0x82,0x12,0x02,0x14,0xc3,0xe4,0x99, +0xf9,0xe4,0x98,0xf8,0xc0,0x82,0xc0,0x83,0x8a,0x83,0x8b,0x82,0xc0,0x82,0xc0,0x83, +0x80,0x2a,0x78,0x00,0x79,0x01,0x80,0x04,0x78,0xff,0x79,0xff,0xd0,0x83,0xd0,0x82, +0xe4,0x93,0xa3,0xc0,0x82,0xc0,0x83,0x8a,0x83,0x8b,0x82,0xb4,0x01,0x07,0xe0,0x29, +0xf0,0xc3,0x99,0xfb,0x22,0xc0,0x82,0xc0,0x83,0xb4,0x02,0x19,0xe0,0xfa,0xa3,0xe0, +0x29,0xfb,0xea,0x38,0xfa,0xd0,0x83,0xd0,0x82,0xf0,0xa3,0xeb,0xf0,0xc3,0x99,0xfb, +0xea,0x98,0xfa,0x4b,0x22,0x75,0xf0,0x04,0xc0,0x00,0x78,0x02,0xe0,0xf6,0x08,0xa3, +0xd5,0xf0,0xf9,0x29,0xfd,0xd0,0x00,0xec,0x38,0xfc,0xeb,0x38,0xfb,0xea,0x38,0xfa, +0xd0,0x83,0xd0,0x82,0x75,0xf0,0x04,0xc0,0x00,0x78,0x02,0xe6,0xf0,0xa3,0x08,0xd5, +0xf0,0xf9,0xd0,0x00,0xc3,0x99,0xfd,0xec,0x98,0xfc,0xeb,0x98,0xfb,0xea,0x98,0xfa, +0x4b,0x4c,0x4d,0x22,0xeb,0x60,0x11,0x24,0xf0,0x50,0x04,0xe4,0xfa,0xfb,0x22,0xec, +0xc3,0x13,0xfc,0xed,0x13,0xfd,0xdb,0xf7,0x8c,0x02,0x8d,0x03,0xea,0x4b,0x22,0xeb, +0x60,0x11,0x24,0xf0,0x50,0x04,0xe4,0xfa,0xfb,0x22,0xed,0xc3,0x33,0xfd,0xec,0x33, +0xfc,0xdb,0xf7,0x8c,0x02,0x8d,0x03,0xea,0x4b,0x22,0xea,0x5c,0xfa,0xeb,0x5d,0xfb, +0x4a,0x22,0xea,0x4c,0xfa,0xeb,0x4d,0xfb,0x4a,0x22,0x8a,0x83,0x8b,0x82,0xed,0xf0, +0xfb,0x22,0x8a,0x83,0x8b,0x82,0xec,0xf0,0xfa,0xa3,0xed,0xf0,0xfb,0x4a,0x22,0xc0, +0x03,0xc0,0x02,0x12,0x05,0xf9,0xd0,0x83,0xd0,0x82,0x78,0x04,0x79,0x02,0xe7,0xf0, +0x09,0xa3,0xd8,0xfa,0x4c,0x4b,0x4a,0x22,0x78,0x03,0x79,0x01,0x80,0x08,0x79,0x02, +0x80,0x02,0x79,0x04,0x78,0x02,0xd0,0x83,0xd0,0x82,0xe4,0x93,0x2b,0xf5,0xf0,0xa3, +0xe4,0x93,0x3a,0x80,0x27,0x78,0x03,0x79,0x01,0x80,0x14,0x78,0x05,0x79,0x01,0x80, +0x0e,0x79,0x02,0x80,0x08,0x78,0x04,0x79,0x02,0x80,0x04,0x79,0x04,0x78,0x02,0xd0, +0x83,0xd0,0x82,0xe4,0x93,0x2f,0xf5,0xf0,0xa3,0xe4,0x93,0x3e,0xa3,0xc0,0x82,0xc0, +0x83,0xf5,0x83,0x85,0xf0,0x82,0x75,0xf0,0x00,0xe0,0xf6,0x42,0xf0,0x08,0xa3,0xd9, +0xf8,0xe5,0xf0,0x22,0x78,0x03,0x79,0x01,0x80,0x08,0x79,0x02,0x80,0x02,0x79,0x04, +0x78,0x02,0xd0,0x83,0xd0,0x82,0xe4,0x93,0x2f,0xf5,0xf0,0xa3,0xe4,0x93,0x3e,0xa3, +0xc0,0x82,0xc0,0x83,0xf5,0x83,0x85,0xf0,0x82,0x75,0xf0,0x00,0xe6,0xf0,0xa3,0x08, +0x42,0xf0,0xd9,0xf8,0xe5,0xf0,0x22,0xeb,0x2d,0xfb,0xea,0x3c,0xfa,0x4b,0x22,0xed, +0xc3,0x9b,0xfb,0xec,0x9a,0xfa,0x4b,0x22,0xeb,0x8c,0xf0,0xa4,0xca,0x8d,0xf0,0xa4, +0x2a,0xfa,0xeb,0x8d,0xf0,0xa4,0xfb,0xe5,0xf0,0x2a,0xfa,0x4b,0x22,0x79,0x00,0xea, +0x30,0xe7,0x07,0x79,0x01,0x78,0x03,0x12,0x04,0x5f,0xec,0x30,0xe7,0x08,0x63,0x01, +0x01,0x78,0x05,0x12,0x04,0x5f,0xc0,0x01,0x12,0x04,0x25,0xd0,0x01,0xb9,0x01,0x05, +0x78,0x05,0x12,0x04,0x5f,0x8c,0x02,0x8d,0x03,0xea,0x4b,0x22,0xe4,0xc3,0x9b,0xfb, +0xe4,0x9a,0xfa,0x4b,0x22,0xbb,0x00,0x04,0xba,0x00,0x01,0x22,0x78,0x00,0x79,0x00, +0x74,0x10,0xc0,0xe0,0xc3,0xed,0x33,0xfd,0xec,0x33,0xfc,0xe9,0x33,0xf9,0xe8,0x33, +0xf8,0xc3,0xe9,0x9b,0xf5,0xf0,0xe8,0x9a,0x40,0x0b,0xf8,0xa9,0xf0,0xed,0x24,0x01, +0xfd,0xec,0x34,0x00,0xfc,0xd0,0xe0,0x14,0xc0,0xe0,0x70,0xd8,0xd0,0xe0,0x22,0xe4, +0xc3,0x96,0xf6,0x18,0xe4,0x96,0xf6,0x22,0xd0,0x83,0xd0,0x82,0xe4,0x93,0xf8,0xa3, +0xe4,0x93,0xf9,0xa3,0xe4,0x93,0xfc,0xa3,0xe4,0x93,0xfd,0xa3,0xc3,0xeb,0x99,0xf9, +0xea,0x98,0xf8,0x20,0xe7,0x08,0xc3,0xed,0x9b,0xec,0x9a,0x30,0xe7,0x0d,0xe4,0x93, +0xf5,0xf0,0xa3,0xe4,0x93,0xa3,0xc0,0xe0,0xc0,0xf0,0x22,0xa3,0xa3,0xe9,0x29,0x50, +0x02,0x05,0x83,0x25,0x82,0xf5,0x82,0xe5,0x83,0x38,0x28,0xf5,0x83,0xe4,0x93,0xf5, +0xf0,0xa3,0xe4,0x93,0xc0,0xe0,0xc0,0xf0,0x22,0x90,0x46,0xc2,0xaa,0x83,0xab,0x82, +0x90,0x42,0x80,0x12,0x05,0x14,0x90,0x9b,0x15,0xaa,0x83,0xab,0x82,0x90,0x9b,0x15, +0x12,0x05,0x1f,0x60,0x2c,0xc0,0x02,0xc0,0x03,0xc0,0x82,0xc0,0x83,0x78,0x02,0x79, +0x04,0xe4,0x93,0xf6,0xa3,0x08,0xd9,0xf9,0x8b,0x82,0x8a,0x83,0x8c,0x02,0x8d,0x03, +0x12,0x05,0x14,0xd0,0x83,0xd0,0x82,0xd0,0x03,0xd0,0x02,0xa3,0xa3,0xa3,0xa3,0x80, +0xcf,0x12,0x05,0x28,0x9b,0x15,0x4c,0x83,0x46,0xc2,0x12,0x05,0x28,0x9b,0x15,0x9b, +0x15,0x95,0x54,0x22,0x12,0x05,0x1f,0x70,0x01,0x22,0xe4,0xf0,0xa3,0x80,0xf5,0xeb, +0x65,0x82,0x70,0x03,0xea,0x65,0x83,0x22,0xd0,0x83,0xd0,0x82,0x78,0x02,0x79,0x06, +0xe4,0x93,0xf6,0xa3,0x08,0xd9,0xf9,0xc0,0x82,0xc0,0x83,0x8d,0x82,0x8c,0x83,0x12, +0x05,0x1f,0x60,0x13,0xe4,0x93,0xa3,0xad,0x82,0xac,0x83,0x8f,0x82,0x8e,0x83,0xf0, +0xa3,0xaf,0x82,0xae,0x83,0x80,0xe4,0x22,0x60,0x19,0xc0,0x00,0xc0,0x01,0xc0,0x83, +0xc0,0x82,0xf8,0x54,0x01,0x24,0x02,0xf9,0x12,0x06,0x00,0xd0,0x82,0xd0,0x83,0xd0, +0x01,0xd0,0x00,0xef,0x25,0x82,0xfd,0xee,0x35,0x83,0xfc,0xc3,0xef,0x99,0xf5,0x82, +0xff,0xee,0x98,0xfe,0xf5,0x83,0xd0,0x02,0xd0,0x03,0xd0,0xe0,0xf0,0xa3,0xd0,0xe0, +0xf0,0xa3,0xec,0xf0,0xa3,0xed,0xf0,0xc0,0x03,0xc0,0x02,0x22,0x8e,0x83,0x8f,0x82, +0xe0,0xf5,0xf0,0xa3,0xe0,0xc0,0xe0,0xc0,0xf0,0xa3,0xe0,0xfe,0xa3,0xe0,0xff,0x22, +0xd0,0x01,0xd0,0x00,0xd0,0xe0,0xd0,0xf0,0xc0,0x00,0xc0,0x01,0xc0,0xf0,0xc0,0xe0, +0x22,0x78,0x01,0x79,0x03,0x02,0x06,0x00,0x78,0x01,0x79,0x05,0x02,0x06,0x00,0x78, +0x02,0x79,0x02,0x02,0x06,0x00,0x78,0x02,0x79,0x04,0x02,0x06,0x00,0x78,0x01,0x79, +0x03,0x02,0x06,0x13,0x78,0x01,0x79,0x05,0x02,0x06,0x13,0x78,0x02,0x79,0x02,0x02, +0x06,0x13,0x78,0x02,0x79,0x04,0x02,0x06,0x13,0x78,0x04,0x79,0x02,0x02,0x06,0x13, +0xef,0xc3,0x98,0xff,0xee,0x94,0x00,0xfe,0x8f,0x82,0x8e,0x83,0xe7,0xf0,0xa3,0x09, +0xd8,0xfa,0x22,0xc0,0xe0,0x8f,0x82,0x8e,0x83,0xe0,0xf7,0xa3,0x09,0xd8,0xfa,0xae, +0x83,0xaf,0x82,0xd0,0xe0,0x22,0x74,0x02,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12, +0x03,0x61,0x04,0x00,0x90,0x42,0x80,0xea,0xf0,0xeb,0xa3,0xf0,0x7b,0x00,0x12,0x05, +0xc1,0x7b,0x05,0x12,0x05,0xc1,0x7b,0x01,0x90,0x00,0x03,0x12,0x0d,0x73,0x7b,0x02, +0x12,0x05,0xc1,0x7b,0x05,0x12,0x05,0xc1,0x7b,0x01,0x90,0x00,0x03,0x12,0x0d,0x73, +0x90,0x00,0x00,0x12,0x0d,0xed,0x7b,0xf9,0x90,0x42,0x0c,0xeb,0xf0,0x90,0x42,0x0c, +0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b,0x60, +0x1a,0x7b,0x00,0x12,0x05,0xc1,0x90,0x42,0x0c,0xe0,0xfb,0x90,0x00,0x02,0x12,0x0c, +0xe7,0x7b,0x0c,0x7a,0x42,0x12,0x02,0x48,0x01,0x80,0xd2,0x7b,0x1b,0x12,0x05,0xc1, +0x7b,0xf8,0x90,0x00,0x02,0x12,0x0c,0xe7,0x7b,0x01,0x7a,0x00,0x02,0x05,0x9c,0xe4, +0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04, +0x12,0x05,0x58,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x20,0xeb,0x2d,0xfb,0x90, +0x42,0x0d,0xeb,0xf0,0x7b,0x82,0x7a,0x42,0x8b,0x05,0x8a,0x04,0x90,0x42,0x0d,0xe0, +0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x8b,0x05,0x7b,0xfe, +0xeb,0x5d,0xfb,0x8b,0x05,0x12,0x03,0x55,0x05,0x00,0xeb,0x4d,0xfb,0x12,0x05,0xc1, +0x90,0x42,0x0d,0xe0,0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x7b,0xe8,0x8b,0x05,0x7b, +0x0d,0x7a,0x42,0x8a,0x83,0x8b,0x82,0xe0,0x2d,0xf0,0xfb,0x12,0x03,0x55,0x06,0x00, +0x12,0x05,0xc1,0x90,0x42,0x0d,0xe0,0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x02,0x05, +0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x7b,0x00,0x7a,0x00,0x90,0x42, +0x0f,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x0f,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05, +0x8a,0x04,0x7b,0xe8,0x7a,0x03,0x12,0x00,0xc4,0x60,0x1a,0x90,0x00,0x00,0x12,0x0d, +0xed,0x8b,0x05,0x7b,0x02,0xeb,0x5d,0x70,0x02,0x80,0x0a,0x7b,0x0f,0x7a,0x42,0x12, +0x02,0x42,0x02,0x80,0xd1,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0x8b,0x05,0x8a,0x04, +0x7b,0x80,0x7a,0x00,0x12,0x03,0xc7,0x90,0x42,0x0e,0xeb,0xf0,0x74,0x05,0x2f,0xfb, +0xe4,0x3e,0xfa,0x12,0x02,0x42,0x02,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x8b,0x05,0x7b, +0x3f,0xeb,0x5d,0xfb,0x12,0x05,0xc1,0x90,0x42,0x0e,0xe0,0xfb,0x90,0x00,0x02,0x12, +0x0c,0xe7,0x7b,0x18,0x8b,0x05,0x7b,0x0e,0x7a,0x42,0x8a,0x83,0x8b,0x82,0xe0,0x2d, +0xf0,0xfb,0x74,0x05,0x2f,0xfb,0xe4,0x3e,0xfa,0x12,0x02,0x42,0x02,0x8a,0x83,0x8b, +0x82,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x0e,0xe0,0xfb,0x90,0x00,0x02,0x12,0x0c, +0xe7,0x7b,0x18,0x8b,0x05,0x7b,0x0e,0x7a,0x42,0x8a,0x83,0x8b,0x82,0xe0,0x2d,0xf0, +0xfb,0x74,0x05,0x2f,0xfb,0xe4,0x3e,0xfa,0x12,0x02,0x42,0x02,0x8a,0x83,0x8b,0x82, +0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x0e,0xe0,0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7, +0x7b,0x18,0x8b,0x05,0x7b,0x0e,0x7a,0x42,0x8a,0x83,0x8b,0x82,0xe0,0x2d,0xf0,0xfb, +0x74,0x05,0x2f,0xfb,0xe4,0x3e,0xfa,0x12,0x02,0x42,0x02,0x8a,0x83,0x8b,0x82,0xe0, +0xfb,0x12,0x05,0xc1,0x90,0x42,0x0e,0xe0,0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x7b, +0x18,0x8b,0x05,0x7b,0x0e,0x7a,0x42,0x8a,0x83,0x8b,0x82,0xe0,0x2d,0xf0,0xfb,0x74, +0x05,0x2f,0xfb,0xe4,0x3e,0xfa,0x12,0x02,0x42,0x02,0x8a,0x83,0x8b,0x82,0xe0,0xfb, +0x8b,0x05,0x7b,0x07,0xeb,0x5d,0xfb,0x12,0x05,0xc1,0x90,0x42,0x0e,0xe0,0xfb,0x90, +0x00,0x02,0x12,0x0c,0xe7,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05, +0x58,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x50,0xeb,0x2d,0xfb,0x90,0x42,0x11, +0xeb,0xf0,0x12,0x03,0x55,0x05,0x00,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x12, +0x02,0xdf,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x06,0x00,0x12,0x05,0xd6,0x8b,0x05, +0x7b,0x01,0xeb,0x5d,0xfb,0x12,0x05,0xf2,0x7a,0x00,0x12,0x03,0x02,0x12,0x05,0xc1, +0x90,0x42,0x11,0xe0,0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x02,0x05,0x9c,0x74,0x01, +0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x68, +0xeb,0x2d,0xfb,0x90,0x42,0x12,0xeb,0xf0,0x7b,0x82,0x7a,0x42,0x8b,0x05,0x8a,0x04, +0x90,0x42,0x12,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb, +0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0x30,0x7a,0xff,0x12,0x02,0xfa,0x8b,0x05,0x8a, +0x04,0x12,0x03,0x55,0x05,0x00,0x12,0x05,0xd6,0x8b,0x05,0x7b,0x0f,0xeb,0x5d,0xfb, +0x12,0x05,0xf2,0x7a,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x80,0x7a,0x00, +0x12,0x03,0x02,0x12,0x05,0xf2,0x12,0x03,0x02,0x12,0x05,0xc1,0x90,0x42,0x12,0xe0, +0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04, +0x12,0x05,0x58,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x68,0xeb,0x2d,0xfb,0x90, +0x42,0x13,0xeb,0xf0,0x7b,0x82,0x7a,0x42,0x8b,0x05,0x8a,0x04,0x90,0x42,0x13,0xe0, +0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05, +0x8a,0x04,0x7b,0x7f,0x7a,0xff,0x12,0x02,0xfa,0x12,0x05,0xc1,0x90,0x42,0x13,0xe0, +0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04, +0x12,0x05,0x58,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x38,0xeb,0x2d,0xfb,0x90, +0x42,0x14,0xeb,0xf0,0x7b,0x82,0x7a,0x42,0x8b,0x05,0x8a,0x04,0x90,0x42,0x14,0xe0, +0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05, +0x8a,0x04,0x7b,0x08,0x7a,0xff,0x12,0x02,0xfa,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55, +0x05,0x00,0x7a,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x12,0x02,0xdf, +0x12,0x05,0xf2,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0xf0,0x7a,0x00,0x12,0x02, +0xfa,0x12,0x05,0xf2,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x12,0x03,0x61,0x08,0x00, +0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x07,0x12,0x02,0xc4,0x12,0x05,0xf2,0x12, +0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x07,0x7a,0x00,0x12,0x02,0xfa,0x12,0x05,0xf2, +0x12,0x03,0x02,0x12,0x05,0xf2,0x12,0x03,0x02,0x12,0x05,0xc1,0x90,0x42,0x14,0xe0, +0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x7b,0xe8,0x8b,0x05,0x7b,0x14,0x7a,0x42,0x8a, +0x83,0x8b,0x82,0xe0,0x2d,0xf0,0xfb,0x7b,0x82,0x7a,0x42,0x8b,0x05,0x8a,0x04,0x90, +0x42,0x14,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a, +0x00,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a,0xff,0x12,0x02,0xfa,0x8b,0x05,0x8a,0x04, +0x12,0x03,0x61,0x06,0x00,0x7a,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x01, +0x12,0x02,0xdf,0x12,0x05,0xf2,0x12,0x03,0x02,0x12,0x05,0xc1,0x90,0x42,0x14,0xe0, +0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04, +0x12,0x05,0x58,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x68,0xeb,0x2d,0xfb,0x90, +0x42,0x15,0xeb,0xf0,0x7b,0x82,0x7a,0x42,0x8b,0x05,0x8a,0x04,0x90,0x42,0x15,0xe0, +0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x8b,0x05,0x7b,0xf0, +0xeb,0x5d,0xfb,0x8b,0x05,0x12,0x03,0x55,0x05,0x00,0x12,0x05,0xc8,0x8b,0x05,0x7b, +0x0f,0xeb,0x5d,0xfb,0x12,0x05,0xe4,0xeb,0x4d,0xfb,0x12,0x05,0xc1,0x90,0x42,0x15, +0xe0,0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79, +0x04,0x12,0x05,0x58,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x68,0xeb,0x2d,0xfb, +0x90,0x42,0x16,0xeb,0xf0,0x7b,0x82,0x7a,0x42,0x8b,0x05,0x8a,0x04,0x90,0x42,0x16, +0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x8b,0x05,0x7b, +0xbf,0xeb,0x5d,0xfb,0x8b,0x05,0x12,0x03,0x55,0x05,0x00,0x8d,0x03,0x60,0x0d,0x8b, +0x05,0x7b,0x40,0x90,0x42,0x17,0xeb,0xf0,0x8d,0x03,0x80,0x0b,0x8b,0x05,0x7b,0x00, +0x90,0x42,0x17,0xeb,0xf0,0x8d,0x03,0x8b,0x05,0x90,0x42,0x17,0xe0,0xfb,0xeb,0x4d, +0xfb,0x12,0x05,0xc1,0x90,0x42,0x16,0xe0,0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x02, +0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03,0x55,0x04,0x00, +0x8b,0x05,0x7b,0x68,0xeb,0x2d,0xfb,0x90,0x42,0x18,0xeb,0xf0,0x7b,0x82,0x7a,0x42, +0x8b,0x05,0x8a,0x04,0x90,0x42,0x18,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83, +0x8b,0x82,0xe0,0xfb,0x8b,0x05,0x7b,0xdf,0xeb,0x5d,0xfb,0x8b,0x05,0x12,0x03,0x55, +0x05,0x00,0x8d,0x03,0x60,0x0d,0x8b,0x05,0x7b,0x20,0x90,0x42,0x19,0xeb,0xf0,0x8d, +0x03,0x80,0x0b,0x8b,0x05,0x7b,0x00,0x90,0x42,0x19,0xeb,0xf0,0x8d,0x03,0x8b,0x05, +0x90,0x42,0x19,0xe0,0xfb,0xeb,0x4d,0xfb,0x12,0x05,0xc1,0x90,0x42,0x18,0xe0,0xfb, +0x90,0x00,0x02,0x12,0x0c,0xe7,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12, +0x05,0x58,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x68,0xeb,0x2d,0xfb,0x90,0x42, +0x1a,0xeb,0xf0,0x7b,0x82,0x7a,0x42,0x8b,0x05,0x8a,0x04,0x90,0x42,0x1a,0xe0,0xfb, +0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x8b,0x05,0x7b,0xef,0xeb, +0x5d,0xfb,0x8b,0x05,0x12,0x03,0x55,0x05,0x00,0x8d,0x03,0x60,0x0d,0x8b,0x05,0x7b, +0x10,0x90,0x42,0x1b,0xeb,0xf0,0x8d,0x03,0x80,0x0b,0x8b,0x05,0x7b,0x00,0x90,0x42, +0x1b,0xeb,0xf0,0x8d,0x03,0x8b,0x05,0x90,0x42,0x1b,0xe0,0xfb,0xeb,0x4d,0xfb,0x12, +0x05,0xc1,0x90,0x42,0x1a,0xe0,0xfb,0x90,0x00,0x02,0x12,0x0c,0xe7,0x02,0x05,0x9c, +0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03,0x55,0x04,0x00,0x8b,0x05, +0x7b,0x38,0xeb,0x2d,0xfb,0x90,0x42,0x1c,0xeb,0xf0,0x7b,0x82,0x7a,0x42,0x8b,0x05, +0x8a,0x04,0x90,0x42,0x1c,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82, +0xe0,0xfb,0x8b,0x05,0x7b,0xf7,0xeb,0x5d,0xfb,0x8b,0x05,0x12,0x03,0x55,0x05,0x00, +0x8d,0x03,0x60,0x0d,0x8b,0x05,0x7b,0x08,0x90,0x42,0x1d,0xeb,0xf0,0x8d,0x03,0x80, +0x0b,0x8b,0x05,0x7b,0x00,0x90,0x42,0x1d,0xeb,0xf0,0x8d,0x03,0x8b,0x05,0x90,0x42, +0x1d,0xe0,0xfb,0xeb,0x4d,0xfb,0x12,0x05,0xc1,0x90,0x42,0x1c,0xe0,0xfb,0x90,0x00, +0x02,0x12,0x0c,0xe7,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58, +0xc2,0xb4,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x90,0x46,0xc2,0xe0,0xfa,0xa3,0xe0, +0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00,0x12,0x03,0xc7,0x12, +0x05,0xe4,0x12,0x03,0x0a,0x12,0x03,0x55,0x05,0x00,0x8b,0x05,0x90,0x46,0xc2,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x05,0x7a,0x00,0x12, +0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0x8b, +0x05,0x8a,0x04,0x7b,0x00,0x7a,0x01,0x12,0x00,0xc4,0x60,0x22,0x12,0x03,0x55,0x05, +0x00,0x8b,0x05,0x7b,0x82,0x7a,0x42,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x12,0x03, +0x55,0x05,0x00,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0xd2,0xb4, +0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0xc2,0xb4,0x12,0x03, +0x55,0x04,0x00,0x60,0x09,0x7b,0x02,0x90,0x42,0x20,0xeb,0xf0,0x80,0x07,0x7b,0x00, +0x90,0x42,0x20,0xeb,0xf0,0x90,0x42,0x20,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04, +0x90,0x46,0xc2,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xc7,0x90,0x42,0x1e,0xea,0xf0, +0xeb,0xa3,0xf0,0x12,0x03,0x55,0x05,0x00,0x8b,0x05,0x90,0x42,0x1e,0xe0,0xfa,0xa3, +0xe0,0xfb,0x12,0x03,0x0a,0x12,0x03,0x55,0x06,0x00,0x8b,0x05,0x90,0x42,0x1e,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a,0x00,0x12, +0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0xd2,0xb4,0x02,0x05,0x9c,0xe4,0x78,0x00, +0x79,0x04,0x12,0x05,0x58,0xc2,0xb4,0x90,0x46,0xc2,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a, +0x83,0x8b,0x82,0xe0,0xfb,0x90,0x42,0x21,0xeb,0xf0,0xd2,0xb4,0x90,0x42,0x21,0xe0, +0xfb,0x02,0x05,0x9c,0xe4,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x90,0x00,0x00,0x12, +0x0e,0x2f,0x90,0x00,0x00,0x12,0x42,0x7e,0x7b,0x00,0x7a,0x00,0x02,0x05,0x9c,0xe4, +0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x90,0x00,0x00,0x12,0x1a,0xf2,0x02,0x05,0x9c, +0xe4,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0xab,0x87,0x90,0x42,0x22,0xeb,0xf0,0x7b, +0x7f,0x8b,0x05,0x7b,0x22,0x7a,0x42,0x8a,0x83,0x8b,0x82,0xe0,0x5d,0xf0,0xfb,0x90, +0x42,0x22,0xe0,0xfb,0x8b,0x87,0xab,0x89,0x90,0x42,0x22,0xeb,0xf0,0x90,0x42,0x22, +0xe0,0xfb,0x8b,0x05,0x7b,0x0f,0xeb,0x5d,0xfb,0x8b,0x05,0x7b,0x20,0xeb,0x4d,0xfb, +0x90,0x42,0x22,0xeb,0xf0,0x90,0x42,0x22,0xe0,0xfb,0x8b,0x89,0x7b,0xfe,0x8b,0x8d, +0x7b,0x00,0x8b,0x8b,0xd2,0x8e,0xc2,0x9f,0xd2,0x9e,0xc2,0x9d,0xd2,0x9c,0xd2,0xac, +0x7b,0x00,0x7a,0x00,0x02,0x05,0x9c,0xe4,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x90, +0x43,0x84,0xe0,0xfb,0x8b,0x05,0x7b,0x0f,0xeb,0x5d,0xfb,0x90,0x42,0x27,0xeb,0xf0, +0x90,0x43,0x85,0xe0,0xfb,0x90,0x42,0x28,0xeb,0xf0,0x90,0x43,0x86,0xe0,0xfb,0x90, +0x42,0x29,0xeb,0xf0,0x7b,0x87,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0, +0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05, +0x8a,0x04,0x7b,0x07,0x12,0x02,0xdf,0x8b,0x05,0x8a,0x04,0x7b,0x97,0x7a,0x43,0x12, +0x05,0xd6,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7, +0x12,0x05,0xf2,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x90,0x42, +0x23,0xea,0xf0,0xeb,0xa3,0xf0,0x7b,0xa7,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42, +0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00, +0x8b,0x05,0x8a,0x04,0x7b,0x07,0x12,0x02,0xdf,0x8b,0x05,0x8a,0x04,0x7b,0xb7,0x7a, +0x43,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12, +0x03,0xc7,0x12,0x05,0xf2,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7, +0x90,0x42,0x25,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x28,0xe0,0xfb,0x7a,0x00,0x12, +0x04,0x68,0x00,0x06,0x00,0x7f,0x15,0x60,0x10,0x7c,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x11,0x72,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x12,0x68,0x13,0x4e, +0x14,0x34,0x14,0x78,0x14,0xbc,0x15,0x00,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60, +0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x60,0x15,0x44, +0x15,0x60,0x15,0x52,0x15,0x52,0x15,0x52,0x15,0x52,0x15,0x52,0x7b,0xc7,0x7a,0x43, +0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83, +0x8b,0x82,0xe0,0x60,0x6e,0x7b,0x87,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27, +0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b, +0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b,0x60,0x45,0x7b,0x97,0x7a,0x43, +0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83, +0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01, +0x3b,0x60,0x1e,0x90,0x42,0x29,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x23,0xe0,0xfa, +0xa3,0xe0,0xfb,0x12,0x05,0xcf,0x90,0x42,0x27,0xe0,0xfb,0x90,0x00,0x04,0x12,0x28, +0xf4,0x80,0x6c,0x7b,0xa7,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb, +0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a, +0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b,0x60,0x45,0x7b,0xb7,0x7a,0x43,0x8b,0x05, +0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82, +0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b,0x60, +0x1e,0x90,0x42,0x29,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x25,0xe0,0xfa,0xa3,0xe0, +0xfb,0x12,0x05,0xc1,0x90,0x42,0x27,0xe0,0xfb,0x90,0x00,0x03,0x12,0x33,0xda,0x02, +0x16,0x7b,0x7b,0xc7,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a, +0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0x60,0x6e,0x7b,0x87,0x7a,0x43,0x8b, +0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b, +0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b, +0x60,0x45,0x7b,0x97,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a, +0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04, +0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b,0x60,0x1e,0x90,0x42,0x29,0xe0,0xfb,0x12,0x05, +0xc1,0x90,0x42,0x23,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x27,0xe0, +0xfb,0x90,0x00,0x03,0x12,0x33,0xb6,0x80,0x6c,0x7b,0xa7,0x7a,0x43,0x8b,0x05,0x8a, +0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0, +0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b,0x60,0x45, +0x7b,0xb7,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12, +0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff, +0x7a,0x00,0x12,0x01,0x3b,0x60,0x1e,0x90,0x42,0x29,0xe0,0xfb,0x12,0x05,0xc1,0x90, +0x42,0x25,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x27,0xe0,0xfb,0x90, +0x00,0x03,0x12,0x33,0xe6,0x02,0x16,0x7b,0x7b,0xc7,0x7a,0x43,0x8b,0x05,0x8a,0x04, +0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0x60, +0x66,0x7b,0x87,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00, +0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b, +0xff,0x7a,0x00,0x12,0x01,0x3b,0x60,0x3d,0x7b,0x97,0x7a,0x43,0x8b,0x05,0x8a,0x04, +0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb, +0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b,0x60,0x16,0x90, +0x42,0x23,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x27,0xe0,0xfb,0x90, +0x00,0x02,0x12,0x33,0xc2,0x80,0x64,0x7b,0xa7,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90, +0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a, +0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b,0x60,0x3d,0x7b,0xb7, +0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7, +0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00, +0x12,0x01,0x3b,0x60,0x16,0x90,0x42,0x25,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc1, +0x90,0x42,0x27,0xe0,0xfb,0x90,0x00,0x02,0x12,0x33,0xf2,0x02,0x16,0x7b,0x7b,0xc7, +0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7, +0x8a,0x83,0x8b,0x82,0xe0,0x60,0x66,0x7b,0x87,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90, +0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a, +0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b,0x60,0x3d,0x7b,0x97, +0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7, +0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00, +0x12,0x01,0x3b,0x60,0x16,0x90,0x42,0x23,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc1, +0x90,0x42,0x27,0xe0,0xfb,0x90,0x00,0x02,0x12,0x33,0xce,0x80,0x64,0x7b,0xa7,0x7a, +0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a, +0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12, +0x01,0x3b,0x60,0x3d,0x7b,0xb7,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0, +0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05, +0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x3b,0x60,0x16,0x90,0x42,0x25,0xe0,0xfa, +0xa3,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x27,0xe0,0xfb,0x90,0x00,0x02,0x12,0x33, +0xfe,0x02,0x16,0x7b,0x90,0x42,0x29,0xe0,0xfb,0x8b,0x05,0x7b,0xb7,0x7a,0x43,0x12, +0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7, +0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x00,0x8b,0x05,0x7b,0xc7,0x7a,0x43,0x12,0x05, +0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x12, +0x05,0xe4,0x12,0x03,0x0a,0x02,0x16,0x7b,0x90,0x42,0x29,0xe0,0xfb,0x8b,0x05,0x7b, +0xa7,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a, +0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x00,0x8b,0x05,0x7b,0xc7, +0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00, +0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x02,0x16,0x7b,0x90,0x42,0x29,0xe0, +0xfb,0x8b,0x05,0x7b,0x97,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42, +0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x01, +0x8b,0x05,0x7b,0xc7,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27, +0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x02,0x16,0x7b, +0x90,0x42,0x29,0xe0,0xfb,0x8b,0x05,0x7b,0x87,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05, +0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12, +0x03,0x0a,0x7b,0x01,0x8b,0x05,0x7b,0xc7,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a, +0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03, +0x0a,0x02,0x16,0x7b,0x90,0x42,0x27,0xe0,0xfb,0x90,0x00,0x01,0x12,0x32,0x3e,0x02, +0x16,0x7b,0x90,0x42,0x27,0xe0,0xfb,0x90,0x00,0x01,0x12,0x31,0x8a,0x02,0x16,0x7b, +0x90,0x42,0x28,0xe0,0xfb,0x8b,0x05,0x7b,0x20,0x12,0x00,0xdb,0x60,0x66,0x90,0x42, +0x29,0xe0,0xfb,0x8b,0x05,0x7b,0xd7,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04, +0x90,0x42,0x27,0xe0,0xfb,0x7a,0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x20,0x12,0x03, +0xc7,0x12,0x05,0xe4,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x28,0xe0,0xfb, +0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x90,0x42,0x29,0xe0,0xfb, +0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0x07,0x12,0x02,0xdf,0x12,0x05,0xcf,0x90,0x42, +0x28,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x27,0xe0,0xfb,0x90,0x00,0x04,0x12,0x34, +0xed,0x02,0x16,0x7b,0x90,0x42,0x28,0xe0,0xfb,0x8b,0x05,0x7b,0x40,0x12,0x00,0xdb, +0x60,0x6e,0x7b,0xe0,0x8b,0x05,0x7b,0x28,0x7a,0x42,0x8a,0x83,0x8b,0x82,0xe0,0x2d, +0xf0,0xfb,0x7b,0xd7,0x7a,0x43,0x8b,0x05,0x8a,0x04,0x90,0x42,0x27,0xe0,0xfb,0x7a, +0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x20,0x12,0x03,0xc7,0x8b,0x05,0x8a,0x04,0x90, +0x42,0x28,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a, +0x00,0x8b,0x05,0x8a,0x04,0x7b,0x07,0x12,0x02,0xdf,0x8b,0x05,0x8a,0x04,0x90,0x42, +0x29,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xcf,0x90,0x42,0x28,0xe0,0xfb, +0x12,0x05,0xc1,0x90,0x42,0x27,0xe0,0xfb,0x90,0x00,0x04,0x12,0x34,0xed,0x80,0x2b, +0x90,0x43,0x85,0xe0,0xfb,0x8b,0x05,0x7b,0x60,0x12,0x00,0xdb,0x60,0x1d,0x90,0x42, +0x29,0xe0,0xfb,0x7a,0x00,0x12,0x05,0xcf,0x90,0x42,0x28,0xe0,0xfb,0x12,0x05,0xc1, +0x90,0x42,0x27,0xe0,0xfb,0x90,0x00,0x04,0x12,0x34,0xed,0x02,0x05,0x9c,0x74,0x01, +0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x90,0x43,0x83,0xe0,0xfb,0x8b,0x05,0x7b,0x00, +0x12,0x01,0x28,0x60,0x03,0x02,0x05,0x9c,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b, +0x85,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x82,0x7a,0x43,0x12,0x02, +0x42,0x01,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x90,0x43,0x83, +0xe0,0xfb,0x8b,0x05,0x90,0x43,0x82,0xe0,0xfb,0x12,0x01,0x37,0x60,0x03,0x02,0x05, +0x9c,0x7b,0x00,0x90,0x43,0x82,0xeb,0xf0,0x90,0x43,0x84,0xe0,0xfb,0x7a,0x00,0x8b, +0x05,0x8a,0x04,0x7b,0xf0,0x7a,0x00,0x12,0x02,0xfa,0x12,0x04,0x68,0x00,0x80,0x00, +0xe0,0x18,0xae,0x17,0xb5,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x17,0xda,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0x2e,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0x52,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0x5a,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0x76,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18,0xae,0x18, +0xae,0x18,0xae,0x18,0x92,0x90,0x43,0x86,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x43,0x85, +0xe0,0xfb,0x12,0x05,0xc1,0x90,0x43,0x84,0xe0,0xfb,0x8b,0x05,0x7b,0x0f,0xeb,0x5d, +0xfb,0x90,0x00,0x03,0x12,0x26,0xe4,0x02,0x18,0xae,0x90,0x43,0x86,0xe0,0xfb,0x8b, +0x05,0x7b,0x00,0x12,0x01,0x28,0x60,0x21,0x7b,0x40,0x12,0x05,0xc1,0x90,0x43,0x85, +0xe0,0xfb,0x12,0x05,0xc1,0x90,0x43,0x84,0xe0,0xfb,0x8b,0x05,0x7b,0x0f,0xeb,0x5d, +0xfb,0x90,0x00,0x03,0x12,0x26,0xe4,0x80,0x22,0x90,0x43,0x86,0xe0,0xfb,0x12,0x05, +0xc1,0x90,0x43,0x85,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x43,0x84,0xe0,0xfb,0x8b,0x05, +0x7b,0x0f,0xeb,0x5d,0xfb,0x90,0x00,0x03,0x12,0x21,0x57,0x02,0x18,0xae,0x90,0x43, +0x86,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x43,0x85,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x43, +0x84,0xe0,0xfb,0x8b,0x05,0x7b,0x0f,0xeb,0x5d,0xfb,0x90,0x00,0x03,0x12,0x27,0xd2, +0x80,0x5c,0x90,0x00,0x00,0x12,0x0e,0xa7,0x80,0x54,0x90,0x43,0x85,0xe0,0xfb,0x12, +0x05,0xc1,0x90,0x43,0x84,0xe0,0xfb,0x8b,0x05,0x7b,0x0f,0xeb,0x5d,0xfb,0x90,0x00, +0x02,0x12,0x27,0xea,0x80,0x38,0x90,0x43,0x85,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x43, +0x84,0xe0,0xfb,0x8b,0x05,0x7b,0x0f,0xeb,0x5d,0xfb,0x90,0x00,0x02,0x12,0x27,0xde, +0x80,0x1c,0x90,0x43,0x86,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x43,0x84,0xe0,0xfb,0x8b, +0x05,0x7b,0x0f,0xeb,0x5d,0xfb,0x90,0x00,0x02,0x12,0x3b,0xc3,0x80,0x00,0x02,0x05, +0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03,0x55,0x04,0x00,0x7a, +0x00,0x12,0x04,0x68,0x00,0xf0,0x00,0xff,0x19,0x56,0x18,0xea,0x19,0x1a,0x19,0x31, +0x19,0x1a,0x19,0x03,0x19,0x03,0x19,0x03,0x18,0xfb,0x19,0x56,0x19,0x56,0x19,0x56, +0x19,0x56,0x19,0x56,0x19,0x56,0x19,0x56,0x19,0x48,0x7b,0x01,0x90,0x45,0xd7,0xeb, +0xf0,0x7b,0xf0,0x90,0x00,0x01,0x12,0x2a,0x68,0x80,0x5b,0x7b,0xf7,0x90,0x00,0x01, +0x12,0x2a,0x68,0x7b,0x00,0x90,0x43,0x84,0xeb,0xf0,0x7b,0x00,0x90,0x43,0x83,0xeb, +0xf0,0x7b,0x00,0x90,0x43,0x82,0xeb,0xf0,0x80,0x3c,0x7b,0x00,0x90,0x43,0x84,0xeb, +0xf0,0x7b,0x01,0x90,0x43,0x83,0xeb,0xf0,0x7b,0x00,0x90,0x43,0x82,0xeb,0xf0,0x80, +0x25,0x7b,0x00,0x90,0x43,0x84,0xeb,0xf0,0x7b,0x02,0x90,0x43,0x83,0xeb,0xf0,0x7b, +0x00,0x90,0x43,0x82,0xeb,0xf0,0x80,0x0e,0x90,0x46,0xc4,0xe0,0x60,0x06,0x90,0x00, +0x00,0x12,0x1a,0xf2,0x80,0x00,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12, +0x05,0x58,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0x80,0x7a, +0x00,0x12,0x00,0xf3,0x60,0x1b,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0x8b,0x05,0x8a, +0x04,0x7b,0xf8,0x7a,0x00,0x12,0x00,0xc4,0x60,0x07,0x7b,0x00,0x90,0x45,0xd7,0xeb, +0xf0,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xf0,0x7a,0x00, +0x12,0x02,0xfa,0x12,0x04,0x68,0x00,0x80,0x00,0xf0,0x1a,0xcf,0x1a,0x8e,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0x8e,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0x8e,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0x8e,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xa8,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xa8,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0x8e,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf, +0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xcf,0x1a,0xc2,0x12,0x03, +0x55,0x04,0x00,0x90,0x43,0x84,0xeb,0xf0,0x7b,0x00,0x90,0x43,0x82,0xeb,0xf0,0x7b, +0x02,0x90,0x43,0x83,0xeb,0xf0,0x80,0x47,0x12,0x03,0x55,0x04,0x00,0x90,0x43,0x84, +0xeb,0xf0,0x7b,0x00,0x90,0x43,0x82,0xeb,0xf0,0x7b,0x01,0x90,0x43,0x83,0xeb,0xf0, +0x80,0x2d,0x12,0x03,0x55,0x04,0x00,0x90,0x00,0x01,0x12,0x18,0xb1,0x80,0x20,0x90, +0x45,0xd7,0xe0,0x60,0x0d,0x12,0x03,0x55,0x04,0x00,0x90,0x00,0x01,0x12,0x2a,0x68, +0x80,0x0b,0x12,0x03,0x55,0x04,0x00,0x90,0x00,0x01,0x12,0x16,0x7e,0x80,0x00,0x02, +0x05,0x9c,0xe4,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x90,0x00,0x00,0x12,0x2a,0x57, +0x7b,0x00,0x90,0x43,0x82,0xeb,0xf0,0x7b,0x00,0x90,0x43,0x83,0xeb,0xf0,0x7b,0x00, +0x90,0x43,0x84,0xeb,0xf0,0x7b,0x00,0x90,0x45,0xd7,0xeb,0xf0,0x7b,0x00,0x7a,0x00, +0x90,0x42,0x2a,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x2a,0xe0,0xfa,0xa3,0xe0,0xfb, +0x8b,0x05,0x8a,0x04,0x7b,0x10,0x7a,0x00,0x12,0x00,0xc4,0x70,0x03,0x02,0x1c,0x52, +0x7b,0xff,0x8b,0x05,0x7b,0x87,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90, +0x42,0x2a,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a, +0x7b,0xff,0x8b,0x05,0x7b,0x97,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90, +0x42,0x2a,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a, +0x7b,0xff,0x8b,0x05,0x7b,0xa7,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90, +0x42,0x2a,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a, +0x7b,0xff,0x8b,0x05,0x7b,0xb7,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90, +0x42,0x2a,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a, +0x7b,0x00,0x8b,0x05,0x7b,0xc7,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90, +0x42,0x2a,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a, +0x7b,0x00,0x7a,0x00,0x90,0x42,0x2c,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x2c,0xe0, +0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x20,0x7a,0x00,0x12,0x00,0xc4,0x60, +0x46,0x7b,0x00,0x8b,0x05,0x7b,0xd7,0x7a,0x43,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04, +0x90,0x42,0x2a,0xe0,0xfa,0xa3,0xe0,0xfb,0x78,0x03,0x12,0x01,0x46,0x00,0x20,0x12, +0x03,0xc7,0x12,0x05,0xe4,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x2c,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x2c,0x7a, +0x42,0x12,0x02,0x42,0x02,0x80,0xa5,0x7b,0x2a,0x7a,0x42,0x12,0x02,0x42,0x02,0x02, +0x1b,0x28,0x02,0x05,0x9c,0x74,0x02,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03, +0x61,0x04,0x00,0x12,0x03,0x38,0x04,0x00,0x8b,0x05,0x7b,0x04,0x7c,0x00,0x12,0x02, +0xc4,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xf8,0x7a,0xff,0x12,0x03,0xc7,0x90,0x42, +0x2e,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x2e,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05, +0x8a,0x04,0x7b,0x0c,0x7a,0x00,0x12,0x03,0xd8,0x8b,0x05,0x8a,0x04,0x12,0x03,0x61, +0x04,0x00,0x12,0x03,0x38,0x05,0x00,0x12,0x05,0xd6,0x8b,0x05,0x7b,0x7f,0xeb,0x5d, +0xfb,0x12,0x05,0xf2,0x7a,0x00,0x12,0x03,0xcf,0x90,0x42,0x2e,0xea,0xf0,0xeb,0xa3, +0xf0,0x90,0x42,0x2e,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x80,0x7a, +0x00,0x12,0x03,0xd8,0x8b,0x05,0x8a,0x04,0x12,0x03,0x61,0x04,0x00,0x12,0x03,0x38, +0x07,0x00,0x12,0x05,0xd6,0x8b,0x05,0x7b,0x7f,0xeb,0x5d,0xfb,0x12,0x05,0xf2,0x7a, +0x00,0x12,0x03,0xc7,0x8b,0x05,0x8a,0x04,0x7b,0xc0,0x7a,0xff,0x12,0x03,0xc7,0x90, +0x42,0x2e,0xea,0xf0,0xeb,0xa3,0xf0,0x7b,0x00,0x7a,0x36,0x8b,0x05,0x8a,0x04,0x7b, +0x2e,0x7a,0x42,0x12,0x00,0x53,0x90,0x42,0x2e,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05, +0x8a,0x04,0x12,0x03,0x61,0x04,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x08, +0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xf2,0x12,0x03,0x12,0x02,0x05,0x9c,0xe4,0x78, +0x00,0x79,0x04,0x12,0x05,0x58,0x7b,0x14,0x7a,0x00,0x90,0x00,0x02,0x12,0x06,0x26, +0x90,0x88,0x43,0xe0,0x70,0x03,0x02,0x1e,0x84,0x7b,0x00,0x90,0x88,0x43,0xeb,0xf0, +0x7b,0x00,0x7a,0x00,0x90,0x42,0x34,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x34,0xe0, +0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x80,0x7a,0x00,0x12,0x00,0xc4,0x70, +0x03,0x02,0x1e,0x84,0x7b,0xc1,0x7a,0x4a,0x8b,0x05,0x8a,0x04,0x90,0x42,0x34,0xe0, +0xfa,0xa3,0xe0,0xfb,0xeb,0x2b,0xfb,0xea,0x3a,0xfa,0x12,0x03,0xc7,0x8a,0x83,0x8b, +0x82,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x42,0x3e,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42, +0x3e,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x00,0x7a,0x00,0x12,0x01, +0x3b,0x60,0x0e,0x90,0x42,0x3e,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x00,0x02,0x12,0x1c, +0x55,0x7b,0xb3,0x7a,0x7f,0x8b,0x05,0x8a,0x04,0x90,0x42,0x34,0xe0,0xfa,0xa3,0xe0, +0xfb,0xeb,0x2b,0xfb,0xea,0x3a,0xfa,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfa, +0xa3,0xe0,0xfb,0x90,0x42,0x3c,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x3c,0xe0,0xfa, +0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00,0x12,0x03,0xc7,0x90,0x42, +0x3a,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x3c,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83, +0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x90,0x42,0x36,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42, +0x36,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x00,0x7a,0x00,0x12,0x00, +0x8e,0x60,0x36,0x90,0x42,0x3a,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x3e,0x02,0x00, +0x90,0x42,0x3e,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x3e,0xe0,0xfa,0xa3,0xe0,0xfb, +0x90,0x00,0x02,0x12,0x1c,0x55,0x7b,0x36,0x7a,0x42,0x12,0x02,0x48,0x02,0x7b,0x3a, +0x7a,0x42,0x12,0x02,0x1d,0x00,0x04,0x80,0xb5,0x7b,0x34,0x7a,0x42,0x12,0x02,0x42, +0x02,0x02,0x1d,0x6c,0x7b,0x00,0x7a,0x00,0x90,0x42,0x34,0xea,0xf0,0xeb,0xa3,0xf0, +0x90,0x42,0x34,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x10,0x7a,0x00, +0x12,0x00,0xc4,0x70,0x03,0x02,0x1f,0x26,0x90,0x42,0x34,0xe0,0xfa,0xa3,0xe0,0xfb, +0x8b,0x05,0x7b,0xe0,0x7a,0x45,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x34, +0xe0,0xfa,0xa3,0xe0,0xfb,0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x12, +0x05,0xe4,0x12,0x03,0x0a,0x7b,0x00,0x8b,0x05,0x7b,0xe0,0x7a,0x45,0x12,0x05,0xc8, +0x8b,0x05,0x8a,0x04,0x90,0x42,0x34,0xe0,0xfa,0xa3,0xe0,0xfb,0x78,0x03,0x12,0x01, +0x46,0x00,0x0e,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04, +0x7b,0x01,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x90,0x42,0x34, +0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x00,0x01,0x12,0x32,0x3e,0x7b,0x34,0x7a,0x42,0x12, +0x02,0x42,0x02,0x02,0x1e,0x90,0x7b,0x00,0x7a,0x00,0x90,0x42,0x34,0xea,0xf0,0xeb, +0xa3,0xf0,0x90,0x42,0x34,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x18, +0x7a,0x00,0x12,0x00,0xc4,0x70,0x03,0x02,0x20,0xf4,0x7b,0xff,0x8b,0x05,0x7b,0x02, +0x7a,0x00,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x34,0xe0,0xfa,0xa3,0xe0, +0xfb,0x78,0x03,0x12,0x01,0x46,0x00,0x0a,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x05, +0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12, +0x03,0x0a,0x7b,0x80,0x8b,0x05,0x7b,0x02,0x7a,0x00,0x12,0x05,0xc8,0x8b,0x05,0x8a, +0x04,0x90,0x42,0x34,0xe0,0xfa,0xa3,0xe0,0xfb,0x78,0x03,0x12,0x01,0x46,0x00,0x0a, +0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x02,0x7a, +0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x00,0x7a,0x00,0x8b,0x05, +0x8a,0x04,0x7b,0x02,0x7a,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x90,0x42,0x34, +0xe0,0xfa,0xa3,0xe0,0xfb,0x78,0x03,0x12,0x01,0x46,0x00,0x0a,0x12,0x03,0xc7,0x12, +0x05,0xf2,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00,0x12,0x03,0xc7, +0x12,0x05,0xf2,0x12,0x03,0x12,0x90,0x42,0x34,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05, +0x7b,0x02,0x7a,0x00,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x34,0xe0,0xfa, +0xa3,0xe0,0xfb,0x78,0x03,0x12,0x01,0x46,0x00,0x0a,0x12,0x03,0xc7,0x12,0x05,0xe4, +0x12,0x03,0x0a,0x7b,0x02,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x90,0x42,0x34,0xe0,0xfa, +0xa3,0xe0,0xfb,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0xff,0x12,0x03, +0xc7,0x12,0x05,0xf2,0x78,0x03,0x12,0x01,0x46,0x00,0x0a,0x12,0x03,0xc7,0x8b,0x05, +0x8a,0x04,0x7b,0x02,0x7a,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x90,0x42,0x34, +0xe0,0xfa,0xa3,0xe0,0xfb,0x78,0x03,0x12,0x01,0x46,0x00,0x0a,0x12,0x03,0xc7,0x12, +0x05,0xf2,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x06,0x7a,0x00,0x12,0x03,0xc7, +0x12,0x05,0xf2,0x12,0x03,0x12,0x7b,0x02,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x90,0x42, +0x34,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a, +0x00,0x12,0x03,0xc7,0x12,0x05,0xf2,0x78,0x03,0x12,0x01,0x46,0x00,0x0a,0x12,0x03, +0xc7,0x8b,0x05,0x8a,0x04,0x7b,0x02,0x7a,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04, +0x90,0x42,0x34,0xe0,0xfa,0xa3,0xe0,0xfb,0x78,0x03,0x12,0x01,0x46,0x00,0x0a,0x12, +0x03,0xc7,0x12,0x05,0xf2,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x08,0x7a,0x00, +0x12,0x03,0xc7,0x12,0x05,0xf2,0x12,0x03,0x12,0x7b,0x34,0x7a,0x42,0x12,0x02,0x42, +0x02,0x02,0x1f,0x32,0x7b,0x00,0x7a,0x00,0x90,0x00,0x08,0xea,0xf0,0xeb,0xa3,0xf0, +0x7b,0x00,0x7a,0x00,0x90,0x00,0xf0,0xea,0xf0,0xeb,0xa3,0xf0,0x7b,0x02,0x7a,0x00, +0x90,0x45,0xd8,0xea,0xf0,0xeb,0xa3,0xf0,0x7b,0xe8,0x7a,0x00,0x90,0x45,0xda,0xea, +0xf0,0xeb,0xa3,0xf0,0x7b,0x00,0x7a,0x00,0x90,0x45,0xde,0xea,0xf0,0xeb,0xa3,0xf0, +0x90,0x45,0xdc,0xea,0xf0,0xeb,0xa3,0xf0,0x7b,0x12,0x90,0x46,0xc0,0xeb,0xf0,0x7b, +0x01,0x7a,0x00,0x02,0x05,0x9c,0xe4,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x90,0x00, +0x00,0x12,0x06,0xaf,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58, +0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x09,0x12,0x01,0x28,0x60,0x16,0x12,0x03, +0x55,0x06,0x00,0x12,0x05,0xc1,0x12,0x03,0x55,0x06,0x00,0x90,0x00,0x02,0x12,0x24, +0x93,0x02,0x05,0x9c,0x7b,0xe0,0x7a,0x45,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x04, +0x00,0x7a,0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x90,0x42,0x42, +0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x42,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38, +0x01,0x00,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xff,0x7a,0x00,0x12,0x01,0x2c,0x60, +0x03,0x02,0x05,0x9c,0x7b,0xb3,0x7a,0x7f,0x8b,0x05,0x8a,0x04,0x90,0x42,0x42,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x01,0x00,0x7a,0x00,0xeb,0x2b,0xfb,0xea,0x3a, +0xfa,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x42,0x48, +0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x48,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a, +0x04,0x7b,0x04,0x7a,0x00,0x12,0x03,0xc7,0x90,0x42,0x46,0xea,0xf0,0xeb,0xa3,0xf0, +0x90,0x42,0x48,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x90,0x42, +0x44,0xeb,0xf0,0x90,0x42,0x44,0xe0,0xfb,0x8b,0x05,0x7b,0x00,0x12,0x00,0x82,0x70, +0x03,0x02,0x24,0x90,0x90,0x42,0x46,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82, +0xe0,0xfb,0x8b,0x05,0x12,0x03,0x55,0x05,0x00,0x12,0x00,0xa9,0x70,0x19,0x90,0x42, +0x46,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x01,0x00,0x8b,0x05,0x12,0x03,0x55, +0x05,0x00,0x12,0x00,0xdb,0x60,0x03,0x02,0x24,0x7c,0x90,0x42,0x46,0xe0,0xfa,0xa3, +0xe0,0xfb,0x12,0x03,0x3e,0x02,0x00,0x90,0x42,0x4a,0xea,0xf0,0xeb,0xa3,0xf0,0x90, +0x00,0x00,0x12,0x3a,0xeb,0x90,0x42,0x40,0xea,0xf0,0xeb,0xa3,0xf0,0x8b,0x05,0x8a, +0x04,0x7b,0x00,0x7a,0x00,0x12,0x01,0x2c,0x60,0x03,0x02,0x05,0x9c,0x90,0x42,0x40, +0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x02,0x00,0x8b,0x05,0x7b,0x80,0x12,0x01, +0x37,0x60,0x14,0x90,0x42,0x40,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0, +0xfb,0x90,0x00,0x01,0x12,0x39,0xf6,0x90,0x42,0x40,0xe0,0xfa,0xa3,0xe0,0xfb,0x12, +0x03,0x3e,0x04,0x00,0x8b,0x05,0x8a,0x04,0x90,0x42,0x4a,0xe0,0xfa,0xa3,0xe0,0xfb, +0x12,0x01,0x3b,0x60,0x63,0x90,0x42,0x4a,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38, +0x03,0x00,0x12,0x05,0xc1,0x90,0x42,0x4a,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38, +0x04,0x00,0x8b,0x05,0x7b,0x01,0xeb,0x5d,0xfb,0x12,0x05,0xc1,0x90,0x42,0x40,0xe0, +0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x90,0x00,0x03,0x12,0x39,0x8b, +0x90,0x42,0x4a,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x90,0x42,0x40,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00,0x12, +0x03,0xc7,0x12,0x05,0xf2,0x12,0x03,0x12,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x90, +0x42,0x40,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x01, +0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x12,0x03,0x55,0x05,0x00, +0x8b,0x05,0x90,0x42,0x40,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a, +0x04,0x7b,0x02,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x12,0x03, +0x55,0x06,0x00,0x8b,0x05,0x90,0x42,0x40,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8, +0x8b,0x05,0x8a,0x04,0x7b,0x03,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03, +0x0a,0x90,0x42,0x42,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x0d,0x00,0x8b,0x05, +0x7b,0x01,0xeb,0x5d,0xfb,0x12,0x05,0xc1,0x90,0x42,0x40,0xe0,0xfa,0xa3,0xe0,0xfb, +0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x90,0x00,0x02,0x12,0x3a,0x0d,0x90,0x42,0x40,0xe0, +0xfa,0xa3,0xe0,0xfb,0x90,0x00,0x02,0x12,0x3b,0x2b,0x90,0x42,0x40,0xe0,0xfa,0xa3, +0xe0,0xfb,0x90,0x00,0x02,0x12,0x2d,0x5b,0x90,0x42,0x4a,0xe0,0xfa,0xa3,0xe0,0xfb, +0x8b,0x05,0x8a,0x04,0x7b,0x0b,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xcf,0x90,0x42, +0x40,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x90,0x00,0x03,0x12, +0x38,0xd9,0x90,0x42,0x4a,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb, +0x8b,0x05,0x7b,0x04,0x7c,0x00,0x12,0x02,0xc4,0x90,0x42,0x45,0xeb,0xf0,0x90,0x42, +0x45,0xe0,0xfb,0x8b,0x05,0x7b,0x00,0x12,0x01,0x28,0x60,0x12,0x90,0x42,0x42,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x09,0x00,0x90,0x42,0x45,0xeb,0xf0,0x90,0x42, +0x45,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x40,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83, +0x8b,0x82,0xe0,0xfb,0x90,0x00,0x02,0x12,0x39,0xd9,0x80,0x14,0x7b,0x44,0x7a,0x42, +0x12,0x02,0x48,0x01,0x7b,0x46,0x7a,0x42,0x12,0x02,0x1d,0x00,0x04,0x02,0x22,0x23, +0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x7b,0xc1,0x7a,0x4a, +0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0xeb,0x2b,0xfb,0xea,0x3a, +0xfa,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x42,0x51, +0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x51,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a, +0x04,0x7b,0x00,0x7a,0x00,0x12,0x01,0x2c,0x60,0x03,0x02,0x05,0x9c,0x7b,0x5e,0x7a, +0x46,0x90,0x42,0x4e,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x00,0x00,0x12,0x3a,0xeb,0x90, +0x42,0x4c,0xea,0xf0,0xeb,0xa3,0xf0,0x8b,0x05,0x8a,0x04,0x7b,0x00,0x7a,0x00,0x12, +0x01,0x2c,0x60,0x03,0x02,0x05,0x9c,0x90,0x42,0x4c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12, +0x03,0x38,0x02,0x00,0x8b,0x05,0x7b,0x80,0x12,0x01,0x37,0x60,0x14,0x90,0x42,0x4c, +0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x90,0x00,0x01,0x12,0x39, +0xf6,0x90,0x42,0x4c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x3e,0x04,0x00,0x8b,0x05, +0x8a,0x04,0x90,0x42,0x51,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x01,0x3b,0x60,0x63,0x90, +0x42,0x51,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x03,0x00,0x12,0x05,0xc1,0x90, +0x42,0x51,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x04,0x00,0x8b,0x05,0x7b,0x01, +0xeb,0x5d,0xfb,0x12,0x05,0xc1,0x90,0x42,0x4c,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83, +0x8b,0x82,0xe0,0xfb,0x90,0x00,0x03,0x12,0x39,0x8b,0x90,0x42,0x51,0xe0,0xfa,0xa3, +0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x90,0x42,0x4c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05, +0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xf2,0x12, +0x03,0x12,0x7b,0x09,0x8b,0x05,0x90,0x42,0x4c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05, +0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12, +0x03,0x0a,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x90,0x42,0x4c,0xe0,0xfa,0xa3,0xe0, +0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x02,0x7a,0x00,0x12,0x03,0xc7,0x12, +0x05,0xe4,0x12,0x03,0x0a,0x12,0x03,0x55,0x05,0x00,0x8b,0x05,0x90,0x42,0x4c,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x03,0x7a,0x00,0x12, +0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x90,0x42,0x4e,0xe0,0xfa,0xa3,0xe0,0xfb, +0x12,0x03,0x38,0x0d,0x00,0x8b,0x05,0x7b,0x01,0xeb,0x5d,0xfb,0x12,0x05,0xc1,0x90, +0x42,0x4c,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x90,0x00,0x02, +0x12,0x3a,0x0d,0x90,0x42,0x4c,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x00,0x02,0x12,0x3b, +0x2b,0x90,0x42,0x4c,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x00,0x02,0x12,0x2d,0x5b,0x90, +0x42,0x51,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x0b,0x7a,0x00,0x12, +0x03,0xc7,0x12,0x05,0xcf,0x90,0x42,0x4c,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b, +0x82,0xe0,0xfb,0x90,0x00,0x03,0x12,0x38,0xd9,0x90,0x42,0x51,0xe0,0xfa,0xa3,0xe0, +0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x8b,0x05,0x7b,0x04,0x7c,0x00,0x12,0x02,0xc4, +0x90,0x42,0x50,0xeb,0xf0,0x90,0x42,0x50,0xe0,0xfb,0x8b,0x05,0x7b,0x00,0x12,0x01, +0x28,0x60,0x12,0x90,0x42,0x4e,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x09,0x00, +0x90,0x42,0x50,0xeb,0xf0,0x90,0x42,0x50,0xe0,0xfb,0x12,0x05,0xc1,0x90,0x42,0x4c, +0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x90,0x00,0x02,0x12,0x39, +0xd9,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x7b,0xe0,0x7a, +0x45,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0x78,0x03,0x12,0x01, +0x46,0x00,0x0e,0x12,0x03,0xc7,0x12,0x03,0x38,0x0d,0x00,0x8b,0x05,0x7b,0x00,0xeb, +0x5d,0xfb,0x90,0x42,0x57,0xeb,0xf0,0x90,0x45,0xdc,0xe0,0xfa,0xa3,0xe0,0xfb,0x90, +0x42,0x53,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x53,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b, +0x05,0x8a,0x04,0x7b,0x00,0x7a,0x00,0x12,0x01,0x3b,0x70,0x03,0x02,0x27,0xcf,0x90, +0x42,0x53,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x3e,0x08,0x00,0x90,0x42,0x55,0xea, +0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x53,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x01, +0x00,0x8b,0x05,0x12,0x03,0x55,0x04,0x00,0x12,0x01,0x28,0x60,0x4f,0x90,0x42,0x53, +0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x02,0x00,0x8b,0x05,0x12,0x03,0x55,0x05, +0x00,0x12,0x01,0x28,0x60,0x36,0x90,0x42,0x57,0xe0,0x60,0x22,0x7b,0x81,0x8b,0x05, +0x90,0x42,0x53,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b, +0x02,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x80,0x0e,0x90,0x42, +0x53,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x00,0x02,0x12,0x31,0x2c,0x90,0x42,0x55,0xe0, +0xfa,0xa3,0xe0,0xfb,0x90,0x42,0x53,0xea,0xf0,0xeb,0xa3,0xf0,0x02,0x27,0x27,0x02, +0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x02,0x05,0x9c,0x74,0x01, +0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04, +0x12,0x05,0x58,0x90,0x42,0x00,0xe0,0x60,0x4f,0x7b,0xd4,0x7a,0x94,0x8b,0x05,0x8a, +0x04,0x12,0x03,0x55,0x05,0x00,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0, +0xfb,0x8b,0x05,0x7b,0xe0,0x7a,0x45,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x12,0x03, +0x55,0x05,0x00,0x7a,0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x12, +0x05,0xe4,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a,0x00,0x12,0x03,0xc7, +0x12,0x05,0xe4,0x12,0x03,0x0a,0x80,0x3a,0x12,0x03,0x55,0x05,0x00,0x8b,0x05,0x7b, +0xe0,0x7a,0x45,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x05,0x00,0x7a, +0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x05, +0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12, +0x03,0x0a,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03, +0x55,0x04,0x00,0x8b,0x05,0x7b,0x09,0x12,0x01,0x28,0x60,0x03,0x02,0x05,0x9c,0x7b, +0xe0,0x7a,0x45,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0x78,0x03, +0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x90,0x42,0x58,0xea,0xf0,0xeb,0xa3,0xf0, +0x12,0x03,0x55,0x05,0x00,0x8b,0x05,0x90,0x42,0x58,0xe0,0xfa,0xa3,0xe0,0xfb,0x12, +0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4, +0x12,0x03,0x0a,0x90,0x42,0x58,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x00,0x02,0x12,0x3b, +0xe5,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03,0x55, +0x04,0x00,0x8b,0x05,0x7b,0x09,0x12,0x01,0x28,0x60,0x03,0x02,0x05,0x9c,0x12,0x03, +0x61,0x05,0x00,0x12,0x04,0x68,0x00,0x00,0x00,0x02,0x2a,0x54,0x29,0x22,0x29,0x74, +0x29,0xe2,0x12,0x03,0x55,0x07,0x00,0x8b,0x05,0x7b,0x0c,0x12,0x00,0xa9,0x60,0x07, +0x7b,0x0c,0x12,0x03,0x94,0x07,0x00,0x12,0x03,0x55,0x07,0x00,0x8b,0x05,0x7b,0xe0, +0x7a,0x45,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x05,0x00,0x7a,0x00, +0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x05,0xc8, +0x8b,0x05,0x8a,0x04,0x7b,0x05,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03, +0x0a,0x02,0x2a,0x54,0x7b,0xe0,0x7a,0x45,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x04, +0x00,0x7a,0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x90,0x42,0x5a, +0xea,0xf0,0xeb,0xa3,0xf0,0x12,0x03,0x55,0x07,0x00,0xeb,0x33,0xe4,0x95,0xe0,0xfa, +0x8b,0x05,0x8a,0x04,0x7b,0xc0,0x7a,0xff,0x12,0x03,0xc7,0x8b,0x05,0x8a,0x04,0x7b, +0x01,0x12,0x02,0xdf,0x8b,0x05,0x90,0x42,0x5a,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05, +0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x08,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12, +0x03,0x0a,0x90,0x42,0x5a,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x00,0x02,0x12,0x3b,0xe5, +0x80,0x72,0x7b,0xe0,0x7a,0x45,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x04,0x00,0x7a, +0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x90,0x42,0x5a,0xea,0xf0, +0xeb,0xa3,0xf0,0x12,0x03,0x55,0x07,0x00,0xeb,0x33,0xe4,0x95,0xe0,0xfa,0x8b,0x05, +0x8a,0x04,0x7b,0xc0,0x7a,0xff,0x12,0x03,0xc7,0x8b,0x05,0x8a,0x04,0x7b,0x80,0x7a, +0x00,0x12,0x03,0xd8,0x8b,0x05,0x8a,0x04,0x90,0x42,0x5a,0xe0,0xfa,0xa3,0xe0,0xfb, +0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x06,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05, +0xf2,0x12,0x03,0x12,0x90,0x42,0x5a,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x00,0x02,0x12, +0x3b,0xe5,0x80,0x00,0x02,0x05,0x9c,0xe4,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x90, +0x00,0x00,0x12,0x1d,0x3e,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05, +0x58,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0xf0,0x7a,0x00, +0x12,0x01,0x2c,0x60,0x09,0x7b,0x01,0x90,0x46,0xc1,0xeb,0xf0,0x80,0x5b,0x90,0x46, +0xc1,0xe0,0xfb,0x8b,0x05,0x7b,0x02,0x12,0x01,0x28,0x60,0x0a,0x7b,0xc1,0x7a,0x46, +0x12,0x02,0x42,0x01,0x80,0x43,0x90,0x46,0xc1,0xe0,0xfb,0x8b,0x05,0x7b,0x05,0x12, +0x01,0x18,0x60,0x35,0x7b,0x44,0x7a,0x88,0x8b,0x05,0x8a,0x04,0x90,0x46,0xc1,0xe0, +0xfb,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x8b,0x05,0x12,0x03, +0x55,0x04,0x00,0x12,0x01,0x28,0x60,0x0a,0x7b,0xc1,0x7a,0x46,0x12,0x02,0x42,0x01, +0x80,0x07,0x7b,0xe7,0x90,0x46,0xc1,0xeb,0xf0,0x90,0x46,0xc1,0xe0,0xfb,0x8b,0x05, +0x7b,0x06,0x12,0x01,0x28,0x60,0x06,0x90,0x00,0x00,0x12,0x1a,0xf2,0x02,0x05,0x9c, +0x74,0x02,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03,0x61,0x04,0x00,0x8a,0x83, +0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x12,0x03,0x61,0x06,0x00,0x12, +0x00,0x8e,0x60,0x26,0x12,0x03,0x61,0x04,0x00,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a, +0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04, +0x7b,0x80,0x7a,0xff,0x12,0x03,0xc7,0x02,0x05,0x9c,0x7b,0x00,0x7a,0x00,0x90,0x42, +0x5c,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x5c,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05, +0x8a,0x04,0x7b,0x03,0x7a,0x00,0x12,0x00,0xc4,0x70,0x03,0x02,0x2d,0x2a,0x12,0x03, +0x61,0x04,0x00,0x8b,0x05,0x8a,0x04,0x90,0x42,0x5c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12, +0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xf2, +0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x12, +0x03,0x61,0x06,0x00,0x12,0x00,0x8e,0x70,0x03,0x02,0x2d,0x1f,0x12,0x03,0x61,0x04, +0x00,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00,0x12,0x03,0xc7,0x8b,0x05,0x8a,0x04, +0x90,0x42,0x5c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0, +0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x7b,0x80,0x7a,0xff,0x12,0x03,0xc7,0x8b,0x05, +0x8a,0x04,0x12,0x03,0x61,0x04,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x90,0x42, +0x5c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xc7,0x12,0x05,0xf2,0x8a,0x83,0x8b,0x82, +0xe0,0xfb,0x7a,0x00,0x12,0x04,0x1c,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x12,0x03, +0x61,0x08,0x00,0x12,0x03,0xc7,0x12,0x05,0xf2,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04, +0x12,0x03,0x61,0x06,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00, +0x12,0x03,0xc7,0x12,0x05,0xf2,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x90,0x42,0x5c, +0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a,0x00, +0x12,0x03,0xc7,0x12,0x05,0xf2,0x12,0x03,0xc7,0x12,0x05,0xf2,0x8a,0x83,0x8b,0x82, +0xe0,0xfb,0x7a,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x12,0x03,0x61,0x08,0x00, +0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05, +0xf2,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x90,0x42,0x5c,0xe0,0xfa,0xa3,0xe0,0xfb, +0x12,0x03,0xc7,0x12,0x05,0xf2,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x12,0x03, +0xcf,0x12,0x05,0xf2,0x12,0x03,0xd8,0x12,0x05,0xf2,0x12,0x05,0xd6,0x8b,0x05,0x8a, +0x04,0x12,0x03,0x61,0x06,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x90,0x42,0x5c, +0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a,0x00, +0x12,0x03,0xc7,0x12,0x05,0xf2,0x12,0x03,0xc7,0x12,0x05,0xf2,0x8a,0x83,0x8b,0x82, +0xe0,0xfb,0x7a,0x00,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x12,0x03,0x61,0x08,0x00, +0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x90,0x42,0x5c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12, +0x03,0xc7,0x12,0x05,0xf2,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x12,0x03,0xcf, +0x12,0x05,0xf2,0x12,0x03,0xed,0x12,0x05,0xf2,0x12,0x03,0xc7,0x02,0x05,0x9c,0x7b, +0x5c,0x7a,0x42,0x12,0x02,0x42,0x02,0x02,0x2b,0x56,0x12,0x03,0x61,0x04,0x00,0x8b, +0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00,0x12,0x03,0xc7,0x8b,0x05,0x8a,0x04,0x7b,0x03, +0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x8b,0x05,0x8a, +0x04,0x7b,0x80,0x7a,0xff,0x12,0x03,0xc7,0x02,0x05,0x9c,0x74,0x02,0x78,0x00,0x79, +0x04,0x12,0x05,0x58,0x12,0x03,0x61,0x04,0x00,0x12,0x03,0x3e,0x04,0x00,0x12,0x03, +0x38,0x02,0x00,0x90,0x42,0x5e,0xeb,0xf0,0x7b,0x00,0x7a,0x00,0x90,0x42,0x61,0xea, +0xf0,0xeb,0xa3,0xf0,0x7b,0xb3,0x7a,0x80,0x8b,0x05,0x8a,0x04,0x90,0x42,0x5e,0xe0, +0xfb,0x12,0x05,0xd6,0x8b,0x05,0x7b,0x04,0x7c,0x00,0x12,0x02,0xc4,0x12,0x05,0xf2, +0x7a,0x00,0xeb,0x2b,0xfb,0xea,0x3a,0xfa,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0, +0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x12,0x03,0x61,0x04,0x00,0x12,0x03,0x38, +0x03,0x00,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x7a,0x00,0x90, +0x42,0x5f,0xea,0xf0,0xeb,0xa3,0xf0,0x12,0x03,0x61,0x04,0x00,0x12,0x03,0x38,0x01, +0x00,0x8b,0x05,0x7b,0x09,0x12,0x01,0x37,0x70,0x03,0x02,0x2e,0x7b,0x12,0x03,0x61, +0x04,0x00,0x12,0x03,0x3e,0x04,0x00,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x8b,0x05,0x7b, +0x0f,0xeb,0x5d,0xfb,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x90,0x42,0x5e,0xe0,0xfb,0x12, +0x05,0xd6,0x8b,0x05,0x7b,0x0f,0xeb,0x5d,0xfb,0x12,0x05,0xf2,0x7a,0x00,0x12,0x05, +0xd6,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x12,0x02,0xdf,0x12,0x05,0xf2,0x12,0x03,0xc7, +0x8b,0x05,0x8a,0x04,0x7b,0x5f,0x7a,0x42,0x12,0x00,0x53,0x12,0x03,0x61,0x04,0x00, +0x12,0x03,0x38,0x02,0x00,0x7a,0x00,0x12,0x05,0xcf,0x7b,0xcb,0x7a,0x80,0x8b,0x05, +0x8a,0x04,0x12,0x03,0x61,0x06,0x00,0x12,0x03,0x3e,0x04,0x00,0x12,0x03,0x38,0x10, +0x00,0x7a,0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x08,0x12,0x03,0xc7,0x90,0x00,0x04, +0x12,0x2b,0x00,0x90,0x42,0x61,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x5f,0xe0,0xfa, +0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x7e,0x7a,0x00,0x12,0x00,0x8e,0x60,0x0c, +0x7b,0x7e,0x7a,0x00,0x90,0x42,0x5f,0xea,0xf0,0xeb,0xa3,0xf0,0x12,0x03,0x61,0x04, +0x00,0x12,0x03,0x3e,0x04,0x00,0x12,0x03,0x38,0x0a,0x00,0x8b,0x05,0x7b,0x01,0x7c, +0x00,0x12,0x02,0xc4,0x7a,0x00,0x12,0x04,0x1c,0x8b,0x05,0x8a,0x04,0x7b,0x7f,0x7a, +0x00,0x12,0x03,0xc7,0x90,0x42,0x63,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x61,0xe0, +0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x63,0x7a,0x42,0x12,0x00,0x53,0x90, +0x42,0x63,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x7f,0x7a,0x00,0x12, +0x00,0x8e,0x60,0x0c,0x7b,0x7f,0x7a,0x00,0x90,0x42,0x63,0xea,0xf0,0xeb,0xa3,0xf0, +0x90,0x42,0x63,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x01,0x7a,0x00, +0x12,0x00,0xc4,0x60,0x0c,0x7b,0x01,0x7a,0x00,0x90,0x42,0x63,0xea,0xf0,0xeb,0xa3, +0xf0,0x7b,0x7f,0x7a,0x00,0x8b,0x05,0x8a,0x04,0x90,0x42,0x5f,0xe0,0xfa,0xa3,0xe0, +0xfb,0x12,0x03,0xcf,0x8b,0x05,0x8a,0x04,0x90,0x42,0x63,0xe0,0xfa,0xa3,0xe0,0xfb, +0x12,0x03,0xd8,0x8b,0x05,0x8a,0x04,0x7b,0x7f,0x7a,0x00,0x12,0x03,0xed,0x90,0x42, +0x63,0xea,0xf0,0xeb,0xa3,0xf0,0x7b,0xe0,0x7a,0x45,0x8b,0x05,0x8a,0x04,0x12,0x03, +0x61,0x04,0x00,0x12,0x03,0x38,0x01,0x00,0x7a,0x00,0x78,0x03,0x12,0x01,0x46,0x00, +0x0e,0x12,0x03,0xc7,0x12,0x03,0x38,0x0c,0x00,0x7a,0x00,0x12,0x04,0x1c,0x8b,0x05, +0x8a,0x04,0x7b,0x7f,0x7a,0x00,0x12,0x03,0xc7,0x8b,0x05,0x8a,0x04,0x90,0x42,0x63, +0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xd8,0x8b,0x05,0x8a,0x04,0x7b,0x7f,0x7a,0x00, +0x12,0x03,0xed,0x90,0x42,0x63,0xea,0xf0,0xeb,0xa3,0xf0,0x7b,0x7f,0x7a,0x00,0x8b, +0x05,0x8a,0x04,0x90,0x42,0x63,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0xcf,0x90,0x42, +0x63,0xea,0xf0,0xeb,0xa3,0xf0,0x12,0x03,0x61,0x04,0x00,0x12,0x03,0x38,0x01,0x00, +0x8b,0x05,0x7b,0x09,0x12,0x01,0x28,0x60,0x12,0x90,0x46,0xc0,0xe0,0xfb,0x7a,0x00, +0x8b,0x05,0x8a,0x04,0x7b,0x63,0x7a,0x42,0x12,0x00,0x53,0x90,0x42,0x63,0xe0,0xfa, +0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x7e,0x7a,0x00,0x12,0x00,0x8e,0x60,0x0c, +0x7b,0x7e,0x7a,0x00,0x90,0x42,0x63,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x63,0xe0, +0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x00,0x7a,0x00,0x12,0x00,0xc4,0x60, +0x0c,0x7b,0x00,0x7a,0x00,0x90,0x42,0x63,0xea,0xf0,0xeb,0xa3,0xf0,0x7b,0x01,0x12, +0x05,0xc1,0x90,0x42,0x63,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc1,0x12,0x03,0x61, +0x06,0x00,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x90,0x00,0x03,0x12,0x39,0xbc,0x02,0x05, +0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x7b,0x4a,0x7a,0x88,0x8b,0x05, +0x8a,0x04,0x7b,0xe0,0x7a,0x45,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55, +0x06,0x00,0x7a,0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x12,0x05, +0xf2,0x12,0x03,0x38,0x03,0x00,0x7a,0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0, +0xfb,0x8b,0x05,0x7b,0xe0,0x7a,0x45,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x12,0x03, +0x55,0x05,0x00,0x7a,0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x12, +0x05,0xe4,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x0c,0x7a,0x00,0x12,0x03,0xc7, +0x12,0x05,0xe4,0x12,0x03,0x0a,0x90,0x45,0xdc,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x42, +0x65,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x65,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05, +0x8a,0x04,0x7b,0x00,0x7a,0x00,0x12,0x01,0x3b,0x60,0x3e,0x90,0x42,0x65,0xe0,0xfa, +0xa3,0xe0,0xfb,0x12,0x03,0x38,0x01,0x00,0x8b,0x05,0x12,0x03,0x55,0x04,0x00,0x12, +0x01,0x28,0x60,0x0e,0x90,0x42,0x65,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x00,0x02,0x12, +0x2d,0x5b,0x90,0x42,0x65,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x3e,0x08,0x00,0x90, +0x42,0x65,0xea,0xf0,0xeb,0xa3,0xf0,0x80,0xad,0x02,0x05,0x9c,0x74,0x02,0x78,0x00, +0x79,0x04,0x12,0x05,0x58,0x12,0x03,0x61,0x04,0x00,0x8a,0x83,0x8b,0x82,0xe0,0xfb, +0x90,0x00,0x01,0x12,0x39,0xf6,0x7b,0x80,0x8b,0x05,0x12,0x03,0x61,0x04,0x00,0x12, +0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x02,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4, +0x12,0x03,0x0a,0x12,0x03,0x61,0x04,0x00,0x12,0x05,0xcf,0x7b,0xdc,0x7a,0x45,0x90, +0x00,0x04,0x12,0x3a,0x87,0x12,0x03,0x61,0x04,0x00,0x12,0x05,0xcf,0x7b,0xd8,0x7a, +0x45,0x90,0x00,0x04,0x12,0x3a,0x30,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04, +0x12,0x05,0x58,0x90,0x45,0xdc,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x42,0x67,0xea,0xf0, +0xeb,0xa3,0xf0,0x90,0x42,0x67,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b, +0x00,0x7a,0x00,0x12,0x01,0x3b,0x60,0x4e,0x90,0x42,0x67,0xe0,0xfa,0xa3,0xe0,0xfb, +0x12,0x03,0x3e,0x08,0x00,0x90,0x42,0x69,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x67, +0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x01,0x00,0x8b,0x05,0x12,0x03,0x55,0x04, +0x00,0x12,0x01,0x28,0x60,0x0e,0x90,0x42,0x67,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x00, +0x02,0x12,0x31,0x2c,0x90,0x42,0x69,0xe0,0xfa,0xa3,0xe0,0xfb,0x90,0x42,0x67,0xea, +0xf0,0xeb,0xa3,0xf0,0x80,0x9d,0x02,0x05,0x9c,0xe4,0x78,0x00,0x79,0x04,0x12,0x05, +0x58,0x7b,0x00,0x90,0x42,0x6b,0xeb,0xf0,0x90,0x42,0x6b,0xe0,0xfb,0x8b,0x05,0x7b, +0x10,0x12,0x00,0xdb,0x60,0x15,0x90,0x42,0x6b,0xe0,0xfb,0x90,0x00,0x01,0x12,0x31, +0x8a,0x7b,0x6b,0x7a,0x42,0x12,0x02,0x42,0x01,0x80,0xdd,0x02,0x05,0x9c,0x74,0x01, +0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x7b,0xe0,0x7a,0x45,0x8b,0x05,0x8a,0x04,0x12, +0x03,0x55,0x04,0x00,0x7a,0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7, +0x90,0x42,0x6c,0xea,0xf0,0xeb,0xa3,0xf0,0x7b,0x00,0x8b,0x05,0x90,0x42,0x6c,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x02,0x7a,0x00,0x12, +0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x64,0x8b,0x05,0x90,0x42,0x6c,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x03,0x7a,0x00,0x12, +0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x40,0x8b,0x05,0x90,0x42,0x6c,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x04,0x7a,0x00,0x12, +0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x02,0x8b,0x05,0x90,0x42,0x6c,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x05,0x7a,0x00,0x12, +0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x00,0x7a,0x00,0x8b,0x05,0x8a,0x04, +0x90,0x42,0x6c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xd6,0x8b,0x05,0x8a,0x04,0x7b, +0x06,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xf2,0x12,0x03,0x12,0x7b,0x00,0x8b,0x05, +0x90,0x42,0x6c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b, +0x08,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x00,0x8b,0x05, +0x90,0x42,0x6c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b, +0x09,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x00,0x8b,0x05, +0x90,0x42,0x6c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b, +0x0d,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x7b,0x00,0x7a,0x00, +0x8b,0x05,0x8a,0x04,0x90,0x42,0x6c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xd6,0x8b, +0x05,0x8a,0x04,0x7b,0x0a,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xf2,0x12,0x03,0x12, +0x90,0x88,0xae,0xe0,0xfb,0x8b,0x05,0x90,0x42,0x6c,0xe0,0xfa,0xa3,0xe0,0xfb,0x12, +0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x0c,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4, +0x12,0x03,0x0a,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x02, +0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x02,0x05,0x9c,0x74,0x01, +0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04, +0x12,0x05,0x58,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x02, +0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x02,0x05,0x9c,0x74,0x01, +0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x02,0x05,0x9c,0x74,0x01,0x78,0x00,0x79,0x04, +0x12,0x05,0x58,0x7b,0x33,0x7a,0x88,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x05,0x00, +0x12,0x05,0xd6,0x8b,0x05,0x7b,0x03,0x7c,0x00,0x12,0x02,0xc4,0x12,0x05,0xf2,0x7a, +0x00,0x12,0x03,0xc7,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x90,0x42,0x70,0xeb,0xf0,0x90, +0x42,0x70,0xe0,0xfb,0x8b,0x05,0x7b,0xe0,0x7a,0x45,0x12,0x05,0xc8,0x8b,0x05,0x8a, +0x04,0x12,0x03,0x55,0x05,0x00,0x7a,0x00,0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12, +0x03,0xc7,0x12,0x05,0xe4,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b,0x09,0x7a,0x00, +0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x90,0x45,0xdc,0xe0,0xfa,0xa3,0xe0, +0xfb,0x90,0x42,0x6e,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x6e,0xe0,0xfa,0xa3,0xe0, +0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x00,0x7a,0x00,0x12,0x01,0x3b,0x60,0x4c,0x90,0x42, +0x6e,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x01,0x00,0x8b,0x05,0x12,0x03,0x55, +0x04,0x00,0x12,0x01,0x28,0x60,0x1c,0x90,0x42,0x70,0xe0,0xfb,0x12,0x05,0xc1,0x90, +0x42,0x6e,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x90,0x00,0x02, +0x12,0x0a,0xaa,0x90,0x42,0x6e,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x3e,0x08,0x00, +0x90,0x42,0x6e,0xea,0xf0,0xeb,0xa3,0xf0,0x80,0x9f,0x02,0x05,0x9c,0x74,0x01,0x78, +0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03,0x55,0x05,0x00,0x7a,0x00,0x12,0x04,0x68, +0x00,0x07,0x00,0x5b,0x36,0x9b,0x35,0xb0,0x36,0x9b,0x36,0x9b,0x36,0x33,0x36,0x9b, +0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b, +0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b, +0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b, +0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b, +0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b, +0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b, +0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x61,0x36,0x9b,0x36,0x9b,0x36,0x9b, +0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b, +0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b, +0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x9b,0x36,0x86, +0x7b,0xe0,0x7a,0x45,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0x78, +0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x8b,0x05,0x8a,0x04,0x7b,0x03,0x7a, +0x00,0x12,0x03,0xc7,0x90,0x42,0x71,0xea,0xf0,0xeb,0xa3,0xf0,0x12,0x03,0x61,0x06, +0x00,0x8b,0x05,0x8a,0x04,0x7b,0x07,0x12,0x02,0xc4,0x8b,0x05,0x8a,0x04,0x7b,0x7c, +0x7a,0x00,0x12,0x02,0xfa,0x90,0x42,0x73,0xeb,0xf0,0x90,0x42,0x71,0xe0,0xfa,0xa3, +0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb,0x8b,0x05,0x90,0x42,0x73,0xe0,0xfb,0x12, +0x01,0x37,0x60,0x1d,0x90,0x42,0x73,0xe0,0xfb,0x8b,0x05,0x90,0x42,0x71,0xe0,0xfa, +0xa3,0xe0,0xfb,0x12,0x03,0x0a,0x12,0x03,0x55,0x04,0x00,0x90,0x00,0x01,0x12,0x30, +0x51,0x80,0x68,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x09,0x12,0x01,0x28,0x60, +0x02,0x80,0x58,0x12,0x03,0x61,0x06,0x00,0x8b,0x05,0x8a,0x04,0x7b,0x07,0x12,0x02, +0xc4,0x12,0x05,0xc1,0x12,0x03,0x55,0x05,0x00,0x90,0x00,0x02,0x12,0x34,0x0a,0x80, +0x3a,0x12,0x03,0x55,0x04,0x00,0x8b,0x05,0x7b,0x09,0x12,0x01,0x28,0x60,0x02,0x80, +0x2a,0x12,0x03,0x61,0x06,0x00,0x12,0x05,0xc1,0x12,0x03,0x55,0x05,0x00,0x90,0x00, +0x02,0x12,0x37,0xa0,0x80,0x15,0x12,0x03,0x61,0x06,0x00,0x12,0x05,0xc1,0x12,0x03, +0x55,0x05,0x00,0x90,0x00,0x02,0x12,0x36,0x9e,0x80,0x00,0x02,0x05,0x9c,0x74,0x01, +0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03,0x55,0x05,0x00,0x8b,0x05,0x7b,0x40, +0x12,0x01,0x0b,0x60,0x09,0x7b,0x01,0x90,0x42,0x79,0xeb,0xf0,0x80,0x07,0x7b,0x00, +0x90,0x42,0x79,0xeb,0xf0,0x90,0x42,0x79,0xe0,0xfb,0x90,0x42,0x76,0xeb,0xf0,0x7b, +0xe0,0x7a,0x45,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x04,0x00,0x7a,0x00,0x78,0x03, +0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x90,0x42,0x77,0xea,0xf0,0xeb,0xa3,0xf0, +0x90,0x42,0x77,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x0d,0x00,0x8b,0x05,0x7b, +0xfe,0xeb,0x5d,0xfb,0x8b,0x05,0x90,0x42,0x76,0xe0,0xfb,0xeb,0x4d,0xfb,0x8b,0x05, +0x90,0x42,0x77,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a,0x04,0x7b, +0x0d,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x90,0x45,0xdc,0xe0, +0xfa,0xa3,0xe0,0xfb,0x90,0x42,0x74,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x74,0xe0, +0xfa,0xa3,0xe0,0xfb,0x8b,0x05,0x8a,0x04,0x7b,0x00,0x7a,0x00,0x12,0x01,0x3b,0x60, +0x4c,0x90,0x42,0x74,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x01,0x00,0x8b,0x05, +0x12,0x03,0x55,0x04,0x00,0x12,0x01,0x28,0x60,0x1c,0x90,0x42,0x76,0xe0,0xfb,0x12, +0x05,0xc1,0x90,0x42,0x74,0xe0,0xfa,0xa3,0xe0,0xfb,0x8a,0x83,0x8b,0x82,0xe0,0xfb, +0x90,0x00,0x02,0x12,0x3a,0x0d,0x90,0x42,0x74,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03, +0x3e,0x08,0x00,0x90,0x42,0x74,0xea,0xf0,0xeb,0xa3,0xf0,0x80,0x9f,0x02,0x05,0x9c, +0x74,0x01,0x78,0x00,0x79,0x04,0x12,0x05,0x58,0x12,0x03,0x55,0x05,0x00,0x8b,0x05, +0x7b,0x40,0x12,0x01,0x0b,0x60,0x09,0x7b,0x00,0x90,0x42,0x7f,0xeb,0xf0,0x80,0x07, +0x7b,0x00,0x90,0x42,0x7f,0xeb,0xf0,0x90,0x42,0x7f,0xe0,0xfb,0x90,0x42,0x7a,0xeb, +0xf0,0x7b,0xe0,0x7a,0x45,0x8b,0x05,0x8a,0x04,0x12,0x03,0x55,0x04,0x00,0x7a,0x00, +0x78,0x03,0x12,0x01,0x46,0x00,0x0e,0x12,0x03,0xc7,0x90,0x42,0x7b,0xea,0xf0,0xeb, +0xa3,0xf0,0x90,0x42,0x7b,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x0d,0x00,0x8b, +0x05,0x7b,0xff,0xeb,0x5d,0xfb,0x8b,0x05,0x90,0x42,0x7a,0xe0,0xfb,0xeb,0x4d,0xfb, +0x8b,0x05,0x90,0x42,0x7b,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x05,0xc8,0x8b,0x05,0x8a, +0x04,0x7b,0x0d,0x7a,0x00,0x12,0x03,0xc7,0x12,0x05,0xe4,0x12,0x03,0x0a,0x90,0x42, +0x7a,0xe0,0x60,0x03,0x02,0x38,0xb0,0x90,0x45,0xdc,0xe0,0xfa,0xa3,0xe0,0xfb,0x90, +0x42,0x7d,0xea,0xf0,0xeb,0xa3,0xf0,0x90,0x42,0x7d,0xe0,0xfa,0xa3,0xe0,0xfb,0x8b, +0x05,0x8a,0x04,0x7b,0x00,0x7a,0x00,0x12,0x01,0x3b,0x60,0x54,0x90,0x42,0x7d,0xe0, +0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38,0x01,0x00,0x8b,0x05,0x12,0x03,0x55,0x04,0x00, +0x12,0x01,0x28,0x60,0x24,0x90,0x42,0x7d,0xe0,0xfa,0xa3,0xe0,0xfb,0x12,0x03,0x38, +0x02,0x00,0x8b,0x05,0x7b,0x81,0x12,0x01,0x28,0x60,0x0e,0x90,0x42,0x7d,0xe0,0xfa, +0xa3,0xe0,0xfb,0x90,0x00,0x02,0x12,0x31,0x2c,0x90,0x42,0x7d,0xe0,0xfa,0xa3,0xe0, +0xfb,0x12,0x03,0x3e,0x08,0x00,0x90,0x42,0x7d,0xea,0xf0,0xeb,0xa3,0xf0,0x80,0x97, +0x02,0x05,0x9c,0x8b,0xf0,0x12,0x05,0xdd,0xa9,0x03,0xa8,0xf0,0x80,0x00,0xc2,0xb4, +0x90,0xff,0xc4,0xe8,0xf0,0xa3,0xe9,0x00,0xf0,0x74,0x82,0x28,0xf5,0x82,0x74,0x42, +0x34,0x00,0xf5,0x83,0xe9,0xf0,0xd2,0xb4,0x22,0xc2,0xb4,0x7c,0x03,0x7d,0xe8,0x90, +0xff,0xc0,0xe0,0x30,0xe1,0x04,0xdd,0xf7,0xdc,0xf5,0xd2,0xb4,0x12,0x05,0xf2,0xeb, +0x24,0x80,0xf5,0xf0,0xa8,0xf0,0x8c,0x83,0x8d,0x82,0xe0,0xa3,0xac,0x83,0xad,0x82, +0x54,0x3f,0xf9,0x12,0x38,0xbe,0xe5,0xf0,0x24,0x18,0xf5,0xf0,0x7a,0x03,0xa8,0xf0, +0x8c,0x83,0x8d,0x82,0xe0,0xa3,0xac,0x83,0xad,0x82,0xf9,0x12,0x38,0xbe,0xe5,0xf0, +0x24,0x18,0xf5,0xf0,0xda,0xe8,0xa8,0xf0,0x8c,0x83,0x8d,0x82,0xe0,0xa3,0xac,0x83, +0xad,0x82,0x54,0x07,0xf9,0x02,0x38,0xbe,0x22,0xc0,0x03,0x12,0x05,0xe4,0x12,0x05, +0xeb,0xd0,0x00,0xa9,0x05,0x80,0x00,0xe8,0x24,0x38,0xfc,0x74,0x82,0x2c,0xf5,0x82, +0x74,0x42,0x34,0x00,0xf5,0x83,0xe0,0x54,0x08,0xfd,0xe9,0x54,0x0f,0xc4,0x4d,0xfd, +0xeb,0x33,0xea,0x33,0x54,0x07,0x4d,0xf9,0xa8,0x04,0x12,0x38,0xbe,0xec,0xc3,0x94, +0x18,0xfc,0x24,0x82,0xf5,0x82,0x74,0x42,0x34,0x00,0xf5,0x83,0xe0,0x54,0x01,0xfd, +0xeb,0xc3,0x33,0x4d,0xa8,0x04,0xf9,0x02,0x38,0xbe,0x22,0x12,0x05,0xe4,0xc0,0x05, +0x12,0x05,0xe4,0xaa,0x05,0xd0,0x01,0xa8,0x03,0x80,0x00,0xe8,0x24,0x20,0xf8,0x74, +0x82,0x28,0xf5,0x82,0x74,0x42,0x34,0x00,0xf5,0x83,0xe0,0x54,0xfe,0x49,0xf9,0x12, +0x38,0xbe,0xe8,0x24,0xe8,0xf8,0xa9,0x02,0x02,0x38,0xbe,0x22,0x12,0x05,0xe4,0xc0, +0x05,0x12,0x05,0xe4,0xaa,0x05,0xd0,0x01,0xa8,0x03,0x80,0x00,0xe8,0x24,0x50,0xf8, +0xea,0x13,0xe9,0x33,0xf9,0x02,0x38,0xbe,0x22,0x12,0x05,0xe4,0xeb,0x24,0x68,0xf8, +0x74,0x82,0x28,0xf5,0x82,0x74,0x42,0x34,0x00,0xf5,0x83,0xe0,0x54,0x30,0x44,0x80, +0x4d,0xf9,0x02,0x38,0xbe,0x22,0xeb,0x24,0x68,0xf8,0x74,0x82,0x28,0xf5,0x82,0x74, +0x42,0x34,0x00,0xf5,0x83,0xe0,0x54,0x7f,0xf9,0x02,0x38,0xbe,0x22,0x12,0x05,0xe4, +0x80,0x00,0xeb,0x24,0x38,0xf8,0x74,0x82,0x28,0xf5,0x82,0x74,0x42,0x34,0x00,0xf5, +0x83,0xe0,0xbd,0x00,0x05,0x54,0xf7,0x02,0x3a,0x2c,0x44,0x08,0xf9,0x02,0x38,0xbe, +0x12,0x05,0xf2,0xac,0x05,0x02,0x3a,0x38,0x75,0xa0,0x00,0xeb,0x24,0x01,0xf5,0x82, +0xea,0x34,0x00,0xf5,0x83,0xe0,0x70,0x1f,0xec,0xf0,0xe5,0x82,0x24,0x02,0xf5,0x82, +0xe5,0x83,0x34,0x00,0xf5,0x83,0xec,0xf0,0xec,0x24,0x07,0xf8,0xe4,0xf2,0xec,0x24, +0x09,0xf8,0xe4,0xf2,0x02,0x3a,0x86,0xeb,0x24,0x03,0xf5,0x82,0xea,0x34,0x00,0xf5, +0x83,0xe0,0xf9,0x24,0x09,0xf8,0xec,0xf2,0xec,0x24,0x09,0xf8,0xe4,0xf2,0xec,0x24, +0x07,0xf8,0xe9,0xf2,0xec,0xf0,0x22,0x12,0x05,0xf2,0xac,0x05,0x02,0x3a,0x8f,0x75, +0xa0,0x00,0xec,0x24,0x09,0xf8,0xe2,0x70,0x13,0xeb,0x24,0x03,0xf5,0x82,0xea,0x34, +0x00,0xf5,0x83,0xec,0x24,0x07,0xf8,0xe2,0xf0,0x02,0x3a,0xbe,0xec,0x24,0x07,0xf8, +0xe2,0xf5,0xf0,0xec,0x24,0x09,0xf8,0xe2,0x24,0x07,0xf8,0xe5,0xf0,0xf2,0xec,0x24, +0x07,0xf8,0xe2,0x70,0x13,0xeb,0x24,0x01,0xf5,0x82,0xea,0x34,0x00,0xf5,0x83,0xec, +0x24,0x09,0xf8,0xe2,0xf0,0x02,0x3a,0xea,0xec,0x24,0x09,0xf8,0xe2,0xf5,0xf0,0xec, +0x24,0x07,0xf8,0xe2,0x24,0x09,0xf8,0xe5,0xf0,0xf2,0x22,0x90,0x45,0xd9,0xe0,0x60, +0x16,0xf5,0x25,0x7a,0x45,0x7b,0xd8,0xfc,0x12,0x3a,0x8f,0x7a,0x45,0x7b,0xdc,0xac, +0x25,0x12,0x3a,0x38,0x02,0x3b,0x26,0x90,0x45,0xdd,0xe0,0x60,0x16,0xf5,0x25,0x7a, +0x45,0x7b,0xdc,0xfc,0x12,0x3a,0x8f,0x7a,0x45,0x7b,0xdc,0xac,0x25,0x12,0x3a,0x38, +0x02,0x3b,0x26,0x75,0x25,0x00,0x7a,0x00,0xab,0x25,0x22,0x75,0xa0,0x00,0xeb,0x24, +0x02,0xf8,0xe2,0xc3,0x13,0xfc,0xe4,0x92,0xe7,0xfd,0xeb,0x24,0x05,0xf8,0xe2,0x24, +0x05,0x92,0xd5,0xf5,0x82,0xeb,0x24,0x04,0xf8,0xe2,0xa2,0xd5,0x34,0x00,0xf5,0x83, +0xe0,0x30,0xe7,0x04,0x7c,0x1e,0x7d,0x00,0xe5,0x82,0x24,0x03,0xf5,0x82,0xe5,0x83, +0x34,0x00,0xf5,0x83,0xe0,0xf9,0xa3,0xe0,0x2d,0xfd,0xe9,0x3c,0xfc,0xeb,0x24,0x01, +0xf8,0xe2,0x75,0xf0,0x0e,0xa4,0x24,0xea,0xf5,0x82,0xe5,0xf0,0x34,0x45,0xf5,0x83, +0xe0,0xf9,0xa3,0xe0,0x2d,0xfd,0xe9,0x3c,0xfc,0xed,0xc3,0x94,0x00,0xec,0x94,0x60, +0x40,0x04,0x7c,0x5f,0x7d,0xff,0xed,0x33,0xec,0x33,0x75,0xf0,0x0c,0x84,0x94,0x08, +0xfa,0xed,0xc3,0x33,0x24,0xca,0xf5,0x82,0xe5,0xf0,0x34,0x88,0xf5,0x83,0xe0,0xfc, +0xa3,0xe0,0xfd,0xeb,0x24,0x00,0xf8,0xe2,0xf8,0xa9,0x02,0xaa,0x04,0xab,0x05,0x12, +0x39,0x47,0x22,0x12,0x05,0xe4,0x80,0x00,0xbb,0x09,0x01,0x22,0xeb,0x75,0xf0,0x0e, +0xa4,0x24,0xe4,0xf5,0x82,0xe5,0xf0,0x34,0x45,0xf5,0x83,0xed,0xf0,0xa9,0x03,0xaa, +0x83,0xab,0x82,0x80,0x12,0x8b,0x82,0x8a,0x83,0xe0,0xf9,0x80,0x00,0x74,0x04,0x2b, +0xf5,0x82,0xea,0x34,0x00,0xf5,0x83,0xe0,0x24,0xc0,0xc3,0x33,0x92,0xd5,0x50,0x02, +0xf4,0x04,0xfb,0x89,0x24,0xe5,0x82,0x24,0x01,0xf5,0x82,0xe5,0x83,0x34,0x00,0xf5, +0x83,0xe0,0x8b,0xf0,0xa4,0x30,0xd5,0x0a,0xf4,0x24,0x01,0xc5,0xf0,0xf4,0x34,0x00, +0xc5,0xf0,0xaa,0xf0,0xfb,0x74,0x01,0x25,0x82,0xf5,0x82,0xe5,0x83,0x34,0x00,0xf5, +0x83,0xe0,0xfc,0xa3,0xe0,0xfd,0x74,0x01,0x25,0x82,0xf5,0x82,0xe5,0x83,0x34,0x00, +0xf5,0x83,0xe0,0xa2,0xe7,0x92,0xd5,0x2d,0xfd,0xec,0x75,0xf0,0x00,0x30,0xd5,0x03, +0x75,0xf0,0xff,0x35,0xf0,0xfc,0xed,0x2b,0xfd,0xec,0x3a,0xfc,0xe5,0x82,0x24,0x02, +0xf5,0x82,0xe5,0x83,0x34,0x00,0xf5,0x83,0xec,0xf0,0xa3,0xed,0xf0,0x90,0x45,0xdd, +0xe0,0xf5,0x23,0x75,0xa0,0x00,0x60,0x16,0x24,0x01,0xf8,0xe2,0xb5,0x24,0x05,0xab, +0x23,0x12,0x3b,0x2b,0xe5,0x23,0x24,0x09,0xf8,0xe2,0xf5,0x23,0x80,0xe5,0x22,0x75, +0xa0,0x00,0xeb,0x24,0x04,0xf8,0xe2,0xf5,0xf0,0xe8,0x04,0xf8,0xe2,0x24,0x0a,0xf5, +0x82,0xe5,0xf0,0x34,0x00,0xf5,0x83,0xc0,0x83,0xc0,0x82,0xe0,0xc3,0x13,0xfa,0x79, +0x00,0xeb,0x24,0x01,0xf8,0xe2,0xfc,0x75,0xf0,0x0e,0xa4,0x24,0xec,0xf5,0x82,0xe5, +0xf0,0x34,0x45,0xf5,0x83,0xe0,0x2a,0xfa,0xe9,0x34,0x00,0xf9,0xd0,0xe0,0x24,0xf8, +0xf5,0x82,0xd0,0xe0,0x34,0xff,0xf5,0x83,0xe0,0xc4,0x54,0x0f,0xc3,0x33,0x24,0xb3, +0xf5,0x82,0x74,0x80,0x34,0x00,0xf5,0x83,0xe0,0xf5,0xf0,0xa3,0xe0,0xf5,0x82,0x85, +0xf0,0x83,0xeb,0x24,0x03,0xf8,0xe2,0x25,0x82,0xf5,0x82,0xe5,0x83,0x34,0x00,0xf5, +0x83,0xe0,0x2a,0xfa,0xe9,0x34,0x00,0xf9,0xbc,0x09,0x0a,0x90,0x46,0xc0,0xe0,0x2a, +0xfa,0xe9,0x34,0x00,0xf9,0xc3,0xea,0x94,0x7e,0xe9,0x94,0x00,0x40,0x02,0x7a,0x7e, +0xeb,0x24,0x00,0xf8,0xe2,0xf8,0xa9,0x02,0x7a,0x01,0x12,0x39,0xcc,0x22,0x74,0xaa, +0x90,0xff,0xe1,0xf0,0x12,0x40,0x19,0x12,0x4c,0x48,0x22,0x12,0x40,0x3d,0xc3,0x33, +0x40,0x05,0x90,0x3d,0x59,0x80,0x03,0x90,0x3e,0x59,0xf5,0xf0,0x93,0xc5,0xf0,0xa3, +0x93,0x85,0xf0,0x83,0xf5,0x82,0xe4,0x73,0x22,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x3f,0x5f,0x40,0x16,0x40,0x16,0x40, +0x16,0x3f,0x67,0x40,0x16,0x3f,0x6d,0x3f,0x73,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x3f,0x79,0x40,0x16,0x40,0x16,0x40, +0x16,0x3f,0x81,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x3f,0x87,0x3f,0x8f,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x3f,0x92,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x3f,0x98,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x3f,0x9e,0x3f,0xa4,0x3f,0xaa,0x3f,0xb0,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x3f,0x59,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x3f,0xb6,0x3f,0xc1,0x40,0x16,0x3f, +0xc4,0x3f,0xc7,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x3f,0xcc,0x3f,0xd6,0x3f,0xe3,0x40, +0x16,0x3f,0xf5,0x40,0x16,0x40,0x16,0x40,0x16,0x3f,0xfd,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x05,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x0e,0x40,0x16,0x40,0x16,0x40, +0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x40,0x16,0x12,0x40,0xb7,0x02,0x3d,0x58,0x12, +0x40,0x2a,0xf5,0x90,0x02,0x3d,0x58,0x12,0x40,0xa7,0x02,0x3d,0x58,0x12,0x40,0x9f, +0x02,0x3d,0x58,0x12,0x40,0x9f,0x02,0x3d,0x58,0x74,0x00,0x12,0x40,0x42,0x02,0x3d, +0x58,0x12,0x40,0xaf,0x02,0x3d,0x58,0x74,0xf8,0x12,0x40,0x42,0x02,0x3d,0x58,0x02, +0x3d,0x58,0x12,0x40,0x2a,0x02,0x3d,0x58,0x12,0x40,0xbf,0x02,0x3d,0x58,0x12,0x40, +0x87,0x02,0x3d,0x58,0x12,0x40,0x93,0x02,0x3d,0x58,0x12,0x40,0x9f,0x02,0x3d,0x58, +0x12,0x40,0x9f,0x02,0x3d,0x58,0xc2,0xa9,0x74,0x00,0x90,0xff,0xe4,0xf0,0x02,0x3d, +0x58,0x02,0x3d,0x58,0x02,0x3d,0x58,0xd2,0xa9,0x02,0x3d,0x58,0x12,0x40,0x2a,0xf4, +0x12,0x40,0x42,0x02,0x3d,0x58,0x74,0x01,0x12,0x40,0x42,0x74,0x05,0x12,0x40,0x42, +0x02,0x3d,0x58,0x12,0x40,0x2a,0x12,0x4c,0x4e,0x12,0x40,0x42,0x74,0x01,0x90,0xff, +0xe4,0xf0,0x02,0x3d,0x58,0x12,0x40,0x2a,0xf5,0x26,0x02,0x3d,0x58,0xe5,0x26,0x12, +0x40,0x42,0x02,0x3d,0x58,0x74,0x01,0x90,0xff,0xe5,0xf0,0x02,0x3d,0x58,0x74,0x00, +0x12,0x40,0x42,0x02,0x3d,0x58,0x02,0x3d,0x58,0xe5,0x89,0x54,0xf0,0x44,0x02,0xf5, +0x89,0x75,0x8c,0x83,0x75,0x8a,0x83,0xd2,0x8c,0x22,0x90,0xff,0xe2,0x75,0x27,0x00, +0x75,0x28,0x00,0xe0,0x20,0xe0,0x06,0xd5,0x27,0xf9,0xd5,0x28,0xf6,0x90,0xff,0xe0, +0xe0,0x22,0xc0,0xe0,0x90,0xff,0xe3,0x75,0x27,0x00,0x75,0x28,0x00,0xe0,0x30,0xe0, +0x06,0xd5,0x27,0xf9,0xd5,0x28,0xf6,0xd0,0xe0,0x90,0xff,0xe1,0xf0,0x22,0x12,0x40, +0x2a,0xf5,0x2a,0x12,0x40,0x2a,0xf5,0x29,0x30,0x08,0x14,0xc2,0x09,0xe5,0x2a,0x20, +0xe0,0x02,0xd2,0x09,0xe5,0x29,0xc3,0x13,0xf5,0x29,0xe5,0x2a,0x13,0xf5,0x2a,0xe4, +0x90,0xff,0xe4,0xf0,0xd2,0xa9,0x22,0xc2,0x00,0xc2,0x07,0xd2,0x01,0xd2,0x05,0xc2, +0x06,0x80,0xcb,0xc2,0x00,0xc2,0x07,0xd2,0x01,0xd2,0x05,0xd2,0x06,0x80,0xbf,0xc2, +0x00,0xc2,0x07,0xd2,0x01,0x80,0xb7,0xc2,0x00,0xc2,0x07,0xc2,0x01,0x80,0xaf,0xd2, +0x00,0xc2,0x07,0xc2,0x01,0x80,0xa7,0xc2,0x00,0xc2,0x01,0xd2,0x07,0x80,0x9f,0x12, +0x40,0x2a,0xc2,0x08,0xb4,0xce,0x00,0x40,0x04,0xc3,0x33,0xd2,0x08,0xc3,0x94,0x80, +0x40,0x06,0xc2,0x03,0x33,0x02,0x40,0xdc,0x24,0x80,0xd2,0x03,0xf5,0x8c,0xf5,0x8a, +0x22,0x30,0x03,0x07,0xb2,0x04,0x20,0x04,0x02,0x80,0x76,0x30,0x00,0x03,0x02,0x42, +0x4b,0x20,0x01,0x6e,0xc0,0xd0,0xc0,0x83,0xc0,0x82,0xc0,0xe0,0x20,0x07,0x43,0x30, +0x08,0x21,0x30,0x09,0x05,0xc2,0x09,0x02,0x41,0x23,0x74,0x01,0x90,0xff,0xe4,0xf0, +0x90,0xff,0xd2,0x75,0x27,0x00,0x75,0x28,0x00,0xe0,0x30,0xe4,0x06,0xd5,0x27,0xf9, +0xd5,0x28,0xf6,0x74,0x01,0x90,0xff,0xe4,0xf0,0x90,0xff,0xd2,0x75,0x27,0x00,0x75, +0x28,0x00,0xe0,0x30,0xe4,0x06,0xd5,0x27,0xf9,0xd5,0x28,0xf6,0x90,0xff,0xe4,0xe0, +0xf5,0x90,0xe5,0x2a,0xc3,0x94,0x01,0xf5,0x2a,0xe5,0x29,0x94,0x00,0xf5,0x29,0x50, +0x08,0xc2,0xa9,0x74,0x01,0x90,0xff,0xe5,0xf0,0xd0,0xe0,0xd0,0x82,0xd0,0x83,0xd0, +0xd0,0x32,0xc0,0xd0,0xc0,0x82,0xc0,0x83,0xc0,0xe0,0x30,0x05,0x36,0x74,0x01,0x90, +0xff,0xe4,0xf0,0x90,0xff,0xd2,0x75,0x27,0x00,0x75,0x28,0x00,0xe0,0x30,0xe4,0x06, +0xd5,0x27,0xf9,0xd5,0x28,0xf6,0x90,0xff,0xe4,0xe0,0xf5,0x2b,0xe5,0x2a,0xc3,0x94, +0x01,0xf5,0x2a,0xe5,0x29,0x94,0x00,0xf5,0x29,0x50,0x08,0xc2,0xa9,0x74,0x01,0x90, +0xff,0xe5,0xf0,0x30,0x06,0x0e,0x85,0x2b,0x2c,0x75,0x2d,0x01,0x85,0x2b,0x90,0xc2, +0x06,0x02,0x42,0x3a,0xb2,0x05,0xe5,0x2b,0xc4,0xf5,0x2b,0x85,0x2d,0x2f,0xe5,0x2d, +0xc3,0x33,0xf5,0x2e,0xe5,0x2b,0x30,0xe0,0x06,0xe5,0x2f,0x25,0x2e,0xf5,0x2f,0xe5, +0x2e,0xc3,0x33,0xf5,0x2e,0xe5,0x2b,0x30,0xe1,0x06,0xe5,0x2f,0x25,0x2e,0xf5,0x2f, +0xe5,0x2e,0xc3,0x33,0xf5,0x2e,0xe5,0x2b,0x30,0xe2,0x06,0xe5,0x2f,0x25,0x2e,0xf5, +0x2f,0xe5,0x2b,0x30,0xe3,0x11,0xe5,0x2f,0xc3,0x13,0xc3,0xc5,0x2c,0x95,0x2c,0x50, +0x02,0x74,0x00,0xf5,0x2c,0x80,0x0c,0xe5,0x2f,0xc3,0x13,0x25,0x2c,0x50,0x02,0x74, +0xff,0xf5,0x2c,0xe5,0x2b,0x54,0x07,0x24,0x29,0x83,0x30,0xe0,0x06,0xe5,0x2d,0xc3, +0x13,0x80,0x07,0x30,0xe1,0x11,0xe5,0x2d,0xc3,0x33,0x70,0x02,0x74,0x01,0xb4,0x09, +0x00,0x40,0x02,0x74,0x08,0xf5,0x2d,0x85,0x2c,0x90,0xd0,0xe0,0xd0,0x83,0xd0,0x82, +0xd0,0xd0,0x32,0x01,0x00,0x00,0x00,0x00,0x02,0x02,0x02,0xc0,0xd0,0xc0,0x83,0xc0, +0x82,0xc0,0xe0,0x90,0xff,0xe1,0xe4,0xf0,0x74,0x01,0x90,0xff,0xe4,0xf0,0xe5,0x2a, +0xc3,0x94,0x01,0xf5,0x2a,0xe5,0x29,0x94,0x00,0xf5,0x29,0x50,0x08,0xc2,0xa9,0x74, +0x01,0x90,0xff,0xe5,0xf0,0xd0,0xe0,0xd0,0x82,0xd0,0x83,0xd0,0xd0,0x32,0x90,0x01, +0x00,0xe4,0xf0,0xa3,0xf0,0xe4,0xf5,0x30,0xf5,0x31,0xf5,0x32,0xf5,0x33,0xe4,0xf5, +0x34,0xf5,0x35,0xf5,0x36,0xf5,0x37,0xe4,0xf5,0x3c,0xf5,0x3d,0xf5,0x3e,0xf5,0x3f, +0xe4,0xf5,0x38,0xf5,0x39,0xf5,0x3a,0xf5,0x3b,0xc2,0x14,0xc2,0x15,0xc2,0x16,0x90, +0x42,0x08,0xe0,0xb4,0x41,0x07,0xa3,0xe0,0xb4,0x50,0x02,0x80,0x1d,0x90,0x42,0x08, +0x74,0x41,0xf0,0xa3,0x74,0x50,0xf0,0x90,0x42,0x01,0x74,0x0a,0xf0,0xc2,0x10,0xd2, +0x11,0xc2,0x12,0xd2,0x13,0x90,0x42,0x00,0xe4,0xf0,0xe4,0x90,0x42,0x02,0xf0,0x90, +0x42,0x03,0xf0,0x90,0x00,0x00,0x12,0x0e,0x40,0x90,0xff,0xd0,0xe0,0x90,0xff,0xd1, +0xe0,0x90,0xff,0xe0,0xe0,0x90,0xff,0xe4,0xe0,0xd2,0xaa,0xd2,0xaf,0x30,0x16,0x18, +0xc2,0x16,0x90,0x00,0x00,0x12,0x32,0x09,0xc2,0xaf,0x85,0x3a,0x38,0x85,0x3b,0x39, +0x85,0x32,0x30,0x85,0x33,0x31,0xd2,0xaf,0x30,0x11,0x36,0x90,0xff,0xd2,0xe0,0x30, +0xe6,0x2f,0xe5,0x3d,0xb5,0x3f,0x08,0xe5,0x3c,0xb5,0x3e,0x03,0xc3,0x80,0x1c,0xe5, +0x3c,0x24,0x00,0xf5,0x82,0x74,0x32,0x35,0x3d,0xf5,0x83,0xe5,0x3c,0x24,0x01,0xf5, +0x3c,0xe5,0x3d,0x34,0x00,0x54,0x0f,0xf5,0x3d,0xe0,0xd3,0x50,0x04,0x90,0xff,0xd0, +0xf0,0xe5,0x39,0xb5,0x3b,0x08,0xe5,0x38,0xb5,0x3a,0x03,0xc3,0x80,0x1c,0xe5,0x38, +0x24,0x00,0xf5,0x82,0x74,0x22,0x35,0x39,0xf5,0x83,0xe5,0x38,0x24,0x01,0xf5,0x38, +0xe5,0x39,0x34,0x00,0x54,0x0f,0xf5,0x39,0xe0,0xd3,0x50,0x14,0x30,0x13,0x0b,0xc0, +0xe0,0xfb,0x90,0x00,0x01,0x12,0x19,0x59,0xd0,0xe0,0x30,0x12,0x03,0x12,0x44,0x00, +0xe5,0x31,0xb5,0x33,0x08,0xe5,0x30,0xb5,0x32,0x03,0xc3,0x80,0x1c,0xe5,0x30,0x24, +0x00,0xf5,0x82,0x74,0x02,0x35,0x31,0xf5,0x83,0xe5,0x30,0x24,0x01,0xf5,0x30,0xe5, +0x31,0x34,0x00,0x54,0x0f,0xf5,0x31,0xe0,0xd3,0x50,0x42,0x30,0x10,0x0b,0xc0,0xe0, +0xfb,0x90,0x00,0x01,0x12,0x19,0x59,0xd0,0xe0,0x30,0x11,0x31,0xf5,0xf0,0xe5,0x3e, +0x24,0x01,0xf5,0x42,0xe5,0x3f,0x34,0x00,0x54,0x0f,0xf5,0x43,0xb5,0x3d,0x08,0xe5, +0x42,0xb5,0x3c,0x03,0xd3,0x80,0x16,0x74,0x00,0x25,0x3e,0xf5,0x82,0x74,0x32,0x35, +0x3f,0xf5,0x83,0xe5,0xf0,0xf0,0x85,0x42,0x3e,0x85,0x43,0x3f,0xc3,0x02,0x42,0xfd, +0xc2,0xaf,0x20,0x14,0x06,0xf5,0x99,0xd2,0x14,0x80,0x31,0xf5,0xf0,0xe5,0x36,0x24, +0x01,0xf5,0x42,0xe5,0x37,0x34,0x00,0x54,0x0f,0xf5,0x43,0xb5,0x35,0x08,0xe5,0x42, +0xb5,0x34,0x03,0xd3,0x80,0x16,0x74,0x00,0x25,0x36,0xf5,0x82,0x74,0x12,0x35,0x37, +0xf5,0x83,0xe5,0xf0,0xf0,0x85,0x42,0x36,0x85,0x43,0x37,0xc3,0xd2,0xaf,0x22,0xc0, +0xd0,0xc0,0xe0,0xc0,0xf0,0xc0,0x83,0xc0,0x82,0x90,0xff,0xd2,0xe0,0x20,0xe4,0x12, +0x90,0xff,0xe2,0xe0,0x20,0xe0,0x06,0x90,0xff,0xe4,0xe0,0x80,0x63,0x12,0x3d,0x3b, +0x80,0x5e,0x30,0xe7,0x4f,0x90,0xff,0xd0,0xe0,0xf5,0xf0,0xe5,0x3a,0x24,0x01,0xf5, +0x40,0xe5,0x3b,0x34,0x00,0x54,0x0f,0xf5,0x41,0xb5,0x39,0x08,0xe5,0x40,0xb5,0x38, +0x03,0xd3,0x80,0x16,0x74,0x00,0x25,0x3a,0xf5,0x82,0x74,0x22,0x35,0x3b,0xf5,0x83, +0xe5,0xf0,0xf0,0x85,0x40,0x3a,0x85,0x41,0x3b,0xc3,0x50,0x24,0xd2,0x16,0x85,0x3a, +0x38,0x85,0x3b,0x39,0x90,0x42,0x0b,0xe0,0x24,0x01,0xf0,0x90,0x42,0x0a,0xe0,0x34, +0x00,0xf0,0x80,0x0c,0x30,0xe5,0x09,0x90,0xff,0xd1,0xe0,0xf5,0xf0,0x12,0x45,0x50, +0xd0,0x82,0xd0,0x83,0xd0,0xf0,0xd0,0xe0,0xd0,0xd0,0x32,0xc0,0xd0,0xc0,0xe0,0xc0, +0xf0,0xc0,0x83,0xc0,0x82,0x30,0x99,0x35,0xc2,0x99,0xe5,0x35,0xb5,0x37,0x08,0xe5, +0x34,0xb5,0x36,0x03,0xc3,0x80,0x1c,0xe5,0x34,0x24,0x00,0xf5,0x82,0x74,0x12,0x35, +0x35,0xf5,0x83,0xe5,0x34,0x24,0x01,0xf5,0x34,0xe5,0x35,0x34,0x00,0x54,0x0f,0xf5, +0x35,0xe0,0xd3,0x50,0x06,0xf5,0x99,0xd2,0x14,0x80,0x02,0xc2,0x14,0x30,0x98,0x35, +0xe5,0x99,0xc2,0x98,0xf5,0xf0,0xe5,0x32,0x24,0x01,0xf5,0x40,0xe5,0x33,0x34,0x00, +0x54,0x0f,0xf5,0x41,0xb5,0x31,0x08,0xe5,0x40,0xb5,0x30,0x03,0xd3,0x80,0x16,0x74, +0x00,0x25,0x32,0xf5,0x82,0x74,0x02,0x35,0x33,0xf5,0x83,0xe5,0xf0,0xf0,0x85,0x40, +0x32,0x85,0x41,0x33,0xc3,0xd0,0x82,0xd0,0x83,0xd0,0xf0,0xd0,0xe0,0xd0,0xd0,0x32, +0x20,0x15,0x06,0x90,0xff,0xd0,0x74,0xfe,0xf0,0x90,0x42,0x03,0xe0,0xb4,0x00,0x4e, +0x90,0x42,0x02,0xe0,0x70,0x1b,0xe5,0xf0,0xb4,0x3f,0x05,0xd2,0x15,0x02,0x47,0x47, +0xb4,0xff,0x05,0xc2,0x15,0x02,0x47,0x47,0xb4,0xb9,0x05,0xd2,0x16,0x02,0x47,0x47, +0xe0,0x24,0xca,0xf5,0x82,0x74,0x94,0x34,0x00,0xf5,0x83,0xe0,0xc3,0x95,0xf0,0x60, +0x08,0x90,0x42,0x02,0xe4,0xf0,0x02,0x47,0x47,0x90,0x42,0x02,0xe0,0x04,0xf0,0x24, +0xf8,0x70,0x08,0xe4,0xf0,0x90,0x42,0x03,0x74,0x01,0xf0,0x02,0x47,0x47,0xb4,0x01, +0x74,0x90,0x42,0x03,0xe5,0xf0,0xb4,0x00,0x06,0x74,0x02,0xf0,0x02,0x47,0x47,0xb4, +0x01,0x06,0x74,0x03,0xf0,0x02,0x47,0x47,0xb4,0x02,0x06,0x74,0x04,0xf0,0x02,0x47, +0x47,0xb4,0x03,0x1d,0x90,0x42,0x0a,0x74,0x00,0xf0,0xa3,0xf0,0x90,0x42,0x04,0x74, +0x42,0xf0,0xa3,0x74,0x01,0xf0,0x90,0x42,0x06,0xe4,0xf0,0xa3,0x04,0xf0,0x02,0x46, +0xb7,0xb4,0x04,0x15,0x90,0x42,0x04,0x74,0x42,0xf0,0xa3,0x74,0x00,0xf0,0x90,0x42, +0x06,0xe4,0xf0,0xa3,0x04,0xf0,0x02,0x46,0xb7,0xb4,0x05,0x16,0x90,0x42,0x04,0x74, +0x94,0xf0,0xa3,0x74,0xca,0xf0,0x90,0x42,0x06,0xe4,0xf0,0xa3,0x74,0x0a,0xf0,0x02, +0x46,0xb7,0x02,0x47,0x42,0xb4,0x02,0x27,0xc2,0x10,0xc2,0x11,0xc2,0x12,0xc2,0x13, +0xe5,0xf0,0x54,0x0f,0x90,0x42,0x01,0xf0,0x30,0xe0,0x02,0xd2,0x10,0x30,0xe1,0x02, +0xd2,0x11,0x30,0xe2,0x02,0xd2,0x12,0x30,0xe3,0x02,0xd2,0x13,0x02,0x47,0x42,0xb4, +0x03,0x10,0xe5,0xf0,0x70,0x03,0xe4,0x80,0x02,0x74,0x01,0x90,0x42,0x00,0xf0,0x02, +0x47,0x42,0xb4,0x04,0x0f,0xe5,0xf0,0x90,0x42,0x04,0xf0,0x90,0x42,0x03,0x74,0x05, +0xf0,0x02,0x47,0x47,0xb4,0x05,0x0f,0xe5,0xf0,0x90,0x42,0x05,0xf0,0x90,0x42,0x03, +0x74,0x06,0xf0,0x02,0x47,0x47,0xb4,0x06,0x0f,0xe5,0xf0,0x90,0x42,0x06,0xf0,0x90, +0x42,0x03,0x74,0x07,0xf0,0x02,0x47,0x47,0xb4,0x07,0x0f,0xe5,0xf0,0x90,0x42,0x07, +0xf0,0x90,0x42,0x03,0x74,0x08,0xf0,0x02,0x47,0x47,0xb4,0x08,0x07,0xe5,0xf0,0x60, +0x06,0x02,0x47,0x09,0x02,0x47,0x42,0xc0,0x00,0xc0,0x01,0xc0,0x02,0xc0,0x03,0x90, +0x42,0x06,0xe0,0x04,0xf8,0xa3,0xe0,0x04,0xf9,0x90,0x42,0x04,0xe0,0xc0,0xe0,0xa3, +0xe0,0xc0,0xe0,0x02,0x46,0xf6,0x7a,0xc4,0x7b,0x51,0x90,0xff,0xd2,0xe0,0x20,0xe6, +0x07,0xdb,0xfa,0xda,0xf8,0x02,0x46,0xfa,0xd0,0x82,0xd0,0x83,0xe0,0xa3,0xc0,0x83, +0xc0,0x82,0x90,0xff,0xd0,0xf0,0xd9,0xde,0xd8,0xdc,0xd0,0xe0,0xd0,0xe0,0xd0,0x03, +0xd0,0x02,0xd0,0x01,0xd0,0x00,0x02,0x47,0x42,0xc0,0x00,0xc0,0x01,0xc0,0x02,0xc0, +0x03,0x90,0x42,0x07,0xe0,0x04,0xf9,0x90,0x42,0x05,0xe0,0xf8,0x80,0x17,0x7a,0xc4, +0x7b,0x51,0x90,0xff,0xd2,0xe0,0x20,0xe6,0x06,0xdb,0xfa,0xda,0xf8,0x80,0x08,0xe6, +0x08,0x90,0xff,0xd0,0xf0,0xd9,0xe7,0xd0,0x03,0xd0,0x02,0xd0,0x01,0xd0,0x00,0x02, +0x47,0x42,0x90,0x42,0x03,0xe4,0xf0,0x22,0xa5,0x69,0x5a,0x96,0xa4,0x68,0x5b,0x97, +0xa7,0x6b,0x58,0x94,0xa6,0x6a,0x59,0x95,0xa1,0x6d,0x5e,0x92,0xa0,0x6c,0x5f,0x93, +0xa3,0x6f,0x5c,0x90,0xa2,0x6e,0x5d,0x91,0xad,0x61,0x52,0x9e,0xac,0x60,0x53,0x9f, +0xaf,0x63,0x50,0x9c,0xae,0x62,0x51,0x9d,0xa9,0x65,0x56,0x9a,0xa8,0x64,0x57,0x9b, +0xab,0x67,0x54,0x98,0xaa,0x66,0x55,0x99,0xb5,0x79,0x4a,0x86,0xb4,0x78,0x4b,0x87, +0xb7,0x7b,0x48,0x84,0xb6,0x7a,0x49,0x85,0xb1,0x7d,0x4e,0x82,0xb0,0x7c,0x4f,0x83, +0xb3,0x7f,0x4c,0x80,0xb2,0x7e,0x4d,0x81,0xbd,0x71,0x42,0x8e,0xbc,0x70,0x43,0x8f, +0xbf,0x73,0x40,0x8c,0xbe,0x72,0x41,0x8d,0xb9,0x75,0x46,0x8a,0xb8,0x74,0x47,0x8b, +0xbb,0x77,0x44,0x88,0xba,0x76,0x45,0x89,0x85,0x49,0x7a,0xb6,0x84,0x48,0x7b,0xb7, +0x87,0x4b,0x78,0xb4,0x86,0x4a,0x79,0xb5,0x81,0x4d,0x7e,0xb2,0x80,0x4c,0x7f,0xb3, +0x83,0x4f,0x7c,0xb0,0x82,0x4e,0x7d,0xb1,0x8d,0x41,0x72,0xbe,0x8c,0x40,0x73,0xbf, +0x8f,0x43,0x70,0xbc,0x8e,0x42,0x71,0xbd,0x89,0x45,0x76,0xba,0x88,0x44,0x77,0xbb, +0x8b,0x47,0x74,0xb8,0x8a,0x46,0x75,0xb9,0x95,0x59,0x6a,0xa6,0x94,0x58,0x6b,0xa7, +0x97,0x5b,0x68,0xa4,0x96,0x5a,0x69,0xa5,0x91,0x5d,0x6e,0xa2,0x90,0x5c,0x6f,0xa3, +0x93,0x5f,0x6c,0xa0,0x92,0x5e,0x6d,0xa1,0x9d,0x51,0x62,0xae,0x9c,0x50,0x63,0xaf, +0x9f,0x53,0x60,0xac,0x9e,0x52,0x61,0xad,0x99,0x55,0x66,0xaa,0x98,0x54,0x67,0xab, +0x9b,0x57,0x64,0xa8,0x9a,0x56,0x65,0xa9,0xe5,0x29,0x1a,0xd6,0xe4,0x28,0x1b,0xd7, +0xe7,0x2b,0x18,0xd4,0xe6,0x2a,0x19,0xd5,0xe1,0x2d,0x1e,0xd2,0xe0,0x2c,0x1f,0xd3, +0xe3,0x2f,0x1c,0xd0,0xe2,0x2e,0x1d,0xd1,0xed,0x21,0x12,0xde,0xec,0x20,0x13,0xdf, +0xef,0x23,0x10,0xdc,0xee,0x22,0x11,0xdd,0xe9,0x25,0x16,0xda,0xe8,0x24,0x17,0xdb, +0xeb,0x27,0x14,0xd8,0xea,0x26,0x15,0xd9,0xf5,0x39,0x0a,0xc6,0xf4,0x38,0x0b,0xc7, +0xf7,0x3b,0x08,0xc4,0xf6,0x3a,0x09,0xc5,0xf1,0x3d,0x0e,0xc2,0xf0,0x3c,0x0f,0xc3, +0xf3,0x3f,0x0c,0xc0,0xf2,0x3e,0x0d,0xc1,0xfd,0x31,0x02,0xce,0xfc,0x30,0x03,0xcf, +0xff,0x33,0x00,0xcc,0xfe,0x32,0x01,0xcd,0xf9,0x35,0x06,0xca,0xf8,0x34,0x07,0xcb, +0xfb,0x37,0x04,0xc8,0xfa,0x36,0x05,0xc9,0xc5,0x09,0x3a,0xf6,0xc4,0x08,0x3b,0xf7, +0xc7,0x0b,0x38,0xf4,0xc6,0x0a,0x39,0xf5,0xc1,0x0d,0x3e,0xf2,0xc0,0x0c,0x3f,0xf3, +0xc3,0x0f,0x3c,0xf0,0xc2,0x0e,0x3d,0xf1,0xcd,0x01,0x32,0xfe,0xcc,0x00,0x33,0xff, +0xcf,0x03,0x30,0xfc,0xce,0x02,0x31,0xfd,0xc9,0x05,0x36,0xfa,0xc8,0x04,0x37,0xfb, +0xcb,0x07,0x34,0xf8,0xca,0x06,0x35,0xf9,0xd5,0x19,0x2a,0xe6,0xd4,0x18,0x2b,0xe7, +0xd7,0x1b,0x28,0xe4,0xd6,0x1a,0x29,0xe5,0xd1,0x1d,0x2e,0xe2,0xd0,0x1c,0x2f,0xe3, +0xd3,0x1f,0x2c,0xe0,0xd2,0x1e,0x2d,0xe1,0xdd,0x11,0x22,0xee,0xdc,0x10,0x23,0xef, +0xdf,0x13,0x20,0xec,0xde,0x12,0x21,0xed,0xd9,0x15,0x26,0xea,0xd8,0x14,0x27,0xeb, +0xdb,0x17,0x24,0xe8,0xda,0x16,0x25,0xe9,0x25,0xe9,0xda,0x16,0x24,0xe8,0xdb,0x17, +0x27,0xeb,0xd8,0x14,0x26,0xea,0xd9,0x15,0x21,0xed,0xde,0x12,0x20,0xec,0xdf,0x13, +0x23,0xef,0xdc,0x10,0x22,0xee,0xdd,0x11,0x2d,0xe1,0xd2,0x1e,0x2c,0xe0,0xd3,0x1f, +0x2f,0xe3,0xd0,0x1c,0x2e,0xe2,0xd1,0x1d,0x29,0xe5,0xd6,0x1a,0x28,0xe4,0xd7,0x1b, +0x2b,0xe7,0xd4,0x18,0x2a,0xe6,0xd5,0x19,0x35,0xf9,0xca,0x06,0x34,0xf8,0xcb,0x07, +0x37,0xfb,0xc8,0x04,0x36,0xfa,0xc9,0x05,0x31,0xfd,0xce,0x02,0x30,0xfc,0xcf,0x03, +0x33,0xff,0xcc,0x00,0x32,0xfe,0xcd,0x01,0x3d,0xf1,0xc2,0x0e,0x3c,0xf0,0xc3,0x0f, +0x3f,0xf3,0xc0,0x0c,0x3e,0xf2,0xc1,0x0d,0x39,0xf5,0xc6,0x0a,0x38,0xf4,0xc7,0x0b, +0x3b,0xf7,0xc4,0x08,0x3a,0xf6,0xc5,0x09,0x05,0xc9,0xfa,0x36,0x04,0xc8,0xfb,0x37, +0x07,0xcb,0xf8,0x34,0x06,0xca,0xf9,0x35,0x01,0xcd,0xfe,0x32,0x00,0xcc,0xff,0x33, +0x03,0xcf,0xfc,0x30,0x02,0xce,0xfd,0x31,0x0d,0xc1,0xf2,0x3e,0x0c,0xc0,0xf3,0x3f, +0x0f,0xc3,0xf0,0x3c,0x0e,0xc2,0xf1,0x3d,0x09,0xc5,0xf6,0x3a,0x08,0xc4,0xf7,0x3b, +0x0b,0xc7,0xf4,0x38,0x0a,0xc6,0xf5,0x39,0x15,0xd9,0xea,0x26,0x14,0xd8,0xeb,0x27, +0x17,0xdb,0xe8,0x24,0x16,0xda,0xe9,0x25,0x11,0xdd,0xee,0x22,0x10,0xdc,0xef,0x23, +0x13,0xdf,0xec,0x20,0x12,0xde,0xed,0x21,0x1d,0xd1,0xe2,0x2e,0x1c,0xd0,0xe3,0x2f, +0x1f,0xd3,0xe0,0x2c,0x1e,0xd2,0xe1,0x2d,0x19,0xd5,0xe6,0x2a,0x18,0xd4,0xe7,0x2b, +0x1b,0xd7,0xe4,0x28,0x1a,0xd6,0xe5,0x29,0x65,0xa9,0x9a,0x56,0x64,0xa8,0x9b,0x57, +0x67,0xab,0x98,0x54,0x66,0xaa,0x99,0x55,0x61,0xad,0x9e,0x52,0x60,0xac,0x9f,0x53, +0x63,0xaf,0x9c,0x50,0x62,0xae,0x9d,0x51,0x6d,0xa1,0x92,0x5e,0x6c,0xa0,0x93,0x5f, +0x6f,0xa3,0x90,0x5c,0x6e,0xa2,0x91,0x5d,0x69,0xa5,0x96,0x5a,0x68,0xa4,0x97,0x5b, +0x6b,0xa7,0x94,0x58,0x6a,0xa6,0x95,0x59,0x75,0xb9,0x8a,0x46,0x74,0xb8,0x8b,0x47, +0x77,0xbb,0x88,0x44,0x76,0xba,0x89,0x45,0x71,0xbd,0x8e,0x42,0x70,0xbc,0x8f,0x43, +0x73,0xbf,0x8c,0x40,0x72,0xbe,0x8d,0x41,0x7d,0xb1,0x82,0x4e,0x7c,0xb0,0x83,0x4f, +0x7f,0xb3,0x80,0x4c,0x7e,0xb2,0x81,0x4d,0x79,0xb5,0x86,0x4a,0x78,0xb4,0x87,0x4b, +0x7b,0xb7,0x84,0x48,0x7a,0xb6,0x85,0x49,0x45,0x89,0xba,0x76,0x44,0x88,0xbb,0x77, +0x47,0x8b,0xb8,0x74,0x46,0x8a,0xb9,0x75,0x41,0x8d,0xbe,0x72,0x40,0x8c,0xbf,0x73, +0x43,0x8f,0xbc,0x70,0x42,0x8e,0xbd,0x71,0x4d,0x81,0xb2,0x7e,0x4c,0x80,0xb3,0x7f, +0x4f,0x83,0xb0,0x7c,0x4e,0x82,0xb1,0x7d,0x49,0x85,0xb6,0x7a,0x48,0x84,0xb7,0x7b, +0x4b,0x87,0xb4,0x78,0x4a,0x86,0xb5,0x79,0x55,0x99,0xaa,0x66,0x54,0x98,0xab,0x67, +0x57,0x9b,0xa8,0x64,0x56,0x9a,0xa9,0x65,0x51,0x9d,0xae,0x62,0x50,0x9c,0xaf,0x63, +0x53,0x9f,0xac,0x60,0x52,0x9e,0xad,0x61,0x5d,0x91,0xa2,0x6e,0x5c,0x90,0xa3,0x6f, +0x5f,0x93,0xa0,0x6c,0x5e,0x92,0xa1,0x6d,0x59,0x95,0xa6,0x6a,0x58,0x94,0xa7,0x6b, +0x5b,0x97,0xa4,0x68,0x5a,0x96,0xa5,0x69,0x40,0x41,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b, +0x48,0x49,0x46,0x47,0x44,0x45,0x42,0x43,0x30,0x31,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b, +0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x60,0x61,0x5e,0x5f,0x5c,0x5d,0x5a,0x5b, +0x68,0x69,0x66,0x67,0x64,0x65,0x62,0x63,0x50,0x51,0x4e,0x4f,0x4c,0x4d,0x4a,0x4b, +0x58,0x59,0x56,0x57,0x54,0x55,0x52,0x53,0x80,0x81,0x7e,0x7f,0x7c,0x7d,0x7a,0x7b, +0x88,0x89,0x86,0x87,0x84,0x85,0x82,0x83,0x70,0x71,0x6e,0x6f,0x6c,0x6d,0x6a,0x6b, +0x78,0x79,0x76,0x77,0x74,0x75,0x72,0x73,0xa0,0xa1,0x9e,0x9f,0x9c,0x9d,0x9a,0x9b, +0xa8,0xa9,0xa6,0xa7,0xa4,0xa5,0xa2,0xa3,0x90,0x91,0x8e,0x8f,0x8c,0x8d,0x8a,0x8b, +0x98,0x99,0x96,0x97,0x94,0x95,0x92,0x93,0xc0,0xc1,0xbe,0xbf,0xbc,0xbd,0xba,0xbb, +0xc8,0xc9,0xc6,0xc7,0xc4,0xc5,0xc2,0xc3,0xb0,0xb1,0xae,0xaf,0xac,0xad,0xaa,0xab, +0xb8,0xb9,0xb6,0xb7,0xb4,0xb5,0xb2,0xb3,0xe0,0xe1,0xde,0xdf,0xdc,0xdd,0xda,0xdb, +0xe8,0xe9,0xe6,0xe7,0xe4,0xe5,0xe2,0xe3,0xd0,0xd1,0xce,0xcf,0xcc,0xcd,0xca,0xcb, +0xd8,0xd9,0xd6,0xd7,0xd4,0xd5,0xd2,0xd3,0x00,0x01,0xfe,0xff,0xfc,0xfd,0xfa,0xfb, +0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0xf0,0xf1,0xee,0xef,0xec,0xed,0xea,0xeb, +0xf8,0xf9,0xf6,0xf7,0xf4,0xf5,0xf2,0xf3,0x20,0x21,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b, +0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x10,0x11,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b, +0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x75,0x44,0x00,0xd2,0x02,0x22,0x30,0x02, +0x0b,0x90,0x4b,0x48,0x93,0xc2,0x02,0xf5,0x45,0x02,0x4c,0x82,0x75,0xf0,0x00,0xc3, +0x33,0xc5,0xf0,0x33,0xc5,0xf0,0xc3,0x33,0xc5,0xf0,0x33,0xc5,0xf0,0x24,0x48,0xf5, +0x82,0xe5,0xf0,0x34,0x47,0xf5,0x83,0xe5,0x44,0x54,0x03,0x93,0x25,0x45,0xf5,0x45, +0x05,0x44,0x22,0xff,0xc0,0x01,0x30,0x10,0x00,0xcb,0x80,0xba,0x3c,0x3d,0x00,0x00, +0x0b,0x38,0xc9,0x1c,0x0c,0xa0,0x00,0x00,0x00,0x20,0xc4,0x82,0xcf,0x3c,0x42,0x00, +0x00,0x1f,0x00,0x97,0x19,0x09,0xa0,0x00,0x00,0x00,0x28,0xc4,0x86,0xd1,0x3c,0x7f, +0x00,0x00,0x15,0x00,0x61,0x01,0x07,0xa0,0x00,0x00,0x00,0x20,0xc4,0x82,0xd0,0x3c, +0x42,0x00,0x00,0x15,0x00,0xf7,0x19,0x09,0xa0,0x00,0x00,0x00,0x28,0xc4,0x86,0xd0, +0x3c,0x42,0x00,0x00,0x15,0x00,0x81,0x01,0x07,0xa0,0x00,0x00,0x00,0x00,0xc3,0x80, +0xc7,0x3c,0x6f,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0,0x00,0x00,0x00,0x00,0xd1, +0x82,0xcc,0x3c,0x0a,0x00,0x00,0x29,0x00,0xf0,0x05,0x05,0xa0,0x00,0x00,0x00,0x00, +0xd2,0x82,0xbe,0x3c,0x77,0x00,0x00,0x33,0x00,0xf0,0x00,0x09,0xa0,0x00,0x00,0x00, +0x00,0x11,0x82,0xce,0x3c,0x42,0x00,0x00,0x15,0x00,0xf0,0x16,0x06,0xa0,0x00,0x00, +0x00,0x00,0xd2,0x82,0xca,0x3c,0x74,0x00,0x00,0x29,0x00,0xf0,0x00,0x06,0xa0,0x00, +0x00,0x00,0x00,0x11,0x82,0xcc,0x3c,0x42,0x00,0x00,0x15,0x00,0xf0,0x16,0x06,0xa0, +0x00,0x00,0x00,0x00,0x11,0x82,0xca,0x3c,0x52,0x00,0x00,0x15,0x00,0xf0,0x00,0x06, +0xa0,0x00,0x00,0x00,0x00,0xd2,0x82,0xca,0x3c,0x74,0x00,0x00,0x29,0x00,0xf0,0x00, +0x06,0xa0,0x00,0x00,0x00,0x00,0xd1,0x82,0xca,0x3c,0x0a,0x00,0x00,0x29,0x00,0xf0, +0x05,0x05,0xa0,0x00,0xf0,0x00,0x00,0x0a,0x82,0xca,0x3c,0x73,0x00,0x00,0x29,0x00, +0xf0,0x00,0x09,0xa0,0x00,0x00,0x00,0x00,0xd1,0x82,0xc8,0x3c,0x0a,0x00,0x00,0x23, +0x00,0xf0,0x07,0x07,0xa0,0x00,0xc0,0x00,0x00,0xc8,0x82,0xcb,0x3c,0x52,0x00,0x00, +0x29,0x00,0xf6,0x16,0x06,0xa0,0x00,0x20,0x00,0x00,0x79,0x82,0xbc,0x3c,0x50,0x00, +0x00,0x1f,0x00,0xf5,0x19,0x09,0xa0,0x00,0xd0,0x00,0x00,0xc8,0x82,0xc8,0x3c,0x52, +0x00,0x00,0x29,0x00,0xf6,0x16,0x06,0xa0,0x00,0x20,0x00,0x00,0x7b,0x82,0xbd,0x3c, +0x57,0x00,0x00,0x29,0x00,0xf0,0x00,0x09,0xa0,0x00,0xe0,0x00,0x00,0xc8,0x82,0xc2, +0x3c,0x52,0x00,0x00,0x29,0x00,0xf6,0x16,0x06,0xa0,0x00,0x20,0x00,0x00,0x7a,0x82, +0xc0,0x3c,0x7a,0x00,0x00,0x29,0x00,0xf0,0x00,0x05,0xa0,0x00,0x20,0x00,0x00,0xc7, +0x82,0xc3,0x3c,0x01,0x00,0x00,0x1f,0x00,0xf6,0x17,0x07,0xa0,0x00,0x30,0x00,0x00, +0xc7,0x82,0xc0,0x3c,0x52,0x00,0x00,0x1f,0x00,0xf6,0x17,0x07,0xa0,0x00,0x20,0x00, +0x00,0x31,0x88,0xca,0x3c,0x6b,0x00,0x00,0x15,0x00,0xf0,0x05,0x05,0xa0,0x00,0x40, +0x00,0x00,0xc7,0x82,0xbc,0x3c,0x52,0x00,0x00,0x1f,0x00,0xf6,0x17,0x07,0xa0,0x00, +0xe0,0x00,0x00,0x2e,0x84,0xca,0x3c,0x67,0x00,0x00,0x15,0x00,0xf0,0x05,0x05,0xa0, +0x00,0xe0,0x00,0x00,0x7a,0x84,0xd4,0x3c,0x01,0x00,0x00,0x15,0x00,0xf2,0x15,0x05, +0xa0,0x00,0xd0,0x00,0x00,0x21,0x84,0xc9,0x3c,0x6a,0x00,0x00,0x29,0x00,0xf0,0x05, +0x05,0xa0,0x00,0x10,0x00,0x00,0x25,0x82,0xc4,0x3c,0x6b,0x00,0x00,0x1f,0x00,0xf0, +0x00,0x09,0xa0,0x00,0xf0,0x00,0x00,0x31,0x86,0xc3,0x3c,0x67,0x00,0x00,0x15,0x00, +0xf5,0x16,0x06,0xa0,0x00,0x20,0x00,0x00,0x1d,0x80,0xca,0x3c,0x6b,0x00,0x00,0x1f, +0x00,0xf0,0x00,0x09,0xa0,0x00,0x43,0x6f,0x70,0x79,0x72,0x69,0x67,0x68,0x74,0x20, +0x59,0x41,0x4d,0x41,0x48,0x41,0x00,0xe0,0x00,0x00,0x31,0x88,0xc8,0x3c,0x67,0x00, +0x00,0x15,0x00,0xf4,0x16,0x06,0xa0,0x00,0xc0,0x00,0x00,0x9d,0x84,0xc9,0x3c,0x0a, +0x00,0x00,0x1f,0x00,0xf4,0x17,0x07,0xa0,0x00,0xe0,0x00,0x00,0x2e,0x84,0xc8,0x3c, +0x73,0x00,0x00,0x15,0x00,0xf0,0x06,0x06,0xa0,0x00,0x40,0x00,0x00,0x1c,0x82,0xc7, +0x3c,0x61,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0,0x00,0x40,0x00,0x00,0x1c,0x82, +0xc8,0x3c,0x43,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0,0x00,0xbd,0x91,0x90,0x87, +0x8e,0x97,0x99,0x98,0xe0,0xa7,0xbf,0xb3,0xbf,0xb8,0xbf,0xf6,0x00,0xd0,0x00,0x00, +0x1e,0x82,0xca,0x3c,0x72,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0,0x00,0xd0,0x00, +0x00,0x1f,0x82,0xca,0x3c,0x63,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0,0x00,0xe0, +0x00,0x00,0x1f,0x82,0xcd,0x3c,0x4f,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0,0x00, +0x20,0x00,0x00,0x9c,0x82,0xc9,0x3c,0x6e,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0, +0x00,0x20,0x00,0x00,0x9c,0x82,0xcd,0x3c,0x70,0x00,0x00,0x15,0x00,0xf0,0x00,0x09, +0xa0,0x00,0xc0,0x00,0x00,0x0b,0x80,0xc7,0x3c,0x43,0x00,0x00,0x15,0x00,0xf0,0x00, +0x09,0xa0,0x00,0xc0,0x00,0x00,0x0b,0x80,0xcb,0x3c,0x13,0x00,0x00,0x15,0x00,0xf0, +0x00,0x09,0xa0,0x00,0xc0,0x00,0x00,0x2f,0x82,0xc7,0x3c,0x73,0x00,0x00,0x29,0x00, +0xf0,0x00,0x09,0xa0,0x00,0xc0,0x00,0x00,0x30,0x82,0xc8,0x3c,0x79,0x00,0x00,0x29, +0x00,0xf0,0x00,0x09,0xa0,0x00,0x40,0x00,0x08,0x44,0x83,0xc9,0x3c,0x67,0x00,0x00, +0x15,0x00,0xf0,0x00,0x0b,0xa0,0x00,0x40,0x00,0x08,0x44,0x85,0xcc,0x3c,0x4f,0x00, +0x00,0x15,0x00,0xf0,0x00,0x0b,0xa0,0x00,0x30,0x00,0x00,0x24,0x82,0xba,0x3c,0x48, +0x00,0x00,0x1f,0x00,0xf0,0x00,0x09,0xa0,0x00,0x30,0x00,0x08,0x24,0x84,0xcc,0x3c, +0x70,0x00,0x00,0x33,0x00,0xf0,0x00,0x09,0xa0,0x00,0x20,0x00,0x00,0x20,0x80,0xcd, +0x3c,0x61,0x00,0x00,0x1f,0x00,0xf0,0x00,0x09,0xa0,0x00,0x40,0x00,0x00,0x2c,0x80, +0xca,0x3c,0x72,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0,0x00,0x40,0x00,0x00,0x2c, +0x80,0xd0,0x3c,0x5c,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0,0x00,0xe0,0x00,0x00, +0x22,0x82,0xcc,0x3c,0x61,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0,0x00,0xe0,0x00, +0x00,0x23,0x82,0xcc,0x3c,0x73,0x00,0x00,0x15,0x00,0xf0,0x00,0x09,0xa0,0x00,0xc0, +0x00,0x00,0x32,0x80,0xca,0x3c,0x7c,0x00,0x00,0x29,0x00,0xf0,0x08,0x09,0xa0,0x00, +0xc0,0x00,0x00,0x32,0x84,0xca,0x3c,0x61,0x00,0x00,0x29,0x00,0xf0,0x00,0x09,0xa0, +0x00,0xf0,0x00,0x00,0x2f,0x82,0xbe,0x3c,0x6f,0x00,0x00,0x29,0x00,0xf0,0x00,0x09, +0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0xc5,0x46,0xd6,0x46,0xe7,0x46,0xf8,0x47,0x09,0x00,0x00,0x47,0x1a, +0x47,0x2b,0x47,0x3c,0x47,0x4d,0x00,0x00,0x47,0x6f,0x47,0x80,0x47,0x91,0x47,0xa2, +0x47,0xb3,0x47,0xc4,0x47,0xd5,0x47,0xe6,0x47,0xf7,0x48,0x08,0x48,0x19,0x48,0x2a, +0x48,0x3b,0x48,0x4c,0x48,0x5d,0x48,0x6e,0x48,0x7f,0x48,0x90,0x48,0xa1,0x48,0xb2, +0x48,0xc3,0x48,0xd4,0x48,0xf6,0x49,0x07,0x49,0x18,0x49,0x29,0x49,0x3a,0x49,0x5c, +0x49,0x6d,0x49,0x7e,0x49,0x8f,0x49,0xa0,0x49,0xb1,0x49,0xc2,0x49,0xd3,0x49,0xe4, +0x49,0xf5,0x4a,0x06,0x4a,0x17,0x4a,0x28,0x4a,0x39,0x4a,0x4a,0x4a,0x5b,0x4a,0x6c, +0x4a,0x7d,0x4a,0x8e,0x4a,0x9f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d, +0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d, +0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0xff,0x27,0x28,0x29,0x2a,0x2b,0x2a,0x2d, +0x2a,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d, +0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x47,0x49,0x49,0x4b,0x4c,0x4d, +0x4e,0x4e,0x50,0x50,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d, +0x5e,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d, +0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d, +0x7e,0x7f,0x00,0x10,0x00,0x2c,0x81,0x32,0x3c,0x72,0x00,0x00,0x37,0x20,0xf2,0x13, +0x08,0xa0,0x00,0x00,0x10,0x00,0x2d,0x81,0x37,0x3c,0x60,0x00,0x00,0x37,0x20,0xf2, +0x14,0x08,0xa0,0x00,0x00,0x10,0x00,0x2e,0x81,0x3e,0x3c,0x4b,0x00,0x00,0x37,0x20, +0xf2,0x14,0x08,0xa0,0x00,0x00,0x10,0x00,0x2f,0x81,0x43,0x3c,0x6a,0x00,0x00,0x37, +0x20,0xf2,0x14,0x08,0xa0,0x00,0x0a,0x10,0x00,0x30,0x81,0x4b,0x3c,0x64,0x00,0x00, +0x37,0x20,0xf2,0x14,0x08,0xa0,0x00,0x0a,0x10,0x00,0x31,0x81,0x50,0x3c,0x69,0x00, +0x00,0x37,0x20,0xf2,0x14,0x08,0xa0,0x00,0x04,0x10,0x00,0x32,0x81,0x54,0x3c,0x6c, +0x00,0x00,0x37,0x20,0xf2,0x14,0x08,0xa0,0x00,0x03,0x10,0x00,0x33,0x81,0x5c,0x3c, +0x61,0x00,0x00,0x37,0x20,0xf2,0x14,0x18,0xa0,0x00,0x07,0x10,0x00,0x34,0x81,0x61, +0x3c,0x64,0x00,0x00,0x37,0x20,0xf3,0x14,0x18,0xa0,0x00,0x00,0x10,0x00,0x35,0x81, +0x5d,0x3c,0x3b,0x00,0x00,0x37,0x20,0xf4,0x15,0x08,0xa0,0x00,0x00,0x10,0x00,0x2c, +0x81,0x32,0x3c,0x72,0x00,0x00,0x37,0x20,0xf2,0x13,0x08,0xa0,0x01,0x00,0x10,0x00, +0x2d,0x81,0x37,0x3c,0x60,0x00,0x00,0x37,0x20,0xf2,0x14,0x08,0xa0,0x01,0x00,0x10, +0x00,0x2e,0x81,0x3e,0x3c,0x4b,0x00,0x00,0x37,0x20,0xf2,0x14,0x08,0xa0,0x01,0x00, +0x10,0x00,0x2f,0x81,0x43,0x3c,0x6a,0x00,0x00,0x37,0x20,0xf2,0x14,0x08,0xa0,0x01, +0x0a,0x10,0x00,0x30,0x81,0x4b,0x3c,0x64,0x00,0x00,0x37,0x20,0xf2,0x14,0x08,0xa0, +0x01,0x0a,0x10,0x00,0x31,0x81,0x50,0x3c,0x69,0x00,0x00,0x37,0x20,0xf2,0x14,0x08, +0xa0,0x01,0x04,0x10,0x00,0x32,0x81,0x54,0x3c,0x6c,0x00,0x00,0x37,0x20,0xf2,0x14, +0x08,0xa0,0x01,0x07,0x10,0x00,0x33,0x81,0x5c,0x3c,0x61,0x00,0x00,0x37,0x20,0xf2, +0x14,0x18,0xa0,0x01,0x0a,0x10,0x00,0x34,0x81,0x61,0x3c,0x64,0x00,0x00,0x37,0x20, +0xf3,0x14,0x18,0xa0,0x01,0x00,0x10,0x00,0x35,0x81,0x5d,0x3c,0x3b,0x00,0x00,0x37, +0x20,0xf4,0x15,0x08,0xa0,0x01,0x10,0x10,0x00,0x2c,0x81,0x32,0x3c,0x74,0x00,0x00, +0x51,0x20,0xf2,0x13,0x07,0xa0,0x02,0x10,0x10,0x00,0x2d,0x81,0x37,0x3c,0x62,0x00, +0x00,0x51,0x20,0xf2,0x14,0x07,0xa0,0x02,0x10,0x10,0x00,0x2e,0x81,0x3e,0x3c,0x4d, +0x00,0x00,0x51,0x20,0xf2,0x14,0x07,0xa0,0x02,0x10,0x10,0x00,0x2f,0x81,0x43,0x3c, +0x6c,0x00,0x00,0x51,0x20,0xf2,0x14,0x07,0xa0,0x02,0x10,0x10,0x00,0x30,0x81,0x4b, +0x3c,0x66,0x00,0x00,0x51,0x20,0xf2,0x14,0x07,0xa0,0x02,0x10,0x10,0x00,0x31,0x81, +0x50,0x3c,0x6b,0x00,0x00,0x51,0x20,0xf2,0x14,0x07,0xa0,0x02,0x10,0x10,0x00,0x32, +0x81,0x54,0x3c,0x6e,0x00,0x00,0x51,0x20,0xf2,0x14,0x07,0xa0,0x02,0x10,0x10,0x00, +0x33,0x81,0x5c,0x3c,0x63,0x00,0x00,0x51,0x20,0xf2,0x14,0x17,0xa0,0x02,0x10,0x10, +0x00,0x34,0x81,0x61,0x3c,0x66,0x00,0x00,0x51,0x20,0xf3,0x14,0x17,0xa0,0x02,0x10, +0x10,0x00,0x35,0x81,0x5d,0x3c,0x3d,0x00,0x00,0x51,0x20,0xf4,0x15,0x07,0xa0,0x02, +0xf0,0x10,0x00,0x6c,0x80,0x3a,0x2e,0x27,0x00,0x00,0x3d,0x28,0xf4,0x23,0x18,0xa0, +0x03,0x00,0x10,0x00,0x2c,0x81,0x32,0x3c,0x72,0x00,0x00,0x4b,0x20,0xf2,0x13,0x08, +0xa0,0x04,0x00,0x10,0x00,0x2d,0x81,0x37,0x3c,0x60,0x00,0x00,0x4b,0x20,0xf2,0x14, +0x08,0xa0,0x04,0x00,0x10,0x00,0x2e,0x81,0x3e,0x3c,0x4b,0x00,0x00,0x4b,0x20,0xf2, +0x14,0x08,0xa0,0x04,0x00,0x10,0x00,0x2f,0x81,0x43,0x3c,0x6a,0x00,0x00,0x4b,0x20, +0xf2,0x14,0x08,0xa0,0x04,0x0a,0x10,0x00,0x30,0x81,0x4b,0x3c,0x64,0x00,0x00,0x4b, +0x20,0xf2,0x14,0x08,0xa0,0x04,0x0a,0x10,0x00,0x31,0x81,0x50,0x3c,0x69,0x00,0x00, +0x4b,0x20,0xf2,0x14,0x08,0xa0,0x04,0x04,0x10,0x00,0x32,0x81,0x54,0x3c,0x6c,0x00, +0x00,0x4b,0x20,0xf2,0x14,0x08,0xa0,0x04,0x03,0x10,0x00,0x33,0x81,0x5c,0x3c,0x61, +0x00,0x00,0x4b,0x20,0xf2,0x14,0x18,0xa0,0x04,0x07,0x10,0x00,0x34,0x81,0x61,0x3c, +0x64,0x00,0x00,0x4b,0x20,0xf3,0x14,0x18,0xa0,0x04,0x00,0x10,0x00,0x35,0x81,0x5d, +0x3c,0x3b,0x00,0x00,0x4b,0x20,0xf4,0x15,0x08,0xa0,0x04,0x00,0x10,0x00,0x2c,0x81, +0x32,0x3c,0x7e,0x00,0x00,0x4b,0x20,0xf2,0x13,0x08,0xa0,0x04,0x00,0x10,0x00,0x2d, +0x81,0x37,0x3c,0x53,0x00,0x00,0x4b,0x20,0xf2,0x14,0x08,0xa0,0x04,0x00,0x10,0x00, +0x2e,0x81,0x3e,0x3c,0x58,0x00,0x00,0x4b,0x20,0xf2,0x14,0x08,0xa0,0x04,0x00,0x10, +0x00,0x2f,0x81,0x43,0x3c,0x5b,0x00,0x00,0x4b,0x20,0xf2,0x14,0x08,0xa0,0x04,0x0a, +0x10,0x00,0x30,0x81,0x4b,0x3c,0x72,0x00,0x00,0x4b,0x20,0xf2,0x14,0x08,0xa0,0x04, +0x0a,0x10,0x00,0x31,0x81,0x50,0x3c,0x5b,0x00,0x00,0x4b,0x20,0xf2,0x14,0x08,0xa0, +0x04,0x04,0x10,0x00,0x32,0x81,0x54,0x3c,0x79,0x00,0x00,0x4b,0x20,0xf2,0x14,0x08, +0xa0,0x04,0x07,0x10,0x00,0x33,0x81,0x5c,0x3c,0x5a,0x00,0x00,0x4b,0x20,0xf2,0x14, +0x18,0xa0,0x04,0x01,0x10,0x00,0x34,0x81,0x61,0x3c,0x71,0x00,0x00,0x4b,0x20,0xf3, +0x14,0x18,0xa0,0x04,0x00,0x10,0x00,0x35,0x81,0x5d,0x3c,0x2f,0x00,0x00,0x4b,0x20, +0xf4,0x15,0x08,0xa0,0x04,0x00,0x10,0x00,0x0b,0xa0,0x51,0x3c,0x6a,0x00,0x00,0xd7, +0x38,0xf0,0x00,0x0c,0xa0,0x05,0x00,0x12,0x00,0x6c,0x80,0x3a,0x3c,0x27,0x00,0x00, +0x4f,0x22,0xf4,0x23,0x19,0xa0,0x06,0x00,0x10,0x00,0x2c,0x81,0x32,0x3c,0x74,0x00, +0x00,0x5d,0x1b,0xf2,0x13,0x08,0xa0,0x07,0x00,0x10,0x00,0x2d,0x81,0x37,0x3c,0x62, +0x00,0x00,0x5d,0x1b,0xf2,0x14,0x08,0xa0,0x07,0x00,0x10,0x00,0x2e,0x81,0x3e,0x3c, +0x4d,0x00,0x00,0x5d,0x1b,0xf2,0x14,0x08,0xa0,0x07,0x00,0x10,0x00,0x2f,0x81,0x43, +0x3c,0x6c,0x00,0x00,0x5d,0x1b,0xf2,0x14,0x08,0xa0,0x07,0x0a,0x10,0x00,0x30,0x81, +0x4b,0x3c,0x66,0x00,0x00,0x5d,0x1b,0xf2,0x14,0x08,0xa0,0x07,0x0a,0x10,0x00,0x31, +0x81,0x50,0x3c,0x6b,0x00,0x00,0x5d,0x1b,0xf2,0x14,0x08,0xa0,0x07,0x04,0x10,0x00, +0x32,0x81,0x54,0x3c,0x6e,0x00,0x00,0x5d,0x1b,0xf2,0x14,0x08,0xa0,0x07,0x03,0x10, +0x00,0x33,0x81,0x5c,0x3c,0x63,0x00,0x00,0x5d,0x1b,0xf2,0x14,0x18,0xa0,0x07,0x07, +0x10,0x00,0x34,0x81,0x61,0x3c,0x66,0x00,0x00,0x5d,0x1b,0xf3,0x14,0x18,0xa0,0x07, +0x00,0x10,0x00,0x35,0x81,0x5d,0x3c,0x3d,0x00,0x00,0x5d,0x1b,0xf4,0x15,0x08,0xa0, +0x07,0x00,0x10,0x00,0x2c,0x81,0x32,0x68,0x70,0x00,0x00,0x5d,0x18,0xf2,0x13,0x08, +0xa0,0x04,0x00,0x10,0x00,0x2d,0x81,0x37,0x68,0x5e,0x00,0x00,0x5d,0x18,0xf2,0x14, +0x08,0xa0,0x04,0x00,0x10,0x00,0x2e,0x81,0x3e,0x68,0x49,0x00,0x00,0x5d,0x18,0xf2, +0x14,0x08,0xa0,0x04,0x00,0x10,0x00,0x2f,0x81,0x43,0x68,0x68,0x00,0x00,0x5d,0x18, +0xf2,0x14,0x08,0xa0,0x04,0x0a,0x10,0x00,0x30,0x81,0x4b,0x68,0x62,0x00,0x00,0x5d, +0x18,0xf2,0x14,0x08,0xa0,0x04,0x0a,0x10,0x00,0x31,0x81,0x50,0x68,0x67,0x00,0x00, +0x5d,0x18,0xf2,0x14,0x08,0xa0,0x04,0x04,0x10,0x00,0x32,0x81,0x54,0x68,0x6a,0x00, +0x00,0x5d,0x18,0xf2,0x14,0x08,0xa0,0x04,0x07,0x10,0x00,0x33,0x81,0x5c,0x68,0x5f, +0x00,0x00,0x5d,0x18,0xf2,0x14,0x18,0xa0,0x04,0x0a,0x10,0x00,0x34,0x81,0x61,0x68, +0x62,0x00,0x00,0x5d,0x18,0xf3,0x14,0x18,0xa0,0x04,0x00,0x10,0x00,0x35,0x81,0x5d, +0x68,0x39,0x00,0x00,0x5d,0x18,0xf4,0x15,0x08,0xa0,0x04,0x00,0x10,0x00,0x80,0x80, +0x44,0x3c,0x66,0x00,0x00,0x4d,0x20,0xf5,0x24,0x19,0xa0,0x08,0x00,0x10,0x00,0x81, +0x80,0x4a,0x3c,0x78,0x00,0x00,0x4d,0x20,0xf5,0x25,0x09,0xa0,0x08,0x00,0x10,0x00, +0x82,0x80,0x50,0x3c,0x66,0x00,0x00,0x4d,0x20,0xf5,0x25,0x09,0xa0,0x08,0x00,0x10, +0x00,0x83,0x80,0x56,0x3c,0x5b,0x00,0x00,0x4d,0x20,0xf5,0x25,0x19,0xa0,0x08,0x00, +0x10,0x00,0x84,0x80,0x62,0x3c,0x5b,0x00,0x00,0x4d,0x20,0xf5,0x25,0x29,0xa0,0x08, +0x00,0x10,0x00,0x27,0x90,0x51,0x3c,0x51,0x00,0x00,0x2d,0x28,0xf5,0x13,0x2b,0xa0, +0x09,0x00,0x10,0x00,0x28,0x90,0x5d,0x3c,0x67,0x00,0x00,0x2d,0x28,0xf5,0x13,0x3b, +0xa0,0x09,0x00,0x10,0x00,0x2b,0x80,0x52,0x3c,0x03,0x00,0x00,0x23,0x20,0xf4,0x15, +0x07,0xa3,0x0a,0x00,0x10,0x00,0xf3,0x60,0x52,0x3c,0x5d,0x00,0x00,0x3d,0x28,0xf6, +0x25,0x25,0xa0,0x0b,0x00,0x10,0x00,0xf3,0x80,0x52,0x3c,0x62,0x00,0x00,0x49,0x20, +0xa6,0x25,0x25,0xa0,0x0c,0x00,0x10,0x00,0x01,0x81,0x47,0x75,0x65,0x00,0x00,0x55, +0x20,0xd4,0x14,0x16,0xa0,0x0d,0x00,0x10,0x70,0x01,0x81,0x47,0x3c,0x6a,0x00,0x00, +0x3f,0x28,0xf4,0x14,0x16,0xa4,0x0e,0x00,0x10,0x00,0xf4,0x80,0x47,0x3c,0x6a,0x00, +0x00,0x3b,0x38,0xf7,0x47,0x08,0xa0,0x0f,0x00,0x10,0x00,0xf5,0x80,0x53,0x3c,0x51, +0x00,0x00,0x3b,0x38,0xf7,0x47,0x08,0xa0,0x0f,0x00,0x10,0x00,0xf5,0x80,0x53,0x3c, +0x51,0x00,0x00,0x3b,0x38,0xf7,0x48,0x08,0xa0,0x0f,0x00,0x10,0x00,0xf5,0x80,0x53, +0x3c,0x52,0x00,0x00,0x3b,0x38,0xf7,0x48,0x18,0xa0,0x0f,0x00,0x10,0x00,0x36,0x81, +0x5e,0x3c,0x01,0x00,0x00,0x2d,0x38,0xf0,0x06,0x36,0xa0,0x10,0x00,0x10,0x10,0xff, +0x80,0x4d,0x3c,0x5f,0x00,0x00,0x01,0x21,0xf4,0xa3,0x25,0xa1,0x11,0x00,0x14,0x00, +0x3f,0x80,0x4b,0x3c,0x4c,0x00,0x00,0x43,0x29,0xf5,0x16,0x07,0xa0,0x12,0x0e,0x13, +0x00,0x40,0x80,0x4b,0x3c,0x4c,0x00,0x00,0x6b,0x2a,0xf5,0x16,0x07,0xa0,0x13,0x04, +0x12,0x01,0x8e,0x80,0x4a,0x3c,0x6a,0x00,0x00,0x3d,0x3a,0xf0,0x00,0x0a,0xa0,0x14, +0x00,0x13,0x00,0x8c,0x80,0x3d,0x3c,0x6e,0x00,0x00,0x41,0x3b,0xf0,0x00,0x09,0xa0, +0x15,0x00,0x13,0x00,0x8d,0x80,0x55,0x3c,0x68,0x00,0x00,0x41,0x3b,0xf0,0x00,0x09, +0xa0,0x15,0x00,0x10,0x00,0x28,0x81,0x39,0x3c,0x6e,0x00,0x00,0x33,0x39,0xf0,0x00, +0x0a,0xa0,0x16,0x00,0x10,0x00,0x29,0x81,0x45,0x3c,0x70,0x00,0x00,0x33,0x39,0xf0, +0x00,0x0a,0xa0,0x16,0x00,0x10,0x00,0x2a,0x81,0x51,0x3c,0x6a,0x00,0x00,0x33,0x39, +0xf0,0x00,0x0a,0xa0,0x16,0x00,0x10,0x00,0x2b,0x81,0x5d,0x3c,0x65,0x00,0x00,0x33, +0x39,0xf0,0x00,0x0a,0xa0,0x16,0x00,0x10,0x00,0x27,0x71,0x5d,0x3c,0x67,0x00,0x00, +0x33,0x39,0xf0,0x00,0x0a,0xa0,0x16,0x00,0x10,0x00,0x87,0x80,0x32,0x3c,0x6a,0x00, +0x00,0x3b,0x11,0xf0,0x00,0x09,0xa0,0x17,0x00,0x10,0x00,0x88,0x80,0x3a,0x3c,0x78, +0x00,0x00,0x3b,0x11,0xf0,0x00,0x09,0xa0,0x17,0x00,0x10,0x00,0x89,0x80,0x42,0x3c, +0x74,0x00,0x00,0x3b,0x11,0xf0,0x00,0x09,0xa0,0x17,0x00,0x10,0x00,0x8a,0x80,0x4a, +0x3c,0x78,0x00,0x00,0x3b,0x11,0xf0,0x00,0x09,0xa0,0x17,0x00,0x10,0x00,0x8b,0x80, +0x51,0x3c,0x0e,0x00,0x00,0x3b,0x11,0xf0,0x00,0x09,0xa0,0x17,0x06,0x10,0x00,0xac, +0x80,0x40,0x3c,0x02,0x00,0x00,0x3f,0x38,0xf0,0x00,0x09,0xa1,0x18,0x00,0x10,0x00, +0xad,0x80,0x58,0x3c,0x01,0x00,0x00,0x3f,0x38,0xf0,0x00,0x09,0xa1,0x18,0x00,0x10, +0x00,0x06,0x80,0x4b,0x3c,0x65,0x00,0x00,0x5b,0x22,0x90,0x00,0x09,0xa0,0x19,0x00, +0x10,0x00,0x07,0x80,0x60,0x3c,0x3a,0x00,0x00,0x5b,0x22,0x90,0x00,0x09,0xa0,0x19, +0x00,0x10,0x00,0x70,0x80,0x4a,0x63,0x67,0x00,0x00,0x75,0x23,0xa0,0x00,0x09,0xa0, +0x1a,0x00,0x1c,0x00,0x70,0x80,0x4a,0x3c,0x78,0x00,0x00,0x51,0x30,0xa0,0x00,0x09, +0xa2,0x1b,0x00,0x10,0x00,0xac,0x80,0x40,0x3c,0x05,0x00,0x00,0x51,0x38,0xf0,0x00, +0x09,0xa0,0x1c,0x00,0x10,0x00,0xad,0x80,0x58,0x3c,0x04,0x00,0x00,0x51,0x38,0xf0, +0x00,0x09,0xa0,0x1c,0x00,0x10,0x00,0x41,0xa0,0x42,0x6b,0x5f,0x00,0x00,0x95,0x3a, +0x75,0x20,0x0a,0xa0,0x1d,0x05,0x10,0x00,0xb3,0x80,0x36,0x3c,0x74,0x00,0x00,0x35, +0x28,0xf5,0x34,0x09,0xa0,0x1e,0x0c,0x10,0x00,0xb7,0x80,0x41,0x3c,0x7f,0x00,0x00, +0x35,0x28,0xf5,0x34,0x09,0xa0,0x1e,0x00,0x10,0x00,0xb5,0x80,0x48,0x3c,0x6d,0x00, +0x00,0x35,0x28,0xf6,0x34,0x09,0xa0,0x1e,0x00,0x10,0x00,0xb4,0x80,0x4e,0x3c,0x74, +0x00,0x00,0x35,0x28,0xf6,0x35,0x09,0xa0,0x1e,0x00,0x10,0x00,0xb6,0x80,0x57,0x3c, +0x63,0x00,0x00,0x35,0x28,0xf6,0x35,0x19,0xa0,0x1e,0x00,0x13,0x00,0x0c,0x80,0x36, +0x3c,0x59,0x00,0x00,0x43,0x28,0xf0,0x04,0x19,0xa0,0x1f,0x00,0x13,0x00,0x0d,0x80, +0x42,0x3c,0x62,0x00,0x00,0x43,0x28,0xf0,0x05,0x09,0xa0,0x1f,0x00,0x13,0x00,0x0e, +0x80,0x4a,0x3c,0x5b,0x00,0x00,0x43,0x28,0xf5,0x94,0x09,0xa0,0x1f,0x00,0x13,0x00, +0x0f,0x80,0x56,0x3c,0x5b,0x00,0x00,0x43,0x28,0xf6,0x95,0x09,0xa0,0x1f,0x00,0x10, +0x00,0x5a,0x80,0x37,0x3c,0x70,0x00,0x00,0x35,0x28,0xf6,0x34,0x09,0xa0,0x20,0x00, +0x10,0x00,0x5b,0x80,0x46,0x3c,0x61,0x00,0x00,0x35,0x28,0xf6,0x34,0x09,0xa0,0x20, +0x00,0x10,0x00,0x5c,0x80,0x53,0x3c,0x52,0x00,0x00,0x35,0x28,0xf6,0x34,0x09,0xa0, +0x20,0x00,0x15,0x20,0x61,0x80,0x35,0x3c,0x4d,0x00,0x00,0x4b,0x29,0xf5,0x54,0x0a, +0xa0,0x21,0x00,0x15,0x20,0x60,0x80,0x3a,0x3c,0x62,0x00,0x00,0x4b,0x29,0xf5,0x54, +0x0a,0xa0,0x21,0x00,0x15,0x20,0x63,0x80,0x3f,0x3c,0x44,0x00,0x00,0x41,0x29,0xf5, +0x55,0x0a,0xa0,0x21,0x00,0x15,0x20,0x62,0x80,0x48,0x3c,0x53,0x00,0x00,0x4b,0x29, +0xf5,0x55,0x0a,0xa0,0x21,0x00,0x15,0x20,0x65,0x80,0x4d,0x3c,0x3b,0x00,0x00,0x4b, +0x29,0xf5,0x55,0x1a,0xa0,0x21,0x00,0x15,0x20,0x64,0x80,0x52,0x3c,0x31,0x00,0x00, +0x4b,0x29,0xf5,0x55,0x1a,0xa0,0x21,0x00,0x15,0x20,0x66,0x80,0x59,0x3c,0x5e,0x00, +0x00,0x4b,0x29,0xf5,0x55,0x2a,0xa0,0x21,0x00,0x15,0x20,0x67,0x80,0x62,0x3c,0x5b, +0x00,0x00,0x4b,0x29,0xf6,0x56,0x0a,0xa0,0x21,0x00,0x10,0x00,0x68,0x80,0x4a,0x3c, +0x78,0x00,0x00,0x33,0x28,0xf6,0x15,0x09,0xa0,0x22,0x00,0x13,0x00,0xa5,0x80,0x39, +0x3c,0x7d,0x00,0x00,0x3f,0x29,0xf2,0x11,0x09,0xa0,0x23,0x00,0x13,0x00,0xa6,0x80, +0x42,0x3c,0x74,0x00,0x00,0x3f,0x29,0xf2,0x11,0x09,0xa0,0x23,0x00,0x12,0x00,0x51, +0x80,0x36,0x3c,0x50,0x00,0x00,0x43,0x21,0xa2,0x12,0x0a,0xa0,0x24,0x00,0x12,0x00, +0x52,0x80,0x3a,0x3c,0x61,0x00,0x00,0x43,0x21,0xa2,0x12,0x0a,0xa0,0x24,0x00,0x12, +0x00,0x53,0x80,0x3e,0x3c,0x78,0x00,0x00,0x43,0x21,0xa2,0x12,0x0a,0xa0,0x24,0x00, +0x12,0x00,0x54,0x80,0x42,0x3c,0x4f,0x00,0x00,0x43,0x21,0xa2,0x12,0x0a,0xa0,0x24, +0x00,0x12,0x00,0x55,0x80,0x46,0x3c,0x61,0x00,0x00,0x43,0x21,0xa2,0x12,0x0a,0xa0, +0x24,0x00,0x12,0x00,0x56,0x80,0x4a,0x3c,0x78,0x00,0x00,0x43,0x21,0xa2,0x12,0x0a, +0xa0,0x24,0x00,0x12,0x00,0x57,0x80,0x4e,0x3c,0x74,0x00,0x00,0x43,0x21,0xa2,0x12, +0x0a,0xa0,0x24,0x00,0x12,0x00,0x58,0x80,0x52,0x3c,0x61,0x00,0x00,0x43,0x21,0xa2, +0x12,0x0a,0xa0,0x24,0x00,0x12,0x00,0x59,0x80,0x56,0x3c,0x20,0x00,0x00,0x43,0x21, +0xa2,0x12,0x0a,0xa0,0x24,0x00,0x10,0x00,0x5e,0x90,0x4d,0x3c,0x3b,0x00,0x00,0x31, +0x28,0xf4,0x24,0x09,0xa0,0x25,0x00,0x10,0x00,0x5d,0x90,0x52,0x3c,0x32,0x00,0x00, +0x1d,0x28,0xf4,0x24,0x09,0xa0,0x25,0x00,0x10,0x00,0x5f,0x90,0x57,0x3c,0x63,0x00, +0x00,0x31,0x28,0xf4,0x24,0x09,0xa0,0x25,0x00,0x10,0x00,0x04,0x80,0x2d,0x3c,0x35, +0x00,0x00,0x1d,0x18,0xf5,0x15,0x09,0xa0,0x26,0x00,0x10,0x00,0x05,0x80,0x47,0x3c, +0x52,0x00,0x00,0x1d,0x18,0xf5,0x15,0x09,0xa0,0x26,0x00,0x10,0x00,0x4a,0x80,0x28, +0x3c,0x7a,0x00,0x00,0x21,0x18,0xf6,0x14,0x09,0xa0,0x27,0x00,0x10,0x00,0x4b,0x80, +0x2d,0x3c,0x72,0x00,0x00,0x21,0x18,0xf6,0x14,0x09,0xa0,0x27,0x00,0x10,0x00,0x4c, +0x80,0x32,0x3c,0x73,0x00,0x00,0x21,0x18,0xf6,0x14,0x09,0xa0,0x27,0x00,0x10,0x00, +0x4d,0x80,0x37,0x3c,0x79,0x00,0x00,0x21,0x18,0xf6,0x14,0x09,0xa0,0x27,0x00,0x12, +0x00,0x4f,0x90,0x3a,0x3c,0x52,0x00,0x00,0x33,0x18,0xf3,0x90,0x0a,0xa0,0x28,0x05, +0x12,0x00,0x50,0x90,0x3f,0x3c,0x56,0x00,0x00,0x33,0x18,0xf3,0x90,0x1a,0xa0,0x28, +0x00,0x12,0x00,0x6b,0x90,0x44,0x3c,0x3e,0x00,0x00,0x33,0x18,0xf3,0x90,0x2a,0xa0, +0x28,0x00,0x12,0x00,0x69,0x90,0x49,0x3c,0x4f,0x00,0x00,0x33,0x18,0xf5,0xb0,0x0a, +0xa0,0x28,0x00,0x12,0x00,0x6a,0x90,0x4e,0x3c,0x51,0x00,0x00,0x33,0x18,0xf5,0xb0, +0x0a,0xa0,0x28,0x00,0x10,0x00,0x4e,0x80,0x43,0x3c,0x71,0x00,0x00,0x2d,0x10,0xf3, +0x63,0x19,0xa0,0x29,0x00,0x12,0x00,0xa3,0x90,0x3d,0x3c,0x76,0x00,0x00,0x21,0x19, +0xf5,0x32,0x1a,0xa0,0x2a,0x00,0x10,0x00,0xa2,0x90,0x44,0x3c,0x66,0x00,0x00,0x25, +0x20,0xb0,0x03,0x09,0xa0,0x2b,0x00,0x12,0x00,0xbe,0x90,0x3e,0x3c,0x6a,0x00,0x00, +0x47,0x39,0xf4,0x14,0x09,0xa0,0x2c,0x00,0x12,0x00,0x17,0x81,0x2d,0x3c,0x67,0x00, +0x00,0x35,0x39,0xf3,0x50,0x08,0xa0,0x2d,0x00,0x13,0x00,0x05,0x81,0x44,0x3c,0x66, +0x00,0x00,0x33,0x3b,0xf3,0x20,0x09,0xa0,0x2e,0x00,0x13,0x00,0x02,0x81,0x47,0x3c, +0x52,0x00,0x00,0x33,0x3b,0xf3,0x20,0x09,0xa0,0x2e,0x00,0x13,0x00,0x06,0x81,0x4c, +0x3c,0x64,0x00,0x00,0x33,0x3b,0xf3,0x20,0x09,0xa0,0x2e,0x00,0x13,0x00,0x07,0x81, +0x4c,0x3c,0x01,0x00,0x00,0x33,0x3b,0xf3,0x20,0x09,0xa0,0x2e,0x00,0x13,0x00,0x08, +0x81,0x50,0x3c,0x3c,0x00,0x00,0x33,0x3b,0xf3,0x20,0x09,0xa0,0x2e,0x00,0x13,0x00, +0x0a,0x81,0x55,0x3c,0x4d,0x00,0x00,0x33,0x3b,0xf3,0x20,0x09,0xa0,0x2e,0x00,0x13, +0x00,0x0b,0x81,0x57,0x3c,0x64,0x00,0x00,0x33,0x3b,0xf3,0x20,0x09,0xa0,0x2e,0x00, +0x13,0x00,0x0c,0x81,0x58,0x3c,0x22,0x00,0x00,0x33,0x3b,0xf3,0x20,0x09,0xa0,0x2e, +0x00,0x13,0x00,0x0d,0x81,0x5b,0x3c,0x36,0x00,0x00,0x33,0x3b,0xf3,0x20,0x09,0xa0, +0x2e,0x00,0x13,0x00,0x09,0x81,0x5e,0x3c,0x61,0x00,0x00,0x33,0x3b,0xf3,0x20,0x09, +0xa0,0x2e,0x00,0x12,0x00,0x03,0x81,0x3f,0x3c,0x54,0x00,0x00,0x3b,0x3b,0xa3,0x20, +0x09,0xa0,0x2f,0x00,0x12,0x00,0x04,0x81,0x41,0x3c,0x5e,0x00,0x00,0x3b,0x3b,0xa3, +0x20,0x09,0xa0,0x2f,0x00,0x12,0x00,0x05,0x81,0x44,0x3c,0x66,0x00,0x00,0x3b,0x3b, +0xa3,0x20,0x09,0xa0,0x2f,0x00,0x12,0x00,0x02,0x81,0x47,0x3c,0x52,0x00,0x00,0x35, +0x3b,0xa3,0x20,0x09,0xa0,0x2f,0x00,0x12,0x00,0x06,0x81,0x4c,0x3c,0x64,0x00,0x00, +0x3b,0x3b,0xa3,0x20,0x09,0xa0,0x2f,0x00,0x12,0x00,0x07,0x81,0x4c,0x3c,0x01,0x00, +0x00,0x3b,0x3b,0xa3,0x20,0x09,0xa0,0x2f,0x00,0x12,0x00,0x08,0x81,0x50,0x3c,0x3c, +0x00,0x00,0x2f,0x3b,0xa3,0x20,0x09,0xa0,0x2f,0x00,0x12,0x00,0x0a,0x81,0x55,0x3c, +0x4d,0x00,0x00,0x35,0x3b,0xa3,0x20,0x09,0xa0,0x2f,0x00,0x12,0x00,0x0b,0x81,0x57, +0x3c,0x64,0x00,0x00,0x2f,0x3b,0xa3,0x20,0x09,0xa0,0x2f,0x00,0x12,0x00,0x0c,0x81, +0x58,0x3c,0x22,0x00,0x00,0x35,0x3b,0xa3,0x20,0x09,0xa0,0x2f,0x00,0x12,0x00,0x0d, +0x81,0x5b,0x3c,0x36,0x00,0x00,0x2f,0x3b,0xa3,0x20,0x09,0xa0,0x2f,0x00,0x12,0x00, +0x09,0x81,0x5e,0x3c,0x61,0x00,0x00,0x2f,0x3b,0xa3,0x20,0x09,0xa0,0x2f,0x00,0x13, +0x00,0x12,0x91,0x45,0x3c,0x51,0x00,0x00,0x3f,0x33,0xa0,0x00,0x08,0xa0,0x30,0x00, +0x13,0x00,0x13,0x91,0x4b,0x3c,0x44,0x00,0x00,0x3f,0x33,0xa0,0x00,0x08,0xa0,0x30, +0x00,0x13,0x00,0x15,0x91,0x56,0x3c,0x5a,0x00,0x00,0x3f,0x33,0xa0,0x00,0x18,0xa0, +0x30,0x00,0x13,0x00,0x16,0x91,0x59,0x3c,0x5e,0x00,0x00,0x3f,0x33,0xa0,0x00,0x18, +0xa0,0x30,0x00,0x13,0x00,0x14,0x91,0x5f,0x3c,0x52,0x00,0x00,0x3f,0x33,0xa0,0x00, +0x18,0xa0,0x30,0x00,0x13,0x00,0x10,0x81,0x30,0x3c,0x61,0x00,0x00,0x3d,0x19,0x90, +0x00,0x09,0xa0,0x31,0x00,0x13,0x00,0x11,0x81,0x3c,0x3c,0x52,0x00,0x00,0x3d,0x19, +0x90,0x00,0x09,0xa0,0x31,0x0a,0x12,0x00,0xb0,0x80,0x46,0x3c,0x0a,0x00,0x00,0x21, +0x38,0xf0,0x00,0x07,0xa6,0x32,0x05,0x12,0x00,0x35,0x80,0x4c,0x3c,0x03,0x00,0x00, +0x21,0x38,0xf0,0x00,0x07,0xa6,0x32,0x05,0x12,0x00,0x33,0x80,0x54,0x3c,0x79,0x00, +0x00,0x21,0x38,0xf0,0x00,0x07,0xa6,0x32,0x05,0x12,0x00,0x34,0x80,0x57,0x3c,0x01, +0x00,0x00,0x21,0x38,0xf0,0x00,0x07,0xa6,0x32,0x00,0x12,0x00,0xaf,0x80,0x65,0x3c, +0x68,0x00,0x00,0x21,0x38,0xf0,0x00,0x07,0xa6,0x32,0x00,0x10,0x00,0xb8,0x80,0x3c, +0x3c,0x6a,0x00,0x00,0x43,0x28,0xf0,0x00,0x05,0xa0,0x33,0x00,0x10,0x00,0xb9,0x80, +0x45,0x3c,0x67,0x00,0x00,0x43,0x28,0xf0,0x00,0x05,0xa0,0x33,0x00,0x10,0x00,0xbb, +0x80,0x4c,0x3c,0x72,0x00,0x00,0x43,0x28,0xf0,0x00,0x05,0xa0,0x33,0x00,0x10,0x00, +0xba,0x80,0x53,0x3c,0x6d,0x00,0x00,0x3d,0x28,0xf0,0x00,0x05,0xa0,0x33,0x00,0x10, +0x00,0xbc,0x80,0x5a,0x3c,0x70,0x00,0x00,0x43,0x28,0xf0,0x00,0x05,0xa0,0x33,0x00, +0x10,0x00,0x7e,0x80,0x4f,0x3c,0x5c,0x00,0x00,0x2d,0x29,0xf5,0x25,0x07,0xa0,0x34, +0x00,0x10,0x00,0x7f,0x80,0x5a,0x3c,0x4f,0x00,0x00,0x2d,0x29,0xf5,0x25,0x07,0xa0, +0x34,0x00,0x10,0x70,0x00,0x81,0x39,0x3c,0x6a,0x00,0x00,0x15,0x28,0xf0,0x05,0x16, +0xa0,0x35,0x00,0x10,0x00,0x3c,0x81,0x46,0x3c,0x06,0x00,0x00,0x37,0x20,0x80,0x00, +0x07,0xa0,0x36,0x00,0x10,0x00,0x3e,0x81,0x4c,0x3c,0x03,0x00,0x00,0x37,0x20,0x80, +0x00,0x07,0xa0,0x36,0x00,0x10,0x00,0x3d,0x81,0x54,0x3c,0x72,0x00,0x00,0x37,0x20, +0x80,0x00,0x07,0xa0,0x36,0x00,0x10,0x00,0x3f,0x81,0x57,0x3c,0x05,0x00,0x00,0x41, +0x20,0x80,0x00,0x07,0xa0,0x36,0x00,0x10,0x00,0x40,0x81,0x65,0x3c,0x76,0x00,0x00, +0x41,0x20,0x80,0x00,0x07,0xa0,0x36,0x0a,0x10,0x00,0xb0,0x80,0x46,0x3c,0x0a,0x00, +0x00,0x41,0x19,0xf0,0x00,0x07,0xa0,0x37,0x05,0x10,0x00,0x35,0x80,0x4c,0x3c,0x03, +0x00,0x00,0x41,0x19,0xf0,0x00,0x07,0xa0,0x37,0x05,0x10,0x00,0x33,0x80,0x54,0x3c, +0x79,0x00,0x00,0x41,0x19,0xf0,0x00,0x07,0xa0,0x37,0x05,0x10,0x00,0x34,0x80,0x57, +0x3c,0x01,0x00,0x00,0x41,0x19,0xf0,0x00,0x07,0xa0,0x37,0x00,0x10,0x00,0xaf,0x80, +0x65,0x3c,0x68,0x00,0x00,0x41,0x19,0xf0,0x00,0x07,0xa0,0x37,0xe0,0x10,0x00,0x02, +0x90,0x49,0x3c,0x5d,0x00,0x00,0x59,0x20,0x93,0x22,0x06,0xa0,0x38,0x20,0x10,0x00, +0xae,0x70,0x47,0x3a,0x7d,0x00,0x00,0x39,0x20,0x70,0x01,0x06,0xa0,0x39,0x50,0x10, +0x00,0x02,0x90,0x55,0x3c,0x61,0x00,0x00,0x4b,0x19,0x70,0x00,0x06,0xa0,0x3a,0xb0, +0x10,0x00,0x02,0x90,0x55,0x79,0x55,0x00,0x00,0x4b,0x19,0x70,0x00,0x06,0xa0,0x3b, +0x00,0x10,0x00,0x18,0x80,0x45,0x3c,0x52,0x00,0x00,0x3d,0x1a,0x70,0x00,0x08,0xa0, +0x3c,0x00,0x10,0x00,0x19,0x80,0x4a,0x3c,0x52,0x00,0x00,0x3d,0x1a,0x70,0x00,0x08, +0xa0,0x3c,0x00,0x10,0x00,0x1a,0x80,0x51,0x3c,0x56,0x00,0x00,0x3d,0x1a,0x70,0x00, +0x08,0xa0,0x3c,0x00,0x10,0x00,0x1b,0x80,0x5b,0x3c,0x55,0x00,0x00,0x3d,0x1a,0x70, +0x00,0x08,0xa0,0x3c,0x00,0x10,0x00,0x29,0x80,0x50,0x3c,0x4c,0x00,0x00,0x19,0x20, +0xf7,0x20,0x08,0xa0,0x3d,0x00,0x10,0x00,0x2a,0x80,0x51,0x3c,0x5a,0x00,0x00,0x3d, +0x19,0x85,0x21,0x07,0xa0,0x3e,0x00,0x10,0x00,0x49,0x70,0x3e,0x3c,0x6a,0x00,0x00, +0x01,0x30,0x80,0x05,0x05,0xa0,0x3f,0x00,0x12,0x00,0xf6,0x80,0x47,0x3c,0x22,0x00, +0x00,0x29,0x32,0xf3,0x20,0x0a,0xa0,0x40,0x00,0x12,0x00,0xf8,0x80,0x4e,0x3c,0x76, +0x00,0x00,0x29,0x32,0xf3,0x20,0x0a,0xa0,0x40,0x00,0x12,0x00,0xf7,0x80,0x54,0x3c, +0x6e,0x00,0x00,0x29,0x32,0xf3,0x20,0x0a,0xa0,0x40,0x00,0x12,0x00,0xfa,0x80,0x5a, +0x3c,0x52,0x00,0x00,0x29,0x32,0xf3,0x20,0x0a,0xa0,0x40,0x00,0x12,0x00,0xf9,0x80, +0x5f,0x3c,0x22,0x00,0x00,0x29,0x32,0xf3,0x20,0x0a,0xa0,0x40,0x00,0x12,0x00,0xfb, +0x80,0x66,0x3c,0x52,0x00,0x00,0x29,0x32,0xf3,0x20,0x0a,0xa0,0x40,0x00,0x13,0x00, +0xf0,0x80,0x45,0x3c,0x7d,0x00,0x00,0x29,0x21,0xf0,0x00,0x09,0xa0,0x41,0x00,0x13, +0x00,0xf1,0x80,0x4a,0x3c,0x02,0x00,0x00,0x29,0x21,0xf0,0x00,0x09,0xa0,0x41,0x00, +0x13,0x00,0xf2,0x80,0x50,0x3c,0x3c,0x00,0x00,0x29,0x21,0xf0,0x00,0x09,0xa0,0x41, +0x00,0x13,0x00,0x85,0x80,0x35,0x3c,0x78,0x00,0x00,0x21,0x21,0xf5,0x10,0x09,0xa0, +0x42,0x00,0x13,0x00,0x86,0x80,0x3d,0x3c,0x3e,0x00,0x00,0x21,0x21,0xf5,0x10,0x09, +0xa0,0x42,0x00,0x15,0x00,0xb1,0x80,0x4c,0x3c,0x67,0x00,0x00,0x33,0x28,0xf3,0xf0, +0x0a,0xa1,0x43,0x00,0x15,0x00,0xb2,0x80,0x58,0x3c,0x67,0x00,0x00,0x33,0x28,0x83, +0xf0,0x0a,0xa1,0x43,0x00,0x13,0x00,0x7c,0x80,0x50,0x3c,0x68,0x00,0x00,0x2f,0x1a, +0xf0,0x00,0x09,0xa0,0x44,0x00,0x13,0x00,0x7d,0x80,0x57,0x3c,0x28,0x00,0x00,0x2f, +0x1a,0xf0,0x00,0x09,0xa0,0x44,0x00,0x1a,0x00,0xfc,0x80,0x4a,0x3c,0x57,0x00,0x00, +0x29,0x28,0xf0,0x00,0x0a,0xa0,0x45,0x00,0x1a,0x00,0xfd,0x80,0x56,0x3c,0x66,0x00, +0x00,0x29,0x28,0xf0,0x00,0x0a,0xa0,0x45,0xf0,0x14,0x00,0xd3,0x80,0x25,0x3c,0x49, +0xfc,0x04,0x41,0x18,0xa5,0x11,0x08,0xa0,0x46,0xf0,0x14,0x00,0xda,0x80,0x2b,0x3c, +0x52,0xfc,0x04,0x41,0x18,0xa5,0x11,0x08,0xa0,0x46,0xf0,0x14,0x00,0xd4,0x80,0x31, +0x3c,0x49,0xfc,0x04,0x41,0x18,0xa5,0x11,0x08,0xa0,0x46,0xf0,0x14,0x00,0xdb,0x80, +0x37,0x3c,0x44,0xfc,0x04,0x41,0x18,0xa5,0x11,0x08,0xa0,0x46,0xf0,0x14,0x00,0xd5, +0x80,0x3d,0x3c,0x5d,0xfc,0x04,0x41,0x18,0xa5,0x11,0x08,0xa0,0x46,0xf0,0x14,0x00, +0xdc,0x80,0x43,0x3c,0x4c,0xfc,0x04,0x41,0x18,0xa5,0x11,0x08,0xa0,0x46,0xf0,0x14, +0x00,0xd6,0x80,0x49,0x3c,0x50,0xfc,0x04,0x41,0x18,0xa5,0x11,0x08,0xa0,0x46,0xf0, +0x14,0x00,0xdd,0x80,0x4f,0x3c,0x60,0xfc,0x04,0x41,0x18,0xa5,0x11,0x08,0xa0,0x46, +0xf0,0x14,0x00,0xd7,0x80,0x54,0x3c,0x07,0xfc,0x04,0x41,0x18,0xa5,0x11,0x08,0xa0, +0x46,0xf0,0x14,0x00,0xde,0x80,0x5b,0x3c,0x3a,0xfc,0x04,0x41,0x18,0xa5,0x11,0x08, +0xa0,0x46,0xf0,0x14,0x00,0xd8,0x80,0x61,0x3c,0x50,0xfc,0x04,0x41,0x18,0xa5,0x11, +0x08,0xa0,0x46,0xf0,0x14,0x00,0xd9,0x80,0x6d,0x3c,0x50,0xfc,0x04,0x41,0x18,0xa5, +0x11,0x08,0xa0,0x46,0x10,0x12,0x00,0x18,0x81,0x25,0x79,0x57,0xfc,0x04,0x41,0x19, +0x85,0x23,0x08,0xa0,0x47,0x10,0x12,0x00,0x19,0x81,0x2b,0x79,0x5c,0xfc,0x04,0x41, +0x19,0x85,0x23,0x08,0xa0,0x47,0x10,0x12,0x00,0x1a,0x81,0x31,0x79,0x55,0xfc,0x04, +0x41,0x19,0x85,0x23,0x08,0xa0,0x47,0x10,0x12,0x00,0x1b,0x81,0x37,0x79,0x5c,0xfc, +0x04,0x41,0x19,0x85,0x23,0x08,0xa0,0x47,0x10,0x12,0x00,0x1c,0x81,0x3d,0x79,0x62, +0xfc,0x04,0x41,0x19,0x85,0x23,0x08,0xa0,0x47,0x10,0x12,0x00,0x1d,0x81,0x43,0x79, +0x65,0xfc,0x04,0x41,0x19,0x85,0x23,0x08,0xa0,0x47,0x10,0x12,0x00,0x1e,0x81,0x49, +0x79,0x70,0xfc,0x04,0x41,0x19,0x85,0x23,0x08,0xa0,0x47,0x10,0x12,0x00,0x1f,0x81, +0x4f,0x79,0x65,0xfc,0x04,0x41,0x19,0x85,0x23,0x08,0xa0,0x47,0x10,0x12,0x00,0x20, +0x81,0x54,0x79,0x0c,0xfc,0x04,0x41,0x19,0x85,0x23,0x08,0xa0,0x47,0x10,0x12,0x00, +0x21,0x81,0x5b,0x79,0x3e,0xfc,0x04,0x41,0x19,0x85,0x23,0x08,0xa0,0x47,0x10,0x12, +0x00,0x22,0x81,0x61,0x79,0x55,0xfc,0x04,0x41,0x19,0x85,0x23,0x08,0xa0,0x47,0x10, +0x12,0x00,0x23,0x81,0x6d,0x79,0x55,0xfc,0x04,0x41,0x19,0x85,0x23,0x08,0xa0,0x47, +0x30,0x10,0x00,0x18,0x81,0x25,0x3c,0x59,0x00,0x00,0x51,0x26,0x85,0x23,0x08,0xa0, +0x48,0x30,0x10,0x00,0x19,0x81,0x2b,0x3c,0x5e,0x00,0x00,0x51,0x26,0x85,0x23,0x08, +0xa0,0x48,0x30,0x10,0x00,0x1a,0x81,0x31,0x3c,0x57,0x00,0x00,0x51,0x26,0x85,0x23, +0x08,0xa0,0x48,0x30,0x10,0x00,0x1b,0x81,0x37,0x3c,0x5e,0x00,0x00,0x51,0x26,0x85, +0x23,0x08,0xa0,0x48,0x30,0x10,0x00,0x1c,0x81,0x3d,0x3c,0x64,0x00,0x00,0x51,0x26, +0x85,0x23,0x08,0xa0,0x48,0x30,0x10,0x00,0x1d,0x81,0x43,0x3c,0x67,0x00,0x00,0x51, +0x26,0x85,0x23,0x08,0xa0,0x48,0x30,0x10,0x00,0x1e,0x81,0x49,0x3c,0x72,0x00,0x00, +0x51,0x26,0x85,0x23,0x08,0xa0,0x48,0x30,0x10,0x00,0x1f,0x81,0x4f,0x3c,0x67,0x00, +0x00,0x51,0x26,0x85,0x23,0x08,0xa0,0x48,0x30,0x10,0x00,0x20,0x81,0x54,0x3c,0x0e, +0x00,0x00,0x51,0x26,0x85,0x23,0x08,0xa0,0x48,0x30,0x10,0x00,0x21,0x81,0x5b,0x3c, +0x40,0x00,0x00,0x51,0x26,0x85,0x23,0x08,0xa0,0x48,0x30,0x10,0x00,0x22,0x81,0x61, +0x3c,0x57,0x00,0x00,0x51,0x26,0x85,0x23,0x08,0xa0,0x48,0x30,0x10,0x00,0x23,0x81, +0x6d,0x3c,0x57,0x00,0x00,0x51,0x26,0x85,0x23,0x08,0xa0,0x48,0xd0,0x10,0x00,0x24, +0x71,0x40,0x79,0x02,0x00,0x00,0x15,0x22,0x85,0x23,0x08,0xa0,0x49,0x00,0x12,0x00, +0xe3,0x80,0x4b,0x3c,0x44,0x00,0x00,0x37,0x21,0xf5,0x20,0x0a,0xa0,0x4a,0x00,0x12, +0x00,0xe4,0x80,0x51,0x3c,0x67,0x00,0x00,0x37,0x21,0xf5,0x20,0x0a,0xa0,0x4a,0x00, +0x12,0x00,0xe5,0x80,0x57,0x3c,0x24,0x00,0x00,0x29,0x21,0xf5,0x20,0x0a,0xa0,0x4a, +0x00,0x12,0x00,0xe6,0x80,0x5c,0x3c,0x12,0x00,0x00,0x29,0x21,0xf5,0x20,0x0a,0xa0, +0x4a,0x00,0x12,0x00,0xe7,0x80,0x63,0x3c,0x62,0x00,0x00,0x29,0x21,0xf5,0x20,0x0a, +0xa0,0x4a,0x00,0x12,0x00,0xe8,0x80,0x5e,0x3c,0x02,0x00,0x00,0x37,0x21,0xf5,0x20, +0x0a,0xa0,0x4a,0x00,0x12,0x00,0x92,0x80,0x3c,0x3c,0x7c,0x00,0x00,0x41,0x19,0xf5, +0x20,0x0b,0xa0,0x4b,0x00,0x12,0x00,0x96,0x80,0x3f,0x3c,0x74,0x00,0x00,0x41,0x19, +0xf5,0x20,0x0b,0xa0,0x4b,0x00,0x12,0x00,0x99,0x80,0x42,0x3c,0x06,0x00,0x00,0x41, +0x19,0xf5,0x20,0x0b,0xa0,0x4b,0x00,0x12,0x00,0x8f,0x80,0x44,0x3c,0x14,0x00,0x00, +0x41,0x19,0xf5,0x20,0x0b,0xa0,0x4b,0x00,0x12,0x00,0x93,0x80,0x47,0x3c,0x22,0x00, +0x00,0x41,0x19,0xf5,0x20,0x0b,0xa0,0x4b,0x00,0x12,0x00,0x97,0x80,0x4a,0x3c,0x21, +0x00,0x00,0x41,0x19,0xf5,0x20,0x0b,0xa0,0x4b,0x00,0x12,0x00,0x9a,0x80,0x4e,0x3c, +0x75,0x00,0x00,0x41,0x19,0xf5,0x20,0x0b,0xa0,0x4b,0x00,0x12,0x00,0x90,0x80,0x51, +0x3c,0x69,0x00,0x00,0x41,0x19,0xf5,0x20,0x0b,0xa0,0x4b,0x00,0x12,0x00,0x94,0x80, +0x54,0x3c,0x6f,0x00,0x00,0x41,0x19,0xf5,0x20,0x0b,0xa0,0x4b,0x00,0x12,0x00,0x98, +0x80,0x57,0x3c,0x66,0x00,0x00,0x41,0x19,0xf5,0x20,0x0b,0xa0,0x4b,0x00,0x12,0x00, +0x9b,0x80,0x59,0x3c,0x19,0x00,0x00,0x41,0x19,0xf5,0x20,0x0b,0xa0,0x4b,0x00,0x12, +0x00,0x91,0x80,0x5c,0x3c,0x68,0x00,0x00,0x41,0x19,0xf5,0x20,0x0b,0xa0,0x4b,0x00, +0x12,0x00,0x95,0x80,0x5e,0x3c,0x04,0x00,0x00,0x41,0x19,0xf5,0x20,0x0b,0xa0,0x4b, +0x00,0x12,0x00,0xe9,0x80,0x3b,0x3c,0x5d,0x00,0x00,0x43,0x19,0xf4,0x10,0x0b,0xa0, +0x4c,0x00,0x12,0x00,0xea,0x80,0x3f,0x3c,0x55,0x00,0x00,0x43,0x19,0xf4,0x10,0x0b, +0xa0,0x4c,0x00,0x12,0x00,0xeb,0x80,0x45,0x3c,0x7d,0x00,0x00,0x43,0x19,0xf4,0x10, +0x0b,0xa0,0x4c,0x00,0x12,0x00,0xed,0x80,0x4b,0x3c,0x05,0x00,0x00,0x43,0x19,0xf4, +0x10,0x0b,0xa0,0x4c,0x00,0x12,0x00,0xec,0x80,0x53,0x3c,0x52,0x00,0x00,0x43,0x19, +0xf4,0x10,0x0b,0xa0,0x4c,0x00,0x12,0x00,0xee,0x80,0x59,0x3c,0x5e,0x00,0x00,0x43, +0x19,0xf4,0x10,0x0b,0xa0,0x4c,0x00,0x12,0x00,0xef,0x80,0x61,0x3c,0x4d,0x00,0x00, +0x43,0x19,0xf4,0x10,0x0b,0xa0,0x4c,0x00,0x12,0x00,0xdf,0x80,0x38,0x3c,0x7a,0x00, +0x00,0x31,0x19,0xf0,0x00,0x0a,0xa0,0x4d,0x00,0x12,0x00,0xe1,0x80,0x41,0x3c,0x70, +0x00,0x00,0x31,0x19,0xf0,0x00,0x0a,0xa0,0x4d,0x00,0x12,0x00,0xe2,0x80,0x44,0x3c, +0x7a,0x00,0x00,0x31,0x19,0xf0,0x00,0x0a,0xa0,0x4d,0x00,0x12,0x00,0xe0,0x80,0x49, +0x3c,0x15,0x00,0x00,0x31,0x19,0xf0,0x00,0x0a,0xa0,0x4d,0x00,0x12,0x00,0x42,0x80, +0x49,0x3c,0x4d,0x00,0x00,0x19,0x39,0xf4,0x10,0x0a,0xa0,0x4e,0x00,0x12,0x00,0x44, +0x80,0x4f,0x3c,0x36,0x00,0x00,0x23,0x39,0xf4,0x10,0x0a,0xa0,0x4e,0x00,0x12,0x00, +0x43,0x80,0x55,0x3c,0x4c,0x00,0x00,0x23,0x39,0xf4,0x10,0x0a,0xa0,0x4e,0x00,0x12, +0x00,0x46,0x80,0x5b,0x3c,0x36,0x00,0x00,0x23,0x39,0xf4,0x10,0x0a,0xa0,0x4e,0x00, +0x12,0x00,0x45,0x80,0x61,0x3c,0x4c,0x00,0x00,0x23,0x39,0xf4,0x10,0x0a,0xa0,0x4e, +0x00,0x12,0x00,0x47,0x80,0x67,0x3c,0x36,0x00,0x00,0x19,0x39,0xf4,0x10,0x0a,0xa0, +0x4e,0x00,0x12,0x00,0x3c,0x80,0x44,0x3c,0x2a,0x00,0x00,0x3b,0x31,0xf0,0x00,0x09, +0xa0,0x4f,0x00,0x12,0x00,0x3b,0x80,0x4a,0x3c,0x03,0x00,0x00,0x3b,0x31,0xf0,0x00, +0x09,0xa0,0x4f,0x00,0x12,0x00,0x3d,0x80,0x50,0x3c,0x14,0x00,0x00,0x3b,0x31,0xf0, +0x00,0x09,0xa0,0x4f,0x00,0x13,0x00,0x38,0x80,0x2f,0x3c,0x59,0x00,0x00,0x39,0x31, +0xf0,0x00,0x0b,0xa0,0x50,0x00,0x13,0x00,0x3a,0x80,0x35,0x3c,0x5e,0x00,0x00,0x39, +0x31,0xf0,0x00,0x0b,0xa0,0x50,0x00,0x13,0x00,0x39,0x80,0x3b,0x3c,0x5e,0x00,0x00, +0x39,0x31,0xf0,0x00,0x0b,0xa0,0x50,0x00,0x13,0x00,0x9e,0x80,0x3e,0x3c,0x4c,0x00, +0x00,0x37,0x29,0xf3,0x20,0x0a,0xa0,0x51,0x00,0x13,0x00,0xa0,0x80,0x44,0x3c,0x66, +0x00,0x00,0x37,0x29,0xf3,0x20,0x0a,0xa0,0x51,0x00,0x13,0x00,0x9f,0x80,0x4b,0x3c, +0x64,0x00,0x00,0x37,0x29,0xf3,0x20,0x0a,0xa0,0x51,0x00,0x13,0x00,0xa1,0x80,0x55, +0x3c,0x4d,0x00,0x00,0x37,0x29,0xf3,0x20,0x0a,0xa0,0x51,0x00,0x10,0x00,0x71,0x80, +0x46,0x3c,0x03,0x00,0x00,0x19,0x38,0xf0,0x00,0x0a,0xa2,0x52,0x00,0x10,0x00,0x72, +0x80,0x52,0x3c,0x32,0x00,0x00,0x19,0x38,0xf0,0x00,0x0a,0xa2,0x52,0x00,0x10,0x00, +0x73,0x80,0x5e,0x3c,0x03,0x00,0x00,0x19,0x38,0xf0,0x00,0x0a,0xa2,0x52,0x00,0x10, +0x00,0x74,0x80,0x5c,0x3c,0x65,0x00,0x00,0x19,0x38,0xf0,0x00,0x0a,0xa2,0x52,0x00, +0x10,0x00,0x75,0x80,0x61,0x3c,0x4d,0x00,0x00,0x19,0x38,0xf0,0x00,0x0a,0xa2,0x52, +0x00,0x16,0x00,0x71,0x80,0x46,0x3c,0x03,0x00,0x00,0x23,0x38,0xf0,0x00,0x0a,0xa2, +0x53,0x00,0x16,0x00,0x72,0x80,0x52,0x3c,0x32,0x00,0x00,0x23,0x38,0xf0,0x00,0x0a, +0xa2,0x53,0x00,0x16,0x00,0x73,0x80,0x5e,0x3c,0x03,0x00,0x00,0x19,0x38,0xf0,0x00, +0x0a,0xa2,0x53,0x00,0x10,0x00,0xbd,0x80,0x46,0x3c,0x61,0x00,0x00,0x13,0x30,0x70, +0x00,0x09,0xa1,0x54,0x00,0x10,0x00,0x77,0x80,0x5a,0x3c,0x77,0x00,0x00,0x21,0x38, +0xf0,0x00,0x09,0xa3,0x55,0x00,0x13,0x00,0x77,0x80,0x5a,0x3c,0x77,0x00,0x00,0x37, +0x38,0xf0,0x00,0x09,0xa1,0x56,0x0e,0x10,0x01,0x25,0x81,0x52,0x3c,0x6d,0xfa,0x12, +0x7f,0x00,0xf0,0x00,0x09,0xa0,0x57,0x00,0x10,0x00,0xab,0x80,0x48,0x3c,0x04,0x00, +0x00,0x29,0x30,0xf0,0x00,0x0a,0xa3,0x58,0x00,0x12,0x00,0x76,0x80,0x4f,0x3c,0x44, +0x00,0x00,0x5d,0x28,0x70,0x00,0x09,0xa2,0x59,0x00,0x10,0x00,0xaa,0x80,0x5e,0x3c, +0x03,0xfb,0x01,0x2d,0x2c,0x70,0x00,0x0a,0xa0,0x5a,0x00,0x10,0x00,0xaa,0x80,0x5e, +0x3c,0x03,0x00,0x00,0x31,0x29,0x90,0x00,0x0a,0xa1,0x5b,0x30,0x10,0x00,0xcc,0x90, +0x2b,0x3c,0x3d,0x00,0x00,0x53,0x38,0xc6,0x21,0x09,0xa0,0x5c,0x30,0x10,0x00,0xcd, +0x80,0x37,0x3c,0x41,0x00,0x00,0x53,0x38,0xc6,0x21,0x09,0xa0,0x5c,0x30,0x10,0x00, +0xca,0x80,0x43,0x3c,0x40,0x00,0x00,0x53,0x38,0xc6,0x21,0x09,0xa0,0x5c,0x30,0x10, +0x00,0xcf,0x80,0x4f,0x3c,0x41,0x00,0x00,0x53,0x38,0xc6,0x21,0x09,0xa0,0x5c,0x30, +0x10,0x00,0xce,0x80,0x5b,0x3c,0x40,0x00,0x00,0x53,0x38,0xc6,0x21,0x09,0xa0,0x5c, +0x30,0x10,0x00,0xcb,0x80,0x67,0x3c,0x40,0x00,0x00,0x53,0x38,0xc6,0x21,0x09,0xa0, +0x5c,0xd0,0x10,0x00,0xcc,0x90,0x2b,0x79,0x34,0x00,0x00,0x53,0x08,0xc6,0x21,0x09, +0xa0,0x5d,0xd0,0x10,0x00,0xcd,0x80,0x37,0x79,0x38,0x00,0x00,0x53,0x08,0xc6,0x21, +0x09,0xa0,0x5d,0xd0,0x10,0x00,0xca,0x80,0x43,0x79,0x37,0x00,0x00,0x53,0x08,0xc6, +0x21,0x09,0xa0,0x5d,0xd0,0x10,0x00,0xcf,0x80,0x4f,0x79,0x38,0x00,0x00,0x53,0x08, +0xc6,0x21,0x09,0xa0,0x5d,0xd0,0x10,0x00,0xce,0x80,0x5b,0x79,0x37,0x00,0x00,0x53, +0x08,0xc6,0x21,0x09,0xa0,0x5d,0xd0,0x10,0x00,0xcb,0x80,0x67,0x79,0x37,0x00,0x00, +0x53,0x08,0xc6,0x21,0x09,0xa0,0x5d,0x00,0x10,0x00,0x18,0x81,0x25,0x3c,0x54,0x00, +0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x19,0x81,0x2b,0x3c,0x59, +0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x1a,0x81,0x31,0x3c, +0x52,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x1b,0x81,0x37, +0x3c,0x59,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x1c,0x81, +0x3d,0x3c,0x5f,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x1d, +0x81,0x43,0x3c,0x62,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00, +0x1e,0x81,0x49,0x3c,0x6d,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10, +0x00,0x1f,0x81,0x4f,0x3c,0x62,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00, +0x10,0x00,0x20,0x81,0x54,0x3c,0x09,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e, +0x00,0x10,0x00,0x21,0x81,0x5b,0x3c,0x3b,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0, +0x5e,0x00,0x10,0x00,0x22,0x81,0x61,0x3c,0x52,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a, +0xa0,0x5e,0x00,0x10,0x00,0x23,0x81,0x6d,0x3c,0x52,0x00,0x00,0x37,0x30,0xf2,0x22, +0x0a,0xa0,0x5e,0x00,0x10,0x00,0x18,0x81,0x25,0x3c,0x4a,0x00,0x00,0x37,0x30,0xf2, +0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x19,0x81,0x2b,0x3c,0x4f,0x00,0x00,0x37,0x30, +0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x1a,0x81,0x31,0x3c,0x48,0x00,0x00,0x37, +0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x1b,0x81,0x37,0x3c,0x4f,0x00,0x00, +0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x1c,0x81,0x3d,0x3c,0x55,0x00, +0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x1d,0x81,0x43,0x3c,0x58, +0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x1e,0x81,0x49,0x3c, +0x63,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x1f,0x81,0x4f, +0x3c,0x58,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x20,0x81, +0x55,0x3c,0x7f,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00,0x21, +0x81,0x5b,0x3c,0x31,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10,0x00, +0x22,0x81,0x61,0x3c,0x48,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00,0x10, +0x00,0x23,0x81,0x6d,0x3c,0x48,0x00,0x00,0x37,0x30,0xf2,0x22,0x0a,0xa0,0x5e,0x00, +0x10,0x00,0xaa,0x80,0x5e,0x3c,0x03,0x00,0x00,0x3d,0x28,0x90,0x00,0x0a,0xa2,0x5f, +0x00,0x10,0x00,0x76,0x80,0x4f,0x79,0x44,0x00,0x00,0x49,0x28,0xb0,0x00,0x09,0xa2, +0x60,0x00,0x12,0x00,0x3a,0x71,0x43,0x3c,0x11,0x00,0x00,0x33,0x2a,0xf0,0x00,0x09, +0xa1,0x61,0x00,0x10,0x00,0xfe,0x80,0x4f,0x79,0x0c,0x00,0x00,0x41,0x28,0xf3,0x00, +0x17,0xa0,0x62,0x00,0x12,0x00,0xa5,0x80,0x38,0x3c,0x02,0x00,0x00,0x45,0x33,0xf2, +0x11,0x09,0xa0,0x63,0x00,0x12,0x00,0xa6,0x80,0x42,0x3c,0x79,0x00,0x00,0x45,0x33, +0xf2,0x11,0x09,0xa0,0x63,0x00,0x13,0x00,0x18,0x81,0x25,0x79,0x4a,0x00,0x00,0x5b, +0x2a,0xf2,0x22,0x0e,0xa0,0x64,0x00,0x13,0x00,0x19,0x81,0x2b,0x79,0x4f,0x00,0x00, +0x5b,0x2a,0xf2,0x22,0x0e,0xa0,0x64,0x00,0x13,0x00,0x1a,0x81,0x31,0x79,0x48,0x00, +0x00,0x5b,0x2a,0xf2,0x22,0x0e,0xa0,0x64,0x00,0x13,0x00,0x1b,0x81,0x37,0x79,0x4f, +0x00,0x00,0x5b,0x2a,0xf2,0x22,0x0e,0xa0,0x64,0x00,0x13,0x00,0x1c,0x81,0x3d,0x79, +0x55,0x00,0x00,0x5b,0x2a,0xf2,0x22,0x0e,0xa0,0x64,0x00,0x13,0x00,0x1d,0x81,0x43, +0x79,0x58,0x00,0x00,0x5b,0x2a,0xf2,0x22,0x0e,0xa0,0x64,0x00,0x13,0x00,0x1e,0x81, +0x49,0x79,0x63,0x00,0x00,0x5b,0x2a,0xf2,0x22,0x0e,0xa0,0x64,0x00,0x13,0x00,0x1f, +0x81,0x4f,0x79,0x58,0x00,0x00,0x5b,0x2a,0xf2,0x22,0x0e,0xa0,0x64,0x00,0x13,0x00, +0x20,0x81,0x55,0x79,0x7f,0x00,0x00,0x5b,0x2a,0xf2,0x22,0x0e,0xa0,0x64,0x00,0x13, +0x00,0x21,0x81,0x5b,0x79,0x31,0x00,0x00,0x5b,0x2a,0xf2,0x22,0x0e,0xa0,0x64,0x00, +0x13,0x00,0x22,0x81,0x61,0x79,0x48,0x00,0x00,0x5b,0x2a,0xf2,0x22,0x0e,0xa0,0x64, +0x00,0x13,0x00,0x23,0x81,0x6d,0x79,0x48,0x00,0x00,0x5b,0x2a,0xf2,0x22,0x0e,0xa0, +0x64,0x00,0x10,0x00,0xaa,0x80,0x5e,0x3c,0x0b,0x00,0x00,0x73,0x2e,0x90,0x00,0x0a, +0xa0,0x65,0x00,0x10,0x00,0x2a,0x80,0x51,0x79,0x52,0x00,0x00,0x27,0x29,0xf0,0x05, +0x0a,0xa0,0x66,0x00,0x10,0x00,0x18,0x81,0x2a,0x3c,0x54,0x00,0x00,0x2f,0x32,0xf5, +0x20,0x08,0xa0,0x67,0x00,0x10,0x00,0x19,0x81,0x30,0x3c,0x59,0x00,0x00,0x2f,0x32, +0xf5,0x20,0x08,0xa0,0x67,0x00,0x10,0x00,0x1a,0x81,0x36,0x3c,0x52,0x00,0x00,0x2f, +0x32,0xf5,0x20,0x08,0xa0,0x67,0x00,0x10,0x00,0x1b,0x81,0x3c,0x3c,0x59,0x00,0x00, +0x2f,0x32,0xf5,0x20,0x08,0xa0,0x67,0x00,0x10,0x00,0x1c,0x81,0x42,0x3c,0x5f,0x00, +0x00,0x2f,0x32,0xf5,0x20,0x08,0xa0,0x67,0x00,0x10,0x00,0x1d,0x81,0x48,0x3c,0x62, +0x00,0x00,0x2f,0x32,0xf5,0x20,0x08,0xa0,0x67,0x00,0x10,0x00,0x1e,0x81,0x4e,0x3c, +0x6d,0x00,0x00,0x2f,0x32,0xf5,0x20,0x08,0xa0,0x67,0x00,0x10,0x00,0x1f,0x81,0x54, +0x3c,0x62,0x00,0x00,0x2f,0x32,0xf5,0x20,0x08,0xa0,0x67,0x00,0x10,0x00,0x20,0x81, +0x59,0x3c,0x09,0x00,0x00,0x2f,0x32,0xf5,0x20,0x08,0xa0,0x67,0x00,0x10,0x00,0x21, +0x81,0x60,0x3c,0x3b,0x00,0x00,0x2f,0x32,0xf5,0x20,0x08,0xa0,0x67,0x00,0x10,0x00, +0x22,0x81,0x66,0x3c,0x52,0x00,0x00,0x2f,0x32,0xf5,0x20,0x08,0xa0,0x67,0x00,0x10, +0x00,0x23,0x81,0x72,0x3c,0x52,0x00,0x00,0x2f,0x32,0xf5,0x20,0x08,0xa0,0x67,0x00, +0x10,0x00,0x02,0x90,0x55,0x79,0x57,0x00,0x00,0x47,0x31,0xb3,0x20,0x0b,0xa0,0x68, +0x00,0x10,0x00,0x17,0x81,0x2d,0x3c,0x6d,0x00,0x00,0x43,0x29,0xf1,0x50,0x08,0xa0, +0x69,0x00,0x10,0x00,0x18,0x81,0x25,0x79,0x49,0x00,0x00,0x43,0x2a,0xf2,0x20,0x0a, +0xa0,0x6a,0x00,0x10,0x00,0x19,0x81,0x2b,0x79,0x4e,0x00,0x00,0x43,0x2a,0xf2,0x20, +0x0a,0xa0,0x6a,0x00,0x10,0x00,0x1a,0x81,0x31,0x79,0x47,0x00,0x00,0x43,0x2a,0xf2, +0x20,0x0a,0xa0,0x6a,0x00,0x10,0x00,0x1b,0x81,0x37,0x79,0x4e,0x00,0x00,0x43,0x2a, +0xf2,0x20,0x0a,0xa0,0x6a,0x00,0x10,0x00,0x1c,0x81,0x3d,0x79,0x54,0x00,0x00,0x43, +0x2a,0xf2,0x20,0x0a,0xa0,0x6a,0x00,0x10,0x00,0x1d,0x81,0x43,0x79,0x57,0x00,0x00, +0x43,0x2a,0xf2,0x20,0x0a,0xa0,0x6a,0x00,0x10,0x00,0x1e,0x81,0x49,0x79,0x62,0x00, +0x00,0x43,0x2a,0xf2,0x20,0x0a,0xa0,0x6a,0x00,0x10,0x00,0x1f,0x81,0x4f,0x79,0x57, +0x00,0x00,0x43,0x2a,0xf2,0x20,0x0a,0xa0,0x6a,0x00,0x10,0x00,0x20,0x81,0x55,0x79, +0x7e,0x00,0x00,0x43,0x2a,0xf2,0x20,0x0a,0xa0,0x6a,0x00,0x10,0x00,0x21,0x81,0x5b, +0x79,0x30,0x00,0x00,0x43,0x2a,0xf2,0x20,0x0a,0xa0,0x6a,0x00,0x10,0x00,0x22,0x81, +0x61,0x79,0x47,0x00,0x00,0x43,0x2a,0xf2,0x20,0x0a,0xa0,0x6a,0x00,0x10,0x00,0x23, +0x81,0x6d,0x79,0x47,0x00,0x00,0x43,0x2a,0xf2,0x20,0x0a,0xa0,0x6a,0x00,0x12,0x00, +0x02,0x90,0x55,0x3c,0x55,0x00,0x00,0x5b,0x24,0x80,0x01,0x05,0xa0,0x6b,0x00,0x12, +0x30,0xf3,0x80,0x4b,0x3c,0x5d,0x00,0x00,0x73,0x23,0xa2,0x14,0x06,0xa1,0x6c,0x20, +0x10,0x30,0x4e,0x80,0x43,0x3c,0x7a,0x00,0x00,0x6d,0x2a,0x34,0x23,0x05,0xa2,0x6d, +0xe0,0x10,0x00,0x29,0x80,0x50,0x3c,0x37,0x00,0x00,0x41,0x22,0x31,0x23,0x06,0xa0, +0x6e,0x00,0x15,0x10,0x18,0x81,0x25,0x3c,0x57,0x00,0x00,0x45,0x23,0xf0,0x20,0x07, +0xa1,0x6f,0x00,0x15,0x10,0x19,0x81,0x2b,0x3c,0x5c,0x00,0x00,0x45,0x23,0xf0,0x20, +0x07,0xa1,0x6f,0x00,0x15,0x10,0x1a,0x81,0x31,0x3c,0x55,0x00,0x00,0x45,0x23,0xf0, +0x20,0x07,0xa1,0x6f,0x00,0x15,0x10,0x1b,0x81,0x37,0x3c,0x5c,0x00,0x00,0x45,0x23, +0xf0,0x20,0x07,0xa1,0x6f,0x00,0x15,0x10,0x1c,0x81,0x3d,0x3c,0x62,0x00,0x00,0x45, +0x23,0xf0,0x20,0x07,0xa1,0x6f,0x00,0x15,0x10,0x1d,0x81,0x43,0x3c,0x65,0x00,0x00, +0x45,0x23,0xf0,0x20,0x07,0xa1,0x6f,0x00,0x15,0x10,0x1e,0x81,0x49,0x3c,0x70,0x00, +0x00,0x45,0x23,0xf0,0x20,0x07,0xa1,0x6f,0x00,0x15,0x10,0x1f,0x81,0x4f,0x3c,0x65, +0x00,0x00,0x45,0x23,0xf0,0x20,0x07,0xa1,0x6f,0x00,0x15,0x00,0x20,0x81,0x54,0x3c, +0x0c,0x00,0x00,0x45,0x23,0xf0,0x20,0x07,0xa1,0x6f,0x00,0x15,0x10,0x21,0x81,0x5b, +0x3c,0x3e,0x00,0x00,0x45,0x23,0xf0,0x20,0x07,0xa1,0x6f,0x00,0x15,0x10,0x22,0x81, +0x61,0x3c,0x55,0x00,0x00,0x45,0x23,0xf0,0x20,0x07,0xa1,0x6f,0x00,0x15,0x10,0x23, +0x81,0x6d,0x3c,0x55,0x00,0x00,0x45,0x23,0xf0,0x20,0x07,0xa1,0x6f,0x00,0x12,0x30, +0x24,0x71,0x41,0x3c,0x78,0x00,0x00,0x45,0x22,0xe2,0x21,0x08,0xa0,0x70,0x00,0x10, +0x00,0x18,0x80,0x45,0x3c,0x52,0x00,0x00,0x4f,0x25,0x70,0x00,0x06,0xa0,0x71,0x00, +0x10,0x00,0x19,0x80,0x4a,0x3c,0x52,0x00,0x00,0x4f,0x25,0x70,0x00,0x06,0xa0,0x71, +0x00,0x10,0x00,0x1a,0x80,0x51,0x3c,0x56,0x00,0x00,0x4f,0x25,0x70,0x00,0x06,0xa0, +0x71,0x00,0x10,0x00,0x1b,0x80,0x5b,0x3c,0x55,0x00,0x00,0x4f,0x25,0x70,0x00,0x06, +0xa0,0x71,0x00,0x10,0x00,0x2a,0x80,0x51,0x79,0x5a,0x00,0x00,0x57,0x24,0x65,0x21, +0x06,0xa0,0x72,0xf0,0x10,0x00,0x01,0x81,0x47,0x3c,0x76,0x00,0x00,0x41,0x19,0x44, +0x14,0x16,0xa0,0x73,0x10,0x10,0x00,0xaa,0x80,0x5f,0x79,0x78,0x00,0x00,0x6b,0x19, +0x40,0x00,0x06,0xa0,0x74,0x00,0x10,0x00,0x0c,0x80,0x36,0x3c,0x5f,0x00,0x00,0x51, +0x0a,0x60,0x01,0x06,0xa0,0x75,0x00,0x10,0x00,0x0d,0x80,0x42,0x3c,0x68,0x00,0x00, +0x51,0x0a,0x60,0x01,0x06,0xa0,0x75,0x00,0x10,0x00,0x0e,0x80,0x4a,0x3c,0x61,0x00, +0x00,0x51,0x0a,0x60,0x01,0x06,0xa0,0x75,0x00,0x10,0x00,0x0f,0x80,0x56,0x3c,0x61, +0x00,0x00,0x51,0x0a,0x60,0x01,0x06,0xa0,0x75,0x00,0x10,0x00,0x3f,0x80,0x4b,0x79, +0x40,0x00,0x00,0x59,0x31,0x65,0x12,0x07,0xa0,0x76,0x00,0x10,0x00,0x24,0x71,0x40, +0x3c,0x06,0x00,0x00,0x59,0x1a,0x85,0x23,0x08,0xa0,0x77,0x00,0x10,0x00,0x2a,0x80, +0x51,0x79,0x4f,0x00,0x00,0x3f,0x1b,0xc0,0x05,0x06,0xa0,0x78,0x00,0x10,0x70,0xd3, +0x80,0x25,0x3c,0x4c,0x00,0x00,0x29,0x19,0x33,0x11,0x06,0xa0,0x79,0x00,0x10,0x70, +0xda,0x80,0x2b,0x3c,0x55,0x00,0x00,0x29,0x19,0x33,0x11,0x06,0xa0,0x79,0x00,0x10, +0x70,0xd4,0x80,0x31,0x3c,0x4c,0x00,0x00,0x29,0x19,0x33,0x11,0x06,0xa0,0x79,0x00, +0x10,0x70,0xdb,0x80,0x37,0x3c,0x47,0x00,0x00,0x29,0x19,0x33,0x11,0x06,0xa0,0x79, +0x00,0x10,0x70,0xd5,0x80,0x3d,0x3c,0x60,0x00,0x00,0x29,0x19,0x33,0x11,0x06,0xa0, +0x79,0x00,0x10,0x70,0xdc,0x80,0x43,0x3c,0x4f,0x00,0x00,0x29,0x19,0x33,0x11,0x06, +0xa0,0x79,0x00,0x10,0x70,0xd6,0x80,0x49,0x3c,0x53,0x00,0x00,0x29,0x19,0x33,0x11, +0x06,0xa0,0x79,0x00,0x10,0x70,0xdd,0x80,0x4f,0x3c,0x63,0x00,0x00,0x29,0x19,0x33, +0x11,0x06,0xa0,0x79,0x00,0x10,0x70,0xd7,0x80,0x54,0x3c,0x0a,0x00,0x00,0x29,0x19, +0x33,0x11,0x06,0xa0,0x79,0x00,0x10,0x70,0xde,0x80,0x5b,0x3c,0x3d,0x00,0x00,0x29, +0x19,0x33,0x11,0x06,0xa0,0x79,0x00,0x10,0x70,0xd8,0x80,0x61,0x3c,0x53,0x00,0x00, +0x29,0x19,0x33,0x11,0x06,0xa0,0x79,0x00,0x10,0x70,0xd9,0x80,0x6d,0x3c,0x53,0x00, +0x00,0x29,0x19,0x33,0x11,0x06,0xa0,0x79,0x00,0x10,0x70,0xd3,0x80,0x25,0x3c,0x40, +0x00,0x00,0x29,0x18,0x33,0x11,0x06,0xa0,0x7a,0x00,0x10,0x70,0xda,0x80,0x2b,0x3c, +0x49,0x00,0x00,0x29,0x18,0x33,0x11,0x06,0xa0,0x7a,0x00,0x10,0x70,0xd4,0x80,0x31, +0x3c,0x40,0x00,0x00,0x29,0x18,0x33,0x11,0x06,0xa0,0x7a,0x00,0x10,0x70,0xdb,0x80, +0x37,0x3c,0x3b,0x00,0x00,0x29,0x18,0x33,0x11,0x06,0xa0,0x7a,0x00,0x10,0x70,0xd5, +0x80,0x3d,0x3c,0x54,0x00,0x00,0x29,0x18,0x33,0x11,0x06,0xa0,0x7a,0x00,0x10,0x70, +0xdc,0x80,0x43,0x3c,0x43,0x00,0x00,0x29,0x18,0x33,0x11,0x06,0xa0,0x7a,0x00,0x10, +0x70,0xd6,0x80,0x49,0x3c,0x47,0x00,0x00,0x29,0x18,0x33,0x11,0x06,0xa0,0x7a,0x00, +0x10,0x70,0xdd,0x80,0x4f,0x3c,0x57,0x00,0x00,0x29,0x18,0x33,0x11,0x06,0xa0,0x7a, +0x00,0x10,0x70,0xd7,0x80,0x55,0x3c,0x7e,0x00,0x00,0x29,0x18,0x33,0x11,0x06,0xa0, +0x7a,0x00,0x10,0x70,0xde,0x80,0x5b,0x3c,0x31,0x00,0x00,0x29,0x18,0x33,0x11,0x06, +0xa0,0x7a,0x00,0x10,0x70,0xd8,0x80,0x61,0x3c,0x47,0x00,0x00,0x29,0x18,0x33,0x11, +0x06,0xa0,0x7a,0x00,0x10,0x70,0xd9,0x80,0x6d,0x3c,0x47,0x00,0x00,0x29,0x18,0x33, +0x11,0x06,0xa0,0x7a,0x00,0x10,0x00,0x4e,0x80,0x43,0x3c,0x71,0xfd,0x01,0x33,0x22, +0xa3,0x63,0x17,0xa0,0x7b,0x00,0x10,0x30,0x43,0x61,0x47,0x5b,0x7d,0x2b,0x2a,0x91, +0x2a,0xf0,0x05,0x05,0x20,0x7c,0x00,0x10,0x00,0x02,0x90,0x55,0x3c,0x55,0x00,0x00, +0x49,0x2a,0x60,0x01,0x05,0xa0,0x7d,0x00,0x10,0x30,0xf3,0x70,0x57,0x79,0x48,0x00, +0x00,0x57,0x2d,0x52,0x14,0x06,0xa2,0x7e,0x00,0x10,0x00,0xf3,0x70,0x52,0x3c,0x62, +0x00,0x00,0x47,0x33,0xf6,0x25,0x25,0xa0,0x7f,0x00,0x10,0x00,0x6d,0x80,0x32,0x3c, +0x5e,0x00,0x00,0x53,0x3b,0x85,0x24,0x06,0xa0,0x80,0x00,0x10,0x00,0x6e,0x80,0x3e, +0x3c,0x65,0x00,0x00,0x53,0x3b,0x85,0x24,0x06,0xa0,0x80,0x09,0x10,0x00,0x6f,0x80, +0x4a,0x3c,0x73,0x00,0x00,0x53,0x3b,0x85,0x24,0x06,0xa0,0x80,0x00,0x12,0x00,0x02, +0x90,0x55,0x3c,0x5d,0x00,0x00,0x37,0x32,0x73,0x22,0x06,0xa1,0x81,0x05,0x12,0x00, +0xb3,0x80,0x36,0x79,0x74,0x00,0x00,0x3d,0x32,0xf5,0x34,0x07,0xa2,0x82,0x0c,0x12, +0x00,0xb7,0x80,0x41,0x79,0x7f,0x00,0x00,0x3d,0x32,0xf5,0x34,0x07,0xa2,0x82,0x00, +0x12,0x00,0xb5,0x80,0x48,0x79,0x6d,0x00,0x00,0x3d,0x32,0xf6,0x34,0x07,0xa2,0x82, +0x00,0x12,0x00,0xb4,0x80,0x4e,0x79,0x74,0x00,0x00,0x3d,0x32,0xf6,0x35,0x07,0xa2, +0x82,0x00,0x12,0x00,0xb6,0x80,0x57,0x79,0x63,0x00,0x00,0x3d,0x32,0xf6,0x35,0x17, +0xa2,0x82,0x00,0x10,0x00,0x37,0x91,0x4f,0x3c,0x65,0x00,0x00,0x41,0x2a,0xf5,0x18, +0x08,0xa0,0x83,0x00,0x10,0x00,0x2a,0x80,0x51,0x79,0x59,0x00,0x00,0x37,0x29,0xc0, +0x05,0x05,0xa0,0x84,0xf0,0x10,0x00,0x02,0x90,0x55,0x3c,0x55,0x00,0x00,0x35,0x2a, +0x30,0x00,0x05,0xa0,0x85,0x10,0x10,0x30,0x09,0x70,0x44,0x52,0x52,0xef,0x01,0xc3, +0x0c,0x20,0x00,0x06,0x20,0x86,0x00,0x10,0x00,0x2a,0x80,0x51,0x3c,0x5f,0x00,0x00, +0x51,0x2b,0xf5,0x21,0x06,0xa0,0x87,0x00,0x10,0x00,0x24,0x71,0x41,0x79,0x7b,0x00, +0x00,0x51,0x2b,0x85,0x23,0x07,0xa0,0x88,0x00,0x10,0x30,0x0c,0x80,0x36,0x3c,0x5c, +0x00,0x00,0x01,0x2b,0x90,0x05,0x06,0xa3,0x89,0x00,0x10,0x30,0x0d,0x80,0x42,0x3c, +0x65,0x00,0x00,0x01,0x2b,0x90,0x05,0x06,0xa3,0x89,0x00,0x10,0x30,0x0e,0x80,0x4a, +0x3c,0x5e,0x00,0x00,0x01,0x2b,0x90,0x05,0x06,0xa3,0x89,0x00,0x10,0x30,0x0f,0x80, +0x56,0x3c,0x5e,0x00,0x00,0x01,0x2b,0x90,0x05,0x06,0xa3,0x89,0x00,0x10,0x00,0x02, +0x90,0x55,0x3c,0x52,0x00,0x00,0x01,0x22,0x80,0x01,0x05,0xa0,0x8a,0x00,0x10,0x00, +0x0f,0x81,0x4a,0x3c,0x78,0x00,0x00,0x01,0x32,0xf4,0x15,0x16,0xa1,0x8b,0x00,0x10, +0x00,0x13,0x80,0x40,0x3c,0x75,0x00,0x00,0x23,0x38,0xf0,0x05,0x09,0xa0,0x8c,0x00, +0x10,0x00,0x14,0x80,0x45,0x3c,0x51,0x00,0x00,0x23,0x38,0xf0,0x05,0x09,0xa0,0x8c, +0x00,0x10,0x00,0x12,0x80,0x49,0x3c,0x68,0x00,0x00,0x23,0x38,0xf0,0x05,0x09,0xa0, +0x8c,0x00,0x10,0x00,0x15,0x80,0x50,0x3c,0x66,0x00,0x00,0x23,0x38,0xf0,0x05,0x09, +0xa0,0x8c,0x00,0x10,0x00,0x17,0x80,0x57,0x3c,0x25,0x00,0x00,0x23,0x38,0xf0,0x05, +0x09,0xa0,0x8c,0x00,0x10,0x00,0x16,0x80,0x5f,0x3c,0x20,0x00,0x00,0x23,0x38,0xf0, +0x05,0x09,0xa0,0x8c,0x00,0x10,0x20,0x0e,0x81,0x52,0x3c,0x09,0x00,0x00,0x07,0x28, +0xf7,0x35,0x45,0xa2,0x8d,0x00,0x12,0x00,0xa9,0x80,0x4c,0x3c,0x01,0x00,0x00,0x39, +0x20,0xf0,0x04,0x07,0xa0,0x8e,0x00,0x10,0x00,0x37,0x81,0x4f,0x3c,0x65,0x00,0x00, +0x31,0x38,0xf5,0x18,0x08,0xa0,0x8f,0x00,0x12,0x20,0xa4,0x80,0x30,0x3c,0x43,0x00, +0x00,0x1f,0x1c,0xf0,0x00,0x09,0xa0,0x90,0x00,0x10,0x20,0xa7,0x80,0x30,0x61,0x40, +0x00,0x00,0x1f,0x19,0xf0,0x00,0x09,0xa0,0x91,0x00,0x10,0x20,0xa8,0x80,0x4f,0x61, +0x31,0x00,0x00,0x1f,0x19,0xf0,0x00,0x09,0xa0,0x91,0x00,0x12,0x00,0x05,0x81,0x44, +0x3c,0x66,0x00,0x00,0x35,0x31,0xf3,0x20,0x09,0xa0,0x92,0x00,0x12,0x00,0x02,0x81, +0x47,0x3c,0x52,0x00,0x00,0x35,0x31,0xf3,0x20,0x09,0xa0,0x92,0x00,0x12,0x00,0x06, +0x81,0x4c,0x3c,0x64,0x00,0x00,0x35,0x31,0xf3,0x20,0x09,0xa0,0x92,0x00,0x12,0x00, +0x07,0x81,0x4c,0x3c,0x01,0x00,0x00,0x35,0x31,0xf3,0x20,0x09,0xa0,0x92,0x00,0x12, +0x00,0x08,0x81,0x50,0x3c,0x3c,0x00,0x00,0x35,0x31,0xf3,0x20,0x09,0xa0,0x92,0x00, +0x12,0x00,0x0a,0x81,0x55,0x3c,0x4d,0x00,0x00,0x35,0x31,0xf3,0x20,0x09,0xa0,0x92, +0x00,0x12,0x00,0x0b,0x81,0x57,0x3c,0x64,0x00,0x00,0x35,0x31,0xf3,0x20,0x09,0xa0, +0x92,0x00,0x12,0x00,0x0c,0x81,0x58,0x3c,0x22,0x00,0x00,0x35,0x31,0xf3,0x20,0x09, +0xa0,0x92,0x00,0x12,0x00,0x0d,0x81,0x5b,0x3c,0x36,0x00,0x00,0x35,0x31,0xf3,0x20, +0x09,0xa0,0x92,0x00,0x12,0x00,0x09,0x81,0x5e,0x3c,0x61,0x00,0x00,0x35,0x31,0xf3, +0x20,0x09,0xa0,0x92,0x00,0x13,0x00,0x41,0x90,0x42,0x3c,0x62,0x00,0x00,0x3b,0x31, +0x95,0x20,0x09,0xa0,0x93,0x00,0x10,0x00,0xf3,0x70,0x52,0x3c,0x5d,0x00,0x00,0x37, +0x3b,0xd6,0x25,0x25,0xa0,0x94,0x00,0x10,0x00,0x37,0x91,0x4c,0x6e,0x65,0x00,0x00, +0x99,0x3b,0xf5,0x18,0x08,0xa0,0x95,0x00,0x10,0x00,0x0b,0x80,0x59,0x3c,0x6a,0x00, +0x00,0x2d,0x38,0xf0,0x00,0x09,0xa0,0x96,0x00,0x10,0x00,0xfe,0x80,0x4f,0x3c,0x16, +0x00,0x00,0x35,0x38,0xf3,0x06,0x17,0xa1,0x04,0x00,0x10,0x00,0x00,0xa1,0x39,0x74, +0x42,0x00,0x00,0xab,0x38,0xb0,0x05,0x16,0xa1,0x04,0x07,0x10,0x00,0x2c,0x80,0x55, +0x3c,0x53,0x00,0x00,0x2b,0x00,0xf0,0x00,0x09,0x50,0x04,0x00,0x10,0x10,0x3e,0x50, +0x3f,0x3c,0x6a,0x00,0x00,0x21,0x38,0xf0,0x04,0x04,0x50,0x04,0x00,0x10,0x00,0xc7, +0x80,0x3a,0x3c,0x52,0x00,0x00,0x1b,0x38,0xf0,0x05,0x01,0x50,0x04,0x00,0x10,0x00, +0x26,0x80,0x4e,0x3c,0x7a,0x00,0x00,0x2f,0x38,0xf0,0x04,0x04,0x50,0x04,0x00,0x10, +0x00,0x31,0x80,0x4c,0x3c,0x6a,0x00,0x00,0x01,0x38,0x3a,0xf0,0x09,0x50,0x04,0x00, +0x10,0x00,0x38,0x81,0x43,0x3c,0x52,0x00,0x00,0x5f,0x38,0xf0,0x00,0x09,0xa0,0x04, +0x0e,0x10,0x01,0x25,0x81,0x4b,0x3c,0x6d,0x00,0x00,0x2f,0x38,0xf0,0x00,0x09,0xa0, +0x04,0xe0,0x10,0x00,0x08,0x80,0x55,0x3c,0x55,0x00,0x00,0x01,0x00,0x20,0x03,0x04, +0x20,0x04,0x24,0x10,0x01,0x37,0x80,0x4a,0x3c,0x6a,0x00,0x00,0x01,0x00,0x20,0x04, +0x05,0x20,0x04,0xc0,0x10,0x00,0x09,0x70,0x37,0xba,0x52,0xbf,0x50,0x3d,0x0f,0x22, +0x12,0x07,0x08,0x04,0x40,0x10,0x00,0x09,0x70,0x46,0xbc,0x52,0x0f,0x32,0x51,0x15, +0x72,0x12,0x07,0x08,0x04,0x00,0x10,0x00,0x03,0x80,0x50,0x3c,0x52,0x00,0x00,0x31, +0x00,0xf0,0x00,0x0f,0x10,0x04,0xe0,0x10,0x00,0x01,0x80,0x55,0x3c,0x55,0x00,0x00, +0x1f,0x08,0x30,0x01,0x07,0x10,0x04,0x24,0x10,0x04,0x37,0x80,0x4a,0x3c,0x6a,0x00, +0x00,0x89,0x00,0x30,0x01,0x07,0x10,0x04,0xa0,0x10,0x00,0x36,0x80,0x49,0x3c,0x01, +0xf1,0x02,0x3b,0x0f,0x40,0x01,0x07,0x20,0x04,0x60,0x10,0x00,0x36,0x80,0x4e,0x3e, +0x70,0xf9,0x02,0x39,0x07,0x40,0x01,0x07,0x08,0x04,0x00,0x10,0x10,0x39,0x81,0x56, +0x3c,0x6a,0x00,0x00,0x0f,0x38,0xf0,0x03,0x03,0x20,0x04,0x00,0x15,0x20,0x67,0x80, +0x62,0x3c,0x5b,0x00,0x00,0x01,0x32,0xf4,0x15,0x16,0xa1,0x8b,0x7f,0x7d,0x7c,0x7a, +0x78,0x77,0x75,0x73,0x71,0x70,0x6e,0x6c,0x6b,0x69,0x67,0x66,0x64,0x62,0x60,0x5e, +0x5c,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4d,0x4c,0x4a,0x48,0x46,0x45,0x44,0x43, +0x42,0x41,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x34,0x33,0x32,0x31, +0x30,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x22,0x21, +0x20,0x20,0x1f,0x1e,0x1e,0x1d,0x1c,0x1c,0x1b,0x1a,0x1a,0x19,0x18,0x18,0x17,0x17, +0x16,0x15,0x15,0x14,0x13,0x13,0x12,0x11,0x11,0x10,0x0f,0x0f,0x0e,0x0e,0x0d,0x0d, +0x0c,0x0c,0x0b,0x0b,0x0a,0x0a,0x09,0x09,0x09,0x08,0x08,0x07,0x07,0x06,0x06,0x05, +0x05,0x05,0x04,0x04,0x03,0x03,0x02,0x02,0x01,0x01,0x00,0x00,0x7f,0x7e,0x7e,0x7d, +0x7c,0x7c,0x7b,0x7b,0x7a,0x79,0x79,0x78,0x77,0x77,0x76,0x76,0x75,0x74,0x72,0x71, +0x70,0x6f,0x6d,0x6c,0x6b,0x6a,0x68,0x67,0x66,0x65,0x63,0x62,0x61,0x60,0x5e,0x5d, +0x5c,0x5a,0x59,0x58,0x56,0x55,0x54,0x53,0x51,0x50,0x4f,0x4d,0x4c,0x4b,0x49,0x48, +0x47,0x45,0x44,0x43,0x41,0x40,0x3f,0x3e,0x3c,0x3b,0x3a,0x38,0x37,0x36,0x35,0x34, +0x33,0x32,0x32,0x31,0x30,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x28,0x27,0x26, +0x25,0x24,0x23,0x22,0x21,0x20,0x1f,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17, +0x17,0x16,0x15,0x14,0x13,0x12,0x12,0x11,0x10,0x0f,0x0e,0x0d,0x0d,0x0c,0x0b,0x0a, +0x09,0x08,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x00,0x7f,0x7d,0x7c,0x7a, +0x78,0x77,0x75,0x73,0x71,0x70,0x6e,0x6c,0x6b,0x69,0x67,0x66,0x64,0x62,0x5f,0x5d, +0x5b,0x58,0x56,0x54,0x51,0x4f,0x4d,0x4b,0x48,0x46,0x44,0x41,0x3f,0x3e,0x3d,0x3c, +0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x31,0x30,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a, +0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x1a,0x19, +0x19,0x18,0x18,0x17,0x16,0x16,0x15,0x15,0x14,0x14,0x13,0x13,0x12,0x11,0x11,0x10, +0x10,0x0f,0x0f,0x0e,0x0d,0x0d,0x0c,0x0c,0x0b,0x0b,0x0a,0x0a,0x09,0x09,0x08,0x08, +0x08,0x08,0x07,0x07,0x07,0x06,0x06,0x06,0x06,0x05,0x05,0x05,0x04,0x04,0x04,0x03, +0x03,0x03,0x03,0x02,0x02,0x02,0x01,0x01,0x01,0x01,0x00,0x00,0x7f,0x7e,0x7d,0x7c, +0x7b,0x7a,0x79,0x78,0x77,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70,0x6f,0x6d,0x6c, +0x6b,0x69,0x68,0x67,0x65,0x64,0x63,0x62,0x60,0x5f,0x5e,0x5c,0x5b,0x59,0x57,0x55, +0x53,0x52,0x50,0x4e,0x4c,0x4a,0x48,0x46,0x44,0x43,0x41,0x3f,0x3d,0x3b,0x39,0x37, +0x35,0x34,0x32,0x30,0x2e,0x2c,0x2a,0x28,0x26,0x25,0x23,0x21,0x1f,0x1e,0x1e,0x1d, +0x1c,0x1c,0x1b,0x1a,0x1a,0x19,0x18,0x18,0x17,0x16,0x16,0x15,0x14,0x14,0x13,0x13, +0x12,0x11,0x11,0x10,0x0f,0x0f,0x0e,0x0d,0x0d,0x0c,0x0b,0x0b,0x0a,0x0a,0x09,0x09, +0x09,0x08,0x08,0x08,0x07,0x07,0x07,0x06,0x06,0x06,0x05,0x05,0x05,0x05,0x04,0x04, +0x04,0x03,0x03,0x03,0x02,0x02,0x02,0x01,0x01,0x01,0x00,0x00,0x7f,0x7f,0x7f,0x7f, +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, +0x0b,0x0b,0x0a,0x0a,0x0a,0x09,0x09,0x08,0x08,0x08,0x07,0x07,0x06,0x06,0x05,0x05, +0x05,0x04,0x04,0x03,0x03,0x03,0x02,0x02,0x01,0x01,0x00,0x00,0x7f,0x7d,0x7c,0x7a, +0x78,0x77,0x75,0x74,0x72,0x70,0x6f,0x6d,0x6b,0x6a,0x68,0x67,0x65,0x63,0x61,0x5f, +0x5d,0x5c,0x5a,0x58,0x56,0x54,0x52,0x50,0x4e,0x4d,0x4b,0x49,0x47,0x46,0x45,0x44, +0x43,0x42,0x40,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x35,0x34,0x33,0x32, +0x31,0x30,0x2f,0x2e,0x2d,0x2c,0x2b,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x23,0x22, +0x21,0x21,0x20,0x1f,0x1f,0x1e,0x1d,0x1d,0x1c,0x1b,0x1b,0x1a,0x19,0x19,0x18,0x18, +0x17,0x16,0x16,0x15,0x15,0x14,0x13,0x13,0x12,0x11,0x11,0x10,0x0f,0x0d,0x0c,0x0c, +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7c,0x79,0x77, +0x74,0x71,0x6e,0x6c,0x69,0x65,0x62,0x5e,0x5b,0x57,0x54,0x50,0x4d,0x4b,0x48,0x46, +0x44,0x42,0x3f,0x3d,0x3b,0x3a,0x39,0x38,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x2f, +0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x26,0x25,0x24,0x23,0x22,0x21,0x20, +0x1f,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x18,0x17,0x16,0x15,0x14,0x14,0x13, +0x12,0x12,0x11,0x11,0x10,0x0f,0x0f,0x0e,0x0e,0x0d,0x0d,0x0d,0x0d,0x0c,0x0c,0x0b, +0x0b,0x0b,0x0a,0x0a,0x09,0x09,0x08,0x08,0x08,0x07,0x07,0x07,0x07,0x06,0x06,0x06, +0x06,0x05,0x05,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x03,0x03,0x03,0x02,0x02,0x02, +0x02,0x02,0x02,0x02,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x7f,0x7c,0x7a,0x78, +0x76,0x74,0x72,0x70,0x6d,0x6a,0x68,0x65,0x63,0x60,0x5d,0x5b,0x58,0x56,0x53,0x51, +0x4f,0x4d,0x4a,0x48,0x46,0x44,0x43,0x41,0x3f,0x3d,0x3c,0x3a,0x38,0x37,0x36,0x35, +0x34,0x33,0x32,0x31,0x30,0x2f,0x2e,0x2d,0x2b,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25, +0x23,0x23,0x22,0x21,0x20,0x1f,0x1e,0x1d,0x1b,0x1b,0x1a,0x19,0x18,0x17,0x17,0x16, +0x15,0x15,0x14,0x14,0x13,0x12,0x12,0x11,0x11,0x10,0x10,0x10,0x0f,0x0e,0x0e,0x0d, +0x0d,0x0d,0x0c,0x0c,0x0b,0x0b,0x0a,0x0a,0x09,0x09,0x08,0x08,0x08,0x07,0x07,0x07, +0x07,0x06,0x06,0x05,0x05,0x05,0x05,0x05,0x05,0x04,0x04,0x04,0x03,0x03,0x03,0x02, +0x02,0x02,0x02,0x02,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, +0x14,0x27,0x4c,0x41,0x28,0x2d,0x4c,0x52,0x2e,0x33,0x4c,0x63,0x34,0x39,0x4c,0x74, +0x3a,0x3f,0x4c,0x85,0x40,0x45,0x4c,0x96,0x46,0x4b,0x4c,0xa7,0x4c,0x52,0x4c,0xb8, +0x53,0x58,0x4c,0xc9,0x59,0x6d,0x4c,0xda,0x0a,0x00,0x00,0x00,0x14,0x2d,0x4c,0xeb, +0x2e,0x33,0x4c,0xfc,0x34,0x39,0x4d,0x0d,0x3a,0x3f,0x4d,0x1e,0x40,0x45,0x4d,0x2f, +0x46,0x4b,0x4d,0x40,0x4c,0x52,0x4d,0x51,0x53,0x58,0x4d,0x62,0x59,0x5e,0x4d,0x73, +0x5f,0x6d,0x4d,0x84,0x0b,0x00,0x00,0x00,0x14,0x2d,0x4d,0x95,0x2e,0x33,0x4d,0xa6, +0x34,0x39,0x4d,0xb7,0x3a,0x3f,0x4d,0xc8,0x40,0x45,0x4d,0xd9,0x46,0x4b,0x4d,0xea, +0x4c,0x52,0x4d,0xfb,0x53,0x58,0x4e,0x0c,0x59,0x5e,0x4e,0x1d,0x5f,0x6d,0x4e,0x2e, +0x00,0x7f,0x4e,0x3f,0x14,0x00,0x00,0x00,0x14,0x27,0x4e,0x50,0x28,0x2d,0x4e,0x61, +0x2e,0x33,0x4e,0x72,0x34,0x39,0x4e,0x83,0x3a,0x3f,0x4e,0x94,0x40,0x45,0x4e,0xa5, +0x46,0x4b,0x4e,0xb6,0x4c,0x52,0x4e,0xc7,0x53,0x58,0x4e,0xd8,0x59,0x6d,0x4e,0xe9, +0x14,0x27,0x4e,0xfa,0x28,0x2d,0x4f,0x0b,0x2e,0x33,0x4f,0x1c,0x34,0x39,0x4f,0x2d, +0x3a,0x3f,0x4f,0x3e,0x40,0x45,0x4f,0x4f,0x46,0x4b,0x4f,0x60,0x4c,0x52,0x4f,0x71, +0x53,0x58,0x4f,0x82,0x59,0x6d,0x4f,0x93,0x02,0x00,0x00,0x00,0x00,0x7f,0x4f,0xb5, +0x15,0x6c,0x4f,0xa4,0x14,0x00,0x00,0x00,0x14,0x27,0x4f,0xc6,0x28,0x2d,0x4f,0xd7, +0x2e,0x33,0x4f,0xe8,0x34,0x39,0x4f,0xf9,0x3a,0x3f,0x50,0x0a,0x40,0x45,0x50,0x1b, +0x46,0x4b,0x50,0x2c,0x4c,0x52,0x50,0x3d,0x53,0x58,0x50,0x4e,0x59,0x6d,0x50,0x5f, +0x14,0x2d,0x50,0x70,0x2e,0x33,0x50,0x81,0x34,0x39,0x50,0x92,0x3a,0x3f,0x50,0xa3, +0x40,0x45,0x50,0xb4,0x46,0x4b,0x50,0xc5,0x4c,0x52,0x50,0xd6,0x53,0x58,0x50,0xe7, +0x59,0x5e,0x50,0xf8,0x5f,0x6d,0x51,0x09,0x05,0x00,0x00,0x00,0x15,0x39,0x51,0x1a, +0x3a,0x3f,0x51,0x2b,0x40,0x45,0x51,0x3c,0x46,0x4d,0x51,0x4d,0x4e,0x6c,0x51,0x5e, +0x02,0x00,0x00,0x00,0x15,0x51,0x51,0x6f,0x52,0x6c,0x51,0x80,0x01,0x00,0x00,0x00, +0x15,0x6c,0x51,0x91,0x01,0x00,0x00,0x00,0x15,0x6c,0x51,0xa2,0x02,0x00,0x00,0x00, +0x15,0x6c,0x51,0xb3,0x15,0x6c,0x51,0xc4,0x01,0x00,0x00,0x00,0x15,0x6c,0x51,0xd5, +0x04,0x00,0x00,0x00,0x15,0x3f,0x51,0xe6,0x40,0x4c,0x51,0xf7,0x4d,0x5a,0x52,0x08, +0x5b,0x7f,0x52,0x19,0x01,0x00,0x00,0x00,0x00,0x7f,0x52,0x2a,0x01,0x00,0x00,0x00, +0x01,0x7f,0x52,0x3b,0x02,0x00,0x00,0x00,0x00,0x7f,0x52,0x4c,0x00,0x7f,0x52,0x5d, +0x01,0x00,0x00,0x00,0x01,0x7f,0x52,0x6e,0x02,0x00,0x00,0x00,0x15,0x3b,0x52,0x7f, +0x3c,0x6c,0x52,0x90,0x05,0x00,0x00,0x00,0x15,0x30,0x52,0xa1,0x31,0x3c,0x52,0xb2, +0x3d,0x48,0x52,0xc3,0x49,0x54,0x52,0xd4,0x55,0x6c,0x52,0xe5,0x05,0x00,0x00,0x00, +0x15,0x29,0x52,0xf6,0x2a,0x30,0x53,0x07,0x31,0x38,0x53,0x18,0x39,0x41,0x53,0x29, +0x42,0x6c,0x53,0x3a,0x02,0x00,0x00,0x00,0x00,0x53,0x53,0x4b,0x54,0x7f,0x53,0x5c, +0x03,0x00,0x00,0x00,0x15,0x4c,0x53,0x6d,0x4d,0x6c,0x53,0x7e,0x15,0x6c,0x53,0x8f, +0x01,0x00,0x00,0x00,0x15,0x6c,0x53,0xa0,0x03,0x00,0x00,0x00,0x00,0x53,0x53,0xb1, +0x54,0x7f,0x53,0xc2,0x15,0x6c,0x53,0xd3,0x05,0x00,0x00,0x00,0x15,0x2f,0x53,0xe4, +0x30,0x36,0x53,0xf5,0x37,0x3c,0x54,0x06,0x3d,0x43,0x54,0x17,0x44,0x60,0x54,0x28, +0x04,0x00,0x00,0x00,0x15,0x31,0x54,0x39,0x32,0x38,0x54,0x4a,0x39,0x47,0x54,0x5b, +0x48,0x6c,0x54,0x6c,0x03,0x00,0x00,0x00,0x15,0x31,0x54,0x7d,0x32,0x3f,0x54,0x8e, +0x40,0x6c,0x54,0x9f,0x08,0x00,0x00,0x00,0x15,0x2c,0x54,0xb0,0x2d,0x31,0x54,0xc1, +0x32,0x38,0x54,0xd2,0x39,0x3f,0x54,0xe3,0x40,0x44,0x54,0xf4,0x45,0x4b,0x55,0x05, +0x4c,0x54,0x55,0x16,0x55,0x6c,0x55,0x27,0x01,0x00,0x00,0x00,0x01,0x7f,0x55,0x38, +0x02,0x00,0x00,0x00,0x00,0x40,0x55,0x49,0x41,0x7f,0x55,0x5a,0x09,0x00,0x00,0x00, +0x15,0x2a,0x55,0x6b,0x2b,0x2e,0x55,0x7c,0x2f,0x32,0x55,0x8d,0x33,0x36,0x55,0x9e, +0x37,0x3a,0x55,0xaf,0x3b,0x3e,0x55,0xc0,0x3f,0x42,0x55,0xd1,0x43,0x46,0x55,0xe2, +0x47,0x6c,0x55,0xf3,0x03,0x00,0x00,0x00,0x15,0x44,0x56,0x04,0x45,0x49,0x56,0x15, +0x4a,0x6c,0x56,0x26,0x02,0x00,0x00,0x00,0x15,0x30,0x56,0x37,0x31,0x6c,0x56,0x48, +0x04,0x00,0x00,0x00,0x01,0x20,0x56,0x59,0x21,0x25,0x56,0x6a,0x26,0x2a,0x56,0x7b, +0x2b,0x7f,0x56,0x8c,0x05,0x00,0x00,0x00,0x15,0x23,0x56,0x9d,0x24,0x2a,0x56,0xae, +0x2b,0x2f,0x56,0xbf,0x30,0x47,0x56,0xd0,0x48,0x6c,0x56,0xe1,0x01,0x00,0x00,0x00, +0x01,0x7f,0x56,0xf2,0x01,0x00,0x00,0x00,0x15,0x6c,0x57,0x03,0x01,0x00,0x00,0x00, +0x01,0x7f,0x57,0x14,0x01,0x00,0x00,0x00,0x15,0x6c,0x57,0x25,0x01,0x00,0x00,0x00, +0x00,0x7f,0x57,0x36,0x0a,0x00,0x00,0x00,0x15,0x3a,0x57,0x47,0x3b,0x3f,0x57,0x58, +0x40,0x41,0x57,0x69,0x42,0x44,0x57,0x7a,0x45,0x47,0x57,0x8b,0x48,0x4a,0x57,0x9c, +0x4b,0x4c,0x57,0xad,0x4d,0x4e,0x57,0xbe,0x4f,0x51,0x57,0xcf,0x52,0x6c,0x57,0xe0, +0x0c,0x00,0x00,0x00,0x15,0x32,0x57,0xf1,0x33,0x35,0x58,0x02,0x36,0x38,0x58,0x13, +0x39,0x3d,0x58,0x24,0x3e,0x3f,0x58,0x35,0x40,0x42,0x58,0x46,0x43,0x45,0x58,0x57, +0x46,0x48,0x58,0x68,0x49,0x4a,0x58,0x79,0x4b,0x4c,0x58,0x8a,0x4d,0x4f,0x58,0x9b, +0x50,0x6c,0x58,0xac,0x05,0x00,0x00,0x00,0x15,0x2d,0x58,0xbd,0x2e,0x37,0x58,0xce, +0x38,0x3e,0x58,0xdf,0x3f,0x44,0x58,0xf0,0x45,0x6c,0x59,0x01,0x02,0x00,0x00,0x00, +0x15,0x29,0x59,0x12,0x2a,0x6c,0x59,0x23,0x05,0x00,0x00,0x00,0x15,0x3b,0x59,0x34, +0x3c,0x41,0x59,0x45,0x42,0x47,0x59,0x56,0x48,0x52,0x59,0x67,0x53,0x6c,0x59,0x78, +0x05,0x00,0x00,0x00,0x15,0x32,0x59,0x89,0x33,0x3b,0x59,0x9a,0x3c,0x42,0x59,0xab, +0x43,0x48,0x59,0xbc,0x49,0x6c,0x59,0xcd,0x02,0x00,0x00,0x00,0x15,0x46,0x59,0xde, +0x47,0x6c,0x59,0xef,0x01,0x00,0x00,0x00,0x15,0x6c,0x5a,0x00,0x05,0x00,0x00,0x00, +0x15,0x3b,0x5a,0x11,0x3c,0x41,0x5a,0x22,0x42,0x47,0x5a,0x33,0x48,0x52,0x5a,0x44, +0x53,0x6c,0x5a,0x55,0x05,0x00,0x00,0x00,0x15,0x3b,0x5a,0x66,0x3c,0x41,0x5a,0x77, +0x42,0x47,0x5a,0x88,0x48,0x52,0x5a,0x99,0x53,0x6c,0x5a,0xaa,0x02,0x00,0x00,0x00, +0x05,0x71,0x5a,0xbb,0x15,0x6c,0x5a,0xcc,0x02,0x00,0x00,0x00,0x15,0x6c,0x5a,0xdd, +0x15,0x6c,0x5a,0xee,0x04,0x00,0x00,0x00,0x15,0x3a,0x5a,0xff,0x3b,0x40,0x5b,0x10, +0x41,0x47,0x5b,0x21,0x48,0x6c,0x5b,0x32,0x01,0x00,0x00,0x00,0x15,0x6c,0x5b,0x43, +0x01,0x00,0x00,0x00,0x15,0x6c,0x5b,0x54,0x01,0x00,0x00,0x00,0x15,0x6c,0x5b,0x65, +0x06,0x00,0x00,0x00,0x15,0x3c,0x5b,0x76,0x3d,0x43,0x5b,0x87,0x44,0x48,0x5b,0x98, +0x49,0x4e,0x5b,0xa9,0x4f,0x55,0x5b,0xba,0x56,0x6c,0x5b,0xcb,0x03,0x00,0x00,0x00, +0x15,0x3a,0x5b,0xdc,0x3b,0x3f,0x5b,0xed,0x40,0x6c,0x5b,0xfe,0x02,0x00,0x00,0x00, +0x15,0x2d,0x5c,0x0f,0x2e,0x6c,0x5c,0x20,0x02,0x00,0x00,0x00,0x15,0x45,0x5c,0x31, +0x46,0x6c,0x5c,0x42,0x02,0x00,0x00,0x00,0x15,0x49,0x5c,0x53,0x4a,0x6c,0x5c,0x64, +0x02,0x00,0x00,0x00,0x15,0x42,0x5c,0x75,0x43,0x6c,0x5c,0x86,0x18,0x00,0x00,0x00, +0x01,0x27,0x5c,0x97,0x28,0x2d,0x5c,0xa8,0x2e,0x33,0x5c,0xb9,0x34,0x39,0x5c,0xca, +0x3a,0x3f,0x5c,0xdb,0x40,0x45,0x5c,0xec,0x46,0x4b,0x5c,0xfd,0x4c,0x51,0x5d,0x0e, +0x52,0x57,0x5d,0x1f,0x58,0x5d,0x5d,0x30,0x5e,0x63,0x5d,0x41,0x64,0x7f,0x5d,0x52, +0x01,0x27,0x5d,0x63,0x28,0x2d,0x5d,0x74,0x2e,0x33,0x5d,0x85,0x34,0x39,0x5d,0x96, +0x3a,0x3f,0x5d,0xa7,0x40,0x45,0x5d,0xb8,0x46,0x4b,0x5d,0xc9,0x4c,0x51,0x5d,0xda, +0x52,0x57,0x5d,0xeb,0x58,0x5d,0x5d,0xfc,0x5e,0x64,0x5e,0x0d,0x65,0x7f,0x5e,0x1e, +0x0d,0x00,0x00,0x00,0x01,0x27,0x5e,0x2f,0x28,0x2d,0x5e,0x40,0x2e,0x33,0x5e,0x51, +0x34,0x39,0x5e,0x62,0x3a,0x3f,0x5e,0x73,0x40,0x45,0x5e,0x84,0x46,0x4b,0x5e,0x95, +0x4c,0x51,0x5e,0xa6,0x52,0x57,0x5e,0xb7,0x58,0x5d,0x5e,0xc8,0x5e,0x64,0x5e,0xd9, +0x65,0x7f,0x5e,0xea,0x00,0x7f,0x5e,0xfb,0x06,0x00,0x00,0x00,0x15,0x3f,0x5f,0x0c, +0x40,0x45,0x5f,0x1d,0x46,0x4b,0x5f,0x2e,0x4c,0x51,0x5f,0x3f,0x52,0x59,0x5f,0x50, +0x59,0x6c,0x5f,0x61,0x0d,0x00,0x00,0x00,0x15,0x32,0x5f,0x72,0x33,0x35,0x5f,0x83, +0x36,0x3a,0x5f,0x94,0x3b,0x3b,0x5f,0xa5,0x3c,0x3e,0x5f,0xb6,0x3f,0x41,0x5f,0xc7, +0x42,0x44,0x5f,0xd8,0x45,0x47,0x5f,0xe9,0x48,0x4a,0x5f,0xfa,0x4b,0x4d,0x60,0x0b, +0x4e,0x50,0x60,0x1c,0x51,0x53,0x60,0x2d,0x54,0x6c,0x60,0x3e,0x07,0x00,0x00,0x00, +0x24,0x30,0x60,0x4f,0x31,0x34,0x60,0x60,0x35,0x3a,0x60,0x71,0x3b,0x41,0x60,0x82, +0x42,0x47,0x60,0x93,0x48,0x51,0x60,0xa4,0x52,0x6c,0x60,0xb5,0x04,0x00,0x00,0x00, +0x15,0x2d,0x60,0xc6,0x2e,0x34,0x60,0xd7,0x35,0x39,0x60,0xe8,0x3a,0x6c,0x60,0xf9, +0x06,0x00,0x00,0x00,0x15,0x3c,0x61,0x0a,0x3d,0x43,0x61,0x1b,0x44,0x49,0x61,0x2c, +0x4a,0x4f,0x61,0x3d,0x50,0x55,0x61,0x4e,0x56,0x6c,0x61,0x5f,0x03,0x00,0x00,0x00, +0x15,0x38,0x61,0x70,0x39,0x3e,0x61,0x81,0x3f,0x6c,0x61,0x92,0x03,0x00,0x00,0x00, +0x15,0x22,0x61,0xa3,0x23,0x2e,0x61,0xb4,0x2f,0x6c,0x61,0xc5,0x04,0x00,0x00,0x00, +0x15,0x3b,0x61,0xd6,0x3c,0x41,0x61,0xe7,0x42,0x4a,0x61,0xf8,0x4b,0x6c,0x62,0x09, +0x05,0x00,0x00,0x00,0x15,0x40,0x62,0x1a,0x41,0x4d,0x62,0x2b,0x4e,0x53,0x62,0x3c, +0x54,0x5f,0x62,0x4d,0x60,0x6c,0x62,0x5e,0x03,0x00,0x00,0x00,0x15,0x40,0x62,0x6f, +0x41,0x4d,0x62,0x80,0x4e,0x6c,0x62,0x91,0x01,0x00,0x00,0x00,0x15,0x6f,0x62,0xa2, +0x01,0x00,0x00,0x00,0x15,0x6c,0x62,0xb3,0x02,0x00,0x00,0x00,0x15,0x6c,0x62,0xc4, +0x01,0x7f,0x62,0xd5,0x02,0x00,0x00,0x00,0x00,0x7f,0x62,0xe6,0x15,0x6c,0x62,0xf7, +0x01,0x00,0x00,0x00,0x00,0x7f,0x63,0x08,0x01,0x00,0x00,0x00,0x00,0x7f,0x63,0x19, +0x0c,0x00,0x00,0x00,0x01,0x2a,0x63,0x2a,0x2b,0x36,0x63,0x3b,0x37,0x42,0x63,0x4c, +0x43,0x4e,0x63,0x5d,0x4f,0x5a,0x63,0x6e,0x5b,0x7f,0x63,0x7f,0x01,0x2a,0x63,0x90, +0x2b,0x36,0x63,0xa1,0x37,0x42,0x63,0xb2,0x43,0x4e,0x63,0xc3,0x4f,0x5a,0x63,0xd4, +0x5b,0x7f,0x63,0xe5,0x18,0x00,0x00,0x00,0x01,0x27,0x63,0xf6,0x28,0x2d,0x64,0x07, +0x2e,0x33,0x64,0x18,0x34,0x39,0x64,0x29,0x3a,0x3f,0x64,0x3a,0x40,0x45,0x64,0x4b, +0x46,0x4b,0x64,0x5c,0x4c,0x51,0x64,0x6d,0x52,0x57,0x64,0x7e,0x58,0x5d,0x64,0x8f, +0x5e,0x66,0x64,0xa0,0x67,0x7f,0x64,0xb1,0x01,0x27,0x64,0xc2,0x28,0x2d,0x64,0xd3, +0x2e,0x33,0x64,0xe4,0x34,0x39,0x64,0xf5,0x3a,0x3f,0x65,0x06,0x40,0x45,0x65,0x17, +0x46,0x4b,0x65,0x28,0x4c,0x51,0x65,0x39,0x52,0x57,0x65,0x4a,0x58,0x5d,0x65,0x5b, +0x5e,0x66,0x65,0x6c,0x67,0x7f,0x65,0x7d,0x02,0x00,0x00,0x00,0x00,0x7f,0x65,0x8e, +0x15,0x6c,0x65,0x9f,0x02,0x00,0x00,0x00,0x00,0x7f,0x65,0xb0,0x01,0x7f,0x65,0xc1, +0x0e,0x00,0x00,0x00,0x00,0x40,0x65,0xd2,0x41,0x7f,0x65,0xe3,0x01,0x27,0x65,0xf4, +0x28,0x2d,0x66,0x05,0x2e,0x33,0x66,0x16,0x34,0x39,0x66,0x27,0x3a,0x3f,0x66,0x38, +0x40,0x45,0x66,0x49,0x46,0x4b,0x66,0x5a,0x4c,0x51,0x66,0x6b,0x52,0x57,0x66,0x7c, +0x58,0x5d,0x66,0x8d,0x5e,0x66,0x66,0x9e,0x67,0x7f,0x66,0xaf,0x02,0x00,0x00,0x00, +0x00,0x7f,0x66,0xc0,0x15,0x6c,0x66,0xd1,0x0d,0x00,0x00,0x00,0x01,0x27,0x66,0xe2, +0x28,0x2d,0x66,0xf3,0x2e,0x33,0x67,0x04,0x34,0x39,0x67,0x15,0x3a,0x3f,0x67,0x26, +0x40,0x45,0x67,0x37,0x46,0x4b,0x67,0x48,0x4c,0x51,0x67,0x59,0x52,0x57,0x67,0x6a, +0x58,0x5d,0x67,0x7b,0x5e,0x64,0x67,0x8c,0x65,0x7f,0x67,0x9d,0x05,0x71,0x67,0xae, +0x0d,0x00,0x00,0x00,0x00,0x7f,0x67,0xbf,0x01,0x27,0x67,0xd0,0x28,0x2d,0x67,0xe1, +0x2e,0x33,0x67,0xf2,0x34,0x39,0x68,0x03,0x3a,0x3f,0x68,0x14,0x40,0x45,0x68,0x25, +0x46,0x4b,0x68,0x36,0x4c,0x51,0x68,0x47,0x52,0x57,0x68,0x58,0x58,0x5d,0x68,0x69, +0x5e,0x66,0x68,0x7a,0x67,0x7f,0x68,0x8b,0x02,0x00,0x00,0x00,0x15,0x6c,0x68,0x9c, +0x15,0x6c,0x68,0xad,0x02,0x00,0x00,0x00,0x15,0x6c,0x68,0xbe,0x15,0x6c,0x68,0xcf, +0x0d,0x00,0x00,0x00,0x01,0x27,0x68,0xe0,0x28,0x2d,0x68,0xf1,0x2e,0x33,0x69,0x02, +0x34,0x39,0x69,0x13,0x3a,0x3f,0x69,0x24,0x40,0x45,0x69,0x35,0x46,0x4b,0x69,0x46, +0x4c,0x51,0x69,0x57,0x52,0x57,0x69,0x68,0x58,0x5d,0x69,0x79,0x5e,0x66,0x69,0x8a, +0x67,0x7f,0x69,0x9b,0x00,0x7f,0x69,0xac,0x05,0x00,0x00,0x00,0x15,0x3a,0x69,0xbd, +0x3b,0x40,0x69,0xce,0x41,0x47,0x69,0xdf,0x48,0x6c,0x69,0xf0,0x15,0x6c,0x6a,0x01, +0x02,0x00,0x00,0x00,0x15,0x6c,0x6a,0x12,0x00,0x7f,0x6a,0x23,0x05,0x00,0x00,0x00, +0x15,0x31,0x6a,0x34,0x32,0x38,0x6a,0x45,0x39,0x47,0x6a,0x56,0x48,0x6c,0x6a,0x67, +0x00,0x7f,0x6a,0x78,0x02,0x00,0x00,0x00,0x00,0x7f,0x6a,0x89,0x15,0x6c,0x6a,0x9a, +0x18,0x00,0x00,0x00,0x01,0x27,0x6a,0xab,0x28,0x2d,0x6a,0xbc,0x2e,0x33,0x6a,0xcd, +0x34,0x39,0x6a,0xde,0x3a,0x3f,0x6a,0xef,0x40,0x45,0x6b,0x00,0x46,0x4b,0x6b,0x11, +0x4c,0x51,0x6b,0x22,0x52,0x57,0x6b,0x33,0x58,0x5d,0x6b,0x44,0x5e,0x63,0x6b,0x55, +0x64,0x7f,0x6b,0x66,0x01,0x27,0x6b,0x77,0x28,0x2d,0x6b,0x88,0x2e,0x33,0x6b,0x99, +0x34,0x39,0x6b,0xaa,0x3a,0x3f,0x6b,0xbb,0x40,0x45,0x6b,0xcc,0x46,0x4b,0x6b,0xdd, +0x4c,0x51,0x6b,0xee,0x52,0x57,0x6b,0xff,0x58,0x5d,0x6c,0x10,0x5e,0x63,0x6c,0x21, +0x64,0x7f,0x6c,0x32,0x02,0x00,0x00,0x00,0x01,0x7f,0x6c,0x43,0x00,0x7f,0x6c,0x54, +0x02,0x00,0x00,0x00,0x15,0x6c,0x6c,0x65,0x15,0x6c,0x6c,0x76,0x04,0x00,0x00,0x00, +0x15,0x6c,0x6c,0x87,0x15,0x2c,0x6c,0x98,0x2d,0x36,0x6c,0xa9,0x37,0x6c,0x6c,0xba, +0x06,0x00,0x00,0x00,0x05,0x71,0x6c,0xcb,0x15,0x2f,0x6c,0xdc,0x30,0x36,0x6c,0xed, +0x37,0x3c,0x6c,0xfe,0x3d,0x43,0x6d,0x0f,0x44,0x60,0x6d,0x20,0x02,0x00,0x00,0x00, +0x00,0x7f,0x6d,0x31,0x15,0x6c,0x6d,0x42,0x02,0x00,0x00,0x00,0x15,0x6c,0x6d,0x53, +0x15,0x6c,0x6d,0x64,0x02,0x00,0x00,0x00,0x15,0x6c,0x6d,0x75,0x00,0x7f,0x6d,0x86, +0x05,0x00,0x00,0x00,0x15,0x31,0x6d,0x97,0x32,0x38,0x6d,0xa8,0x39,0x47,0x6d,0xb9, +0x48,0x6c,0x6d,0xca,0x15,0x6c,0x6d,0xdb,0x02,0x00,0x00,0x00,0x00,0x83,0x6d,0xec, +0x84,0x7f,0x70,0xfa,0x06,0x00,0x00,0x00,0x15,0x34,0x6d,0xfd,0x35,0x38,0x6e,0x0e, +0x39,0x3c,0x6e,0x1f,0x3d,0x44,0x6e,0x30,0x45,0x4c,0x6e,0x41,0x4d,0x6d,0x6e,0x52, +0x01,0x00,0x00,0x00,0x15,0x6c,0x6e,0x63,0x01,0x00,0x00,0x00,0x00,0x7f,0x6e,0x74, +0x01,0x00,0x00,0x00,0x00,0x7f,0x6e,0x85,0x03,0x00,0x00,0x00,0x15,0x39,0x6e,0x96, +0x15,0x39,0x6e,0xa7,0x3a,0x6c,0x6e,0xb8,0x0a,0x00,0x00,0x00,0x15,0x3a,0x6e,0xc9, +0x3b,0x3f,0x6e,0xda,0x40,0x41,0x6e,0xeb,0x42,0x44,0x6e,0xfc,0x45,0x47,0x6f,0x0d, +0x48,0x4a,0x6f,0x1e,0x4b,0x4c,0x6f,0x2f,0x4d,0x4e,0x6f,0x40,0x4f,0x51,0x6f,0x51, +0x52,0x6c,0x6f,0x62,0x01,0x00,0x00,0x00,0x15,0x6c,0x6f,0x73,0x02,0x00,0x00,0x00, +0x15,0x6c,0x6f,0x84,0x00,0x7f,0x6f,0x95,0x01,0x00,0x00,0x00,0x15,0x6c,0x6f,0xa6, +0x02,0x00,0x00,0x00,0x01,0x7f,0x6f,0xb7,0x15,0x6c,0x6f,0xc8,0x01,0x00,0x00,0x00, +0x15,0x6c,0x6f,0xd9,0x01,0x00,0x00,0x00,0x13,0x6c,0x6f,0xea,0x01,0x00,0x00,0x00, +0x15,0x6c,0x6f,0xfb,0x01,0x00,0x00,0x00,0x15,0x6c,0x70,0x0c,0x01,0x00,0x00,0x00, +0x15,0x6c,0x70,0x1d,0x01,0x00,0x00,0x00,0x15,0x6c,0x70,0x2e,0x01,0x00,0x00,0x00, +0x01,0x7f,0x70,0x3f,0x02,0x00,0x00,0x00,0x15,0x6c,0x70,0x50,0x01,0x7f,0x70,0x61, +0x02,0x00,0x00,0x00,0x15,0x6c,0x70,0x72,0x15,0x6c,0x70,0x83,0x01,0x00,0x00,0x00, +0x15,0x6c,0x70,0x94,0x02,0x00,0x00,0x00,0x0c,0x7f,0x70,0xa5,0x01,0x7f,0x70,0xb6, +0x02,0x00,0x00,0x00,0x15,0x6c,0x70,0xc7,0x15,0x6c,0x70,0xd8,0x01,0x00,0x00,0x00, +0x15,0x6c,0x70,0xe9,0x75,0x0b,0x75,0x37,0x75,0x63,0x75,0x93,0x75,0xe7,0x75,0xf3, +0x76,0x47,0x76,0x5f,0x76,0x6b,0x76,0x73,0x76,0x7b,0x76,0x87,0x76,0x8f,0x76,0xa3, +0x76,0xab,0x76,0xb3,0x76,0xbf,0x76,0xc7,0x76,0xd3,0x76,0xeb,0x77,0x03,0x77,0x0f, +0x77,0x1f,0x77,0x27,0x77,0x37,0x77,0x4f,0x77,0x63,0x77,0x73,0x77,0x97,0x77,0x9f, +0x77,0xab,0x77,0xd3,0x77,0xe3,0x77,0xef,0x78,0x03,0x78,0x1b,0x78,0x23,0x78,0x2b, +0x78,0x33,0x78,0x3b,0x78,0x43,0x78,0x6f,0x78,0xa3,0x78,0xbb,0x78,0xc7,0x78,0xdf, +0x78,0xf7,0x79,0x03,0x79,0x0b,0x79,0x23,0x79,0x3b,0x79,0x47,0x79,0x53,0x79,0x67, +0x79,0x6f,0x79,0x77,0x79,0x7f,0x79,0x9b,0x79,0xab,0x79,0xb7,0x79,0xc3,0x79,0xcf, +0x79,0xdb,0x7a,0x3f,0x7a,0x77,0x7a,0x93,0x7a,0xcb,0x7a,0xeb,0x7a,0xff,0x7b,0x1b, +0x7b,0x2b,0x7b,0x3b,0x7b,0x4f,0x7b,0x67,0x7b,0x77,0x7b,0x7f,0x7b,0x87,0x7b,0x93, +0x7b,0x9f,0x7b,0xa7,0x7b,0xaf,0x7b,0xe3,0x7c,0x47,0x7c,0x53,0x7c,0x5f,0x7c,0x9b, +0x7c,0xa7,0x7c,0xdf,0x7d,0x17,0x7d,0x23,0x7d,0x2f,0x7d,0x67,0x7d,0x7f,0x7d,0x8b, +0x7d,0xa3,0x7d,0xaf,0x7e,0x13,0x7e,0x1f,0x7e,0x2b,0x7e,0x3f,0x7e,0x5b,0x7e,0x67, +0x7e,0x73,0x7e,0x7f,0x7e,0x97,0x7e,0xa3,0x7e,0xbf,0x7e,0xc7,0x7e,0xcf,0x7e,0xd7, +0x7e,0xe7,0x7f,0x13,0x7f,0x1b,0x7f,0x27,0x7f,0x2f,0x7f,0x3b,0x7f,0x43,0x7f,0x4b, +0x7f,0x53,0x7f,0x5b,0x7f,0x63,0x7f,0x6b,0x7f,0x73,0x7f,0x7f,0x7f,0x8b,0x7f,0x93, +0x7f,0x9f,0x7f,0xab,0x71,0x0b,0x71,0x8b,0x72,0x0b,0x72,0x8b,0x73,0x0b,0x73,0x8b, +0x74,0x0b,0x74,0x8b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2f,0x40,0x4d,0x5c, +0x80,0x80,0x79,0x73,0x30,0x3c,0x54,0x60,0x6c,0x80,0x76,0x6c,0x40,0x40,0x40,0x69, +0x80,0x80,0x80,0x39,0x40,0x40,0x40,0x69,0x80,0x80,0x80,0x4f,0x18,0x1a,0x3c,0x54, +0x6b,0x76,0x76,0x76,0x1d,0x35,0x48,0x53,0x73,0x80,0x80,0x73,0x40,0x40,0x4c,0x5c, +0x80,0x80,0x7d,0x69,0x40,0x40,0x57,0x6c,0x80,0x80,0x6f,0x4f,0x40,0x40,0x4a,0x6b, +0x80,0x80,0x7b,0x61,0x1c,0x40,0x5a,0x6b,0x61,0x80,0x65,0x38,0x28,0x40,0x58,0x70, +0x67,0x75,0x81,0x79,0x18,0x24,0x3c,0x54,0x8a,0x6e,0x82,0x7d,0x1c,0x32,0x40,0x65, +0x59,0x80,0x80,0x65,0x1a,0x33,0x5a,0x69,0x4f,0x80,0x80,0x67,0x40,0x40,0x54,0x69, +0x80,0x80,0x7b,0x65,0x40,0x40,0x53,0x68,0x80,0x80,0x7d,0x63,0x1b,0x3c,0x56,0x6c, +0x5f,0x80,0x7d,0x77,0x24,0x30,0x48,0x54,0x72,0x78,0x78,0x70,0x40,0x40,0x5e,0x6d, +0x80,0x80,0x6f,0x2d,0x40,0x40,0x5d,0x6c,0x80,0x80,0x75,0x2b,0x40,0x40,0x54,0x71, +0x80,0x80,0x5f,0x35,0x40,0x40,0x49,0x6a,0x80,0x80,0x79,0x5b,0x40,0x40,0x5a,0x73, +0x80,0x80,0x7b,0x49,0x40,0x40,0x5a,0x68,0x80,0x80,0x6b,0x4d,0x40,0x40,0x56,0x74, +0x80,0x80,0x75,0x5d,0x2b,0x40,0x5d,0x74,0x76,0x80,0x6b,0x53,0x40,0x40,0x5b,0x6e, +0x76,0x80,0x61,0x3e,0x40,0x40,0x5b,0x62,0x80,0x80,0x65,0x1f,0x40,0x40,0x5b,0x68, +0x80,0x80,0x7b,0x63,0x40,0x40,0x5b,0x6a,0x80,0x80,0x79,0x63,0x40,0x40,0x57,0x60, +0x80,0x80,0x7a,0x49,0x40,0x4d,0x58,0x67,0x80,0x80,0x7a,0x4b,0x40,0x4e,0x5a,0x67, +0x80,0x80,0x79,0x5f,0x40,0x4d,0x5d,0x6f,0x80,0x80,0x79,0x4d,0x40,0x40,0x4e,0x6d, +0x80,0x80,0x6a,0x4c,0x40,0x4e,0x58,0x6e,0x80,0x80,0x7b,0x31,0x40,0x40,0x58,0x69, +0x80,0x80,0x7c,0x29,0x18,0x3c,0x48,0x54,0x8d,0x87,0x9a,0x7a,0x1f,0x25,0x40,0x54, +0x8b,0x80,0x75,0x4b,0x1c,0x28,0x40,0x68,0x8b,0x80,0x80,0x2e,0x40,0x40,0x56,0x67, +0x80,0x80,0x6f,0x4f,0x40,0x40,0x50,0x61,0x80,0x80,0x59,0x2d,0x40,0x40,0x4e,0x67, +0x80,0x80,0x6c,0x2a,0x40,0x40,0x50,0x64,0x80,0x80,0x63,0x21,0x40,0x40,0x54,0x66, +0x80,0x80,0x71,0x45,0x40,0x40,0x5c,0x6c,0x80,0x80,0x6f,0x4b,0x40,0x40,0x5a,0x6d, +0x80,0x80,0x73,0x29,0x40,0x40,0x56,0x6a,0x80,0x80,0x65,0x27,0x18,0x30,0x48,0x54, +0x86,0x86,0x7d,0x69,0x40,0x40,0x44,0x67,0x80,0x80,0x79,0x29,0x40,0x40,0x5e,0x77, +0x80,0x80,0x73,0x62,0x40,0x40,0x52,0x63,0x80,0x80,0x73,0x61,0x40,0x40,0x54,0x65, +0x80,0x80,0x79,0x63,0x24,0x3c,0x48,0x54,0x7b,0x79,0x6c,0x48,0x40,0x40,0x5e,0x70, +0x80,0x80,0x7b,0x72,0x40,0x40,0x61,0x70,0x80,0x80,0x7b,0x71,0x20,0x40,0x5a,0x75, +0x80,0x80,0x71,0x2b,0x2b,0x40,0x5d,0x75,0x80,0x80,0x71,0x24,0x40,0x40,0x51,0x63, +0x80,0x80,0x6d,0x3d,0x40,0x40,0x52,0x5f,0x80,0x80,0x73,0x51,0x40,0x40,0x56,0x69, +0x80,0x80,0x6b,0x37,0x40,0x4e,0x63,0x75,0x80,0x80,0x76,0x4e,0x40,0x4c,0x5b,0x68, +0x80,0x80,0x77,0x63,0x24,0x30,0x48,0x54,0x6b,0x72,0x78,0x75,0x40,0x40,0x54,0x66, +0x80,0x80,0x7f,0x5f,0x40,0x40,0x52,0x60,0x80,0x80,0x77,0x23,0x40,0x40,0x52,0x68, +0x80,0x80,0x75,0x43,0x40,0x40,0x56,0x67,0x80,0x80,0x78,0x49,0x40,0x4d,0x5b,0x67, +0x80,0x80,0x78,0x32,0x40,0x55,0x5b,0x66,0x80,0x7d,0x6f,0x07,0x40,0x4c,0x57,0x6c, +0x80,0x80,0x77,0x2d,0x40,0x4c,0x54,0x67,0x80,0x80,0x77,0x4b,0x2d,0x40,0x50,0x5d, +0x6d,0x80,0x76,0x24,0x40,0x40,0x4f,0x5b,0x80,0x80,0x75,0x3f,0x40,0x40,0x40,0x73, +0x80,0x80,0x80,0x29,0x40,0x40,0x55,0x70,0x80,0x80,0x7a,0x07,0x40,0x40,0x53,0x6f, +0x80,0x80,0x7b,0x11,0x40,0x40,0x56,0x62,0x80,0x80,0x80,0x0d,0x2d,0x39,0x4d,0x69, +0x71,0x80,0x7b,0x68,0x40,0x40,0x5e,0x6f,0x80,0x80,0x71,0x47,0x40,0x40,0x4e,0x5f, +0x80,0x80,0x79,0x42,0x40,0x40,0x55,0x66,0x80,0x80,0x6b,0x43,0x25,0x40,0x58,0x65, +0x69,0x80,0x79,0x73,0x40,0x40,0x55,0x61,0x80,0x80,0x7b,0x63,0x18,0x3c,0x48,0x54, +0x72,0x63,0x5f,0x63,0x29,0x40,0x5c,0x67,0x6e,0x80,0x7a,0x58,0x2d,0x40,0x59,0x68, +0x73,0x80,0x75,0x59,0x40,0x40,0x5f,0x75,0x80,0x80,0x73,0x64,0x40,0x40,0x55,0x6e, +0x80,0x80,0x73,0x49,0x40,0x40,0x48,0x5a,0x80,0x80,0x73,0x5f,0x1c,0x40,0x5e,0x67, +0x5f,0x80,0x77,0x61,0x1c,0x40,0x5b,0x67,0x71,0x80,0x73,0x5b,0x40,0x40,0x5c,0x6f, +0x80,0x80,0x73,0x57,0x40,0x40,0x5e,0x6f,0x80,0x80,0x71,0x49,0x40,0x4c,0x5b,0x6c, +0x80,0x80,0x76,0x58,0x40,0x40,0x5e,0x6b,0x80,0x80,0x73,0x57,0x40,0x40,0x5c,0x72, +0x80,0x80,0x6f,0x4e,0x40,0x40,0x59,0x6c,0x80,0x80,0x71,0x59,0x40,0x40,0x5c,0x6e, +0x80,0x80,0x71,0x45,0x40,0x40,0x58,0x6d,0x80,0x80,0x71,0x54,0x40,0x40,0x63,0x74, +0x80,0x80,0x6e,0x51,0x40,0x4e,0x5f,0x6c,0x80,0x80,0x6d,0x59,0x40,0x4c,0x5d,0x72, +0x80,0x80,0x71,0x56,0x40,0x40,0x56,0x69,0x80,0x80,0x6d,0x4f,0x40,0x40,0x5c,0x6f, +0x80,0x80,0x6b,0x49,0x18,0x30,0x48,0x54,0x8c,0x7a,0x7a,0x78,0x40,0x4c,0x5d,0x74, +0x80,0x80,0x77,0x55,0x40,0x40,0x60,0x71,0x80,0x80,0x71,0x53,0x33,0x4c,0x5a,0x6d, +0x74,0x80,0x77,0x62,0x40,0x40,0x5d,0x6d,0x80,0x80,0x70,0x4b,0x40,0x40,0x5c,0x6b, +0x80,0x80,0x6c,0x47,0x30,0x3c,0x48,0x54,0x78,0x73,0x72,0x71,0x30,0x3c,0x48,0x54, +0x78,0x73,0x72,0x71,0x18,0x30,0x3c,0x54,0x8b,0x88,0x8a,0x82,0x18,0x30,0x3c,0x54, +0x8b,0x88,0x8a,0x82,0x40,0x40,0x5c,0x6c,0x80,0x80,0x73,0x5a,0x40,0x40,0x58,0x71, +0x80,0x80,0x73,0x5c,0x40,0x40,0x5c,0x6b,0x80,0x80,0x73,0x61,0x40,0x40,0x5b,0x6c, +0x80,0x80,0x71,0x4e,0x40,0x40,0x5d,0x6e,0x80,0x80,0x73,0x4e,0x40,0x40,0x64,0x71, +0x80,0x80,0x71,0x4b,0x24,0x3c,0x48,0x54,0x5a,0x6e,0x79,0x76,0x40,0x40,0x60,0x73, +0x80,0x80,0x65,0x4d,0x40,0x4c,0x5b,0x6b,0x80,0x80,0x75,0x5d,0x40,0x48,0x5c,0x6b, +0x80,0x80,0x75,0x61,0x40,0x49,0x60,0x6f,0x80,0x80,0x75,0x65,0x40,0x51,0x63,0x72, +0x7e,0x80,0x76,0x62,0x40,0x40,0x5f,0x75,0x80,0x80,0x75,0x63,0x40,0x40,0x5a,0x70, +0x80,0x80,0x75,0x5b,0x40,0x40,0x63,0x76,0x80,0x80,0x77,0x55,0x40,0x40,0x5f,0x70, +0x80,0x80,0x73,0x55,0x40,0x40,0x5a,0x6a,0x80,0x80,0x6c,0x4d,0x40,0x40,0x55,0x6c, +0x80,0x80,0x75,0x4a,0x40,0x40,0x5c,0x73,0x80,0x80,0x71,0x5d,0x40,0x40,0x5b,0x6f, +0x80,0x80,0x74,0x51,0x40,0x40,0x63,0x75,0x80,0x80,0x75,0x58,0x40,0x40,0x5f,0x74, +0x80,0x80,0x73,0x55,0x24,0x3c,0x48,0x54,0x6e,0x6a,0x5d,0x64,0x24,0x3c,0x48,0x54, +0x6e,0x6a,0x5d,0x64,0x24,0x30,0x48,0x54,0x73,0x78,0x7d,0x76,0x40,0x40,0x54,0x68, +0x80,0x80,0x80,0x4f,0x24,0x3c,0x48,0x54,0x7b,0x7f,0x83,0x6e,0x40,0x40,0x54,0x67, +0x80,0x80,0x67,0x42,0x40,0x40,0x55,0x68,0x80,0x80,0x77,0x57,0x24,0x3c,0x48,0x54, +0x77,0x7e,0x7a,0x68,0x24,0x3c,0x48,0x54,0x77,0x7e,0x7a,0x68,0x18,0x24,0x3c,0x54, +0x67,0x5e,0x79,0x84,0x40,0x40,0x54,0x63,0x80,0x80,0x73,0x2f,0x40,0x40,0x54,0x6c, +0x80,0x80,0x71,0x2b,0x40,0x40,0x4e,0x5f,0x80,0x80,0x69,0x47,0x40,0x40,0x48,0x6c, +0x80,0x80,0x80,0x8c,0x40,0x40,0x5e,0x72,0x80,0x80,0x75,0x67,0x40,0x40,0x60,0x71, +0x80,0x80,0x75,0x61,0x40,0x40,0x60,0x6a,0x80,0x80,0x7b,0x31,0x40,0x40,0x59,0x73, +0x80,0x80,0x7b,0x6b,0x40,0x40,0x55,0x60,0x80,0x80,0x7b,0x31,0x40,0x40,0x5e,0x6f, +0x80,0x80,0x67,0x53,0x40,0x40,0x54,0x6a,0x80,0x80,0x6f,0x43,0x40,0x40,0x5c,0x73, +0x80,0x80,0x75,0x52,0x40,0x40,0x59,0x75,0x80,0x80,0x71,0x52,0x40,0x40,0x5f,0x75, +0x80,0x80,0x75,0x5d,0x40,0x40,0x60,0x79,0x80,0x80,0x71,0x59,0x40,0x40,0x65,0x75, +0x80,0x80,0x77,0x5b,0x40,0x40,0x61,0x73,0x80,0x80,0x79,0x5f,0x40,0x40,0x58,0x69, +0x80,0x80,0x75,0x57,0x40,0x40,0x55,0x66,0x80,0x80,0x6b,0x51,0x40,0x40,0x51,0x66, +0x80,0x80,0x6d,0x53,0x40,0x40,0x55,0x6a,0x80,0x80,0x71,0x61,0x40,0x40,0x54,0x62, +0x80,0x80,0x79,0x61,0x40,0x40,0x5b,0x6c,0x80,0x80,0x75,0x65,0x40,0x40,0x60,0x70, +0x80,0x80,0x73,0x59,0x40,0x40,0x61,0x6d,0x80,0x80,0x77,0x63,0x40,0x40,0x59,0x6a, +0x80,0x80,0x73,0x5d,0x40,0x40,0x51,0x69,0x80,0x80,0x79,0x69,0x40,0x40,0x57,0x69, +0x80,0x80,0x73,0x61,0x40,0x40,0x56,0x67,0x80,0x80,0x75,0x6b,0x40,0x40,0x5b,0x6d, +0x80,0x80,0x77,0x69,0x40,0x40,0x5c,0x69,0x80,0x80,0x75,0x65,0x40,0x40,0x5b,0x6c, +0x80,0x80,0x71,0x5b,0x40,0x49,0x56,0x62,0x80,0x80,0x6f,0x21,0x40,0x55,0x62,0x6e, +0x80,0x80,0x6f,0x25,0x40,0x40,0x5d,0x70,0x80,0x80,0x73,0x59,0x40,0x40,0x5e,0x76, +0x80,0x80,0x69,0x29,0x40,0x41,0x41,0x42,0x80,0x80,0x80,0x00,0x41,0x42,0x55,0x66, +0x00,0x80,0x75,0x55,0x40,0x40,0x57,0x68,0x80,0x80,0x73,0x49,0x40,0x40,0x52,0x68, +0x80,0x80,0x65,0x47,0x40,0x40,0x56,0x66,0x80,0x80,0x71,0x43,0x40,0x40,0x54,0x63, +0x80,0x80,0x71,0x4f,0x40,0x40,0x51,0x69,0x80,0x80,0x65,0x3d,0x40,0x40,0x53,0x68, +0x80,0x80,0x67,0x3f,0x40,0x40,0x55,0x67,0x80,0x80,0x65,0x35,0x40,0x40,0x55,0x68, +0x80,0x80,0x5d,0x39,0x40,0x40,0x59,0x72,0x80,0x80,0x6b,0x47,0x40,0x40,0x59,0x71, +0x80,0x80,0x6b,0x47,0x40,0x53,0x5b,0x6d,0x80,0x80,0x65,0x45,0x40,0x52,0x5c,0x73, +0x80,0x80,0x75,0x61,0x40,0x51,0x5f,0x6b,0x80,0x80,0x65,0x51,0x40,0x4d,0x60,0x6d, +0x80,0x80,0x6d,0x59,0x40,0x40,0x5a,0x6b,0x80,0x80,0x71,0x60,0x40,0x40,0x5a,0x72, +0x80,0x80,0x73,0x61,0x40,0x40,0x5b,0x6f,0x80,0x80,0x69,0x43,0x40,0x40,0x5a,0x6a, +0x80,0x80,0x77,0x57,0x40,0x49,0x54,0x60,0x80,0x80,0x79,0x4b,0x40,0x40,0x4f,0x67, +0x80,0x80,0x7a,0x4b,0x40,0x40,0x56,0x68,0x80,0x80,0x77,0x59,0x40,0x40,0x4d,0x54, +0x80,0x80,0x79,0x4d,0x40,0x40,0x5b,0x60,0x80,0x80,0x71,0x2c,0x40,0x5b,0x60,0x65, +0x00,0x00,0x80,0x64,0x40,0x40,0x55,0x62,0x80,0x80,0x6b,0x43,0x40,0x40,0x53,0x65, +0x80,0x80,0x6d,0x57,0x40,0x40,0x52,0x66,0x80,0x80,0x71,0x63,0x40,0x40,0x52,0x68, +0x80,0x80,0x69,0x53,0x40,0x40,0x5c,0x6e,0x80,0x80,0x73,0x4f,0x40,0x40,0x54,0x66, +0x80,0x80,0x6f,0x4f,0x40,0x4f,0x5d,0x6e,0x80,0x80,0x6d,0x4f,0x40,0x40,0x5b,0x6f, +0x80,0x80,0x73,0x55,0x40,0x40,0x55,0x66,0x80,0x80,0x71,0x5d,0x40,0x40,0x54,0x69, +0x80,0x80,0x73,0x5a,0x40,0x40,0x40,0x4c,0x80,0x80,0x82,0x02,0x35,0x41,0x59,0x6b, +0x00,0x80,0x67,0x2f,0x40,0x40,0x62,0x71,0x80,0x80,0x73,0x53,0x40,0x40,0x62,0x73, +0x80,0x80,0x73,0x57,0x40,0x40,0x5a,0x6d,0x80,0x80,0x77,0x63,0x40,0x40,0x5c,0x6d, +0x80,0x80,0x75,0x67,0x40,0x40,0x66,0x6f,0x80,0x80,0x71,0x4d,0x40,0x40,0x66,0x6e, +0x80,0x80,0x65,0x37,0x40,0x40,0x61,0x6d,0x80,0x80,0x73,0x5d,0x40,0x40,0x5e,0x6c, +0x80,0x80,0x6f,0x57,0x40,0x40,0x5f,0x6f,0x80,0x80,0x6d,0x4f,0x40,0x40,0x5b,0x72, +0x80,0x80,0x71,0x57,0x40,0x40,0x63,0x72,0x80,0x80,0x75,0x48,0x40,0x56,0x69,0x77, +0x80,0x80,0x6c,0x51,0x40,0x4f,0x64,0x74,0x80,0x80,0x6b,0x53,0x1e,0x2a,0x36,0x37, +0x80,0x80,0x80,0x00,0x40,0x40,0x5a,0x69,0x80,0x80,0x6c,0x4b,0x40,0x40,0x55,0x68, +0x80,0x80,0x71,0x4d,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x01,0xf0,0x7e,0x00,0x09,0x01,0xf7,0x7f,0x7f,0x7f,0x7f,0x7f, +0x7f,0x7f,0x7f,0x7f,0x7a,0x75,0x71,0x6d,0x69,0x66,0x62,0x5f,0x5d,0x5a,0x58,0x55, +0x53,0x51,0x4f,0x4d,0x4b,0x49,0x47,0x46,0x44,0x42,0x41,0x3f,0x3e,0x3d,0x3b,0x3a, +0x39,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29, +0x28,0x27,0x26,0x25,0x25,0x24,0x23,0x22,0x21,0x21,0x20,0x1f,0x1f,0x1e,0x1d,0x1c, +0x1c,0x1b,0x1a,0x1a,0x19,0x19,0x18,0x17,0x17,0x16,0x15,0x15,0x14,0x14,0x13,0x13, +0x12,0x12,0x11,0x10,0x10,0x0f,0x0f,0x0e,0x0e,0x0d,0x0d,0x0c,0x0c,0x0c,0x0b,0x0b, +0x0a,0x0a,0x09,0x09,0x08,0x08,0x07,0x07,0x07,0x06,0x06,0x05,0x05,0x05,0x04,0x04, +0x03,0x03,0x03,0x02,0x02,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x01,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x03,0x00,0x03,0x00,0x04,0x00, +0x04,0x00,0x05,0x00,0x05,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x07,0x00,0x07,0x00, +0x08,0x00,0x08,0x00,0x09,0x00,0x09,0x00,0x0a,0x00,0x0a,0x00,0x0b,0x00,0x0b,0x00, +0x0c,0x00,0x0c,0x00,0x0d,0x00,0x0d,0x00,0x0d,0x00,0x0e,0x00,0x0e,0x00,0x0f,0x00, +0x0f,0x00,0x10,0x00,0x10,0x00,0x11,0x00,0x11,0x00,0x12,0x00,0x12,0x00,0x13,0x00, +0x13,0x00,0x14,0x00,0x14,0x00,0x15,0x00,0x15,0x00,0x15,0x00,0x16,0x00,0x16,0x00, +0x17,0x00,0x17,0x00,0x18,0x00,0x18,0x00,0x19,0x00,0x19,0x00,0x1a,0x00,0x1a,0x00, +0x1b,0x00,0x1b,0x00,0x1c,0x00,0x1c,0x00,0x1d,0x00,0x1d,0x00,0x1e,0x00,0x1e,0x00, +0x1e,0x00,0x1f,0x00,0x1f,0x00,0x20,0x00,0x20,0x00,0x21,0x00,0x21,0x00,0x22,0x00, +0x22,0x00,0x23,0x00,0x23,0x00,0x24,0x00,0x24,0x00,0x25,0x00,0x25,0x00,0x26,0x00, +0x26,0x00,0x27,0x00,0x27,0x00,0x28,0x00,0x28,0x00,0x29,0x00,0x29,0x00,0x29,0x00, +0x2a,0x00,0x2a,0x00,0x2b,0x00,0x2b,0x00,0x2c,0x00,0x2c,0x00,0x2d,0x00,0x2d,0x00, +0x2e,0x00,0x2e,0x00,0x2f,0x00,0x2f,0x00,0x30,0x00,0x30,0x00,0x31,0x00,0x31,0x00, +0x32,0x00,0x32,0x00,0x33,0x00,0x33,0x00,0x34,0x00,0x34,0x00,0x35,0x00,0x35,0x00, +0x36,0x00,0x36,0x00,0x37,0x00,0x37,0x00,0x38,0x00,0x38,0x00,0x38,0x00,0x39,0x00, +0x39,0x00,0x3a,0x00,0x3a,0x00,0x3b,0x00,0x3b,0x00,0x3c,0x00,0x3c,0x00,0x3d,0x00, +0x3d,0x00,0x3e,0x00,0x3e,0x00,0x3f,0x00,0x3f,0x00,0x40,0x00,0x40,0x00,0x41,0x00, +0x41,0x00,0x42,0x00,0x42,0x00,0x43,0x00,0x43,0x00,0x44,0x00,0x44,0x00,0x45,0x00, +0x45,0x00,0x46,0x00,0x46,0x00,0x47,0x00,0x47,0x00,0x48,0x00,0x48,0x00,0x49,0x00, +0x49,0x00,0x4a,0x00,0x4a,0x00,0x4b,0x00,0x4b,0x00,0x4c,0x00,0x4c,0x00,0x4d,0x00, +0x4d,0x00,0x4e,0x00,0x4e,0x00,0x4f,0x00,0x4f,0x00,0x50,0x00,0x50,0x00,0x51,0x00, +0x51,0x00,0x52,0x00,0x52,0x00,0x53,0x00,0x53,0x00,0x54,0x00,0x54,0x00,0x55,0x00, +0x55,0x00,0x56,0x00,0x56,0x00,0x57,0x00,0x57,0x00,0x58,0x00,0x58,0x00,0x59,0x00, +0x59,0x00,0x5a,0x00,0x5a,0x00,0x5b,0x00,0x5b,0x00,0x5c,0x00,0x5c,0x00,0x5d,0x00, +0x5d,0x00,0x5e,0x00,0x5e,0x00,0x5f,0x00,0x5f,0x00,0x60,0x00,0x60,0x00,0x61,0x00, +0x61,0x00,0x62,0x00,0x62,0x00,0x63,0x00,0x63,0x00,0x64,0x00,0x64,0x00,0x65,0x00, +0x65,0x00,0x66,0x00,0x66,0x00,0x67,0x00,0x67,0x00,0x68,0x00,0x68,0x00,0x69,0x00, +0x69,0x00,0x6a,0x00,0x6a,0x00,0x6b,0x00,0x6b,0x00,0x6c,0x00,0x6c,0x00,0x6d,0x00, +0x6d,0x00,0x6e,0x00,0x6e,0x00,0x6f,0x00,0x6f,0x00,0x70,0x00,0x71,0x00,0x71,0x00, +0x72,0x00,0x72,0x00,0x73,0x00,0x73,0x00,0x74,0x00,0x74,0x00,0x75,0x00,0x75,0x00, +0x76,0x00,0x76,0x00,0x77,0x00,0x77,0x00,0x78,0x00,0x78,0x00,0x79,0x00,0x79,0x00, +0x7a,0x00,0x7a,0x00,0x7b,0x00,0x7b,0x00,0x7c,0x00,0x7c,0x00,0x7d,0x00,0x7d,0x00, +0x7e,0x00,0x7e,0x00,0x7f,0x00,0x7f,0x00,0x80,0x00,0x81,0x00,0x81,0x00,0x82,0x00, +0x82,0x00,0x83,0x00,0x83,0x00,0x84,0x00,0x84,0x00,0x85,0x00,0x85,0x00,0x86,0x00, +0x86,0x00,0x87,0x00,0x87,0x00,0x88,0x00,0x88,0x00,0x89,0x00,0x89,0x00,0x8a,0x00, +0x8a,0x00,0x8b,0x00,0x8c,0x00,0x8c,0x00,0x8d,0x00,0x8d,0x00,0x8e,0x00,0x8e,0x00, +0x8f,0x00,0x8f,0x00,0x90,0x00,0x90,0x00,0x91,0x00,0x91,0x00,0x92,0x00,0x92,0x00, +0x93,0x00,0x93,0x00,0x94,0x00,0x94,0x00,0x95,0x00,0x96,0x00,0x96,0x00,0x97,0x00, +0x97,0x00,0x98,0x00,0x98,0x00,0x99,0x00,0x99,0x00,0x9a,0x00,0x9a,0x00,0x9b,0x00, +0x9b,0x00,0x9c,0x00,0x9c,0x00,0x9d,0x00,0x9e,0x00,0x9e,0x00,0x9f,0x00,0x9f,0x00, +0xa0,0x00,0xa0,0x00,0xa1,0x00,0xa1,0x00,0xa2,0x00,0xa2,0x00,0xa3,0x00,0xa3,0x00, +0xa4,0x00,0xa4,0x00,0xa5,0x00,0xa6,0x00,0xa6,0x00,0xa7,0x00,0xa7,0x00,0xa8,0x00, +0xa8,0x00,0xa9,0x00,0xa9,0x00,0xaa,0x00,0xaa,0x00,0xab,0x00,0xab,0x00,0xac,0x00, +0xad,0x00,0xad,0x00,0xae,0x00,0xae,0x00,0xaf,0x00,0xaf,0x00,0xb0,0x00,0xb0,0x00, +0xb1,0x00,0xb1,0x00,0xb2,0x00,0xb3,0x00,0xb3,0x00,0xb4,0x00,0xb4,0x00,0xb5,0x00, +0xb5,0x00,0xb6,0x00,0xb6,0x00,0xb7,0x00,0xb7,0x00,0xb8,0x00,0xb8,0x00,0xb9,0x00, +0xba,0x00,0xba,0x00,0xbb,0x00,0xbb,0x00,0xbc,0x00,0xbc,0x00,0xbd,0x00,0xbd,0x00, +0xbe,0x00,0xbf,0x00,0xbf,0x00,0xc0,0x00,0xc0,0x00,0xc1,0x00,0xc1,0x00,0xc2,0x00, +0xc2,0x00,0xc3,0x00,0xc3,0x00,0xc4,0x00,0xc5,0x00,0xc5,0x00,0xc6,0x00,0xc6,0x00, +0xc7,0x00,0xc7,0x00,0xc8,0x00,0xc8,0x00,0xc9,0x00,0xca,0x00,0xca,0x00,0xcb,0x00, +0xcb,0x00,0xcc,0x00,0xcc,0x00,0xcd,0x00,0xcd,0x00,0xce,0x00,0xcf,0x00,0xcf,0x00, +0xd0,0x00,0xd0,0x00,0xd1,0x00,0xd1,0x00,0xd2,0x00,0xd2,0x00,0xd3,0x00,0xd4,0x00, +0xd4,0x00,0xd5,0x00,0xd5,0x00,0xd6,0x00,0xd6,0x00,0xd7,0x00,0xd7,0x00,0xd8,0x00, +0xd9,0x00,0xd9,0x00,0xda,0x00,0xda,0x00,0xdb,0x00,0xdb,0x00,0xdc,0x00,0xdc,0x00, +0xdd,0x00,0xde,0x00,0xde,0x00,0xdf,0x00,0xdf,0x00,0xe0,0x00,0xe0,0x00,0xe1,0x00, +0xe2,0x00,0xe2,0x00,0xe3,0x00,0xe3,0x00,0xe4,0x00,0xe4,0x00,0xe5,0x00,0xe5,0x00, +0xe6,0x00,0xe7,0x00,0xe7,0x00,0xe8,0x00,0xe8,0x00,0xe9,0x00,0xe9,0x00,0xea,0x00, +0xeb,0x00,0xeb,0x00,0xec,0x00,0xec,0x00,0xed,0x00,0xed,0x00,0xee,0x00,0xef,0x00, +0xef,0x00,0xf0,0x00,0xf0,0x00,0xf1,0x00,0xf1,0x00,0xf2,0x00,0xf3,0x00,0xf3,0x00, +0xf4,0x00,0xf4,0x00,0xf5,0x00,0xf5,0x00,0xf6,0x00,0xf7,0x00,0xf7,0x00,0xf8,0x00, +0xf8,0x00,0xf9,0x00,0xf9,0x00,0xfa,0x00,0xfb,0x00,0xfb,0x00,0xfc,0x00,0xfc,0x00, +0xfd,0x00,0xfd,0x00,0xfe,0x00,0xff,0x00,0xff,0x01,0x00,0x01,0x00,0x01,0x01,0x01, +0x02,0x01,0x02,0x01,0x03,0x01,0x03,0x01,0x04,0x01,0x04,0x01,0x05,0x01,0x06,0x01, +0x06,0x01,0x07,0x01,0x07,0x01,0x08,0x01,0x08,0x01,0x09,0x01,0x0a,0x01,0x0a,0x01, +0x0b,0x01,0x0b,0x01,0x0c,0x01,0x0d,0x01,0x0d,0x01,0x0e,0x01,0x0e,0x01,0x0f,0x01, +0x0f,0x01,0x10,0x01,0x11,0x01,0x11,0x01,0x12,0x01,0x12,0x01,0x13,0x01,0x14,0x01, +0x14,0x01,0x15,0x01,0x15,0x01,0x16,0x01,0x17,0x01,0x17,0x01,0x18,0x01,0x18,0x01, +0x19,0x01,0x19,0x01,0x1a,0x01,0x1b,0x01,0x1b,0x01,0x1c,0x01,0x1c,0x01,0x1d,0x01, +0x1e,0x01,0x1e,0x01,0x1f,0x01,0x1f,0x01,0x20,0x01,0x21,0x01,0x21,0x01,0x22,0x01, +0x22,0x01,0x23,0x01,0x24,0x01,0x24,0x01,0x25,0x01,0x25,0x01,0x26,0x01,0x27,0x01, +0x27,0x01,0x28,0x01,0x28,0x01,0x29,0x01,0x29,0x01,0x2a,0x01,0x2b,0x01,0x2b,0x01, +0x2c,0x01,0x2c,0x01,0x2d,0x01,0x2e,0x01,0x2e,0x01,0x2f,0x01,0x2f,0x01,0x30,0x01, +0x31,0x01,0x31,0x01,0x32,0x01,0x32,0x01,0x33,0x01,0x34,0x01,0x34,0x01,0x35,0x01, +0x35,0x01,0x36,0x01,0x37,0x01,0x37,0x01,0x38,0x01,0x38,0x01,0x39,0x01,0x3a,0x01, +0x3a,0x01,0x3b,0x01,0x3c,0x01,0x3c,0x01,0x3d,0x01,0x3d,0x01,0x3e,0x01,0x3f,0x01, +0x3f,0x01,0x40,0x01,0x40,0x01,0x41,0x01,0x42,0x01,0x42,0x01,0x43,0x01,0x43,0x01, +0x44,0x01,0x45,0x01,0x45,0x01,0x46,0x01,0x46,0x01,0x47,0x01,0x48,0x01,0x48,0x01, +0x49,0x01,0x49,0x01,0x4a,0x01,0x4b,0x01,0x4b,0x01,0x4c,0x01,0x4d,0x01,0x4d,0x01, +0x4e,0x01,0x4e,0x01,0x4f,0x01,0x50,0x01,0x50,0x01,0x51,0x01,0x51,0x01,0x52,0x01, +0x53,0x01,0x53,0x01,0x54,0x01,0x55,0x01,0x55,0x01,0x56,0x01,0x56,0x01,0x57,0x01, +0x58,0x01,0x58,0x01,0x59,0x01,0x59,0x01,0x5a,0x01,0x5b,0x01,0x5b,0x01,0x5c,0x01, +0x5d,0x01,0x5d,0x01,0x5e,0x01,0x5e,0x01,0x5f,0x01,0x60,0x01,0x60,0x01,0x61,0x01, +0x62,0x01,0x62,0x01,0x63,0x01,0x63,0x01,0x64,0x01,0x65,0x01,0x65,0x01,0x66,0x01, +0x67,0x01,0x67,0x01,0x68,0x01,0x68,0x01,0x69,0x01,0x6a,0x01,0x6a,0x01,0x6b,0x01, +0x6c,0x01,0x6c,0x01,0x6d,0x01,0x6d,0x01,0x6e,0x01,0x6f,0x01,0x6f,0x01,0x70,0x01, +0x71,0x01,0x71,0x01,0x72,0x01,0x72,0x01,0x73,0x01,0x74,0x01,0x74,0x01,0x75,0x01, +0x76,0x01,0x76,0x01,0x77,0x01,0x77,0x01,0x78,0x01,0x79,0x01,0x79,0x01,0x7a,0x01, +0x7b,0x01,0x7b,0x01,0x7c,0x01,0x7d,0x01,0x7d,0x01,0x7e,0x01,0x7e,0x01,0x7f,0x01, +0x80,0x01,0x80,0x01,0x81,0x01,0x82,0x01,0x82,0x01,0x83,0x01,0x84,0x01,0x84,0x01, +0x85,0x01,0x85,0x01,0x86,0x01,0x87,0x01,0x87,0x01,0x88,0x01,0x89,0x01,0x89,0x01, +0x8a,0x01,0x8b,0x01,0x8b,0x01,0x8c,0x01,0x8c,0x01,0x8d,0x01,0x8e,0x01,0x8e,0x01, +0x8f,0x01,0x90,0x01,0x90,0x01,0x91,0x01,0x92,0x01,0x92,0x01,0x93,0x01,0x94,0x01, +0x94,0x01,0x95,0x01,0x95,0x01,0x96,0x01,0x97,0x01,0x97,0x01,0x98,0x01,0x99,0x01, +0x99,0x01,0x9a,0x01,0x9b,0x01,0x9b,0x01,0x9c,0x01,0x9d,0x01,0x9d,0x01,0x9e,0x01, +0x9f,0x01,0x9f,0x01,0xa0,0x01,0xa0,0x01,0xa1,0x01,0xa2,0x01,0xa2,0x01,0xa3,0x01, +0xa4,0x01,0xa4,0x01,0xa5,0x01,0xa6,0x01,0xa6,0x01,0xa7,0x01,0xa8,0x01,0xa8,0x01, +0xa9,0x01,0xaa,0x01,0xaa,0x01,0xab,0x01,0xac,0x01,0xac,0x01,0xad,0x01,0xae,0x01, +0xae,0x01,0xaf,0x01,0xb0,0x01,0xb0,0x01,0xb1,0x01,0xb1,0x01,0xb2,0x01,0xb3,0x01, +0xb3,0x01,0xb4,0x01,0xb5,0x01,0xb5,0x01,0xb6,0x01,0xb7,0x01,0xb7,0x01,0xb8,0x01, +0xb9,0x01,0xb9,0x01,0xba,0x01,0xbb,0x01,0xbb,0x01,0xbc,0x01,0xbd,0x01,0xbd,0x01, +0xbe,0x01,0xbf,0x01,0xbf,0x01,0xc0,0x01,0xc1,0x01,0xc1,0x01,0xc2,0x01,0xc3,0x01, +0xc3,0x01,0xc4,0x01,0xc5,0x01,0xc5,0x01,0xc6,0x01,0xc7,0x01,0xc7,0x01,0xc8,0x01, +0xc9,0x01,0xc9,0x01,0xca,0x01,0xcb,0x01,0xcb,0x01,0xcc,0x01,0xcd,0x01,0xcd,0x01, +0xce,0x01,0xcf,0x01,0xcf,0x01,0xd0,0x01,0xd1,0x01,0xd1,0x01,0xd2,0x01,0xd3,0x01, +0xd3,0x01,0xd4,0x01,0xd5,0x01,0xd5,0x01,0xd6,0x01,0xd7,0x01,0xd7,0x01,0xd8,0x01, +0xd9,0x01,0xda,0x01,0xda,0x01,0xdb,0x01,0xdc,0x01,0xdc,0x01,0xdd,0x01,0xde,0x01, +0xde,0x01,0xdf,0x01,0xe0,0x01,0xe0,0x01,0xe1,0x01,0xe2,0x01,0xe2,0x01,0xe3,0x01, +0xe4,0x01,0xe4,0x01,0xe5,0x01,0xe6,0x01,0xe6,0x01,0xe7,0x01,0xe8,0x01,0xe8,0x01, +0xe9,0x01,0xea,0x01,0xeb,0x01,0xeb,0x01,0xec,0x01,0xed,0x01,0xed,0x01,0xee,0x01, +0xef,0x01,0xef,0x01,0xf0,0x01,0xf1,0x01,0xf1,0x01,0xf2,0x01,0xf3,0x01,0xf3,0x01, +0xf4,0x01,0xf5,0x01,0xf5,0x01,0xf6,0x01,0xf7,0x01,0xf8,0x01,0xf8,0x01,0xf9,0x01, +0xfa,0x01,0xfa,0x01,0xfb,0x01,0xfc,0x01,0xfc,0x01,0xfd,0x01,0xfe,0x01,0xfe,0x01, +0xff,0x02,0x00,0x02,0x01,0x02,0x01,0x02,0x02,0x02,0x03,0x02,0x03,0x02,0x04,0x02, +0x05,0x02,0x05,0x02,0x06,0x02,0x07,0x02,0x07,0x02,0x08,0x02,0x09,0x02,0x0a,0x02, +0x0a,0x02,0x0b,0x02,0x0c,0x02,0x0c,0x02,0x0d,0x02,0x0e,0x02,0x0e,0x02,0x0f,0x02, +0x10,0x02,0x11,0x02,0x11,0x02,0x12,0x02,0x13,0x02,0x13,0x02,0x14,0x02,0x15,0x02, +0x15,0x02,0x16,0x02,0x17,0x02,0x18,0x02,0x18,0x02,0x19,0x02,0x1a,0x02,0x1a,0x02, +0x1b,0x02,0x1c,0x02,0x1d,0x02,0x1d,0x02,0x1e,0x02,0x1f,0x02,0x1f,0x02,0x20,0x02, +0x21,0x02,0x21,0x02,0x22,0x02,0x23,0x02,0x24,0x02,0x24,0x02,0x25,0x02,0x26,0x02, +0x26,0x02,0x27,0x02,0x28,0x02,0x29,0x02,0x29,0x02,0x2a,0x02,0x2b,0x02,0x2b,0x02, +0x2c,0x02,0x2d,0x02,0x2e,0x02,0x2e,0x02,0x2f,0x02,0x30,0x02,0x30,0x02,0x31,0x02, +0x32,0x02,0x33,0x02,0x33,0x02,0x34,0x02,0x35,0x02,0x35,0x02,0x36,0x02,0x37,0x02, +0x38,0x02,0x38,0x02,0x39,0x02,0x3a,0x02,0x3a,0x02,0x3b,0x02,0x3c,0x02,0x3d,0x02, +0x3d,0x02,0x3e,0x02,0x3f,0x02,0x40,0x02,0x40,0x02,0x41,0x02,0x42,0x02,0x42,0x02, +0x43,0x02,0x44,0x02,0x45,0x02,0x45,0x02,0x46,0x02,0x47,0x02,0x47,0x02,0x48,0x02, +0x49,0x02,0x4a,0x02,0x4a,0x02,0x4b,0x02,0x4c,0x02,0x4d,0x02,0x4d,0x02,0x4e,0x02, +0x4f,0x02,0x4f,0x02,0x50,0x02,0x51,0x02,0x52,0x02,0x52,0x02,0x53,0x02,0x54,0x02, +0x55,0x02,0x55,0x02,0x56,0x02,0x57,0x02,0x58,0x02,0x58,0x02,0x59,0x02,0x5a,0x02, +0x5a,0x02,0x5b,0x02,0x5c,0x02,0x5d,0x02,0x5d,0x02,0x5e,0x02,0x5f,0x02,0x60,0x02, +0x60,0x02,0x61,0x02,0x62,0x02,0x63,0x02,0x63,0x02,0x64,0x02,0x65,0x02,0x66,0x02, +0x66,0x02,0x67,0x02,0x68,0x02,0x68,0x02,0x69,0x02,0x6a,0x02,0x6b,0x02,0x6b,0x02, +0x6c,0x02,0x6d,0x02,0x6e,0x02,0x6e,0x02,0x6f,0x02,0x70,0x02,0x71,0x02,0x71,0x02, +0x72,0x02,0x73,0x02,0x74,0x02,0x74,0x02,0x75,0x02,0x76,0x02,0x77,0x02,0x77,0x02, +0x78,0x02,0x79,0x02,0x7a,0x02,0x7a,0x02,0x7b,0x02,0x7c,0x02,0x7d,0x02,0x7d,0x02, +0x7e,0x02,0x7f,0x02,0x80,0x02,0x80,0x02,0x81,0x02,0x82,0x02,0x83,0x02,0x83,0x02, +0x84,0x02,0x85,0x02,0x86,0x02,0x86,0x02,0x87,0x02,0x88,0x02,0x89,0x02,0x89,0x02, +0x8a,0x02,0x8b,0x02,0x8c,0x02,0x8c,0x02,0x8d,0x02,0x8e,0x02,0x8f,0x02,0x8f,0x02, +0x90,0x02,0x91,0x02,0x92,0x02,0x92,0x02,0x93,0x02,0x94,0x02,0x95,0x02,0x96,0x02, +0x96,0x02,0x97,0x02,0x98,0x02,0x99,0x02,0x99,0x02,0x9a,0x02,0x9b,0x02,0x9c,0x02, +0x9c,0x02,0x9d,0x02,0x9e,0x02,0x9f,0x02,0x9f,0x02,0xa0,0x02,0xa1,0x02,0xa2,0x02, +0xa2,0x02,0xa3,0x02,0xa4,0x02,0xa5,0x02,0xa6,0x02,0xa6,0x02,0xa7,0x02,0xa8,0x02, +0xa9,0x02,0xa9,0x02,0xaa,0x02,0xab,0x02,0xac,0x02,0xac,0x02,0xad,0x02,0xae,0x02, +0xaf,0x02,0xb0,0x02,0xb0,0x02,0xb1,0x02,0xb2,0x02,0xb3,0x02,0xb3,0x02,0xb4,0x02, +0xb5,0x02,0xb6,0x02,0xb7,0x02,0xb7,0x02,0xb8,0x02,0xb9,0x02,0xba,0x02,0xba,0x02, +0xbb,0x02,0xbc,0x02,0xbd,0x02,0xbe,0x02,0xbe,0x02,0xbf,0x02,0xc0,0x02,0xc1,0x02, +0xc1,0x02,0xc2,0x02,0xc3,0x02,0xc4,0x02,0xc5,0x02,0xc5,0x02,0xc6,0x02,0xc7,0x02, +0xc8,0x02,0xc8,0x02,0xc9,0x02,0xca,0x02,0xcb,0x02,0xcc,0x02,0xcc,0x02,0xcd,0x02, +0xce,0x02,0xcf,0x02,0xd0,0x02,0xd0,0x02,0xd1,0x02,0xd2,0x02,0xd3,0x02,0xd3,0x02, +0xd4,0x02,0xd5,0x02,0xd6,0x02,0xd7,0x02,0xd7,0x02,0xd8,0x02,0xd9,0x02,0xda,0x02, +0xdb,0x02,0xdb,0x02,0xdc,0x02,0xdd,0x02,0xde,0x02,0xdf,0x02,0xdf,0x02,0xe0,0x02, +0xe1,0x02,0xe2,0x02,0xe3,0x02,0xe3,0x02,0xe4,0x02,0xe5,0x02,0xe6,0x02,0xe7,0x02, +0xe7,0x02,0xe8,0x02,0xe9,0x02,0xea,0x02,0xeb,0x02,0xeb,0x02,0xec,0x02,0xed,0x02, +0xee,0x02,0xef,0x02,0xef,0x02,0xf0,0x02,0xf1,0x02,0xf2,0x02,0xf3,0x02,0xf3,0x02, +0xf4,0x02,0xf5,0x02,0xf6,0x02,0xf7,0x02,0xf7,0x02,0xf8,0x02,0xf9,0x02,0xfa,0x02, +0xfb,0x02,0xfb,0x02,0xfc,0x02,0xfd,0x02,0xfe,0x02,0xff,0x02,0xff,0x03,0x00,0x03, +0x01,0x03,0x02,0x03,0x03,0x03,0x03,0x03,0x04,0x03,0x05,0x03,0x06,0x03,0x07,0x03, +0x08,0x03,0x08,0x03,0x09,0x03,0x0a,0x03,0x0b,0x03,0x0c,0x03,0x0c,0x03,0x0d,0x03, +0x0e,0x03,0x0f,0x03,0x10,0x03,0x10,0x03,0x11,0x03,0x12,0x03,0x13,0x03,0x14,0x03, +0x15,0x03,0x15,0x03,0x16,0x03,0x17,0x03,0x18,0x03,0x19,0x03,0x19,0x03,0x1a,0x03, +0x1b,0x03,0x1c,0x03,0x1d,0x03,0x1e,0x03,0x1e,0x03,0x1f,0x03,0x20,0x03,0x21,0x03, +0x22,0x03,0x23,0x03,0x23,0x03,0x24,0x03,0x25,0x03,0x26,0x03,0x27,0x03,0x27,0x03, +0x28,0x03,0x29,0x03,0x2a,0x03,0x2b,0x03,0x2c,0x03,0x2c,0x03,0x2d,0x03,0x2e,0x03, +0x2f,0x03,0x30,0x03,0x31,0x03,0x31,0x03,0x32,0x03,0x33,0x03,0x34,0x03,0x35,0x03, +0x36,0x03,0x36,0x03,0x37,0x03,0x38,0x03,0x39,0x03,0x3a,0x03,0x3b,0x03,0x3b,0x03, +0x3c,0x03,0x3d,0x03,0x3e,0x03,0x3f,0x03,0x40,0x03,0x40,0x03,0x41,0x03,0x42,0x03, +0x43,0x03,0x44,0x03,0x45,0x03,0x45,0x03,0x46,0x03,0x47,0x03,0x48,0x03,0x49,0x03, +0x4a,0x03,0x4b,0x03,0x4b,0x03,0x4c,0x03,0x4d,0x03,0x4e,0x03,0x4f,0x03,0x50,0x03, +0x50,0x03,0x51,0x03,0x52,0x03,0x53,0x03,0x54,0x03,0x55,0x03,0x56,0x03,0x56,0x03, +0x57,0x03,0x58,0x03,0x59,0x03,0x5a,0x03,0x5b,0x03,0x5b,0x03,0x5c,0x03,0x5d,0x03, +0x5e,0x03,0x5f,0x03,0x60,0x03,0x61,0x03,0x61,0x03,0x62,0x03,0x63,0x03,0x64,0x03, +0x65,0x03,0x66,0x03,0x67,0x03,0x67,0x03,0x68,0x03,0x69,0x03,0x6a,0x03,0x6b,0x03, +0x6c,0x03,0x6d,0x03,0x6d,0x03,0x6e,0x03,0x6f,0x03,0x70,0x03,0x71,0x03,0x72,0x03, +0x73,0x03,0x73,0x03,0x74,0x03,0x75,0x03,0x76,0x03,0x77,0x03,0x78,0x03,0x79,0x03, +0x79,0x03,0x7a,0x03,0x7b,0x03,0x7c,0x03,0x7d,0x03,0x7e,0x03,0x7f,0x03,0x80,0x03, +0x80,0x03,0x81,0x03,0x82,0x03,0x83,0x03,0x84,0x03,0x85,0x03,0x86,0x03,0x86,0x03, +0x87,0x03,0x88,0x03,0x89,0x03,0x8a,0x03,0x8b,0x03,0x8c,0x03,0x8d,0x03,0x8d,0x03, +0x8e,0x03,0x8f,0x03,0x90,0x03,0x91,0x03,0x92,0x03,0x93,0x03,0x94,0x03,0x94,0x03, +0x95,0x03,0x96,0x03,0x97,0x03,0x98,0x03,0x99,0x03,0x9a,0x03,0x9b,0x03,0x9b,0x03, +0x9c,0x03,0x9d,0x03,0x9e,0x03,0x9f,0x03,0xa0,0x03,0xa1,0x03,0xa2,0x03,0xa2,0x03, +0xa3,0x03,0xa4,0x03,0xa5,0x03,0xa6,0x03,0xa7,0x03,0xa8,0x03,0xa9,0x03,0xaa,0x03, +0xaa,0x03,0xab,0x03,0xac,0x03,0xad,0x03,0xae,0x03,0xaf,0x03,0xb0,0x03,0xb1,0x03, +0xb2,0x03,0xb2,0x03,0xb3,0x03,0xb4,0x03,0xb5,0x03,0xb6,0x03,0xb7,0x03,0xb8,0x03, +0xb9,0x03,0xba,0x03,0xba,0x03,0xbb,0x03,0xbc,0x03,0xbd,0x03,0xbe,0x03,0xbf,0x03, +0xc0,0x03,0xc1,0x03,0xc2,0x03,0xc3,0x03,0xc3,0x03,0xc4,0x03,0xc5,0x03,0xc6,0x03, +0xc7,0x03,0xc8,0x03,0xc9,0x03,0xca,0x03,0xcb,0x03,0xcb,0x03,0xcc,0x03,0xcd,0x03, +0xce,0x03,0xcf,0x03,0xd0,0x03,0xd1,0x03,0xd2,0x03,0xd3,0x03,0xd4,0x03,0xd5,0x03, +0xd5,0x03,0xd6,0x03,0xd7,0x03,0xd8,0x03,0xd9,0x03,0xda,0x03,0xdb,0x03,0xdc,0x03, +0xdd,0x03,0xde,0x03,0xde,0x03,0xdf,0x03,0xe0,0x03,0xe1,0x03,0xe2,0x03,0xe3,0x03, +0xe4,0x03,0xe5,0x03,0xe6,0x03,0xe7,0x03,0xe8,0x03,0xe9,0x03,0xe9,0x03,0xea,0x03, +0xeb,0x03,0xec,0x03,0xed,0x03,0xee,0x03,0xef,0x03,0xf0,0x03,0xf1,0x03,0xf2,0x03, +0xf3,0x03,0xf4,0x03,0xf4,0x03,0xf5,0x03,0xf6,0x03,0xf7,0x03,0xf8,0x03,0xf9,0x03, +0xfa,0x03,0xfb,0x03,0xfc,0x03,0xfd,0x03,0xfe,0x03,0xff,0x54,0x52,0x49,0x58,0x2d, +0x50,0x52,0x4f,0x01,0x0a,0x00,0x01,0x02,0x03,0x04,0x05,0x04,0x03,0x10,0x10,0x48, +0x12,0x13,0x14,0x15,0x17,0x06,0x06,0x06,0x06,0x07,0x07,0x08,0x08,0x3e,0x3f,0x3e, +0x3f,0x27,0x27,0x51,0x26,0x58,0x5e,0x36,0x5c,0x61,0x63,0x62,0x6d,0x66,0x60,0x44, +0x5d,0x50,0x51,0x09,0x50,0x30,0x30,0x31,0x2d,0x28,0x29,0x2a,0x2a,0x2b,0x2e,0x2e, +0x18,0x19,0x1a,0x1b,0x68,0x20,0x21,0x22,0x22,0x24,0x25,0x23,0x20,0x49,0x49,0x48, +0x48,0x4a,0x4b,0x40,0x41,0x42,0x43,0x47,0x47,0x44,0x45,0x46,0x16,0x38,0x38,0x39, +0x39,0x3c,0x3c,0x3a,0x3d,0x3e,0x0b,0x62,0x2e,0x64,0x09,0x0e,0x0d,0x0c,0x6b,0x56, +0x52,0x4e,0x4e,0x4b,0x5e,0x2f,0x75,0x76,0x76,0x76,0x74,0x73,0x77,0x73,0x09,0x37, +0x7c,0x7b,0x7d,0x70,0x52 +}; + diff --git a/sys/i386/isa/sound/tuning.h b/sys/i386/isa/sound/tuning.h new file mode 100644 index 0000000..858e1fe --- /dev/null +++ b/sys/i386/isa/sound/tuning.h @@ -0,0 +1,29 @@ +#ifdef SEQUENCER_C + +unsigned short semitone_tuning[24] = +{ +/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, +/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, +/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 +}; + +unsigned short cent_tuning[100] = +{ +/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, +/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, +/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, +/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, +/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, +/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, +/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, +/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, +/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, +/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, +/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, +/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, +/* 96 */ 10570, 10576, 10582, 10589 +}; +#else +extern unsigned short semitone_tuning[24]; +extern unsigned short cent_tuning[100]; +#endif diff --git a/sys/i386/isa/sound/uart6850.c b/sys/i386/isa/sound/uart6850.c new file mode 100644 index 0000000..e978fc7 --- /dev/null +++ b/sys/i386/isa/sound/uart6850.c @@ -0,0 +1,296 @@ +/* + * sound/uart6850.c + * + * Copyright by Hannu Savolainen 1993 + * + * Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl: added 6850 support, used + * with COVOX SoundMaster II and custom cards. + * + * 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 <i386/isa/sound/sound_config.h> + +#if NSND > 0 + +#if 1 +/* #if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) */ + +#define DATAPORT (uart6850_base) /* * * Midi6850 Data I/O Port on IBM */ +#define COMDPORT (uart6850_base+1) /* * * Midi6850 Command Port on IBM */ +#define STATPORT (uart6850_base+1) /* * * Midi6850 Status Port on IBM */ + +#define uart6850_status() inb( STATPORT) +#define input_avail() (uart6850_status()&INPUT_AVAIL) +#define output_ready() (uart6850_status()&OUTPUT_READY) +#define uart6850_cmd(cmd) outb( COMDPORT, cmd) +#define uart6850_read() inb( DATAPORT) +#define uart6850_write(byte) outb( DATAPORT, byte) + +#define OUTPUT_READY 0x02 /* * * Mask for Data Read Ready Bit */ +#define INPUT_AVAIL 0x01 /* * * Mask for Data Send Ready Bit */ + +#define UART_RESET 0x95 /* * * 6850 Total Reset Command */ +#define UART_MODE_ON 0x03 /* * * 6850 Send/Receive UART Mode */ + +static int uart6850_opened = 0; +static int uart6850_base = 0x330; +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, u_char data); +static void poll_uart6850(void *dummy); + + +static sound_os_info *uart6850_osp; + +static void +uart6850_input_loop(void) +{ + int count; + + count = 10; + + while (count) /* Not timed out */ + if (input_avail()) { + u_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) +{ + if (input_avail()) + uart6850_input_loop(); +} + +/* + * It looks like there is no input interrupts in the UART mode. Let's try + * polling. + */ + +static void +poll_uart6850(void * dummy) +{ + u_long flags; + + if (!(uart6850_opened & OPEN_READ)) + return; /* Device has been closed */ + + flags = splhigh(); + + if (input_avail()) + uart6850_input_loop(); + + + timeout( poll_uart6850, 0, 1); /* Come back later */ + + splx(flags); +} + +static int +uart6850_open(int dev, int mode, + void (*input) (int dev, u_char data), + void (*output) (int dev) +) +{ + if (uart6850_opened) { + printf("Midi6850: Midi busy\n"); + return -(EBUSY); + } + uart6850_cmd(UART_RESET); + + uart6850_input_loop(); + + midi_input_intr = input; + uart6850_opened = mode; + poll_uart6850(0); /* Enable input polling */ + + return 0; +} + +static void +uart6850_close(int dev) +{ + uart6850_cmd(UART_MODE_ON); + + uart6850_opened = 0; +} + +static int +uart6850_out(int dev, u_char midi_byte) +{ + int timeout; + u_long flags; + + /* + * Test for input since pending input seems to block the output. + */ + + flags = splhigh(); + + if (input_avail()) + uart6850_input_loop(); + + splx(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()) { + printf("Midi6850: Timeout\n"); + return 0; + } + uart6850_write(midi_byte); + return 1; +} + +static int +uart6850_command(int dev, u_char *midi_byte) +{ + return 1; +} + +static int +uart6850_start_read(int dev) +{ + return 0; +} + +static int +uart6850_end_read(int dev) +{ + return 0; +} + +static int +uart6850_ioctl(int dev, u_int cmd, ioctl_arg arg) +{ + return -(EINVAL); +} + +static void +uart6850_kick(int dev) +{ +} + +static int +uart6850_buffer_status(int dev) +{ + return 0; /* No data in buffers */ +} + +#define MIDI_SYNTH_NAME "6850 UART Midi" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include <i386/isa/sound/midi_synth.h> + +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 +}; + + +void +attach_uart6850(struct address_info * hw_config) +{ + int ok, timeout; + u_long flags; + + if (num_midis >= MAX_MIDI_DEV) { + printf("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 ; + + flags = splhigh(); + + for (timeout = 30000; timeout < 0 && !output_ready(); timeout--); /* Wait */ + uart6850_cmd(UART_MODE_ON); + + ok = 1; + + splx(flags); + + conf_printf("6850 Midi Interface", hw_config); + + std_midi_synth.midi_dev = my_dev = num_midis; + midi_devs[num_midis++] = &uart6850_operations; + return ; +} + +static int +reset_uart6850(void) +{ + uart6850_read(); + return 1; /* OK */ +} + + +int +probe_uart6850(struct address_info * hw_config) +{ + int ok = 0; + + uart6850_osp = hw_config->osp; + uart6850_base = hw_config->io_base; + uart6850_irq = hw_config->irq; + + if (snd_set_irq_handler(uart6850_irq, m6850intr, uart6850_osp) < 0) + return 0; + + ok = reset_uart6850(); + + uart6850_detected = ok; + return ok; +} + +#endif +#endif diff --git a/sys/i386/isa/sound/ulaw.h b/sys/i386/isa/sound/ulaw.h new file mode 100644 index 0000000..6a25ab4 --- /dev/null +++ b/sys/i386/isa/sound/ulaw.h @@ -0,0 +1,71 @@ +static unsigned char ulaw_dsp[] = { + 3, 7, 11, 15, 19, 23, 27, 31, + 35, 39, 43, 47, 51, 55, 59, 63, + 66, 68, 70, 72, 74, 76, 78, 80, + 82, 84, 86, 88, 90, 92, 94, 96, + 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, + 113, 114, 114, 115, 115, 116, 116, 117, + 117, 118, 118, 119, 119, 120, 120, 121, + 121, 121, 122, 122, 122, 122, 123, 123, + 123, 123, 124, 124, 124, 124, 125, 125, + 125, 125, 125, 125, 126, 126, 126, 126, + 126, 126, 126, 126, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 253, 249, 245, 241, 237, 233, 229, 225, + 221, 217, 213, 209, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 174, 172, 170, 168, 166, 164, 162, 160, + 158, 157, 156, 155, 154, 153, 152, 151, + 150, 149, 148, 147, 146, 145, 144, 143, + 143, 142, 142, 141, 141, 140, 140, 139, + 139, 138, 138, 137, 137, 136, 136, 135, + 135, 135, 134, 134, 134, 134, 133, 133, + 133, 133, 132, 132, 132, 132, 131, 131, + 131, 131, 131, 131, 130, 130, 130, 130, + 130, 130, 130, 130, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, +}; + +#ifndef DSP_ULAW_NOT_WANTED +static unsigned char dsp_ulaw[] = { + 0, 0, 0, 0, 0, 1, 1, 1, + 1, 2, 2, 2, 2, 3, 3, 3, + 3, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 9, 9, 9, + 9, 10, 10, 10, 10, 11, 11, 11, + 11, 12, 12, 12, 12, 13, 13, 13, + 13, 14, 14, 14, 14, 15, 15, 15, + 15, 16, 16, 17, 17, 18, 18, 19, + 19, 20, 20, 21, 21, 22, 22, 23, + 23, 24, 24, 25, 25, 26, 26, 27, + 27, 28, 28, 29, 29, 30, 30, 31, + 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, + 47, 49, 51, 53, 55, 57, 59, 61, + 63, 66, 70, 74, 78, 84, 92, 104, + 254, 231, 219, 211, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 175, 174, 173, 172, 171, 170, 169, 168, + 167, 166, 165, 164, 163, 162, 161, 160, + 159, 159, 158, 158, 157, 157, 156, 156, + 155, 155, 154, 154, 153, 153, 152, 152, + 151, 151, 150, 150, 149, 149, 148, 148, + 147, 147, 146, 146, 145, 145, 144, 144, + 143, 143, 143, 143, 142, 142, 142, 142, + 141, 141, 141, 141, 140, 140, 140, 140, + 139, 139, 139, 139, 138, 138, 138, 138, + 137, 137, 137, 137, 136, 136, 136, 136, + 135, 135, 135, 135, 134, 134, 134, 134, + 133, 133, 133, 133, 132, 132, 132, 132, + 131, 131, 131, 131, 130, 130, 130, 130, + 129, 129, 129, 129, 128, 128, 128, 128, +}; +#endif /* !DSP_ULAW_NOT_WANTED */ diff --git a/sys/i386/isa/sound/ultrasound.h b/sys/i386/isa/sound/ultrasound.h new file mode 100644 index 0000000..5c63b03 --- /dev/null +++ b/sys/i386/isa/sound/ultrasound.h @@ -0,0 +1,137 @@ +#ifndef _ULTRASOUND_H_ +#define _ULTRASOUND_H_ +/* + * Copyright by Hannu Savolainen 1993 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE 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. + * + */ + +/* + * ultrasound.h - Macros for programming the Gravis Ultrasound + * These macros are extremely device dependent + * and not portable. + */ + +/* + * Private events for Gravis Ultrasound (GUS) + * + * Format: + * byte 0 - SEQ_PRIVATE (0xfe) + * byte 1 - Synthesizer device number (0-N) + * byte 2 - Command (see below) + * byte 3 - Voice number (0-31) + * bytes 4 and 5 - parameter P1 (u_short) + * bytes 6 and 7 - parameter P2 (u_short) + * + * Commands: + * Each command affects one voice defined in byte 3. + * Unused parameters (P1 and/or P2 *MUST* be initialized to zero). + * _GUS_NUMVOICES - Sets max. number of concurrent voices (P1=14-31, default 16) + * _GUS_VOICESAMPLE- ************ OBSOLETE ************* + * _GUS_VOICEON - Starts voice (P1=voice mode) + * _GUS_VOICEOFF - Stops voice (no parameters) + * _GUS_VOICEFADE - Stops the voice smoothly. + * _GUS_VOICEMODE - Alters the voice mode, don't start or stop voice (P1=voice mode) + * _GUS_VOICEBALA - Sets voice balence (P1, 0=left, 7=middle and 15=right, default 7) + * _GUS_VOICEFREQ - Sets voice (sample) playback frequency (P1=Hz) + * _GUS_VOICEVOL - Sets voice volume (P1=volume, 0xfff=max, 0xeff=half, 0x000=off) + * _GUS_VOICEVOL2 - Sets voice volume (P1=volume, 0xfff=max, 0xeff=half, 0x000=off) + * (Like GUS_VOICEVOL but doesn't change the hw + * volume. It just updates volume in the voice table). + * + * _GUS_RAMPRANGE - Sets limits for volume ramping (P1=low volume, P2=high volume) + * _GUS_RAMPRATE - Sets the speed for volume ramping (P1=scale, P2=rate) + * _GUS_RAMPMODE - Sets the volume ramping mode (P1=ramping mode) + * _GUS_RAMPON - Starts volume ramping (no parameters) + * _GUS_RAMPOFF - Stops volume ramping (no parameters) + * _GUS_VOLUME_SCALE - Changes the volume calculation constants + * for all voices. + */ + +#define _GUS_NUMVOICES 0x00 +#define _GUS_VOICESAMPLE 0x01 /* OBSOLETE */ +#define _GUS_VOICEON 0x02 +#define _GUS_VOICEOFF 0x03 +#define _GUS_VOICEMODE 0x04 +#define _GUS_VOICEBALA 0x05 +#define _GUS_VOICEFREQ 0x06 +#define _GUS_VOICEVOL 0x07 +#define _GUS_RAMPRANGE 0x08 +#define _GUS_RAMPRATE 0x09 +#define _GUS_RAMPMODE 0x0a +#define _GUS_RAMPON 0x0b +#define _GUS_RAMPOFF 0x0c +#define _GUS_VOICEFADE 0x0d +#define _GUS_VOLUME_SCALE 0x0e +#define _GUS_VOICEVOL2 0x0f +#define _GUS_VOICE_POS 0x10 + +/* + * GUS API macros + */ + +#define _GUS_CMD(chn, voice, cmd, p1, p2) {\ + _SEQ_NEEDBUF(8); _seqbuf[_seqbufptr] = SEQ_PRIVATE;\ + _seqbuf[_seqbufptr+1] = (chn); _seqbuf[_seqbufptr+2] = cmd;\ + _seqbuf[_seqbufptr+3] = voice;\ + *(u_short*)&_seqbuf[_seqbufptr+4] = p1;\ + *(u_short*)&_seqbuf[_seqbufptr+6] = p2;\ + _SEQ_ADVBUF(8);} + +#define GUS_NUMVOICES(chn, p1) _GUS_CMD(chn, 0, _GUS_NUMVOICES, (p1), 0) +#define GUS_VOICESAMPLE(chn, voice, p1) \ + _GUS_CMD(chn, voice, _GUS_VOICESAMPLE, (p1), 0) /* OBSOLETE */ +#define GUS_VOICEON(chn, voice, p1) \ + _GUS_CMD(chn, voice, _GUS_VOICEON, (p1), 0) +#define GUS_VOICEOFF(chn, voice) \ + _GUS_CMD(chn, voice, _GUS_VOICEOFF, 0, 0) +#define GUS_VOICEFADE(chn, voice) \ + _GUS_CMD(chn, voice, _GUS_VOICEFADE, 0, 0) +#define GUS_VOICEMODE(chn, voice, p1) \ + _GUS_CMD(chn, voice, _GUS_VOICEMODE, (p1), 0) +#define GUS_VOICEBALA(chn, voice, p1) \ + _GUS_CMD(chn, voice, _GUS_VOICEBALA, (p1), 0) +#define GUS_VOICEFREQ(chn, voice, p) \ + _GUS_CMD(chn, voice, _GUS_VOICEFREQ, \ + (p) & 0xffff, ((p) >> 16) & 0xffff) +#define GUS_VOICEVOL(chn, voice, p1) \ + _GUS_CMD(chn, voice, _GUS_VOICEVOL, (p1), 0) +#define GUS_VOICEVOL2(chn, voice, p1) \ + _GUS_CMD(chn, voice, _GUS_VOICEVOL2, (p1), 0) +#define GUS_RAMPRANGE(chn, voice, low, high) \ + _GUS_CMD(chn, voice, _GUS_RAMPRANGE, (low), (high)) +#define GUS_RAMPRATE(chn, voice, p1, p2) \ + _GUS_CMD(chn, voice, _GUS_RAMPRATE, (p1), (p2)) +#define GUS_RAMPMODE(chn, voice, p1) \ + _GUS_CMD(chn, voice, _GUS_RAMPMODE, (p1), 0) +#define GUS_RAMPON(chn, voice, p1) \ + _GUS_CMD(chn, voice, _GUS_RAMPON, (p1), 0) +#define GUS_RAMPOFF(chn, voice) \ + _GUS_CMD(chn, voice, _GUS_RAMPOFF, 0, 0) +#define GUS_VOLUME_SCALE(chn, voice, p1, p2) \ + _GUS_CMD(chn, voice, _GUS_VOLUME_SCALE, (p1), (p2)) +#define GUS_VOICE_POS(chn, voice, p) \ + _GUS_CMD(chn, voice, _GUS_VOICE_POS, \ + (p) & 0xffff, ((p) >> 16) & 0xffff) + +#endif |