Index: oldkernel/linux/Documentation/Configure.help diff -u linux/Documentation/Configure.help:1.6 linux/Documentation/Configure.help:1.7 --- linux/Documentation/Configure.help:1.6 Thu Jun 1 16:47:27 2000 +++ linux/Documentation/Configure.help Thu Jun 1 16:52:14 2000 @@ -10169,6 +10169,11 @@ of PCI sound chips. These include the Maestro 1, Maestro 2, and Maestro 2E. See Documentation/sound/Maestro for more details. +EMU10K1 derived boards +CONFIG_SOUND_EMU10k1 + Say Y or M if you have a Creative SBLive! sound card, as this + is the only card currently supported by this driver. + Are you using a crosscompiler CONFIG_CROSSCOMPILE Say Y here if you are compiling the kernel on a different Index: oldkernel/linux/Documentation/sound/EMU10K1 diff -u /dev/null linux/Documentation/sound/EMU10K1:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/Documentation/sound/EMU10K1 Thu Jun 1 16:52:15 2000 @@ -0,0 +1,75 @@ +Installation Instructions for Creative EMU10K1 drivers (v0.4) +English v1.0 +------------------------------------------------------------- + +Card support +------------ +- Creative Sound Blaster Live! + +Features +-------- +- Open Sound System (OSS) compatible +- Full duplex wave playback/recording functionality (/dev/dsp) +- Simultaneous wave playback streams +- Analog mixer (AC97) support (/dev/mixer) +- Sound status reporting (/dev/sndstat) +- MIDI UART support (/dev/midi) +- Joystick interface support (>= 2.2.x kernels only) +- Supports multiple EMU10K1-based cards + + +Requirements +------------ +- The kernel must be compiled: + - With loadable modules support (CONFIG_MODULES = y) + - With soundcard support as a module (CONFIG_SOUND = m) + - Without any integrated soundcard drivers (CONFIG_SOUND_* = n) +- "PnP-compatible OS installed" option in BIOS must be disabled +- Recommended system configuration: Min. 200 MHz Pentium-class w/ 32 MB RAM +- Kernel headers matching the kernel for which you are compiling the + driver. + + +Compilation +----------- +To compile the driver, simply type "make" in the main +directory. +Make sure the "KERNELVERSION", "SMPSUPP" and "MODVERSIONS" +options are correctly detected, if not edit the "Makefile" +and manually override them. +This will generate the file "emu10k1.o". + +Installation +------------ +1. As root type "make install". +2. Add a new reference to the driver in /etc/conf.modules: + (eg. "alias sound emu10k1" or "alias char-major-14 emu10k1") +3. Play some sound. The module should be auto-loaded. + +If this doesn't work: +1. Determine your kernel sound modules installation location + (usually /lib/modules/2.x.y/misc) +2. Copy the driver to that location as emu10k1.o + (eg. /lib/modules/2.x.y/misc/emu10k1.o) +3. Unload all existing soundcard drivers, including soundcore +4. Remove all old soundcard references from /etc/conf.modules +5. Add a new reference to the driver in /etc/conf.modules: + (eg. "alias sound emu10k1" or "alias char-major-14 emu10k1") +6. Generate module dependencies: "depmod -a" +7. Load the driver: "modprobe emu10k1" + +Note for Debians' users: use /etc/modutils directory, + create file emu10k1 here with the same content as is suggested in + the paragraph (5.) and run update-modules afterwards - this + will create the correct /etc/modules.conf file) + +Joystick support +---------------- +1. Load the driver with parameter "joystick=0x200" + eg. insmod emu10k1 joystick=0x200 +2. Load base joystick driver + eg. insmod joystick +3. Load specific joystick driver + eg. insmod joy-analog (If you are using an analog joystick) + +Read /usr/src/linux/Documentation/joystick.txt for more info. Index: oldkernel/linux/drivers/sound/Config.in diff -u linux/drivers/sound/Config.in:1.1.1.1 linux/drivers/sound/Config.in:1.2 --- linux/drivers/sound/Config.in:1.1.1.1 Wed May 31 12:33:52 2000 +++ linux/drivers/sound/Config.in Thu Jun 1 16:52:15 2000 @@ -30,6 +30,7 @@ fi dep_tristate 'ESS Maestro' CONFIG_SOUND_MAESTRO $CONFIG_SOUND +dep_tristate 'EMU10K1' CONFIG_SOUND_EMU10K1 $CONFIG_SOUND if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate 'ESS Solo1 (Experimental)' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND Index: oldkernel/linux/drivers/sound/Makefile diff -u linux/drivers/sound/Makefile:1.1.1.1 linux/drivers/sound/Makefile:1.2 --- linux/drivers/sound/Makefile:1.1.1.1 Wed May 31 12:33:52 2000 +++ linux/drivers/sound/Makefile Thu Jun 1 16:52:15 2000 @@ -10,13 +10,17 @@ SUB_DIRS := MOD_SUB_DIRS := MOD_IN_SUB_DIRS := -ALL_SUB_DIRS := $(SUB_DIRS) lowlevel +ALL_SUB_DIRS := $(SUB_DIRS) lowlevel emu10k1 ifeq ($(CONFIG_LOWLEVEL_SOUND),y) SUB_DIRS += lowlevel MOD_IN_SUB_DIRS += lowlevel endif +ifeq ($(CONFIG_SOUND_EMU10K1),m) + SUB_DIRS += emu10k1 + MOD_IN_SUB_DIRS += emu10k1 +endif # All of the (potential) objects that export symbols. @@ -160,6 +164,10 @@ ifeq ($(CONFIG_LOWLEVEL_SOUND),y) L_OBJS += lowlevel/lowlevel.o +endif + +ifeq ($(CONFIG_SOUND_EMU10K1),y) + M_OBJS += emu10k1/emu10k1.o endif include $(TOPDIR)/Rules.make Index: oldkernel/linux/drivers/sound/emu10k1/8010.h diff -u /dev/null linux/drivers/sound/emu10k1/8010.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/8010.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,602 @@ +/* + ********************************************************************** + * 8010.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox Cleaned of 8bit chars, DOS + * line endings + * December 8, 1999 Jon Taylor Added lots of new register info + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * + ********************************************************************** + */ + + +#ifndef _8010_H +#define _8010_H + +/* ------------------- DEFINES -------------------- */ + +#define EMUPAGESIZE 4096 +#define MAXREQVOICES 8 +#define MAXPAGES (16384 * 64 / PAGE_SIZE) /* WAVEOUT_MAXBUFSIZE * NUM_G / PAGE_SIZE */ +#define RESERVED 0 +#define NUM_MIDI 16 +#define NUM_G 64 /* use all channels */ +#define NUM_FXSENDS 4 + + +#define TMEMSIZE 256*1024 +#define TMEMSIZEREG 4 + +#define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL)) + +/************************************************************************************************/ +/* PCI function 0 registers, address = + PCIBASE0 */ +/************************************************************************************************/ + +#define PTR 0x00 /* Indexed register set pointer register */ +#define PTR_CHANNELNUM_MASK 0x0000003f /* For each per-channel register, indicates the */ +/* channel number of the specific register to be*/ +/* accessed. For non per-channel registers, the*/ +/* value should be set to zero. */ +#define PTR_ADDRESS_MASK 0x07ff0000 /* Register index */ + +#define DATA 0x04 /* Indexed register set data register */ + +#define IPR 0x08 /* Global interrupt pending register */ +#define IPR_SAMPLERATETRACKER 0x01000000 /* Sample rate tracker lock status change */ +#define IPR_FXDSP 0x00800000 /* Enable FX DSP interrupts */ +#define IPR_FORCEINT 0x00400000 /* Force Sound Blaster interrupt */ +#define IPR_PCIERROR 0x00200000 /* PCI bus error */ +#define IPR_VOLINCR 0x00100000 /* Volume increment button pressed */ +#define IPR_VOLDECR 0x00080000 /* Volume decrement button pressed */ +#define IPR_MUTE 0x00040000 /* Mute button pressed */ +#define IPR_MICBUFFULL 0x00020000 /* Microphone buffer full */ +#define IPR_MICBUFHALFFULL 0x00010000 /* Microphone buffer half full */ +#define IPR_ADCBUFFULL 0x00008000 /* ADC buffer full */ +#define IPR_ADCBUFHALFFULL 0x00004000 /* ADC buffer half full */ +#define IPR_EFXBUFFULL 0x00002000 /* Effects buffer full */ +#define IPR_EFXBUFHALFFULL 0x00001000 /* Effects buffer half full */ +#define IPR_GPSPDIFSTATUSCHANGE 0x00000800 /* GPSPDIF channel status change */ +#define IPR_CDROMSTATUSCHANGE 0x00000400 /* CD-ROM channel status change */ +#define IPR_INTERVALTIMER 0x00000200 /* Interval timer terminal count */ +#define IPR_MIDITRANSBUFEMPTY 0x00000100 /* MIDI UART transmit buffer empty */ +#define IPR_MIDIRECVBUFEMPTY 0x00000080 /* MIDI UART receive buffer empty */ +#define IPR_CHANNELLOOP 0x00000040 /* One or more channel loop interrupts pending */ +#define IPR_CHANNELNUMBERMASK 0x0000003f /* When IPR_CHANNELLOOP is set, indicates the */ +/* Highest set channel in [finish] */ + +#define INTE 0x0c /* Interrupt enable register */ +#define INTE_VIRTUALSB_MASK 0xc0000000 /* Virtual Soundblaster I/O port capture */ +#define INTE_VIRTUALSB_220 0x00000000 /* Capture at I/O base address 0x220-0x22f */ +#define INTE_VIRTUALSB_240 0x40000000 /* Capture at I/O base address 0x240 */ +#define INTE_VIRTUALSB_260 0x80000000 /* Capture at I/O base address 0x260 */ +#define INTE_VIRTUALSB_280 0xc0000000 /* Capture at I/O base address 0x280 */ +#define INTE_VIRTUALMPU_MASK 0x30000000 /* Virtual MPU I/O port capture */ +#define INTE_VIRTUALMPU_300 0x00000000 /* Capture at I/O base address 0x300-0x301 */ +#define INTE_VIRTUALMPU_310 0x10000000 /* Capture at I/O base address 0x310 */ +#define INTE_VIRTUALMPU_320 0x20000000 /* Capture at I/O base address 0x320 */ +#define INTE_VIRTUALMPU_330 0x30000000 /* Capture at I/O base address 0x330 */ +#define INTE_MASTERDMAENABLE 0x08000000 /* Master DMA emulation at 0x000-0x00f */ +#define INTE_SLAVEDMAENABLE 0x04000000 /* Slave DMA emulation at 0x0c0-0x0df */ +#define INTE_MASTERPICENABLE 0x02000000 /* Master PIC emulation at 0x020-0x021 */ +#define INTE_SLAVEPICENABLE 0x01000000 /* Slave PIC emulation at 0x0a0-0x0a1 */ +#define INTE_VSBENABLE 0x00800000 /* Enable virtual Soundblaster */ +#define INTE_ADLIBENABLE 0x00400000 /* Enable AdLib emulation at 0x388-0x38b */ +#define INTE_MPUENABLE 0x00200000 /* Enable virtual MPU */ +#define INTE_FORCEINT 0x00100000 /* Continuously assert INTAN */ + +#define INTE_MRHANDENABLE 0x00080000 /* Enable the "Mr. Hand" logic */ +// Not implemented or relevant under Linux. It will cause odd behaviour +// and may cause seg faults if implemented + +#define INTE_SAMPLERATETRACKER 0x00002000 /* Enable sample rate tracker interrupts MUST BE ENABLED */ +#define INTE_FXDSPENABLE 0x00001000 /* Enable FX DSP interrupts */ +#define INTE_PCIERRORENABLE 0x00000800 /* Enable PCI bus error interrupts */ +#define INTE_VOLINCRENABLE 0x00000400 /* Enable volume increment button interrupts */ +#define INTE_VOLDECRENABLE 0x00000200 /* Enable volume decrement button interrupts */ +#define INTE_MUTEENABLE 0x00000100 /* Enable mute button interrupts */ +#define INTE_MICBUFENABLE 0x00000080 /* Enable microphone buffer interrupts */ +#define INTE_ADCBUFENABLE 0x00000040 /* Enable ADC buffer interrupts */ +#define INTE_EFXBUFENABLE 0x00000020 /* Enable Effects buffer interrupts */ +#define INTE_GPSPDIFENABLE 0x00000010 /* Enable GPSPDIF status interrupts */ +#define INTE_CDSPDIFENABLE 0x00000008 /* Enable CDSPDIF status interrupts */ +#define INTE_INTERVALTIMERENB 0x00000004 /* Enable interval timer interrupts */ +#define INTE_MIDITXENABLE 0x00000002 /* Enable MIDI transmit-buffer-empty interrupts */ +#define INTE_MIDIRXENABLE 0x00000001 /* Enable MIDI receive-buffer-empty interrupts */ + +#define WC 0x10 /* Wall Clock register */ +#define WC_SAMPLECOUNTER_MASK 0x03FFFFC0 /* Sample periods elapsed since reset */ +#define WC_SAMPLECOUNTER 0x14060010 +#define WC_CURRENTCHANNEL 0x0000003F /* Channel [0..63] currently being serviced */ + +#define HCFG 0x14 /* Hardware config register */ +#define HCFG_LEGACYFUNC_MASK 0xe0000000 /* Legacy function number - DO NOT USE */ +#define HCFG_LEGACYFUNC_MPU 0x00000000 /* Legacy MPU - DO NOT USE under Linux */ +#define HCFG_LEGACYFUNC_SB 0x40000000 /* Legacy SB - DO NOT USE under Linux */ +#define HCFG_LEGACYFUNC_AD 0x60000000 /* Legacy AD - DO NOT USE under Linux */ +#define HCFG_LEGACYFUNC_MPIC 0x80000000 /* Legacy MPIC - DO NOT USE under Linux */ +#define HCFG_LEGACYFUNC_MDMA 0x90000000 /* Legacy MDMA - DO NOT USE under Linux */ +#define HCFG_LEGACYFUNC_SPCI 0xa0000000 /* Legacy SPCI - DO NOT USE under Linux */ +#define HCFG_LEGACYFUNC_SDMA 0xe0000000 /* Legacy SDMA - DO NOT USE under Linux */ +#define HCFG_IOCAPTUREADDR 0x1f000000 /* The 4 LSBs of the captured I/O address. DO NOT USE under Linux */ +#define HCFG_LEGACYWRITE 0x00800000 /* 1 = write, 0 = read - DO NOT USE under Linux */ +#define HCFG_LEGACYWORD 0x00400000 /* 1 = word, 0 = byte - DO NOT USE under Linux */ +#define HCFG_LEGACYINT 0x00200000 /* 1 = legacy event captured. Write 1 to clear. DO NOT USE under Linux */ +#define HCFG_CODECFORMAT_MASK 0x00070000 /* CODEC format */ +#define HCFG_CODECFORMAT_AC97 0x00000000 /* AC97 CODEC format -- Primary Output */ +#define HCFG_CODECFORMAT_I2S 0x00020000 /* I2S CODEC format -- Secondary (Rear) Output */ +#define HCFG_GPINPUT0 0x00004000 /* External pin112 */ +#define HCFG_GPINPUT1 0x00002000 /* External pin110 */ +#define HCFG_GPOUTPUTMASK 0x00001c00 /* External pins which may be controlled */ +#define HCFG_JOYENABLE 0x00000200 /* Joystick enable */ +#define HCFG_PHASETRACKENABLE 0x00000100 /* Phase tracking enable */ +#define HCFG_AC3ENABLE_MASK 0x0x0000e0 /* AC3 async input control - Not implemented */ +#define HCFG_AC3ENABLE_ZVIDEO 0x00000080 /* Channels 0 and 1 replace ZVIDEO */ +#define HCFG_AC3ENABLE_CDSPDIF 0x00000040 /* Channels 0 and 1 replace CDSPDIF */ +#define HCFG_AUTOMUTE 0x00000010 /* When set, the async sample rate convertors */ +/* will automatically mute their output when */ +/* they are not rate-locked to the external */ +/* audio source */ +#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache - Do not adjust this */ +#define HCFG_LOCKTANKCACHE 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache - Do not adjust this */ +#define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Enable master mute button */ +#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ + +#define MUDATA 0x18 /* MPU401 data register */ + +#define MUCMD 0x19 /* MPU401 command register */ +#define MUCMD_RESET 0xff /* RESET command */ +#define MUCMD_ENTERUARTMODE 0x3f /* Enter_UART_mode command */ + +#define MUSTAT MUCMD /* MPU401 status register */ +#define MUSTAT_IRDYN 0x80 /* 0 = MIDI data or command ACK */ +#define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ + +#define TIMR 0x1a /* Timer terminal count register */ +#define TIMR_RATE_MASK 0x000003ff /* Timer interrupt rate in sample periods */ +#define TIMR_RATE 0x0a00001a + +#define AC97DATA 0x1c /* AC97 indexed register set data register */ + +#define AC97ADDRESS 0x1e /* AC97 indexed register set address register */ +#define AC97ADDRESS_READY 0x80 /* Read-only bit, reflects CODEC READY signal */ +#define AC97ADDRESS_ADDRESS 0x7f /* Address of indexed AC97 register */ + +/************************************************************************************************/ +/* PCI function 1 registers, address = + PCIBASE1 */ +/************************************************************************************************/ + +#define JOYSTICK1 0x00 /* Analog joystick port register */ +#define JOYSTICK2 0x01 /* Analog joystick port register */ +#define JOYSTICK3 0x02 /* Analog joystick port register */ +#define JOYSTICK4 0x03 /* Analog joystick port register */ +#define JOYSTICK5 0x04 /* Analog joystick port register */ +#define JOYSTICK6 0x05 /* Analog joystick port register */ +#define JOYSTICK7 0x06 /* Analog joystick port register */ +#define JOYSTICK8 0x07 /* Analog joystick port register */ +#define JOYSTICK_BUTTONS 0x0f /* Joystick button data */ +#define JOYSTICK_COMPARATOR 0xf0 /* Joystick comparator data */ + +/********************************************************************************************************/ +/* AC97 pointer-offset register set, accessed through the AC97ADDRESS and AC97DATA registers */ +/********************************************************************************************************/ + +#define AC97_RESET 0x00 +#define AC97_MASTERVOLUME 0x02 /* Master volume */ +#define AC97_HEADPHONEVOLUME 0x04 /* Headphone volume */ +#define AC97_MASTERVOLUMEMONO 0x06 /* Mast volume mono */ +#define AC97_MASTERTONE 0x08 +#define AC97_PCBEEPVOLUME 0x0a /* PC speaker system beep volume */ +#define AC97_PHONEVOLUME 0x0c +#define AC97_MICVOLUME 0x0e +#define AC97_LINEINVOLUME 0x10 +#define AC97_CDVOLUME 0x12 +#define AC97_VIDEOVOLUME 0x14 +#define AC97_AUXVOLUME 0x16 +#define AC97_PCMOUTVOLUME 0x18 +#define AC97_RECORDSELECT 0x1a +#define AC97_RECORDGAIN 0x1c +#define AC97_RECORDGAINMIC 0x1e +#define AC97_GENERALPUPOSE 0x20 +#define AC97_3DCONTROL 0x22 +#define AC97_MODEMRATE 0x24 +#define AC97_POWERDOWN 0x26 +#define AC97_RESERVED4 0x28 +#define AC97_VENDORRESERVED1 0x5a +#define AC97_VENDORRESERVED2 0x7a +#define AC97_VENDORID1 0x7c +#define AC97_VENDORID2 0x7e +#define AC97_ZVIDEOVOLUME 0xec +#define AC97_AC3VOLUME 0xed + +/********************************************************************************************************/ +/* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers */ +/********************************************************************************************************/ + +#define CPF 0x00 /* Current pitch and fraction register */ +#define CPF_CURRENTPITCH_MASK 0xffff0000 /* Current pitch (0x4000 == unity pitch shift) */ +#define CPF_CURRENTPITCH 0x10100000 +#define CPF_STEREO_MASK 0x00008000 /* 1 = Even channel interleave, odd channel locked */ +#define CPF_STOP_MASK 0x00004000 /* 1 = Current pitch forced to 0 */ +#define CPF_FRACADDRESS_MASK 0x00003fff /* Linear fractional address of the current channel */ + +#define PTRX 0x01 /* Pitch target and send A/B amounts register */ +#define PTRX_PITCHTARGET_MASK 0xffff0000 /* Pitch target of specified channel */ +#define PTRX_PITCHTARGET 0x10100001 +#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00 /* Linear level of channel output sent to FX send bus A */ +#define PTRX_FXSENDAMOUNT_A 0x08080001 +#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff /* Linear level of channel output sent to FX send bus B */ +#define PTRX_FXSENDAMOUNT_B 0x08000001 + +#define CVCF 0x02 /* Current volume and filter cutoff register */ +#define CVCF_CURRENTVOL_MASK 0xffff0000 /* Current linear volume of specified channel */ +#define CVCF_CURRENTVOL 0x10100002 +#define CVCF_CURRENTFILTER_MASK 0x0000ffff /* Current filter cutoff frequency of specified channel */ +#define CVCF_CURRENTFILTER 0x10000002 + +#define VTFT 0x03 /* Volume target and filter cutoff target register */ +#define VTFT_VOLUMETARGET_MASK 0xffff0000 /* Volume target of specified channel */ +#define VTFT_FILTERTARGET_MASK 0x0000ffff /* Filter cutoff target of specified channel */ + +#define Z1 0x05 /* Filter delay memory 1 register */ + +#define Z2 0x04 /* Filter delay memory 2 register */ + +#define PSST 0x06 /* Send C amount and loop start address register */ +#define PSST_FXSENDAMOUNT_C_MASK 0xff000000 /* Linear level of channel output sent to FX send bus C */ + +#define PSST_FXSENDAMOUNT_C 0x08180006 + +#define PSST_LOOPSTARTADDR_MASK 0x00ffffff /* Loop start address of the specified channel */ +#define PSST_LOOPSTARTADDR 0x18000006 + +#define DSL 0x07 /* Send D amount and loop start address register */ +#define DSL_FXSENDAMOUNT_D_MASK 0xff000000 /* Linear level of channel output sent to FX send bus D */ + +#define DSL_FXSENDAMOUNT_D 0x08180007 + +#define DSL_LOOPENDADDR_MASK 0x00ffffff /* Loop end address of the specified channel */ +#define DSL_LOOPENDADDR 0x18000007 + +#define CCCA 0x08 /* Filter Q, interp. ROM, byte size, cur. addr register */ +#define CCCA_RESONANCE 0xf0000000 /* Lowpass filter resonance height */ +#define CCCA_INTERPROMMASK 0x0e000000 /* Selects passband of interpolation ROM */ +#define CCCA_INTERPROM_0 0x00000000 /* Select interpolation ROM 0 */ +#define CCCA_INTERPROM_1 0x02000000 /* Select interpolation ROM 1 */ +#define CCCA_INTERPROM_2 0x04000000 /* Select interpolation ROM 2 */ +#define CCCA_INTERPROM_3 0x06000000 /* Select interpolation ROM 3 */ +#define CCCA_INTERPROM_4 0x08000000 /* Select interpolation ROM 4 */ +#define CCCA_INTERPROM_5 0x0a000000 /* Select interpolation ROM 5 */ +#define CCCA_INTERPROM_6 0x0c000000 /* Select interpolation ROM 6 */ +#define CCCA_INTERPROM_7 0x0e000000 /* Select interpolation ROM 7 */ +#define CCCA_8BITSELECT 0x01000000 /* 1 = Sound memory for this channel uses 8-bit samples */ +#define CCCA_CURRADDR_MASK 0x00ffffff /* Current address of the selected channel */ +#define CCCA_CURRADDR 0x18000008 + +#define CCR 0x09 /* Cache control register */ +#define CCR_CACHEINVALIDSIZE 0xfe000000 /* Number of invalid samples in the cache */ +#define CCR_CACHELOOPFLAG 0x01000000 /* 1 = Cache has a loop service pending */ +#define CCR_INTERLEAVEDSAMPLES 0x00800000 /* 1 = A cache service will fetch interleaved samples */ +#define CCR_WORDSIZEDSAMPLES 0x00400000 /* 1 = A cache service will fetch word sized samples */ +#define CCR_READADDRESS_MASK 0x003f0000 /* Location of cache just beyond current cache service */ +#define CCR_LOOPINVALSIZE 0x0000fe00 /* Number of invalid samples in cache prior to loop */ +#define CCR_LOOPFLAG 0x00000100 /* Set for a single sample period when a loop occurs */ +#define CCR_CACHELOOPADDRHI 0x000000ff /* DSL_LOOPSTARTADDR's hi byte if CCR_CACHELOOPFLAG = 1 */ + +/* NOTE: This register is normally not used */ +#define CLP 0x0a /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */ +#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address (copy of DSL_LOOPSTARTADDR) */ + +#define FXRT 0x0b /* Effects send routing register */ +#define FXRT_CHANNELA 0x000f0000 /* Effects send bus number for channel's effects send A */ +#define FXRT_CHANNELB 0x00f00000 /* Effects send bus number for channel's effects send B */ +#define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */ +#define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */ + +#define MAPA 0x0c /* Cache map A */ +#define MAP_PTE_MASK 0xffffe000 /* The 19 MSBs of the PTE indexed by PTI */ +#define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the PTE dwords */ + +#define MAPB 0x0d /* Cache map B */ + +#define ENVVOL 0x10 /* Volume envelope register */ +#define ENVVOL_MASK 0x0000ffff /* 16-bit value */ + +#define ATKHLDV 0x11 /* Volume envelope hold and attack register */ +#define ATKHLDV_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDV_HOLDTIME_MASK 0x00007f00 /* Envelope hold time (127-n == n*88.2msec) */ +#define ATKHLDV_ATTACKTIME_MASK 0x0000007f /* Envelope attack time, log encoded */ +/* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec */ + +#define DCYSUSV 0x12 /* Volume envelope sustain and decay register */ +#define DCYSUSV_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSV_CHANNELENABLE_MASK 0x00000080 /* 1 = Inhibit envelope engine from writing to registers*/ +#define DCYSUSV_DECAYTIME_MASK 0x0000007f /* Envelope decay time, log encoded */ +/* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL1 0x13 /* Modulation LFO value */ +#define LFOVAL_MASK 0x0000ffff /* 16-bit value */ + +#define ENVVAL 0x14 /* Modulation envelope register */ +#define ENVVAL_MASK 0x0000ffff /* 16-bit value */ + +#define ATKHLDM 0x15 /* Modulation envelope hold and attack register */ +#define ATKHLDM_PHASE0 0x00008000 /* 0 = Begin attack phase */ +#define ATKHLDM_HOLDTIME 0x00007f00 /* Envelope hold time (127-n == n*42msec) */ +#define ATKHLDM_ATTACKTIME 0x0000007f /* Envelope attack time, log encoded */ +/* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec */ + +#define DCYSUSM 0x16 /* Modulation envelope decay and sustain register */ +#define DCYSUSM_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */ +#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */ +#define DCYSUSM_DECAYTIME_MASK 0x0000007f /* Envelope decay time, log encoded */ +/* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */ + +#define LFOVAL2 0x17 /* Vibrato LFO register */ +#define LFOVAL2_MASK 0x0000ffff /* 16-bit value */ + +#define IP 0x18 /* Initial pitch register */ +#define IP_MASK 0x0000ffff /* Exponential initial pitch shift (16-bit value) */ +/* 4 bits of octave, 12 bits of fractional octave */ +#define IP_UNITY 0x0000e000 /* Unity pitch shift */ + +#define IFATN 0x19 /* Initial filter cutoff and attenuation register */ +#define IFATN_FILTERCUTOFF_MASK 0x0000ff00 /* Initial filter cutoff frequency in exponential units */ +/* 6 most significant bits are semitones */ +/* 2 least significant bits are fractions */ +#define IFATN_FILTERCUTOFF 0x08080019 +#define IFATN_ATTENUATION_MASK 0x000000ff /* Initial attenuation in 0.375dB steps */ +#define IFATN_ATTENUATION 0x08000019 + + +#define PEFE 0x1a /* Pitch envelope and filter envelope amount register */ +#define PEFE_PITCHAMOUNT_MASK 0x0000ff00 /* Pitch envlope amount */ +/* Signed 2's complement, +/- one octave peak extremes */ +#define PEFE_PITCHAMOUNT 0x0808001a +#define PEFE_FILTERAMOUNT_MASK 0x000000ff /* Filter envlope amount */ +/* Signed 2's complement, +/- six octaves peak extremes */ +#define PEFE_FILTERAMOUNT 0x0800001a +#define FMMOD 0x1b /* Vibrato/filter modulation from LFO register */ +#define FMMOD_MODVIBRATO 0x0000ff00 /* Vibrato LFO modulation depth */ +/* Signed 2's complement, +/- one octave extremes */ +#define FMMOD_MOFILTER 0x000000ff /* Filter LFO modulation depth */ +/* Signed 2's complement, +/- three octave extremes */ + + +#define TREMFRQ 0x1c /* Tremolo amount and modulation LFO frequency register */ +#define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */ +/* Signed 2's complement, with +/- 12dB extremes */ + +#define FM2FRQ2 0x1d /* Vibrato amount and vibrato LFO frequency register */ +#define FM2FRQ2_DEPTH 0x0000ff00 /* Vibrato LFO vibrato depth */ +/* Signed 2's complement, +/- one octave extremes */ +#define FM2FRQ2_FREQUENCY 0x000000ff /* Vibrato LFO frequency */ +/* 0.039Hz steps, maximum of 9.85 Hz. */ + +#define TEMPENV 0x1e /* Tempory envelope register */ +#define TEMPENV_MASK 0x0000ffff /* 16-bit value */ + +#define CD0 0x20 /* Reserved Bit - Do not program */ +#define CD1 0x21 /* Reserved Bit - Do not program */ +#define CD2 0x22 /* Reserved Bit - Do not program */ +#define CD3 0x23 /* Reserved Bit - Do not program */ +#define CD4 0x24 /* Reserved Bit - Do not program */ +#define CD5 0x25 /* Reserved Bit - Do not program */ +#define CD6 0x26 /* Reserved Bit - Do not program */ +#define CD7 0x27 /* Reserved Bit - Do not program */ +#define CD8 0x28 /* Reserved Bit - Do not program */ +#define CD9 0x29 /* Reserved Bit - Do not program */ +#define CDA 0x2a /* Reserved Bit - Do not program */ +#define CDB 0x2b /* Reserved Bit - Do not program */ +#define CDC 0x2c /* Reserved Bit - Do not program */ +#define CDD 0x2d /* Reserved Bit - Do not program */ +#define CDE 0x2e /* Reserved Bit - Do not program */ +#define CDF 0x2f /* Reserved Bit - Do not program */ + +#define PTB 0x40 /* Page table base register */ +#define PTB_MASK 0xfffff000 /* Physical address of the page table in host memory */ + +#define TCB 0x41 /* Tank cache base register */ +#define TCB_MASK 0xfffff000 /* Physical address of the bottom of host based tank memory */ + +#define ADCCR 0x42 /* ADC sample rate/stereo control register */ +#define ADCCR_RCHANENABLE 0x00000010 /* Right channel enable */ +#define ADCCR_LCHANENABLE 0x00000008 /* Left channel enable */ +#define ADCCR_SAMPLERATE_MASK 0x00000007 /* Sample rate convertor output rate */ +#define ADCCR_SAMPLERATE_48 0x00000000 /* 48kHz sample rate */ +#define ADCCR_SAMPLERATE_44 0x00000001 /* 44.1kHz sample rate */ +#define ADCCR_SAMPLERATE_32 0x00000002 /* 32kHz sample rate */ +#define ADCCR_SAMPLERATE_24 0x00000003 /* 24kHz sample rate */ +#define ADCCR_SAMPLERATE_22 0x00000004 /* 22.05kHz sample rate */ +#define ADCCR_SAMPLERATE_16 0x00000005 /* 16kHz sample rate */ +#define ADCCR_SAMPLERATE_11 0x00000006 /* 11.025kHz sample rate */ +#define ADCCR_SAMPLERATE_8 0x00000007 /* 8kHz sample rate */ + +#define FXWC 0x43 /* FX output write channels register */ + +#define TCBS 0x44 /* Tank cache buffer size register */ +#define TCBS_MASK 0x00000007 /* Tank cache buffer size field */ +#define TCBS_BUFFSIZE_16K 0x00000000 +#define TCBS_BUFFSIZE_32K 0x00000001 +#define TCBS_BUFFSIZE_64K 0x00000002 +#define TCBS_BUFFSIZE_128K 0x00000003 +#define TCBS_BUFFSIZE_256K 0x00000004 +#define TCBS_BUFFSIZE_512K 0x00000005 +#define TCBS_BUFFSIZE_1024K 0x00000006 +#define TCBS_BUFFSIZE_2048K 0x00000007 + +#define MICBA 0x45 /* AC97 microphone buffer address register */ +#define MICBA_MASK 0xfffff000 /* 20 bit base address */ + +#define ADCBA 0x46 /* ADC buffer address register */ +#define ADCBA_MASK 0xfffff000 /* 20 bit base address */ + +#define FXBA 0x47 /* FX Buffer Address */ + +#define MICBS 0x49 /* Microphone buffer size register */ + +#define ADCBS 0x4a /* ADC buffer size register */ + +/* The following mask values define the size of the ADC buffers in bytes */ +#define ADCBS_BUFSIZE_NONE 0x00000000 +#define ADCBS_BUFSIZE_384 0x00000001 +#define ADCBS_BUFSIZE_448 0x00000002 +#define ADCBS_BUFSIZE_512 0x00000003 +#define ADCBS_BUFSIZE_640 0x00000004 +#define ADCBS_BUFSIZE_768 0x00000005 +#define ADCBS_BUFSIZE_896 0x00000006 +#define ADCBS_BUFSIZE_1024 0x00000007 +#define ADCBS_BUFSIZE_1280 0x00000008 +#define ADCBS_BUFSIZE_1536 0x00000009 +#define ADCBS_BUFSIZE_1792 0x0000000a +#define ADCBS_BUFSIZE_2048 0x0000000b +#define ADCBS_BUFSIZE_2560 0x0000000c +#define ADCBS_BUFSIZE_3072 0x0000000d +#define ADCBS_BUFSIZE_3584 0x0000000e +#define ADCBS_BUFSIZE_4096 0x0000000f +#define ADCBS_BUFSIZE_5120 0x00000010 +#define ADCBS_BUFSIZE_6144 0x00000011 +#define ADCBS_BUFSIZE_7168 0x00000012 +#define ADCBS_BUFSIZE_8192 0x00000013 +#define ADCBS_BUFSIZE_10240 0x00000014 +#define ADCBS_BUFSIZE_12288 0x00000015 +#define ADCBS_BUFSIZE_14366 0x00000016 +#define ADCBS_BUFSIZE_16384 0x00000017 +#define ADCBS_BUFSIZE_20480 0x00000018 +#define ADCBS_BUFSIZE_24576 0x00000019 +#define ADCBS_BUFSIZE_28672 0x0000001a +#define ADCBS_BUFSIZE_32768 0x0000001b +#define ADCBS_BUFSIZE_40960 0x0000001c +#define ADCBS_BUFSIZE_49152 0x0000001d +#define ADCBS_BUFSIZE_57344 0x0000001e +#define ADCBS_BUFSIZE_65536 0x0000001f + +#define FXBS 0x4b /* FX buffer size register */ + +#define CDCS 0x50 /* CD-ROM digital channel status register */ + +#define GPSCS 0x51 /* General Purpose SPDIF channel status register*/ + +#define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ + +#define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ + +#define SPCS0 0x54 /* SPDIF output Channel Status 0 register */ + +#define SPCS1 0x55 /* SPDIF output Channel Status 1 register */ + +#define SPCS2 0x56 /* SPDIF output Channel Status 2 register */ + +#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */ +#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */ +#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */ +#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */ +#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */ +#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */ +#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */ +#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */ +#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */ +#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */ +#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */ +#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */ +#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */ +#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */ +#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */ +#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */ +#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */ +#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */ +#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */ +#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */ +#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */ +#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */ +#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */ + +/* The 32-bit CLIx registers all have one bit per channel control/status */ +#define CLIEL 0x58 /* Channel loop interrupt enable low register */ + +#define CLIEH 0x59 /* Channel loop interrupt enable high register */ + +#define CLIPL 0x5a /* Channel loop interrupt pending low register */ + +#define CLIPH 0x5b /* Channel loop interrupt pending high register */ + +#define SOLEL 0x5c /* Stop on loop enable low register */ + +#define SOLEH 0x5d /* Stop on loop enable high register */ + +#define SPBYPASS 0x5e /* SPDIF BYPASS mode register */ + +#define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */ + +#define GPSRCS 0x61 /* General Purpose SPDIF sample rate cvt status */ + +#define ZVSRCS 0x62 /* ZVideo sample rate converter status */ +/* NOTE: This one has no SPDIFLOCKED field */ +/* Assumes sample lock */ + +/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS. */ +#define SRCS_SPDIFLOCKED 0x02000000 /* SPDIF stream locked */ +#define SRCS_RATELOCKED 0x01000000 /* Sample rate locked */ +#define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */ + +#define ADCIDX 0x63 /* ADC recording buffer index register */ +#define ADCIDX_MASK 0x0000ffff /* 16 bit index field */ + +#define MICIDX 0x64 /* Microphone recording buffer index register */ +#define MICIDX_MASK 0x0000ffff /* 16-bit value */ + +#define FXIDX 0x65 /* FX recording buffer index register */ + +/* Each FX general purpose register is 32 bits in length, all bits are used */ +#define FXGPREGBASE 0x100 /* FX general purpose registers base */ + +/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */ +/* decompressed back to 20 bits on a read. There are a total of 160 locations, the last 32 */ +/* locations are for external TRAM. */ +#define TANKMEMDATAREGBASE 0x200 /* Tank memory data registers base */ +#define TANKMEMDATAREG_MASK 0x000fffff /* 20 bit tank audio data field */ + +/* Combined address field and memory opcode or flag field. 160 locations, last 32 are external */ +#define TANKMEMADDRREGBASE 0x300 /* Tank memory address registers base */ +#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ +#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ +#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ +#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ +#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ + +#define MICROCODEBASE 0x400 /* Microcode data base address */ + +/* Each DSP microcode instruction is mapped into 2 doublewords */ +/* NOTE: When writing, always write the HI doubleword first. Reads can be in either order. */ +#define LOWORD_OPX_MASK 0x000ffc00 /* Instruction operand X */ +#define LOWORD_OPY_MASK 0x000003ff /* Instruction operand Y */ +#define HIWORD_OPCODE_MASK 0x00f00000 /* Instruction opcode */ +#define HIWORD_RESULT_MASK 0x000ffc00 /* Instruction result */ +#define HIWORD_OPA_MASK 0x000003ff /* Instruction operand A */ + +#endif /* _8010_H */ Index: oldkernel/linux/drivers/sound/emu10k1/Makefile diff -u /dev/null linux/drivers/sound/emu10k1/Makefile:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/Makefile Thu Jun 1 16:52:15 2000 @@ -0,0 +1,14 @@ + +#MOD_LIST_NAME := SOUND_MODULES + +EMU = audio.o cardmi.o cardmo.o cardwi.o cardwo.o efxmgr.o emuadxmg.o hwaccess.o irqmgr.o main.o midi.o mixer.o osutils.o recmgr.o sndstat.o timer.o voicemgr.o + +M_OBJS := emu10k1.o + +$(EMU): %.o: %.c + $(CC) -c $(CFLAGS) -I. $< -o $@ + +emu10k1.o: $(EMU) + $(LD) -r $(EMU) -o $@ + +include $(TOPDIR)/Rules.make Index: oldkernel/linux/drivers/sound/emu10k1/audio.c diff -u /dev/null linux/drivers/sound/emu10k1/audio.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/audio.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,1801 @@ +/* + ********************************************************************** + * audio.c -- /dev/dsp interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up types/leaks + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +/* + * FIXME: + * There are some 32bit assumptions here + * Cast of pointers to 32bit values is unsafe + * - Wave callbacks and elsewhere + */ + +#include "hwaccess.h" +#include "mycommon.h" +#include "mmwave.h" +#include "cardwo.h" +#include "cardwi.h" +#include "audio.h" + +static void calculate_ofrag(struct woinst *woinst); +static void calculate_ifrag(struct wiinst *wiinst); + +/* Audio file operations */ +static loff_t emu10k1_audio_llseek(struct file *file, loff_t offset, int nOrigin) +{ + DPF(2, "emu10k1_audio_lseek() called\n"); + return -ESPIPE; +} + +static ssize_t emu10k1_audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos) +{ + struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data; + struct wiinst *wiinst = wave_dev->wiinst; + ssize_t ret = 0; + u32 flags; + + spin_lock_irqsave(&wiinst->lock, flags); + + DPD(4, "emu10k1_audio_read() called, buffer=%x, count=%d\n", (unsigned int) buffer, count); + + if (wiinst->mapped) + { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -ENXIO; + } + + if (file->f_mode & FMODE_READ) + { + struct wave_in *wave_in = wiinst->wave_in; + + if (!wave_in) + { + calculate_ifrag(wiinst); + + if (sblive_waveinOpen(wave_dev->sb_hw, &wiinst->wave_fmt, + &wiinst->fragment_size, + wiinst->numfrags, &wiinst->wave_in) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPF(2, "sblive_waveinOpen failed!\n"); + return -EINVAL; + } + + DPD(2, "fragment_size size is -> %x\n", wiinst->fragment_size); + + wave_in = wiinst->wave_in; + } + + if (!access_ok(VERIFY_WRITE, buffer, count)) + { + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPF(2, "Cannot write to buffer!\n"); + + return -EFAULT; + } + + while (count > 0) + { + u32 bytestocopy, pending; + + if (wave_dev->enablebits & PCM_ENABLE_INPUT && wave_in->state != CARDWAVE_STATE_STARTED) + { + if (sblive_waveinStart(wave_dev) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPF(2, "sblive_waveinStart() failed.\n"); + return -EFAULT; + } + + DPF(2, "sblive_waveinStart() succeeded.\n"); + } + + if (sblive_waveinGetXferSize(wave_dev->sb_hw->card_wavein, wave_in, &bytestocopy, &pending) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPF(2, "sblive_waveinGetXferSize failed\n"); + return -EFAULT; + } + + DPD(4, "bytestocopy --> %x\n", bytestocopy); + + if (bytestocopy) { + + bytestocopy = min(bytestocopy, count); + + if (sblive_waveinXferData(wave_dev->sb_hw->card_wavein, wave_in, (u8 *)buffer, &bytestocopy) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&wiinst->lock, flags); + DPF(2, "sblive_waveinXferData failed.\n"); + return ret ? ret : -EFAULT; + } + + count -= bytestocopy; + buffer += bytestocopy; + ret += bytestocopy; + } + + if (count > 0) + { + if ((file->f_flags & O_NONBLOCK) + || (!(wave_dev->enablebits & PCM_ENABLE_INPUT))) + { + spin_unlock_irqrestore(&wiinst->lock, flags); + return (ret ? ret : -EAGAIN); + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + + interruptible_sleep_on(&wiinst->wait_queue); + + if (signal_pending(current)) + return (ret ? ret : -ERESTARTSYS); + + spin_lock_irqsave(&wiinst->lock, flags); + } + } + DPD(4, "bytes copied -> %x\n", ret); + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + + return ret; +} + +ssize_t emu10k1_audio_write(struct file *file, const char *buffer, size_t count, loff_t * ppos) +{ + struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data; + struct woinst *woinst = wave_dev->woinst; + struct wave_out *wave_out = woinst->wave_out; + ssize_t ret; + u32 flags; + GET_INODE_STRUCT(); + + spin_lock_irqsave(&woinst->lock, flags); + + DPD(4, "emu10k1_audio_write() called, buffer=%#x, count=%d\n", (unsigned int) buffer, count); + + if (woinst->mapped) + { + spin_unlock_irqrestore(&woinst->lock, flags); + return -ENXIO; + } + + + if (wave_out == NULL) + { + + calculate_ofrag(woinst); + + while (sblive_waveoutOpen(wave_dev->sb_hw, &woinst->wave_fmt, + &woinst->fragment_size, + woinst->numfrags, &woinst->wave_out) != CTSTATUS_SUCCESS) + { + if (file->f_flags & O_NONBLOCK) + { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EAGAIN; + } + + UP_INODE_SEM(&inode->i_sem); + spin_unlock_irqrestore(&woinst->lock, flags); + + interruptible_sleep_on(&wave_dev->sb_hw->open_wait); + DOWN_INODE_SEM(&inode->i_sem); + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&woinst->lock, flags); + } + wave_out = woinst->wave_out; + } + + if (!access_ok(VERIFY_READ, buffer, count)) + { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EFAULT; + } + + ret = 0; + while (count > 0) + { + u32 bytestocopy, pending; + int status; + + status = sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout, + wave_out, &bytestocopy, &pending); + + if (status != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + DPF(2, "sblive_waveoutGetXferSize failed\n"); + return -EFAULT; + } + + if (woinst->silence_filled > 0) + { + if (woinst->silence_filled == 1) + { + if (pending <= woinst->fragment_size) + woinst->silence_filled = 2; + else + bytestocopy += woinst->fragment_size; + } + + if (woinst->silence_filled == 2) + bytestocopy = woinst->fragment_size * woinst->numfrags; + } + + DPD(4, "bytestocopy --> %x\n", bytestocopy); + + if (bytestocopy) { + + bytestocopy = min(bytestocopy, count); + + if (woinst->silence_filled > 0) + { + u32 tmp; + + if (woinst->silence_filled == 1) + tmp = woinst->silence_start; + else + { + sblive_waveoutGetControl(wave_dev->sb_hw->card_waveout, + wave_out, WAVECURPOS, &tmp); + pending = 0; + } + + if (pending + bytestocopy < woinst->fragment_size) + { + woinst->silence_filled = 1; + woinst->silence_start = tmp + bytestocopy; + + if (woinst->silence_start > woinst->fragment_size * woinst->numfrags) + woinst->silence_start -= woinst->fragment_size * woinst->numfrags; + } + else + woinst->silence_filled = 0; + + sblive_waveoutSetControl(wave_dev->sb_hw, wave_out, + WAVEWRITEPOINTER, &tmp); + } + + status = sblive_waveoutXferData(wave_dev->sb_hw->card_waveout, + wave_out, (u8 *)buffer, &bytestocopy); + + if (woinst->silence_filled) + { + u32 tmp = woinst->fragment_size; + sblive_waveoutFillSilence(wave_dev->sb_hw->card_waveout, wave_out, &tmp); + } + + if (status != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + DPF(2, "sblive_waveoutXferData failed.\n"); + return (ret ? ret : -EFAULT); + } + + count -= bytestocopy; + buffer += bytestocopy; + ret += bytestocopy; + woinst->total_copied += bytestocopy; + + if ((wave_dev->enablebits & PCM_ENABLE_OUTPUT) + && (wave_out->state != CARDWAVE_STATE_STARTED) + && (woinst->total_copied >= woinst->fragment_size)) + { + if (sblive_waveoutStart(wave_dev) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + DPF(2, "sblive_waveoutStart failed.\n"); + return -EFAULT; + } + else + DPF(2, "sblive_waveoutStart\n"); + } + } + + if (count > 0) + { + if ((file->f_flags & O_NONBLOCK) + || (!(wave_dev->enablebits & PCM_ENABLE_OUTPUT))) + { + spin_unlock_irqrestore(&woinst->lock, flags); + return (ret ? ret : -EAGAIN); + } + + UP_INODE_SEM(&inode->i_sem); + spin_unlock_irqrestore(&woinst->lock, flags); + + interruptible_sleep_on(&woinst->wait_queue); + DOWN_INODE_SEM(&inode->i_sem); + if (signal_pending(current)) + return (ret ? ret : -ERESTARTSYS); + + spin_lock_irqsave(&woinst->lock, flags); + } + + } + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(4, "bytes copied -> %x\n", ret); + + return ret; +} + +static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data; + int val = 0; + struct woinst *woinst = NULL; + struct wave_out *wave_out = NULL; + struct wiinst *wiinst = NULL; + struct wave_in *wave_in = NULL; + u32 flags, pending, bytestocopy, dummy; + + DPF(4, "emu10k1_audio_ioctl() called:\n"); + + if (file->f_mode & FMODE_WRITE){ + woinst = wave_dev->woinst; + wave_out = woinst->wave_out; + } + + if (file->f_mode & FMODE_READ){ + wiinst = wave_dev->wiinst; + wave_in = wiinst->wave_in; + } + + switch (cmd) + { + case OSS_GETVERSION: + DPF(2, "OSS_GETVERSION:\n"); + return put_user(SOUND_VERSION, (int *) arg); + + case SNDCTL_DSP_RESET: + DPF(2, "SNDCTL_DSP_RESET:\n"); + wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT; + + if (file->f_mode & FMODE_WRITE) + { + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) + { + sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy); + wave_out->wavexferbuf->xferpos = 0; + wave_out->wavexferbuf->stopposition = 0; + } + + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->silence_filled = 0; + woinst->silence_start = 0; + woinst->getoptr_blocks = 0; + woinst->wave_ptr = 0; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) + { + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) + sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy); + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + break; + + case SNDCTL_DSP_SYNC: + DPF(2, "SNDCTL_DSP_SYNC:\n"); + + if (file->f_mode & FMODE_WRITE) + { + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out && wave_out->state == CARDWAVE_STATE_STARTED) + while ((woinst->total_played < woinst->total_copied) + && !signal_pending(current)) + { + spin_unlock_irqrestore(&woinst->lock, flags); + interruptible_sleep_on(&woinst->wait_queue); + spin_lock_irqsave(&woinst->lock, flags); + } + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + break; + + case SNDCTL_DSP_SETDUPLEX: + DPF(2, "SNDCTL_DSP_SETDUPLEX:\n"); + break; + + case SNDCTL_DSP_GETCAPS: + DPF(2, "SNDCTL_DSP_GETCAPS:\n"); + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_COPROC, (int *)arg); + + case SNDCTL_DSP_SPEED: + DPF(2, "SNDCTL_DSP_SPEED:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + DPD(2, "val is %d\n", val); + + if (val >= 0) + { + if (file->f_mode & FMODE_WRITE) + { + struct wave_format *wave_fmt; + + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) + sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy); + + wave_fmt = &woinst->wave_fmt; + wave_fmt->samplingrate = val; + + sblive_waveoutQueryFormat(wave_dev->sb_hw->card_waveout, wave_fmt, CARDWAVE_QF_RATE); + + if (woinst->mapped) + { + struct wave_format wave_fmt = woinst->wave_fmt; + + if (sblive_waveoutSetControl(wave_dev->sb_hw, wave_out, WAVEINSTANCEFORMAT, (u32 *)&wave_fmt) + != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + } + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(2, "Set playback sampling rate -> %d\n", woinst->wave_fmt.samplingrate); + } + + if (file->f_mode & FMODE_READ) + { + struct wave_format *wave_fmt; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) + sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy); + + wave_fmt = &wiinst->wave_fmt; + wave_fmt->samplingrate = val; + + sblive_waveinQueryFormat(wave_dev->sb_hw->card_wavein, wave_fmt, CARDWAVE_QF_RATE); + + DPD(2, "Set recording sampling rate -> %d\n", wave_fmt->samplingrate); + + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + return put_user(val, (int *) arg); + } else + { + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.samplingrate; + else if (file->f_mode & FMODE_WRITE) + val = woinst->wave_fmt.samplingrate; + + return put_user(val, (int *) arg); + } + break; + + case SNDCTL_DSP_STEREO: + DPF(2, "SNDCTL_DSP_STEREO:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + DPD(2, " val is %d\n", val); + + if (file->f_mode & FMODE_WRITE) + { + struct wave_format *wave_fmt; + + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) + sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy); + + wave_fmt = &woinst->wave_fmt; + wave_fmt->channels = val ? 2 : 1; + + if (sblive_waveoutQueryFormat(wave_dev->sb_hw->card_waveout, wave_fmt, CARDWAVE_QF_STEREO) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + DPF(2, "waveoutQueryFormat() failed!\n"); + return -EINVAL; + } + + if (woinst->mapped) + { + if (sblive_waveoutSetControl(wave_dev->sb_hw, wave_out, WAVEINSTANCEFORMAT, (u32 *)&wave_fmt) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + DPF(2, "waveoutSetControl() failed!\n"); + return -EINVAL; + } + } + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(2, "Set playback stereo -> %d\n", wave_fmt->channels); + } + + if (file->f_mode & FMODE_READ) + { + struct wave_format *wave_fmt; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) + sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy); + + wave_fmt = &wiinst->wave_fmt; + wave_fmt->channels = val ? 2 : 1; + + if (sblive_waveinQueryFormat(wave_dev->sb_hw->card_wavein, wave_fmt, CARDWAVE_QF_STEREO) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&wiinst->lock, flags); + DPF(2, "waveinQueryFormat() failed!\n"); + return -EINVAL; + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + DPD(2, "Set recording stereo -> %d\n", wave_fmt->channels); + } + + break; + + case SNDCTL_DSP_CHANNELS: + DPF(2, "SNDCTL_DSP_CHANNELS:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + DPD(2, " val is %d\n", val); + + if (val != 0) + { + if (file->f_mode & FMODE_WRITE) + { + struct wave_format *wave_fmt; + + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) + sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy); + + wave_fmt = &woinst->wave_fmt; + wave_fmt->channels = val; + + if (sblive_waveoutQueryFormat(wave_dev->sb_hw->card_waveout, wave_fmt, CARDWAVE_QF_CHANNEL) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + DPF(2, "waveoutQueryFormat() called!\n"); + return -EINVAL; + } + /* + if (woinst->mapped) + { + struct wave_format wave_fmt = woinst->wave_fmt; + + if (sblive_waveoutSetControl(wave_dev->sb_hw, wave_out, WAVEINSTANCEFORMAT, (u32 *)&wave_fmt) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + DPF(2, "waveoutSetControl() failed!\n"); + return -EINVAL; + } + } + */ + DPD(2, "Set playback number of channels -> %d\n", wave_fmt->channels); + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) + { + struct wave_format *wave_fmt; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) + sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy); + + wave_fmt = &wiinst->wave_fmt; + wave_fmt->channels = val; + + if (sblive_waveinQueryFormat(wave_dev->sb_hw->card_wavein, wave_fmt, CARDWAVE_QF_CHANNEL) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&wiinst->lock, flags); + DPF(2, "waveinQueryFormat() called!\n"); + return -EINVAL; + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPD(2, "Set recording number of channels -> %d\n", wave_fmt->channels); + } + + return put_user(val, (int *) arg); + } else + { + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.channels; + else if (file->f_mode & FMODE_WRITE) + val = woinst->wave_fmt.channels; + + return put_user(val, (int *) arg); + } + break; + + case SNDCTL_DSP_GETFMTS: + DPF(2, "SNDCTL_DSP_GETFMTS:\n"); + return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg); + + case SNDCTL_DSP_SETFMT: /* Same as SNDCTL_DSP_SAMPLESIZE */ + DPF(2, "SNDCTL_DSP_SETFMT:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + DPD(2, " val is %d\n", val); + + if (val != AFMT_QUERY) + { + if (file->f_mode & FMODE_WRITE) + { + struct wave_format *wave_fmt; + + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) + sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy); + + wave_fmt = &woinst->wave_fmt; + wave_fmt->bitspersample = val; + + sblive_waveoutQueryFormat(wave_dev->sb_hw->card_waveout, wave_fmt, CARDWAVE_QF_BITS); + + if (woinst->mapped) + { + struct wave_format wave_fmt = woinst->wave_fmt; + + if (sblive_waveoutSetControl(wave_dev->sb_hw, wave_out, WAVEINSTANCEFORMAT, (u32 *) &wave_fmt) != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + DPF(2, "waveoutSetControl() failed!\n"); + return -EINVAL; + } + } + + spin_unlock_irqrestore(&woinst->lock, flags); + + DPD(2, "Set playback sample size -> %d\n", wave_fmt->bitspersample); + } + + if (file->f_mode & FMODE_READ) + { + struct wave_format *wave_fmt; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) + sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy); + + wave_fmt = &wiinst->wave_fmt; + wave_fmt->bitspersample = val; + + sblive_waveinQueryFormat(wave_dev->sb_hw->card_wavein, wave_fmt, CARDWAVE_QF_BITS); + + spin_unlock_irqrestore(&wiinst->lock, flags); + + DPD(2, "set recording sample size -> %d\n", wave_fmt->bitspersample); + } + + return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + } else + { + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.bitspersample; + else if (file->f_mode & FMODE_WRITE) + val = woinst->wave_fmt.bitspersample; + + return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *) arg); + } + break; + + case SOUND_PCM_READ_BITS: + + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.bitspersample; + else if ( file->f_mode & FMODE_WRITE ) + val = woinst->wave_fmt.bitspersample; + + return put_user((val == 16) ? AFMT_S16_LE : AFMT_U8, (int *)arg); + + case SOUND_PCM_READ_RATE: + + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.samplingrate; + else if ( file->f_mode & FMODE_WRITE ) + val = woinst->wave_fmt.samplingrate; + + return put_user(val, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + + if (file->f_mode & FMODE_READ) + val = wiinst->wave_fmt.channels; + else if ( file->f_mode & FMODE_WRITE ) + val = woinst->wave_fmt.channels; + + return put_user(val, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + DPF(2, "SNDCTL_DSP_POST: not implemented\n"); + break; + + case SOUND_PCM_READ_FILTER: + DPF(2, "SNDCTL_DSP_POST: not implemented\n"); + break; + + case SNDCTL_DSP_SETSYNCRO: + DPF(2, "SNDCTL_DSP_POST: not implemented\n"); + break; + + case SNDCTL_DSP_GETTRIGGER: + DPF(2, "SNDCTL_DSP_GETTRIGGER:\n"); + + if (file->f_mode & FMODE_WRITE && (wave_dev->enablebits & PCM_ENABLE_OUTPUT)) + val |= PCM_ENABLE_OUTPUT; + if (file->f_mode & FMODE_READ && (wave_dev->enablebits & PCM_ENABLE_INPUT)) + val |= PCM_ENABLE_INPUT; + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_SETTRIGGER: + DPF(2, "SNDCTL_DSP_SETTRIGGER:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + + if (file->f_mode & FMODE_WRITE) + { + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) + { + if (val & PCM_ENABLE_OUTPUT) + { + sblive_waveoutStart(wave_dev); + wave_dev->enablebits |= PCM_ENABLE_OUTPUT; + } else + { + sblive_waveoutStop(wave_dev->sb_hw, wave_out, &dummy); + wave_dev->enablebits &= ~PCM_ENABLE_OUTPUT; + } + } + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (file->f_mode & FMODE_READ) + { + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) + { + if (val & PCM_ENABLE_INPUT) + { + sblive_waveinStart(wave_dev); + wave_dev->enablebits |= PCM_ENABLE_INPUT; + } else + { + sblive_waveinStop(wave_dev->sb_hw, wave_in, &dummy); + wave_dev->enablebits &= ~PCM_ENABLE_INPUT; + } + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + } + break; + + case SNDCTL_DSP_GETOSPACE: + { + audio_buf_info info; + + DPF(4, "SNDCTL_DSP_GETOSPACE:\n"); + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) + { + if (sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout, wave_out, &bytestocopy, &pending) + != CTSTATUS_SUCCESS) + { + DPF(2, "sblive_waveoutGetXferSize failed\n"); + + spin_unlock_irqrestore(&woinst->lock, flags); + return -EFAULT; + } + info.bytes = bytestocopy; + } else + { + calculate_ofrag(woinst); + info.bytes = woinst->numfrags * woinst->fragment_size; + } + + if (woinst->silence_filled > 0) + { + if (woinst->silence_filled == 1) + { + if (pending <= woinst->fragment_size) + woinst->silence_filled = 2; + else + info.bytes += woinst->fragment_size; + } + + if (woinst->silence_filled == 2) + info.bytes = woinst->numfrags * woinst->fragment_size; + } + + info.fragstotal = woinst->numfrags; + info.fragments = info.bytes / woinst->fragment_size; + info.fragsize = woinst->fragment_size; + + spin_unlock_irqrestore(&woinst->lock, flags); + + if (copy_to_user((int *) arg, &info, sizeof(info))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info info; + + DPF(4, "SNDCTL_DSP_GETISPACE:\n"); + + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) + { + if (sblive_waveinGetXferSize(wave_dev->sb_hw->card_wavein, wave_in, &bytestocopy, &pending) + != CTSTATUS_SUCCESS) + { + DPF(2, "sblive_waveinGetXferSize failed\n"); + + spin_unlock_irqrestore(&wiinst->lock, flags); + return -EFAULT; + } + + info.bytes = bytestocopy; + + } else + { + calculate_ifrag(wiinst); + info.bytes = wiinst->numfrags * wiinst->fragment_size; + } + + info.fragstotal = wiinst->numfrags; + info.fragments = info.bytes / wiinst->fragment_size; + info.fragsize = wiinst->fragment_size; + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (copy_to_user((int *) arg, &info, sizeof(info))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_NONBLOCK: + DPF(2, "SNDCTL_DSP_NONBLOCK:\n"); + + file->f_flags |= O_NONBLOCK; + break; + + case SNDCTL_DSP_GETODELAY: + DPF(4, "SNDCTL_DSP_GETODELAY:\n"); + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) + { + if (sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout, wave_out, &bytestocopy, &pending) + != CTSTATUS_SUCCESS) + { + DPF(2, "sblive_waveoutGetXferSize failed\n"); + + spin_unlock_irqrestore(&woinst->lock, flags); + return -EFAULT; + } + + if (woinst->silence_filled > 0) + { + if (woinst->silence_filled == 1) + { + if (pending <= woinst->fragment_size) + woinst->silence_filled = 2; + else + pending -= woinst->fragment_size; + } + + if (woinst->silence_filled == 2) + pending = 0; + } + + val = pending; + } + + spin_unlock_irqrestore(&woinst->lock, flags); + + return put_user(val, (int *) arg); + + case SNDCTL_DSP_GETIPTR: + { + count_info cinfo; + + DPF(4, "SNDCTL_DSP_GETIPTR: \n"); + + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + + spin_lock_irqsave(&wiinst->lock, flags); + + if (wave_in) + { + if (sblive_waveinGetControl(wave_dev->sb_hw->card_wavein, wave_in, WAVECURPOS, (u32 *)&cinfo.ptr) + != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&wiinst->lock, flags); + return -EINVAL; + } + } else + cinfo.ptr = 0; + + DPD(4, "cinfo.ptr -> %d\n", cinfo.ptr); + + cinfo.bytes = cinfo.ptr + wiinst->total_recorded - wiinst->total_recorded % (wiinst->fragment_size * wiinst->numfrags); + cinfo.blocks = wiinst->getiptr_blocks; + + wiinst->getiptr_blocks = 0; + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_GETOPTR: + { + count_info cinfo; + + DPF(4, "SNDCTL_DSP_GETOPTR:\n"); + + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + + spin_lock_irqsave(&woinst->lock, flags); + + if (wave_out) + { + if (sblive_waveoutGetControl(wave_dev->sb_hw->card_waveout, wave_out, WAVECURPOS, (u32 *)&cinfo.ptr) + != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + } else + cinfo.ptr = 0; + + DPD(4, "cinfo.ptr -> %d\n", cinfo.ptr); + + cinfo.bytes = cinfo.ptr + woinst->total_played - woinst->total_played % (woinst->fragment_size * woinst->numfrags); + cinfo.blocks = cinfo.bytes / woinst->fragment_size - woinst->getoptr_blocks; + + woinst->getoptr_blocks = cinfo.bytes / woinst->fragment_size; + + spin_unlock_irqrestore(&woinst->lock, flags); + + if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + } + break; + + case SNDCTL_DSP_GETBLKSIZE: + DPF(2, "SNDCTL_DSP_GETBLKSIZE:\n"); + + if (file->f_mode & FMODE_WRITE) + { + spin_lock_irqsave(&woinst->lock, flags); + + calculate_ofrag(woinst); + + spin_unlock_irqrestore(&woinst->lock, flags); + + return put_user(woinst->fragment_size, (int *) arg); + } + + if (file->f_mode & FMODE_READ) + { + spin_lock_irqsave(&wiinst->lock, flags); + + calculate_ifrag(wiinst); + + spin_unlock_irqrestore(&wiinst->lock, flags); + + return put_user(wiinst->fragment_size, (int *) arg); + } + break; + + case SNDCTL_DSP_POST: + DPF(2, "SNDCTL_DSP_POST: not implemented\n"); + break; + + case SNDCTL_DSP_SUBDIVIDE: + DPF(2, "SNDCTL_DSP_SUBDIVIDE: not implemented\n"); + break; + + case SNDCTL_DSP_SETFRAGMENT: + DPF(2, "SNDCTL_DSP_SETFRAGMENT:\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + + DPD(2, "val is %x\n", val); + + if (val == 0) + return -EIO; + + if (file->f_mode & FMODE_WRITE) + { + if (wave_out) + return -EINVAL; /* too late to change */ + + woinst->ossfragshift = val & 0xffff; + woinst->numfrags = (val >> 16) & 0xffff; + } + + if (file->f_mode & FMODE_READ) + { + if (wave_in) + return -EINVAL; /* too late to change */ + + wiinst->ossfragshift = val & 0xffff; + wiinst->numfrags = (val >> 16) & 0xffff; + } + + break; + + case SNDCTL_COPR_LOAD: + { + copr_buffer buf; + u32 i; + + DPF(2, "SNDCTL_COPR_LOAD:\n"); + + if (copy_from_user(&buf, (copr_buffer *) arg, sizeof(buf))) + return -EFAULT; + + if ((buf.command != 1) && (buf.command != 2)) + return -EINVAL; + + if ((buf.offs < 0) || (buf.offs + buf.len > 0x500) || (buf.len > 1000)) + return -EINVAL; + + buf.offs += 0x100; + + if (buf.command == 1) { + for (i = 0; i < buf.len; i++) + ((u32 *) buf.data)[i] = sblive_readptr(wave_dev->sb_hw, buf.offs + i, 0); + if (copy_to_user((copr_buffer *) arg, &buf, sizeof(buf))) + return -EFAULT; + } else { + for (i = 0; i < buf.len; i++) + sblive_writeptr(wave_dev->sb_hw, buf.offs + i, 0, ((u32 *) buf.data)[i]); + } + break; + } + + default: /* Default is unrecognized command */ + DPD(2, "default: %x\n", cmd); + return -EINVAL; + } + return 0; +} + +static int emu10k1_audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data; + + DPF(2, "emu10k1_audio_mmap() called\n"); + + if (vma_get_pgoff(vma) != 0) + return -ENXIO; + + if (vma->vm_flags & VM_WRITE){ + struct woinst *woinst = wave_dev->woinst; + u32 size, flags; + int i; + + spin_lock_irqsave(&woinst->lock, flags); + + if (!woinst->wave_out) + { + calculate_ofrag(woinst); + + if (sblive_waveoutOpen(wave_dev->sb_hw, &woinst->wave_fmt, &woinst->fragment_size, woinst->numfrags, &woinst->wave_out) + != CTSTATUS_SUCCESS) + { + DPF(2, "sblive_waveoutOpen failed!\n"); + + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + /* Now mark the pages as reserved, otherwise remap_page_range doesn't do what we want */ + for (i = 0; i < woinst->wave_out->wavexferbuf->numpages; i++) + set_bit(PG_reserved, &mem_map[MAP_NR(woinst->wave_out->pagetable[i])].flags); + } + + size = vma->vm_end - vma->vm_start; + + if (size > (PAGE_SIZE * woinst->wave_out->wavexferbuf->numpages)) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EINVAL; + } + + for (i = 0; i < woinst->wave_out->wavexferbuf->numpages; i++) { + if (remap_page_range(vma->vm_start + (i * 4096), virt_to_phys(woinst->wave_out->pagetable[i]), 4096, vma->vm_page_prot)) { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EAGAIN; + } + } + + woinst->mapped = TRUE; + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + if (vma->vm_flags & VM_READ){ + struct wiinst *wiinst = wave_dev->wiinst; + u32 flags; + + spin_lock_irqsave(&wiinst->lock, flags); + wiinst->mapped = TRUE; + spin_unlock_irqrestore(&wiinst->lock, flags); + } + + return 0; +} + +static int emu10k1_audio_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct sblive_hw *sb_hw = sblive_devs; + struct sblive_wavedevice *wave_dev; + + DPF(2, "emu10k1_audio_open() called\n"); + + /* Check for correct device to open */ + while (sb_hw && ((sb_hw->audio_num ^ minor) & ~0x0f)) + sb_hw = sb_hw->next; + + if (!sb_hw) + return -ENODEV; + + /* Wait for device to become free */ + down(&sb_hw->open_sem); + + while ((file->f_mode & FMODE_READ) && (sb_hw->open_mode & FMODE_READ)) + { + if (file->f_flags & O_NONBLOCK) + { + up(&sb_hw->open_sem); + return -EAGAIN; + } + + up(&sb_hw->open_sem); + + DPF(2, "open sleep....\n"); + interruptible_sleep_on(&sb_hw->open_wait); + + if (signal_pending(current)) + return -ERESTARTSYS; + + down(&sb_hw->open_sem); + } + + wave_dev = (struct sblive_wavedevice *)kmalloc(sizeof(struct sblive_wavedevice), GFP_KERNEL); + if (!wave_dev) + { + DPF(2, "struct sblive_wavedevice alloc fail.\n"); + up(&sb_hw->open_sem); + return -EINVAL; + } + + DPD(3, "kmalloc: [%p]\n", wave_dev); + + wave_dev->sb_hw = sb_hw; + wave_dev->wiinst = NULL; + wave_dev->woinst = NULL; + wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT; /* Default */ + + if (file->f_mode & FMODE_WRITE) + { + struct woinst *woinst; + + woinst = (struct woinst *) kmalloc(sizeof(struct woinst), GFP_KERNEL); + if (woinst == NULL) + { + DPF(2, "struct woinst alloc failed.\n"); + up(&sb_hw->open_sem); + return -ENODEV; + } + + DPD(3, "kmalloc: [%p]\n", woinst); + + /* Set default format to : mono 8-bit 8kHz */ + woinst->wave_fmt.flags = 0; + woinst->wave_fmt.samplingrate = 8000; + woinst->wave_fmt.bitspersample = 8; + woinst->wave_fmt.channels = 1; + woinst->wave_fmt.isinput = (file->f_mode == FMODE_READ) ? TRUE : FALSE; + woinst->ossfragshift = 0; + woinst->fragment_size = 0; + woinst->numfrags = 0; + woinst->wave_out = NULL; // wave_out instance + + init_waitqueue_head(&woinst->wait_queue); + + woinst->mapped = FALSE; + woinst->total_copied = 0; + woinst->total_played = 0; + woinst->silence_filled = 0; + woinst->silence_start = 0; + woinst->getoptr_blocks = 0; + woinst->wave_ptr = 0; + woinst->lock = SPIN_LOCK_UNLOCKED; + wave_dev->woinst = woinst; + } + + if (file->f_mode & FMODE_READ) + { + /* Recording */ + struct wiinst *wiinst; + + /* Only support one wave input instance */ + if (sb_hw->card_wavein->numrecordinst >= 1) + { + DPF(2, "exceed one wave input instance\n"); + up(&sb_hw->open_sem); + return -ENODEV; + } + + wiinst = (struct wiinst *) kmalloc(sizeof(struct wiinst), GFP_KERNEL); + if (wiinst == NULL) + { + DPF(2, "struct wiinst alloc failed\n"); + up(&sb_hw->open_sem); + return -ENODEV; + } + + DPD(3, "kmalloc: [%p]\n", wiinst); + + /* Set default format to : mono 8-bit 8kHz */ + wiinst->wave_fmt.flags = 0; + wiinst->wave_fmt.samplingrate = 8000; + wiinst->wave_fmt.bitspersample = 8; + wiinst->wave_fmt.channels = 1; + wiinst->wave_fmt.isinput = (file->f_mode == FMODE_READ) ? TRUE : FALSE; + wiinst->ossfragshift = 0; + wiinst->fragment_size = 0; + wiinst->numfrags = 0; + wiinst->wave_in = NULL; /* sblive_wavein instance */ + + init_waitqueue_head(&wiinst->wait_queue); + + wiinst->mapped = FALSE; + wiinst->total_recorded = 0; + wiinst->getiptr_blocks = 0; + wiinst->lock = SPIN_LOCK_UNLOCKED; + wave_dev->wiinst = wiinst; + wave_dev->sb_hw->card_wavein->numrecordinst++; + +#ifdef EMU10K1_DEBUG + if (wave_dev->woinst != NULL) + DPF(2, "audio_open opened both WAVEOUT and WAVEIN!\n"); +#endif + + } + + file->private_data = (void *) wave_dev; + + sb_hw->open_mode |= file->f_mode & FMODE_READ; + up(&sb_hw->open_sem); + + MOD_INC_USE_COUNT; + return 0; /* Success? */ +} + + +static int emu10k1_audio_release(struct inode *inode, struct file *file) +{ + struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data; + struct sblive_hw *sb_hw = wave_dev->sb_hw; + + /* FIXME: Do we need a spinlock here? */ + + DPF(2, "emu10k1_audio_release() called\n"); + + if (file->f_mode & FMODE_WRITE) + { + struct woinst *woinst = wave_dev->woinst; + struct wave_out *wave_out = woinst->wave_out; + + if (wave_out) + { + if ((wave_out->state == CARDWAVE_STATE_STARTED) && !(file->f_flags & O_NONBLOCK)) + { + while (!signal_pending(current) + && (woinst->total_played < woinst->total_copied)) + { + DPF(2, "Buffer hasn't been totally played, sleep....\n"); + interruptible_sleep_on(&woinst->wait_queue); + } + } + + if (woinst->mapped && wave_out->pagetable) + { + int i; + /* Undo marking the pages as reserved */ + for (i = 0; i < woinst->wave_out->wavexferbuf->numpages; i++) + set_bit(PG_reserved, &mem_map[MAP_NR(woinst->wave_out->pagetable[i])].flags); + } + + woinst->mapped = FALSE; + sblive_waveoutClose(wave_dev->sb_hw, wave_out); + } + kfree((void *) wave_dev->woinst); + } + + if (file->f_mode & FMODE_READ) + { + struct wiinst *wiinst = wave_dev->wiinst; + struct wave_in *wave_in = wiinst->wave_in; + + if (wave_in) + { + wiinst->mapped = FALSE; + sblive_waveinClose(wave_dev->sb_hw, wave_in); + } + + kfree((void *) wave_dev->wiinst); + + wave_dev->sb_hw->card_wavein->numrecordinst--; + } + + kfree((void *) wave_dev); + + down(&sb_hw->open_sem); + sb_hw->open_mode &= ~(file->f_mode & FMODE_READ); + up(&sb_hw->open_sem); + wake_up(&sb_hw->open_wait); + MOD_DEC_USE_COUNT; + + return 0; +} + + +static unsigned int emu10k1_audio_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) file->private_data; + u16 mask = 0; + + DPF(4, "emu10k1_audio_poll() called\n"); + + if (file->f_mode & FMODE_READ) + { + struct wiinst *wiinst = wave_dev->wiinst; + struct wave_in *wave_in = wiinst->wave_in; + + if (wave_in == NULL) + mask |= POLLIN | POLLRDNORM; + else + { + u32 flags, bytestocopy, pending; + int status; + + poll_wait(file, &wiinst->wait_queue, wait); + + spin_lock_irqsave(&wiinst->lock, flags); + + status = sblive_waveinGetXferSize(wave_dev->sb_hw->card_wavein, wave_in, &bytestocopy, &pending); + + spin_unlock_irqrestore(&wiinst->lock, flags); + + if (status == CTSTATUS_SUCCESS) + if (bytestocopy > 0) + mask |= POLLIN | POLLRDNORM; + } + } + + if (file->f_mode & FMODE_WRITE) + { + struct woinst *woinst = wave_dev->woinst; + struct wave_out *wave_out = woinst->wave_out; + + if (wave_out == NULL) + mask |= POLLOUT | POLLWRNORM; + else + { + u32 flags, bytestocopy, pending; + int status; + + poll_wait(file, &woinst->wait_queue, wait); + + spin_lock_irqsave(&woinst->lock, flags); + status = sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout, wave_out, &bytestocopy, &pending); + + if(status != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EFAULT; + } + + if (woinst->silence_filled > 0) + { + if (woinst->silence_filled == 1) + { + if (pending <= woinst->fragment_size) + woinst->silence_filled = 2; + else + bytestocopy += woinst->fragment_size; + } + + if (woinst->silence_filled == 2) + bytestocopy = woinst->fragment_size * woinst->numfrags; + } + + spin_unlock_irqrestore(&woinst->lock, flags); + + while(bytestocopy < woinst->fragment_size){ + interruptible_sleep_on(&woinst->wait_queue); + + if(signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&woinst->lock, flags); + status = sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout, wave_out, &bytestocopy, &pending); + + if(status != CTSTATUS_SUCCESS) + { + spin_unlock_irqrestore(&woinst->lock, flags); + return -EFAULT; + } + + if (woinst->silence_filled > 0) + { + if (woinst->silence_filled == 1) + { + if (pending <= woinst->fragment_size) + woinst->silence_filled = 2; + else + bytestocopy += woinst->fragment_size; + } + + if (woinst->silence_filled == 2) + bytestocopy = woinst->fragment_size * woinst->numfrags; + } + + spin_unlock_irqrestore(&woinst->lock, flags); + } + + mask |= POLLOUT | POLLWRNORM; + } + } + + return mask; +} + + +static void calculate_ofrag(struct woinst *woinst) +{ + u32 fragsize = 0, bytespersec; + + if (woinst->fragment_size) + { + DPF(2, "fragment_size already calculated!\n"); + return; + } + + bytespersec = woinst->wave_fmt.channels * (woinst->wave_fmt.bitspersample >> 3) * woinst->wave_fmt.samplingrate; + + woinst->fragment_size = 1; + + if (woinst->ossfragshift) + woinst->fragment_size <<= (woinst->ossfragshift < MINFRAGSHIFT ? MINFRAGSHIFT : woinst->ossfragshift); + else + fragsize = (bytespersec * WAVEOUT_DEFAULTFRAGLEN) / 1000 - 1; + + while (fragsize) + { + fragsize >>= 1; + woinst->fragment_size <<= 1; + } + + if (!woinst->numfrags) + { + u32 numfrags; + + numfrags = (bytespersec * WAVEOUT_DEFAULTBUFLEN) / (woinst->fragment_size * 1000) - 1; + + woinst->numfrags = 1; + + while (numfrags) + { + numfrags >>= 1; + woinst->numfrags <<= 1; + } + } + + if (woinst->numfrags < MINFRAGS) + woinst->numfrags = MINFRAGS; + + if (woinst->numfrags * woinst->fragment_size > WAVEOUT_MAXBUFSIZE) + { + woinst->numfrags = WAVEOUT_MAXBUFSIZE / woinst->fragment_size; + + if (woinst->numfrags < MINFRAGS) + { + woinst->numfrags = MINFRAGS; + woinst->fragment_size = WAVEOUT_MAXBUFSIZE / MINFRAGS; + } + + } else + if (woinst->numfrags * woinst->fragment_size < WAVEOUT_MINBUFSIZE) + woinst->numfrags = WAVEOUT_MINBUFSIZE / woinst->fragment_size; + + DPD(2, " calculated playback fragment_size -> %d\n", woinst->fragment_size); + DPD(2, " calculated playback numfrags -> %d\n", woinst->numfrags); +} + +static void calculate_ifrag(struct wiinst *wiinst) +{ + u32 fragsize, bytespersec; + + if (wiinst->fragment_size) + { + DPF(2, "fragment_size already calculated!\n"); + return; + } + + bytespersec = wiinst->wave_fmt.channels * (wiinst->wave_fmt.bitspersample >> 3) * wiinst->wave_fmt.samplingrate; + + wiinst->fragment_size = 1; + + if (wiinst->ossfragshift) + wiinst->fragment_size <<= (wiinst->ossfragshift < MINFRAGSHIFT ? MINFRAGSHIFT : wiinst->ossfragshift); + else + { + fragsize = (bytespersec * WAVEIN_DEFAULTFRAGLEN) / 1000 - 1; + + while (fragsize) + { + fragsize >>= 1; + wiinst->fragment_size <<= 1; + } + } + + if (!wiinst->numfrags) + wiinst->numfrags = (WAVEIN_DEFAULTBUFLEN * bytespersec) / (wiinst->fragment_size * 1000); + + if (wiinst->numfrags < MINFRAGS) + wiinst->numfrags = MINFRAGS; + + if (wiinst->numfrags * wiinst->fragment_size > WAVEIN_MAXBUFSIZE) { + wiinst->numfrags = WAVEIN_MAXBUFSIZE / wiinst->fragment_size; + if (wiinst->numfrags < MINFRAGS) { + wiinst->numfrags = MINFRAGS; + wiinst->fragment_size = WAVEIN_MAXBUFSIZE / MINFRAGS; + } + } + + DPD(2, " calculated recording fragment_size -> %d\n", wiinst->fragment_size); + DPD(2, " calculated recording numfrags -> %d\n", wiinst->numfrags); +} + +void waveInCallbackFn(void *refdata) +{ + struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) refdata; + struct wiinst *wiinst = wave_dev->wiinst; + struct wave_in *wave_in = wiinst->wave_in; + u32 bytestocopy, pending; + unsigned long flags; + + /* Are these locks necessary around the wake_up ? kabi@i.am */ + spin_lock_irqsave(&wiinst->lock, flags); + + wiinst->getiptr_blocks++; + + wiinst->total_recorded += wiinst->fragment_size; + + if (wiinst->mapped){ + spin_unlock_irqrestore(&wiinst->lock, flags); + return; + } + + if (sblive_waveinGetXferSize(wave_dev->sb_hw->card_wavein, wave_in, &bytestocopy, &pending) == CTSTATUS_SUCCESS) + { + if (bytestocopy >= wiinst->fragment_size) + { + DPF(2, "Wake UP!\n"); + wake_up(&wiinst->wait_queue); + } else + DPD(4, "Not enough transfer size, %d\n", bytestocopy); + } + + spin_unlock_irqrestore(&wiinst->lock, flags); + + return; +} + +void waveOutCallbackFn(void *refdata) +{ + struct sblive_wavedevice *wave_dev = (struct sblive_wavedevice *) refdata; + struct woinst *woinst = wave_dev->woinst; + struct wave_out *wave_out = woinst->wave_out; + u32 bytestocopy, pending, waveptr; + unsigned long flags; + + if(wave_out->state == CARDWAVE_STATE_STOPPED) + return; + + spin_lock_irqsave(&woinst->lock, flags); + + if (sblive_waveoutGetControl(wave_dev->sb_hw->card_waveout, wave_out, WAVECURPOS, (u32 *)&waveptr) == CTSTATUS_SUCCESS) + { + woinst->total_played += waveptr - woinst->wave_ptr; + + if (waveptr < woinst->wave_ptr) + woinst->total_played += woinst->fragment_size * woinst->numfrags; + + woinst->wave_ptr = waveptr; + } + + if (woinst->mapped) + { + spin_unlock_irqrestore(&woinst->lock, flags); + return; + } + + if (sblive_waveoutGetXferSize(wave_dev->sb_hw->card_waveout, wave_out, &bytestocopy, &pending) == CTSTATUS_SUCCESS) + { + if (bytestocopy >= woinst->fragment_size || woinst->silence_filled > 0) + { + if (pending <= woinst->fragment_size && bytestocopy >= woinst->fragment_size) + { + if (woinst->silence_filled == 0) + sblive_waveoutGetControl(wave_dev->sb_hw->card_waveout, wave_out, WAVEWRITEPOINTER, &woinst->silence_start); + + bytestocopy = woinst->fragment_size; + + sblive_waveoutFillSilence(wave_dev->sb_hw->card_waveout, wave_out, &bytestocopy); + + if (woinst->silence_filled < 2) + woinst->silence_filled++; + } + + wake_up(&woinst->wait_queue); + } else + DPD(4, "Not enough transfer size -> %x\n", bytestocopy); + } + + spin_unlock_irqrestore(&woinst->lock, flags); + + return; +} + +int audio_init(struct sblive_hw * sb_hw, u8 * pname) +{ + /* Initialize CardwaveOut struct */ + sb_hw->card_waveout = kmalloc(sizeof(struct sblive_waveout), GFP_KERNEL); + if (sb_hw->card_waveout == NULL) + { + printk(KERN_WARNING "sblive: Unable to allocate struct sblive_waveout: out of memory\n"); + return CTSTATUS_ERROR; + } + + DPD(3, "kmalloc: [%p]\n", sb_hw->card_waveout); + + /* Initialize CardwaveIn struct */ + sb_hw->card_wavein = kmalloc(sizeof(struct sblive_wavein), GFP_KERNEL); + if (sb_hw->card_wavein == NULL) + { + kfree(sb_hw->card_waveout); + sb_hw->card_waveout = NULL; + printk(KERN_WARNING "sblive: Unable to allocate struct sblive_wavein: out of memory\n"); + return CTSTATUS_ERROR; + } + + DPD(3, "kmalloc: [%p]\n", sb_hw->card_wavein); + + sblive_waveoutInit(sb_hw->card_waveout, pname); + sblive_waveinInit(sb_hw->card_wavein, pname); + + return CTSTATUS_SUCCESS; +} + +int audio_exit(struct sblive_hw *sb_hw) +{ + sblive_waveoutExit(sb_hw->card_waveout); + sblive_waveinExit(sb_hw->card_wavein); + + kfree(sb_hw->card_waveout); + DPD(3, "kfree: [%p]\n", sb_hw->card_waveout); + sb_hw->card_waveout = NULL; + + kfree(sb_hw->card_wavein); + DPD(3, "kfree: [%p]\n", sb_hw->card_wavein); + sb_hw->card_wavein = NULL; + + return CTSTATUS_SUCCESS; +} + + +struct file_operations emu10k1_audio_fops = +{ + &emu10k1_audio_llseek, + &emu10k1_audio_read, + &emu10k1_audio_write, + NULL, + &emu10k1_audio_poll, + &emu10k1_audio_ioctl, + &emu10k1_audio_mmap, + &emu10k1_audio_open, + NULL, + &emu10k1_audio_release, + NULL, +}; Index: oldkernel/linux/drivers/sound/emu10k1/audio.h diff -u /dev/null linux/drivers/sound/emu10k1/audio.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/audio.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,60 @@ +/* + ********************************************************************** + * audio.c -- /dev/dsp interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up types/leaks + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +/* + * FIXME: + * There are some 32bit assumptions here + * Cast of pointers to 32bit values is unsafe + * - Wave callbacks and elsewhere + */ + +#ifndef _AUDIO_H +#define _AUDIO_H + +#ifdef MODULE +#define __NO_VERSION__ +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#define MINFRAGS 2 +#define MINFRAGSHIFT 4 + +void waveOutCallbackFn(void *); +void waveInCallbackFn(void *); + +int audio_init(struct sblive_hw *sb_hw, u8 *pname); +int audio_exit(struct sblive_hw *sb_hw); + +#endif /* _AUDIO_H */ Index: oldkernel/linux/drivers/sound/emu10k1/cardmi.c diff -u /dev/null linux/drivers/sound/emu10k1/cardmi.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/cardmi.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,1107 @@ +/* + ********************************************************************** + * sblive_mi.c - MIDI UART input HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox clean up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "cardmi.h" + +static struct +{ + int (*Fn) (struct sblive_mpuin * card_mpuin, u8 data); +} midistatefn[] = +{ + { sblive_miStateParse }, + { sblive_miState3Byte }, /* 0x8n, 0x9n, 0xAn, 0xBn, 0xEn */ + { sblive_miState3ByteKey }, /* Byte 1 */ + { sblive_miState3ByteVel }, /* Byte 2 */ + { sblive_miState2Byte }, /* 0xCn, 0xDn */ + { sblive_miState2ByteKey }, /* Byte 1 */ + { sblive_miStateSysCommon2 }, /* 0xF1 , 0xF3 */ + { sblive_miStateSysCommon2Key },/* 0xF1 , 0xF3, Byte 1 */ + { sblive_miStateSysCommon3 }, /* 0xF2 */ + { sblive_miStateSysCommon3Key },/* 0xF2 , Byte 1 */ + { sblive_miStateSysCommon3Vel },/* 0xF2 , Byte 2 */ + { sblive_miStateSysExNorm }, /* 0xF0, 0xF7, Normal mode */ + { sblive_miStateSysReal } /* 0xF4 - 0xF6 ,0xF8 - 0xFF */ +}; + +/****************************************************************************/ +/* int sblive_mpuinInit(struct sblive_mpuin *card_mpuin, PICARD pICard ) */ +/* */ +/* Function: Initialize the parameters and reset the MPU port */ +/****************************************************************************/ +int sblive_mpuinInit(struct sblive_mpuin *card_mpuin, struct sblive_hw *sb_hw) +{ + char name[128]; + + DPF(2, "sblive_mpuinInit\n"); + + memset(card_mpuin, 0, sizeof(struct sblive_mpuin)); + + card_mpuin->status = FLAGS_AVAILABLE; // clear + + card_mpuin->caps.cbsize = sizeof(struct midi_caps); + card_mpuin->caps.support = MIDICAPS_INPUT; + card_mpuin->caps.technology = 0; + card_mpuin->caps.product = MM_CREATIVE_MIDIIN; + card_mpuin->caps.manufacturer = MM_CREATIVE; + card_mpuin->caps.voices = 0; + card_mpuin->caps.notes = 0; + card_mpuin->caps.channelmask = 0; + card_mpuin->caps.caps = CARDMIDI_IN; + + card_mpuin->task.next = NULL; + card_mpuin->task.sync = 0; + card_mpuin->task.routine = sblive_mpuinDpcCallback; + card_mpuin->task.data = card_mpuin; + + spin_lock_init(&card_mpuin->lock); + + strcpy(name, IDS_EMU_MIDIIN_PNAME); + + /* Fill in card caps */ + sprintf(card_mpuin->caps.MIDIname, "%s [%lx]", name, sb_hw->hwaddr ); + + /* Reset the MPU port */ + if (hwmpuReset(sb_hw) != CTSTATUS_SUCCESS) + { + DPF(2, "cardmi.c: MPU hardware reset failure\n"); + return CTSTATUS_NOTENABLED; + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* int sblive_mpuinExit(struct sblive_mpuin *card_mpuin) */ +/* */ +/* Function: Disable the IRQ TX and uninstall the IRQ handler */ +/****************************************************************************/ +int sblive_mpuinExit(struct sblive_hw *sb_hw) +{ + DPF(2, "sblive_mpuinExit\n"); + + /* Disable RX interrupt */ + sblive_irqmgrDisableIrq(sb_hw, INTE_MIDIRXENABLE); + + return CTSTATUS_SUCCESS; +} + +/****************************************************************************/ +/* int sblive_mpuinGetCaps(struct sblive_mpuin *card_mpuin, */ +/* struct midi_caps *midi_caps) */ +/* */ +/* Function: Returns the MPU IN capabilities */ +/****************************************************************************/ +int sblive_mpuinGetCaps(struct sblive_mpuin *card_mpuin, struct midi_caps *midi_caps) +{ + u32 cbsize; + + DPF(2, "sblive_mpuinGetCaps\n"); + + cbsize = min(midi_caps->cbsize, card_mpuin->caps.cbsize); + memcpy(midi_caps, &card_mpuin->caps, cbsize); + midi_caps->cbsize = cbsize; + + return CTSTATUS_SUCCESS; +} + +/****************************************************************************/ +/* int sblive_mpuinOpen(struct sblive_mpuin *card_mpuin, */ +/* struct midi_openinfo *openinfo, */ +/* u32 * handle) */ +/* */ +/* Function: Installs the IRQ handler for the MPU in port */ +/* and initialize parameters */ +/****************************************************************************/ +int sblive_mpuinOpen(struct sblive_hw *sb_hw, struct midi_openinfo *openinfo) +{ + struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin; + + DPF(2, "sblive_mpuinOpen\n"); + + if (!(card_mpuin->status & FLAGS_AVAILABLE)) + return CTSTATUS_INUSE; + + /* Copy open info and mark channel as in use */ + card_mpuin->openinfo = *openinfo; + card_mpuin->status &= ~FLAGS_AVAILABLE; /* clear */ + card_mpuin->status |= FLAGS_READY; /* set */ + card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ + card_mpuin->firstmidiq = NULL; + card_mpuin->lastmidiq = NULL; + card_mpuin->qhead = 0; + card_mpuin->qtail = 0; + + sblive_miStateInit(card_mpuin); + + hwmpuReset(sb_hw); + hwmpuAcquire(sb_hw); + + return CTSTATUS_SUCCESS; +} + +/****************************************************************************/ +/* int sblive_mpuinClose(struct sblive_mpuin *card_mpuin, */ +/* u32 handle) */ +/* */ +/* Function: If Midi Buffers are present, */ +/* return CTSTATUS_STILLPLAYING */ +/* Otherwise disable and uninstall IRQ handler */ +/****************************************************************************/ +int sblive_mpuinClose(struct sblive_hw *sb_hw) +{ + struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin; + + DPF(2, "sblive_mpuinClose\n"); + + /* Check if there are pending input SysEx buffers */ + if (card_mpuin->firstmidiq != NULL) + { + DPF(2, "Cannot close, MIDI buffers are present.\n"); + return CTSTATUS_STILLPLAYING; + } + + /* Disable RX interrupt */ + sblive_irqmgrDisableIrq(sb_hw, INTE_MIDIRXENABLE); + + hwmpuRelease(sb_hw); + + card_mpuin->status |= FLAGS_AVAILABLE; /* set */ + card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ + + return CTSTATUS_SUCCESS; +} + +/****************************************************************************/ +/* int sblive_mpuinAddBuffer(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* struct midi_hdr *INFO midihdrInfo) */ +/* */ +/* Function: Adds MIDI buffer to local queue list */ +/****************************************************************************/ +int sblive_mpuinAddBuffer(struct sblive_mpuin *card_mpuin, struct midi_hdr *midihdr) +{ + struct midi_queue *midiq; + unsigned long flags; + + DPF(2, "sblive_mpuinAddBuffer\n"); + + /* Update MIDI buffer flags */ + midihdr->flags |= MIDIBUF_INQUEUE; /* set */ + midihdr->flags &= ~MIDIBUF_DONE; /* clear */ + + midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_ATOMIC); + if (midiq == NULL) + { + /* Message lost */ + return CTSTATUS_NOMEMORY; + } + + DPD(3, "kmalloc: [%p]\n", midiq); + + midiq->next = NULL; + midiq->qtype = 1; + midiq->length = midihdr->bufferlength; + midiq->sizeLeft = midihdr->bufferlength; + midiq->midibyte = midihdr->lpData; + midiq->refdata = (unsigned long) midihdr; + + spin_lock_irqsave(&card_mpuin->lock, flags); + + if (card_mpuin->firstmidiq == NULL) + { + card_mpuin->firstmidiq = midiq; + card_mpuin->lastmidiq = midiq; + } else + { + (card_mpuin->lastmidiq)->next = midiq; + card_mpuin->lastmidiq = midiq; + } + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* int sblive_mpuinStart(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* */ +/* Function: First set the Time Stamp if MIDI IN has not started. */ +/* Then enable RX Irq. */ +/****************************************************************************/ +int sblive_mpuinStart(struct sblive_hw *sb_hw) +{ + struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin; + u8 dummy; + + DPF(2, "sblive_mpuinStart\n"); + + /* Set timestamp if not set */ + if (card_mpuin->status & FLAGS_MIDM_STARTED) + { + DPF(2, "Time Stamp not changed\n"); + } else + { + while (hwmpuReadData(sb_hw, &dummy) == CTSTATUS_SUCCESS); + + card_mpuin->status |= FLAGS_MIDM_STARTED; /* set */ + + /* Set new time stamp */ + card_mpuin->timestart = (jiffies * 1000)/HZ; + DPD(2, "New Time Stamp = %d\n", card_mpuin->timestart); + + card_mpuin->qhead = 0; + card_mpuin->qtail = 0; + + sblive_irqmgrEnableIrq(sb_hw, INTE_MIDIRXENABLE); + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* int sblive_mpuinStop(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* */ +/* Function: Disable the RX Irq. If a partial recorded buffer */ +/* exist, send it up to IMIDI level. */ +/****************************************************************************/ +int sblive_mpuinStop(struct sblive_hw *sb_hw) +{ + struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin; + struct midi_queue *midiq; + unsigned long flags; + + DPF(2, "sblive_mpuinStop\n"); + + sblive_irqmgrDisableIrq(sb_hw, INTE_MIDIRXENABLE); + + card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */ + + if (card_mpuin->firstmidiq) + { + spin_lock_irqsave(&card_mpuin->lock, flags); + + midiq = card_mpuin->firstmidiq; + if (midiq != NULL) + { + if (midiq->sizeLeft == midiq->length) + midiq = NULL; + else + { + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + } + } + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + if (midiq) + { + sblive_mpuinMidiCallback (card_mpuin, ICARDMIDI_INLONGERROR, (u32)midiq, 0); + kfree(midiq); + DPD(3, "kfree: [%p]\n", midiq); + } + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* int sblive_mpuinWriteShortData(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* u32 midimsg) */ +/* */ +/* Function: Not Used */ +/****************************************************************************/ +int sblive_mpuinWriteShortData(struct sblive_mpuin *card_mpuin, u32 midimsg) +{ + DPF(2, "sblive_mpuinWriteShortData(): not supported\n"); + return CTSTATUS_NOTSUPPORTED; +} + + +/****************************************************************************/ +/* int sblive_mpuinPause(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* */ +/* Function: Not Used */ +/****************************************************************************/ +int sblive_mpuinPause(struct sblive_mpuin *card_mpuin) +{ + DPF(2, "sblive_mpuinPause(): not supported\n"); + return CTSTATUS_NOTSUPPORTED; +} + + +/****************************************************************************/ +/* int sblive_mpuinStream(struct sblive_mpuin * card_mpuin, */ +/* u32 handle, */ +/* struct midi_hdr *INFO midihdrInfo) */ +/* */ +/* Function: Not Used */ +/****************************************************************************/ +int sblive_mpuinStream(struct sblive_mpuin *card_mpuin, struct midi_hdr *midihdr) +{ + DPF(2, "sblive_mpuinStream(): not supported\n"); + return CTSTATUS_NOTSUPPORTED; +} + + +/****************************************************************************/ +/* int sblive_mpuinRestart(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* */ +/* Function: Not Used */ +/****************************************************************************/ +int sblive_mpuinRestart(struct sblive_mpuin *card_mpuin) +{ + DPF(2, "sblive_mpuinRestart(): not supported\n"); + return CTSTATUS_NOTSUPPORTED; +} + + +/****************************************************************************/ +/* int sblive_mpuinReset(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* */ +/* Function: Disable the RX Irq. If any buffer */ +/* exist, send it up to IMIDI level. */ +/****************************************************************************/ +int sblive_mpuinReset(struct sblive_hw *sb_hw) +{ + struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin; + struct midi_queue *midiq; + + DPF(2, "sblive_mpuinReset: start\n"); + + sblive_irqmgrDisableIrq(sb_hw, INTE_MIDIRXENABLE); + + while (card_mpuin->firstmidiq) + { + midiq = card_mpuin->firstmidiq; + card_mpuin->firstmidiq = midiq->next; + + if (midiq->sizeLeft == midiq->length) + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INLONGDATA, (u32) midiq, 0); + else + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INLONGERROR, (u32) midiq, 0); + + kfree(midiq); + DPD(3, "kfree: [%p]\n", midiq); + } + + card_mpuin->lastmidiq = NULL; + card_mpuin->status &= ~FLAGS_MIDM_STARTED; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* int sblive_mpuinCache(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* struct midi_cache * midicache) */ +/* */ +/* Function: Not Used */ +/****************************************************************************/ +int sblive_mpuinCache(struct sblive_mpuin *card_mpuin, struct midi_cache *midicache) +{ + DPF(2, "sblive_mpuinCache(): not supported\n"); + return CTSTATUS_NOTSUPPORTED; +} + + +/****************************************************************************/ +/* int sblive_mpuinGetPosition(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* u32 *position) */ +/* */ +/* Function: Not Used */ +/****************************************************************************/ +int sblive_mpuinGetPosition(struct sblive_mpuin *card_mpuin, u32 *position) +{ + DPF(2, "sblive_mpuinGetPosition(): not supported\n"); + return CTSTATUS_NOTSUPPORTED; +} + + +/****************************************************************************/ +/* int sblive_mpuinGetControl(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* u32 controlid, */ +/* u32 *value) */ +/* */ +/* Function: Not Used */ +/****************************************************************************/ +int sblive_mpuinGetControl(struct sblive_mpuin *card_mpuin, u32 controlid, u32 *value) +{ + DPF(2, "sblive_mpuinGetControl\n"); + + switch (controlid) + { + case MIDIQUERYACTIVEINST: + DPF(2, "controlid = MIDIQUERYACTIVE\n"); + if (card_mpuin == NULL) + return CTSTATUS_ERROR; + if (!(card_mpuin->status & FLAGS_AVAILABLE)) + return CTSTATUS_SUCCESS; + else + return CTSTATUS_ERROR; + default: + break; + } + + return CTSTATUS_NOTSUPPORTED; +} + + +/****************************************************************************/ +/* int sblive_mpuinSetControl(struct sblive_mpuin *card_mpuin, */ +/* u32 handle, */ +/* u32 controlid, */ +/* u32 *value) */ +/* */ +/* Function: Not Used */ +/****************************************************************************/ +int sblive_mpuinSetControl(struct sblive_mpuin *card_mpuin, u32 controlid, u32 *value) +{ + DPF(2, "sblive_mpuinSetControl(): not supported\n"); + return CTSTATUS_NOTSUPPORTED; +} + + +/****************************************************************************/ +/* int sblive_mpuinMidiCallback(struct sblive_mpuin *card_mpuin, */ +/* u32 msg, */ +/* u32 data) */ +/* */ +/* Function: Passes the message with the data back to the client */ +/* via IRQ & DPC callbacks to Ring 3 */ +/****************************************************************************/ +int sblive_mpuinMidiCallback(struct sblive_mpuin *card_mpuin, u32 msg, u32 data, u32 bytesvalid) +{ + u32 timein; + struct midi_queue *midiq; + unsigned long callback_msg[3]; + struct midi_hdr *midihdr; + + /* Called during ISR. The data & code touched are: + * 1. card_mpuin + * 2. The function to be called + */ + + timein = card_mpuin->timein; + if (card_mpuin->timestart <= timein) + callback_msg[0] = timein - card_mpuin->timestart; + else + callback_msg[0] = (~0x0L- card_mpuin->timestart) + timein; + + if (msg == ICARDMIDI_INDATA || msg == ICARDMIDI_INDATAERROR) + { + callback_msg[1] = data; + callback_msg[2] = bytesvalid; + DPD(2, "sblive_mpuinMidiCallback: midimsg = %x\n", data); + } else + { + midiq = (struct midi_queue *) data; + midihdr = (struct midi_hdr *) midiq->refdata; + + callback_msg[1] = midiq->length - midiq->sizeLeft; + callback_msg[2] = midiq->refdata; + midihdr->flags &= ~MIDIBUF_INQUEUE; + midihdr->flags |= MIDIBUF_DONE; + + midihdr->bytesrecorded = midiq->length - midiq->sizeLeft; + } + + /* Notify client that Sysex buffer has been sent */ + midiCallbackFn(msg, card_mpuin->openinfo.refdata, callback_msg); + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* int sblive_mpuinDpcCallback () */ +/****************************************************************************/ +void sblive_mpuinDpcCallback(void *refdata) +{ + u8 data; + unsigned idx; + struct sblive_mpuin *card_mpuin = (struct sblive_mpuin *) refdata; + unsigned long flags; + + while (card_mpuin->qhead != card_mpuin->qtail) + { + spin_lock_irqsave(&card_mpuin->lock, flags); + idx = card_mpuin->qhead; + data = card_mpuin->midiq[idx].data; + card_mpuin->timein = card_mpuin->midiq[idx].timein; + idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE; + card_mpuin->qhead = idx; + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + sblive_miStateEntry(card_mpuin, data); + } + + return; +} + + +/****************************************************************************/ +/* int sblive_mpuinIrqCallback(u32 event, */ +/* u32 refdata, */ +/* u32 param) */ +/* */ +/* Function: IRQ callback handler routine for the MPU in port */ +/****************************************************************************/ +int sblive_mpuinIrqCallback(struct sblive_hw *sb_hw) +{ + unsigned idx; + unsigned count; + u8 MPUIvalue; + struct sblive_mpuin *card_mpuin = sb_hw->card_mpuin; + + + /* IRQ service routine. The data and code touched are: + * 1. card_mpuin + */ + + count = 0; + idx = card_mpuin->qtail; + + while (1) + { + if (hwmpuReadData(sb_hw, &MPUIvalue) == CTSTATUS_SUCCESS) + { + ++count; + card_mpuin->midiq[idx].data = MPUIvalue; + card_mpuin->midiq[idx].timein = (jiffies * 1000)/HZ; + idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE; + } else + { + break; + } + } + + if (count) + { + card_mpuin->qtail = idx; + + queue_task(&card_mpuin->task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + + return CTSTATUS_SUCCESS; +} + + +/*****************************************************************************/ +/* Supporting functions for Midi-In Interpretation State Machine */ +/*****************************************************************************/ + +/* FIXME: This should be a macro */ +int sblive_miStateInit(struct sblive_mpuin *card_mpuin) +{ + card_mpuin->status = 0; /* For MIDI running status */ + card_mpuin->fstatus = 0; /* For 0xFn status only */ + card_mpuin->curstate = STIN_PARSE; + card_mpuin->laststate = STIN_PARSE; + card_mpuin->data = 0; + card_mpuin->timestart = 0; + card_mpuin->timein = 0; + + return CTSTATUS_SUCCESS; +} + +/* FIXME: This should be a macro */ +int sblive_miStateEntry(struct sblive_mpuin *card_mpuin, u8 data) +{ + return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data); +} + + +int sblive_miStateParse(struct sblive_mpuin *card_mpuin, u8 data) +{ + switch (data & 0xf0) + { + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + case 0xE0: + card_mpuin->curstate = STIN_3BYTE; + break; + + case 0xC0: + case 0xD0: + card_mpuin->curstate = STIN_2BYTE; + break; + + case 0xF0: + /* System messages do not affect the previous running status! */ + switch (data & 0x0f) + { + case 0x0: + card_mpuin->laststate = card_mpuin->curstate; + card_mpuin->curstate = STIN_SYS_EX_NORM; + + if (card_mpuin->firstmidiq) + { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + *midiq->midibyte = data; + --midiq->sizeLeft; + ++midiq->midibyte; + } + + return CTSTATUS_NEXT_BYTE; + + case 0x7: + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, 0xF7, 0); + return CTSTATUS_ERROR; + + case 0x2: + card_mpuin->laststate = card_mpuin->curstate; + card_mpuin->curstate = STIN_SYS_COMMON_3; + break; + + case 0x1: + case 0x3: + card_mpuin->laststate = card_mpuin->curstate; + card_mpuin->curstate = STIN_SYS_COMMON_2; + break; + + default: + /* includes 0xF4 - 0xF6, 0xF8 - 0xFF */ + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + } + + break; + + default: + DPF(2, "BUG: default case hit\n"); + return CTSTATUS_ERROR; + } + + return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data); +} + + +int sblive_miState3Byte(struct sblive_mpuin *card_mpuin, u8 data) +{ + u8 bTemp = data & 0xF0; + + if (bTemp < 0x80) + { + return midistatefn[STIN_3BYTE_KEY].Fn(card_mpuin, data); + } else + if (bTemp <= 0xE0 && bTemp != 0xC0 && bTemp != 0xD0) + { + card_mpuin->status = data; + card_mpuin->curstate = STIN_3BYTE_KEY; + + return CTSTATUS_NEXT_BYTE; + } + + return midistatefn[STIN_PARSE].Fn(card_mpuin, data); +} + + +int sblive_miState3ByteKey(struct sblive_mpuin *card_mpuin, u8 data) +/* byte 1 */ +{ + u32 tmp; + + if (data > 0x7F) + { + /* Real-time messages check */ + if (data > 0xF7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = STIN_PARSE; + tmp = ((u32) data) << 8; + tmp |= card_mpuin->status; + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->data = data; + card_mpuin->curstate = STIN_3BYTE_VEL; + + return CTSTATUS_NEXT_BYTE; +} + + +int sblive_miState3ByteVel(struct sblive_mpuin *card_mpuin, u8 data) +/* byte 2 */ +{ + u32 tmp; + + if (data > 0x7F) + { + /* Real-time messages check */ + if (data > 0xF7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = STIN_PARSE; + tmp = ((u32) data) << 8; + tmp |= card_mpuin->data; + tmp = tmp << 8; + tmp |= card_mpuin->status; + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->curstate = STIN_3BYTE; + tmp = (u32) data; + tmp = tmp << 8; + tmp |= card_mpuin->data; + tmp = tmp << 8; + tmp |= card_mpuin->status; + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATA, tmp, 3); + + return CTSTATUS_SUCCESS; +} + + +int sblive_miState2Byte(struct sblive_mpuin *card_mpuin, u8 data) +{ + u8 bTemp = data & 0xF0; + + if ((bTemp == 0xC0) || (bTemp == 0xD0)) + { + card_mpuin->status = data; + card_mpuin->curstate = STIN_2BYTE_KEY; + + return CTSTATUS_NEXT_BYTE; + } + + if (bTemp < 0x80) + return midistatefn[STIN_2BYTE_KEY].Fn(card_mpuin, data); + + return midistatefn[STIN_PARSE].Fn(card_mpuin, data); +} + + +int sblive_miState2ByteKey(struct sblive_mpuin *card_mpuin, u8 data) +/* byte 1 */ +{ + u32 tmp; + + if (data > 0x7F) + { + /* Real-time messages check */ + if (data > 0xF7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = STIN_PARSE; + tmp = (u32) data; + tmp = tmp << 8; + tmp |= card_mpuin->status; + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->curstate = STIN_2BYTE; + tmp = (u32) data; + tmp = tmp << 8; + tmp |= card_mpuin->status; + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATA, tmp, 2); + + return CTSTATUS_SUCCESS; +} + + +int sblive_miStateSysCommon2(struct sblive_mpuin *card_mpuin, u8 data) +{ + card_mpuin->fstatus = data; + card_mpuin->curstate = STIN_SYS_COMMON_2_KEY; + + return CTSTATUS_NEXT_BYTE; +} + + +int sblive_miStateSysCommon2Key(struct sblive_mpuin *card_mpuin, u8 data) +/* byte 1 */ +{ + u32 tmp; + + if (data > 0x7F) + { + /* Real-time messages check */ + if (data > 0xF7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (u32) data; + tmp = tmp << 8; + tmp |= card_mpuin->fstatus; + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (u32) data; + tmp = tmp << 8; + tmp |= card_mpuin->fstatus; + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATA, tmp, 2); + + return CTSTATUS_SUCCESS; +} + + +int sblive_miStateSysCommon3(struct sblive_mpuin *card_mpuin, u8 data) +{ + card_mpuin->fstatus = data; + card_mpuin->curstate = STIN_SYS_COMMON_3_KEY; + + return CTSTATUS_NEXT_BYTE; +} + + +int sblive_miStateSysCommon3Key(struct sblive_mpuin *card_mpuin, u8 data) +/* byte 1 */ +{ + u32 tmp; + + if (data > 0x7F) + { + /* Real-time messages check */ + if (data > 0xF7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (u32) data; + tmp = tmp << 8; + tmp |= card_mpuin->fstatus; + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->data = data; + card_mpuin->curstate = STIN_SYS_COMMON_3_VEL; + + return CTSTATUS_NEXT_BYTE; +} + + +int sblive_miStateSysCommon3Vel(struct sblive_mpuin *card_mpuin, u8 data) +/* byte 2 */ +{ + u32 tmp; + + if (data > 0x7F) + { + /* Real-time messages check */ + if (data > 0xF7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (u32) data; + tmp = tmp << 8; + tmp |= card_mpuin->data; + tmp = tmp << 8; + tmp |= card_mpuin->fstatus; + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0); + + return CTSTATUS_ERROR; + } + + card_mpuin->curstate = card_mpuin->laststate; + tmp = (u32) data; + tmp = tmp << 8; + tmp |= card_mpuin->data; + tmp = tmp << 8; + tmp |= card_mpuin->fstatus; + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATA, tmp, 3); + + return CTSTATUS_SUCCESS; +} + + +int sblive_miStateSysExNorm(struct sblive_mpuin *card_mpuin, u8 data) +{ + unsigned long flags; + + if ((data > 0x7F) && (data != 0xF7)) + { + /* Real-time messages check */ + if (data > 0xF7) + return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data); + + /* Invalid Data! */ + DPF(2, "Invalid data!\n"); + + card_mpuin->curstate = card_mpuin->laststate; + + if (card_mpuin->firstmidiq) + { + struct midi_queue * midiq; + + midiq = card_mpuin->firstmidiq; + *midiq->midibyte = data; + --midiq->sizeLeft; + ++midiq->midibyte; + + spin_lock_irqsave(&card_mpuin->lock, flags); + + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INLONGERROR, (u32) midiq, 0); + + kfree(midiq); + } + + return CTSTATUS_ERROR; + } + + if (card_mpuin->firstmidiq) + { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + *midiq->midibyte = data; + --midiq->sizeLeft; + ++midiq->midibyte; + } + + if (data == 0xF7) + { + /* End of Sysex buffer */ + /* Send down the buffer */ + + card_mpuin->curstate = card_mpuin->laststate; + + if (card_mpuin->firstmidiq) + { + struct midi_queue * midiq; + + midiq = card_mpuin->firstmidiq; + + spin_lock_irqsave(&card_mpuin->lock, flags); + + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INLONGDATA, (u32) midiq, 0); + + kfree(midiq); + } + + return CTSTATUS_SUCCESS; + } + + if (card_mpuin->firstmidiq) + { + struct midi_queue *midiq; + + midiq = card_mpuin->firstmidiq; + + if (midiq->sizeLeft == 0) + { + /* Special case */ + + spin_lock_irqsave(&card_mpuin->lock, flags); + + card_mpuin->firstmidiq = midiq->next; + if (card_mpuin->firstmidiq == NULL) + card_mpuin->lastmidiq = NULL; + + spin_unlock_irqrestore(&card_mpuin->lock, flags); + + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INLONGDATA, (u32) midiq, 0); + + kfree(midiq); + + return CTSTATUS_NEXT_BYTE; + } + } + + return CTSTATUS_NEXT_BYTE; +} + + +int sblive_miStateSysReal(struct sblive_mpuin *card_mpuin, u8 data) +{ + sblive_mpuinMidiCallback(card_mpuin, ICARDMIDI_INDATA, data, 1); + + return CTSTATUS_NEXT_BYTE; +} Index: oldkernel/linux/drivers/sound/emu10k1/cardmi.h diff -u /dev/null linux/drivers/sound/emu10k1/cardmi.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/cardmi.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,128 @@ +/* + ********************************************************************** + * sblive_mi.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _CARDMI_H +#define _CARDMI_H + +#include "icardmid.h" + +typedef enum +{ + STIN_PARSE = 0, + STIN_3BYTE, /* 0x80, 0x90, 0xA0, 0xB0, 0xE0 */ + STIN_3BYTE_KEY, /* Byte 1 */ + STIN_3BYTE_VEL, /* Byte 1 */ + STIN_2BYTE, /* 0xC0, 0xD0 */ + STIN_2BYTE_KEY, /* Byte 1 */ + STIN_SYS_COMMON_2, /* 0xF1, 0xF3 */ + STIN_SYS_COMMON_2_KEY, + STIN_SYS_COMMON_3, /* 0xF2 */ + STIN_SYS_COMMON_3_KEY, + STIN_SYS_COMMON_3_VEL, + STIN_SYS_EX_NORM, /* 0xF0, Normal mode */ + STIN_SYS_REAL +} midi_in_state; + + +/* flags for card MIDI in object */ +#define FLAGS_MIDM_STARTED 0x00001000 // Data has started to come in after Midm Start +#define MIDIIN_MAX_BUFFER_SIZE 200 // Definition for struct sblive_mpuin + +struct midi_data +{ + u8 data; + u32 timein; +}; + +struct sblive_mpuin +{ + spinlock_t lock; + struct midi_queue *firstmidiq; + struct midi_queue *lastmidiq; + unsigned qhead, qtail; + struct midi_data midiq[MIDIIN_MAX_BUFFER_SIZE]; + struct tq_struct task; + struct midi_openinfo openinfo; + struct midi_caps caps; + + /* For MIDI state machine */ + u8 status; /* For MIDI running status */ + u8 fstatus; /* For 0xFn status only */ + midi_in_state curstate; + midi_in_state laststate; + u32 timestart; + u32 timein; + u8 data; +}; + +int sblive_mpuinInit(struct sblive_mpuin *, struct sblive_hw *); +int sblive_mpuinExit(struct sblive_hw *); + +int sblive_mpuinGetCaps(struct sblive_mpuin *, struct midi_caps *); +int sblive_mpuinOpen(struct sblive_hw *, struct midi_openinfo *); +int sblive_mpuinClose(struct sblive_hw *); +int sblive_mpuinAddBuffer(struct sblive_mpuin *, struct midi_hdr *); +int sblive_mpuinStart(struct sblive_hw *); +int sblive_mpuinStop(struct sblive_hw *); +int sblive_mpuinWriteShortData(struct sblive_mpuin *, u32); +int sblive_mpuinPause(struct sblive_mpuin *); +int sblive_mpuinStream(struct sblive_mpuin *, struct midi_hdr *); +int sblive_mpuinRestart(struct sblive_mpuin *); +int sblive_mpuinReset(struct sblive_hw *); +int sblive_mpuinCache(struct sblive_mpuin *, struct midi_cache *); +int sblive_mpuinGetPosition(struct sblive_mpuin *, u32 *); +int sblive_mpuinGetControl(struct sblive_mpuin *, u32, u32 *); +int sblive_mpuinSetControl(struct sblive_mpuin *, u32, u32 *); + +#define IDS_EMU_MIDIIN_PNAME "SB Live! In" + +int sblive_miStateInit(struct sblive_mpuin *); +int sblive_miStateEntry(struct sblive_mpuin *, u8); +int sblive_miStateParse(struct sblive_mpuin *, u8); +int sblive_miState3Byte(struct sblive_mpuin *, u8); +int sblive_miState3ByteKey(struct sblive_mpuin *, u8); +int sblive_miState3ByteVel(struct sblive_mpuin *, u8); +int sblive_miState2Byte(struct sblive_mpuin *, u8); +int sblive_miState2ByteKey(struct sblive_mpuin *, u8); +int sblive_miStateSysCommon2(struct sblive_mpuin *, u8); +int sblive_miStateSysCommon2Key(struct sblive_mpuin *, u8); +int sblive_miStateSysCommon3(struct sblive_mpuin *, u8); +int sblive_miStateSysCommon3Key(struct sblive_mpuin *, u8); +int sblive_miStateSysCommon3Vel(struct sblive_mpuin *, u8); +int sblive_miStateSysExNorm(struct sblive_mpuin *, u8); +int sblive_miStateSysReal(struct sblive_mpuin *, u8); + +int sblive_mpuinIrqCallback(struct sblive_hw *); +void sblive_mpuinDpcCallback(void *); +int sblive_mpuinMidiCallback(struct sblive_mpuin *card_mpuin, u32 msg, u32 data, u32 bytesvalid); + +#endif /* _CARDMI_H */ Index: oldkernel/linux/drivers/sound/emu10k1/cardmo.c diff -u /dev/null linux/drivers/sound/emu10k1/cardmo.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/cardmo.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,474 @@ +/* + ********************************************************************** + * cardmo.c - MIDI UART output HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "cardmo.h" + +/****************************************************************************/ +/*int sblive_mpuoutInit(PICARDMIDI pICardMidi, PICARD pICard ) */ +/* */ +/*Function: Initialize the parameters + reset the MPU port */ +/****************************************************************************/ +int sblive_mpuoutInit(struct sblive_mpuout *card_mpuout, struct sblive_hw *sb_hw) +{ + u8 name[128]; + + DPF(2, "sblive_mpuoutInit\n"); + + memset(card_mpuout, 0, sizeof(struct sblive_mpuout)); + + card_mpuout->intr = TRUE; + card_mpuout->status = FLAGS_AVAILABLE; + card_mpuout->state = CARDMIDIOUT_STATE_DEFAULT; + card_mpuout->caps.cbsize = sizeof(struct midi_caps); + card_mpuout->caps.support = MIDICAPS_OUTPUT; + card_mpuout->caps.technology = MT_MIDIPORT; + card_mpuout->caps.product = MM_CREATIVE_MIDIOUT; + card_mpuout->caps.manufacturer = MM_CREATIVE; + card_mpuout->caps.voices = 0; + card_mpuout->caps.notes = 0; + card_mpuout->caps.channelmask = 0xffff; + card_mpuout->caps.caps = CARDMIDI_OUT; + + card_mpuout->task.next = NULL; + card_mpuout->task.sync = 0; + card_mpuout->task.routine = sblive_mpuoutDpcCallback; + card_mpuout->task.data = sb_hw; + + spin_lock_init(&card_mpuout->lock); + + strcpy(name, IDS_EMU_MIDIOUT_PNAME); + + /* Fill in card caps */ + sprintf(card_mpuout->caps.MIDIname, "%s [%lx]", name, sb_hw->hwaddr); + + /* Reset the MPU port */ + if (hwmpuReset(sb_hw) != CTSTATUS_SUCCESS) + { + DPF(2, "MPU hardware reset failure\n"); + return CTSTATUS_NOTENABLED; + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/*int sblive_mpuoutExit(struct sblive_mpuout *card_mpuout) */ +/* */ +/*Function: Disable the IRQ TX and uninstall the IRQ handler */ +/****************************************************************************/ +int sblive_mpuoutExit(struct sblive_hw *sb_hw) +{ + struct sblive_mpuout *card_mpuout = sb_hw->card_mpuout; + + DPF(2, "sblive_mpuoutExit\n"); + + sblive_irqmgrDisableIrq(sb_hw, INTE_MIDITXENABLE); + + card_mpuout->status |= FLAGS_AVAILABLE; + card_mpuout->state = CARDMIDIOUT_STATE_DEFAULT; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* int sblive_mpuoutOpen(PICARDMIDI pICardMidi, */ +/* struct midi_openinfo *openinfo, */ +/* u32 *handle) */ +/* */ +/* Function: Installs the IRQ handler for the MPU out port */ +/* and initialize parameters */ +/****************************************************************************/ +int sblive_mpuoutOpen(struct sblive_hw *sb_hw, struct midi_openinfo *openinfo) +{ + struct sblive_mpuout *card_mpuout = sb_hw->card_mpuout; + + DPF(2, "sblive_mpuoutOpen\n"); + + if (!(card_mpuout->status & FLAGS_AVAILABLE)) + return CTSTATUS_INUSE; + + /* Copy open info and mark channel as in use */ + card_mpuout->intr = TRUE; + card_mpuout->openinfo = *openinfo; + card_mpuout->status &= ~FLAGS_AVAILABLE; + card_mpuout->laststatus = 0x80; + card_mpuout->firstmidiq = NULL; + card_mpuout->lastmidiq = NULL; + + hwmpuReset(sb_hw); + hwmpuAcquire(sb_hw); + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* int sblive_mpuoutClose(PICARDMIDI pICardMidi, */ +/* u32 handle) */ +/* */ +/* Function: If local buffer is not empty, */ +/* return CTSTATUS_STILLPLAYING */ +/* Otherwise, disable and uninstall TX IRQ */ +/****************************************************************************/ +int sblive_mpuoutClose(struct sblive_hw *sb_hw) +{ + struct sblive_mpuout *card_mpuout = sb_hw->card_mpuout; + struct midi_queue *midiq; + struct midi_hdr *midihdr; + unsigned long flags; + + DPF(2, "sblive_mpuoutClose\n"); + + sblive_irqmgrDisableIrq(sb_hw, INTE_MIDITXENABLE); + + spin_lock_irqsave(&card_mpuout->lock, flags); + + while(card_mpuout->firstmidiq != NULL){ + midiq = card_mpuout->firstmidiq; + midihdr = (struct midi_hdr *) midiq->refdata; + + card_mpuout->firstmidiq = midiq->next; + + kfree(midihdr->lpData); + + DPD(3, "kfree: [%p]\n", midihdr->lpData); + + kfree(midihdr); + + DPD(3, "kfree: [%p]\n", midihdr); + + kfree(midiq); + DPD(3, "kfree: [%p]\n", midiq); + } + + card_mpuout->lastmidiq = NULL; + + hwmpuRelease(sb_hw); + + card_mpuout->status |= FLAGS_AVAILABLE; + + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* int sblive_mpuoutAddBuffer(PICARDMIDI pICardMidi, */ +/* u32 handle, */ +/* struct midi_hdr *INFO midihdrInfo) */ +/* */ +/* Function: If there isn't enough buffer space, reject Midi Buffer. */ +/* Otherwise, disable TX, create object to hold Midi */ +/* Buffer, update buffer flags and other parameters */ +/* before enabling TX again. */ +/****************************************************************************/ +int sblive_mpuoutAddBuffer(struct sblive_hw *sb_hw, struct midi_hdr *midihdr) +{ + struct sblive_mpuout *card_mpuout = sb_hw->card_mpuout; + struct midi_queue *midiq; + unsigned long flags; + + DPF(2, "sblive_mpuoutAddBuffer\n"); + + if (card_mpuout->state == CARDMIDIOUT_STATE_SUSPEND) + return CTSTATUS_SUCCESS; + + midihdr->flags |= MIDIBUF_INQUEUE; + midihdr->flags &= ~MIDIBUF_DONE; + + midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_KERNEL); + if (midiq == NULL) + { + /* Message lost */ + return CTSTATUS_NOMEMORY; + } + + DPD(3, "kmalloc: [%p]\n", midiq); + + midiq->next = NULL; + midiq->qtype = 1; + midiq->length = midihdr->bufferlength; + midiq->sizeLeft = midihdr->bufferlength; + midiq->midibyte = midihdr->lpData; + + midiq->refdata = (unsigned long) midihdr; + + spin_lock_irqsave(&card_mpuout->lock, flags); + + if (card_mpuout->firstmidiq == NULL) + { + card_mpuout->firstmidiq = midiq; + card_mpuout->lastmidiq = midiq; + } else + { + (card_mpuout->lastmidiq)->next = midiq; + card_mpuout->lastmidiq = midiq; + } + + card_mpuout->intr = FALSE; + + sblive_irqmgrEnableIrq(sb_hw, INTE_MIDITXENABLE); + + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return CTSTATUS_SUCCESS; +} + + +#if 0 /* what is this function for? (rsousa) */ + +/****************************************************************************/ +/* int sblive_mpuoutWriteShortData(PICARDMIDI pICardMidi, */ +/* u32 handle, */ +/* u32 midimsg) */ +/* */ +/* Function: Adds the short message to our local buffer */ +/* Note: This writeout must succeed at all cost ... else UART output */ +/* will sound wierd at a sound module. */ +/****************************************************************************/ +int sblive_mpuoutWriteShortData(struct sblive_hw *sb_hw, u32 midimsg) +{ + struct sblive_mpuout *card_mpuout = sb_hw->card_mpuout; + int msglen; + struct midi_queue *midiq; + int status; + unsigned long flags; + + + if (card_mpuout->state == CARDMIDIOUT_STATE_SUSPEND) + return CTSTATUS_SUCCESS; + + if (midimsg & 0x80) + { + if (((u8) midimsg) < 0xF0) + { + card_mpuout->laststatus = (u8) midimsg; + msglen = gabMsgLenChannel[((midimsg & 0xF0) - 0x80) >> 4]; + } else + msglen = gabMsgLenSystem[midimsg & 0x0F]; + } else + { + if (card_mpuout->laststatus) + msglen = gabMsgLenChannel[((card_mpuout->laststatus & 0xF0) - 0x80) >> 4]; + else + { + DPD(2, "sblive_mpuoutWriteShortData error!!: midimsg = %x\n", midimsg); + return CTSTATUS_ERROR; + } + --msglen; + } + + if (card_mpuout->lastmidiq == NULL) + { + unsigned long flags; + /* Wait until TX interrupt has occurred. + * This means that the FIFO is empty. + */ + while (card_mpuout->intr != TRUE); + + spin_lock_irqsave(&card_mpuout->lock, flags); + + while (msglen--) + { + status = hwmpuWriteData(sb_hw, (u8)midimsg); + + if (status != CTSTATUS_SUCCESS) + { + DPD(2, "sblive_mpuoutWriteShortData error!!: byte = %x\n", (u8) midimsg); + DPD(2, "UART BYTE OUT (MISSED) = %x\n", (u8)midimsg); + msglen++; + } else + { + DPD(2, "UART BYTE OUT = %x\n", (u8)midimsg); + midimsg >>= 8; + } + } + + card_mpuout->intr = FALSE; + sblive_irqmgrEnableIrq(sb_hw, INTE_MIDITXENABLE); + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return CTSTATUS_SUCCESS; + } + + midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_KERNEL); + if (midiq == NULL) + { + /* Message lost */ + DPD(2, "sblive_mpuoutWriteShortData error!!: midimsg = %x\n", midimsg); + return CTSTATUS_NOMEMORY; + } + + DPD(3, "kmalloc: [%p]\n", midiq); + + midiq->next = NULL; + midiq->qtype = 0; + midiq->length = msglen; + midiq->sizeLeft = msglen; + midiq->midibyte = (u8 *) &midiq->refdata; + midiq->refdata = midimsg; + + spin_lock_irqsave(&card_mpuout->lock, flags); + + if (card_mpuout->firstmidiq == NULL) + { + card_mpuout->lastmidiq = midiq; + card_mpuout->firstmidiq = midiq; + } else + { + (card_mpuout->lastmidiq)->next = midiq; + card_mpuout->lastmidiq = midiq; + } + + card_mpuout->intr = FALSE; + sblive_irqmgrEnableIrq(sb_hw, INTE_MIDITXENABLE); + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return CTSTATUS_SUCCESS; +} + +#endif + +/****************************************************************************/ +/* int sblive_mpuoutDpcCallback () */ +/****************************************************************************/ +void sblive_mpuoutDpcCallback(void *refdata) +{ + int cByteSent = 0; + int status; + struct midi_queue *midiq; + struct midi_queue *doneq = NULL; + struct sblive_hw *sb_hw = (struct sblive_hw *) refdata; + struct sblive_mpuout *card_mpuout = sb_hw->card_mpuout; + unsigned long flags; + + spin_lock_irqsave(&card_mpuout->lock, flags); + + while (card_mpuout->firstmidiq != NULL) + { + midiq = card_mpuout->firstmidiq; + + while (cByteSent < 4 && midiq->sizeLeft) + { + status = hwmpuWriteData(sb_hw, *midiq->midibyte); + + if (status == CTSTATUS_SUCCESS) + { + ++cByteSent; + --midiq->sizeLeft; + ++midiq->midibyte; + } else + { + DPF(2, "sblive_mpuoutDpcCallback error!!\n"); + } + } + + if (midiq->sizeLeft == 0) + { + if (doneq == NULL) + doneq = midiq; + card_mpuout->firstmidiq = midiq->next; + } else + break; + } + + if (card_mpuout->firstmidiq == NULL) + card_mpuout->lastmidiq = NULL; + + if (doneq != NULL) + { + while (doneq != card_mpuout->firstmidiq) + { + unsigned long callback_msg[3]; + + midiq = doneq; + doneq = midiq->next; + + if (midiq->qtype) + { + callback_msg[0] = 0; + callback_msg[1] = midiq->length; + callback_msg[2] = midiq->refdata; + + midiCallbackFn(ICARDMIDI_OUTLONGDATA, card_mpuout->openinfo.refdata, callback_msg); + } else + if (((u8) midiq->refdata) < 0xF0 && + ((u8) midiq->refdata) > 0x7F) + card_mpuout->laststatus = (u8) midiq->refdata; + + kfree(midiq); + DPD(3, "kfree: [%p]\n", midiq); + } + } + + if ((card_mpuout->firstmidiq != NULL) || cByteSent) + { + card_mpuout->intr = FALSE; + sblive_irqmgrEnableIrq(sb_hw, INTE_MIDITXENABLE); + } + + spin_unlock_irqrestore(&card_mpuout->lock, flags); + + return; +} + + +/****************************************************************************/ +/* int sblive_mpuoutIrqCallback(unsigned long event, */ +/* unsigned long refdata, */ +/* unsigned long param) */ +/* */ +/* Function: IRQ callback handler routine for the MPU out port */ +/****************************************************************************/ +int sblive_mpuoutIrqCallback(struct sblive_hw *sb_hw) +{ + struct sblive_mpuout *card_mpuout = sb_hw->card_mpuout; + + /* Called during ISR. The data & code touched are: + * 1. card_mpuout + * 2. sblive_irqmgrDisableIrq() + */ + + DPF(4, "sblive_mpuoutIrqCallback\n"); + + card_mpuout->intr = TRUE; + sblive_irqmgrDisableIrq(sb_hw, INTE_MIDITXENABLE); + + queue_task(&card_mpuout->task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + return CTSTATUS_SUCCESS; +} Index: oldkernel/linux/drivers/sound/emu10k1/cardmo.h diff -u /dev/null linux/drivers/sound/emu10k1/cardmo.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/cardmo.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,70 @@ +/* + ********************************************************************** + * cardmo.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _CARDMO_H +#define _CARDMO_H + +#include "icardmid.h" + +#define IDS_EMU_MIDIOUT_PNAME "SB Live! MIDI Out" +#define CARDMIDIOUT_STATE_DEFAULT 0x00000000 +#define CARDMIDIOUT_STATE_SUSPEND 0x00000001 + +struct sblive_mpuout +{ + u32 status; + u32 state; + volatile int intr; + struct midi_queue *firstmidiq; + struct midi_queue *lastmidiq; + u8 laststatus; + struct tq_struct task; + spinlock_t lock; + struct midi_caps caps; + struct midi_openinfo openinfo; +}; + +int sblive_mpuoutInit(struct sblive_mpuout *, struct sblive_hw *); +int sblive_mpuoutExit(struct sblive_hw *); + +int sblive_mpuoutOpen(struct sblive_hw *, struct midi_openinfo *); +int sblive_mpuoutClose(struct sblive_hw *); +int sblive_mpuoutAddBuffer(struct sblive_hw *, struct midi_hdr *); + +#if 0 +int sblive_mpuoutWriteShortData(struct sblive_hw *, u32); +#endif + +int sblive_mpuoutIrqCallback(struct sblive_hw *); +void sblive_mpuoutDpcCallback(void *); + +#endif /* _CARDMO_H */ Index: oldkernel/linux/drivers/sound/emu10k1/cardwi.c diff -u /dev/null linux/drivers/sound/emu10k1/cardwi.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/cardwi.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,880 @@ +/* + ********************************************************************** + * cardwi.c - PCM input HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + + +#include "hwaccess.h" +#include "mycommon.h" +#include "mmwave.h" +#include "recmgr.h" +#include "audio.h" +#include "cardwi.h" + +/****************************************************************************/ +/* Function : sblive_waveinInit */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* sb_hw - pointer to hardware object */ +/* mixer - pointer to card mixer */ +/* carddesc - card description */ +/* */ +/* Return : CTSTATUS_SUCCESS -- successful */ +/* CTSTATUS_ERROR -- failure */ +/* */ +/* About : initialize card wave input device. */ +/****************************************************************************/ +int sblive_waveinInit(struct sblive_wavein *card_wavein, u8 *carddesc) +{ + /* Init Cardwave Caps */ + memset(card_wavein, 0, sizeof(struct sblive_wavein)); + card_wavein->caps.product_id = MM_CREATIVE_WAVEIN_PID; + card_wavein->caps.caps = CARDWAVE_IN; + card_wavein->caps.controls = 0; + card_wavein->caps.maxchannels = 2; + card_wavein->caps.minrate = 8000; + card_wavein->caps.maxrate = 48000; + strcpy(card_wavein->caps.wavedesc, carddesc); + + card_wavein->numrecordinst = 0; + card_wavein->wave_inlist = NULL; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveinExit */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* */ +/* Return : CTSTATUS_SUCCESS -- successful */ +/* CTSTATUS_ERROR -- failure */ +/* */ +/* About : exit card wave operation. */ +/****************************************************************************/ +int sblive_waveinExit(struct sblive_wavein *card_wavein) +{ + + return CTSTATUS_SUCCESS; +} + +/****************************************************************************/ +/* Function : sblive_waveinQueryFormat */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* wave_fmt - pointer to wave format object */ +/* flags - flags that identifies the format to be queried. */ +/* */ +/* About : query whether a specified wave format is supported by wave */ +/* device. */ +/****************************************************************************/ +int sblive_waveinQueryFormat(struct sblive_wavein *card_wavein, + struct wave_format *wave_fmt, u32 flags) +{ + if (flags & CARDWAVE_QF_CHANNEL) + { + /* Only support one wave input instance */ + if (card_wavein->numrecordinst > 1) + return CTSTATUS_INUSE; + } + + if (flags & CARDWAVE_QF_STEREO) + { + if((wave_fmt->channels != 2) && (wave_fmt->channels != 1)) + return CTSTATUS_BADFORMAT_STEREO; + } + + if (flags & CARDWAVE_QF_RATE) + { + /* Recording + * Sampling rates supported: + * 48kHz, 44.1kHz, 32kHz, 24kHz, 22.05kHz, 16kHz, 11.025kHz, 8kHz. + */ + + if(wave_fmt->samplingrate >= (0xBB80 + 0xAC44) / 2 ) + wave_fmt->samplingrate = 0xBB80; + else if (wave_fmt->samplingrate >= (0xAC44 + 0x7D00) / 2) + wave_fmt->samplingrate = 0xAC44; + else if (wave_fmt->samplingrate >= (0x7D00 + 0x5DC0) / 2) + wave_fmt->samplingrate = 0x7D00; + else if (wave_fmt->samplingrate >= (0x5DC0 + 0x5622) / 2) + wave_fmt->samplingrate = 0x5DC0; + else if (wave_fmt->samplingrate >= (0x5622 + 0x3E80) / 2) + wave_fmt->samplingrate = 0x5622; + else if (wave_fmt->samplingrate >= (0x3E80 + 0x2B11) / 2) + wave_fmt->samplingrate = 0x3E80; + else if (wave_fmt->samplingrate >= (0x2B11 + 0x1F40) / 2) + wave_fmt->samplingrate = 0x2B11; + else + wave_fmt->samplingrate = 0x1F40; + } + + if (flags & CARDWAVE_QF_BITS) + { + /* 16 bit and 8 bit recording are supported */ + if ((wave_fmt->bitspersample != 16) && (wave_fmt->bitspersample != 8)) + wave_fmt->bitspersample = 16; + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveinOpen */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* wave_fmt - pointer to wave format object */ +/* CallbackFn - IRQ call back function */ +/* refdata - reference data for call back function */ +/* pcallback_size - pointer to the size of data to transfer */ +/* before CallbackFn is called. */ +/* numfrags - number of buffer fragments */ +/* */ +/* Output : pcallback_size - pointer to the actual call back size. */ +/* phandle - pointer to the open handle. */ +/* */ +/* About : Open card wave device. */ +/* 1. query whether a specified wave format is supported by */ +/* device. */ +/* 2. allocate emu channel for the specified channel object. */ +/* 3. attach this wave instance to the channel object list. */ +/* 4. install wave IRQ handler. */ +/* 5. get wave transfer buffer. */ +/* 6. get wave instance format. */ +/* 7. for recording, install IRQ handler. */ +/****************************************************************************/ +int sblive_waveinOpen(struct sblive_hw *sb_hw, struct wave_format *wave_fmt, u32 *callback_size, u32 numfrags, struct wave_in **handle) +{ + struct sblive_wavein *card_wavein = sb_hw->card_wavein; + struct wave_in *wave_in; + int status; + u32 bufsiz; + u8 *buffer; + + if (!(wave_in = (struct wave_in *)kmalloc(sizeof(struct wave_in), GFP_KERNEL))) + { + DPF(2, "Failed to allocate wave instance!\n"); + return CTSTATUS_NOMEMORY; + } + + DPD(3, "kmalloc: [%p]\n", wave_in); + + /* Init wave in instance */ + wave_in->next = NULL; + wave_in->status &= ~FLAGS_AVAILABLE; + wave_in->state = CARDWAVE_STATE_STOPPED; + wave_in->synchstart = FALSE; + wave_in->wave_fmt = *wave_fmt; + wave_in->emu_voice = NULL; + wave_in->memhandle = NULL; + wave_in->callbacksize = 0; + wave_in->process_id = 0; + wave_in->setpos = FALSE; + wave_in->position = 0; + + /* Recording */ + + if (!(wave_in->rec_ptr = (struct record *) kmalloc(sizeof(struct record), GFP_KERNEL))) + { + DPF(2, "Failed to allocate recording buffer!\n"); + kfree((void *) wave_in); + return CTSTATUS_NOMEMORY; + } + + DPD(3, "kmalloc: [%p]\n", wave_in->rec_ptr); + + initRecordObject(sb_hw, wave_in); + + /* Attach this wave instance to the channel object list */ + osListAttach((struct sblive_list **) & card_wavein->wave_inlist, (struct sblive_list *) wave_in); + + if (callback_size != NULL) + wave_in->callbacksize = *callback_size; + else + wave_in->callbacksize = 0; + + bufsiz = (wave_in->callbacksize ? wave_in->callbacksize * numfrags : 0xffff); + + if ((status = sblive_emuGetRecBuffer(card_wavein, wave_in, &bufsiz, &buffer)) != CTSTATUS_SUCCESS) + { + DPF(2, "WaveOpen GetBuffer Fail"); + sblive_waveinClose(sb_hw, wave_in); + return CTSTATUS_ERROR; + } + + wave_in->callbacksize = bufsiz / numfrags; + *callback_size = bufsiz / numfrags; + + /* This callback size returned is the size in the play buffer. + * For 8-bit samples, callbacksize of user buffer should be + * half of the callbacksize in play buffer. */ + if (wave_in->wave_fmt.bitspersample == 8) + *callback_size >>= 1; + + *handle = wave_in; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveinClose */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* wave_in - open handle which is the pointer to the */ +/* specified channel object */ +/* */ +/* About : Close wave device. */ +/* 1. deallocate transfer buffer, for playback, */ +/* free emu addx space. */ +/* 2. free voice channels. */ +/* 3. uninstall wave IRQ handler. */ +/* 4. remove wave instance from channel object list. */ +/****************************************************************************/ +int sblive_waveinClose(struct sblive_hw *sb_hw, struct wave_in *wave_in) +{ + struct sblive_wavein *card_wavein= sb_hw->card_wavein; + struct wave_in *tmp; + u32 dummy; + + /* FIXME: Do we need spinlocks in here? */ + + if (wave_in->state != CARDWAVE_STATE_STOPPED) + sblive_waveinStop(sb_hw, wave_in, &dummy); + + tmp = card_wavein->wave_inlist; + while (tmp) + { + if (tmp == wave_in) + { + if (sblive_emuDeallocRecBuffer(wave_in) != CTSTATUS_SUCCESS) + { + DPF(2, "Failed to deallocate recbuffer\n"); + } + + wave_in->status |= FLAGS_AVAILABLE; + wave_in->state = CARDWAVE_STATE_STOPPED; + wave_in->synchstart = FALSE; + + kfree((void *) wave_in->rec_ptr); + + osListRemove((struct sblive_list **) & card_wavein->wave_inlist, (struct sblive_list *) wave_in); + + /* Free channel object which is allocated in sblive_waveOpen(). */ + kfree((void *) wave_in); + + break; + } + + tmp = (struct wave_in *) tmp->next; + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_emuGetRecBuffer */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* wave_in - pointer to the specified wavein instance */ +/* size - pointer to the size requested */ +/* */ +/* Output : size - pointer to the size allocated */ +/* buffer - pointer to the buffer pointer allocated */ +/* */ +/* About : alloc record buffer. */ +/****************************************************************************/ +int sblive_emuGetRecBuffer(struct sblive_wavein *card_wavein, struct wave_in *wave_in, u32 *size, u8 **buffer) +{ + DPF(2, "CARDWAVE : GetBuffer\n"); + + if (!size || !buffer) + return CTSTATUS_INVALIDPARAM; + + if (*size < wave_in->callbacksize) + { + *size = wave_in->callbacksize; + return CTSTATUS_INVALIDVALUE; + } + + /* Allocate buffer here */ + if (sblive_emuAllocRecBuffer(wave_in, size, buffer) != CTSTATUS_SUCCESS) + { + DPF(2, "CARDWAVE: allocate buffer fail."); + return CTSTATUS_ERROR; + } + + /* recbufsize contains actual record buffer size */ + *size = wave_in->rec_ptr->recbufsize; + + wave_in->rec_ptr->recbuffer = *buffer; + wave_in->rec_ptr->recpos = 0; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_emuAllocRecBuffer */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* size - pointer to the size requested */ +/* */ +/* Output : size - pointer to the size returned */ +/* buffer - pointer to the buffer pointer allocated */ +/* */ +/* About : allocate buffer for wave transfer. */ +/* recording: a) allocate page-aligned, continous PC memory */ +/* for recording buffer. */ +/* b) determine start, end, startloop and */ +/* endloop. */ +/****************************************************************************/ +int sblive_emuAllocRecBuffer(struct wave_in *wave_in, u32 *bufsize, u8 **buffer) +{ + int status; + u32 numpages, reqsize; + int i, j; + u32 size[4]; + + /* NOTE: record buffer size only can be certain sizes. If the requested + * size is not a nice size, use the smaller nearest size. The minimum size is 1k. */ + if (!wave_in->rec_ptr->is16bit) + *bufsize <<= 1; + + if (*bufsize >= 0x10000) + { + *bufsize = reqsize = 0x10000; + wave_in->rec_ptr->bufsizereg = 31; + } else + { + reqsize = 0; + size[0] = 384; + size[1] = 448; + size[2] = 512; + size[3] = 640; + + for (i = 0; i < 8; i++) + for (j = 0; j < 4; j++) + if (*bufsize >= size[j]) + { + reqsize = size[j]; + size[j] = size[j] * 2; + wave_in->rec_ptr->bufsizereg = i * 4 + j + 1; + } else + goto exitloop; + exitloop: + if (reqsize == 0) + { + reqsize = 384; + wave_in->rec_ptr->bufsizereg = 1; + } + + *bufsize = reqsize; + } + + DPD(2, "bufsizereg: %x\n", wave_in->rec_ptr->bufsizereg); + + if (wave_in->rec_ptr->is_stereo) + { + numpages = (reqsize / (PAGE_SIZE * 2)) * 2; + if (reqsize % (PAGE_SIZE * 2)) + numpages += 2; + } else + { + numpages = reqsize / PAGE_SIZE; + if (reqsize % PAGE_SIZE) + numpages += 1; + } + + reqsize = numpages * PAGE_SIZE; + + /* Recording buffer must be continuous and page-aligned */ + status = osAllocMemPhysical(reqsize, &wave_in->memhandle); + + if (status != CTSTATUS_SUCCESS) + return CTSTATUS_NOMEMORY; + + wave_in->rec_ptr->recbuffer = (u8 *) wave_in->memhandle->virtaddx; + wave_in->rec_ptr->physaddx = wave_in->memhandle->physaddx; + wave_in->rec_ptr->recbufsize = *bufsize; + + DPD(2, "recbufsize: %x\n", wave_in->rec_ptr->recbufsize); + + *buffer = (u8 *) wave_in->memhandle->virtaddx; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_emuDeallocRecBuffer */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* */ +/* About : deallocate transfer buffer */ +/* 1. for playback, free emu address space. */ +/* 2. free PC memory allocated for transfer buffer. */ +/* 3. clear VioceParam. */ +/****************************************************************************/ +int sblive_emuDeallocRecBuffer(struct wave_in *wave_in) +{ + if (wave_in->memhandle == NULL) + return CTSTATUS_ERROR; + + osFreeMemPhysical(&wave_in->memhandle); + + if (wave_in->emu_voice != NULL) + { + struct voice_param *left = &wave_in->emu_voice->voice_params; + struct voice_param *right = &wave_in->emu_voice->linked_voice->voice_params; + + /* Clear VoiceParam */ + left->start = 0; + left->startloop = 0; + left->end = 0; + left->endloop = 0; + + if (wave_in->wave_fmt.channels == 2) + { + right->start = 0; + right->startloop = 0; + right->end = 0; + right->endloop = 0; + } + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : initRecordObject */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* */ +/* About : init recording object */ +/****************************************************************************/ +/* FIXME: This should be a macro [jtaylor] */ +void initRecordObject(struct sblive_hw *sb_hw, struct wave_in *wave_in) +{ + wave_in->rec_ptr->reserved = NULL; + wave_in->rec_ptr->sb_hw = sb_hw; + wave_in->rec_ptr->recpos = 0; + wave_in->rec_ptr->recbufsize = 0; + wave_in->rec_ptr->recbuffer = NULL; + wave_in->rec_ptr->physaddx = 0; + wave_in->rec_ptr->samplingrate = wave_in->wave_fmt.samplingrate; + wave_in->rec_ptr->is_stereo = (wave_in->wave_fmt.channels == 2) ? 1 : 0; + wave_in->rec_ptr->is16bit = (wave_in->wave_fmt.bitspersample == 16) ? 1 : 0; + wave_in->rec_ptr->fSetRecSrc = FALSE; + wave_in->rec_ptr->prevadcidx = 0; + wave_in->rec_ptr->pong = FALSE; +} + + +/****************************************************************************/ +/* Function : sblive_waveinStart */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* wave_in - pointer to the specified wavein instance */ +/* */ +/* About : start wave in transfer. */ +/* recording: a) setup voices. */ +/* b) set recording source.(CCCA_CC, ADCCR) */ +/* c) enable IRQ. */ +/* d) start recording. */ +/* e) start CA (for get position). */ +/****************************************************************************/ +int sblive_waveinStart(struct sblive_wavedevice *wave_dev) +{ + struct sblive_hw *sb_hw = wave_dev->sb_hw; + struct wave_in *wave_in = wave_dev->wiinst->wave_in; + + /* If already started, return success */ + if (wave_in->state == CARDWAVE_STATE_STARTED) + return CTSTATUS_SUCCESS; + + /* Recording */ + if (recmgrInit(wave_in->rec_ptr) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + /* If recording source is not set yet, use default: AC97 ADC */ + if (!wave_in->rec_ptr->fSetRecSrc) + { + if (recmgrSetControl(wave_in->rec_ptr, WAVERECORD_AC97, 0) != CTSTATUS_SUCCESS) + return CTSTATUS_SUCCESS; + wave_in->rec_ptr->fSetRecSrc = TRUE; + } + + wave_in->task = (struct tq_struct *) kmalloc(sizeof(struct tq_struct), GFP_KERNEL); + if (wave_in->task == NULL) + { + DPF(2, "task alloc\n"); + return CTSTATUS_NOMEMORY; + } + + DPD(3, "kmalloc: [%p]\n", wave_in->task); + wave_in->task->next = NULL; + wave_in->task->sync = 0; + wave_in->task->routine = waveInCallbackFn; + wave_in->task->data = wave_dev; + + if (sblive_irqmgrEnableIrq(sb_hw, INTE_ADCBUFENABLE) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + /* Actual start */ + if (!wave_in->synchstart) + { + /* Recording */ + if (recmgrStartRecord(wave_in->rec_ptr) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + wave_in->rec_ptr->prevadcidx = recmgrGetRecIdx(wave_in->rec_ptr->sb_hw); + } + + wave_in->state = CARDWAVE_STATE_STARTED; + wave_in->setpos = FALSE; + + DPF(2, "sblive_wave Started.\n"); + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveinStop */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* wave_in - pointer to the specified wavein instance */ +/* pending - for later use */ +/* */ +/* About : stop wave transfer, disable IRQ. */ +/****************************************************************************/ +int sblive_waveinStop(struct sblive_hw *sb_hw, struct wave_in *wave_in, u32 *pending) +{ + if (wave_in->emu_voice != NULL) + if (sblive_voiceStop(wave_in->emu_voice) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + /* Recording */ + if (recmgrStopRecord(wave_in->rec_ptr) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + if (sblive_irqmgrDisableIrq(sb_hw, INTE_ADCBUFENABLE) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + if (wave_in->emu_voice != NULL) + { + struct voice_param *left = &wave_in->emu_voice->voice_params; + + left->startloop = left->start; + + if (wave_in->rec_ptr->is_stereo) + { + struct voice_param *right = &wave_in->emu_voice->linked_voice->voice_params; + + right->startloop = right->start; + } + } + + kfree(wave_in->task); + + DPD(3, "kfree: [%p]\n", wave_in->task); + + wave_in->rec_ptr->fSetRecSrc = FALSE; + wave_in->rec_ptr->recpos = 0; + wave_in->rec_ptr->pong = FALSE; + wave_in->state = CARDWAVE_STATE_STOPPED; + wave_in->process_id = 0; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveinGetXferSize */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* wave_in - pointer to the specified wavein instance */ +/* */ +/* Output : size - pointer to the transfer size */ +/* pending - pointer to the size to be transfered */ +/* */ +/* About : get the size of data in bytes that the specified wave device */ +/* can process at the time of this function is called. */ +/* */ +/* Note : this transfer size returned is referring to user buffer. */ +/****************************************************************************/ +int sblive_waveinGetXferSize(struct sblive_wavein *card_wavein, + struct wave_in *wave_in, u32 *size, u32 *pending) +{ + struct record *rec_ptr = wave_in->rec_ptr; + u32 curpos; + + /* Get position of current address, this is in no. of bytes in play buffer */ + if (sblive_waveinGetControl(card_wavein, wave_in, WAVECURPOS, &curpos) + != CTSTATUS_SUCCESS) + { + DPF(2, "sblive_waveinGetControl() failed!\n"); + return CTSTATUS_ERROR; + } + + curpos *= (rec_ptr->is_stereo + 1); + + /* Recpos is the actual position in user buffer and play buffer */ + if (curpos >= rec_ptr->recpos) + *size = curpos - rec_ptr->recpos; + else + *size = rec_ptr->recbufsize - (rec_ptr->recpos - curpos); + + /* FIXME: Handle this at runtime [jtaylor] */ +#if defined(EMUA0) + /* Only emu8010 A0 silicon needs this workaround, + * A1 silicon, take out this part. + */ + if (*size > 32) + *size -= 32; +#endif + + if (!rec_ptr->is16bit) + *size >>= 1; + + *pending = rec_ptr->recbufsize - *size; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveinXferData */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* wave_in - pointer to the specified wavein instance */ +/* Data - pointer to the data to be transfered */ +/* size - data size to be transfered(size in user buffer, */ +/* for 8-bit sample, this is different from play */ +/* buffer. */ +/* */ +/* Output : size - data size transfered */ +/* */ +/* About : transfer the data to/from the wave device. */ +/****************************************************************************/ +int sblive_waveinXferData(struct sblive_wavein *card_wavein, struct wave_in *wave_in, u8 *data, u32 *size) +{ + struct record *rec_ptr = wave_in->rec_ptr; + u32 sizeToCopy, sizeToCopyNow, sizeCopied; + + if (!data || !size) + return CTSTATUS_INVALIDPARAM; + + DPD(2, "Size request to xfer = %x\n", *size); + + sizeToCopy = min(rec_ptr->recbufsize * (rec_ptr->is16bit + 1) / 2, *size); + sizeCopied = 0; + + if (!sizeToCopy) + { + *size = 0; + return CTSTATUS_SUCCESS; + } + + while (sizeCopied < sizeToCopy) + { + sizeToCopyNow = sizeToCopy - sizeCopied; + sizeToCopyNow = min(sizeToCopyNow,(rec_ptr->recbufsize - rec_ptr->recpos) * (rec_ptr->is16bit + 1) / 2); + + if (rec_ptr->is16bit) + copy_to_user(&data[sizeCopied], &rec_ptr->recbuffer[rec_ptr->recpos], sizeToCopyNow); + else + { + u8 *DstBuf = &data[sizeCopied]; + u16 *SrcBuf = (u16 *) & rec_ptr->recbuffer[rec_ptr->recpos]; + u32 count = sizeToCopyNow; + + while (count--) + { + long lSample; + u8 bSample; + + lSample = (short) *SrcBuf++; +#ifdef RECTEST + DPD(2, " lSample -> %x", lSample); +#endif + lSample += 128; + if (lSample > 32767) + lSample = 32767; + bSample = (u8)((lSample >> 8) ^ 0x80); + copy_to_user(DstBuf, &bSample, 1); + DstBuf++; + } + } + + rec_ptr->recpos += sizeToCopyNow * 2 / (rec_ptr->is16bit + 1); + rec_ptr->recpos %= rec_ptr->recbufsize; + sizeCopied += sizeToCopyNow; + } + + *size = sizeToCopy; + + DPD(2, "Size copied = %x\n", *size); + + return CTSTATUS_SUCCESS; +} + +/****************************************************************************/ +/* Function : sblive_waveinGetControl */ +/* */ +/* Input : card_wavein - pointer to card wavein object */ +/* wave_in - pointer to the specified wavein instance */ +/* ctrlid - control type */ +/* */ +/* Output : value - pointer to the control value */ +/* */ +/* About : get the specified control value of the wave device. */ +/****************************************************************************/ +int sblive_waveinGetControl(struct sblive_wavein *card_wavein, struct wave_in *wave_in, u32 ctrlid, u32 *value) +{ + switch (ctrlid) + { + case WAVEOBJVOLUME: + case WAVEOBJREVERB: + case WAVEOBJCHORUS: + return CTSTATUS_NOTSUPPORTED; + + case WAVEQUERYACTIVEINST: + if (card_wavein->wave_inlist != NULL) + return CTSTATUS_SUCCESS; + else + return CTSTATUS_ERROR; + + default: + break; + } + + if (wave_in == NULL) + return CTSTATUS_ERROR; + + switch (ctrlid) + { + case WAVEINSTANCEVOLUME: + case WAVEINSTANCEREVERB: + case WAVEINSTANCECHORUS: + return CTSTATUS_NOTSUPPORTED; + + case WAVECURPOS: + /* There is no actual start yet */ + if (wave_in->state == CARDWAVE_STATE_STOPPED) + { + *value = 0; + break; + } + + if (wave_in->emu_voice == NULL) + { + /* No voices used for recording, get position from wall clock */ + if (recmgrGetPos(wave_in->rec_ptr, value) != CTSTATUS_SUCCESS) + { + DPF(2, "recmgrGetPos() failed!\n"); + return CTSTATUS_ERROR; + } + + *value /= (wave_in->rec_ptr->is_stereo + 1); + } + else + { + if (sblive_voiceGetControl(wave_in->emu_voice, CCCA_CURRADDR, value) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + /* There is no actual start yet */ + if (wave_in->state == CARDWAVE_STATE_STOPPED) + *value = 0; + else + { + *value -= wave_in->emu_voice->voice_params.start; + *value <<= wave_in->rec_ptr->is16bit; + } + } + + break; + + case WAVESTARTLOOP: + if (sblive_voiceGetControl(wave_in->emu_voice, PSST_LOOPSTARTADDR, value) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + *value -= wave_in->emu_voice->voice_params.start; + *value <<= wave_in->rec_ptr->is16bit; + break; + + case WAVEENDLOOP: + if (sblive_voiceGetControl(wave_in->emu_voice, DSL_LOOPENDADDR, value) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + *value -= wave_in->emu_voice->voice_params.start; + *value <<= wave_in->rec_ptr->is16bit; + break; + + case WAVEWRITEPOINTER: + /* Get write pointer for a particular wave instance */ + *value = wave_in->rec_ptr->recpos; + break; + + default: + DPF(2, "BUG: Default case\n"); + return CTSTATUS_ERROR; + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveinIrqCallback */ +/* */ +/* Input : event - event that cause the callback */ +/* refdata - reference data for this callback */ +/* param - parameter used for this callback */ +/* */ +/* About : wave IRQ callback function. */ +/****************************************************************************/ +int sblive_waveinIrqCallback(struct sblive_hw *sb_hw) +{ + struct wave_in *wave_in = sb_hw->card_wavein->wave_inlist; + + if (wave_in->state == CARDWAVE_STATE_STOPPED) + return CTSTATUS_SUCCESS; + + wave_in->rec_ptr->prevadcidx = recmgrGetRecIdx(wave_in->rec_ptr->sb_hw); + wave_in->rec_ptr->pong ^= 1; + + queue_task(wave_in->task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + return CTSTATUS_SUCCESS; +} + Index: oldkernel/linux/drivers/sound/emu10k1/cardwi.h diff -u /dev/null linux/drivers/sound/emu10k1/cardwi.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/cardwi.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,109 @@ +/* + ********************************************************************** + * cardwi.h -- header file for card wave input functions + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ +#ifndef _CARDWI_H +#define _CARDWI_H + +#include "icardwav.h" + +struct wave_in +{ + struct wave_in *next; + u32 status; + u32 state; + int synchstart; + struct emu_voice *emu_voice; + struct record *rec_ptr; + struct memhandle *memhandle; + u32 callbacksize; + u32 process_id; /* used for synchonize start */ + int setpos; + u32 position; + struct wave_format wave_fmt; + struct tq_struct *task; +}; + +struct wiinst +{ + struct wave_in *wave_in; + struct wave_format wave_fmt; + u16 ossfragshift; + u32 fragment_size; + u32 numfrags; + wait_queue_head_t wait_queue; + int mapped; + u32 total_recorded; + u32 getiptr_blocks; + spinlock_t lock; +}; + +/* sblive_wave states */ +#define CARDWAVE_STATE_STOPPED 0x0001 +#define CARDWAVE_STATE_STARTED 0x0002 +#define CARDWAVE_STATE_SUSPEND 0x0004 + +/* transfer buffer format */ +#define M8 0x00 +#define M16 0x01 +#define S8 0x02 +#define S16 0x03 + +#define INTERPOLATION_BYTES 8 +#define WAVEIN_MAXBUFSIZE 65536 +#define WAVEIN_DEFAULTFRAGLEN 50 +#define WAVEIN_DEFAULTBUFLEN 1000 + +struct sblive_wavein +{ + struct wave_caps caps; + u32 numrecordinst; + struct wave_in *wave_inlist; + u32 lineid; + u32 ctrlid; +}; + + +int sblive_waveinInit(struct sblive_wavein *, u8 *); +int sblive_waveinExit(struct sblive_wavein *); +int sblive_waveinQueryFormat(struct sblive_wavein *, struct wave_format *, u32); +int sblive_waveinOpen(struct sblive_hw *, struct wave_format *, u32 *, u32, struct wave_in **); +int sblive_waveinClose(struct sblive_hw *, struct wave_in *); +int sblive_waveinStart(struct sblive_wavedevice *); +int sblive_waveinStop(struct sblive_hw *, struct wave_in *, u32 *); +int sblive_waveinGetXferSize(struct sblive_wavein *, struct wave_in *, u32 *, u32 *); +int sblive_waveinXferData(struct sblive_wavein *, struct wave_in *, u8 *, u32 *); +int sblive_waveinGetControl(struct sblive_wavein *, struct wave_in *, u32, u32 *); +int sblive_emuAllocRecBuffer(struct wave_in *, u32 *, u8 **); +int sblive_emuGetRecBuffer(struct sblive_wavein *, struct wave_in *, u32 *, u8 **); +int sblive_emuDeallocRecBuffer(struct wave_in *); +void initRecordObject(struct sblive_hw *, struct wave_in *); + + +#endif /* _CARDWI_H */ Index: oldkernel/linux/drivers/sound/emu10k1/cardwo.c diff -u /dev/null linux/drivers/sound/emu10k1/cardwo.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/cardwo.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,1774 @@ +/* + ********************************************************************** + * cardwo.c - PCM output HAL for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "mycommon.h" +#include "mmwave.h" +#include "cardwo.h" +#include "audio.h" + +/* Volume calcs */ +static int set_volume_instance(struct sblive_waveout * card_waveout, struct wave_out * wave_out, struct voice_param * left) +{ + /* only applicable for playback */ + u32 volL, volR, vol = 0; + + volL = (wave_out->localvol & 0xffff); + volR = ((wave_out->localvol >> 16) & 0xffff); + + if (wave_out->globalvolFactor) + { + volL = ((u32) (((u16) card_waveout->globalvol & 0xffff) * (u16) volL)) / 0xffff; + volR = ((u32) (((u16) (card_waveout->globalvol >> 16) & 0xffff) * ((u16) volR))) / 0xffff; + } + + /* BIG ASSUMPTION HERE THAT DEFAULT WAVE PAN/AUX IS 0xff/0xff */ + /* New volume and pan */ + + if (volL == volR) + { + vol = volL; + left->unSends.tSends.pan_send = 0xff; + left->unSends.tSends.aux_send = 0xff; + } else + { + if (volL > volR) + { + vol = volL; + left->unSends.tSends.pan_send = 0xff; + left->unSends.tSends.aux_send = (char) ((volR * 255) / vol); + } else + { + vol = volR; + left->unSends.tSends.aux_send = 0xff; + left->unSends.tSends.pan_send = (char) ((volL * 255) / vol); + } + } + + left->initial_attn = 0xff & sumVolumeToAttenuation(vol * 2); + + return vol; +} + +static void set_volume(struct sblive_waveout * card_waveout) +{ + struct wave_out * currinst = card_waveout->wave_outlist; + struct voice_cntlset setting[3]; + + while (currinst && currinst->emu_voice) + { + struct voice_param * left = &currinst->emu_voice->voice_params; + + /* This flag allows a wave instance to be unaffected by changes to global volume. */ + + if (currinst->globalvolFactor) + { + u32 volL, volR, vol; + + /* Calculate individual channel volumes based on obj and instance volume */ + + volL = ((u32) (((u16) card_waveout->globalvol & 0xffff) * ((u16) currinst->localvol & 0xffff))) / 0xffff; + volR = ((u32) (((u16) (card_waveout->globalvol >> 16) & 0xffff) * ((u16) (currinst->localvol >> 16) & 0xffff))) / 0xffff; + + /* BIG ASSUMPTION HERE THAT DEFAULT WAVE PAN/AUX IS 0xff/0xff */ + /* New volume and pan */ + if (volL == volR) + { + vol = volL; + left->unSends.tSends.pan_send = 0xff; + left->unSends.tSends.aux_send = 0xff; + } else + { + if (volL > volR) + { + vol = volL; + left->unSends.tSends.pan_send = 0xff; + left->unSends.tSends.aux_send = (char) ((volR * 255) / vol); + } else + { + vol = volR; + left->unSends.tSends.aux_send = 0xff; + left->unSends.tSends.pan_send = (char) ((volL * 255) / vol); + } + } + + left->initial_attn = 0xff & sumVolumeToAttenuation(vol * 2); + + if (currinst->wavexferbuf->is_stereo && currinst->emu_voice->linked_voice) + { + struct voice_param * right = &currinst->emu_voice->linked_voice->voice_params; + + right->unSends.tSends.pan_send = 0; /* Left output of right channel is always zero */ + right->unSends.tSends.aux_send = left->unSends.tSends.aux_send; /* Update right channel aux */ + left->unSends.tSends.aux_send = 0; /* Zero out right output of left channel */ + right->initial_attn = 0xff & sumVolumeToAttenuation(vol * 2); /* Update right channel attenuation */ + + setting[0].paramID = PSST_FXSENDAMOUNT_C; + setting[0].value = 0; + setting[1].paramID = PTRX_FXSENDAMOUNT_B; + setting[1].value = right->unSends.tSends.aux_send; + setting[2].paramID = IFATN_ATTENUATION; + setting[2].value = right->initial_attn; + + sblive_voiceSetControl(currinst->emu_voice->linked_voice, setting, 3); + } + + setting[0].paramID = PSST_FXSENDAMOUNT_C; + setting[0].value = left->unSends.tSends.pan_send; + setting[1].paramID = PTRX_FXSENDAMOUNT_B; + setting[1].value = left->unSends.tSends.aux_send; + setting[2].paramID = IFATN_ATTENUATION; + setting[2].value = left->initial_attn; + sblive_voiceSetControl(currinst->emu_voice, setting, 3); + } + + currinst = currinst->next; + } +} + + +/****************************************************************************/ +/* Function : sblive_waveVolumeControlFn */ +/* */ +/* About : Callback function for volume control. */ +/****************************************************************************/ +int sblive_waveVolumeControlFn(u32 event, struct sblive_waveout * card_waveout, u32 left, u32 right) +{ + switch (event) + { + case 0: /* Get */ + *(u32 *)left = card_waveout->left; + *(u32 *)right = card_waveout->right; + break; + + case 1: /* Set */ + /* + ** this is a general set volume affecting all wave instances. + ** WODM_SETVOLUME should result in this controlId and affect + ** the volume of all wave instances. Apps which can set the + ** volume of individual wave instances should use + ** WAVEINSTANCEVOLUME instead + */ + card_waveout->left = left; + card_waveout->right = right; + + if (!card_waveout->mute) + { + card_waveout->globalvol = (left & 0xffff) | (right << 16); + set_volume(card_waveout); + } + break; + } + + return CTSTATUS_SUCCESS; +} + + + +/****************************************************************************/ +/* Function : sblive_waveoutInit */ +/* */ +/* Input : card_waveout - pointer to card wave out object structure */ +/* sb_hw - pointer to hardware object */ +/* */ +/* Return : CTSTATUS_SUCCESS -- successful */ +/* CTSTATUS_ERROR -- failure */ +/* */ +/* About : initialize card wave output device. */ +/****************************************************************************/ +/* FIXME: This should be a macro */ +int sblive_waveoutInit(struct sblive_waveout *card_waveout, u8 *carddesc) +{ + /* Init cardwave caps */ + memset(card_waveout, 0, sizeof(struct sblive_waveout)); + card_waveout->caps.product_id = MM_CREATIVE_WAVEOUT_PID; + card_waveout->caps.caps = CARDWAVE_OUT; + card_waveout->caps.caps |= CARDWAVE_ALLOW_DIRECTXFER; + card_waveout->caps.controls = CARDWAVE_CONTROL_VOLUME; + card_waveout->caps.maxchannels = 2; + card_waveout->caps.minrate = 100; + card_waveout->caps.maxrate = 100000; + strcpy(card_waveout->caps.wavedesc, carddesc); + + card_waveout->numplaybackinst = 0; + card_waveout->maxnumplayinst = CARDWAVE_DEFAULT_MAXPLAYINST; + card_waveout->wave_outlist = NULL; + + /* Init to invalid values */ + card_waveout->lineid = 0xeeeeeeee; + card_waveout->ctrlid = 0xffffffff; + + /* Assign default global volume, reverb, chorus */ + card_waveout->globalvol = 0xFFFFFFFF; + card_waveout->left = 0xFFFF; + card_waveout->right = 0xFFFF; + card_waveout->mute = 0; + card_waveout->globalreverb = 0xFFFFFFFF; + card_waveout->globalchorus = 0xFFFFFFFF; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveoutExit */ +/* */ +/* Input : card_waveout - pointer to card wave object structure */ +/* */ +/* Return : CTSTATUS_SUCCESS -- successful */ +/* CTSTATUS_ERROR -- failure */ +/* */ +/* About : exit card wave operation. */ +/****************************************************************************/ +int sblive_waveoutExit(struct sblive_waveout *card_waveout) +{ + DPF(2, "sblive_waveoutExit()\n"); + + return CTSTATUS_SUCCESS; +} + + +/**************************************************************************/ +/* Function : sblive_waveoutQueryFormat */ +/* */ +/* Input : card_waveout - pointer to card wave out object structure */ +/* pwave_fmt - pointer to wave format object */ +/* flags - flags that identifies the format to be queried. */ +/* */ +/* About : query whether a specified wave format is supported by wave */ +/* out device. */ +/**************************************************************************/ +int sblive_waveoutQueryFormat(struct sblive_waveout *card_waveout, struct wave_format * wave_fmt, u32 flags) +{ + if (flags & CARDWAVE_QF_CHANNEL) + if ((card_waveout->numplaybackinst) > card_waveout->maxnumplayinst) + return CTSTATUS_INUSE; + + if (flags & CARDWAVE_QF_STEREO) + if (wave_fmt->channels != 1 && wave_fmt->channels != 2) + return CTSTATUS_BADFORMAT_STEREO; + + if (flags & CARDWAVE_QF_RATE) + if (wave_fmt->samplingrate >= 0x2EE00) /* FIXME: 0x2ee00 = 192000! Is this valid??? */ + wave_fmt->samplingrate = 0x2EE00; + + if (flags & CARDWAVE_QF_BITS) + if (wave_fmt->bitspersample != 8 && wave_fmt->bitspersample != 16) + wave_fmt->bitspersample = 16; + + return CTSTATUS_SUCCESS; +} + + +/**************************************************************************/ +/* Function : sblive_waveoutOpen */ +/* */ +/* Input : card_waveout - pointer to card wave object structure */ +/* wave_fmt - pointer to wave format object */ +/* CallbackFn - IRQ call back function */ +/* refdata - reference data for call back function */ +/* fragment_size - size of buffer fragment */ +/* numfrags - number of buffer fragments */ +/* */ +/* Output : fragment_size - pointer to the actual fragment size. */ +/* handle - pointer to the open handle. */ +/* */ +/* About : Open card wave out device. */ +/* 1. query whether a specified wave format is supported by */ +/* device. */ +/* 2. allocate emu channel for the specified channel object. */ +/* 3. attach this wave instance to the channel object list. */ +/* 4. install wave IRQ handler. */ +/* 5. get wave transfer buffer. */ +/* 6. get wave instance format. */ +/**************************************************************************/ +int sblive_waveoutOpen(struct sblive_hw *sb_hw, struct wave_format *wave_fmt, u32 *fragment_size, u32 numfrags, struct wave_out **handle) +{ + struct sblive_waveout *card_waveout = sb_hw->card_waveout; + struct wave_out *wave_out; + struct voice_allocdesc voice_allocdesc; + int status; + u32 buffsize; + void **buffer; + + /* If number of wave instances is greater than the maximum instances allowed, fail the open. */ + if (card_waveout->numplaybackinst + 1 > card_waveout->maxnumplayinst) + return CTSTATUS_ERROR; + + wave_out = (struct wave_out *) kmalloc(sizeof(struct wave_out), GFP_KERNEL); + if (wave_out == NULL) + { + DPF(2, "struct wave_out alloc fail\n"); + return CTSTATUS_NOMEMORY; + } + + DPD(3, "kmalloc: [%p]\n", wave_out); + + /* Init channel object */ + wave_out->next = NULL; + wave_out->status = 0; + wave_out->state = CARDWAVE_STATE_STOPPED; + wave_out->synchstart = FALSE; + wave_out->wave_fmt = *wave_fmt; + wave_out->emu_voice = NULL; + wave_out->emuaddr = NULL; + wave_out->wavexferbuf = NULL; + wave_out->pagetable = NULL; + wave_out->callbacksize = 0; + + /* Assign default local volume */ + /* FIXME: Should we be maxing the initial values like this? */ + wave_out->localvol = 0xffffffff; + wave_out->localreverb = 0xffffffff; + wave_out->localchorus = 0xffffffff; + wave_out->globalvolFactor = 0xffff; + wave_out->globalreverbFactor = 0xffff; + wave_out->globalchorusFactor = 0xffff; + + wave_out->process_id = 0; + wave_out->setpos = FALSE; + wave_out->position = 0; + + wave_out->playflags = CARDWAVE_PLAY_LOOPING; + + wave_out->wavexferbuf = (struct wave_xferbuf *) kmalloc(sizeof(struct wave_xferbuf ), GFP_KERNEL); + + if (!wave_out->wavexferbuf) + { + DPF(2, "struct wave_xferbuf alloc failed\n"); + kfree((void *) wave_out); + return CTSTATUS_NOMEMORY; + } + + DPD(3, "kmalloc: [%p]\n", wave_out->wavexferbuf); + initWaveOutXferBuffer(wave_out); + + /* Allocate voices here, if no voices available, return error. + * Init voice_allocdesc first.*/ + + voice_allocdesc.sb_hw = sb_hw; + voice_allocdesc.ownertype = VOICEMGR_USAGE_PLAYBACK; + + voice_allocdesc.callback = NULL; + voice_allocdesc.callback_data = 0; + voice_allocdesc.numvoicereqs = 1; + voice_allocdesc.flags[0] = 0; + + if (wave_fmt->channels == 1) + voice_allocdesc.flags[0] |= VOICEMGR_FLAGS_MONO; + + if (wave_fmt->bitspersample == 16) + voice_allocdesc.flags[0] |= VOICEMGR_FLAGS_16BIT; + + voice_allocdesc.flags[0] |= VOICEMGR_FLAGS_PLAYBACK; + + status = sblive_voiceAlloc(&voice_allocdesc, &wave_out->emu_voice); + if (status != CTSTATUS_SUCCESS) + { + DPF(2, "Channel allocation fail\n"); + kfree((void *) wave_out->wavexferbuf); + kfree((void *) wave_out); + return status; + } + + DPD(2, "voicenum -> %d\n", wave_out->emu_voice->voicenum); + + /* Attach this wave instance to the channel object list */ + card_waveout->numplaybackinst++; + + osListAttach((struct sblive_list **) & card_waveout->wave_outlist, (struct sblive_list *) wave_out); + + wave_out->callbacksize = *fragment_size; + + buffsize = (*fragment_size ? *fragment_size * numfrags : 0xffff); + + if (sblive_emuGetXferBuffer(sb_hw, wave_out, &buffsize, &buffer) != CTSTATUS_SUCCESS) + { + if (buffsize < *fragment_size) + { + *fragment_size = buffsize; + sblive_waveoutClose(sb_hw, wave_out); + return CTSTATUS_INVALIDVALUE; + } + + DPF(2, "WaveOpen GetBuffer Fail\n"); + sblive_waveoutClose(sb_hw, wave_out); + return CTSTATUS_ERROR; + } + + wave_out->callbacksize = buffsize / numfrags; + *fragment_size = buffsize / numfrags; + + *handle = wave_out; + + return CTSTATUS_SUCCESS; +} + + +/**************************************************************************/ +/* Function : sblive_waveoutClose */ +/* */ +/* Input : card_waveout - pointer to card wave object structure */ +/* wave_out - pointer to the specified wave out instance */ +/* */ +/* About : Close wave device. */ +/* 1. deallocate transfer buffer, for playback, */ +/* free emu addx space. */ +/* 2. free voice channels. */ +/* 3. remove wave instance from channel object list. */ +/**************************************************************************/ +int sblive_waveoutClose(struct sblive_hw *sb_hw, struct wave_out *wave_out) +{ + struct sblive_waveout *card_waveout = sb_hw->card_waveout; + struct wave_out *tempchan; + u32 dummy; + + if (wave_out->state != CARDWAVE_STATE_STOPPED) + sblive_waveoutStop(sb_hw, wave_out, &dummy); + + tempchan = card_waveout->wave_outlist; + while (tempchan) + { + if ((tempchan->wavexferbuf->xferbuffer + == wave_out->wavexferbuf->xferbuffer) + && (tempchan != wave_out)) + break; + tempchan = (struct wave_out *) tempchan->next; + } + + osListRemove((struct sblive_list **) & card_waveout->wave_outlist, (struct sblive_list *) wave_out); + + if (tempchan == NULL) + { + if (sblive_emuDeallocXferBuffer(wave_out) != CTSTATUS_SUCCESS) + { + DPF(2, "Failed to deallocate buffer\n"); + } + + kfree((void *) wave_out->wavexferbuf); + DPD(3, "kfree: [%p]\n", wave_out->wavexferbuf); + } + + wave_out->status |= FLAGS_AVAILABLE; + wave_out->state = CARDWAVE_STATE_STOPPED; + wave_out->synchstart = FALSE; + + card_waveout->numplaybackinst--; + + if (sblive_voiceFree(wave_out->emu_voice) != CTSTATUS_SUCCESS) + { + DPF(2, "Failed to free voice\n"); + } + + kfree((void *) wave_out); + + return CTSTATUS_SUCCESS; +} + + +/**************************************************************************/ +/* Function : sblive_emuGetXferBuffer */ +/* */ +/* Input : card_waveout - pointer to card wave object */ +/* wave_out - pointer to the specified wave out instance */ +/* size - pointer to the size requested */ +/* */ +/* Output : size - pointer to the size allocated */ +/* buffer - pointer to the buffer pointer allocated */ +/* */ +/* About : alloc transfer buffer. */ +/**************************************************************************/ +int sblive_emuGetXferBuffer(struct sblive_hw *sb_hw, struct wave_out * wave_out, u32 *size, void ***buffer) +{ + DPF(2, "sblive_emuGetXferBuffer()\n"); + + if (!size || !buffer) + return CTSTATUS_INVALIDPARAM; + + if (!wave_out) + { + DPF(2, "wave_out == NULL\n"); + return CTSTATUS_ERROR; + } + + if (*size < wave_out->callbacksize) + { + *size = wave_out->callbacksize; + return CTSTATUS_INVALIDVALUE; + } + + if (sblive_emuAllocXferBuffer(sb_hw, wave_out, size, buffer) != CTSTATUS_SUCCESS) + { + DPF(2, "Allocate buffer failed\n"); + return CTSTATUS_ERROR; + } + + /* xferbufsize contains actual transfer buffer size */ + wave_out->wavexferbuf->xferbufsize = *size; + wave_out->wavexferbuf->xferbuffer = *buffer; + wave_out->wavexferbuf->xferpos = 0; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveoutSynchStart */ +/* */ +/* Input : card_waveout - pointer to card wave object */ +/* */ +/* About : Synchronize start all wave playback and recording instances */ +/* in a particular application according to process ID. */ +/* Application needs to call SYNCHSTART twice, one is to start */ +/* all wave playback instances, another to start all wave */ +/* recording instances. Because wave out and wave in are */ +/* separate devices. */ +/****************************************************************************/ +int sblive_waveoutSynchStart(struct sblive_waveout * card_waveout, u32 process_id) +{ + struct wave_out * currinst; + + currinst = card_waveout->wave_outlist; + + /* Start wave playback instances */ + while (currinst) + { + if (currinst->process_id == process_id) + { + if (sblive_voiceStart(currinst->emu_voice) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + } + currinst = currinst->next; + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_emuAllocXferBuffer */ +/* */ +/* Input : wave_out - pointer to the specified wave out instance */ +/* size - pointer to the size requested */ +/* */ +/* Output : size - pointer to the size returned */ +/* buffer - pointer to the buffer pointer allocated */ +/* */ +/* About : allocate buffer for wave out transfer. */ +/* 1. playback: a) request for emu address space. */ +/* b) allocate page-aligned PC memory for playback */ +/* buffer. */ +/* c) allocate memory for physical address table, */ +/* copy page table. */ +/* d) determine start, end, startloop and */ +/* endloop. */ +/* e) fill in virtual memory table. */ +/* f) free physical address table. */ +/****************************************************************************/ +int sblive_emuAllocXferBuffer(struct sblive_hw *sb_hw, struct wave_out *wave_out, u32 *size, void ***buffer) +{ + u32 numpages, reqsize; + struct emuaddr_allocdesc dsEmuAddxAllocDesc; + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + u32 pagecount; + + reqsize = *size; + numpages = reqsize / PAGE_SIZE; + + /* If size is not a multiple of PAGE_SIZE then we need to round up */ + if (reqsize % PAGE_SIZE) + numpages += 1; + + DPD(2, "requested pages is: %d\n", numpages); + + wavexferbuf->numpages = numpages; + + /* Only for playback, request for emu address space */ + dsEmuAddxAllocDesc.sb_hw = sb_hw; + dsEmuAddxAllocDesc.ownertype = 0; + dsEmuAddxAllocDesc.callback = 0; + dsEmuAddxAllocDesc.callback_data = 0; + + /* Support non page-aligned buffer, don't need interpolation page */ + dsEmuAddxAllocDesc.size = numpages * PAGE_SIZE; + dsEmuAddxAllocDesc.flags = 0; + + if (emu10kaddxmgrAlloc(&dsEmuAddxAllocDesc, &wave_out->emuaddr) != CTSTATUS_SUCCESS){ + DPF(2, "no memory, increase MAXPAGES\n"); + return CTSTATUS_NOMEMORY; + } + + wave_out->pagetable = (void **) kmalloc(sizeof(void *) * numpages, GFP_KERNEL); + + if (!wave_out->pagetable) { + DPF(2, "no memory\n"); + emu10kaddxmgrFree(wave_out->emuaddr); + return CTSTATUS_NOMEMORY; + } + + /* Fill in virtual memory table */ + for (pagecount = 0; pagecount < numpages; pagecount++) + { + /* Preserve emu page number */ + ((u32 *) sb_hw->virtualpagetable->virtaddx)[(wave_out->emuaddr->emustartaddr >> 11) + pagecount] &= 0x1fff; + + if (!(wave_out->pagetable[pagecount] = (void *)__get_free_page(GFP_KERNEL))) { + DPF(2, "no memory\n"); + while (pagecount--) + free_page((unsigned long)wave_out->pagetable[pagecount]); + emu10kaddxmgrFree(wave_out->emuaddr); + kfree(wave_out->pagetable); + return CTSTATUS_NOMEMORY; + } + + /* OR with physical address */ + ((u32 *) sb_hw->virtualpagetable->virtaddx)[(wave_out->emuaddr->emustartaddr >> 11) + pagecount] |= (((u32) virt_to_bus((char *) wave_out->pagetable[pagecount])) & 0xfffff000) << 1; + + DPD(2, "Physical Addx: %x\n", ((u32 *) sb_hw->virtualpagetable->virtaddx)[(wave_out->emuaddr->emustartaddr >> 11) + pagecount]); + } + + *buffer = wave_out->pagetable; + + { + struct voice_param *left = &wave_out->emu_voice->voice_params; + + reqsize = reqsize * (2 - wavexferbuf->is16bit) / (wavexferbuf->is_stereo + 1) / 2; + left->start = wave_out->emuaddr->emustartaddr; + left->start = left->start / (wavexferbuf->is_stereo + 1) * (2 - wavexferbuf->is16bit); + left->end = left->start + reqsize; + left->startloop = left->start; + left->endloop = left->end; + + DPD(2, "sblive_emuAllocXferBuf: start=%x, end=%x, startloop=%x, endloop=%x\n", + left->start, left->end, left->startloop, left->endloop); + + if (wavexferbuf->is_stereo && wave_out->emu_voice->linked_voice) + { + struct voice_param *right = &wave_out->emu_voice->linked_voice->voice_params; + + /* Do the same thing for right channel */ + right->start = left->start; + right->end = left->end; + right->startloop = left->startloop; + right->endloop = left->endloop; + } + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_emuDeallocXferBuffer */ +/* */ +/* Input : wave_out - pointer to the specified wave out instance */ +/* */ +/* About : deallocate transfer buffer */ +/* 1. for playback, free emu address space. */ +/* 2. free PC memory allocated for transfer buffer. */ +/* 3. clear VioceParam. */ +/****************************************************************************/ +int sblive_emuDeallocXferBuffer(struct wave_out *wave_out) +{ + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + int pagecount; + + /* For playback, free emu address space */ + if (wave_out->emuaddr) + emu10kaddxmgrFree(wave_out->emuaddr); + + if (wave_out->pagetable) { + for (pagecount = 0; pagecount < wavexferbuf->numpages; pagecount++) + free_page((unsigned long)wave_out->pagetable[pagecount]); + kfree(wave_out->pagetable); + wave_out->wavexferbuf->xferbuffer = NULL; + } + + /* Clear VoiceParam */ + if (wave_out->emu_voice != NULL) + { + struct voice_param *left = &wave_out->emu_voice->voice_params; + + left->start = 0; + left->startloop = 0; + left->end = 0; + left->endloop = 0; + + if ((wave_out->wave_fmt.channels == 2) && wave_out->emu_voice->linked_voice) + { + struct voice_param *right = &wave_out->emu_voice->linked_voice->voice_params; + + right->start = 0; + right->startloop = 0; + right->end = 0; + right->endloop = 0; + } + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : initWaveOutXferBuffer */ +/* */ +/* Input : wave_out - pointer to the specified wave out instance */ +/* */ +/* About : init card wave buffer structure */ +/****************************************************************************/ +/* FIXME: This should be a macor [jtaylor] */ +void initWaveOutXferBuffer(struct wave_out *wave_out) +{ + wave_out->wavexferbuf->xferpos = 0; + wave_out->wavexferbuf->xferbufsize = 0; + wave_out->wavexferbuf->numpages = 0; + wave_out->wavexferbuf->xferbuffer = NULL; + wave_out->wavexferbuf->is_stereo = (wave_out->wave_fmt.channels == 2) ? 1 : 0; + wave_out->wavexferbuf->is16bit = (wave_out->wave_fmt.bitspersample == 16) ? 1 : 0; + + if (wave_out->wave_fmt.channels == 2) + { + if (wave_out->wave_fmt.bitspersample == 16) + wave_out->wavexferbuf->format = S16; + else + wave_out->wavexferbuf->format = S8; + } else + { + if (wave_out->wave_fmt.bitspersample == 16) + wave_out->wavexferbuf->format = M16; + else + wave_out->wavexferbuf->format = M8; + } + + wave_out->wavexferbuf->conv = 0; + wave_out->wavexferbuf->interpolationbytes = INTERPOLATION_BYTES * (wave_out->wavexferbuf->is16bit + 1) * (wave_out->wavexferbuf->is_stereo + 1); + wave_out->wavexferbuf->offset = 0; + wave_out->wavexferbuf->stopposition = 0; +} + + +/****************************************************************************/ +/* Function : sblive_waveoutSetFormat */ +/* */ +/* Input : card_waveout - pointer to card wave object */ +/* wave_out - pointer to the specified wave out instance */ +/* */ +/* About : sets a new format for the wave instance */ +/****************************************************************************/ +int sblive_waveoutSetFormat(struct sblive_hw *sb_hw, struct wave_out *wave_out, struct wave_format *wave_fmt) +{ + int status; + struct voice_allocdesc voice_allocdesc; + + if (wave_out->state != CARDWAVE_STATE_STOPPED) + return CTSTATUS_ERROR; + + if ((wave_fmt->channels != wave_out->wave_fmt.channels) || + (wave_fmt->bitspersample != wave_out->wave_fmt.bitspersample)) + { + sblive_voiceFree(wave_out->emu_voice); + + /* Allocate voices here, if no voices available, return error. Init voice_allocdesc first. */ + voice_allocdesc.sb_hw = sb_hw; + voice_allocdesc.ownertype = VOICEMGR_USAGE_PLAYBACK; + + /* for P10, this is to allocate main voices, don't need IRQ callback */ + voice_allocdesc.callback = NULL; + voice_allocdesc.callback_data = 0; + voice_allocdesc.numvoicereqs = 1; + voice_allocdesc.flags[0] = 0; + + if (wave_fmt->channels == 1) + voice_allocdesc.flags[0] |= VOICEMGR_FLAGS_MONO; + if (wave_fmt->bitspersample == 16) + voice_allocdesc.flags[0] |= VOICEMGR_FLAGS_16BIT; + + voice_allocdesc.flags[0] |= VOICEMGR_FLAGS_PLAYBACK; + + if ((status = sblive_voiceAlloc(&voice_allocdesc, &wave_out->emu_voice)) != CTSTATUS_SUCCESS) + return status; + } + + wave_out->wave_fmt = *wave_fmt; + wave_out->wavexferbuf->is_stereo = (wave_out->wave_fmt.channels == 2) ? 1 : 0; + wave_out->wavexferbuf->is16bit = (wave_out->wave_fmt.bitspersample == 16) ? 1 : 0; + + if (wave_out->wave_fmt.channels == 2) + { + if (wave_out->wave_fmt.bitspersample == 16) + wave_out->wavexferbuf->format = S16; + else + wave_out->wavexferbuf->format = S8; + } else + { + if (wave_out->wave_fmt.bitspersample == 16) + wave_out->wavexferbuf->format = M16; + else + wave_out->wavexferbuf->format = M8; + } + + wave_out->wavexferbuf->interpolationbytes = INTERPOLATION_BYTES * (wave_out->wavexferbuf->is16bit + 1) * (wave_out->wavexferbuf->is_stereo + 1); + + { + u32 reqsize; + struct wave_xferbuf * wavexferbuf = wave_out->wavexferbuf; + struct voice_param * left = &wave_out->emu_voice->voice_params; + + /* emu address space is in words, use start and end for actual + * emu start and end address allocated, startloop and endloop + * for loop points. startloop and endloop contain number of samples. */ + reqsize = wavexferbuf->xferbufsize * (2 - wavexferbuf->is16bit) / (wavexferbuf->is_stereo + 1) / 2; + left->start = wave_out->emuaddr->emustartaddr; + left->start = left->start / (wavexferbuf->is_stereo + 1) * (2 - wavexferbuf->is16bit); + left->end = left->start + reqsize; + left->startloop = left->start; + left->endloop = left->end; + + DPD(2, "SetFormat: start=%x, end=%x, startloop=%x, endloop=%x\n", + left->start, left->end, left->startloop, left->endloop); + + if (wavexferbuf->is_stereo && wave_out->emu_voice->linked_voice) + { + struct voice_param *right = &wave_out->emu_voice->linked_voice->voice_params; + + /* Do the same thing for right channel */ + right->start = left->start; + right->end = left->end; + right->startloop = left->startloop; + right->endloop = left->endloop; + } + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveoutStart */ +/* */ +/* Input : card_waveout - pointer to card wave object */ +/* wave_out - pointer to the specified wave out instance */ +/* */ +/* About : start wave transfer. */ +/* playback: a) setup voices. b) set volume. */ +/* c) enable IRQ. d) start playback. */ +/****************************************************************************/ +int sblive_waveoutStart(struct sblive_wavedevice *wave_dev) +{ + struct sblive_hw *sb_hw = wave_dev->sb_hw; + struct wave_out *wave_out = wave_dev->woinst->wave_out; + struct sblive_waveout * card_waveout= sb_hw->card_waveout; + struct voice_param *left = &wave_out->emu_voice->voice_params; + u32 start, startPosition; + u32 delay, bytespersec; + + /* If already started, return success */ + if (wave_out->state == CARDWAVE_STATE_STARTED) + return CTSTATUS_SUCCESS; + + /* Calculate pitch */ + left->initial_pitch = (u16) (srToPitch(wave_out->wave_fmt.samplingrate) >> 8); + + DPD(2, "Initial pitch --> %x\n", left->initial_pitch); + + /* Easy way out.. gotta calculate value */ + left->pitch_target = 0; + left->volume_target = 0; + left->FC_target = 0; + + left->byampl_env_sustain = 0x7f; + left->byampl_env_decay = 0x7f; + + if (wave_out->globalreverbFactor) + { + u8 t = (card_waveout->globalreverb & 0xff) + (wave_out->localreverb & 0xff); + left->unSends.tSends.reverb_send = (t > 255) ? 255 : t; + } else + { + left->unSends.tSends.reverb_send = 0; + } + + if (wave_out->globalchorusFactor) + { + u8 t = (card_waveout->globalchorus & 0xff) + (wave_out->localchorus & 0xff); + left->unSends.tSends.chorus_send = (t > 255) ? 255 : t; + } else + { + left->unSends.tSends.chorus_send = 0; + } + + set_volume_instance(card_waveout, wave_out, left); + + left->pan_target = left->unSends.tSends.pan_send; + left->aux_target = left->unSends.tSends.aux_send; + + if (wave_out->wavexferbuf->is_stereo && wave_out->emu_voice->linked_voice) + { + struct voice_param *right = &wave_out->emu_voice->linked_voice->voice_params; + + right->initial_pitch = left->initial_pitch; + + /* Easy way out.. gotta calculate value */ + right->pitch_target = 0; + right->volume_target = 0; + right->FC_target = 0; + + right->byampl_env_sustain = 0x7f; + right->byampl_env_decay = 0x7f; + + right->unSends.tSends.chorus_send = left->unSends.tSends.chorus_send; + right->unSends.tSends.reverb_send = left->unSends.tSends.reverb_send; + + /* Left output of right channel is always zero */ + right->unSends.tSends.pan_send = 0; + + /* Update right channel aux */ + right->pan_target = 0; + right->unSends.tSends.aux_send = left->unSends.tSends.aux_send; + right->aux_target = right->unSends.tSends.aux_send; + + /* Zero out right output of left channel */ + left->unSends.tSends.aux_send = 0; + left->aux_target = 0; + + /* Update right channel attenuation */ + right->initial_attn = left->initial_attn; + } + + if (wave_out->state == CARDWAVE_STATE_STOPPED && wave_out->setpos) + startPosition = wave_out->position / (wave_out->wavexferbuf->is16bit + 1) / (wave_out->wavexferbuf->is_stereo + 1); + else + startPosition = wave_out->wavexferbuf->stopposition; + + start = wave_out->emu_voice->voice_params.start; + wave_out->emu_voice->voice_params.start += startPosition; + + DPD(2, "CA is %x\n", wave_out->emu_voice->voice_params.start); + + if (sblive_voicePlaybackSetup(wave_out->emu_voice) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + wave_out->emu_voice->voice_params.start = start; + + wave_out->task = (struct tq_struct *) kmalloc(sizeof(struct tq_struct), GFP_KERNEL); + if (wave_out->task == NULL) + { + DPF(2, "task alloc\n"); + return CTSTATUS_NOMEMORY; + } + + DPD(3, "kmalloc: [%p]\n", wave_out->task); + wave_out->task->next = NULL; + wave_out->task->sync = 0; + wave_out->task->routine = waveOutCallbackFn; + wave_out->task->data = wave_dev; + + bytespersec = wave_out->wave_fmt.channels * (wave_out->wave_fmt.bitspersample >> 3) * (wave_out->wave_fmt.samplingrate); + delay = (48000 * wave_out->callbacksize) / bytespersec; + + if (sblive_timerinstall(sb_hw, &wave_out->timer, wave_out->task, delay / 2) != CTSTATUS_SUCCESS) + { + sblive_voiceStop(wave_out->emu_voice); + return CTSTATUS_ERROR; + } + + /* Actual start */ + if (!wave_out->synchstart) + if (sblive_voiceStart(wave_out->emu_voice) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + wave_out->state = CARDWAVE_STATE_STARTED; + wave_out->setpos = FALSE; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveoutStop */ +/* */ +/* Input : card_waveout - pointer to card wave object */ +/* wave_out - pointer to the specified wave out instance */ +/* ppending - for later use */ +/* */ +/* About : stop wave transfer, disable IRQ. */ +/****************************************************************************/ +int sblive_waveoutStop(struct sblive_hw *sb_hw, struct wave_out *wave_out, u32 *pending) +{ + struct sblive_waveout *card_waveout = sb_hw->card_waveout; + struct wave_xferbuf *wavexferbuf; + u32 dummy; + u32 samples = 32; + u32 position; + + if (!wave_out) + return CTSTATUS_INVALIDPARAM; + + wavexferbuf = wave_out->wavexferbuf; + + if (wave_out->state == CARDWAVE_STATE_STOPPING) + return CTSTATUS_SUCCESS; + + if (!wave_out->emu_voice) + { + wave_out->state = CARDWAVE_STATE_STOPPED; + return CTSTATUS_SUCCESS; + } + + if (wave_out->state == CARDWAVE_STATE_STOPPED) + return CTSTATUS_SUCCESS; + + wave_out->state = CARDWAVE_STATE_STOPPING; + + sblive_waveoutGetXferSize(card_waveout, wave_out, &dummy, pending); + + /* Save the stop position */ + if (sblive_voiceGetControl(wave_out->emu_voice, CCCA_CURRADDR, &wave_out->wavexferbuf->stopposition) != CTSTATUS_SUCCESS) + { + wave_out->wavexferbuf->stopposition = 0; + return CTSTATUS_ERROR; + } + + wave_out->wavexferbuf->stopposition -= wave_out->emu_voice->voice_params.start; + + /* Refer to voicemgr.c, CA is not started at zero. We need to take this into account. */ + position = wave_out->wavexferbuf->stopposition * (wave_out->wavexferbuf->is16bit + 1) * (wave_out->wavexferbuf->is_stereo + 1); + + if (!wave_out->wavexferbuf->is16bit) + samples <<= 1; + + if (wave_out->wavexferbuf->is_stereo) + samples <<= 1; + + samples -= 4; + + if (position >= samples * (wave_out->wavexferbuf->is16bit + 1)) + position -= samples * (wave_out->wavexferbuf->is16bit + 1); + else + position += wave_out->wavexferbuf->xferbufsize - samples * (wave_out->wavexferbuf->is16bit + 1); + + wave_out->wavexferbuf->stopposition = position / (wave_out->wavexferbuf->is16bit + 1) / (wave_out->wavexferbuf->is_stereo + 1); + + DPD(2, "sblive_waveoutstop, position is %x\n", wave_out->wavexferbuf->stopposition); + + /* Stop actual voice */ + if (sblive_voiceStop(wave_out->emu_voice) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + if (sblive_timeruninstall(sb_hw, wave_out->timer) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + kfree(wave_out->task); + + DPD(3, "kfree: [%p]\n", wave_out->task); + + wave_out->state = CARDWAVE_STATE_STOPPED; + wave_out->process_id = 0; + wave_out->setpos = FALSE; + wave_out->position = 0; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveoutGetXferSize */ +/* */ +/* Input : card_waveout - pointer to card wave object */ +/* wave_out - pointer to the specified wave out instance */ +/* */ +/* Output : size - pointer to the transfer size */ +/* pending - pointer to the size to be transfered */ +/* */ +/* About : get the size of data in bytes that the specified wave device */ +/* can process at the time of this function is called. */ +/* */ +/* Note : this transfer size returned is referring to user buffer. */ +/****************************************************************************/ +int sblive_waveoutGetXferSize(struct sblive_waveout *card_waveout, struct wave_out *wave_out, u32 *size, u32 *pending) +{ + u32 curpos; + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + + /* Get position of current address, this is in no. of bytes in play buffer */ + if (sblive_waveoutGetControl(card_waveout, wave_out, WAVECURPOS, &curpos) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + if ((curpos > wavexferbuf->xferpos) || ((curpos == wavexferbuf->xferpos) && (wave_out->state == CARDWAVE_STATE_STARTED)) + || ((curpos == wavexferbuf->xferpos) && (wavexferbuf->xferpos != 0) && (wave_out->state == CARDWAVE_STATE_STOPPED))) + { + *size = curpos - wavexferbuf->xferpos; + *pending = wavexferbuf->xferbufsize - *size; + } else + { + *pending = wavexferbuf->xferpos - curpos; + *size = wavexferbuf->xferbufsize - *pending; + } + + return CTSTATUS_SUCCESS; +} + +static void copy_block(u32 dst, u8 *src, u32 len, void **pt) +{ + int i, j, k; + i = dst / 4096; + j = dst % 4096; + k = (len > 4096 - j) ? 4096 - j : len; + copy_from_user(pt[i] + j, src, k); + len -= k; + while (len >= 4096) { + copy_from_user(pt[++i], src + k, 4096); + k += 4096; + len -= 4096; + } + copy_from_user(pt[++i], src + k, len); +} + +static void fill_block(u32 dst, u8 val, u32 len, void **pt) +{ + int i, j, k; + i = dst / 4096; + j = dst % 4096; + k = (len > 4096 - j) ? 4096 -j : len; + memset(pt[i] + j, val, k); + len -= k; + while (len >= 4096) { + memset(pt[++i], val, 4096); + len -= 4096; + } + memset(pt[++i], val, len); +} + +/**************************************************************************/ +/* Function : sblive_waveoutXferData */ +/* */ +/* Input : card_waveout - pointer to card wave object */ +/* wave_out - pointer to the specified wave out instance */ +/* data - pointer to the data to be transfered */ +/* size - data size to be transfered(size in user buffer, */ +/* for 8-bit sample, this is different from play */ +/* buffer. */ +/* */ +/* Output : size - data size transfered */ +/* */ +/* About : transfer the data to/from the wave device. */ +/**************************************************************************/ +int sblive_waveoutXferData(struct sblive_waveout *card_waveout, struct wave_out *wave_out, u8 *data, u32 *size) +{ + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + u32 sizetocopy, sizetocopynow; + + /* FIXME: Do we need spinlocks in here? */ + + if (!data || !size) + return CTSTATUS_INVALIDPARAM; + + sizetocopy = min(wavexferbuf->xferbufsize, *size); + *size = sizetocopy; + + if (!sizetocopy) + return CTSTATUS_SUCCESS; + + sizetocopynow = wavexferbuf->xferbufsize - wavexferbuf->xferpos; + + if(sizetocopy > sizetocopynow) + { + copy_block(wavexferbuf->xferpos, data, sizetocopynow, wavexferbuf->xferbuffer); + sizetocopy -= sizetocopynow; + copy_block(0, data + sizetocopynow, sizetocopy, wavexferbuf->xferbuffer); + wavexferbuf->xferpos = sizetocopy; + } + else + { + copy_block(wavexferbuf->xferpos, data, sizetocopy, wavexferbuf->xferbuffer); + if(sizetocopy == sizetocopynow) + wavexferbuf->xferpos = 0; + else + wavexferbuf->xferpos += sizetocopy; + } + + return CTSTATUS_SUCCESS; +} + + +/**************************************************************************/ +/* Function : sblive_waveoutFillSilence */ +/* */ +/* Input : card_waveout - pointer to card wave object */ +/* wave_out - pointer to the specified wave out instance */ +/* size - data size to be filled */ +/* */ +/* Output : size - data size filled */ +/* */ +/* About : fill silent data to play buffer,only used for wave output. */ +/**************************************************************************/ +int sblive_waveoutFillSilence(struct sblive_waveout *card_waveout, struct wave_out *wave_out, u32 *size) +{ + struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf; + u16 wFillData; + u32 sizetocopy, sizetocopyNow, sizecopied; + + /* FIXME: Do we need spinlocks here? */ + + if (!size) + return CTSTATUS_INVALIDPARAM; + + sizetocopy = min(wavexferbuf->xferbufsize, *size); + sizecopied = 0; + + if (!sizetocopy) + { + *size = 0; + return CTSTATUS_SUCCESS; + } + + if (wave_out->wave_fmt.bitspersample == 8) + wFillData = 0x8080; + else + wFillData = 0x0000; + + while (sizecopied < sizetocopy) + { + sizetocopyNow = sizetocopy - sizecopied; + sizetocopyNow = min(sizetocopyNow, wavexferbuf->xferbufsize - wavexferbuf->xferpos); + + fill_block(wavexferbuf->xferpos, wFillData, sizetocopyNow, wavexferbuf->xferbuffer); + + wavexferbuf->xferpos += sizetocopyNow; + wavexferbuf->xferpos %= wavexferbuf->xferbufsize; + sizecopied += sizetocopyNow; + } + + *size = sizetocopy; + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveoutSetControl */ +/* */ +/* Input : pICardWave - pointer to card wave object */ +/* wave_out - pointer to the specified wave out instance */ +/* ctrl_id - control type */ +/* value - the value of the specified control to be set */ +/* */ +/* About : set the specified control value of the wave device. */ +/****************************************************************************/ +int sblive_waveoutSetControl(struct sblive_hw *sb_hw, struct wave_out *wave_out, u32 ctrl_id, u32 *control_value) +{ + struct sblive_waveout *card_waveout = sb_hw->card_waveout; + struct wave_out *currinst = NULL; + u32 value; + struct voice_cntlset setting[6]; + + if (card_waveout == NULL) + return CTSTATUS_ERROR; + + if (control_value) + value = *control_value; + else + value = 0; + + switch (ctrl_id) + { + case WAVEOBJVOLUME: + return CTSTATUS_SUCCESS; + + case WAVEOBJMUTE: + /* This is a general mute affecting all wave instances */ + card_waveout->mute = value; + currinst = card_waveout->wave_outlist; + + if (value) + { + /* Mute */ + card_waveout->globalvol = 0; + } else + { + /* Unmute */ + card_waveout->globalvol = (0xffff & card_waveout->left) | ((0xffff & card_waveout->right) << 16); + + set_volume(card_waveout); + } + + return CTSTATUS_SUCCESS; + + case WAVEOBJREVERB: + /* This is a general set reverb affecting all wave instances. + * Apps which can set the reverb of individual wave instances + * should use WAVEINSTANCEREVERB instead. */ + + card_waveout->globalreverb = value; + currinst = card_waveout->wave_outlist; + + while (currinst && currinst->emu_voice) + { + struct voice_param * left = &currinst->emu_voice->voice_params; + + /* This flag allows a wave instance to be unaffected by changes to global volume. */ + if (currinst->globalreverbFactor) + { + u8 t = (card_waveout->globalreverb & 0xff) + (currinst->localreverb & 0xff); + left->unSends.tSends.reverb_send = (t > 255) ? 255 : t; + + if (currinst->wavexferbuf->is_stereo && currinst->emu_voice->linked_voice) + { + struct voice_param *right = &currinst->emu_voice->linked_voice->voice_params; + + right->unSends.tSends.reverb_send = left->unSends.tSends.reverb_send; + + setting[0].paramID = PTRX_FXSENDAMOUNT_A; + setting[0].value = right->unSends.tSends.reverb_send; + + sblive_voiceSetControl(currinst->emu_voice->linked_voice, setting, 1); + } + + setting[0].paramID = PTRX_FXSENDAMOUNT_A; + setting[0].value = left->unSends.tSends.reverb_send; + + sblive_voiceSetControl(currinst->emu_voice, setting, 1); + } + currinst = currinst->next; + } + return CTSTATUS_SUCCESS; + + case WAVEOBJCHORUS: + /* This is a general set chorus affecting all wave instances. + * Apps which can set the chorus of individual wave instances + * should use WAVEINSTANCECHORUS instead. */ + + card_waveout->globalchorus = value; + currinst = card_waveout->wave_outlist; + + while (currinst && currinst->emu_voice) + { + struct voice_param *left = &currinst->emu_voice->voice_params; + + if (currinst->globalchorusFactor) + { + u8 t = (card_waveout->globalchorus & 0xff) + (currinst->localchorus & 0xff); + left->unSends.tSends.chorus_send = (t > 255) ? 255 : t; + + if (currinst->wavexferbuf->is_stereo && currinst->emu_voice->linked_voice) + { + struct voice_param *right = &currinst->emu_voice->linked_voice->voice_params; + + right->unSends.tSends.chorus_send = left->unSends.tSends.chorus_send; + + setting[0].paramID = DSL_FXSENDAMOUNT_D; + setting[0].value = right->unSends.tSends.chorus_send; + + sblive_voiceSetControl(currinst->emu_voice->linked_voice, setting, 1); + } + setting[0].paramID = DSL_FXSENDAMOUNT_D; + setting[0].value = left->unSends.tSends.chorus_send; + + sblive_voiceSetControl(currinst->emu_voice, setting, 1); + } + currinst = currinst->next; + } + return CTSTATUS_SUCCESS; + + case WAVESYNCHSTART: + if (sblive_waveoutSynchStart(card_waveout, value) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + return CTSTATUS_SUCCESS; + + default: + break; + } + + if (!wave_out) + return CTSTATUS_ERROR; + + switch (ctrl_id) + { + case WAVECURPOS: + if (wave_out->emu_voice == NULL) + return CTSTATUS_ERROR; + + if (wave_out->state == CARDWAVE_STATE_STOPPED) + { + wave_out->setpos = TRUE; + wave_out->position = value; + } else + { + struct voice_param * left = &wave_out->emu_voice->voice_params; + /* struct wave_xferbuf * wavexferbuf = wave_out->wavexferbuf; */ + + if (wave_out->wave_fmt.bitspersample == 8) + value <<= 1; + if (wave_out->wave_fmt.channels == 2) + value >>= 1; + /* get no. of samples per channel */ + value >>= 1; + + /* if the position is already beyond the loop, return error. */ + /* we could check this value before lock can't we ? */ + if (value >= (left->end - left->start)) + return CTSTATUS_ERROR; + + /* Set current position */ + setting[0].paramID = CCCA_CURRADDR; + setting[0].value = value + left->start; + sblive_voiceSetControl(wave_out->emu_voice, setting, 1); + + if (wave_out->wavexferbuf->is_stereo && wave_out->emu_voice->linked_voice) + { + setting[0].paramID = CCCA_CURRADDR; + setting[0].value = value + wave_out->emu_voice->linked_voice->voice_params.start; + + sblive_voiceSetControl(wave_out->emu_voice->linked_voice, setting, 1); + } + } + break; + + case WAVESTOPPOSITION: + wave_out->wavexferbuf->stopposition = value; + break; + + case WAVEWRITEPOINTER: + /* For wave output device, set write pointer for a particular wave instance. + * For wave input device, set the offset of write pointer. */ + wave_out->wavexferbuf->xferpos = value; + break; + + case WAVEINSTANCEVOLUME: + /* This is a specific set volume affecting only 1 wave instance and can be only called by the owner. + * Apps which can set the volume of individual wave instances should use this message. */ + + /* update wave instance volume */ + wave_out->localvol = value; + + if (wave_out->emu_voice != NULL) + { + u32 vol; + struct voice_param * left = &wave_out->emu_voice->voice_params; + + vol = set_volume_instance(card_waveout, wave_out, left); + + if (wave_out->wavexferbuf->is_stereo && wave_out->emu_voice->linked_voice) + { + struct voice_param *right = &wave_out->emu_voice->linked_voice->voice_params; + + right->unSends.tSends.pan_send = 0; /* Left output of right channel is always zero */ + right->unSends.tSends.aux_send = left->unSends.tSends.aux_send; /* Update right channel aux */ + left->unSends.tSends.aux_send = 0; /* Zero out right output of left channel */ + right->initial_attn = 0xff & sumVolumeToAttenuation(vol * 2); /* Update right channel attenuation */ + + setting[0].paramID = IFATN_ATTENUATION; + setting[0].value = right->initial_attn; + + sblive_voiceSetControl(wave_out->emu_voice->linked_voice, setting, 1); + } + + setting[0].paramID = IFATN_ATTENUATION; + setting[0].value = left->initial_attn; + + sblive_voiceSetControl(wave_out->emu_voice, setting, 1); + } + break; + + + case WAVESYNCHSETUP: + wave_out->process_id = value; + wave_out->synchstart = TRUE; + break; + + case WAVESETFREQUENCY: + if (wave_out->emu_voice != NULL) + { + struct voice_param * left = &wave_out->emu_voice->voice_params; + + left->initial_pitch = (u16) (srToPitch(value) >> 8); /* Calculate pitch */ + + setting[0].paramID = IP; + setting[0].value = left->initial_pitch; + sblive_voiceSetControl(wave_out->emu_voice, setting, 1); + + if (wave_out->wavexferbuf->is_stereo && wave_out->emu_voice->linked_voice) + { + struct voice_param *right = &wave_out->emu_voice->linked_voice->voice_params; + + right->initial_pitch = left->initial_pitch; + setting[0].paramID = IP; + setting[0].value = right->initial_pitch; + + sblive_voiceSetControl(wave_out->emu_voice->linked_voice, setting, 1); + } + } + wave_out->wave_fmt.samplingrate = value; + break; + + case WAVESTARTLOOP: + if (wave_out->emu_voice != NULL) + { + struct voice_param *left = &wave_out->emu_voice->voice_params; + + setting[0].paramID = PSST_LOOPSTARTADDR; + setting[0].value = left->startloop; + + sblive_voiceSetControl(wave_out->emu_voice, setting, 1); + + if (wave_out->wavexferbuf->is_stereo && wave_out->emu_voice->linked_voice) + { + struct voice_param *right = &wave_out->emu_voice->linked_voice->voice_params; + + setting[0].paramID = PSST_LOOPSTARTADDR; + setting[0].value = right->startloop; + + sblive_voiceSetControl(wave_out->emu_voice->linked_voice, setting, 1); + } + } + break; + + case WAVEINSTANCEFORMAT: + return sblive_waveoutSetFormat(sb_hw, wave_out, (struct wave_format *) control_value); + case WAVEVOLFACTOR: + wave_out->globalvolFactor = value; + break; + + case WAVEREVERBFACTOR: + wave_out->globalreverbFactor = value; + break; + + case WAVECHORUSFACTOR: + wave_out->globalchorusFactor = value; + break; + + case WAVESETSTARTFLAG: + if (wave_out->playflags != value && (wave_out->state == CARDWAVE_STATE_STARTED)) + { + struct sblive_hw *sb_hw = wave_out->emu_voice->sb_hw; + + if (value & CARDWAVE_PLAY_LOOPING) + { + halClearStopOnLoop(sb_hw, wave_out->emu_voice->voicenum); + if (wave_out->emu_voice->linked_voice) + halClearStopOnLoop(sb_hw, wave_out->emu_voice->linked_voice->voicenum); + } else + { + halSetStopOnLoop(sb_hw, wave_out->emu_voice->voicenum); + if (wave_out->emu_voice->linked_voice) + halSetStopOnLoop(sb_hw, wave_out->emu_voice->linked_voice->voicenum); + } + } + wave_out->playflags = value; + break; + + case WAVESETSTOPONLOOP: + if (wave_out->state == CARDWAVE_STATE_STARTED) + { + struct sblive_hw * sb_hw = wave_out->emu_voice->sb_hw; + u32 CA, offset, endloop; + struct voice_param * left = &wave_out->emu_voice->voice_params; + struct voice_cntlset setting[3]; + + CA = sblive_readptr(sb_hw, CCCA_CURRADDR, wave_out->emu_voice->voicenum) - left->startloop; + offset = (CA / wave_out->callbacksize) * wave_out->callbacksize; + + if (CA % wave_out->callbacksize) + offset += wave_out->callbacksize; + + if (CA > (offset - 32)) + offset += 32; + + offset = CA; + endloop = offset + left->startloop; + setting[0].paramID = DSL_LOOPENDADDR; + setting[0].value = endloop; + + sblive_voiceSetControl(wave_out->emu_voice, setting, 1); + + if (wave_out->wavexferbuf->is_stereo && wave_out->emu_voice->linked_voice) + { + struct voice_param *right = &wave_out->emu_voice->linked_voice->voice_params; + + endloop = offset + right->startloop; + setting[0].paramID = DSL_LOOPENDADDR; + setting[0].value = endloop; + + sblive_voiceSetControl(wave_out->emu_voice, setting, 1); + } + + halSetStopOnLoop(sb_hw, wave_out->emu_voice->voicenum); + + if (wave_out->emu_voice->linked_voice) + halSetStopOnLoop(sb_hw, wave_out->emu_voice->linked_voice->voicenum); + } + break; + + default: + return CTSTATUS_NOTSUPPORTED; + } + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* Function : sblive_waveoutGetControl */ +/* */ +/* Input : card_waveout - pointer to card wave object */ +/* wave_out - pointer to the specified wave out instance */ +/* ctrl_id - control type */ +/* */ +/* Output : value - pointer to the control value */ +/* */ +/* About : get the specified control value of the wave device. */ +/****************************************************************************/ +int sblive_waveoutGetControl(struct sblive_waveout *card_waveout, struct wave_out *wave_out, u32 ctrl_id, u32 *value) +{ + switch (ctrl_id) + { + case WAVEOBJVOLUME: + *value = card_waveout->globalvol; + return CTSTATUS_SUCCESS; + + case WAVEOBJREVERB: + *value = card_waveout->globalreverb; + return CTSTATUS_SUCCESS; + + case WAVEOBJCHORUS: + *value = card_waveout->globalchorus; + return CTSTATUS_SUCCESS; + + case WAVEQUERYACTIVEINST: + if (card_waveout->wave_outlist != NULL) + return CTSTATUS_SUCCESS; + else + return CTSTATUS_ERROR; + + default: + break; + } + + if (!wave_out) + return CTSTATUS_ERROR; + + switch (ctrl_id) + { + case WAVECURPOS: + if (wave_out->emu_voice == NULL) + return CTSTATUS_ERROR; + + /* There is no actual start yet */ + if (wave_out->state == CARDWAVE_STATE_STOPPED || wave_out->state == CARDWAVE_STATE_SUSPEND) + { + if (wave_out->setpos) + *value = wave_out->position; + else + *value = wave_out->wavexferbuf->stopposition * (wave_out->wavexferbuf->is_stereo + 1) * (wave_out->wavexferbuf->is16bit + 1); + } else + { + if (sblive_voiceGetControl(wave_out->emu_voice, CCCA_CURRADDR, value) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + *value -= wave_out->emu_voice->voice_params.start; + + /* Get number of bytes in play buffer per channel. + * If 8 bit mode is enabled, this needs to be changed. */ + { + u32 samples = 64 * (wave_out->wavexferbuf->is_stereo + 1); + + *value *= (wave_out->wavexferbuf->is_stereo + 1) * (wave_out->wavexferbuf->is16bit + 1); + + /* Refer to voicemgr.c, CA is not started at zero. + * We need to take this into account. */ + + samples -= 4 * (wave_out->wavexferbuf->is16bit + 1); + + if (*value >= samples) + *value -= samples; + else + *value += wave_out->wavexferbuf->xferbufsize - samples; + } + } + + break; + + case WAVEWRITEPOINTER: + /* Get write pointer for a particular wave instance */ + *value = wave_out->wavexferbuf->xferpos; + break; + + case WAVEINSTANCEVOLUME: + *value = wave_out->localvol; + return CTSTATUS_SUCCESS; + + case WAVEINSTANCEREVERB: + *value = wave_out->localreverb; + return CTSTATUS_SUCCESS; + + case WAVEINSTANCECHORUS: + *value = wave_out->localchorus; + return CTSTATUS_SUCCESS; + + case WAVESTARTLOOP: + if (wave_out->emu_voice == NULL) + return CTSTATUS_ERROR; + + if (sblive_voiceGetControl(wave_out->emu_voice, PSST_LOOPSTARTADDR, value) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + *value -= wave_out->emu_voice->voice_params.start; + + /* Get number of bytes in play buffer per channel. + * If 8 bit mode is enabled, this needs to be changed. */ + *value <<= 1; + *value *= (wave_out->wavexferbuf->is_stereo + 1); + *value /= (2 - wave_out->wavexferbuf->is16bit); + break; + + case WAVEENDLOOP: + if (wave_out->emu_voice == NULL) + return CTSTATUS_ERROR; + + if (sblive_voiceGetControl(wave_out->emu_voice, DSL_LOOPENDADDR, value) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + *value -= wave_out->emu_voice->voice_params.start; + + /* Get number of bytes in play buffer per channel. + * Iif 8 bit mode is enabled, this needs to be changed. */ + *value <<= 1; + *value *= (wave_out->wavexferbuf->is_stereo + 1); + *value /= (2 - wave_out->wavexferbuf->is16bit); + break; + + default: + return CTSTATUS_NOTSUPPORTED; + } + + return CTSTATUS_SUCCESS; +} Index: oldkernel/linux/drivers/sound/emu10k1/cardwo.h diff -u /dev/null linux/drivers/sound/emu10k1/cardwo.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/cardwo.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,166 @@ +/* + ********************************************************************** + * cardwo.h -- header file for card wave out functions + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _CARDWO_H +#define _CARDWO_H + +#include "icardwav.h" + +struct wave_xferbuf +{ + u32 xferpos; + u32 xferbufsize; /* transfer buffer size */ + u32 numpages; /* number of pages in transfer buffer */ + void **xferbuffer; /* pointer to the transfer buffer */ + u32 format; + int is_stereo; + int is16bit; + u32 conv; + u32 interpolationbytes; + u32 offset; /* used for non-pagealigned buffer in P10 */ + u32 stopposition; +}; + +typedef struct tCARDMIXEROBJ *PCARDMIXEROBJ; + +struct wave_out +{ + struct wave_out *next; + u32 status; + u32 state; + int synchstart; + struct emu_voice *emu_voice; + struct emu_page *emuaddr; + struct emu_timer *timer; + struct wave_xferbuf *wavexferbuf; + void **pagetable; + u32 callbacksize; + u32 localvol; + u32 localreverb; + u32 localchorus; + u32 globalvolFactor; + u32 globalreverbFactor; + u32 globalchorusFactor; + u32 process_id; /* used for synchonize start */ + int setpos; + u32 position; + short ZL[6]; /* for pre-comp */ + short ZR[6]; /* for pre-comp */ + struct wave_format wave_fmt; + u32 playflags; + struct tq_struct *task; +}; + + +/* sblive_wave states */ +#define CARDWAVE_STATE_STOPPED 0x0001 +#define CARDWAVE_STATE_STOPPING 0x0008 +#define CARDWAVE_STATE_STARTED 0x0002 +#define CARDWAVE_STATE_SUSPEND 0x0004 + + +/* Transfer buffer formats */ +#define M8 0x00 +#define M16 0x01 +#define S8 0x02 +#define S16 0x03 + +#define INTERPOLATION_BYTES 8 +/* setting this to other than a power of two + may break some applications */ +#define WAVEOUT_MAXBUFSIZE 16384 +#define WAVEOUT_MINBUFSIZE 2048 + +#define WAVEOUT_DEFAULTFRAGLEN 100 /* Time to play a fragment in ms (latency) */ +#define WAVEOUT_DEFAULTBUFLEN 1000 /* Time to play the entire buffer in ms */ + +#define CARDWAVE_DEFAULT_MAXPLAYINST 64 + +struct woinst +{ + struct wave_out *wave_out; + struct wave_format wave_fmt; + u16 ossfragshift; + u32 fragment_size; + u32 numfrags; + wait_queue_head_t wait_queue; + int mapped; + u32 total_copied; + u32 total_played; + int silence_filled; + u32 silence_start; + u32 getoptr_blocks; + u32 wave_ptr; + spinlock_t lock; +}; + +struct sblive_waveout +{ + PCARDMIXEROBJ mixer; + int fWhql; + + struct wave_caps caps; + struct wave_out *wave_outlist; + + u32 numplaybackinst; + u32 maxnumplayinst; + u32 globalvol; + u32 mute; + u32 left; + u32 right; + u32 globalreverb; + u32 globalchorus; + u32 lineid; + u32 ctrlid; +}; + +int sblive_waveoutInit(struct sblive_waveout *, u8 *); +int sblive_waveoutExit(struct sblive_waveout *); +int sblive_waveoutQueryFormat(struct sblive_waveout *, struct wave_format *, u32); +int sblive_waveoutOpen(struct sblive_hw *, struct wave_format *, u32 *, u32, struct wave_out **); +int sblive_waveoutClose(struct sblive_hw *, struct wave_out *); +int sblive_waveoutStart(struct sblive_wavedevice *); +int sblive_waveoutStop(struct sblive_hw *, struct wave_out *, u32 *); +int sblive_waveoutGetXferSize(struct sblive_waveout *, struct wave_out *, u32 *, u32 *); +int sblive_waveoutXferData(struct sblive_waveout *, struct wave_out *, u8 *, u32 *); +int sblive_waveoutFillSilence(struct sblive_waveout *, struct wave_out *, u32 *); +int sblive_waveoutSetControl(struct sblive_hw *, struct wave_out *, u32, u32 *); +int sblive_waveoutGetControl(struct sblive_waveout *, struct wave_out *, u32, u32 *); +int sblive_emuAllocXferBuffer(struct sblive_hw *, struct wave_out *, u32 *, void ***); +int sblive_emuGetXferBuffer(struct sblive_hw *, struct wave_out *, u32 *, void ***); +int sblive_emuDeallocXferBuffer(struct wave_out *); +void initWaveOutXferBuffer(struct wave_out *); +int sblive_waveoutSynchStart(struct sblive_waveout *, u32); + +int sblive_mixerLineChange(PCARDMIXEROBJ, u32, u32); +int sblive_mixerSetControlValue(PCARDMIXEROBJ, u32, u32, u32, u32, u32); + +#endif /* _CARDWO_H */ Index: oldkernel/linux/drivers/sound/emu10k1/efxmgr.c diff -u /dev/null linux/drivers/sound/emu10k1/efxmgr.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/efxmgr.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,190 @@ +/* + ********************************************************************** + * sblive_fx.c + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "efxmgr.h" + +int sblive_fxInit(struct sblive_hw *sb_hw) +{ + int i; + u32 z, w, x, y, pc = 16; + + for (i = 0; i < 512; i++) + { + WRITE_EFX(sb_hw, i * 2, 0x10040); + WRITE_EFX(sb_hw, i * 2 + 1, 0x610040); + } + + for (i = 0; i < 256; i++) + sblive_writeptr(sb_hw, FXGPREGBASE + i, 0, 0); + + w = 0x40; + x = 1; + y = 0x44; + z = 0x101; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (4 << 20) | (z << 10) | w); + ++pc; + + w = 0x101; + x = 0x115; + y = 0x11; + z = 0x101; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (0 << 20) | (z << 10) | w); + ++pc; + + w = 0x101; + x = 0x113; + y = 0x13; + z = 0x101; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (0 << 20) | (z << 10) | w); + ++pc; + + w = 0x101; + x = 0x40; + y = 0x40; + z = 0x21; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (6 << 20) | (z << 10) | w); + ++pc; + + z = 0x23; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (6 << 20) | (z << 10) | w); + ++pc; + + w = 0x40; + x = 0x40; + y = 0x40; + z = 0x25; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (6 << 20) | (z << 10) | w); + ++pc; + + z = 0x27; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (6 << 20) | (z << 10) | w); + ++pc; + + w = 0x40; + x = 0x111; + y = 0x101; + z = 0x29; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (0 << 20) | (z << 10) | w); + ++pc; + + w = 0x40; + x = 0x11; + y = 0x40; + z = 0x2B; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (6 << 20) | (z << 10) | w); + ++pc; + + w = 0x40; + x = 0; + y = 0x44; + z = 0x100; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (4 << 20) | (z << 10) | w); + ++pc; + + w = 0x100; + x = 0x114; + y = 0x10; + z = 0x100; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (0 << 20) | (z << 10) | w); + ++pc; + + w = 0x100; + x = 0x112; + y = 0x12; + z = 0x100; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (0 << 20) | (z << 10) | w); + ++pc; + + w = 0x100; + x = 0x40; + y = 0x40; + z = 0x20; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (6 << 20) | (z << 10) | w); + ++pc; + + z = 0x22; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (6 << 20) | (z << 10) | w); + ++pc; + + w = 0x40; + x = 0x40; + y = 0x40; + z = 0x24; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (6 << 20) | (z << 10) | w); + ++pc; + + z = 0x26; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (6 << 20) | (z << 10) | w); + ++pc; + + w = 0x40; + x = 0x110; + y = 0x100; + z = 0x28; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (0 << 20) | (z << 10) | w); + ++pc; + + w = 0x40; + x = 0x10; + y = 0x40; + z = 0x2A; + WRITE_EFX(sb_hw, pc * 2, (x << 10) | y); + WRITE_EFX(sb_hw, pc * 2 + 1, (6 << 20) | (z << 10) | w); + ++pc; + + sblive_writeptr(sb_hw, DBG, 0, 0); + + return CTSTATUS_SUCCESS; +} + +/* FIXME: We should probably be doing something here */ +int sblive_fxExit(struct sblive_hw *sb_hw) +{ + return CTSTATUS_SUCCESS; +} Index: oldkernel/linux/drivers/sound/emu10k1/efxmgr.h diff -u /dev/null linux/drivers/sound/emu10k1/efxmgr.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/efxmgr.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,38 @@ +/* + ********************************************************************** + * sblive_fx.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _EFXMGR_H +#define _EFXMGR_H + +int sblive_fxInit(struct sblive_hw *); +int sblive_fxExit(struct sblive_hw *); + +#endif /* _EFXMGR_H */ Index: oldkernel/linux/drivers/sound/emu10k1/emu_wrapper.h diff -u /dev/null linux/drivers/sound/emu10k1/emu_wrapper.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/emu_wrapper.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,25 @@ +#ifndef __EMU_WRAPPER_H +#define __EMU_WRAPPER_H + +/* wrapper for 2.2 kernel */ + +#include + +#define vma_get_pgoff(v) vma_get_offset(v) +#define wait_queue_head_t struct wait_queue * +#define DECLARE_WAITQUEUE(a, b) struct wait_queue a = {b, NULL}; +#define init_waitqueue_head(a) init_waitqueue(a) + +#define RSRCADDRESS(dev,num) ((dev)->base_address[(num)]) + +#define RSRCISIOREGION(dev,num) (RSRCADDRESS(dev,num) != 0 && \ + (RSRCADDRESS(dev,num) & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) + +#define init_MUTEX(a) *(a) = MUTEX + +#define UP_INODE_SEM(a) up(a) +#define DOWN_INODE_SEM(a) down(a) + +#define GET_INODE_STRUCT() struct inode *inode = file->f_dentry->d_inode + +#endif Index: oldkernel/linux/drivers/sound/emu10k1/emuadxmg.c diff -u /dev/null linux/drivers/sound/emu10k1/emuadxmg.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/emuadxmg.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,262 @@ +/* + ********************************************************************** + * emuadxmg.c - Address space manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" + +/************************************************************************ +* +* int emu10kaddxmgrInit(struct sblive_hw * sb_hw) +* +* ENTRY +* sb_hw - Pointer to the HWOBJ to init voice manager +* +* RETURNS +* Always returns CTSTATUS_SUCCESS +* +* ABOUT +* Inits data +* +************************************************************************/ +int emu10kaddxmgrInit(struct sblive_hw *sb_hw) +{ + u32 count; + + for (count = 0; count < MAXPAGES; count++) + sb_hw->emu_addrmgr.emupagetable[count].flags = 0; + + /* Mark first page as used */ + /* This page is reserved by the driver */ + sb_hw->emu_addrmgr.emupagetable[0].flags = 0x8001; + sb_hw->emu_addrmgr.emupagetable[1].flags = MAXPAGES - RESERVED - 1; + + return CTSTATUS_SUCCESS; +} + + +/************************************************************************ +* +* int emu10kaddxmgrExit(struct sblive_hw * sb_hw) +* +* ENTRY +* sb_hw - Pointer to the HWOBJ to exit +* +* RETURNS +* Always returns CTSTATUS_SUCCESS +* +* ABOUT +* Exits +* +************************************************************************/ +int emu10kaddxmgrExit(struct sblive_hw *sb_hw) +{ + u32 count; + + /* TODO : callback all address owners */ + + for (count = 0; count < MAXPAGES; count++) + sb_hw->emu_addrmgr.emupagetable[count].flags = 0; + + sb_hw->emu_addrmgr.emupagetable[0].flags = MAXPAGES - RESERVED; + + return CTSTATUS_SUCCESS; +} + + +/************************************************************************ +* +* u32 emu10kaddxmgrMaxContigPages(struct sblive_hw * sb_hw) +* +* ENTRY +* sb_hw - Pointer to the HWOBJ to check +* +* RETURNS +* largest free contiguous block in pages +* +* ABOUT +* Gets largest free contiguous block in pages +* +************************************************************************/ +u32 emu10kaddxmgrMaxContigPages(struct sblive_hw *sb_hw) +{ + u32 maxpages = 0; + u16 pageindex = 0; + struct emu_addrmgr *mgr; + + mgr = &sb_hw->emu_addrmgr; + + while (pageindex < (MAXPAGES - RESERVED - 1)) + { + if (mgr->emupagetable[pageindex].flags & 0x8000) + { + /* This block of pages is in use, jump to the start of the next block */ + pageindex += (mgr->emupagetable[pageindex].flags & 0x7fff); + } else + { + /* Found free block */ + if (mgr->emupagetable[pageindex].flags > (u16) maxpages) + maxpages = mgr->emupagetable[pageindex].flags; /* Block is large enough */ + else + pageindex += mgr->emupagetable[pageindex].flags; /* Block too small - jump to the start of the next block */ + } + } + + return maxpages; +} + + +/************************************************************************ +* +* int emu10kaddxmgrAlloc(struct emuaddr_allocdesc * emuaddrAllocDesc, +* struct emu_page **pemuaddrObj) +* +* ENTRY +* emuaddrAllocDesc - +* Pointer to a struct emuaddr_allocdesc that describes the +* address space needed. +* +* pemuaddrObj - +* Address to return allocated struct emu_page +* +* RETURNS +* SUCCESS - CTSTATUS_SUCCESS +* FAILURE - CTSTATUS_NOMEMORY (not enough available address space) +* +* ABOUT +* Allocates emu address space +* +************************************************************************/ +int emu10kaddxmgrAlloc(struct emuaddr_allocdesc *allocdesc, struct emu_page **emu_pageptr) +{ + struct sblive_hw * sb_hw; + u32 emupageindex = 0; + u32 pages; + struct emu_page * pemupagetable; + unsigned long flags; + + sb_hw = allocdesc->sb_hw; + pemupagetable = sb_hw->emu_addrmgr.emupagetable; + *emu_pageptr = NULL; + + /* Convert bytes to pages */ + pages = (allocdesc->size / PAGE_SIZE) + + ((allocdesc->size % PAGE_SIZE) ? 1 : 0); + + while (emupageindex < (MAXPAGES - RESERVED - 1)) + { + if (pemupagetable[emupageindex].flags & 0x8000) + { + /* This block of pages is in use, jump to the start of the next block. */ + emupageindex += (pemupagetable[emupageindex].flags & 0x7fff); + } else + { + /* Found free block */ + if (pemupagetable[emupageindex].flags >= (u16) pages) + { + spin_lock_irqsave(&sb_hw->emu_lock, flags); + + /* Block is large enough */ + + /* If free block is larger than the block requested + * then adjust the size of the block remaining */ + if (pemupagetable[emupageindex].flags > (u16) pages) + pemupagetable[emupageindex + pages].flags = pemupagetable[emupageindex].flags - (u16) pages; + + pemupagetable[emupageindex].flags = (u16) (pages | 0x8000); /* Mark block as used */ + + /* Return emu address */ + pemupagetable[emupageindex].sb_hw = sb_hw; + pemupagetable[emupageindex].emustartaddr = emupageindex << 11; + *emu_pageptr = &pemupagetable[emupageindex]; + + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return CTSTATUS_SUCCESS; + } else + { + /* Block too small, jump to the start of the next block */ + emupageindex += pemupagetable[emupageindex].flags; + } + } + } + + return CTSTATUS_NOMEMORY; +} + +/************************************************************************ +* +* int emu10kaddxmgrFree(struct emu_page * emuaddrObj) +* +* ENTRY +* emuaddrObj - +* pointer to a struct emu_page returned +* by a call to emu10kaddxmgrAlloc() +* +* RETURNS +* SUCCESS - CTSTATUS_SUCCESS +* FAILURE - CTSTATUS_ERROR +* +* ABOUT +* Frees a previously allocated emu address space. +* +************************************************************************/ +int emu10kaddxmgrFree(struct emu_page *page) +{ + struct sblive_hw * sb_hw; + u16 origsize = 0; + u32 pageindex; + struct emu_page * pemupagetable; + unsigned long flags; + + sb_hw = page->sb_hw; + spin_lock_irqsave(&sb_hw->emu_lock, flags); + + pemupagetable = sb_hw->emu_addrmgr.emupagetable; + pageindex = page->emustartaddr; + + /* Convert Emu address to Emu page number */ + pageindex >>= 11; + + if (pemupagetable[pageindex].flags & 0x8000) + { + /* Block is allocated - mark block as free */ + origsize = pemupagetable[pageindex].flags & 0x7fff; + pemupagetable[pageindex].flags = origsize; + pemupagetable[pageindex].emustartaddr = 0; + + /* If next block is free, we concat both blocks */ + if (!(pemupagetable[pageindex + origsize].flags & 0x8000)) + pemupagetable[pageindex].flags += pemupagetable[pageindex + origsize].flags & 0x7fff; + } + + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return origsize; +} Index: oldkernel/linux/drivers/sound/emu10k1/hwaccess.c diff -u /dev/null linux/drivers/sound/emu10k1/hwaccess.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/hwaccess.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,891 @@ +/* + ********************************************************************** + * hwaccess.c -- Hardware access layer + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * December 9, 1999 Jon Taylor rewrote the I/O subsystem + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "mycommon.h" +#include "emu_wrapper.h" + + +spinlock_t sblive_spinlock = SPIN_LOCK_UNLOCKED; + +int halHWInit(struct sblive_hw *sb_hw); + +int halInit(struct sblive_hw *sb_hw, struct sblive_config *config, u32 *hwflags) +{ + unsigned long ioaddr; + + /* should fill in default values here */ + sb_hw->paneffectsbus = 0; + sb_hw->auxeffectsbus = 1; + sb_hw->choruseffectsbus = 2; + sb_hw->reverbeffectsbus = 3; + + /* Setup Critical Sections */ + spin_lock_init(&sb_hw->emu_lock); + + sb_hw->numvoices = NUM_G; + sb_hw->dsCardCfg = *config; + + sb_hw->hwaddr = config->ioportbase[0]; + ioaddr = config->ioportbase[0]; + + sb_hw->mixeraddx = (u32) (ioaddr + AC97DATA); + sb_hw->hwconfigaddx = (u32) (ioaddr + HCFG); + + sb_hw->hw_irq.irq = config->IRQregs[0]; + if (sb_hw->hw_irq.irq) + { + if (sb_hw->hw_irq.irq == 2) + sb_hw->hw_irq.irq = 9; + } + + sb_hw->hw_irq.sb_hw = sb_hw; + + /* Init Card */ + if (halHWInit(sb_hw) != CTSTATUS_SUCCESS) + return CTSTATUS_ERROR; + + if (sblive_irqmgrInit(&sb_hw->hw_irq) != CTSTATUS_SUCCESS) + { + DPF(2, "Failed to initialize IRQ manager\n"); + sb_hw->hw_irq.irq = 0; + } + + sblive_voiceInit(sb_hw); + sblive_timerinit(sb_hw); + emu10kaddxmgrInit(sb_hw); + + DPD(2, " hw control register -> %x\n", sblive_readfn0(sb_hw, HCFG)); + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* halExit(struct sblive_hw * sb_hw) */ +/****************************************************************************/ +int halExit(struct sblive_hw * sb_hw) +{ + int ch; + + sblive_voiceExit(sb_hw); + emu10kaddxmgrExit(sb_hw); + + if (sb_hw->hw_irq.irq) + { + sblive_writefn0(sb_hw,INTE, DISABLE); + sblive_irqmgrExit(&sb_hw->hw_irq); + } + + /** Shutdown the chip **/ + for (ch = 0; ch < NUM_G; ch++) + sblive_writeptr(sb_hw, DCYSUSV, ch, ENV_OFF); + + for (ch = 0; ch < NUM_G; ch++) + { + sblive_writeptr(sb_hw, VTFT, ch, 0); + sblive_writeptr(sb_hw, CVCF, ch, 0); + sblive_writeptr(sb_hw, PTRX, ch, 0); + sblive_writeptr(sb_hw, CPF, ch, 0); + } + + /* Reset recording buffers */ + sblive_writeptr(sb_hw, MICBS, 0, 0); + sblive_writeptr(sb_hw, MICBA, 0, 0); + sblive_writeptr(sb_hw, FXBS, 0, 0); + sblive_writeptr(sb_hw, FXBA, 0, 0); + sblive_writeptr(sb_hw, FXWC, 0, 0); + sblive_writeptr(sb_hw, ADCBS, 0, ADCBS_BUFSIZE_NONE); + sblive_writeptr(sb_hw, ADCBA, 0, 0); + sblive_writeptr(sb_hw, TCBS, 0, TCBS_BUFFSIZE_16K); + sblive_writeptr(sb_hw, TCB, 0, 0); + sblive_writeptr(sb_hw, DBG, 0, 0x8000); + + /* Disable channel interrupt */ + sblive_writeptr(sb_hw, CLIEL, 0, 0); + sblive_writeptr(sb_hw, CLIEH, 0, 0); + sblive_writeptr(sb_hw, SOLEL, 0, 0); + sblive_writeptr(sb_hw, SOLEH, 0, 0); + + /* Disable audio and lock cache */ + sblive_writefn0(sb_hw, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE | HCFG_MUTEBUTTONENABLE); + sblive_writeptr(sb_hw, PTB, 0, 0); + + osFreeMemPhysical(&sb_hw->silentpage); + osFreeMemPhysical(&sb_hw->virtualpagetable); + osFreeMemPhysical(&sb_hw->tankmem); + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/* halHWInit (struct sblive_hw *sb_hw) */ +/****************************************************************************/ +int halHWInit(struct sblive_hw *sb_hw) +{ + int nCh; + u32 size = 0; + u32 sizeIdx = 0; + u16 count; + int status; + + /* Disable audio and lock cache */ + sblive_writefn0(sb_hw, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE | HCFG_MUTEBUTTONENABLE); + + /* Reset recording buffers */ + sblive_writeptr(sb_hw, MICBS, 0, 0); + sblive_writeptr(sb_hw, MICBA, 0, 0); + sblive_writeptr(sb_hw, FXBS, 0, 0); + sblive_writeptr(sb_hw, FXBA, 0, 0); + sblive_writeptr(sb_hw, ADCBS, 0, ADCBS_BUFSIZE_NONE); + sblive_writeptr(sb_hw, ADCBA, 0, 0); + + /* Disable channel interrupt */ + sblive_writefn0(sb_hw,INTE, DISABLE); + sblive_writeptr(sb_hw, CLIEL, 0, 0); + sblive_writeptr(sb_hw, CLIEH, 0, 0); + sblive_writeptr(sb_hw, SOLEL, 0, 0); + sblive_writeptr(sb_hw, SOLEH, 0, 0); + + /* Init envelope engine */ + for (nCh = 0; nCh < NUM_G; nCh++) + { + sblive_writeptr(sb_hw, DCYSUSV, nCh, ENV_OFF); + sblive_writeptr(sb_hw, IP, nCh, 0); + sblive_writeptr(sb_hw, VTFT, nCh, 0xffff); + sblive_writeptr(sb_hw, CVCF, nCh, 0xffff); + sblive_writeptr(sb_hw, PTRX, nCh, 0); + sblive_writeptr(sb_hw, CPF, nCh, 0); + sblive_writeptr(sb_hw, CCR, nCh, 0); + + sblive_writeptr(sb_hw, PSST, nCh, 0); + sblive_writeptr(sb_hw, DSL, nCh, 0x10); + sblive_writeptr(sb_hw, CCCA, nCh, 0); + sblive_writeptr(sb_hw, Z1, nCh, 0); + sblive_writeptr(sb_hw, Z2, nCh, 0); + sblive_writeptr(sb_hw, FXRT, nCh, 0xd01c0000); + + sblive_writeptr(sb_hw, ATKHLDM, nCh, 0); + sblive_writeptr(sb_hw, DCYSUSM, nCh, 0); + sblive_writeptr(sb_hw, IFATN, nCh, 0xffff); + sblive_writeptr(sb_hw, PEFE, nCh, 0); + sblive_writeptr(sb_hw, FMMOD, nCh, 0); + sblive_writeptr(sb_hw, TREMFRQ, nCh, 24); /* 1 Hz */ + sblive_writeptr(sb_hw, FM2FRQ2, nCh, 24); /* 1 Hz */ + sblive_writeptr(sb_hw, TEMPENV, nCh, 0); + + /*** These are last so OFF prevents writing ***/ + sblive_writeptr(sb_hw, LFOVAL2, nCh, 0); + sblive_writeptr(sb_hw, LFOVAL1, nCh, 0); + sblive_writeptr(sb_hw, ATKHLDV, nCh, 0); + sblive_writeptr(sb_hw, ENVVOL, nCh, 0); + sblive_writeptr(sb_hw, ENVVAL, nCh, 0); + } + + + /* + ** Init to 0x02109204 : + ** Clock accuracy = 0 (1000ppm) + ** Sample Rate = 2 (48kHz) + ** Audio Channel = 1 (Left of 2) + ** Source Number = 0 (Unspecified) + ** Generation Status = 1 (Original for Cat Code 12) + ** Cat Code = 12 (Digital Signal Mixer) + ** Mode = 0 (Mode 0) + ** Emphasis = 0 (None) + ** CP = 1 (Copyright unasserted) + ** AN = 0 (Digital audio) + ** P = 0 (Consumer) + */ + + sblive_writeptr(sb_hw, SPCS0, 0, SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC| + SPCS_GENERATIONSTATUS | 0x00001200 | + SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); /* SPDIF0 */ + + sblive_writeptr(sb_hw, SPCS1, 0, SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC| + SPCS_GENERATIONSTATUS | 0x00001200 | + SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); /* SPDIF1 */ + + sblive_writeptr(sb_hw, SPCS2, 0, SPCS_CLKACCY_1000PPM | 0x002000000 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC| + SPCS_GENERATIONSTATUS | 0x00001200 | + SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); /* SPDIF2 & SPDIF3 */ + + sblive_fxInit(sb_hw); /* initialize effects engine */ + +#ifdef TANKMEM + size = TMEMSIZE; + sizeIdx = TMEMSIZEREG; + while (size > 16384) + { + status = osAllocMemPhysical(size,&sb_hw->tankmem); + + if(status == CTSTATUS_SUCCESS) + break; + + size /= 2; + sizeIdx -= 1; + } + + if(status != CTSTATUS_SUCCESS){ + sb_hw->tmemsize = 0; + return CTSTATUS_ERROR; + } + + sb_hw->tmemsize = size; + +#else /* !TANKMEM */ + sb_hw->tankmem = NULL; + sb_hw->tmemsize = 0; +#endif /* TANKMEM */ + + status = osAllocMemPhysical((MAXPAGES - RESERVED) * sizeof(u32),&sb_hw->virtualpagetable); + + if (status != CTSTATUS_SUCCESS) + { + DPF(2, "Failed to allocate physical memory (1)\n"); + osFreeMemPhysical(&sb_hw->tankmem); + return status; + } + + status = osAllocMemPhysical(EMUPAGESIZE, &sb_hw->silentpage); + + if (status != CTSTATUS_SUCCESS) + { + DPF(2, "Failed to allocate physical memory (2)\n"); + osFreeMemPhysical(&sb_hw->tankmem); + osFreeMemPhysical(&sb_hw->virtualpagetable); + return status; + } else + memset(sb_hw->silentpage->virtaddx, 0, EMUPAGESIZE); /* FIXME: Use bzero() */ + + /* Init page table */ + /* All the entries are inialized to point to themselves */ + /* so that when illegal memory access occurs, the system */ + /* won't hang. */ + for (count = 0; count < (MAXPAGES - RESERVED); count++) + ((u32 *) sb_hw->virtualpagetable->virtaddx)[count] = (sb_hw->silentpage->physaddx * 2) | count; + + /* Init page table & tank memory base register */ + /* is this correct? PTB_MASK is 0xfffff000, it may be a 4k alignment... */ + sblive_writeptr(sb_hw, PTB, 0, sb_hw->virtualpagetable->physaddx); +#ifdef TANKMEM + sblive_writeptr(sb_hw, TCB, 0, sb_hw->tankmem->physaddx); +#else + sblive_writeptr(sb_hw, TCB, 0, 0); +#endif + sblive_writeptr(sb_hw, TCBS, 0, sizeIdx); + + for (nCh = 0; nCh < NUM_G; nCh++) + { + sblive_writeptr(sb_hw, MAPA, nCh, MAP_PTI_MASK | (sb_hw->silentpage->physaddx * 2)); + sblive_writeptr(sb_hw, MAPB, nCh, MAP_PTI_MASK | (sb_hw->silentpage->physaddx * 2)); + } + + /* Hokay, now enable the AUD bit */ + /* Enable Audio = 1 */ + /* Mute Disable Audio = 0 */ + /* Lock Tank Memory = 1 */ + /* Lock Sound Memory = 0 */ + /* Auto Mute = 1 */ + + sblive_rmwac97(sb_hw, AC97_MASTERVOLUME, 0x8000, 0x8000); + + sblive_writeac97(sb_hw, AC97_MASTERVOLUME, 0); + sblive_writeac97(sb_hw, AC97_PCMOUTVOLUME, 0); + + if (sb_hw->dsCardCfg.chiprev < 6) + sblive_writefn0(sb_hw, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE | HCFG_AUTOMUTE); + else + /* With on-chip joystick */ + sblive_writefn0(sb_hw, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE | HCFG_AUTOMUTE | HCFG_JOYENABLE); + /* TOSLink detection */ + sb_hw->has_toslink = 0; + + size = sblive_readfn0(sb_hw, HCFG); + if (size & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) + { + volatile unsigned delay; + sblive_writefn0(sb_hw, HCFG, size | 0x800); + + for (delay = 0; delay < 512; delay++); /* FIXME: Use udelay() */ + + if (size != (sblive_readfn0(sb_hw, HCFG) & ~0x800)) + { + sb_hw->has_toslink = 1; + sblive_writefn0(sb_hw, HCFG, size); + } + } + + return CTSTATUS_SUCCESS; +} + +/* FIXME: This belongs in a headerfile */ +/** Channel status message lengths **/ +u8 gabMsgLenChannel[] = +{ + 3, /* 0x80 note off */ + 3, /* 0x90 note on */ + 3, /* 0xA0 key pressure */ + 3, /* 0xB0 control change */ + 2, /* 0xC0 program change */ + 2, /* 0xD0 channel pressure */ + 3, /* 0xE0 pitch bend */ + 1 +}; + +/** System status message lengths **/ +u8 gabMsgLenSystem[] = +{ + 1, /* 0xF0 sysex begin */ + 2, /* 0xF1 midi tcqf */ + 3, /* 0xF2 song position */ + 2, /* 0xF3 song select */ + 1, /* 0xF4 undefined */ + 1, /* 0xF5 undefined */ + 1, /* 0xF6 tune request */ + 1, /* 0xF7 sysex eox */ + + 1, /* 0xF8 timing clock */ + 1, /* 0xF9 undefined */ + 1, /* 0xFA start */ + 1, /* 0xFB continue */ + 1, /* 0xFC stop */ + 1, /* 0xFD undefined */ + 1, /* 0xFE active sensing */ + 1 /* 0xFF system reset */ +}; + + +/****************************************************************************/ +/** Function : srToPitch **/ +/** **/ +/** Input : sampleRate - sampling rate **/ +/** **/ +/** Return : pitch value **/ +/** **/ +/** About : convert sampling rate to pitch **/ +/** **/ +/** Note : for 8010, sampling rate is at 48kHz, this function should **/ +/** be changed. **/ +/****************************************************************************/ +u32 srToPitch(u32 sampleRate) +{ + int i; + + /* FIXME: These tables should be defined in a headerfile */ + static u32 logMagTable[128] = + { + 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, + 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, + 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, + 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, + 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, + 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, + 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, + 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, + 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, + 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, + 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, + 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, + 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, + 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, + 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, + 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df + }; + +static char logSlopeTable[128] = +{ + 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, + 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, + 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, + 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, + 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, + 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, + 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, + 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, + 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, + 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, + 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, + 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f +}; + +if (sampleRate == 0) + return (0); /* Bail out if no leading "1" */ + +sampleRate *= 11185; /* Scale 48000 to 0x20002380 */ + +for (i = 31; i > 0; i--) +{ + if (sampleRate & 0x80000000) + { /* Detect leading "1" */ + return (u32) (((s32) (i - 15) << 20) + + logMagTable[0x7f & (sampleRate >> 24)] + + (0x7f & (sampleRate >> 17)) * + logSlopeTable[0x7f & (sampleRate >> 24)]); + } + sampleRate = sampleRate << 1; +} + +DPF(2, "srToPitch: BUG!\n"); +return 0; /* Should never reach this point */ +} + + +/****************************************************************************/ +/** Function : sumVolumeToAttenuation **/ +/** **/ +/** Input : inputValue - input volume **/ +/** **/ +/** Return : attenuation value **/ +/** **/ +/** About : convert volume to attenuation **/ +/****************************************************************************/ +/* Returns an attenuation based upon a cumulative volume value */ +/* Algorithm calculates 0x200 - 0x10 log2 (input) */ +u8 sumVolumeToAttenuation(u32 value) +{ + u16 count = 16; + s16 ans; + + if (value == 0) + return 0xFF; + + /* Find first SET bit. This is the integer part of the value */ + while ((value & 0x10000) == 0) + { + value <<= 1; + count--; + } + + /* The REST of the data is the fractional part. */ + ans = (s16) (0x110 - ((count << 4) + ((value & 0x0FFFFL) >> 12))); + if (ans > 0xFF) + ans = 0xFF; + + return (u8) ans; +} + +/**************************************************************/ +/* write/read PCI function 0 registers */ +/**************************************************************/ +void sblive_writefn0(struct sblive_hw *sb_hw, u8 reg, u32 data) +{ + unsigned long flags; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + outl(data, sb_hw->hwaddr + reg); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return; +} + +void sblive_wrtmskfn0(struct sblive_hw *sb_hw, u8 reg, u32 mask, u32 data) +{ + unsigned long flags; + + data &= mask; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + data |= inl(sb_hw->hwaddr + reg) & ~mask; + outl(data, sb_hw->hwaddr + reg); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return; +} + +u32 sblive_readfn0(struct sblive_hw *sb_hw, u8 reg) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + val = inl(sb_hw->hwaddr + reg); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + return val; +} + +u32 sblive_rdmskfn0(struct sblive_hw *sb_hw, u8 reg, u32 mask) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + val = inl(sb_hw->hwaddr + reg); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + return val & mask; +} + +/****************************************************************************/ +/* write/read Emu10k1 pointer-offset register set, accessed through */ +/* the PTR and DATA registers */ +/****************************************************************************/ +void sblive_writeptr(struct sblive_hw *sb_hw, u32 reg, u32 channel, u32 data) +{ + u32 regptr; + unsigned long flags; + + regptr = ((reg << 16) & PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) + { + u32 mask; + u8 size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + data = (data << offset ) & mask; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + outl(regptr, sb_hw->hwaddr + PTR); + data |= inl(sb_hw->hwaddr + DATA) & ~mask; + outl(data, sb_hw->hwaddr + DATA); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + } else + { + spin_lock_irqsave(&sb_hw->emu_lock, flags); + outl(regptr, sb_hw->hwaddr + PTR); + outl(data, sb_hw->hwaddr + DATA); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + } + + return; +} + +u32 sblive_readptr(struct sblive_hw *sb_hw, u32 reg, u32 channel) +{ + u32 regptr,val; + unsigned long flags; + + regptr = ((reg << 16) & PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK); + + if (reg & 0xff000000) + { + u32 mask; + u8 size, offset; + + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + outl(regptr, sb_hw->hwaddr + PTR); + val = inl(sb_hw->hwaddr + DATA); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return (val & mask) >> offset; + } else + { + spin_lock_irqsave(&sb_hw->emu_lock, flags); + outl(regptr, sb_hw->hwaddr + PTR); + val = inl(sb_hw->hwaddr + DATA); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return val; + } +} + +/*****************************************************************************/ +/* halSetStopOnLoop (struct sblive_hw *sb_hw, u32 voicenum) */ +/*****************************************************************************/ +void halSetStopOnLoop(struct sblive_hw *sb_hw, u32 voicenum) +{ + /* Voice interrupt */ + if (voicenum >= 32) + sblive_writeptr(sb_hw, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 1); + else + sblive_writeptr(sb_hw, SOLEL | ((0x0100 | voicenum) << 16), 0, 1); + + return; +} + +/*****************************************************************************/ +/* halClearStopOnLoop (struct sblive_hw *sb_hw, u32 voicenum ) */ +/*****************************************************************************/ +void halClearStopOnLoop(struct sblive_hw *sb_hw, u32 voicenum) +{ + /* Voice interrupt */ + if (voicenum >= 32) + sblive_writeptr(sb_hw, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 0); + else + sblive_writeptr(sb_hw, SOLEL | ((0x0100 | voicenum) << 16), 0, 0); + + return; +} + +/*****************************************************************************/ +/* halWC_WAIT (struct sblive_hw *sb_hw, u32 wait) */ +/*****************************************************************************/ +void halWC_WAIT(struct sblive_hw *sb_hw, u32 wait) +{ + volatile unsigned uCount; + u32 newtime = 0, curtime; + + curtime = READ_FN0(sb_hw, WC_SAMPLECOUNTER); + while (wait--) + { + uCount = 0; + while (uCount++ < TIMEOUT) + { + newtime = READ_FN0(sb_hw, WC_SAMPLECOUNTER); + if (newtime != curtime) + break; + } + + if (uCount >= TIMEOUT) + break; + + curtime = newtime; + } +} + + +/*****************************************************************************/ +/* sblive_readac97 (struct sblive_hw *sb_hw, u8 index, u16 *pdata) */ +/*****************************************************************************/ +int sblive_readac97(struct sblive_hw *sb_hw, u8 index, u16 *pdata) +{ + unsigned long flags; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + + outb(index, sb_hw->mixeraddx + 2); + *pdata = inw(sb_hw->mixeraddx); + + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return CTSTATUS_SUCCESS; +} + + +/*****************************************************************************/ +/* sblive_writeac97(struct sblive_hw *sb_hw, u8 index, u16 data) */ +/*****************************************************************************/ +int sblive_writeac97(struct sblive_hw *sb_hw, u8 index, u16 data) +{ + unsigned long flags; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + + outb(index, sb_hw->mixeraddx + 2); + outw(data, sb_hw->mixeraddx); + + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return CTSTATUS_SUCCESS; +} + + +/*****************************************************************************/ +/* sblive_rmwac97(struct sblive_hw *sb_hw, u8 index, u16 data, u16 mask) */ +/*****************************************************************************/ +int sblive_rmwac97(struct sblive_hw *sb_hw, u8 index, u16 data, u16 mask) +{ + u16 temp; + unsigned long flags; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + + outb(index, sb_hw->mixeraddx + 2); + temp = inw(sb_hw->mixeraddx); + temp &= ~mask; + data &= mask; + temp |= data; + outw(temp, sb_hw->mixeraddx); + + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return CTSTATUS_SUCCESS; +} + +/****************************************************************/ +/* MPU access functions */ +/****************************************************************/ + +/*****************************************************************************/ +/* Function Name : hwmpuWriteData */ +/* */ +/* Description : Writing data to MPU with timeout */ +/* */ +/* Input : sb_hw | Port object */ +/* bData | contain the command */ +/* */ +/* Return : int | contain the status of this call */ +/* */ +/*****************************************************************************/ +int hwmpuWriteData(struct sblive_hw *sb_hw, u8 data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + + if ((inb(sb_hw->hwaddr + MUSTAT) & MUSTAT_ORDYN) == 0) + { + outb(data, sb_hw->hwaddr + MUDATA); + ret = CTSTATUS_SUCCESS; + } else + ret = CTSTATUS_BUSY; + + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return ret; +} + + +/*****************************************************************************/ +/* Function Name : hwmpuReadData */ +/* */ +/* Description : Reading data from MPU with timeout */ +/* */ +/* Input : sb_hw | Port object */ +/* bData | contain the command */ +/* */ +/* Return : int | contain the status of this call */ +/* */ +/*****************************************************************************/ +int hwmpuReadData(struct sblive_hw *sb_hw, u8 *data) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + + if ((inb(sb_hw->hwaddr + MUSTAT) & MUSTAT_IRDYN) == 0) + { + *data = inb(sb_hw->hwaddr + MUDATA); + ret = CTSTATUS_SUCCESS; + } else + ret = CTSTATUS_NODATA; + + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + return ret; +} + + +/*****************************************************************************/ +/* Function Name : hwmpuReset */ +/* */ +/* Description : Hardware reset the MPU */ +/* */ +/* Input : sb_hw | Port object */ +/* */ +/* Return : int | contain the status of this call */ +/* */ +/*****************************************************************************/ +int hwmpuReset(struct sblive_hw *sb_hw) +{ + u8 status; + unsigned long flags; + + DPF(2, "hwmpuReset() called\n"); + + if (sb_hw->mpuacqcount == 0) + { + spin_lock_irqsave(&sb_hw->emu_lock, flags); + outb(MUCMD_RESET, sb_hw->hwaddr + MUCMD); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + halWC_WAIT(sb_hw, 8); + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + outb(MUCMD_RESET, sb_hw->hwaddr + MUCMD); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + halWC_WAIT(sb_hw, 8); + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + outb(MUCMD_ENTERUARTMODE , sb_hw->hwaddr + MUCMD); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + halWC_WAIT(sb_hw, 8); + + spin_lock_irqsave(&sb_hw->emu_lock, flags); + status = inb(sb_hw->hwaddr + MUDATA); + spin_unlock_irqrestore(&sb_hw->emu_lock, flags); + + if (status == 0xfe) + return CTSTATUS_SUCCESS; + else + return CTSTATUS_ERROR; + } + + + return CTSTATUS_SUCCESS; +} + + +/*****************************************************************************/ +/* Function Name : hwmpuAcquire */ +/* */ +/* Description : Increase MPU acquire count */ +/* */ +/* Input : sb_hw | Port object */ +/* */ +/* Return : int | contain the status of this call */ +/* */ +/*****************************************************************************/ +int hwmpuAcquire(struct sblive_hw *sb_hw) +{ + /* FIXME: This should be a macro */ + ++sb_hw->mpuacqcount; + + return CTSTATUS_SUCCESS; +} + + +/*****************************************************************************/ +/* Function Name : hwmpuRelease */ +/* */ +/* Description : Decrease MPU acquire count */ +/* */ +/* Input : sb_hw | Port object */ +/* */ +/* Return : int | contain the status of this call */ +/* */ +/*****************************************************************************/ +int hwmpuRelease(struct sblive_hw *sb_hw) +{ + /* FIXME: this should be a macro */ + --sb_hw->mpuacqcount; + + return CTSTATUS_SUCCESS; +} Index: oldkernel/linux/drivers/sound/emu10k1/hwaccess.h diff -u /dev/null linux/drivers/sound/emu10k1/hwaccess.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/hwaccess.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,336 @@ +/* + ********************************************************************** + * hwaccess.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _HWACCESS_H +#define _HWACCESS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +typedef int (*CALLBACKFN)(unsigned long,unsigned long ,unsigned long); + +enum GlobalErrorCode +{ + CTSTATUS_SUCCESS = 0x0000, + CTSTATUS_ERROR, + CTSTATUS_INVALIDPARAM, + CTSTATUS_NOTSUPPORTED, + CTSTATUS_NOMEMORY, + CTSTATUS_INVALIDIO, + CTSTATUS_INVALIDIRQ, + CTSTATUS_INVALIDDMA, + CTSTATUS_INVALIDID, + CTSTATUS_INVALIDVALUE, + CTSTATUS_BADFORMAT_BITS, + CTSTATUS_BADFORMAT_RATE, + CTSTATUS_BADFORMAT_CHANNELS, + CTSTATUS_BADFORMAT_STEREO, + CTSTATUS_INUSE, + CTSTATUS_STILLPLAYING, + CTSTATUS_ALLOCATED, + CTSTATUS_INVALID_FORMAT, + CTSTATUS_OUT_OF_RESOURCE, + CTSTATUS_CHIP_INUSE, + CTSTATUS_NOCHIPRESOURCE, + CTSTATUS_PORTS_INUSE +}; + +enum LocalErrorCode +{ + CTSTATUS_NOTENABLED = 0x7000, + CTSTATUS_READY, + CTSTATUS_BUSY, + CTSTATUS_DATAAVAIL, + CTSTATUS_NODATA, + CTSTATUS_NEXT_BYTE +}; + +#define FLAGS_ENABLED 0x0001 +#define FLAGS_AVAILABLE 0x0002 +#define FLAGS_READY 0x0004 +#define FLAGS_CLOSEPENDING 0x0008 + +/* FIXME: Make this stuff go away please */ +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#define _stdcall +#define __cdecl + +#define min(x,y) ((x) < (y)) ? (x) : (y) + +#define MM_CREATIVE 2 +#define MAXPNAMELEN 32 /* max product name length (including NULL) */ +#define MAX_CARD_DESC 80 + +#define MM_CREATIVE_DRIVER_NAME "SoundBlaster" +#define MM_CREATIVE_DRIVER_VERSION 0x400 +#define MM_CREATIVE_MIXER_PID 409 +#define MM_CREATIVE_WAVEOUT_PID 104 +#define MM_CREATIVE_WAVEIN_PID 4 +#define MM_CREATIVE_MIDIOUT_PID 302 +#define MM_CREATIVE_MIDIIN_PID 202 + +struct memhandle +{ + unsigned long physaddx; + void *virtaddx; + u32 order; +}; + +struct sblive_list +{ + struct sblive_list *next; +}; + +int osAllocMemPhysical(u32, struct memhandle **); +int osFreeMemPhysical(struct memhandle **); +int osListAttach(struct sblive_list **head, struct sblive_list *new); +int osListRemove(struct sblive_list **head, struct sblive_list *pDead); +struct sblive_list *osListGetNext(struct sblive_list *head, struct sblive_list *curr); + +#define MAX_MEMREGISTER 1 +#define MAX_IOPORT 4 +#define MAX_IRQ 2 +#define MAX_DMA 2 + +struct sblive_config +{ + u32 vendorid; /* Vendor ID */ + u32 serialno; /* Serial number */ + u32 logdevid; /* Logical device ID */ + u32 chiprev; /* Chip revision */ + u16 nummemwindows; /* Number of memory windows */ + u32 membase[MAX_MEMREGISTER]; /* Memory window base */ + u32 memlength[MAX_MEMREGISTER]; /* Memory window length */ + u16 memattrib[MAX_MEMREGISTER]; /* Memory window attributes */ + u16 numioports; /* Number of I/O ports */ + u16 ioportbase[MAX_IOPORT]; /* I/O port base */ + u16 ioportlenh[MAX_IOPORT]; /* I/O port length */ + u16 numirq; /* Number of IRQs */ + u8 IRQregs[MAX_IRQ]; /* IRQ list */ + u8 irqattr[MAX_IRQ]; /* IRQ attributes list */ + u16 numdma; /* Number of DMA channels */ + u8 dmalst[MAX_DMA]; /* DMA list */ + u16 dmaattr[MAX_DMA]; /* DMA attributes list */ +}; + +#define DEBUG_LEVEL 3 + +#ifdef EMU10K1_DEBUG +# define DPD(level,x,y...) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ , y );} while(0); +# define DPF(level,x) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ );} while(0); +#else +# define DPD(level,x,y...) /* not debugging: nothing */ +# define DPF(level,x) +#endif /* EMU10K1_DEBUG */ + + +#include "8010.h" + +struct sblive_hw; + +#include "voicemgr.h" + +struct emuaddr_allocdesc +{ + struct sblive_hw *sb_hw; + u32 ownertype; + CALLBACKFN callback; + unsigned long callback_data; + u32 size; /* Size in bytes requested */ + u32 flags; /* stereo/mono 8/16 */ +}; + +struct emu_page +{ + u16 flags; + struct sblive_hw *sb_hw; + u32 emustartaddr; +}; + +struct emu_addrmgr +{ + struct emu_page emupagetable[MAXPAGES]; +}; + +int emu10kaddxmgrInit(struct sblive_hw *); +int emu10kaddxmgrExit(struct sblive_hw *); +int emu10kaddxmgrAlloc(struct emuaddr_allocdesc *,struct emu_page * *); +int emu10kaddxmgrFree(struct emu_page *); + +#include "timer.h" +#include "irqmgr.h" +#include "efxmgr.h" + +/* DATA STRUCTURES */ + +struct sblive_hw +{ + struct sblive_hw *next, *prev; + + unsigned long mixeraddx; + unsigned long hwconfigaddx; + + struct memhandle *virtualpagetable; + + struct memhandle *tankmem; + u32 tmemsize; + struct memhandle *silentpage; + + spinlock_t emu_lock; + + struct voice_mgr voice_manager; + struct emu_addrmgr emu_addrmgr; + + struct emu_timer *timer; + unsigned timer_delay; + spinlock_t timer_lock; + + struct sblive_irq hw_irq; + + unsigned long hwaddr; + + unsigned numvoices; + unsigned awecount; + u32 intrstate; + + unsigned long joybase; + struct pci_dev *pcidev_joy; + unsigned long audio_num; + unsigned long mixer_num; + unsigned long midi_num; + unsigned long stat_num; + struct sblive_waveout *card_waveout; + struct sblive_wavein *card_wavein; + struct sblive_mpuout *card_mpuout; + struct sblive_mpuin *card_mpuin; + u16 arrwVol[25]; /* Size is hardcoded. Bad! */ + unsigned int modcnt; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + u8 paneffectsbus; + u8 auxeffectsbus; + u8 choruseffectsbus; + u8 reverbeffectsbus; + + u32 powerstate; // Current Power State + u32 mpuacqcount; // Mpu acquire count + struct sblive_config dsCardCfg; + u32 has_toslink; // TOSLink detection +}; + +#define ENABLE 0xffffffff +#define DISABLE 0x00000000 + +#define ENV_ON 0x80 +#define ENV_OFF 0x00 + +/* EMU Irq Types */ +#define IRQTYPE_PCIBUSERROR IPR_PCIERROR +#define IRQTYPE_MIXERBUTTON (IPR_VOLINCR | IPR_VOLDECR | IPR_MUTE) +#define IRQTYPE_VOICE (IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK) +#define IRQTYPE_RECORD (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL | IPR_MICBUFFULL | IPR_MICBUFHALFFULL | IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL) +#define IRQTYPE_MPUOUT IPR_MIDITRANSBUFEMPTY +#define IRQTYPE_MPUIN IPR_MIDIRECVBUFEMPTY +#define IRQTYPE_TIMER IPR_INTERVALTIMER +#define IRQTYPE_SPDIF (IPR_GPSPDIFSTATUSCHANGE | IPR_CDROMSTATUSCHANGE) +#define IRQTYPE_DSP IPR_FXDSP + +#define TIMEOUT 16384 + +u32 srToPitch(u32); +u8 sumVolumeToAttenuation(u32); + +int halInit(struct sblive_hw *, struct sblive_config *, u32 *); +int halExit(struct sblive_hw *); + +extern u8 gabMsgLenChannel[]; +extern u8 gabMsgLenSystem[]; +extern spinlock_t sblive_spinlock; + +/* Hardware Abstraction Layer access functions */ + +#define WRITE_FN0(a,b,c) sblive_wrtmskfn0((a),(u8)(b), ((1 << (((b) >> 24) & 0x3f)) - 1) << (((b) >> 16) & 0x1f), (c) << (((b) >> 16) & 0x1f)) + +#define READ_FN0(a,b) sblive_rdmskfn0((a),(u8)(b),((1 << (((b) >> 24) & 0x3f)) - 1) << (((b) >> 16) & 0x1f)) >> (((b) >> 16) & 0x1f) + +#define WRITE_EFX(a, b, c) sblive_writeptr((a), MICROCODEBASE + (b), 0, (c)) + +void sblive_writefn0(struct sblive_hw *, u8 , u32 ); +void sblive_wrtmskfn0(struct sblive_hw *, u8 , u32 , u32 ); + +u32 sblive_readfn0(struct sblive_hw *, u8 ); +u32 sblive_rdmskfn0(struct sblive_hw *, u8, u32 ); + +void sblive_writeptr(struct sblive_hw *, u32 , u32 , u32 ); +u32 sblive_readptr(struct sblive_hw *, u32 , u32 ); + +void halSetStopOnLoop(struct sblive_hw *, u32); +void halClearStopOnLoop(struct sblive_hw *, u32); +void halWC_WAIT(struct sblive_hw *, u32); + +/* AC97 Mixer access function */ +int sblive_readac97(struct sblive_hw *, u8, u16 *); +int sblive_writeac97(struct sblive_hw *, u8, u16); +int sblive_rmwac97(struct sblive_hw *, u8, u16, u16); + +/* MPU access function*/ +int hwmpuWriteData(struct sblive_hw *, u8); +int hwmpuReadData(struct sblive_hw *, u8 *); +int hwmpuReset(struct sblive_hw *); +int hwmpuAcquire(struct sblive_hw *); +int hwmpuRelease(struct sblive_hw *); + +#endif /* _HWACCESS_H */ Index: oldkernel/linux/drivers/sound/emu10k1/icardmid.h diff -u /dev/null linux/drivers/sound/emu10k1/icardmid.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/icardmid.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,199 @@ +/* + ********************************************************************** + * isblive_mid.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _ICARDMIDI_H +#define _ICARDMIDI_H + +/* MIDI defines */ +#define MIDI_DATA_FIRST 0x00 +#define MIDI_DATA_LAST 0x7F +#define MIDI_STATUS_FIRST 0x80 +#define MIDI_STATUS_LAST 0xFF + +/* Channel status bytes */ +#define MIDI_STATUS_CHANNEL_FIRST 0x80 +#define MIDI_STATUS_CHANNEL_LAST 0xE0 +#define MIDI_STATUS_CHANNEL_MASK 0xF0 + +/* Channel voice messages */ +#define MIDI_VOICE_NOTE_OFF 0x80 +#define MIDI_VOICE_NOTE_ON 0x90 +#define MIDI_VOICE_POLY_PRESSURE 0xA0 +#define MIDI_VOICE_CONTROL_CHANGE 0xB0 +#define MIDI_VOICE_PROGRAM_CHANGE 0xC0 +#define MIDI_VOICE_CHANNEL_PRESSURE 0xD0 +#define MIDI_VOICE_PITCH_BEND 0xE0 + +/* Channel mode messages */ +#define MIDI_MODE_CHANNEL MIDI_VOICE_CONTROL_CHANGE + +/* System status bytes */ +#define MIDI_STATUS_SYSTEM_FIRST 0xF0 +#define MIDI_STATUS_SYSTEM_LAST 0xFF + +/* System exclusive messages */ +#define MIDI_SYSEX_BEGIN 0xF0 +#define MIDI_SYSEX_EOX 0xF7 + +/* System common messages */ +#define MIDI_COMMON_TCQF 0xF1 /* Time code quarter frame */ +#define MIDI_COMMON_SONG_POSITION 0xF2 +#define MIDI_COMMON_SONG_SELECT 0xF3 +#define MIDI_COMMON_UNDEFINED_F4 0xF4 +#define MIDI_COMMON_UNDEFINED_F5 0xF5 +#define MIDI_COMMON_TUNE_REQUEST 0xF6 + +/* System real-time messages */ +#define MIDI_RTIME_TIMING_CLOCK 0xF8 +#define MIDI_RTIME_UNDEFINED_F9 0xF9 +#define MIDI_RTIME_START 0xFA +#define MIDI_RTIME_CONTINUE 0xFB +#define MIDI_RTIME_STOP 0xFC +#define MIDI_RTIME_UNDEFINED_FD 0xFD +#define MIDI_RTIME_ACTIVE_SENSING 0xFE +#define MIDI_RTIME_SYSTEM_RESET 0xFF + +/* Flags for flags parm of midiOutCachePatches(), midiOutCacheDrumPatches() */ +#define MIDI_CACHE_ALL 1 +#define MIDI_CACHE_BESTFIT 2 +#define MIDI_CACHE_QUERY 3 +#define MIDI_UNCACHE 4 + +/* Event declarations for MPU IRQ Callbacks */ +#define ICARDMIDI_INLONGDATA 0x00000001 /* MIM_LONGDATA */ +#define ICARDMIDI_INLONGERROR 0x00000002 /* MIM_LONGERROR */ +#define ICARDMIDI_OUTLONGDATA 0x00000004 /* MOM_DONE for MPU OUT buffer */ +#define ICARDMIDI_INDATA 0x00000010 /* MIM_DATA */ +#define ICARDMIDI_INDATAERROR 0x00000020 /* MIM_ERROR */ + +/* Declaration for flags in CARDMIDIBUFFERHDR */ +/* Make it the same as MHDR_DONE, MHDR_INQUEUE in mmsystem.h */ +#define MIDIBUF_DONE 0x00000001 +#define MIDIBUF_INQUEUE 0x00000004 + +/* Declaration for msg parameter in midiCallbackFn */ +#define ICARDMIDI_OUTBUFFEROK 0x00000001 +#define ICARDMIDI_INMIDIOK 0x00000002 + +#define MIDICAPS_INPUT 0x00001000 +#define MIDICAPS_OUTPUT 0x00002000 +#define MIDICAPS_SOUNDFONT 0x00004000 + +/* Declaration for technology in struct midi_caps */ +#define MT_MIDIPORT 0x00000001 /* In original MIDIOUTCAPS structure */ +#define MT_FMSYNTH 0x00000004 /* In original MIDIOUTCAPS structure */ +#define MT_AWESYNTH 0x00001000 +#define MT_PCISYNTH 0x00002000 +#define MT_PCISYNTH64 0x00004000 +#define CARDMIDI_AWEMASK 0x0000F000 + +/* Flags for wTechnology field of MIDIOUTCAPS structure */ +#define MOD_MIDIPORT 1 /* Output port */ +#define MOD_SYNTH 2 /* Generic internal synth */ +#define MOD_SQSYNTH 3 /* Square wave internal synth */ +#define MOD_FMSYNTH 4 /* FM internal synth */ +#define MOD_MAPPER 5 /* MIDI mapper */ + +/* Bit definitions for caps for struct midi_caps */ +#define CARDMIDI_OUT 0x00000001 +#define CARDMIDI_IN 0x00000002 + +/* Definitions for product in struct midi_caps */ +#define MM_CREATIVE_MIDIOUT 201 +#define MM_CREATIVE_MIDIIN 202 +#define MM_CREATIVE_FMSYNTH_STEREO 302 +#define MM_CREATIVE_MIDI_AWE32 303 +#define MM_CREATIVE_MIDI_EMU8008 305 +#define MM_CREATIVE_MIDI_EMU10K1 306 + +/* MIDI data block header */ +struct midi_hdr +{ + u8 *reserved; /* Pointer to original locked data block */ + u32 bufferlength; /* Length of data in data block */ + u32 bytesrecorded; /* Used for input only */ + u32 user; /* For client's use */ + u32 flags; /* Assorted flags (see defines) */ + struct midi_hdr *next; /* Reserved for driver */ + u8 *lpData; /* Second copy of first pointer */ +}; + +struct midi_caps +{ + u32 cbsize; + u32 support; + u32 technology; + u32 product; + u32 manufacturer; + u32 voices; + u32 notes; + u32 channelmask; + u8 MIDIname[MAXPNAMELEN]; + u32 caps; +}; + +struct midi_cache +{ + u32 cbsize; + u32 action; + u32 bank; + u16 array[128]; +}; + +/* Enumeration for SetControl */ +enum +{ + MIDIOBJVOLUME = 0x1, + MIDIQUERYACTIVEINST +}; + + +struct midi_queue +{ + struct midi_queue *next; + u32 qtype; /* 0 = short message, 1 = long data */ + u32 length; + u32 sizeLeft; + u8 *midibyte; + unsigned long refdata; +}; + +struct midi_openinfo +{ + u32 cbsize; + u32 flags; + unsigned long refdata; + u32 streamid; +}; + +int midiCallbackFn(unsigned long , unsigned long, unsigned long *); + +#endif /* _ICARDMIDI_H */ Index: oldkernel/linux/drivers/sound/emu10k1/icardwav.h diff -u /dev/null linux/drivers/sound/emu10k1/icardwav.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/icardwav.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,96 @@ +/* + ********************************************************************** + * icardwav.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _ICARDWAV_H +#define _ICARDWAV_H + +#define MAX_CARDWAVE_DESC 80 + +struct wave_caps +{ + u32 product_id; + u32 caps; + u32 controls; + u32 maxchannels; + u32 minrate, maxrate; + char wavedesc[MAX_CARDWAVE_DESC]; +}; + +#define CARDWAVE_OUT 0x0001 +#define CARDWAVE_IN 0x0002 +#define CARDWAVE_ALLOW_DIRECTXFER 0x0004 +#define CARDWAVE_CONTINUOUS_RATE 0x0008 +#define CARDWAVE_ALLOW_3D 0x0010 + +#define CARDWAVE_CONTROL_VOLUME 0x0001 +#define CARDWAVE_CONTROL_MUTE 0x0002 +#define CARDWAVE_CONTROL_PAN 0x0004 +#define CARDWAVE_CONTROL_RATE 0x0008 +#define CARDWAVE_CONTROL_XFERPOS 0x1000 +#define CARDWAVE_CONTROL_LOOPING 0x2000 + +#define CARDWAVE_PLAY_LOOPING 0x00000001 + +struct wave_format +{ + u32 samplingrate; + u32 bitspersample; + u32 channels; /* 1 = Mono, 2 = Stereo */ + int isinput; + u32 flags; +}; + +#define CARDWAVE_FORMAT_NORMAL 0x0000 +#define CARDWAVE_FORMAT_NOCONVERT_RATE 0x0001 +#define CARDWAVE_FORMAT_NOCONVERT_BITS 0x0002 +#define CARDWAVE_FORMAT_NOCONVERT_CHANNEL 0x0004 +#define CARDWAVE_FORMAT_NOCONVERT_ALL (CARDWAVE_FORMAT_NOCONVERT_RATE | CARDWAVE_FORMAT_NOCONVERT_BITS | CARDWAVE_FORMAT_CHANNEL ) +#define CARDWAVE_FORMAT_DIRECTSOUND 0x2000 +#define CARDWAVE_FORMAT_DUP_BUFFER 0x4000 +#define CARDWAVE_FORMAT_DO_DIRECTXFER 0x8000 +#define CARDWAVE_FORMAT_3D_BUFFER 0x0100 +#define CARDWAVE_FORMAT_SOFTSYNTH 0x0200 +#define CARDWAVE_FORMAT_PRIMARY_BUFFER 0x0400 + +/* Flags for QueryFormat */ +#define CARDWAVE_QF_RATE 0x00000001 +#define CARDWAVE_QF_BITS 0x00000002 +#define CARDWAVE_QF_CHANNEL 0x00000004 +#define CARDWAVE_QF_STEREO 0x00000008 +#define CARDWAVE_QF_ALL (CARDWAVE_QF_RATE | CARDWAVE_QF_BITS | CARDWAVE_QF_CHANNEL | CARDWAVE_QF_STEREO) +#define CARDWAVE_QF_DIRECTXFER 0x40000000 +#define CARDWAVE_QF_PHYSICAL 0x80000000 + +/* Buffer type */ +#define CARDWAVE_DMA_BUFFER 0x0001 +#define CARDWAVE_LINEAR_BUFFER 0x0002 + +#endif /* _ICARDWAV_H */ Index: oldkernel/linux/drivers/sound/emu10k1/irqmgr.c diff -u /dev/null linux/drivers/sound/emu10k1/irqmgr.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/irqmgr.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,238 @@ +/* + ********************************************************************** + * irqmgr.c - IRQ manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "cardmi.h" +#include "cardmo.h" +#include "irqmgr.h" + +/* Interrupt handler */ +static void emu10k1_interrupt(int nIRQ, void *pvDevID, struct pt_regs *pRegs) +{ + struct sblive_irq *irq_ptr = (struct sblive_irq *) pvDevID; + struct sblive_hw *sb_hw = irq_ptr->sb_hw; + u32 irqstatus, ptr, tmp; + + if (sb_hw->hw_irq.ID != EMU10K1_INTERRUPT_ID) + return; + + DPD(4, "emu10k1_interrupt called, nIRQ = %u\n", nIRQ); + + /* Preserve PTR register */ + ptr = sblive_readfn0(sb_hw, PTR); + + /* + ** NOTE : + ** We do a 'while loop' here cos on certain machines, with both + ** playback and recording going on at the same time, IRQs will + ** stop coming in after a while. Checking IPND indeed shows that + ** there are interrupts pending but the PIC says no IRQs pending. + ** I suspect that some boards need edge-triggered IRQs but are not + ** getting that condition if we don't completely clear the IPND + ** (make sure no more interrupts are pending). + ** - Eric + */ + + irqstatus = sblive_readfn0(sb_hw, IPR); + + do + { + DPD(4, "irq status %x\n", irqstatus); + + tmp = irqstatus; + + if (irqstatus & IRQTYPE_TIMER) + { + sblive_timerIrqCallback(sb_hw); + irqstatus &= ~IRQTYPE_TIMER; + } + + if (irqstatus & IRQTYPE_RECORD) + { + sblive_waveinIrqCallback(sb_hw); + irqstatus &= ~IRQTYPE_RECORD; + } + + if (irqstatus & IRQTYPE_MPUIN) + { + sblive_mpuinIrqCallback(sb_hw); + irqstatus &= ~IRQTYPE_MPUIN; + } + + if (irqstatus & IRQTYPE_MPUOUT) + { + sblive_mpuoutIrqCallback(sb_hw); + irqstatus &= ~IRQTYPE_MPUOUT; + } + + if(irqstatus) + sblive_irqmgrDisableIrq(sb_hw, irqstatus); + + sblive_writefn0(sb_hw, IPR, tmp); + + } while ((irqstatus = sblive_readfn0(sb_hw, IPR))); + + sblive_writefn0(sb_hw, PTR, ptr); + + return; +} + +/************************************************************************ + * + * int sblive_irqmgrInit(struct sblive_hw * sb_hw) + * + * ENTRY + * sb_hw - Card object + * + * RETURNS + * SUCCESS - CTSTATUS_SUCCESS + * FAILURE - CTSTATUS_ERROR + * + * ABOUT + * Initialization + * + ************************************************************************/ +int sblive_irqmgrInit(struct sblive_irq *irq_ptr) +{ + unsigned long flags; + + irq_ptr->status = 0; + irq_ptr->ID = EMU10K1_INTERRUPT_ID; + + flags = SA_SHIRQ; + + /* Reserve IRQ Line */ + if (request_irq(irq_ptr->irq, emu10k1_interrupt, flags, "emu10k1", irq_ptr)) + { + DPD(2, "emu10k1: irq %u in use\n", (unsigned int) irq_ptr->irq); + irq_ptr->status &= ~FLAGS_ENABLED; + DPF(2, "Error initializing IRQ\n"); + return CTSTATUS_ERROR; + } + + irq_ptr->status |= (FLAGS_ENABLED | FLAGS_AVAILABLE); + + if (irq_ptr->status & (FLAGS_ENABLED | FLAGS_AVAILABLE)) + irq_ptr->status &= (~FLAGS_AVAILABLE); + else + { + DPF(2, "IRQ acquire failure\n"); + return CTSTATUS_INUSE; + } + + DPD(2, "IRQ : Acquired channel %u\n", irq_ptr->irq); + return CTSTATUS_SUCCESS; +} + + +/************************************************************************ + * + * int sblive_irqmgrExit(struct sblive_irq *irq_ptr) + * + * ENTRY + * irq_ptr - Card irq object + * + * RETURNS + * Always returns CTSTATUS_SUCCESS + * + * ABOUT + * Shutdown code + * + ************************************************************************/ +int sblive_irqmgrExit(struct sblive_irq *irq_ptr) +{ + irq_ptr->status |= FLAGS_AVAILABLE; + free_irq(irq_ptr->irq, irq_ptr); + irq_ptr->status &= ~FLAGS_ENABLED; + + return CTSTATUS_SUCCESS; +} + +/************************************************************************ + * + * int sblive_irqmgrEnableIrq(struct sblive_hw * sb_hw, u16 wIrqType) + * + * ENTRY + * sb_hw - Card object + * wIrqType- type of service to enable interrupt for + * + * RETURNS + * Always returns CTSTATUS_SUCCESS + * + * ABOUT + * Enables the specified irq service + * + ************************************************************************/ +int sblive_irqmgrEnableIrq(struct sblive_hw *sb_hw, u32 irqtype) +{ + /* + * TODO : + * put protection here so that we don't accidentally + * screw-up another cardxxx objects irqs + */ + + DPD(4, "sblive_irqmgrEnableIrq %x\n", irqtype); + sblive_wrtmskfn0(sb_hw, INTE, irqtype, ENABLE); + + return CTSTATUS_SUCCESS; +} + + +/************************************************************************ + * + * int sblive_irqmgrDisableIrq(struct sblive_hw * sb_hw, u16 wIrqType) + * + * ENTRY + * sb_hw - Card object + * wIrqType- type of service to disable interrupt for + * + * RETURNS + * Always returns CTSTATUS_SUCCESS + * + * ABOUT + * Disables the specified irq service + * + ************************************************************************/ + +int sblive_irqmgrDisableIrq(struct sblive_hw *sb_hw, u32 irqtype) +{ + /* + * TODO : + * put protection here so that we don't accidentally + * screw-up another cardxxx objects irqs + */ + + DPD(4, "sblive_irqmgrDisableIrq %x\n", irqtype); + sblive_wrtmskfn0(sb_hw, INTE, irqtype, DISABLE); + + return CTSTATUS_SUCCESS; +} + Index: oldkernel/linux/drivers/sound/emu10k1/irqmgr.h diff -u /dev/null linux/drivers/sound/emu10k1/irqmgr.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/irqmgr.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,61 @@ +/* + ********************************************************************** + * irq.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _IRQ_H +#define _IRQ_H + +struct sblive_irq +{ + u32 ID; + u32 status; + u32 irq; + struct sblive_hw *sb_hw; +}; + +struct sblive_wavedevice +{ + struct sblive_hw *sb_hw; + struct wiinst *wiinst; + struct woinst *woinst; + u16 enablebits; +}; + +int sblive_timerIrqCallback(struct sblive_hw *); +int sblive_waveinIrqCallback(struct sblive_hw *); + +int sblive_irqmgrInit(struct sblive_irq *); +int sblive_irqmgrExit(struct sblive_irq *); +int sblive_irqmgrEnableIrq(struct sblive_hw *, u32); +int sblive_irqmgrDisableIrq(struct sblive_hw *, u32); + +#define EMU10K1_INTERRUPT_ID 0xfeebbeef + +#endif /* _IRQ_H */ Index: oldkernel/linux/drivers/sound/emu10k1/main.c diff -u /dev/null linux/drivers/sound/emu10k1/main.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/main.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,369 @@ +/* + ********************************************************************** + * main.c - Creative EMU10K1 audio driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up stuff + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + * + * Supported devices: + * /dev/dsp: Standard /dev/dsp device, OSS-compatible + * /dev/mixer: Standard /dev/mixer device, OSS-compatible + * /dev/sndstat: Standard /dev/sndstat device, mostly OSS-compatible + * /dev/midi: Raw MIDI UART device, mostly OSS-compatible + * + * Revision history: + * 0.1 beta Initial release + * 0.2 Lowered initial mixer vol. Improved on stuttering wave playback. Added MIDI UART support. + * 0.3 Fixed mixer routing bug, added APS, joystick support. + * 0.4 Added rear-channel, SPDIF support. + * + ********************************************************************** + */ + + +/* These are only included once per module */ +#ifdef MODULE +#include +#else + +#endif + +#include "hwaccess.h" +#include "mycommon.h" +#include "cardwo.h" +#include "audio.h" +#include "midi.h" +#include "sndstat.h" + +#ifndef PCI_VENDOR_ID_CREATIVE +#define PCI_VENDOR_ID_CREATIVE 0x1102 +#endif + +#ifndef PCI_DEVICE_ID_CREATIVE_EMU10K1 +#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 +#endif + +#define EMU10K1_EXTENT 0x20 /* 32 byte I/O space */ +#define JOY_EXTENT 0x8 /* 8 byte I/O space */ +#define MAX_EMU10K1 5 + +/* Global var instantiation */ + +struct sblive_hw *sblive_devs = NULL; +static unsigned long joystick[MAX_EMU10K1 + 1] = +{0,}; + +/* Function prototypes */ +extern int sblive_mixer_init(struct sblive_hw *sb_hw); + +/* Driver initialization routine */ +#ifdef MODULE +int init_module(void) +#else + int __init init_emu10k1(void) +#endif +{ + struct sblive_hw *hw_obj; + u32 hwflags = 0; + unsigned int index = 0, serial = 0; + unsigned long ioaddr; + unsigned char bus, devfn, ucRevID = 0; + struct sblive_config dsConfig; + struct sblive_config *config = &dsConfig; + struct pci_dev *pcidev=NULL, *pcidev_joy=NULL; + unsigned long irq; + unsigned long ioaddr1 = 0; + + DPF(2, "init_module() called\n"); + + /* Do not export symbols */ +#ifdef MODULE + EXPORT_NO_SYMBOLS; +#endif /* MODULE */ + + if (!pci_present()) + return -ENODEV; + + /* PCI BIOS detected */ + for (index = 0; index < MAX_EMU10K1 && + (pcidev = pci_find_device(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_EMU10K1, pcidev)); + index++) + { + bus = pcidev->bus->number; + devfn = pcidev->devfn; + DPD(2, "Function 0: devfn %x\n", devfn); + /* Joystick is on the same bus and dev, but is function #1 */ + if ((pcidev_joy=pci_find_slot(bus, devfn + 1))) { + DPD(2, "Function 1 exists: devfn %x\n", devfn + 1); + } + /* Device found - Query PCI BIOS info */ + DPD(1, "Emu10k1 device %d found!\n", index); + + ioaddr = RSRCADDRESS(pcidev, 0); + if (pcidev_joy) + ioaddr1 = RSRCADDRESS(pcidev_joy, 0); + + if (!RSRCISIOREGION(pcidev, 0) || !RSRCISIOREGION(pcidev_joy, 0)) + continue; + + ioaddr = ioaddr & PCI_BASE_ADDRESS_IO_MASK; + DPD(1, "Function 0 IO Base Address = %#lx\n", ioaddr); + + if (pcidev_joy) + { + ioaddr1 = ioaddr1 & PCI_BASE_ADDRESS_IO_MASK; + DPD(1, "Function 1 IO Base Address = %#lx\n", ioaddr1); + } + + irq = pcidev->irq; + + /* + * Some BIOSes screw this stuff up. Its worth reporting + * to the user + */ + + if (irq == 0) + { + printk(KERN_ERR "sblive: No IRQ has been assigned to this device. Check your BIOS settings.\n"); + continue; + } + + DPD(1, "IRQ Line = %lu\n", irq); + + pci_read_config_byte(pcidev, PCI_REVISION_ID, &ucRevID); + DPD(1, "Revision ID = %u\n", ucRevID); + + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &serial); + DPD(1, "Serial No = %08x\n", serial); + + /* Initialize device struct */ + hw_obj = kmalloc(sizeof(*hw_obj), GFP_KERNEL); + if (!hw_obj) + { + printk(KERN_WARNING "emu10k1: out of memory\n"); + continue; + } + DPD(3, "kmalloc: [%p]\n", hw_obj); + memset(hw_obj, 0, sizeof(*hw_obj)); /* FIXME: use bzero()? */ + + hw_obj->pcidev_joy = pcidev_joy; + hw_obj->joybase = ioaddr1; + printk(KERN_INFO "Creative SBLive! at %#lx on irq %lu\n", ioaddr, irq); + + DPD(3, "kmalloc: [%p]\n", config); + memset(config, 0, sizeof(*config)); /* FIXME: use bzero()? */ + + config->vendorid = PCI_VENDOR_ID_CREATIVE; + config->serialno = serial; + config->logdevid = PCI_DEVICE_ID_CREATIVE_EMU10K1; + config->chiprev = ucRevID; + + config->nummemwindows = 0; + + config->numioports = 1; + config->ioportbase[0] = ioaddr; + config->ioportlenh[0] = EMU10K1_EXTENT; + + config->numirq = 1; + config->IRQregs[0] = irq; + config->irqattr[0] = 0; /* Doesn't matter */ + + config->numdma = 0; + + /* Reserve I/O region */ + if (check_region(ioaddr, EMU10K1_EXTENT)) + { + printk(KERN_ERR "emu10k1: io ports %#lx-%#lx in use\n", + ioaddr, ioaddr + EMU10K1_EXTENT - 1); + goto err_region1; + } + request_region(ioaddr, EMU10K1_EXTENT, "emu10k1"); + + if (check_region(ioaddr1, JOY_EXTENT)) + { + printk(KERN_ERR "emu10k1: io ports %#x-%#x in use\n", + (unsigned int) ioaddr1, (unsigned int) ioaddr1 + JOY_EXTENT - 1); + goto err_region2; + } + request_region(ioaddr1, JOY_EXTENT, "emu10k1 joystick"); + + /* Register devices */ + hw_obj->audio_num = register_sound_dsp(&emu10k1_audio_fops, -1); + if (hw_obj->audio_num < 0) + { + printk(KERN_ERR "emu10k1: cannot register audio device!\n"); + goto err_dev1; + } + + hw_obj->mixer_num = register_sound_mixer(&emu10k1_mixer_fops, -1); + if (hw_obj->mixer_num < 0) + { + printk(KERN_ERR "emu10k1: cannot register mixer device!\n"); + goto err_dev2; + } + + hw_obj->midi_num = register_sound_midi(&emu10k1_midi_fops, -1); + if (hw_obj->midi_num < 0) + { + printk(KERN_ERR "emu10k1: cannot register MIDI device!\n"); + goto err_dev4; + } + + if (pcidev_joy) + { + if ((joystick[index] & ~0x18) == 0x200) + { + if (check_region(joystick[index], JOY_EXTENT)) + printk(KERN_ERR "emu10k1: joystick io ports %lx-%lx in use\n", + joystick[index], joystick[index] + JOY_EXTENT - 1); + else { + /* Write joystick base address to PCI configuration space */ + pci_write_config_dword(pcidev_joy, PCI_BASE_ADDRESS_0, joystick[index]); +#ifdef EMU10K1_DEBUG + { + u32 base; + pci_read_config_dword(pcidev_joy, PCI_BASE_ADDRESS_0, &base); + DPD(2, " read back joystick base address is %x\n", base); + } +#endif + } + } + } + + /* Register-level initialization goes here.... */ + DPF(2, "Initializing hardware....\n"); + /* IRQ reservation happens in halInit */ + if (halInit(hw_obj, config, &hwflags) != CTSTATUS_SUCCESS) { + printk(KERN_ERR "emu10k1: cannot initialize device!\n"); + goto err_hal_init; + } + init_MUTEX(&hw_obj->open_sem); + hw_obj->open_mode = 0; + init_waitqueue_head(&hw_obj->open_wait); + + DPD(2, "Hardware initialized. TRAM allocated: %u bytes\n", (unsigned int) hw_obj->tmemsize); + + + audio_init(hw_obj, "SBLive!"); + sblive_mixer_init(hw_obj); + + midi_init(hw_obj); + + /* AOK, update global device list */ + hw_obj->next = sblive_devs; + sblive_devs = hw_obj; + + hw_obj->prev = NULL; + if (hw_obj->next) + hw_obj->next->prev = hw_obj; + continue; + + /* Error handlers */ + /* FIXME: These must reflect the device init order... */ + err_hal_init: + halExit(hw_obj); /* Should this be called? */ + + unregister_sound_midi(hw_obj->midi_num); + err_dev4: + unregister_sound_mixer(hw_obj->mixer_num); + err_dev2: + unregister_sound_dsp(hw_obj->audio_num); + err_dev1: + /* free_irq(hw_obj->irq, hw_obj); */ + /* err_irq: */ + release_region(ioaddr1, JOY_EXTENT); + err_region2: + release_region(ioaddr, EMU10K1_EXTENT); + err_region1: + kfree(hw_obj); + DPD(3, "kfree: [%p]\n", hw_obj); + } + + if (!sblive_devs) + return -ENODEV; + + sblive_devs->stat_num = register_sound_special(&emu10k1_sndstat_fops, 6); + if (sblive_devs->stat_num < 0) + printk(KERN_ERR "emu10k1: cannot register sndstat device!\n"); + else + sndstat_init(); + + DPF(2, "Module successfully initialized!\n"); + return 0; +} + +#ifdef MODULE + +MODULE_PARM(joystick, "1-" __MODULE_STRING(MAX_EMU10K1) "i"); +MODULE_PARM_DESC(joystick, "sets address and enables joystick interface (still need separate driver)"); +MODULE_AUTHOR("Bertrand Lee, Cai Ying. (Email to: linux_bug@soundblaster.com)"); +MODULE_DESCRIPTION("Creative EMU10K1 Driver v0.4\nCopyright (C) 1999 Creative Technology Ltd."); + +/* Driver exit routine */ +void cleanup_module(void) +{ + struct sblive_hw *hw_obj; + + DPF(2, "cleanup_module() called\n"); + + if (sblive_devs && sblive_devs->stat_num >= 0) + unregister_sound_special(6); + + while ((hw_obj = sblive_devs)) + { + sblive_devs = sblive_devs->next; + + /* write original joystick base address to PCI configuration space */ + pci_write_config_dword(hw_obj->pcidev_joy, PCI_BASE_ADDRESS_0, hw_obj->joybase); +#ifdef EMU10K1_DEBUG + { + u32 base; + pci_read_config_dword(hw_obj->pcidev_joy, PCI_BASE_ADDRESS_0, &base); + DPD(2, "cleanup_module: read back joystick base address is %x\n", base); + } +#endif + + audio_exit(hw_obj); + midi_exit(hw_obj); + + /* IRQ is freed in halExit */ + halExit(hw_obj); + DPF(2, "halExit completed\n"); + + release_region(hw_obj->hwaddr, EMU10K1_EXTENT); + release_region(hw_obj->joybase, JOY_EXTENT); + unregister_sound_dsp(hw_obj->audio_num); + unregister_sound_mixer(hw_obj->mixer_num); + unregister_sound_midi(hw_obj->midi_num); + kfree(hw_obj); + DPD(3, "kfree: [%p]\n", hw_obj); + } + + DPF(2, "Module unloaded\n"); +} + +#endif // MODULE Index: oldkernel/linux/drivers/sound/emu10k1/midi.c diff -u /dev/null linux/drivers/sound/emu10k1/midi.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/midi.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,589 @@ +/* + ********************************************************************** + * midi.c - /dev/midi interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifdef MODULE +#define __NO_VERSION__ +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include "hwaccess.h" +#include "mycommon.h" +#include "cardmo.h" +#include "cardmi.h" +#include "midi.h" + + +static spinlock_t midi_spinlock = SPIN_LOCK_UNLOCKED; + +static int emu10k1_midi_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct sblive_hw *sb_hw = sblive_devs; + struct sblive_mididevice *midi_dev; + + DPF(2, "emu10k1_midi_open() called\n"); + + /* Check for correct device to open */ + while (sb_hw && sb_hw->midi_num != minor) + sb_hw = sb_hw->next; + if (!sb_hw) + return -ENODEV; + + /* Wait for device to become free */ + down(&sb_hw->open_sem); + while (sb_hw->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) + { + if (file->f_flags & O_NONBLOCK) + { + up(&sb_hw->open_sem); + return -EBUSY; + } + + up(&sb_hw->open_sem); + interruptible_sleep_on(&sb_hw->open_wait); + + if (signal_pending(current)) + return -ERESTARTSYS; + + down(&sb_hw->open_sem); + } + + midi_dev = (struct sblive_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL); + if (midi_dev == NULL) + { + DPF(2, " MIDIDEVICEOBJ alloc fail."); + return -EINVAL; + } + + DPD(3, "kmalloc: [%p]\n", midi_dev); + + midi_dev->sb_hw = sb_hw; + midi_dev->mistate = MIDIIN_STATE_STOPPED; + init_waitqueue_head(&midi_dev->oWait); + init_waitqueue_head(&midi_dev->iWait); + midi_dev->ird = 0; + midi_dev->iwr = 0; + midi_dev->icnt = 0; + midi_dev->pmiHdrList = NULL; + + if (file->f_mode & FMODE_READ) + { + struct midi_openinfo dsCardMidiOpenInfo; + struct midi_hdr *midihdr1; + struct midi_hdr *midihdr2; + + dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; + + if (sblive_mpuinOpen(sb_hw, &dsCardMidiOpenInfo) + != CTSTATUS_SUCCESS) + { + DPF(2, "sblive_mpuinOpen failed.\n"); + + kfree(midi_dev); + DPD(3, "kfree: [%p]\n", midi_dev); + + return -ENODEV; + } + + /* Add two buffers to receive sysex buffer */ + if (midiInAddBuffer(midi_dev, &midihdr1) != CTSTATUS_SUCCESS) + { + kfree(midi_dev); + DPD(3, "kfree: [%p]\n", midi_dev); + + return -ENODEV; + } + + if (midiInAddBuffer(midi_dev, &midihdr2) != CTSTATUS_SUCCESS) + { + osListRemove((struct sblive_list **) &midi_dev->pmiHdrList, (struct sblive_list *) midihdr1); + + kfree(midihdr1->lpData); + DPD(3, "kfree: [%p]\n", midihdr1->lpData); + + kfree(midihdr1); + kfree(midi_dev); + + DPD(3, "kfree: [%p]\n", midihdr1); + DPD(3, "kfree: [%p]\n", midi_dev); + + return -ENODEV; + } + } + + if (file->f_mode & FMODE_WRITE) + { + struct midi_openinfo dsCardMidiOpenInfo; + + dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev; + + if (sblive_mpuoutOpen(sb_hw, &dsCardMidiOpenInfo) + != CTSTATUS_SUCCESS) + { + DPF(2, "sblive_mpuoutOpen failed.\n"); + + kfree(midi_dev); + DPD(3, "kfree: [%p]\n", midi_dev); + + return -ENODEV; + } + } + + file->private_data = (void *) midi_dev; + + sb_hw->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE); + + up(&sb_hw->open_sem); + + MOD_INC_USE_COUNT; + return 0; +} + +static int emu10k1_midi_release(struct inode *inode, struct file *file) +{ + struct sblive_mididevice *midi_dev = (struct sblive_mididevice *) file->private_data; + struct sblive_hw *sb_hw = midi_dev->sb_hw; + + DPF(2, "emu10k1_midi_release() called\n"); + + if (file->f_mode & FMODE_WRITE) + { + if(!(file->f_flags & O_NONBLOCK)){ + + while (!signal_pending(current) && (sb_hw->card_mpuout->firstmidiq !=NULL)){ + DPF(2, "Cannot close - buffers not empty\n"); + + interruptible_sleep_on(&midi_dev->oWait); + + } + } + + sblive_mpuoutClose(sb_hw); + } + + if (file->f_mode & FMODE_READ) + { + struct midi_hdr *midihdr; + + if (midi_dev->mistate == MIDIIN_STATE_STARTED) + { + sblive_mpuinStop(sb_hw); + midi_dev->mistate = MIDIIN_STATE_STOPPED; + DPF(2, "MIDI in stopped\n"); + } + + sblive_mpuinReset(sb_hw); + sblive_mpuinClose(sb_hw); + + DPF(2, "MIDI in closed\n"); + + while ((midihdr = (struct midi_hdr *) osListGetNext((struct sblive_list *) midi_dev->pmiHdrList, NULL))) + { + osListRemove((struct sblive_list **) &midi_dev-> pmiHdrList, (struct sblive_list *) midihdr); + + kfree(midihdr->lpData); + DPD(3, "kfree: [%p]\n", midihdr->lpData); + + kfree(midihdr); + DPD(3, "kfree: [%p]\n", midihdr); + } + } + + kfree(midi_dev); + + DPD(3, "kfree: [%p]\n", midi_dev); + + down(&sb_hw->open_sem); + sb_hw->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE)); + up(&sb_hw->open_sem); + wake_up(&sb_hw->open_wait); + + MOD_DEC_USE_COUNT; + + return 0; +} + + +static ssize_t emu10k1_midi_read(struct file *file, char *buffer, size_t count, loff_t *pos) +{ + struct sblive_mididevice *midi_dev = (struct sblive_mididevice *) file->private_data; + ssize_t ret = 0; + u16 cnt; + unsigned long flags; + + DPD(2, "emu10k1_midi_read() called, count %d\n", count); + + if (pos != &file->f_pos) + return -ESPIPE; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + if (midi_dev->mistate == MIDIIN_STATE_STOPPED) + { + if (sblive_mpuinStart(midi_dev->sb_hw) + != CTSTATUS_SUCCESS) + { + DPF(2, "sblive_mpuinStart failed.\n"); + return -EINVAL; + } + + midi_dev->mistate = MIDIIN_STATE_STARTED; + } + + while (count > 0) + { + cnt = MIDIIN_BUFLEN - midi_dev->ird; + + spin_lock_irqsave(&midi_spinlock, flags); + + if (midi_dev->icnt < cnt) + cnt = midi_dev->icnt; + + spin_unlock_irqrestore(&midi_spinlock, flags); + + if (cnt > count) + cnt = count; + + if (cnt <= 0) + { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + DPF(2, " Go to sleep...\n"); + + interruptible_sleep_on(&midi_dev->iWait); + + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + + continue; + } + + if (copy_to_user(buffer, midi_dev->iBuf + midi_dev->ird, cnt)) { + DPF(2, " copy_to_user failed."); + return ret ? ret : -EFAULT; + } + + midi_dev->ird += cnt; + midi_dev->ird %= MIDIIN_BUFLEN; + + spin_lock_irqsave(&midi_spinlock, flags); + + midi_dev->icnt -= cnt; + + spin_unlock_irqrestore(&midi_spinlock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + + if (midi_dev->icnt == 0) + break; + + } + + return ret; +} + + +static ssize_t emu10k1_midi_write(struct file *file, const char *buffer, size_t count, loff_t *pos) +{ + struct sblive_mididevice *midi_dev = (struct sblive_mididevice *) file->private_data; + struct midi_hdr *midihdr; + ssize_t ret = 0; + unsigned long flags; + + DPD(2, "emu10k1_midi_write() called, count=%d\n", count); + + if (pos != &file->f_pos) + return -ESPIPE; + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr *), GFP_KERNEL); + if (midihdr == NULL) + return -EINVAL; + + DPD(3, "kmalloc: [%p]\n", midihdr); + + midihdr->bufferlength = count; + midihdr->bytesrecorded = 0; + midihdr->flags = 0; + midihdr->next = NULL; + + midihdr->lpData = (u8 *) kmalloc(count, GFP_KERNEL); + if (midihdr->lpData == NULL) + { + DPF(2, "midihdr->lpData alloc failed\n"); + + kfree(midihdr); + DPD(3, "kfree: [%p]\n", midihdr); + + return -EINVAL; + } + + DPD(3, "kmalloc: [%p]\n", midihdr->lpData); + + if (copy_from_user(midihdr->lpData, buffer, count)) + { + kfree(midihdr->lpData); + DPD(3, "kfree: [%p]\n", midihdr->lpData); + + kfree(midihdr); + DPD(3, "kfree: [%p]\n", midihdr); + + return ret ? ret : -EFAULT; + } + + spin_lock_irqsave(&midi_spinlock, flags); + + if (sblive_mpuoutAddBuffer(midi_dev->sb_hw, midihdr) != CTSTATUS_SUCCESS) + { + DPF(2, "sblive_mpuoutAddBuffer failed.\n"); + + kfree(midihdr->lpData); + DPD(3, "kfree: [%p]\n", midihdr->lpData); + + kfree(midihdr); + DPD(3, "kfree: [%p]\n", midihdr); + + spin_unlock_irqrestore(&midi_spinlock, flags); + return -EINVAL; + } + + spin_unlock_irqrestore(&midi_spinlock, flags); + + return count; +} + + +static unsigned int emu10k1_midi_poll(struct file *file, struct poll_table_struct *wait) +{ + DPF(2, "emu10k1_midi_poll() called\n"); + return 0; +} + + +int midiCallbackFn(unsigned long msg, unsigned long refdata, unsigned long *pmsg) +{ + struct sblive_mididevice *midi_dev = (struct sblive_mididevice *) refdata; + struct midi_hdr *midihdr = NULL; + unsigned long flags; + int i; + + DPF(2, "midiCallbackFn!\n"); + + spin_lock_irqsave(&midi_spinlock, flags); + + switch (msg) + { + case ICARDMIDI_OUTLONGDATA: + midihdr = (struct midi_hdr *) pmsg[2]; + + kfree(midihdr->lpData); + DPD(3, "kfree: [%p]\n", midihdr->lpData); + + kfree(midihdr); + DPD(3, "kfree: [%p]\n", midihdr); + + wake_up(&midi_dev->oWait); + + break; + + case ICARDMIDI_INLONGDATA: + DPF(2, " ICARDMIDI_INLONGDATA\n"); + midihdr = (struct midi_hdr *) pmsg[2]; + + for (i = 0; i < midihdr->bytesrecorded; i++) { + midi_dev->iBuf[midi_dev->iwr++] = midihdr->lpData[i]; + midi_dev->iwr %= MIDIIN_BUFLEN; + } + + midi_dev->icnt += midihdr->bytesrecorded; + + if (midi_dev->mistate == MIDIIN_STATE_STARTED) + { + initMidiHdr(midihdr); + sblive_mpuinAddBuffer(midi_dev->sb_hw->card_mpuin, midihdr); + wake_up(&midi_dev->iWait); + } + break; + + case ICARDMIDI_INDATA: + DPF(2, " ICARDMIDI_INDATA\n"); + { + u8 *pBuf = (u8 *) & pmsg[1]; + u16 bytesvalid = pmsg[2]; + + for (i = 0; i < bytesvalid; i++) { + midi_dev->iBuf[midi_dev->iwr++] = pBuf[i]; + midi_dev->iwr %= MIDIIN_BUFLEN; + } + + midi_dev->icnt += bytesvalid; + } + + wake_up(&midi_dev->iWait); + break; + + default: /* Unknown message */ + DPF(2, "Unknown message\n"); + return CTSTATUS_ERROR; + } + + spin_unlock_irqrestore(&midi_spinlock, flags); + + return CTSTATUS_SUCCESS; +} + + +/* Local functions */ +int midiInAddBuffer(struct sblive_mididevice *midi_dev, struct midi_hdr **midihdrptr) +{ + struct midi_hdr *midihdr; + + midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr *), GFP_KERNEL); + if (midihdr == NULL) + return -EINVAL; + + DPD(3, "kmalloc: [%p]\n", midihdr); + + initMidiHdr(midihdr); + + midihdr->lpData = (u8 *) kmalloc(MIDIIN_BUFLEN, GFP_KERNEL); + if (midihdr->lpData == NULL) + { + DPF(2, "midihdr->lpData alloc fail.\n"); + + kfree(midihdr); + DPD(3, "kfree: [%p]\n", midihdr); + + return CTSTATUS_ERROR; + } + + DPD(3, "kmalloc: [%p]\n", midihdr->lpData); + + if (sblive_mpuinAddBuffer(midi_dev->sb_hw->card_mpuin, midihdr) != CTSTATUS_SUCCESS) + { + DPF(2, "sblive_mpuinAddBuffer failed.\n"); + + kfree(midihdr->lpData); + DPD(3, "kfree: [%p]\n", midihdr->lpData); + + kfree(midihdr); + DPD(3, "kfree: [%p]\n", midihdr); + + return CTSTATUS_ERROR; + } + + *midihdrptr = midihdr; + osListAttach((struct sblive_list **) &midi_dev->pmiHdrList, (struct sblive_list *) midihdr); + + return CTSTATUS_SUCCESS; +} + +void initMidiHdr(struct midi_hdr * midihdr) +{ + midihdr->bufferlength = MIDIIN_BUFLEN; + midihdr->bytesrecorded = 0; + midihdr->flags = 0; + midihdr->next = NULL; +} + +/* End of local functions */ + + +int midi_init(struct sblive_hw *sb_hw) +{ + /* Initialize CardMpuOut struct */ + sb_hw->card_mpuout = kmalloc(sizeof(struct sblive_mpuout), GFP_KERNEL); + if (sb_hw->card_mpuout == NULL) + { + printk(KERN_WARNING "alloc struct sblive_mpuout: out of memory\n"); + return CTSTATUS_ERROR; + } + + DPD(3, "kmalloc: [%p]\n", sb_hw->card_mpuout); + + sblive_mpuoutInit(sb_hw->card_mpuout, sb_hw); + + /* Initialize CardMpuIn struct */ + sb_hw->card_mpuin = kmalloc(sizeof(struct sblive_mpuin), GFP_KERNEL); + if (sb_hw->card_mpuin == NULL) + { + printk(KERN_WARNING "alloc struct sblive_mpuin: out of memory\n"); + return CTSTATUS_ERROR; + } + + DPD(3, "kmalloc: [%p]\n", sb_hw->card_mpuin); + + sblive_mpuinInit(sb_hw->card_mpuin, sb_hw); + + return CTSTATUS_SUCCESS; +} + +int midi_exit(struct sblive_hw *sb_hw) +{ + sblive_mpuoutExit(sb_hw); + sblive_mpuinExit(sb_hw); + kfree(sb_hw->card_mpuout); + kfree(sb_hw->card_mpuin); + + DPD(3, "kfree: [%p]\n", sb_hw->card_mpuout); + DPD(3, "kfree: [%p]\n", sb_hw->card_mpuin); + + sb_hw->card_mpuout = NULL; + sb_hw->card_mpuin = NULL; + + return CTSTATUS_SUCCESS; +} + + +/* MIDI file operations */ +struct file_operations emu10k1_midi_fops = +{ + NULL, + &emu10k1_midi_read, + &emu10k1_midi_write, + NULL, /* readdir */ + &emu10k1_midi_poll, + NULL, /* ioctl */ + NULL, /* mmap */ + &emu10k1_midi_open, + NULL, /* flush */ + &emu10k1_midi_release, + NULL, /* fsync */ + NULL, /* fasync */ +}; Index: oldkernel/linux/drivers/sound/emu10k1/midi.h diff -u /dev/null linux/drivers/sound/emu10k1/midi.h:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/midi.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,60 @@ +/* + ********************************************************************** + * midi.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _MIDI_H +#define _MIDI_H + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define MIDIIN_STATE_STARTED 0x00000001 +#define MIDIIN_STATE_STOPPED 0x00000002 + +#define MIDIIN_BUFLEN 1024 + +struct sblive_mididevice +{ + struct sblive_hw *sb_hw; + u32 mistate; + wait_queue_head_t oWait; + wait_queue_head_t iWait; + s8 iBuf[MIDIIN_BUFLEN]; + u16 ird, iwr, icnt; + struct midi_hdr *pmiHdrList; +}; + +int midi_init(struct sblive_hw *); +int midi_exit(struct sblive_hw *); +int midiInAddBuffer(struct sblive_mididevice *, struct midi_hdr **); +void initMidiHdr(struct midi_hdr *); + +#endif /* _MIDI_H */ Index: oldkernel/linux/drivers/sound/emu10k1/mixer.c diff -u /dev/null linux/drivers/sound/emu10k1/mixer.c:1.1 --- /dev/null Mon Jul 31 21:13:55 2000 +++ linux/drivers/sound/emu10k1/mixer.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,663 @@ +/* + ********************************************************************** + * mixer.c - /dev/mixer interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + * This program uses some code from es1317.c, Copyright 1998-1999 + * Thomas Sailer + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up stuff + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifdef MODULE +#define __NO_VERSION__ /* Kernel version only defined once */ +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include "hwaccess.h" +#include "mycommon.h" + +#define AC97_PESSIMISTIC +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +#define vol_to_hw_5(swvol) (31 - (((swvol) * 31) / 100)) +#define vol_to_hw_4(swvol) (15 - (((swvol) * 15) / 100)) + +#define vol_to_sw_5(hwvol) (((31 - (hwvol)) * 100) / 31) +#define vol_to_sw_4(hwvol) (((15 - (hwvol)) * 100) / 15) + +/* --------------------------------------------------------------------- */ +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ + +#ifdef hweight32 +#undef hweight32 +#endif + +extern __inline__ unsigned int hweight32(unsigned int w) +{ + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +} + + +/* Mapping arrays */ +static const unsigned int recsrc[] = +{ + SOUND_MASK_MIC, + SOUND_MASK_CD, + SOUND_MASK_VIDEO, + SOUND_MASK_LINE1, + SOUND_MASK_LINE, + SOUND_MASK_VOLUME, + SOUND_MASK_OGAIN, /* Used to be PHONEOUT */ + SOUND_MASK_PHONEIN, + SOUND_MASK_TREBLE, + SOUND_MASK_BASS +}; + +static const unsigned char volreg[SOUND_MIXER_NRDEVICES] = +{ + /* 5 bit stereo */ + [SOUND_MIXER_LINE] = AC97_LINEINVOLUME, + [SOUND_MIXER_CD] = AC97_CDVOLUME, + [SOUND_MIXER_VIDEO] = AC97_VIDEOVOLUME, + [SOUND_MIXER_LINE1] = AC97_AUXVOLUME, + [SOUND_MIXER_PCM] = AC97_PCMOUTVOLUME, + /* 5 bit stereo, setting 6th bit equal to maximum attenuation */ + [SOUND_MIXER_VOLUME] = AC97_MASTERVOLUME, + [SOUND_MIXER_PHONEOUT] = AC97_HEADPHONEVOLUME, + /* 5 bit mono, setting 6th bit equal to maximum attenuation */ + [SOUND_MIXER_OGAIN] = AC97_MASTERVOLUMEMONO, + /* 5 bit mono */ + [SOUND_MIXER_PHONEIN] = AC97_PHONEVOLUME, + /* 4 bit mono but shifted by 1 */ + [SOUND_MIXER_SPEAKER] = AC97_PCBEEPVOLUME, + /* 5 bit mono, 7th bit = preamp */ + [SOUND_MIXER_MIC] = AC97_MICVOLUME, + /* 4 bit stereo */ + [SOUND_MIXER_RECLEV] = AC97_RECORDGAIN, + /* 4 bit mono */ + [SOUND_MIXER_IGAIN] = AC97_RECORDGAINMIC, + /* test code */ + [SOUND_MIXER_BASS] = AC97_GENERALPUPOSE, + [SOUND_MIXER_TREBLE] = AC97_MASTERTONE +}; + +/* FIXME: OSS_DOCUMENTED_MIXER_SEMANTICS is #undefed. + * This needs to be explored more and cleaned up [jtaylor] */ +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + +#define swab(x) ((((x) >> 8) & 0xff) | (((x) << 8) & 0xff00)) + +/* FIXME: mixer_rdch() is broken. */ + +static int mixer_rdch(struct sblive_hw * sb_hw, unsigned int ch, int *arg) +{ + u16 reg; + int j; + int nL, nR; + + switch (ch) { + case SOUND_MIXER_LINE: + case SOUND_MIXER_CD: + case SOUND_MIXER_VIDEO: + case SOUND_MIXER_LINE1: + case SOUND_MIXER_PCM: + case SOUND_MIXER_VOLUME: + sblive_readac97(sb_hw, volreg[ch], ®); + nL = ((~(reg >> 8) & 0x1f) * 100) / 32; + nR = (~(reg & 0x1f) * 100) / 32; + DPD(2, "mixer_rdch: l=%d, r=%d\n", nL, nR); + return put_user(reg & 0x8000 ? 0 : (nL << 8) | nR, (int *) arg); + + case SOUND_MIXER_OGAIN: + case SOUND_MIXER_PHONEIN: + sblive_readac97(sb_hw, volreg[ch], ®); + return put_user(reg & 0x8000 ? 0 : ~(reg & 0x1f) * 0x64 / 0x20 * 0x101, (int *) arg); + + case SOUND_MIXER_SPEAKER: + sblive_readac97(sb_hw, volreg[ch], ®); + return put_user(reg & 0x8000 ? 0 : ~((reg >> 1) & 0xf) * 0x64 / 0x10 * 0x101, (int *) arg); + + case SOUND_MIXER_MIC: + sblive_readac97(sb_hw, volreg[ch], ®); + return put_user(reg & 0x8000 ? 0 : ~(reg & 0x1f) * 0x64 / 0x20 * 0x101 + ((reg & 0x40) ? 0x1e1e : 0), (int *) arg); + + case SOUND_MIXER_RECLEV: + sblive_readac97(sb_hw, volreg[ch], ®); + nL = ((~(reg >> 8) & 0x1f) * 100) / 16; + nR = (~(reg & 0x1f) * 100) / 16; + return put_user(reg & 0x8000 ? 0 : (nL << 8) | nR, (int *) arg); + + case SOUND_MIXER_TREBLE: + case SOUND_MIXER_BASS: + return put_user(0x0000, (int *) arg); + default: + return -EINVAL; + } +} + +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + +static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = +{ + /* 5 bit stereo */ + [SOUND_MIXER_LINE] = 1, + [SOUND_MIXER_CD] = 2, + [SOUND_MIXER_VIDEO] = 3, + [SOUND_MIXER_LINE1] = 4, + [SOUND_MIXER_PCM] = 5, + /* 6 bit stereo */ + [SOUND_MIXER_VOLUME] = 6, + [SOUND_MIXER_PHONEOUT] = 7, + /* 6 bit mono */ + [SOUND_MIXER_OGAIN] = 8, + [SOUND_MIXER_PHONEIN] = 9, + /* 4 bit mono but shifted by 1 */ + [SOUND_MIXER_SPEAKER] = 10, + /* 6 bit mono + preamp */ + [SOUND_MIXER_MIC] = 11, + /* 4 bit stereo */ + [SOUND_MIXER_RECLEV] = 12, + /* 4 bit mono */ + [SOUND_MIXER_IGAIN] = 13, + [SOUND_MIXER_TREBLE] = 14, + [SOUND_MIXER_BASS] = 15, + [SOUND_MIXER_DIGITAL1] = 17, + [SOUND_MIXER_DIGITAL2] = 18, + [SOUND_MIXER_LINE2] = 19 +}; + +static void update_rearfront(struct sblive_hw *sb_hw) +{ + int i, l1, l2, l3, r1, r2, r3; + u16 reg, wval; + + reg = sb_hw->arrwVol[volidx[SOUND_MIXER_PCM] - 1]; + l1 = (reg & 0xff); + r1 = (reg >> 8) & 0xff; + + reg = sb_hw->arrwVol[volidx[SOUND_MIXER_LINE2] - 1]; + l2 = (reg & 0xff); + r2 = (reg >> 8) & 0xff; + + reg = sb_hw->arrwVol[volidx[SOUND_MIXER_VOLUME] - 1]; + l3 = (reg & 0xff); + r3 = (reg >> 8) & 0xff; + + i = (r1 * r2) / 50; + if (r2 > 50) + r2 = 2 * r1 - i; + else { + r2 = r1; + r1 = i; + } + + i = (l1 * l2) / 50; + if (l2 > 50) + l2 = 2 * l1 - i; + else { + l2 = l1; + l1 = i; + } + + sblive_writeptr(sb_hw, FXGPREGBASE + 0x10, 0, l2 * l3 * (0x7fffffff / 10000)); + sblive_writeptr(sb_hw, FXGPREGBASE + 0x11, 0, r2 * r3 * (0x7fffffff / 10000)); + wval = ((((100 - l1) * 32 + 50) / 100) << 8) | (((100 - r1) * 32 + 50) / 100); + if (wval == 0x2020) + wval = 0x8000; + else + wval -= ((wval & 0x2020) / 0x20); + sblive_writeac97(sb_hw, volreg[SOUND_MIXER_PCM], wval); +} + +static int mixer_wrch(struct sblive_hw * sb_hw, unsigned int ch, int val) +{ + int i; + unsigned l1, r1; + u16 wval; + + l1 = val & 0xff; + r1 = (val >> 8) & 0xff; + if (l1 > 100) + l1 = 100; + if (r1 > 100) + r1 = 100; + + DPD(2, "mixer_wrch() called: ch=%u, l1=%u, r1=%u\n", ch, l1, r1); + + if (!volidx[ch]) return -EINVAL; + sb_hw->arrwVol[volidx[ch] - 1] = (r1 << 8) | l1; + + switch (ch) + { + case SOUND_MIXER_LINE2: + DPF(2, "SOUND_MIXER_LINE2:\n"); + update_rearfront(sb_hw); + return 0; + case SOUND_MIXER_DIGITAL2: + DPF(2, "SOUND_MIXER_DIGITAL2:\n"); + sblive_writeptr(sb_hw, FXGPREGBASE + 0x14, 0, l1 * (0x7fffffff / 100)); + sblive_writeptr(sb_hw, FXGPREGBASE + 0x15, 0, r1 * (0x7fffffff / 100)); + return 0; + case SOUND_MIXER_DIGITAL1: + DPF(2, "SOUND_MIXER_DIGITAL1:\n"); + sblive_writeptr(sb_hw, FXGPREGBASE + 0x12, 0, l1 * (0x7fffffff / 100)); + sblive_writeptr(sb_hw, FXGPREGBASE + 0x13, 0, r1 * (0x7fffffff / 100)); + return 0; + case SOUND_MIXER_PCM: + DPF(2, "SOUND_MIXER_PCM:\n"); + update_rearfront(sb_hw); + return 0; + case SOUND_MIXER_LINE1: + DPF(2, "SOUND_MIXER_LINE1:\n"); + case SOUND_MIXER_LINE: + DPF(2, "SOUND_MIXER_LINE:\n"); + case SOUND_MIXER_CD: + DPF(2, "SOUND_MIXER_CD:\n"); + case SOUND_MIXER_VOLUME: + DPF(2, "SOUND_MIXER_VOLUME:\n"); + wval = ((((100 - l1) * 32 + 50) / 100) << 8) | (((100 - r1) * 32 + 50) / 100); + if (wval == 0x2020) + wval = 0x8000; + else + wval -= ((wval & 0x2020) / 0x20); + sblive_writeac97(sb_hw, volreg[ch], wval); + if (ch == SOUND_MIXER_VOLUME) update_rearfront(sb_hw); + return 0; + + case SOUND_MIXER_OGAIN: + DPF(2, "SOUND_MIXER_OGAIN:\n"); + case SOUND_MIXER_PHONEIN: + DPF(2, "SOUND_MIXER_PHONEIN:\n"); + sblive_writeac97(sb_hw, volreg[ch], (l1 < 2) ? 0x8000 : ((100 - l1) * 32 + 50) / 100); + return 0; + + case SOUND_MIXER_SPEAKER: + DPF(2, "SOUND_MIXER_SPEAKER:\n"); + sblive_writeac97(sb_hw, volreg[ch], (l1 < 4) ? 0x8000 : (((100 - l1) * 16 + 50) / 100) << 1); + return 0; + + case SOUND_MIXER_MIC: + DPF(2, "SOUND_MIXER_MIC:\n"); + i = 0; + if (l1 >= 30) + // 20dB / (34.5dB + 12dB + 20dB) * 100 = 30 + { + l1 -= 30; + i = 0x40; + } + sblive_writeac97(sb_hw, volreg[ch], (l1 < 2) ? 0x8000 : ((((70 - l1) * 0x20 + 35) / 70) | i)); + return 0; + + case SOUND_MIXER_RECLEV: + DPF(2, "SOUND_MIXER_RECLEV:\n"); + wval = (((l1 * 16 + 50) / 100) << 8) | ((r1 * 16 + 50) / 100); + if (wval == 0) + wval = 0x8000; + else { + if (wval & 0xff) wval--; + if (wval & 0xff00) wval -= 0x0100; + } + sblive_writeac97(sb_hw, volreg[ch], wval); + return 0; + + case SOUND_MIXER_TREBLE: + DPF(2, "SOUND_MIXER_TREBLE:\n"); + + { + //u8 atnL = sumVolumeToAttenuation(100 - l1); + //u8 atnR = sumVolumeToAttenuation(100 - r1); + u8 atnL = (100 - l1) * 0xa0 / 100; + u8 atnR = (100 - r1) * 0xa0 / 100; + int i = 0; + + DPD(2, "atnL = 0x%08x, atnR = 0x%08x\n", atnL, atnR); + + while (i < 64) + { + sblive_writeptr(sb_hw, IFATN_ATTENUATION, i++, atnL); + sblive_writeptr(sb_hw, IFATN_ATTENUATION, i++, atnR); + } + return 0; + } + + case SOUND_MIXER_BASS: + DPF(2, "SOUND_MIXER_BASS:\n"); + + { + static int idxB = 0; + u8 peL = (l1 * 255 / 100 - 128) & 0xff; + u8 peR = (r1 * 255 / 100 - 128) & 0xff; + int i = 0; /* Usually first channel */ + + idxB += 1; + idxB &= 0xf; + DPD(2, "using 0x%08x\n", idxB); + + /* for (i = 9; i <= 9; i++) { */ + /* sblive_writeptr(sb_hw, CVCF_CURRENTFILTER, i, idx); */ + /* sblive_writeptr(sb_hw, CVCF_CURRENTVOL, i, idx); */ + + /* this is pure hack - its for 4 channels */ + /* FIXME: This is very odd, most of this code will + * never be executed. */ + while (i < 64) + { + if (1) + { + sblive_writeptr(sb_hw, PEFE_FILTERAMOUNT, i++, peL); + sblive_writeptr(sb_hw, PEFE_FILTERAMOUNT, i++, peR); + } else if (0) + { + sblive_writeptr(sb_hw, PEFE_PITCHAMOUNT, i++, peL); + sblive_writeptr(sb_hw, PEFE_FILTERAMOUNT, i++, peR); + } else if (0) + { + sblive_writeptr(sb_hw, IFATN_FILTERCUTOFF, i++, peL); + sblive_writeptr(sb_hw, IFATN_FILTERCUTOFF, i++, peR); + } else if (0) + { + sblive_writeptr(sb_hw, IP, i++, idxB << 12); + sblive_writeptr(sb_hw, IP, i++, idxB << 12); + } + } + return 0; + } + default: + DPF(2, "Got unknown SOUND_MIXER ioctl\n"); + return -EINVAL; + } +} + + +static loff_t sblive_mixer_llseek(struct file *file, loff_t offset, int nOrigin) +{ + DPF(2, "sblive_mixer_llseek() called\n"); + return -ESPIPE; +} + +/* Mixer file operations */ +/* FIXME: Do we need spinlocks in here? */ +static int sblive_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static const char id[] = "SBLive"; + static const char name[] = "Creative SBLive"; + int i, val; + struct sblive_hw * sb_hw = (struct sblive_hw *) file->private_data; + u16 reg; + + if (cmd == SOUND_MIXER_INFO) + { + mixer_info info; + DPF(2, "SOUND_MIXER_INFO\n"); + + strncpy(info.id, id, sizeof(info.id)); + strncpy(info.name, name, sizeof(info.name)); + + info.modify_counter = sb_hw->modcnt; + if (copy_to_user((void *) arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + if (cmd == SOUND_OLD_MIXER_INFO) + { + _old_mixer_info info; + DPF(2, "SOUND_OLD_MIXER_INFO\n"); + + strncpy(info.id, id, sizeof(info.id)); + strncpy(info.name, name, sizeof(info.name)); + + if (copy_to_user((void *) arg, &info, sizeof(info))) + return -EFAULT; + + return 0; + } + + if (cmd == OSS_GETVERSION) + { + DPF(2, "OSS_GETVERSION\n"); + return put_user(SOUND_VERSION, (int *) arg); + } + + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (_IOC_DIR(cmd) == _IOC_READ) + { + switch (_IOC_NR(cmd)) + { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + DPF(2, "SOUND_MIXER_READ_RECSRC\n"); + sblive_readac97(sb_hw, AC97_RECORDSELECT, ®); + return put_user(recsrc[reg & 7], (int *) arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + DPF(4, "SOUND_MIXER_READ_DEVMASK\n"); + return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | + SOUND_MASK_OGAIN | SOUND_MASK_LINE1 | + SOUND_MASK_PCM | SOUND_MASK_VOLUME | + SOUND_MASK_PHONEIN | SOUND_MASK_MIC | + /* SOUND_MASK_BASS | SOUND_MASK_TREBLE | */ /* Why not enable these? [jtaylor] */ + SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER | + SOUND_MASK_LINE2 | SOUND_MASK_DIGITAL1 | + SOUND_MASK_DIGITAL2, + (int *) arg); + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + DPF(2, "SOUND_MIXER_READ_RECMASK\n"); + return put_user(SOUND_MASK_MIC | SOUND_MASK_CD | + SOUND_MASK_LINE1 | SOUND_MASK_LINE | + SOUND_MASK_VOLUME | SOUND_MASK_OGAIN | + SOUND_MASK_PHONEIN, (int *) arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + DPF(2, "SOUND_MIXER_READ_STEREODEVS\n"); + return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | + SOUND_MASK_OGAIN | SOUND_MASK_LINE1 | + SOUND_MASK_PCM | SOUND_MASK_VOLUME | + /*SOUND_MASK_BASS | SOUND_MASK_TREBLE |*/ /* ...especially since they are enabled here */ + SOUND_MASK_RECLEV | SOUND_MASK_LINE2 | + SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2, + (int *) arg); + + case SOUND_MIXER_CAPS: + DPF(2, "SOUND_MIXER_READ_CAPS\n"); + return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg); + + default: + i = _IOC_NR(cmd); + DPD(4, "SOUND_MIXER_READ(%d)\n", i); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + return mixer_rdch(sb_hw, i, (int *) arg); +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + if (!volidx[i]) + return -EINVAL; + return put_user(sb_hw->arrwVol[volidx[i] - 1], (int *) arg); + +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + } + } /* End of _IOC_READ */ + + if (_IOC_DIR(cmd) != (_IOC_READ | _IOC_WRITE)) + return -EINVAL; + + /* _IOC_WRITE */ + sb_hw->modcnt++; + + switch (_IOC_NR(cmd)) + { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + DPF(2, "SOUND_MIXER_WRITE_RECSRC\n"); + + get_user_ret(val, (int *) arg, -EFAULT); + i = hweight32(val); + if (i == 0) + return 0; /*val = mixer_recmask(s); */ + else if (i > 1) + { + sblive_readac97(sb_hw, AC97_RECORDSELECT, ®); + val &= ~recsrc[reg & 7]; + } + + for (i = 0; i < 8; i++) + { + if (val & recsrc[i]) + { + DPD(2, "Selecting record source to be 0x%04x\n", 0x0101 * i); + sblive_writeac97(sb_hw, AC97_RECORDSELECT, 0x0101 * i); + return 0; + } + } + return 0; + + default: + i = _IOC_NR(cmd); + DPD(2, "SOUND_MIXER_WRITE(%d)\n", i); + + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + get_user_ret(val, (int *) arg, -EFAULT); + if (mixer_wrch(sb_hw, i, val)) + return -EINVAL; + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + return mixer_rdch(sb_hw, i, (int *) arg); +#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + return put_user(sb_hw->arrwVol[volidx[i] - 1], (int *) arg); +#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ + + } +} + +static int sblive_mixer_open(struct inode *inode, struct file *file) +{ + int nMinor = MINOR(inode->i_rdev); + struct sblive_hw * currobj = sblive_devs; + + DPF(4, "sblive_mixer_open() called!\n"); + + while (currobj && currobj->mixer_num != nMinor) + currobj = currobj->next; + if (!currobj) + return -ENODEV; + + file->private_data = currobj; + MOD_INC_USE_COUNT; + return 0; +} + +static int sblive_mixer_close(struct inode *inode, struct file *file) +{ + DPF(4, "sblive_mixer_close() called!\n"); + MOD_DEC_USE_COUNT; + return 0; +} + + +struct file_operations emu10k1_mixer_fops = +{ + &sblive_mixer_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select/poll */ + &sblive_mixer_ioctl, + NULL, /* mmap */ + &sblive_mixer_open, + NULL, /* flush */ + &sblive_mixer_close, + NULL, /* fsync */ + NULL, /* fasync */ +}; + +static const struct initvol +{ + int mixch; + int vol; +} initvol[] __initdata = +{ + { SOUND_MIXER_VOLUME, 0x5050 }, + { SOUND_MIXER_OGAIN, 0x5050 }, + { SOUND_MIXER_SPEAKER, 0x5050 }, + { SOUND_MIXER_PHONEIN, 0x5050 }, + { SOUND_MIXER_MIC, 0x5050 }, + { SOUND_MIXER_LINE, 0x5050 }, + { SOUND_MIXER_CD, 0x5050 }, + { SOUND_MIXER_LINE1, 0x5050 }, + { SOUND_MIXER_LINE2, 0x3232 }, + { SOUND_MIXER_DIGITAL1, 0x5050 }, + { SOUND_MIXER_DIGITAL2, 0x0000 }, + { SOUND_MIXER_PCM, 0x5050 }, + { SOUND_MIXER_RECLEV, 0x5050 } /*, + { SOUND_MIXER_TREBLE, 0x5050 }, + { SOUND_MIXER_BASS, 0x5050 } */ +}; + +/* FIXME: Do we need spinlocks in here? */ +int sblive_mixer_init(struct sblive_hw * sb_hw) +{ + int count; + + /* Reset */ + sblive_writeac97(sb_hw, AC97_RESET, 0); + +#if 0 + /* Check status word */ + { + u16 reg; + sblive_readac97(sb_hw, AC97_RESET, ®); + DPD(2, "RESET 0x%x\n", reg); + sblive_readac97(sb_hw, AC97_MASTERTONE, ®); + DPD(2, "MASTER_TONE 0x%x\n", reg); + } +#endif + + /* Set default recording source to mic in */ + sblive_writeac97(sb_hw, AC97_RECORDSELECT, 0); + + /* Set default volumes for all mixer channels */ + for (count = 0; count < sizeof(initvol) / sizeof(initvol[0]); count++) + { + mixer_wrch(sb_hw, initvol[count].mixch, initvol[count].vol); + } + + sb_hw->modcnt = 0; // Should this be here or in open() ? + + return CTSTATUS_SUCCESS; +} Index: oldkernel/linux/drivers/sound/emu10k1/mmwave.h diff -u /dev/null linux/drivers/sound/emu10k1/mmwave.h:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/mmwave.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,76 @@ +/* + ********************************************************************** + * mmwave.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _MMWAVE_H +#define _MMWAVE_H + +/* Enumeration for SetControl */ +enum +{ + WAVESTARTLOOP = 0x10, + WAVEENDLOOP, + WAVECURPOS, + WAVESTOPPOSITION, + WAVEWRITEPOINTER, + WAVESETSTOPONLOOP, + + WAVEOBJVOLUME = 0x20, + WAVEINSTANCEVOLUME, + WAVEOBJREVERB, + WAVEINSTANCEREVERB, + WAVEOBJCHORUS, + WAVEINSTANCECHORUS, + WAVEOBJMUTE, + WAVEVOLFACTOR, + WAVEREVERBFACTOR, + WAVECHORUSFACTOR, + WAVEINSTANCEPITCHOFFSET, + WAVEINSTANCESEND, + + WAVESYNCHSETUP = 0x30, + WAVESYNCHSTART, + WAVESETFREQUENCY, + WAVESETSTARTFLAG, + WAVEQUERYACTIVEINST, + WAVEINSTANCEFILTERQ, + WAVEINSTANCEFILTERCUTOFF, + WAVEINSTANCEFORMAT, + + WAVEPROPERTYSETGET = 0x200, + WAVEPROPERTYSETSET, + WAVEPROPERTYSETQUERYSUPPORT, + + WAVEGET3DBUFFERPROPERTYSETIFACE = 0x250, + WAVEGET3DLISTENERPROPERTYSETIFACE, + WAVENUMFREE3DBUFFERS +}; + +#endif /* _MMWAVE_H */ Index: oldkernel/linux/drivers/sound/emu10k1/mycommon.h diff -u /dev/null linux/drivers/sound/emu10k1/mycommon.h:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/mycommon.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,48 @@ +/* + ********************************************************************** + * common.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _MYCOMMON_H +#define _MYCOMMON_H + +#include +#include +#include +#include + +/* extern global variables */ +extern struct sblive_hw * sblive_devs; +extern struct file_operations emu10k1_audio_fops; +extern struct file_operations emu10k1_dac_fops; +extern struct file_operations emu10k1_mixer_fops; +extern struct file_operations emu10k1_midi_fops; +extern struct file_operations emu10k1_sndstat_fops; + +#endif /* _MYCOMMON_H */ Index: oldkernel/linux/drivers/sound/emu10k1/osutils.c diff -u /dev/null linux/drivers/sound/emu10k1/osutils.c:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/osutils.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,174 @@ +/* + ********************************************************************** + * osutils.c - OS Services layer for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox cleaned up + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +/* FIXME! This file should not exist! + * We do not need the crossplatform support anymore + * This is a Linux-only driver + */ + +#include "hwaccess.h" +#include "mycommon.h" + + +int osAllocMemPhysical(u32 size, struct memhandle **handle) +{ + u32 reqpage, order; + + *handle = (struct memhandle *) kmalloc(sizeof(struct memhandle), GFP_KERNEL); + if (*handle == NULL) + return CTSTATUS_NOMEMORY; + + DPD(3, "kmalloc: [%p]\n", *handle); + + order = 0; + reqpage = size / PAGE_SIZE; + + if (size % PAGE_SIZE) + reqpage++; + + if (reqpage != 0) + { + reqpage--; + while (reqpage > 0) + { + reqpage >>= 1; + order++; + } + } + + if (((*handle)->virtaddx = (void *)__get_free_pages(GFP_KERNEL, order)) == 0) + { + kfree((void *) *handle); + + DPD(3, "kfree: [%p]\n", *handle); + *handle = NULL; + return CTSTATUS_NOMEMORY; + } + + DPD(3, "__get_free_pages: [%p]\n", (*handle)->virtaddx); + + /* in linux, we can directly access physical address, don't need to do + * phys_to_virt. + * In linux kernel 2.0.36, virt_to_bus does nothing, get_free_pages + * returns physical address. But in kernel 2.2.1 upwards, + * get_free_pages returns virtual address, we need to convert it + * to physical address. Then this physical address can be used to + * program hardware registers. */ + (*handle)->physaddx = virt_to_bus((*handle)->virtaddx); + (*handle)->order = order; + + return CTSTATUS_SUCCESS; +} + +int osFreeMemPhysical(struct memhandle **handle) +{ + if (*handle == NULL) + return CTSTATUS_ERROR; + + free_pages((unsigned long) (*handle)->virtaddx, (*handle)->order); + kfree((void*) *handle); + + DPD(3, "free_pages: [%p]\n", (*handle)->virtaddx); + DPD(3, "kfree: [%p]\n", *handle); + + *handle = NULL; + return CTSTATUS_SUCCESS; +} + + +int osListAttach(struct sblive_list **head, struct sblive_list *n) +{ + unsigned long flags; + + if (head == NULL) + return CTSTATUS_ERROR; + + spin_lock_irqsave(&sblive_spinlock, flags); + + n->next = NULL; + + if (*head != NULL) + { + struct sblive_list *t = *head; + + while (t->next != NULL) + t = t->next; + t->next = n; + } else + *head = n; + + spin_unlock_irqrestore(&sblive_spinlock, flags); + + return CTSTATUS_SUCCESS; +} + + +int osListRemove(struct sblive_list **head, struct sblive_list *dead) +{ + u32 status = CTSTATUS_SUCCESS; + unsigned long flags; + + if (head == NULL) + return CTSTATUS_ERROR; + + spin_lock_irqsave(&sblive_spinlock, flags); + + if (*head != dead) + { + struct sblive_list *t = *head; + + while (t != NULL && t->next != dead) + t = t->next; + + if (t != NULL) + { + t->next = dead->next; + dead->next = NULL; + } else + status = CTSTATUS_ERROR; + } else + *head = dead->next; + + spin_unlock_irqrestore(&sblive_spinlock, flags); + + return status; +} + + +struct sblive_list *osListGetNext(struct sblive_list *head, struct sblive_list *curr) +{ + if (curr == NULL) + return head; + + return curr->next; +} + Index: oldkernel/linux/drivers/sound/emu10k1/recmgr.c diff -u /dev/null linux/drivers/sound/emu10k1/recmgr.c:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/recmgr.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,185 @@ +/* + ********************************************************************** + * recmgr.c -- Recording manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" +#include "recmgr.h" + +/****************************************************************************/ +/** Function: recmgrInit **/ +/** **/ +/** Input : rec_ptr - pointer recording object **/ +/** **/ +/** About : stop recording when init **/ +/****************************************************************************/ +int recmgrInit(struct record *rec_ptr) +{ + struct sblive_hw *hw_ptr = rec_ptr->sb_hw; + + /* Disable record transfer */ + sblive_writeptr(hw_ptr, ADCBS, 0, ADCBS_BUFSIZE_NONE); + sblive_writeptr(hw_ptr, ADCCR, 0, 0); + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/** Function: recmgrStartRecord **/ +/** **/ +/** Input : rec_ptr - pointer recording object **/ +/** **/ +/** About : start recording **/ +/****************************************************************************/ +int recmgrStartRecord(struct record *rec_ptr) +{ + struct sblive_hw *hw_ptr = rec_ptr->sb_hw; + u32 adcsr; + + +#ifdef RECTEST + DPD(2, " data in 0x120 -> %x\n", sblive_readptr(hw_ptr, FXGPREGBASE + 0x20, 0)); + DPD(2, " data in 0x121 -> %x\n", sblive_readptr(hw_ptr, FXGPREGBASE + 0x21, 0)); +#endif + + DPD(2, "recmgrStartRecord: base addx: %x\n", rec_ptr->physaddx >> 12); + sblive_writeptr(hw_ptr, ADCBA, 0, rec_ptr->physaddx); + + switch (rec_ptr->samplingrate) + { + case 0xBB80: adcsr = ADCCR_SAMPLERATE_48; break; + case 0xAC44: adcsr = ADCCR_SAMPLERATE_44; break; + case 0x7D00: adcsr = ADCCR_SAMPLERATE_32; break; + case 0x5DC0: adcsr = ADCCR_SAMPLERATE_24; break; + case 0x5622: adcsr = ADCCR_SAMPLERATE_22; break; + case 0x3E80: adcsr = ADCCR_SAMPLERATE_16; break; + case 0x2B11: adcsr = ADCCR_SAMPLERATE_11; break; + case 0x1F40: adcsr = ADCCR_SAMPLERATE_8; break; + default: + DPF(2, "Unknown sampling rate!\n"); + return CTSTATUS_ERROR; + } + + adcsr |= ADCCR_LCHANENABLE; + + if (rec_ptr->is_stereo) + adcsr |= ADCCR_RCHANENABLE; + + sblive_writeptr(hw_ptr, ADCCR, 0, adcsr); + sblive_writeptr(hw_ptr, ADCBS, 0, rec_ptr->bufsizereg); + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/** Function: recmgrStopRecord **/ +/** **/ +/** Input : rec_ptr - pointer recording object **/ +/** **/ +/** About : stop recording **/ +/****************************************************************************/ +int recmgrStopRecord(struct record *rec_ptr) +{ + struct sblive_hw *hw_ptr = rec_ptr->sb_hw; + + /* Disable record transfer */ + sblive_writeptr(hw_ptr, ADCBS, 0, ADCBS_BUFSIZE_NONE); + sblive_writeptr(hw_ptr, ADCCR, 0, 0); + + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/** Function: recmgrSetControl **/ +/** **/ +/** Input : rec_ptr - pointer recording object **/ +/** controlid - control ID **/ +/** value - value to set for a specified control **/ +/** **/ +/** About : set recording resource **/ +/** **/ +/** Note : **/ +/** **/ +/** XD, DMA, WR and RIGHT bits in CCCA register controls the way to **/ +/** record for 8005. **/ +/** **/ +/** XD DMA WR RIGHT **/ +/** 1 1 0 0 --- record from external(AC97, SPDIF, MIC) **/ +/** 1 1 1 0 --- record from the output of Emu8000 **/ +/** effects engine. **/ +/** **/ +/****************************************************************************/ +int recmgrSetControl(struct record *rec_ptr, u32 controlid, u32 value) +{ + /* FIXME: Implement me! */ + return CTSTATUS_SUCCESS; +} + + +/****************************************************************************/ +/** Function: recmgrGetPos **/ +/** **/ +/** Input : rec_ptr - pointer recording object **/ +/** pos - pointer to position returned **/ +/** **/ +/** About : get recording position(no. of bytes per channel) **/ +/** **/ +/** Formula : (WC*SamplingRate/48kHz)%(record buffer size) **/ +/****************************************************************************/ +int recmgrGetPos(struct record *rec_ptr, u32 *pos) +{ + struct sblive_hw *hw_ptr = rec_ptr->sb_hw; + + u32 curidx; + + curidx = recmgrGetRecIdx(hw_ptr); + if (curidx >= rec_ptr->prevadcidx) + *pos = curidx - rec_ptr->prevadcidx; + else + *pos = 0xfffff - (rec_ptr->prevadcidx - curidx); + + *pos = (*pos) * ((u16) rec_ptr->samplingrate) / 48000 + * 2 * (rec_ptr->is_stereo + 1); + + DPD(2, " Recmgr: *pos: %x\n", *pos); + + if (rec_ptr->pong) + *pos += rec_ptr->recbufsize / 2; + + return CTSTATUS_SUCCESS; +} + + +u32 recmgrGetRecIdx(struct sblive_hw *sb_hw) +{ + return READ_FN0(sb_hw, WC_SAMPLECOUNTER); +} Index: oldkernel/linux/drivers/sound/emu10k1/recmgr.h diff -u /dev/null linux/drivers/sound/emu10k1/recmgr.h:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/recmgr.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,65 @@ +/* + ********************************************************************** + * recmgr.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _RECORDMGR_H +#define _RECORDMGR_H + +struct record +{ + void *reserved; + struct sblive_hw *sb_hw; + u32 recpos; + u32 recbufsize; + u32 bufsizereg; + u8 *recbuffer; + u32 physaddx; + u32 samplingrate; + int is_stereo; + int is16bit; + int fSetRecSrc; + u32 recsrc; + u32 prevadcidx; + int pong; +}; + +/* Recording resources */ +#define WAVERECORD_AC97 0x00000001 +#define WAVERECORD_E8K 0x00000002 + +int recmgrInit(struct record *); +int recmgrStartRecord(struct record *); +int recmgrStopRecord(struct record *); +int recmgrSetControl(struct record *, u32, u32); +int recmgrGetPos(struct record *, u32 *); +u32 recmgrGetRecIdx(struct sblive_hw *); + + +#endif /* _RECORDMGR_H */ Index: oldkernel/linux/drivers/sound/emu10k1/sndstat.c diff -u /dev/null linux/drivers/sound/emu10k1/sndstat.c:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/sndstat.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,300 @@ +/* + ********************************************************************** + * + * sndstat.c - /dev/sndstat interface for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + * This file uses some code from soundcard.c, Copyright 1993-1997 + * Hannu Savolainen + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifdef MODULE +#define __NO_VERSION__ +#include +#endif + +#include + +#include "hwaccess.h" +#include "mycommon.h" +#include "sndstat.h" + +static int in_use = 0; +static int is_unloading = 0; +static int sound_started = 0; +static int soundcard_configured = 0; + +/* unneeded ??? - will be removed shortly +int num_sound_cards = 0; +struct card_info snd_installed_cards[20] = {{0}}; + +int num_sound_drivers = 0; +struct driver_info sound_drivers[20] = {{0}}; + +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; + +*/ + +static int sound_proc_get_info(char *buffer, char **start, off_t offset, int length, int inout) +{ + int len, count; + off_t begin = 0; + struct sblive_hw *lastdev, *dev; + +#ifdef MODULE +#define MODULEPROCSTRING "Driver loaded as a module" +#else +#define MODULEPROCSTRING "Driver compiled into kernel" +#endif + + for (lastdev = sblive_devs; lastdev->next; lastdev = lastdev->next); + + len = sprintf(buffer, "OSS/Free:" SOUND_VERSION_STRING "\n" + "Load type: " MODULEPROCSTRING "\n" + "Kernel: %s %s %s %s %s\n" + "Config options: %x\n\nInstalled drivers: \n", + system_utsname.sysname, system_utsname.nodename, system_utsname.release, + system_utsname.version, system_utsname.machine, SELECTED_SOUND_OPTIONS); + + len += sprintf(buffer + len, "Type 0: %s\n", "Creative EMU10K1 (0.4)"); + + len += sprintf(buffer + len, "\nCard config: \n"); + dev = lastdev; + while (dev) + { + len += sprintf(buffer + len, "("); + len += sprintf(buffer + len, "%s", "Sound Blaster Live!"); + len += sprintf(buffer + len, " at 0x%lx", dev->hwaddr); + len += sprintf(buffer + len, " irq %d", dev->hw_irq.irq); + len += sprintf(buffer + len, ")"); + len += sprintf(buffer + len, "\n"); + dev = dev->prev; + } + + len += sprintf(buffer + len, "\nAudio devices:\n"); + dev = lastdev; + count = 0; + while (dev) + { + len += sprintf(buffer + len, "%d: %s%s\n", count++, "Sound Blaster Live!", " (DUPLEX)"); + dev = dev->prev; + } + + len += sprintf(buffer + len, "\nSynth devices: \nNot supported by current driver\n"); + + len += sprintf(buffer + len, "\nMidi devices: \n"); + dev = lastdev; + count = 0; + + while (dev) + { + len += sprintf(buffer + len, "%d: %s\n", count++, "Sound Blaster Live!"); + dev = dev->prev; + } + + len += sprintf(buffer + len, "\nTimers:\n"); + len += sprintf(buffer + len, "0: %s\n", "System Clock"); + + len += sprintf(buffer + len, "\nMixers:\n"); + dev = lastdev; + count = 0; + + while (dev) + { + len += sprintf(buffer + len, "%d: %s\n", count++, "Sound Blaster Live!"); + dev = dev->prev; + } + + *start = buffer + (offset - begin); + len -= (offset - begin); + if (len > length) + len = length; + return len; +} + +/* 4K page size but our output routines use some slack for overruns */ +#define PROC_BLOCK_SIZE (3*1024) + +/* + * basically copied from fs/proc/generic.c:proc_file_read + * should be removed sometime in the future together with /dev/sndstat + * (a symlink /dev/sndstat -> /proc/sound will do as well) + */ +static int sndstat_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) +{ + char *page; + int retval=0; + int eof=0; + int n, count; + char *start; + + if (!(page = (char*) __get_free_page(GFP_KERNEL))) + return -ENOMEM; + + while ((nbytes > 0) && !eof) + { + count = min(PROC_BLOCK_SIZE, nbytes); + + start = NULL; + n = sound_proc_get_info(page, &start, *ppos, count, 0); + if (n < count) + eof = 1; + + if (!start) + { + /* For proc files that are less than 4k */ + start = page + *ppos; + n -= *ppos; + if (n <= 0) + break; + if (n > count) + n = count; + } + + if (n == 0) + break; /* End of file */ + + if (n < 0) + { + if (retval == 0) + retval = n; + break; + } + + n -= copy_to_user(buf, start, n); /* BUG ??? */ + if (n == 0) + { + if (retval == 0) + retval = -EFAULT; + break; + } + + *ppos += n; /* Move down the file */ + nbytes -= n; + buf += n; + retval += n; + } + + free_page((unsigned long) page); + return retval; +} + +static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + + DPD(2, "sound_read(dev=%d, count=%d)\n", dev, count); + + switch (dev & 0x0f) + { + case SND_DEV_STATUS: + return sndstat_file_read(file, buf, count, &file->f_pos); + + default: + break; + } + return -EINVAL; +} + +static int sound_open(struct inode *inode, struct file *file) +{ + int dev; + + if (is_unloading) + { + DPF(2, "Sound: Driver partially removed. Can't open device\n"); + return -EBUSY; + } + dev = MINOR(inode->i_rdev); + if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS) + { + DPF(2, "SoundCard Error: The sound system has not been configured\n"); + return -ENXIO; + } + DPD(2, "sound_open(dev=%d)\n", dev); + if ((dev >= SND_NDEVS) || (dev < 0)) + { + DPD(2, "Invalid minor device %d\n", dev); + return -ENXIO; + } + + switch (dev & 0x0f) + { + case SND_DEV_STATUS: + break; + default: + DPD(2, "Invalid minor device %d\n", dev); + return -ENXIO; + } + in_use++; + return 0; +} + +static int sound_release(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + + DPD(2, "sound_release(dev=%d)\n", dev); + switch (dev & 0x0f) + { + case SND_DEV_STATUS: + break; + default: + DPD(2, "Sound error: Releasing unknown device 0x%02x\n", dev); + } + in_use--; + + return 0; +} + + +void sndstat_init(void) +{ + soundcard_configured = 1; + sound_started = 1; +} + +struct file_operations emu10k1_sndstat_fops = +{ + NULL, + sound_read, + NULL, + NULL, /* sound_readdir */ + NULL, + NULL, + NULL, + sound_open, + NULL, + sound_release, + NULL, + NULL, +}; Index: oldkernel/linux/drivers/sound/emu10k1/sndstat.h diff -u /dev/null linux/drivers/sound/emu10k1/sndstat.h:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/sndstat.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,106 @@ +/* + ********************************************************************** + * + * sndstat.h + * Copyright 1999, 2000 Creative Labs, Inc. + * + * This file uses some code from sound_config.h, Copyright 1993- + * 1997, Hannu Savolainen + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _SNDSTAT_H +#define _SNDSTAT_H + +/* Minor numbers for the sound driver */ + +#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 */ +#define SND_DEV_AWFM 7 /* Reserved */ +#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 SOUND_VERSION_STRING "3.8s2++-971130" + +#define SELECTED_SOUND_OPTIONS 0x0 + +#ifndef ONECARD + +#define MAX_AUDIO_DEV 5 +#define MAX_MIXER_DEV 5 +#define DMA_DUPLEX 0x04 + +struct address_info +{ + int io_base; + int irq; + int dma; + int dma2; + int always_detect; /* 1=Trust me, it's there */ + char *name; + int driver_use_1; /* Driver defined field 1 */ + int driver_use_2; /* Driver defined field 2 */ + int *osp; /* OS specific info */ + int card_subtype; /* Driver specific. Usually 0 */ + void *memptr; /* Module memory chainer */ + int slots[6]; /* To remember driver slot ids */ +}; + +struct driver_info +{ + char *driver_id; + int card_subtype; /* Driver specific. Usually 0 */ + int card_type; /* From soundcard.h */ + char *name; + void (*attach) (struct address_info *hw_config); + int (*probe) (struct address_info *hw_config); + void (*unload) (struct address_info *hw_config); +}; + +struct card_info +{ + int card_type; /* Link (search key) to the driver list */ + struct address_info config; + int enabled; + void *for_driver_use; +}; + +#define CONFIG_AUDIO + +#endif /* ONECARD */ + +void sndstat_init(void); + +#endif /* _SNDSTAT_H */ Index: oldkernel/linux/drivers/sound/emu10k1/timer.c diff -u /dev/null linux/drivers/sound/emu10k1/timer.c:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/timer.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,165 @@ +/* + ********************************************************************** + * timer.c + * Copyright (C) 1999, 2000 Creative Labs, inc. + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#include "hwaccess.h" + +int sblive_timerIrqCallback(struct sblive_hw *sb_hw) +{ + struct emu_timer *timer; + + spin_lock(&sb_hw->timer_lock); + + timer = sb_hw->timer; + while (timer != NULL) + { + queue_task(timer->task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + timer = timer->next; + } + + spin_unlock(&sb_hw->timer_lock); + + return CTSTATUS_SUCCESS; +} + +int sblive_timerinstall(struct sblive_hw *sb_hw, struct emu_timer **timer, struct tq_struct *task, unsigned delay) +{ + struct emu_timer *t; + unsigned long flags; + + t = (struct emu_timer *) kmalloc(sizeof(struct emu_timer), GFP_KERNEL); + if (t == NULL) + { + DPF(2, "timer alloc failed\n"); + return CTSTATUS_NOMEMORY; + } + + DPD(3, "kmalloc: [%p]\n", t); + + if(delay < 1 ) + delay = 1; + else if (delay >= 1024) + delay = 1023; + + t->delay = delay; + t->task = task; + + *timer = t; + + spin_lock_irqsave(&sb_hw->timer_lock, flags); + + if(sb_hw->timer == NULL) + { + sb_hw->timer = *timer; + sb_hw->timer->next = NULL; + sb_hw->timer_delay = delay; + WRITE_FN0(sb_hw, TIMR_RATE, delay); + sblive_irqmgrEnableIrq(sb_hw, INTE_INTERVALTIMERENB); + } + else + { + t = sb_hw->timer; + sb_hw->timer = *timer; + sb_hw->timer->next = t; + if (delay < sb_hw->timer_delay) + { + sb_hw->timer_delay = delay; + WRITE_FN0(sb_hw, TIMR_RATE, delay); + } + } + + spin_unlock_irqrestore(&sb_hw->timer_lock, flags); + + DPD(2, "timer rate --> %d\n", sb_hw->timer_delay); + + return CTSTATUS_SUCCESS; +} + +int sblive_timeruninstall(struct sblive_hw *sb_hw, struct emu_timer *timer) +{ + struct emu_timer *t; + unsigned delay; + unsigned long flags; + + spin_lock_irqsave(&sb_hw->timer_lock, flags); + + t = sb_hw->timer; + + if( t == NULL){ + spin_unlock_irqrestore(&sb_hw->timer_lock, flags); + return CTSTATUS_SUCCESS; + } + + delay = timer->delay; + + if(sb_hw->timer == timer) + { + sb_hw->timer = timer->next; + } + else + { + while(t->next != timer) + t = t->next; + + t->next = timer->next; + } + + kfree(timer); + + DPD(3, "kfree: [%p]\n", timer); + + if(sb_hw->timer == NULL) + { + sblive_irqmgrDisableIrq(sb_hw, INTE_INTERVALTIMERENB); + } + else if (delay == sb_hw->timer_delay) + { + t = sb_hw->timer; + sb_hw->timer_delay = 1024; + + while(t != NULL){ + + if(t->delay < sb_hw->timer_delay) + sb_hw->timer_delay = t->delay; + t = t->next; + } + + WRITE_FN0(sb_hw, TIMR_RATE, sb_hw->timer_delay); + + DPD(2, "timer rate --> %d\n", sb_hw->timer_delay); + } + + spin_unlock_irqrestore(&sb_hw->timer_lock, flags); + + return CTSTATUS_SUCCESS; +} + +int sblive_timerinit(struct sblive_hw *sb_hw) +{ + sb_hw->timer = NULL; + sb_hw->timer_lock = SPIN_LOCK_UNLOCKED; + + return CTSTATUS_SUCCESS; +} Index: oldkernel/linux/drivers/sound/emu10k1/timer.h diff -u /dev/null linux/drivers/sound/emu10k1/timer.h:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/timer.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,41 @@ +/* + ********************************************************************** + * timer.h + * Copyright (C) 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + + +#ifndef _TIMER_H +#define _TIMER_H + +struct emu_timer +{ + struct emu_timer *next; + struct tq_struct *task; + unsigned delay; +}; + +int sblive_timerinstall(struct sblive_hw *, struct emu_timer **, struct tq_struct *, unsigned); +int sblive_timeruninstall(struct sblive_hw *, struct emu_timer *); +int sblive_timerinit(struct sblive_hw *); + +#endif /* _TIMER_H */ Index: oldkernel/linux/drivers/sound/emu10k1/voicemgr.c diff -u /dev/null linux/drivers/sound/emu10k1/voicemgr.c:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/voicemgr.c Thu Jun 1 16:52:15 2000 @@ -0,0 +1,993 @@ +/* + ********************************************************************** + * sblive_voice.c - Voice manager for emu10k1 driver + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + + +#include "hwaccess.h" + +/************************************************************************ + * + * static struct emu_voice * sblive_emuVoiceAlloc(struct sblive_hw *sb_hw, u32 flags) + * + * ENTRY + * sb_hw - Pointer to the HWOBJ to allocate voice from + * flags - flags from struct voice_allocdesc from the ICardxxx + * object that called sblive_voiceAlloc + * + * RETURNS + * SUCCESS - Pointer to a struct emu_voice + * FAILURE - NULL + * + * ABOUT + * This is a static funciton that searches for free emuvoices + * and tries to return the type needed according to flags + * + ************************************************************************/ +static struct emu_voice *sblive_emuVoiceAlloc(struct voice_mgr *voice, u32 flags) +{ + unsigned int voicenum; + struct sblive_hw *sb_hw = voice->sb_hw; + struct emu_voice *foundvoice = NULL; + struct emu_voice *slavevoice = NULL; + struct emu_voice *testvoice, *testslave; + u32 curvol, lowestvol, mastervol, slavevol, CA, dcysusv; + + DPF(2, "Entered sblive_emuVoiceAlloc\n"); + + if (flags & VOICEMGR_FLAGS_MONO) + { + DPF(2, "sblive_emuVoiceAlloc MONO PLAYBACK voicei\n"); + + /* Check for free voices first */ + if (voice->free_voices) + { + voice->free_voices->voicestate = VOICEMGR_STATE_TRANSITION; + foundvoice = voice->free_voices; + foundvoice->flags = 0; + DPD(2, " Voice found %d\n", foundvoice->voicenum); + return foundvoice; + } + + /* We couldn't find any free voices so we need to free up + * some voices. We only rip off MIDI voices at the moment. */ + testvoice = dlFirstNode(&voice->midi_voices); + + lowestvol = 0xffffffff; + + while (testvoice) + { + /* Voice stealing algorithm goes here */ + voicenum = testvoice->voicenum; + + if (!(testvoice->voicestate & VOICEMGR_STATE_TRANSITION)) + { + /* Check if voice is looping silence */ + CA = sblive_readptr(sb_hw, CCCA_CURRADDR, voicenum); + if (CA > testvoice->voice_params.end) + { + testvoice->voicestate |= VOICEMGR_STATE_TRANSITION; + if (foundvoice) + foundvoice->voicestate &= ~VOICEMGR_STATE_TRANSITION; + lowestvol = 0; + foundvoice = testvoice; + break; + } + + curvol = sblive_readptr(sb_hw, CVCF_CURRENTVOL, voicenum); + dcysusv = sblive_readptr(sb_hw, DCYSUSV, voicenum); + dcysusv = (dcysusv & DCYSUSV_PHASE1_MASK) ? 0 : 0x1000; /* 0x1000 means what? */ + + if ((curvol < 0x200) && !dcysusv && (testvoice->voicestate & VOICEMGR_STATE_RELEASE)) + { + /* The volume is less than the threshold and + the volume envelope is in the decaying state. + Stop the pitch to stop the busmaster activities. + */ + sblive_writeptr(sb_hw, IP, voicenum, 0); + sblive_writeptr(sb_hw, VTFT, voicenum, VTFT_FILTERTARGET_MASK); + sblive_writeptr(sb_hw, PTRX_PITCHTARGET, voicenum, 0); + } else + { + if (testvoice->voicestate & VOICEMGR_STATE_SUSTAIN) + curvol += 0x500 + dcysusv; + else if (testvoice->voicestate & VOICEMGR_STATE_ATTACK) + curvol += 0x200 + dcysusv; + } + + if (curvol < lowestvol) + { + testvoice->voicestate |= VOICEMGR_STATE_TRANSITION; + + if (foundvoice) + foundvoice->voicestate &= ~VOICEMGR_STATE_TRANSITION; + foundvoice = testvoice; + lowestvol = curvol; + } + } + testvoice = dlNextNode(testvoice); + } + + if (foundvoice) + { + if (foundvoice->callback) + { + /* Note that this function can change the voice suggested to another voice */ + if (foundvoice->callback(VOICECALLBACK_EVENT_FREEVOICE, foundvoice->callback_data, (u32)&foundvoice) + != CTSTATUS_SUCCESS) + { + DPF(2, "Found voide callback failed\n"); + foundvoice = NULL; + } + } + + foundvoice->flags = 0; + foundvoice->master_voice = NULL; + foundvoice->linked_voice = NULL; + } else + { + DPF(2, "Voice not found!\n"); + } + + return foundvoice; + } else + { + DPF(2, "sblive_emuVoiceAlloc STEREO PLAYBACK voice\n"); + + voicenum = 8; + lowestvol = 0xffffffff; + + do + { + /* Since stereo voices must be side-by-side, + we have to check 2 voices at once. */ + if (((voice->voices[voicenum].usage == VOICEMGR_USAGE_FREE) + || (voice->voices[voicenum].usage == VOICEMGR_USAGE_MIDI)) + && ((voice->voices[voicenum + 1].usage == VOICEMGR_USAGE_FREE) + || (voice->voices[voicenum + 1].usage == VOICEMGR_USAGE_MIDI))) + { + mastervol = 0xffffffff; + slavevol = 0xffffffff; + testvoice = &voice->voices[voicenum]; + testslave = &voice->voices[voicenum + 1]; + + if ((!(testvoice->voicestate & VOICEMGR_STATE_TRANSITION)) + && (!(testslave->voicestate & VOICEMGR_STATE_TRANSITION))) + { + /* Voice stealing algorithm goes here */ + + /* Check master voice */ + if (testvoice->usage == VOICEMGR_USAGE_FREE) + mastervol = 0; + else + { + /* Check if voice is looping silence */ + CA = sblive_readptr(sb_hw, CCCA_CURRADDR, voicenum); + + if (CA > testvoice->voice_params.end) + mastervol = 0; + else + { + mastervol = sblive_readptr(sb_hw, CVCF_CURRENTVOL, voicenum); + if (!(testvoice->voicestate & VOICEMGR_STATE_RELEASE)) + mastervol += 0x1300; /* Why??? */ + } + } + + /* Check slave voice */ + if (testslave->usage == VOICEMGR_USAGE_FREE) + slavevol = 0; + else + { + /* Check if voice is looping silence */ + CA = sblive_readptr(sb_hw, CCCA_CURRADDR, voicenum + 1); + + if (CA > testslave->voice_params.end) + slavevol = 0; + else + { + slavevol = sblive_readptr(sb_hw, CVCF_CURRENTVOL, (voicenum + 1)); + if (!(testslave->voicestate & VOICEMGR_STATE_RELEASE)) + slavevol += 0x1300; /* Why??? */ + } + } + + if ((mastervol + slavevol) < lowestvol) + { + testvoice->voicestate |= VOICEMGR_STATE_TRANSITION; + testslave->voicestate |= VOICEMGR_STATE_TRANSITION; + + if (foundvoice) + { + foundvoice->voicestate &= ~VOICEMGR_STATE_TRANSITION; + slavevoice->voicestate &= ~VOICEMGR_STATE_TRANSITION; + } + + foundvoice = testvoice; + slavevoice = testslave; + lowestvol = mastervol + slavevol; + } + } + } + + voicenum = (voicenum + 2) % NUM_G; + + } while (voicenum != 8); + + if (foundvoice) + { + if (foundvoice->callback) + { + /* Note that this function can change the voice suggested to another voice */ + if (foundvoice->callback(VOICECALLBACK_EVENT_FREEVOICE, foundvoice->callback_data, (u32)&foundvoice) + != CTSTATUS_SUCCESS) + { + DPF(2, "Voice callback failed\n"); + } + } + + if (slavevoice->callback) + { + /* Note that this function can change the voice suggested to another voice */ + testvoice = slavevoice; + if (testvoice->callback(VOICECALLBACK_EVENT_FREEVOICE, testvoice->callback_data, (u32)&testvoice) + != CTSTATUS_SUCCESS) + { + DPF(2, "Voice callback failed\n"); + } + + if (testvoice != slavevoice) + { + DPF(2, "testvoice != slavevoice\n"); + return NULL; + } + } + + DPD(2, "Voice found %x\n", (u32)voicenum); + + /* Link next voice in stereo pair */ + foundvoice->master_voice = NULL; + foundvoice->linked_voice = slavevoice; + slavevoice->master_voice = foundvoice; + slavevoice->linked_voice = NULL; + + /* Mark second voice as stereo slave */ + foundvoice->flags = VOICEMGR_FLAGS_VOICEMASTER; + slavevoice->flags = VOICEMGR_FLAGS_STEREOSLAVE; + + return foundvoice; + } + } + + return foundvoice; +} + +/************************************************************************ + * + * int sblive_voiceInit(struct sblive_hw *sb_hw) + * + * ENTRY + * card - Pointer to the card obj to init voice manager + * + * RETURNS + * Always returns CTSTATUS_SUCCESS + * + * ABOUT + * Inits data + * + ************************************************************************/ +int sblive_voiceInit(struct sblive_hw *sb_hw) +{ + struct voice_mgr *voice; + struct emu_voice *emu_voice; + int i; + + voice = &sb_hw->voice_manager; + voice->free_voices = NULL; + + emu_voice = voice->voices; + for (i = 0; i < NUM_G; i++) + { + unsigned long flags; + + emu_voice->sb_hw = sb_hw; + emu_voice->usage = VOICEMGR_USAGE_FREE; + emu_voice->voicestate = VOICEMGR_STATE_IDLE; + emu_voice->voicenum = i; + emu_voice->linked_voice = NULL; + + spin_lock_irqsave(&sblive_spinlock, flags); + + dlAddNode(&voice->free_voices, emu_voice); + + spin_unlock_irqrestore(&sblive_spinlock, flags); + + ++emu_voice; + } + + voice->sb_hw = sb_hw; + voice->playback_voices = NULL; + voice->record_voices = NULL; + voice->midi_voices = NULL; + + return CTSTATUS_SUCCESS; +} + + +/************************************************************************ + * + * int sblive_voiceExit(struct sblive_hw *sb_hw) + * + * ENTRY + * sb_hw - Pointer to the HWOBJ to init voice manager + * + * RETURNS + * Always returns CTSTATUS_SUCCESS + * + * ABOUT + * Exits + * + ************************************************************************/ +int sblive_voiceExit(struct sblive_hw *sb_hw) +{ + int i; + struct emu_voice *emu_voice; + + emu_voice = sb_hw->voice_manager.voices; + for (i = 0; i < NUM_G; i++) + { + /* TODO: CALL ALL RELEASE CALLBACKS FOR BUSY VOICES */ + + emu_voice->usage = VOICEMGR_USAGE_FREE; + emu_voice->voicestate = VOICEMGR_STATE_IDLE; + emu_voice->voicenum = i; + emu_voice->linked_voice = NULL; + ++emu_voice; + } + + sb_hw->voice_manager.sb_hw = NULL; + sb_hw->voice_manager.free_voices = NULL; + sb_hw->voice_manager.playback_voices = NULL; + sb_hw->voice_manager.record_voices = NULL; + sb_hw->voice_manager.midi_voices = NULL; + + return CTSTATUS_SUCCESS; +} + + +/************************************************************************ + * + * int sblive_voiceAlloc(struct voice_allocdesc *voiceallocdesc, + * struct emu_voice * *voices) + * + * ENTRY + * voiceallocdesc - + * Pointer to a struct voice_allocdesc that describes the + * voices needed. + * + * voices - + * Address to return allocated struct emu_voices + * + * RETURNS + * SUCCESS - CTSTATUS_SUCCESS + * FAILURE - CTSTATUS_INUSE (not enough available voices) + * CTSTATUS_ERROR + * + * ABOUT + * Allocates voices according to the types requested. + * + ************************************************************************/ +int sblive_voiceAlloc(struct voice_allocdesc *voiceallocdesc, struct emu_voice **voices) +{ + struct emu_voice **ppOwnerList = NULL; /* Linux: added initialization */ + struct emu_voice *allocvoicelist; + struct emu_voice *lastvoice = NULL; /* Linux: added initialization */ + struct emu_voice *linkvoice; + int status = CTSTATUS_SUCCESS; + struct sblive_hw *sb_hw = voiceallocdesc->sb_hw; + unsigned long flags; + int i; + + if (!voiceallocdesc->numvoicereqs) + { + /* No voices requested??? */ + DPF(2, "No voices requested!\n"); + return CTSTATUS_INVALIDPARAM; + } + + /* Determine 'busy' list to attach voices to */ + if (voiceallocdesc->ownertype == VOICEMGR_USAGE_PLAYBACK) + ppOwnerList = &sb_hw->voice_manager.playback_voices; + else if (voiceallocdesc->ownertype == VOICEMGR_USAGE_MIDI) + ppOwnerList = &sb_hw->voice_manager.midi_voices; + + allocvoicelist = NULL; + + /* Satisfy voice requests one by one */ + for (i = 0; i < voiceallocdesc->numvoicereqs; i++) + { + /* Allocate voices */ + + linkvoice = sblive_emuVoiceAlloc(&sb_hw->voice_manager, voiceallocdesc->flags[i]); + + if (linkvoice) + { + /* Suitable free voice found */ + if (allocvoicelist == NULL) + { + allocvoicelist = linkvoice; + linkvoice->master_voice = NULL; + } else + { + lastvoice->linked_voice = linkvoice; + linkvoice->master_voice = lastvoice; + } + + linkvoice->flags = VOICEMGR_FLAGS_VOICEMASTER; + + /* Repeat for all linked voices */ + while (linkvoice) + { + /* Shut off the voice */ + sblive_writeptr(sb_hw, IFATN, linkvoice->voicenum, 0xffff); + sblive_writeptr(sb_hw, DCYSUSV , linkvoice->voicenum, ENV_OFF); + sblive_writeptr(sb_hw, VTFT, linkvoice->voicenum, 0xffff); + sblive_writeptr(sb_hw, PTRX, linkvoice->voicenum, 0); + + spin_lock_irqsave(&sblive_spinlock, flags); + + dlDelNode(&sb_hw->voice_manager.free_voices, linkvoice); /* Remove voice from free list */ + + linkvoice->sb_hw = sb_hw; + linkvoice->ownertype = voiceallocdesc->ownertype; + linkvoice->callback = voiceallocdesc->callback; + linkvoice->callback_data = voiceallocdesc->callback_data; + + /* We need a transition state to make a voice + as belonging to a owner but not yet assigned + to that owner yet */ + linkvoice->voicestate |= VOICEMGR_STATE_TRANSITION; + linkvoice->usage = voiceallocdesc->ownertype; + linkvoice->flags |= voiceallocdesc->flags[i]; + + dlAddNode(ppOwnerList, linkvoice); /* Attach voice to busy list */ + + spin_unlock_irqrestore(&sblive_spinlock, flags); + + lastvoice = linkvoice; /* Remember last of linked voices */ + + linkvoice = linkvoice->linked_voice; + } + } else + { + /* No free suitable voices found - steal from busy voices */ + status = CTSTATUS_INUSE; + + /* TODO : IMPLEMENT VOICE STEALING ALGORITHM */ + + DPF(2, "Steal a voice: not yet implemented\n"); + + spin_lock_irqsave(&sblive_spinlock, flags); + + while (allocvoicelist) + { + linkvoice = allocvoicelist; + allocvoicelist = allocvoicelist->linked_voice; + dlDelNode(ppOwnerList, linkvoice); + + linkvoice->linked_voice = NULL; + linkvoice->master_voice = NULL; + linkvoice->flags = 0; + linkvoice->callback = 0; + linkvoice->callback_data = 0; + linkvoice->voicestate = VOICEMGR_STATE_IDLE; + linkvoice->usage = VOICEMGR_USAGE_FREE; + + dlAddNode(&sb_hw->voice_manager.free_voices, linkvoice); + } + + spin_unlock_irqrestore(&sblive_spinlock, flags); + + break; + } + } + + if (voiceallocdesc->ownertype != VOICEMGR_USAGE_MIDI) + { + linkvoice = allocvoicelist; + + while (linkvoice) + { + linkvoice->voicestate &= ~VOICEMGR_STATE_TRANSITION; + linkvoice = linkvoice->linked_voice; + } + } + + *voices = allocvoicelist; + + return status; +} + + +/************************************************************************ + * + * int sblive_voiceFree(struct emu_voice * voices) + * + * ENTRY + * voices - + * pointer to a struct emu_voice returned + * by a call to sblive_voiceAlloc() + * + * RETURNS + * SUCCESS - CTSTATUS_SUCCESS + * FAILURE - CTSTATUS_ERROR + * + * ABOUT + * Frees a previously allocated voice. Multiple voices can + * be allocated at once but must be freed individually. + * + ************************************************************************/ +int sblive_voiceFree(struct emu_voice *voices) +{ + struct emu_voice *voicetemp; + struct emu_voice **ppOwnerList; + struct sblive_hw *sb_hw; + unsigned long flags; + + if (voices == NULL) + return CTSTATUS_INVALIDPARAM; + + sb_hw = voices->sb_hw; + + if (voices->usage == VOICEMGR_USAGE_PLAYBACK) + ppOwnerList = &sb_hw->voice_manager.playback_voices; + else if (voices->usage == VOICEMGR_USAGE_MIDI) + ppOwnerList = &sb_hw->voice_manager.midi_voices; + else + { + DPF(2, "Unknown VOICEMGR_USAGE\n"); + return CTSTATUS_ERROR; + } + + if (!(voices->flags & VOICEMGR_FLAGS_VOICEMASTER)) + { + /* Not first voice in list */ + DPF(2, "Not first voice in list!\n"); + return CTSTATUS_ERROR; + } + + while (voices) + { + unsigned dcysusv, voicenum; + + voicenum = voices->voicenum; + + sblive_writeptr(sb_hw, IFATN, voicenum, IFATN_FILTERCUTOFF_MASK | IFATN_ATTENUATION_MASK); + sblive_writeptr(sb_hw, IP, voicenum, 0); + + dcysusv = sblive_readptr(sb_hw, DCYSUSV, voicenum) & (DCYSUSV_PHASE1_MASK | DCYSUSV_SUSTAINLEVEL_MASK | DCYSUSV_DECAYTIME_MASK); + sblive_writeptr(sb_hw, DCYSUSV, voicenum, dcysusv | ENV_OFF); + + sblive_writeptr(sb_hw, VTFT, voicenum, VTFT_FILTERTARGET_MASK); + sblive_writeptr(sb_hw, PTRX_PITCHTARGET, voicenum, 0); + sblive_writeptr(sb_hw, CVCF, voicenum, CVCF_CURRENTFILTER_MASK); + sblive_writeptr(sb_hw, CPF, voicenum, 0); + + { + u32 cra; + u32 sample; + int i; + + sample = (voices->flags & VOICEMGR_FLAGS_16BIT) ? 0 : 0x80808080; + cra = sblive_readptr(sb_hw, CCR, voicenum) & CCR_READADDRESS_MASK; + sblive_writeptr(sb_hw, CCR, voicenum, cra); + cra = (cra >> 18) & 0xf; + sblive_writeptr(sb_hw, CD0 + cra, voicenum, sample); + cra = (cra + 0x1) & 0xf; + sblive_writeptr(sb_hw, CD0 + cra, voicenum, sample); + + for (i = 0; i < NUM_FXSENDS; i++) + if (voices->sendhandle[i]) + voices->sendhandle[i] = 0; + } + + spin_lock_irqsave(&sblive_spinlock, flags); + + /* Reattach to free list */ + voicetemp = voices; + voices = voices->linked_voice; + + dlDelNode(ppOwnerList, voicetemp); /* Detach first */ + voicetemp->linked_voice = NULL; + voicetemp->master_voice = NULL; + voicetemp->flags = 0; + voicetemp->callback = 0; + voicetemp->callback_data = 0; + voicetemp->voicestate = VOICEMGR_STATE_IDLE; + voicetemp->usage = VOICEMGR_USAGE_FREE; + dlAddNode(&sb_hw->voice_manager.free_voices, voicetemp); + + spin_unlock_irqrestore(&sblive_spinlock, flags); + + } + + return CTSTATUS_SUCCESS; +} + + +/************************************************************************ + * + * int sblive_voicePlaybackSetup(struct emu_voice * voices) + * + * ENTRY + * voices - + * pointer to a struct emu_voice returned + * by a call to sblive_voiceAlloc() + * pVoiceSetting - + * pointer to an array of setting params + * + * RETURNS + * SUCCESS - CTSTATUS_SUCCESS + * FAILURE - CTSTATUS_ERROR + * + * ABOUT + * Sets up a voices for Wave Playback + * + ************************************************************************/ +int sblive_voicePlaybackSetup(struct emu_voice *voices) +{ + int voicenum; + struct sblive_hw *sb_hw; + u32 cra = 0; + u32 start = 0; + struct voice_param *pvoice_params; + struct emu_voice *currvoice; + + currvoice = voices; + while (currvoice) + { + /* Or here? (voices can be on different cards) */ + sb_hw = currvoice->sb_hw; + voicenum = currvoice->voicenum; + pvoice_params = &currvoice->voice_params; + + sblive_writeptr(sb_hw, DCYSUSV, voicenum, ENV_OFF); + sblive_writeptr(sb_hw, VTFT, voicenum, VTFT_FILTERTARGET_MASK); + sblive_writeptr(sb_hw, CVCF, voicenum, CVCF_CURRENTFILTER_MASK); + + /* Stop CA */ + /* Assumption that PT is already 0 so no harm overwriting */ + sblive_writeptr(sb_hw, PTRX, voicenum, + (pvoice_params->unSends.tSends.reverb_send << 8) | pvoice_params->unSends.tSends.aux_send); + + if (currvoice->flags & VOICEMGR_FLAGS_VOICEMASTER) + { + u32 sample; + + if (currvoice->linked_voice != NULL) + { + /* Set stereo bit */ + cra = 64; + sblive_writeptr(sb_hw, CPF, voicenum, CPF_STEREO_MASK); + sblive_writeptr(sb_hw, CPF, voicenum + 1, CPF_STEREO_MASK); + } else + { + cra = 32; + sblive_writeptr(sb_hw, CPF, voicenum, 0); + } + + if (currvoice->flags & VOICEMGR_FLAGS_16BIT) + sample = 0; + else + { + cra = cra * 2; + sample = 0x80808080; + } + cra -= 4; + + if (currvoice->linked_voice != NULL) + { + sblive_writeptr(sb_hw, CCR, voicenum, 0x3c << 16); + sblive_writeptr(sb_hw, CCR, voicenum + 1, cra << 16); + sblive_writeptr(sb_hw, CDE, voicenum + 1, sample); + sblive_writeptr(sb_hw, CDF, voicenum + 1, sample); + start = pvoice_params->start + cra / 2; + } else + { + sblive_writeptr(sb_hw, CCR, voicenum, 0x1c << 16); /* FIXME: Is 0x1c correct? */ + sblive_writeptr(sb_hw, CDE, voicenum, sample); + sblive_writeptr(sb_hw, CDF, voicenum, sample); + start = pvoice_params->start + cra; + } + + if (start > pvoice_params->endloop) + { + start -= pvoice_params->endloop; + + if (currvoice->linked_voice != NULL) + cra = (cra << 25) | 0x1bc0000 | ((cra - start) << 9); + else + cra = (cra << 25) | 0x11c0000 | ((cra - start) << 9); + + start += pvoice_params->startloop; + + if (start >= pvoice_params->endloop) + start = pvoice_params->endloop - 1; + } else + if (currvoice->linked_voice != NULL) + cra = (cra << 25) | (0x3c << 16); + else + cra = (cra << 25) | (0x1c << 16); + + start |= CCCA_INTERPROM_0; + } + + /* CSL, ST, CA */ + sblive_writeptr(sb_hw, DSL, voicenum, + pvoice_params->endloop | ((u32) pvoice_params->unSends.tSends.chorus_send << 24)); + + sblive_writeptr(sb_hw, PSST, voicenum, + pvoice_params->startloop | ((u32) pvoice_params->unSends.tSends.pan_send << 24)); + + if (currvoice->flags & VOICEMGR_FLAGS_16BIT) + sblive_writeptr(sb_hw, CCCA, voicenum, start); + else + sblive_writeptr(sb_hw, CCCA, voicenum, start | CCCA_8BITSELECT); + + /* Clear filter delay memory */ + sblive_writeptr(sb_hw, Z1, voicenum, 0); + sblive_writeptr(sb_hw, Z2, voicenum, 0); + + /* Invalidate maps */ + sblive_writeptr(sb_hw, MAPA, voicenum, MAP_PTI_MASK | (sb_hw->silentpage->physaddx * 2)); + sblive_writeptr(sb_hw, MAPB, voicenum, MAP_PTI_MASK | (sb_hw->silentpage->physaddx * 2)); + + /* Fill cache */ + if (currvoice->flags & VOICEMGR_FLAGS_VOICEMASTER) + sblive_writeptr(sb_hw, CCR, voicenum, cra); + + sblive_writeptr(sb_hw, ATKHLDV, voicenum, ATKHLDV_HOLDTIME_MASK | ATKHLDV_ATTACKTIME_MASK); + sblive_writeptr(sb_hw, LFOVAL1, voicenum, 0x8000); + sblive_writeptr(sb_hw, ATKHLDM, voicenum, 0); + sblive_writeptr(sb_hw, DCYSUSM, voicenum, DCYSUSM_DECAYTIME_MASK); + sblive_writeptr(sb_hw, LFOVAL2, voicenum, 0x8000); + sblive_writeptr(sb_hw, IP, voicenum, pvoice_params->initial_pitch); + sblive_writeptr(sb_hw, PEFE, voicenum, 0x7f); + sblive_writeptr(sb_hw, FMMOD, voicenum, 0); + sblive_writeptr(sb_hw, TREMFRQ, voicenum, 0); + sblive_writeptr(sb_hw, FM2FRQ2, voicenum, 0); + sblive_writeptr(sb_hw, ENVVAL, voicenum, 0xbfff); + sblive_writeptr(sb_hw, ENVVOL, voicenum, 0xbfff); + sblive_writeptr(sb_hw, IFATN, voicenum, IFATN_FILTERCUTOFF_MASK | pvoice_params->initial_attn); + + pvoice_params->FC_target = 0xffff; + pvoice_params->pitch_target = (u16)(IP_TO_CP(pvoice_params->initial_pitch) >> 16); + + currvoice = currvoice->linked_voice; + } + + return CTSTATUS_SUCCESS; +} + +/************************************************************************ + * + * int sblive_voiceStart(struct emu_voice * voices) + * + * ENTRY + * voices - + * pointer to a struct emu_voice returned + * by a call to sblive_voiceAlloc() + * + * RETURNS + * SUCCESS - CTSTATUS_SUCCESS + * FAILURE - CTSTATUS_ERROR + * + * ABOUT + * Starts voices + * + ************************************************************************/ +int sblive_voiceStart(struct emu_voice *voices) +{ + struct sblive_hw *sb_hw; + struct emu_voice *tempvoice; + + tempvoice = voices; + sb_hw = tempvoice->sb_hw; + + /* Actual start */ + while (tempvoice != NULL) + { + int voicenum = 0; /* Linux: Added initialization */ + + if (tempvoice->flags & VOICEMGR_FLAGS_PLAYBACK) + { + struct voice_param *pvoice_params = &tempvoice->voice_params; + voicenum = tempvoice->voicenum; + + /* Playback */ + sblive_writeptr(sb_hw, PTRX_PITCHTARGET, voicenum, pvoice_params->pitch_target); + + if (!(tempvoice->flags & VOICEMGR_FLAGS_STEREOSLAVE)) + sblive_writeptr(sb_hw, CPF_CURRENTPITCH, voicenum, pvoice_params->pitch_target); + + sblive_writeptr(sb_hw, VTFT, voicenum, + ((u32) pvoice_params->volume_target << 16) + | pvoice_params->FC_target); + sblive_writeptr(sb_hw, CVCF, voicenum, + ((u32) pvoice_params->volume_target << 16) + | pvoice_params->FC_target); + sblive_writeptr(sb_hw, DCYSUSV , voicenum, + (pvoice_params->byampl_env_sustain << 8) + | ENV_ON | pvoice_params->byampl_env_decay); + + /* Using StopOnLoop for MIDI stops the playback + too early, which may cause a DC level to be played + until the note is released. */ + + if (tempvoice->usage == VOICEMGR_USAGE_MIDI) + halClearStopOnLoop(sb_hw, voicenum); + else + { + if (pvoice_params->startloop > pvoice_params->end) + halSetStopOnLoop(sb_hw, voicenum); + else + halClearStopOnLoop(sb_hw, voicenum); + } + } else + /* Recording */ + sblive_writeptr(sb_hw, DCYSUSV, voicenum, DCYSUSV_SUSTAINLEVEL_MASK | DCYSUSV_DECAYTIME_MASK | ENV_ON); + + tempvoice->voicestate |= VOICEMGR_STATE_ATTACK; + tempvoice = tempvoice->linked_voice; + } + + return CTSTATUS_SUCCESS; +} + + +/************************************************************************ + * + * int sblive_voiceStop(struct emu_voice * voices) + * + * ENTRY + * voices - + * pointer to a struct emu_voice returned + * by a call to sblive_voiceAlloc() + * + * RETURNS + * SUCCESS - CTSTATUS_SUCCESS + * FAILURE - CTSTATUS_ERROR + * + * ABOUT + * Stops voices + * + ************************************************************************/ +int sblive_voiceStop(struct emu_voice *voices) +{ + struct sblive_hw *sb_hw = voices->sb_hw; + struct emu_voice *tempvoice = voices; + unsigned long flags; + + spin_lock_irqsave(&sblive_spinlock, flags); + + while (tempvoice != NULL) + { + + if (voices->flags & VOICEMGR_FLAGS_PLAYBACK) + { + int voicenum = tempvoice->voicenum; + + sblive_writeptr(sb_hw, IFATN, voicenum, 0xffff); + sblive_writeptr(sb_hw, IP, voicenum, 0); + sblive_writeptr(sb_hw, VTFT, voicenum, 0xffff); + sblive_writeptr(sb_hw, PTRX_PITCHTARGET, voicenum, 0); + } + + tempvoice->voicestate = VOICEMGR_STATE_IDLE; + tempvoice = tempvoice->linked_voice; + } + + tempvoice = voices; + + spin_unlock_irqrestore(&sblive_spinlock, flags); + + return CTSTATUS_SUCCESS; +} + + +/************************************************************************ + * + * int sblive_voiceSetControl(struct emu_voice *voices, + * struct voice_cntlset *pSetting, + * u32 numparam) + * + * ENTRY + * voices - + * pointer to a struct emu_voice returned + * by a call to sblive_voiceAlloc() + * + * pSetting - + * An array of CNTLSET structures + * + * numparam - + * Number of struct voice_cntlset structures in pSetting + * + * RETURNS + * SUCCESS - CTSTATUS_SUCCESS + * FAILURE - CTSTATUS_ERROR + * + * ABOUT + * Sets a voice control + * + ************************************************************************/ +int sblive_voiceSetControl(struct emu_voice *voices, struct voice_cntlset *setting, u32 numparam) +{ + struct sblive_hw *sb_hw = voices->sb_hw; + struct emu_voice *currvoice = voices; + int count; + + for (count = 0; count < numparam; count++) + sblive_writeptr(sb_hw, setting[count].paramID, currvoice->voicenum, setting[count].value); + + return CTSTATUS_SUCCESS; +} + + +/************************************************************************ + * + * int sblive_voiceGetControl(struct emu_voice * voices, + * u32 controlid, u32 *value) + * + * ENTRY + * voices - + * pointer to a struct emu_voice returned + * by a call to sblive_voiceAlloc() + * + * controlid - + * control to get + * + * pvalue - + * address to return control value + * + * RETURNS + * SUCCESS - CTSTATUS_SUCCESS + * FAILURE - CTSTATUS_ERROR + * + * ABOUT + * Gets a voice control + * + ************************************************************************/ +int sblive_voiceGetControl(struct emu_voice *voices, u32 controlid, u32 *value) +{ + struct sblive_hw *sb_hw = voices->sb_hw; + + *value = sblive_readptr(sb_hw, controlid, voices->voicenum); + + return CTSTATUS_SUCCESS; +} Index: oldkernel/linux/drivers/sound/emu10k1/voicemgr.h diff -u /dev/null linux/drivers/sound/emu10k1/voicemgr.h:1.1 --- /dev/null Mon Jul 31 21:13:56 2000 +++ linux/drivers/sound/emu10k1/voicemgr.h Thu Jun 1 16:52:15 2000 @@ -0,0 +1,242 @@ +/* + ********************************************************************** + * sblive_voice.h -- EMU Voice Resource Manager header file + * Copyright 1999, 2000 Creative Labs, Inc. + * + ********************************************************************** + * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * + ********************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + ********************************************************************** + */ + +#ifndef _VOICEMGR_H +#define _VOICEMGR_H + + +#define SAMPLETYPE_ROMMASK 0x8000 +#define SAMPLETYPE_MONOSAMPLE 1 +#define SAMPLETYPE_RIGHTSAMPLE 2 +#define SAMPLETYPE_LEFTSAMPLE 4 + +#define SAMPLEMODE_MASK 3 +#define SAMPLEMODE_RELEASEINLOOP 1 +#define SAMPLEMODE_RELEASEOUTLOOP 3 + +#define WAVE_RAMP_TIME_TO_TARGET 100 +#define MIDI_RAMP_TIME_TO_TARGET 200 + +#define RAMP_REVERB_SEND_NUM 0 +#define RAMP_AUX_SEND_NUM 1 +#define RAMP_PAN_SEND_NUM 2 +#define RAMP_CHORUS_SEND_NUM 3 + +/* struct emu_voice.usage flags */ +#define VOICEMGR_USAGE_FREE 0x00000000 +#define VOICEMGR_USAGE_MIDI 0x00000001 +#define VOICEMGR_USAGE_PLAYBACK 0x00000002 +#define VOICEMGR_USAGE_RECORDING 0x00000004 + +/* struct emu_voice.voicestate flags */ +#define VOICEMGR_STATE_IDLE 0x00000000 +#define VOICEMGR_STATE_ATTACK 0x00000001 +#define VOICEMGR_STATE_RELEASE 0x00000002 +#define VOICEMGR_STATE_IRQON 0x00000004 +#define VOICEMGR_STATE_TRANSITION 0x00000008 +#define VOICEMGR_STATE_SUSTAIN 0x00000010 + +/* struct emu_voice.flags flags */ +#define VOICEMGR_FLAGS_PLAYBACK 0x00000001 +#define VOICEMGR_FLAGS_MONO 0x00000002 +#define VOICEMGR_FLAGS_16BIT 0x00000004 +#define VOICEMGR_FLAGS_STEREOSLAVE 0x00000008 +#define VOICEMGR_FLAGS_VOICEMASTER 0x80000000 +#define VOICEMGR_FLAGS_FASTCALLBACK 0x40000000 + +#define VOICECALLBACK_EVENT_CALLBACKSIZEREACHED 0 +#define VOICECALLBACK_EVENT_FREEVOICE 1 +#define VOICECALLBACK_EVENT_TIMER 2 + +#if !defined(DLIST) +#define DLIST(x) struct {x *next, *pPrev;} DList + +#define dlAddNode(list,node) \ + { \ + (node)->DList.next = *(list); \ + (node)->DList.pPrev = NULL; \ + if (*list) (*(list))->DList.pPrev = (node); *(list) = (node); \ + } + +#define dlDelNode(list, node) \ + { \ + if ((node)->DList.pPrev == NULL) \ + *(list) = (node)->DList.next; \ + else \ + ((node)->DList.pPrev)->DList.next = (node)->DList.next; \ + if ((node)->DList.next != NULL) \ + ((node)->DList.next)->DList.pPrev = (node)->DList.pPrev; \ + (node)->DList.next = (node)->DList.pPrev = NULL; \ + } + + +#define dlFirstNode(list) (*(list)) +#define dlNextNode(curnode) ((curnode)->DList.next) +#define dlPrevNode(curnode) ((curnode)->DList.pPrev) +#endif + + +struct voice_param +{ + /* Sound engine */ + u32 start; + u32 startloop; + u32 endloop; + u32 end; + u16 current_pitch; + u16 pitch_target; + u16 current_volume; + u16 volume_target; + u16 current_FC; + u16 FC_target; + u8 pan_target; + u8 aux_target; + u8 coef; + u8 sample_mode; + u8 sample_type; + + union + { + struct + { + /* MUST be in the following order */ + u8 reverb_send; + u8 aux_send; + u8 pan_send; + u8 chorus_send; + } tSends; + } unSends; + + /* Envelope engine */ + u16 ampl_env_delay; + u8 byampl_env_attack; + u8 byampl_env_hold; + u8 byampl_env_decay; + u8 byampl_env_sustain; + u8 byampl_env_release; + + u16 aux_env_delay; + u8 byaux_env_attack; + u8 byaux_env_hold; + u8 byaux_env_decay; + u8 byaux_env_sustain; + u8 byaux_env_release; + + u16 mod_LFO_delay; /* LFO1 */ + u16 vib_LFO_delay; /* LFO2 */ + u8 mod_LFO_freq; /* LFO1 */ + u8 vib_LFO_freq; /* LFO2 */ + + s8 aux_env_to_pitch; + s8 aux_env_to_FC; + s8 mod_LFO_to_pitch; + s8 vib_LFO_to_pitch; + s8 mod_LFO_to_FC; + s8 mod_LFO_to_volume; + + u16 sample_pitch; + u16 initial_pitch; + u8 initial_attn; + u8 initial_FC; + + u8 velocity; /* BUGBUG: to be moved to aweobj */ + + u32 bank; + u8 program; + u16 key_exclusive; +}; + +struct voice_allocdesc +{ + struct sblive_hw *sb_hw; + u32 ownertype; + CALLBACKFN callback; + u32 callback_data; + u32 numvoicereqs; /* Number of voice requests */ + /* Stereo voices count as 1 */ + /* request */ + u32 flags[MAXREQVOICES]; /* stereo/mono rec/playback 8/16 */ +}; + + +struct emu_voice +{ + DLIST(struct emu_voice); /* Doubly-linked list */ + + struct sblive_hw *sb_hw; + u32 ownertype; + CALLBACKFN callback; + u32 callback_data; + u32 usage; /* Free, MIDI, playback, recording, etc */ + u32 voicestate; /* Release, attack, idle, etc */ + u32 voicenum; /* Voice ID */ + u32 flags; /* Stereo/mono, rec/playback, 8/16 bit */ + + struct voice_param voice_params; + struct emu_voice *master_voice; /* Index to previous linked voiceobj */ + struct emu_voice *linked_voice; /* Index to linked voiceobj, else set to NULL */ + + u32 sendhandle[NUM_FXSENDS]; +}; + + +struct voice_mgr +{ + struct sblive_hw *sb_hw; + struct emu_voice *free_voices; + struct emu_voice *playback_voices; + struct emu_voice *record_voices; + struct emu_voice *midi_voices; + + struct emu_voice voices[NUM_G]; + + /* Shouldn't use this directly. Just move pointers around from */ + /* free_voices to the other lists and vice versa. */ + u32 ramp_eng_handle; +}; + +struct voice_cntlset +{ + u32 paramID; + u32 value; +}; + +int sblive_voiceInit(struct sblive_hw *); +int sblive_voiceExit(struct sblive_hw *); +int sblive_voiceAlloc(struct voice_allocdesc *, struct emu_voice **); +int sblive_voiceFree(struct emu_voice *); +int sblive_voicePlaybackSetup(struct emu_voice *); +int sblive_voiceStart(struct emu_voice *); +int sblive_voiceStop(struct emu_voice *); +int sblive_voiceSetControl(struct emu_voice *, struct voice_cntlset *, u32); +int sblive_voiceGetControl(struct emu_voice *, u32, u32 *); + +#endif /* _VOICEMGR_H */