sb16: port of isa sb16 driver to user-space. Port by Peter Boonstoppel.
es1371: By Laurens Bronwasser.
--- /dev/null
+#ifndef AC97_MIXER_H
+#define AC97_MIXER_H
+
+/* This is a main memory cache copy of the codec's ac97 configuration
+ registers. See Intel's Audio Codec 97 standard (rev2.3) for info. */
+
+typedef struct ac97_struct {
+ u16_t Reset; /* 0x00 */
+ u16_t MasterVolume; /* 0x02 */
+ u16_t AUXOutVolume; /* 0x04 */
+ u16_t MonoVolume; /* 0x06 */
+ u16_t MasterTone; /* 0x08 */
+ u16_t PCBeepVolume; /* 0x0A */
+ u16_t PhoneVolume; /* 0x0C */
+ u16_t MicVolume; /* 0x0E */
+ u16_t LineInVolume; /* 0x10 */
+ u16_t CDVolume; /* 0x12 */
+ u16_t VideoVolume; /* 0x14 */
+ u16_t AUXInVolume; /* 0x16 */
+ u16_t PCMOutVolume; /* 0x18 */
+ u16_t RecordSelect; /* 0x1A */
+ u16_t RecordGain; /* 0x1C */
+ u16_t RecordGainMic; /* 0x1E */
+ u16_t GeneralPurpose; /* 0x20 */
+ u16_t Control3D; /* 0x22 */
+ u16_t AudioIntAndPaging; /* 0x24 */
+ u16_t PowerdownControlAndStat; /* 0x26 */
+ u16_t ExtendedAudio1; /* 0x28 */
+ u16_t ExtendedAudio2; /* 0x2A */
+ /* ... */
+ u16_t VendorID1; /* 0x7C */
+ u16_t VendorID2; /* 0x7E */
+} ac97_t;
+
+
+
+/* Source and output volume control register defines */
+#define AC97_MASTER_VOLUME 0x02U /* Master out */
+#define AC97_AUX_OUT_VOLUME 0x04U /* Auxiliary out volume */
+#define AC97_MONO_VOLUME 0x06U /* Mono out volume */
+#define AC97_MASTER_TONE 0x08U /* high byte= bass, low byte= treble*/
+#define AC97_PC_BEEP_VOLUME 0x0aU /* PC speaker volume */
+#define AC97_PHONE_VOLUME 0x0cU /* Phone volume */
+#define AC97_MIC_VOLUME 0x0eU /* Mic, mono */
+#define AC97_LINE_IN_VOLUME 0x10U /* Line volume */
+#define AC97_CD_VOLUME 0x12U /* CD audio volume */
+#define AC97_VIDEO_VOLUME 0x14U /* Video (TV) volume */
+#define AC97_AUX_IN_VOLUME 0x16U /* Aux line source, left */
+#define AC97_PCM_OUT_VOLUME 0x18U /* The DACs - wav+synth */
+#define AC97_RECORD_GAIN_VOLUME 0x1cU /* Record input level */
+
+/* Other CODEC control register defines */
+#define AC97_RESET 0x00U /* any write here to reset CODEC */
+#define AC97_GENERAL_PURPOSE 0x20U /* */
+#define AC97_POWERDOWN_CONTROL_STAT 0x26U /* */
+#define AC97_RECORD_SELECT 0x1aU /* record mux select */
+#define AC97_VENDOR_ID1 0x7cU /* 1st two Vendor ID bytes */
+#define AC97_VENDOR_ID2 0x7eU /* last Vendor ID byte plus rev. number */
+
+/* Record Select defines */
+#define AC97_RECORD_MIC 0
+#define AC97_RECORD_CD 1
+#define AC97_RECORD_VIDEO 2
+#define AC97_RECORD_AUX 3
+#define AC97_RECORD_LINE 4
+#define AC97_RECORD_STEREO_MIX 5
+#define AC97_RECORD_MONO_MIX 6
+#define AC97_RECORD_PHONE 7
+
+#define MASTER_VOL_MASK 0x1F
+#define DAC_VOL_MASK 0x1F
+#define AUX_IN_VOL_MASK 0x1F
+#define MUTE_MASK 0x8000
+#endif /* AC97_MIXER_H */
--- /dev/null
+
+***** Minix 3 Audio drivers *****
+
+Directories:
+framework/ Generic driver framework
+sb16/ SB16 ISA driver
+es1371/ ES1371 driver
+
+
+Install driver:
+* select the directory corresponding to the card you own
+* run make install
+
+
+Creating special files:
+* cd /dev
+* mknod audio c 13 0
+* mknod rec c 13 1
+* mknod mixer c 13 2
+* chmod 666 audio rec mixer
+(for es1371 one can add a special file for the second DAC-channel, major 13, minor 3)
+
+
+Running the driver:
+* service up /usr/sbin/sb16 -dev /dev/audio
+or:
+* service up /usr/sbin/es1371 -dev /dev/audio
+
+
+Minix audio tools:
+recwave
+playwave
+mixer
+(available from /usr/src/commands/ibm)
--- /dev/null
+
+SRC.o: ../../drivers.h
+SRC.o: ../../libpci/pci.h
+SRC.o: /usr/include/ansi.h
+SRC.o: /usr/include/errno.h
+SRC.o: /usr/include/ibm/bios.h
+SRC.o: /usr/include/ibm/interrupt.h
+SRC.o: /usr/include/ibm/ports.h
+SRC.o: /usr/include/limits.h
+SRC.o: /usr/include/minix/bitmap.h
+SRC.o: /usr/include/minix/callnr.h
+SRC.o: /usr/include/minix/com.h
+SRC.o: /usr/include/minix/config.h
+SRC.o: /usr/include/minix/const.h
+SRC.o: /usr/include/minix/devio.h
+SRC.o: /usr/include/minix/dmap.h
+SRC.o: /usr/include/minix/ioctl.h
+SRC.o: /usr/include/minix/ipc.h
+SRC.o: /usr/include/minix/sys_config.h
+SRC.o: /usr/include/minix/syslib.h
+SRC.o: /usr/include/minix/sysutil.h
+SRC.o: /usr/include/minix/type.h
+SRC.o: /usr/include/signal.h
+SRC.o: /usr/include/stddef.h
+SRC.o: /usr/include/stdlib.h
+SRC.o: /usr/include/string.h
+SRC.o: /usr/include/sys/dir.h
+SRC.o: /usr/include/sys/ioc_sound.h
+SRC.o: /usr/include/sys/types.h
+SRC.o: /usr/include/unistd.h
+SRC.o: SRC.c
+SRC.o: SRC.h
+SRC.o: es1371.h
+SRC.o: wait.h
+
+codec.o: ../../drivers.h
+codec.o: ../../libpci/pci.h
+codec.o: ../AC97.h
+codec.o: /usr/include/ansi.h
+codec.o: /usr/include/errno.h
+codec.o: /usr/include/ibm/bios.h
+codec.o: /usr/include/ibm/interrupt.h
+codec.o: /usr/include/ibm/ports.h
+codec.o: /usr/include/limits.h
+codec.o: /usr/include/minix/bitmap.h
+codec.o: /usr/include/minix/callnr.h
+codec.o: /usr/include/minix/com.h
+codec.o: /usr/include/minix/config.h
+codec.o: /usr/include/minix/const.h
+codec.o: /usr/include/minix/devio.h
+codec.o: /usr/include/minix/dmap.h
+codec.o: /usr/include/minix/ioctl.h
+codec.o: /usr/include/minix/ipc.h
+codec.o: /usr/include/minix/sys_config.h
+codec.o: /usr/include/minix/syslib.h
+codec.o: /usr/include/minix/sysutil.h
+codec.o: /usr/include/minix/type.h
+codec.o: /usr/include/signal.h
+codec.o: /usr/include/stddef.h
+codec.o: /usr/include/stdlib.h
+codec.o: /usr/include/string.h
+codec.o: /usr/include/sys/dir.h
+codec.o: /usr/include/sys/ioc_sound.h
+codec.o: /usr/include/sys/types.h
+codec.o: /usr/include/unistd.h
+codec.o: SRC.h
+codec.o: codec.c
+codec.o: codec.h
+codec.o: es1371.h
+codec.o: wait.h
+
+es1371.o: ../../drivers.h
+es1371.o: ../../libpci/pci.h
+es1371.o: ../AC97.h
+es1371.o: ../framework/../../drivers.h
+es1371.o: ../framework/audio_fw.h
+es1371.o: /usr/include/ansi.h
+es1371.o: /usr/include/errno.h
+es1371.o: /usr/include/ibm/bios.h
+es1371.o: /usr/include/ibm/interrupt.h
+es1371.o: /usr/include/ibm/ports.h
+es1371.o: /usr/include/limits.h
+es1371.o: /usr/include/minix/bitmap.h
+es1371.o: /usr/include/minix/callnr.h
+es1371.o: /usr/include/minix/com.h
+es1371.o: /usr/include/minix/config.h
+es1371.o: /usr/include/minix/const.h
+es1371.o: /usr/include/minix/devio.h
+es1371.o: /usr/include/minix/dmap.h
+es1371.o: /usr/include/minix/ioctl.h
+es1371.o: /usr/include/minix/ipc.h
+es1371.o: /usr/include/minix/sys_config.h
+es1371.o: /usr/include/minix/syslib.h
+es1371.o: /usr/include/minix/sysutil.h
+es1371.o: /usr/include/minix/type.h
+es1371.o: /usr/include/signal.h
+es1371.o: /usr/include/stddef.h
+es1371.o: /usr/include/stdlib.h
+es1371.o: /usr/include/string.h
+es1371.o: /usr/include/sys/dir.h
+es1371.o: /usr/include/sys/ioc_sound.h
+es1371.o: /usr/include/sys/types.h
+es1371.o: /usr/include/unistd.h
+es1371.o: SRC.h
+es1371.o: codec.h
+es1371.o: es1371.c
+es1371.o: es1371.h
+es1371.o: wait.h
+
+wait.o: ../../drivers.h
+wait.o: ../../libpci/pci.h
+wait.o: /usr/include/ansi.h
+wait.o: /usr/include/errno.h
+wait.o: /usr/include/ibm/bios.h
+wait.o: /usr/include/ibm/interrupt.h
+wait.o: /usr/include/ibm/ports.h
+wait.o: /usr/include/limits.h
+wait.o: /usr/include/minix/bitmap.h
+wait.o: /usr/include/minix/callnr.h
+wait.o: /usr/include/minix/com.h
+wait.o: /usr/include/minix/config.h
+wait.o: /usr/include/minix/const.h
+wait.o: /usr/include/minix/devio.h
+wait.o: /usr/include/minix/dmap.h
+wait.o: /usr/include/minix/ipc.h
+wait.o: /usr/include/minix/sys_config.h
+wait.o: /usr/include/minix/syslib.h
+wait.o: /usr/include/minix/sysutil.h
+wait.o: /usr/include/minix/type.h
+wait.o: /usr/include/signal.h
+wait.o: /usr/include/stddef.h
+wait.o: /usr/include/stdlib.h
+wait.o: /usr/include/string.h
+wait.o: /usr/include/sys/dir.h
+wait.o: /usr/include/sys/types.h
+wait.o: /usr/include/time.h
+wait.o: /usr/include/unistd.h
+wait.o: wait.c
--- /dev/null
+# Makefile for the ES1371 sounddriver (SB16)
+
+# directories
+u = /usr
+i = $u/include
+s = $i/sys
+m = $i/minix
+b = $i/ibm
+pci_dir = ../../libpci
+gen_drv_dir = ../../gen_drivers/cyclic_dma
+
+# programs, flags, etc.
+CC = exec cc
+CFLAGS = -I$i
+LDFLAGS = -i
+LIBS = -lsys -lsysutil
+PCI = $(pci_dir)/pci.o $(pci_dir)/pci_table.o
+
+# build local binary
+all: es1371
+
+es1371: es1371.o SRC.o codec.o wait.o audio_fw.o $(PCI)
+ $(CC) -o $@ $(LDFLAGS) es1371.o SRC.o codec.o wait.o audio_fw.o $(PCI) $(LIBS)
+
+audio_fw.o: ../framework/audio_fw.c ../framework/audio_fw.h
+ $(CC) -c ../framework/audio_fw.c
+
+install: /usr/sbin/es1371
+/usr/sbin/es1371: es1371
+ install -o root -S 1024k -c $? $@
+
+$(PCI):
+ cd $(pci_dir) && $(MAKE)
+
+# clean up local files
+clean:
+ rm -f *.o *.bak core es1371
+
+depend:
+ /usr/bin/mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
+
+# Include generated dependencies.
+include .depend
+
--- /dev/null
+#include "SRC.h"
+
+#define SRC_RATE 48000U
+#define reg(n) DSP->base + n
+
+
+int SRCInit ( DEV_STRUCT * DSP )
+{
+ u32_t i;
+ int retVal;
+
+ /* Clear all SRC RAM then init - keep SRC disabled until done */
+ if (WaitBitd (reg(CONC_dSRCIO_OFF), SRC_BUSY_BIT, 0, 1000))
+ return (SRC_ERR_NOT_BUSY_TIMEOUT);
+
+ pci_outl(reg(CONC_dSRCIO_OFF), SRC_DISABLE);
+
+ for( i = 0; i < 0x80; ++i )
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, (u16_t)i, 0U)))
+ return (retVal);
+
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_SYNTH_BASE + SRC_TRUNC_N_OFF, 16 << 4)))
+ return (retVal);
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_SYNTH_BASE + SRC_INT_REGS_OFF, 16 << 10)))
+ return (retVal);
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_DAC_BASE + SRC_TRUNC_N_OFF, 16 << 4)))
+ return (retVal);
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_DAC_BASE + SRC_INT_REGS_OFF, 16 << 10)))
+ return (retVal);
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_SYNTH_LVOL, 1 << 12)))
+ return (retVal);
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_SYNTH_RVOL, 1 << 12)))
+ return (retVal);
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_DAC_LVOL, 1 << 12)))
+ return (retVal);
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_DAC_RVOL, 1 << 12)))
+ return (retVal);
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_ADC_LVOL, 1 << 12)))
+ return (retVal);
+ if (SRC_SUCCESS != (retVal = SRCRegWrite(DSP, SRC_ADC_RVOL, 1 << 12)))
+ return (retVal);
+
+ /* default some rates */
+ SRCSetRate(DSP, SRC_SYNTH_BASE, SRC_RATE);
+ SRCSetRate(DSP, SRC_DAC_BASE, SRC_RATE);
+ SRCSetRate(DSP, SRC_ADC_BASE, SRC_RATE);
+
+ /* now enable the whole deal */
+ if (WaitBitd (reg(CONC_dSRCIO_OFF), SRC_BUSY_BIT, 0, 1000))
+ return (SRC_ERR_NOT_BUSY_TIMEOUT);
+
+ pci_outl(reg(CONC_dSRCIO_OFF), 0UL);
+
+ return 0;
+}
+
+
+int SRCRegRead(DEV_STRUCT * DSP, u16_t reg, u16_t *data)
+{
+ u32_t dtemp;
+
+ /* wait for ready */
+ if (WaitBitd (reg(CONC_dSRCIO_OFF), SRC_BUSY_BIT, 0, 1000))
+ return (SRC_ERR_NOT_BUSY_TIMEOUT);
+
+ dtemp = pci_inl(reg(CONC_dSRCIO_OFF));
+
+ /* assert a read request */
+ pci_outl(reg(CONC_dSRCIO_OFF),
+ (dtemp & SRC_CTLMASK) | ((u32_t) reg << 25));
+
+ /* now wait for the data */
+ if (WaitBitd (reg(CONC_dSRCIO_OFF), SRC_BUSY_BIT, 0, 1000))
+ return (SRC_ERR_NOT_BUSY_TIMEOUT);
+
+ if (NULL != data)
+ *data = (u16_t) pci_inl(reg(CONC_dSRCIO_OFF));
+
+ return 0;
+}
+
+
+int SRCRegWrite(DEV_STRUCT * DSP, u16_t reg, u16_t val)
+{
+ u32_t dtemp;
+
+
+ /* wait for ready */
+ if (WaitBitd (reg(CONC_dSRCIO_OFF), SRC_BUSY_BIT, 0, 1000))
+ return (SRC_ERR_NOT_BUSY_TIMEOUT);
+
+ dtemp = pci_inl(reg(CONC_dSRCIO_OFF));
+
+ /* assert the write request */
+ pci_outl(reg(CONC_dSRCIO_OFF),
+ (dtemp & SRC_CTLMASK) | SRC_WENABLE | ((u32_t) reg << 25) | val);
+
+ return 0;
+}
+
+
+void SRCSetRate(DEV_STRUCT * DSP, char base, u16_t rate)
+{
+ u32_t freq, dtemp, i;
+ u16_t N, truncM, truncStart, wtemp;
+
+
+ if( base != SRC_ADC_BASE )
+ {
+ /* freeze the channel */
+ dtemp = base == SRC_SYNTH_BASE ? SRC_SYNTHFREEZE : SRC_DACFREEZE;
+ for( i = 0; i < SRC_IOPOLL_COUNT; ++i )
+ if( !(pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_BUSY) )
+ break;
+ pci_outl(reg(CONC_dSRCIO_OFF),
+ (pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_CTLMASK) |
+ dtemp);
+
+ /* calculate new frequency and write it - preserve accum */
+ /* please don't try to understand. */
+ freq = ((u32_t) rate << 16) / 3000U;
+ SRCRegRead(DSP, base + SRC_INT_REGS_OFF, &wtemp);
+
+ SRCRegWrite(DSP, base + SRC_INT_REGS_OFF,
+ (wtemp & 0x00ffU) |
+ (u16_t) (freq >> 6) & 0xfc00);
+
+ SRCRegWrite(DSP, base + SRC_VFREQ_FRAC_OFF, (u16_t) freq >> 1);
+
+ /* un-freeze the channel */
+ dtemp = base == SRC_SYNTH_BASE ? SRC_SYNTHFREEZE : SRC_DACFREEZE;
+ for( i = 0; i < SRC_IOPOLL_COUNT; ++i )
+ if( !(pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_BUSY) )
+ break;
+ pci_outl(reg(CONC_dSRCIO_OFF),
+ (pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_CTLMASK) &
+ ~dtemp);
+ }
+ else /* setting ADC rate */
+ {
+ /* freeze the channel */
+ for( i = 0; i < SRC_IOPOLL_COUNT; ++i )
+ if( !(pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_BUSY) )
+ break;
+ pci_outl(reg(CONC_dSRCIO_OFF),
+ (pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_CTLMASK) |
+ SRC_ADCFREEZE);
+
+
+
+ /* derive oversample ratio */
+ N = rate/3000U;
+ if( N == 15 || N == 13 || N == 11 || N == 9 )
+ --N;
+ SRCRegWrite(DSP, SRC_ADC_LVOL, N << 8);
+ SRCRegWrite(DSP, SRC_ADC_RVOL, N << 8);
+
+ /* truncate the filter and write n/trunc_start */
+ truncM = (21*N - 1) | 1;
+ if( rate >= 24000U )
+ {
+ if( truncM > 239 )
+ truncM = 239;
+ truncStart = (239 - truncM) >> 1;
+ SRCRegWrite(DSP, base + SRC_TRUNC_N_OFF,
+ (truncStart << 9) | (N << 4));
+ }
+ else
+ {
+ if( truncM > 119 )
+ truncM = 119;
+ truncStart = (119 - truncM) >> 1;
+ SRCRegWrite(DSP, base + SRC_TRUNC_N_OFF,
+ 0x8000U | (truncStart << 9) | (N << 4));
+ }
+
+ /* calculate new frequency and write it - preserve accum */
+ freq = ((48000UL << 16) / rate) * N;
+ SRCRegRead(DSP, base + SRC_INT_REGS_OFF, &wtemp);
+ SRCRegWrite(DSP, base + SRC_INT_REGS_OFF,
+ (wtemp & 0x00ffU) |
+ (u16_t) (freq >> 6) & 0xfc00);
+ SRCRegWrite(DSP, base + SRC_VFREQ_FRAC_OFF, (u16_t) freq >> 1);
+
+ /* un-freeze the channel */
+ for( i = 0; i < SRC_IOPOLL_COUNT; ++i )
+ if( !(pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_BUSY) )
+ break;
+ pci_outl(reg(CONC_dSRCIO_OFF),
+ (pci_inl(reg(CONC_dSRCIO_OFF)) & SRC_CTLMASK) &
+ ~SRC_ADCFREEZE);
+ }
+ return;
+}
+
+
--- /dev/null
+#ifndef SRC_H
+#define SRC_H
+
+#include "es1371.h"
+#include "wait.h"
+
+_PROTOTYPE( int SRCInit, (DEV_STRUCT * DSP) );
+_PROTOTYPE( int SRCRegRead, (DEV_STRUCT * DSP, u16_t reg, u16_t *data) );
+_PROTOTYPE( int SRCRegWrite, (DEV_STRUCT * DSP, u16_t reg, u16_t val) );
+_PROTOTYPE( void SRCSetRate, (DEV_STRUCT * DSP, char src_base, u16_t rate) );
+
+
+/* register/base and control equates for the SRC RAM */
+#define SRC_SYNTH_FIFO 0x00
+#define SRC_DAC_FIFO 0x20
+#define SRC_ADC_FIFO 0x40
+#define SRC_SYNTH_BASE 0x70
+#define SRC_DAC_BASE 0x74
+#define SRC_ADC_BASE 0x78
+#define SRC_SYNTH_LVOL 0x7c
+#define SRC_SYNTH_RVOL 0x7d
+#define SRC_DAC_LVOL 0x7e
+#define SRC_DAC_RVOL 0x7f
+#define SRC_ADC_LVOL 0x6c
+#define SRC_ADC_RVOL 0x6d
+
+#define SRC_TRUNC_N_OFF 0x00
+#define SRC_INT_REGS_OFF 0x01
+#define SRC_ACCUM_FRAC_OFF 0x02
+#define SRC_VFREQ_FRAC_OFF 0x03
+
+/* miscellaneous control defines */
+#define SRC_IOPOLL_COUNT 0x1000UL
+#define SRC_WENABLE (1UL << 24)
+#define SRC_BUSY_BIT 23
+#define SRC_BUSY (1UL << SRC_BUSY_BIT)
+#define SRC_DISABLE (1UL << 22)
+#define SRC_SYNTHFREEZE (1UL << 21)
+#define SRC_DACFREEZE (1UL << 20)
+#define SRC_ADCFREEZE (1UL << 19)
+#define SRC_CTLMASK 0x00780000UL
+
+#endif /* SRC_H */
--- /dev/null
+#include "codec.h"
+
+
+/* Timeouts in milliseconds */
+#define WIP_TIMEOUT 250UL
+#define DRDY_TIMEOUT 250UL
+
+/* The default SRC syncronization state number is 1. This state occurs
+ just after de-assertion of SYNC. This is supposed to be the safest
+ state for accessing the codec with an ES1371 Rev 1. Later versions
+ of the chip allegedly don't require syncronization. Be very careful
+ if you change this ! */
+
+#define SRC_UNSYNCED 0xffffffffUL
+static u32_t SrcSyncState = 0x00010000UL;
+
+
+void CodecSetSrcSyncState (int state)
+{
+ if (state < 0)
+ SrcSyncState = SRC_UNSYNCED;
+ else {
+ SrcSyncState = (u32_t)state << 16;
+ SrcSyncState &= 0x00070000Ul;
+ }
+}
+
+
+int CodecWrite (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData)
+{
+u32_t dtemp, i;
+u16_t wBaseAddr = pCC->base;
+
+ /* wait for WIP bit (Write In Progress) to go away */
+ /* remember, register CONC_dCODECCTL_OFF (0x14)
+ is a pseudo read-write register */
+ if (WaitBitd (wBaseAddr + CONC_dCODECCTL_OFF, 30, 0, WIP_TIMEOUT)){
+ printf("CODEC_ERR_WIP_TIMEOUT\n");
+ return (CODEC_ERR_WIP_TIMEOUT);
+ }
+ if (SRC_UNSYNCED != SrcSyncState)
+ {
+ /* enable SRC state data in SRC mux */
+ if (WaitBitd (wBaseAddr + CONC_dSRCIO_OFF, SRC_BUSY_BIT, 0, 1000))
+ return (CODEC_ERR_SRC_NOT_BUSY_TIMEOUT);
+
+ /* todo: why are we writing an undefined register? */
+ dtemp = pci_inl(wBaseAddr + CONC_dSRCIO_OFF);
+ pci_outl(wBaseAddr + CONC_dSRCIO_OFF, (dtemp & SRC_CTLMASK) |
+ 0x00010000UL);
+
+ /* wait for a SAFE time to write addr/data and then do it */
+ /*_disable(); */
+ for( i = 0; i < 0x1000UL; ++i )
+ if( (pci_inl(wBaseAddr + CONC_dSRCIO_OFF) & 0x00070000UL) ==
+ SrcSyncState )
+ break;
+
+ if (i >= 0x1000UL) {
+ /* _enable(); */
+ return (CODEC_ERR_SRC_SYNC_TIMEOUT);
+ }
+ }
+
+ /* A test for 5880 - prime the PCI data bus */
+ {
+ u32_t dat = ((u32_t) wAddr << 16) | wData;
+ char page = pci_inb(wBaseAddr + CONC_bMEMPAGE_OFF);
+
+ pci_outl (wBaseAddr + CONC_bMEMPAGE_OFF, dat);
+
+ /* write addr and data */
+ pci_outl(wBaseAddr + CONC_dCODECCTL_OFF, dat);
+
+ pci_outb(wBaseAddr + CONC_bMEMPAGE_OFF, page); /* restore page reg */
+ }
+
+ if (SRC_UNSYNCED != SrcSyncState)
+ {
+ /* _enable(); */
+
+ /* restore SRC reg */
+ if (WaitBitd (wBaseAddr + CONC_dSRCIO_OFF, SRC_BUSY_BIT, 0, 1000))
+ return (CODEC_ERR_SRC_NOT_BUSY_TIMEOUT);
+
+ pci_outl(wBaseAddr + CONC_dSRCIO_OFF, dtemp & 0xfff8ffffUL);
+ }
+
+ return 0;
+}
+
+int CodecRead (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data)
+{
+u32_t dtemp, i;
+u16_t base = pCC->base;
+
+ /* wait for WIP to go away */
+ if (WaitBitd (base + CONC_dCODECCTL_OFF, 30, 0, WIP_TIMEOUT))
+ return (CODEC_ERR_WIP_TIMEOUT);
+
+ if (SRC_UNSYNCED != SrcSyncState)
+ {
+ /* enable SRC state data in SRC mux */
+ if (WaitBitd (base + CONC_dSRCIO_OFF, SRC_BUSY_BIT, 0, 1000))
+ return (CODEC_ERR_SRC_NOT_BUSY_TIMEOUT);
+
+ dtemp = pci_inl(base + CONC_dSRCIO_OFF);
+ pci_outl(base + CONC_dSRCIO_OFF, (dtemp & SRC_CTLMASK) |
+ 0x00010000UL);
+
+ /* wait for a SAFE time to write a read request and then do it */
+ /* todo: how do we solve the lock() problem? */
+ /* _disable(); */
+ for( i = 0; i < 0x1000UL; ++i )
+ if( (pci_inl(base + CONC_dSRCIO_OFF) & 0x00070000UL) ==
+ SrcSyncState )
+ break;
+
+ if (i >= 0x1000UL) {
+ /*_enable();*/
+ return (CODEC_ERR_SRC_SYNC_TIMEOUT);
+ }
+ }
+
+ /* A test for 5880 - prime the PCI data bus */
+ {
+ /* set bit 23, this means read in stead of write. */
+ u32_t dat = ((u32_t) wAddr << 16) | (1UL << 23);
+ char page = pci_inb(base + CONC_bMEMPAGE_OFF);
+
+ /* todo: why are we putting data in the mem page register??? */
+ pci_outl(base + CONC_bMEMPAGE_OFF, dat);
+
+ /* write addr w/data=0 and assert read request */
+ pci_outl(base + CONC_dCODECCTL_OFF, dat);
+
+ pci_outb(base + CONC_bMEMPAGE_OFF, page); /* restore page reg */
+
+ }
+ if (SRC_UNSYNCED != SrcSyncState)
+ {
+
+ /*_enable();*/
+
+ /* restore SRC reg */
+ if (WaitBitd (base + CONC_dSRCIO_OFF, SRC_BUSY_BIT, 0, 1000))
+ return (CODEC_ERR_SRC_NOT_BUSY_TIMEOUT);
+
+ pci_outl(base + CONC_dSRCIO_OFF, dtemp & 0xfff8ffffUL);
+ }
+
+ /* now wait for the stinkin' data (DRDY = data ready) */
+ if (WaitBitd (base + CONC_dCODECCTL_OFF, 31, 1, DRDY_TIMEOUT))
+ return (CODEC_ERR_DATA_TIMEOUT);
+
+ dtemp = pci_inl(base + CONC_dCODECCTL_OFF);
+
+ if (data)
+ *data = (u16_t) dtemp;
+
+ return 0;
+}
+
+
+int CodecWriteUnsynced (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData)
+{
+ /* wait for WIP to go away */
+ if (WaitBitd (pCC->base + CONC_dCODECCTL_OFF, 30, 0, WIP_TIMEOUT))
+ return (CODEC_ERR_WIP_TIMEOUT);
+
+ /* write addr and data */
+ pci_outl(pCC->base + CONC_dCODECCTL_OFF, ((u32_t) wAddr << 16) | wData);
+ return 0;
+}
+
+
+int CodecReadUnsynced (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data)
+{
+u32_t dtemp;
+
+ /* wait for WIP to go away */
+ if (WaitBitd (pCC->base + CONC_dCODECCTL_OFF, 30, 0, WIP_TIMEOUT))
+ return (CODEC_ERR_WIP_TIMEOUT);
+
+ /* write addr w/data=0 and assert read request */
+ pci_outl(pCC->base + CONC_dCODECCTL_OFF, ((u32_t) wAddr << 16) | (1UL << 23));
+
+ /* now wait for the stinkin' data (RDY) */
+ if (WaitBitd (pCC->base + CONC_dCODECCTL_OFF, 31, 1, DRDY_TIMEOUT))
+ return (CODEC_ERR_DATA_TIMEOUT);
+
+ dtemp = pci_inl(pCC->base + CONC_dCODECCTL_OFF);
+
+ if (data)
+ *data = (u16_t) dtemp;
+
+ return 0;
+}
+
+int CODECInit( DEV_STRUCT * pCC )
+{
+int retVal;
+ /* All powerdown modes: off */
+
+ retVal = CodecWrite (pCC, AC97_POWERDOWN_CONTROL_STAT, 0x0000U);
+ if (OK != retVal)
+ return (retVal);
+
+ /* Mute Line Out & set to 0dB attenuation */
+
+ retVal = CodecWrite (pCC, AC97_MASTER_VOLUME, 0x0000U);
+ if (OK != retVal)
+ return (retVal);
+
+
+ retVal = CodecWrite (pCC, AC97_MONO_VOLUME, 0x8000U);
+ if (OK != retVal)
+ return (retVal);
+
+ retVal = CodecWrite (pCC, AC97_PHONE_VOLUME, 0x8008U);
+ if (OK != retVal)
+ return (retVal);
+
+ retVal = CodecWrite (pCC, AC97_MIC_VOLUME, 0x0008U);
+ if (OK != retVal)
+ return (retVal);
+
+ retVal = CodecWrite (pCC, AC97_LINE_IN_VOLUME, 0x0808U);
+ if (OK != retVal)
+ return (retVal);
+
+ retVal = CodecWrite (pCC, AC97_CD_VOLUME, 0x0808U);
+ if (OK != retVal)
+ return (retVal);
+
+ retVal = CodecWrite (pCC, AC97_AUX_IN_VOLUME, 0x0808U);
+ if (OK != retVal)
+ return (retVal);
+
+ retVal = CodecWrite (pCC, AC97_PCM_OUT_VOLUME, 0x0808U);
+ if (OK != retVal)
+ return (retVal);
+
+ retVal = CodecWrite (pCC, AC97_RECORD_GAIN_VOLUME, 0x0000U);
+ if (OK != retVal)
+ return (retVal);
+
+ /* Connect Line In to ADC */
+ retVal = CodecWrite (pCC, AC97_RECORD_SELECT, 0x0404U);
+ if (OK != retVal)
+ return (retVal);
+
+ retVal = CodecWrite (pCC, AC97_GENERAL_PURPOSE, 0x0000U);
+ if (OK != retVal)
+ return (retVal);
+
+ return OK;
+}
+
+
+
+
+
+
--- /dev/null
+#ifndef CODEC_H
+#define CODEC_H
+
+#include "es1371.h"
+#include "wait.h"
+#include "../AC97.h"
+#include "SRC.h"
+
+#define CODEC_0DB_GAIN 0x0008
+#define CODEC_MAX_ATTN 0x003f
+#define CODEC_MUTE 0x8000U
+
+
+/* Control function defines */
+#define CODEC_CTL_4SPKR 0x00U /* 4-spkr output mode enable */
+#define CODEC_CTL_MICBOOST 0x01U /* Mic boost (+30 dB) enable */
+#define CODEC_CTL_PWRDOWN 0x02U /* power-down mode */
+#define CODEC_CTL_DOSMODE 0x03U /* A/D sync to DAC1 */
+
+ /* Timeout waiting for: */
+#define CODEC_ERR_WIP_TIMEOUT -1 /* write in progress complete */
+#define CODEC_ERR_DATA_TIMEOUT -2 /* data ready */
+#define CODEC_ERR_SRC_NOT_BUSY_TIMEOUT -3 /* SRC not busy */
+#define CODEC_ERR_SRC_SYNC_TIMEOUT -4 /* state #1 */
+
+/* Function to inform CODEC module which AC97 vendor ID to expect */
+void CodecSetVendorId (char *tbuf);
+
+/* CODEC Mixer and Mode control function prototypes */
+
+int CodecRead (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data);
+int CodecWrite (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData);
+void CodecSetSrcSyncState (int state);
+int CodecWriteUnsynced (DEV_STRUCT * pCC, u16_t wAddr, u16_t wData);
+int CodecReadUnsynced (DEV_STRUCT * pCC, u16_t wAddr, u16_t *data);
+
+/*
+ This function initializes the CODEC to a default mode.
+*/
+int CODECInit( DEV_STRUCT * pCC );
+
+
+
+#endif /* CODEC_H */
--- /dev/null
+/* This is the main file of the ES1371 sound driver
+ * There is no main function over here, instead the main function
+ * is located in the generic dma driver. All this driver does is
+ * implement the interface audio/audio_fw.h. All functions having the
+ * prefix 'drv_' are dictated by audio/audio_fw.h. The function
+ * prototypes you see below define a set of private helper functions.
+ * Control over the sample rate converter and the codec is delegated
+ * to SRC.c and codec.c respectively.
+ *
+ * November 2005 ES1371 driver (Laurens Bronwasser)
+ */
+
+
+#include "../framework/audio_fw.h"
+#include "es1371.h"
+#include "codec.h"
+#include "SRC.h"
+#include "../AC97.h"
+
+
+#define reg(n) dev.base + n
+
+FORWARD _PROTOTYPE( int detect_hw, (void) );
+FORWARD _PROTOTYPE( int disable_int, (int sub_dev) );
+FORWARD _PROTOTYPE( int set_stereo, (u32_t stereo, int sub_dev) );
+FORWARD _PROTOTYPE( int set_bits, (u32_t nr_of_bits, int sub_dev) );
+FORWARD _PROTOTYPE( int set_sample_rate, (u32_t rate, int sub_dev) );
+FORWARD _PROTOTYPE( int set_sign, (u32_t val, int sub_dev) );
+FORWARD _PROTOTYPE( int get_max_frag_size, (u32_t * val, int *len, int sub_dev) );
+FORWARD _PROTOTYPE( int set_frag_size, (u32_t fragment_size, int sub_dev) );
+FORWARD _PROTOTYPE( int set_int_cnt, (int sub_dev) );
+FORWARD _PROTOTYPE( int AC97Write, (u16_t wAddr, u16_t wData));
+FORWARD _PROTOTYPE( int AC97Read, (u16_t wAddr, u16_t *data));
+FORWARD _PROTOTYPE( void set_nice_volume, (void) );
+
+DEV_STRUCT dev;
+u32_t base = 0;
+aud_sub_dev_conf_t aud_conf[4];
+
+
+PUBLIC sub_dev_t sub_dev[4];
+PUBLIC special_file_t special_file[4];
+PUBLIC drv_t drv;
+
+
+PUBLIC int drv_init(void) {
+ drv.DriverName = "ES1371";
+ drv.NrOfSubDevices = 4;
+ drv.NrOfSpecialFiles = 4;
+
+ sub_dev[DAC1_CHAN].readable = 0;
+ sub_dev[DAC1_CHAN].writable = 1;
+ sub_dev[DAC1_CHAN].DmaSize = 64 * 1024;
+ sub_dev[DAC1_CHAN].NrOfDmaFragments = 2;
+ sub_dev[DAC1_CHAN].MinFragmentSize = 1024;
+ sub_dev[DAC1_CHAN].NrOfExtraBuffers = 4;
+
+ sub_dev[ADC1_CHAN].readable = 1;
+ sub_dev[ADC1_CHAN].writable = 0;
+ sub_dev[ADC1_CHAN].DmaSize = 64 * 1024;
+ sub_dev[ADC1_CHAN].NrOfDmaFragments = 2;
+ sub_dev[ADC1_CHAN].MinFragmentSize = 1024;
+ sub_dev[ADC1_CHAN].NrOfExtraBuffers = 4;
+
+ sub_dev[MIXER].writable = 0;
+ sub_dev[MIXER].readable = 0;
+
+ sub_dev[DAC2_CHAN].readable = 0;
+ sub_dev[DAC2_CHAN].writable = 1;
+ sub_dev[DAC2_CHAN].DmaSize = 64 * 1024;
+ sub_dev[DAC2_CHAN].NrOfDmaFragments = 2;
+ sub_dev[DAC2_CHAN].MinFragmentSize = 1024;
+ sub_dev[DAC2_CHAN].NrOfExtraBuffers = 4;
+
+ special_file[0].minor_dev_nr = 0;
+ special_file[0].write_chan = DAC1_CHAN;
+ special_file[0].read_chan = NO_CHANNEL;
+ special_file[0].io_ctl = DAC1_CHAN;
+
+ special_file[1].minor_dev_nr = 1;
+ special_file[1].write_chan = NO_CHANNEL;
+ special_file[1].read_chan = ADC1_CHAN;
+ special_file[1].io_ctl = ADC1_CHAN;
+
+ special_file[2].minor_dev_nr = 2;
+ special_file[2].write_chan = NO_CHANNEL;
+ special_file[2].read_chan = NO_CHANNEL;
+ special_file[2].io_ctl = MIXER;
+
+ special_file[3].minor_dev_nr = 3;
+ special_file[3].write_chan = DAC2_CHAN;
+ special_file[3].read_chan = NO_CHANNEL;
+ special_file[3].io_ctl = DAC2_CHAN;
+}
+
+int drv_init_hw (void)
+{
+ u16_t i, j;
+
+ /* First, detect the hardware */
+ if (detect_hw() != OK) {
+ return EIO;
+ }
+ /*
+ Put HW in a nice state ... all devices enabled except joystick,
+ NMI enables off, clear pending NMIs if any */
+
+ /* PCI command register */
+ pci_attr_w16 (dev.devind, PCI_CR, 0x0105);
+ /* set power management control/status register */
+ pci_attr_w16 (dev.devind, 0xE0, 0x0000);
+
+ pci_outb(reg(CONC_bDEVCTL_OFF), 0x00);
+ pci_outb(reg(CONC_bMISCCTL_OFF), 0x00);
+ pci_outb(reg(CONC_b4SPKR_OFF), 0x00);
+ pci_outb(reg(CONC_bNMIENA_OFF), 0x00);
+ pci_outb(reg(CONC_bNMICTL_OFF), 0x08);
+ pci_outw(reg(CONC_wNMISTAT_OFF), 0x0000);
+ pci_outb(reg(CONC_bSERCTL_OFF), 0x00);
+
+ /* clear all cache RAM */
+ for( i = 0; i < 0x10; ++i )
+ {
+ pci_outb(reg(CONC_bMEMPAGE_OFF), i);
+ for( j = 0; j < 0x10; j += 4 )
+ pci_outl (reg(CONC_MEMBASE_OFF) + j, 0UL);
+ }
+ /* DO NOT SWITCH THE ORDER OF SRCInit and CODECInit function calls!!! */
+ /* The effect is only noticable after a cold reset (reboot) */
+ if (SRCInit(&dev) != OK) {
+ return EIO;
+ }
+ if (CODECInit(&dev) != OK) {
+ return EIO;
+ }
+ set_nice_volume(); /* of course we need a nice mixer to do this */
+
+ /* initialize variables for each sub_device */
+ for (i = 0; i < drv.NrOfSubDevices; i++) {
+ if(i != MIXER) {
+ aud_conf[i].busy = 0;
+ aud_conf[i].stereo = DEFAULT_STEREO;
+ aud_conf[i].sample_rate = DEFAULT_RATE;
+ aud_conf[i].nr_of_bits = DEFAULT_NR_OF_BITS;
+ aud_conf[i].sign = DEFAULT_SIGNED;
+ aud_conf[i].fragment_size = sub_dev[i].DmaSize / sub_dev[i].NrOfDmaFragments;
+ }
+ }
+ return OK;
+}
+
+
+PRIVATE int detect_hw(void) {
+
+ u32_t r;
+ int devind;
+ u16_t v_id, d_id;
+
+ /* detect_hw tries to find device and get IRQ and base address
+ with a little (much) help from the PCI library.
+ This code is quite device independent and you can copy it.
+ (just make sure to get the bugs out first)*/
+
+ pci_init();
+ /* get first device and then search through the list */
+ r = pci_first_dev(&devind, &v_id, &d_id);
+ while( r > 0 ) {
+ /* if we have a match...break */
+ if (v_id == VENDOR_ID && d_id == DEVICE_ID) break;
+ r = pci_next_dev(&devind, &v_id, &d_id);
+ }
+
+ /* did we find anything? */
+ if (v_id != VENDOR_ID || d_id != DEVICE_ID) {
+ return EIO;
+ }
+ /* right here we should reserve the device, but the pci library
+ doesn't support global reservation of devices yet. This would
+ be a problem if more ES1371's were installed on this system. */
+
+ dev.name = pci_dev_name(v_id, d_id);
+ /* get base address of our device, ignore least signif. bit
+ this last bit thing could be device dependent, i don't know */
+ dev.base = pci_attr_r32(devind, PCI_BAR) & 0xfffffffe;
+ /* get IRQ */
+ dev.irq = pci_attr_r8(devind, PCI_ILR);
+ dev.revision = pci_attr_r8(devind, 0x08);
+ dev.d_id = d_id;
+ dev.v_id = v_id;
+ dev.devind = devind; /* pci device identifier */
+ return OK;
+}
+
+
+int drv_reset(void)
+{
+ /* make a WARM reset */
+ u16_t i;
+
+ /* set SYNC_RES bit */
+ pci_outl(reg(CONC_bDEVCTL_OFF),
+ pci_inl(reg(CONC_bDEVCTL_OFF)) | SYNC_RES_BIT);
+
+ /* got to delay at least 1 usec, try 18 usec */
+ for (i=0; i<100; i++) {
+ pci_inb(reg(0));
+ }
+ /* clear SYNC_RES bit */
+ pci_outl(reg(CONC_bDEVCTL_OFF),
+ pci_inl(reg(CONC_bDEVCTL_OFF)) & ~SYNC_RES_BIT);
+ return OK;
+}
+
+
+int drv_start(int sub_dev, int DmaMode)
+{
+ u32_t enable_bit, result = 0;
+
+ /* Write default values to device in case user failed to configure.
+ If user did configure properly, everything is written twice.
+ please raise your hand if you object against to this strategy...*/
+ result |= set_sample_rate(aud_conf[sub_dev].sample_rate, sub_dev);
+ result |= set_stereo(aud_conf[sub_dev].stereo, sub_dev);
+ result |= set_bits(aud_conf[sub_dev].nr_of_bits, sub_dev);
+ result |= set_sign(aud_conf[sub_dev].sign, sub_dev);
+
+ /* set the interrupt count */
+ result |= set_int_cnt(sub_dev);
+
+ if (result) {
+ return EIO;
+ }
+
+ /* if device currently paused, resume */
+ drv_resume(sub_dev);
+
+ switch(sub_dev) {
+ case ADC1_CHAN: enable_bit = ADC1_EN_BIT;break;
+ case DAC1_CHAN: enable_bit = DAC1_EN_BIT;break;
+ case DAC2_CHAN: enable_bit = DAC2_EN_BIT;break;
+ default: return EINVAL;
+ }
+ /* enable interrupts from 'sub device' */
+ drv_reenable_int(sub_dev);
+
+ /* this means GO!!! */
+ pci_outl(reg(CONC_bDEVCTL_OFF),
+ pci_inl(reg(CONC_bDEVCTL_OFF)) | enable_bit);
+
+ aud_conf[sub_dev].busy = 1;
+ return OK;
+}
+
+
+int drv_stop(int sub_dev)
+{
+ u32_t enable_bit;
+
+ switch(sub_dev) {
+ case ADC1_CHAN: enable_bit = ADC1_EN_BIT;break;
+ case DAC1_CHAN: enable_bit = DAC1_EN_BIT;break;
+ case DAC2_CHAN: enable_bit = DAC2_EN_BIT;break;
+ default: return EINVAL;
+ }
+ /* stop the codec */
+ pci_outl(reg(CONC_bDEVCTL_OFF),
+ pci_inl(reg(CONC_bDEVCTL_OFF)) & ~enable_bit);
+
+ aud_conf[sub_dev].busy = 0;
+ disable_int(sub_dev);
+ return OK;
+}
+
+
+/* all IO-ctl's sent to the upper driver are passed to this function */
+int drv_io_ctl(int request, void * val, int * len, int sub_dev) {
+
+ int status;
+
+ switch(request) {
+ case DSPIORATE: status = set_sample_rate(*((u32_t *) val), sub_dev); break;
+ case DSPIOSTEREO: status = set_stereo(*((u32_t *) val), sub_dev); break;
+ case DSPIOBITS: status = set_bits(*((u32_t *) val), sub_dev); break;
+ case DSPIOSIZE: status = set_frag_size(*((u32_t *) val), sub_dev); break;
+ case DSPIOSIGN: status = set_sign(*((u32_t *) val), sub_dev); break;
+ case DSPIOMAX: status = get_max_frag_size(val, len, sub_dev);break;
+ case DSPIORESET: status = drv_reset(); break;
+ case AC97READ: status = AC97Read (*((u16_t *)val), ((u16_t *) val+2));break;
+ case AC97WRITE: status = AC97Write(*((u16_t *)val), *((u16_t *) val+2));break;
+ default: status = EINVAL; break;
+ }
+
+ return OK;
+}
+
+
+int drv_get_irq(char *irq) {
+ *irq = dev.irq;
+ return OK;
+}
+
+
+int drv_get_frag_size(u32_t *frag_size, int sub_dev) {
+ *frag_size = aud_conf[sub_dev].fragment_size;
+ return OK;
+}
+
+
+int drv_set_dma(u32_t dma, u32_t length, int chan) {
+ /* dma length in bytes,
+ max is 64k long words for es1371 = 256k bytes */
+ u32_t page, frame_count_reg, dma_add_reg;
+
+ switch(chan) {
+ case ADC1_CHAN: page = CONC_ADCCTL_PAGE;
+ frame_count_reg = CONC_wADCFC_OFF;
+ dma_add_reg = CONC_dADCPADDR_OFF;
+ break;
+ case DAC1_CHAN: page = CONC_SYNCTL_PAGE;
+ frame_count_reg = CONC_wSYNFC_OFF;
+ dma_add_reg = CONC_dSYNPADDR_OFF;
+ break;;
+ case DAC2_CHAN: page = CONC_DACCTL_PAGE;
+ frame_count_reg = CONC_wDACFC_OFF;
+ dma_add_reg = CONC_dDACPADDR_OFF;
+ break;;
+ default: return EIO;
+ }
+ pci_outb(reg(CONC_bMEMPAGE_OFF), page);
+ pci_outl(reg(dma_add_reg), dma);
+ /* device expects long word count in stead of bytes */
+ length /= 4;
+ /* device expects length -1 */
+ pci_outl(reg(frame_count_reg), (u32_t) (length - 1));
+}
+
+
+/* return status of the interrupt summary bit */
+int drv_int_sum(void) {
+ u32_t int_status;
+ int_status = pci_inl(reg(CONC_bINTSTAT_OFF)) & 0x80000000UL;
+ return int_status;
+}
+
+
+int drv_int(int sub_dev) {
+ u32_t int_status;
+ char bit;
+
+ /* return status of interrupt bit of specified channel*/
+
+ switch (sub_dev) {
+ case DAC1_CHAN: bit = DAC1_INT_STATUS_BIT;break;
+ case DAC2_CHAN: bit = DAC2_INT_STATUS_BIT;break;
+ case ADC1_CHAN: bit = ADC1_INT_STATUS_BIT;break;
+ }
+ int_status = pci_inl(reg(CONC_bINTSTAT_OFF)) & bit;
+ return int_status;
+}
+
+
+int drv_reenable_int(int chan) {
+ u32_t i, int_en_bit;
+
+ switch(chan) {
+ case ADC1_CHAN: int_en_bit = ADC1_INT_EN_BIT;break;
+ case DAC1_CHAN: int_en_bit = DAC1_INT_EN_BIT;break;
+ case DAC2_CHAN: int_en_bit = DAC2_INT_EN_BIT;break;
+ default: EINVAL;
+ }
+ /* clear and reenable an interrupt */
+ i = pci_inl(reg(CONC_bSERFMT_OFF));
+ pci_outl(reg(CONC_bSERFMT_OFF), i & ~int_en_bit);
+ pci_outl(reg(CONC_bSERFMT_OFF), i | int_en_bit);
+}
+
+
+int drv_pause(int sub_dev)
+{
+ u32_t pause_bit;
+
+ disable_int(sub_dev); /* don't send interrupts */
+
+ switch(sub_dev) {
+ case DAC1_CHAN: pause_bit = DAC1_PAUSE_BIT;break;
+ case DAC2_CHAN: pause_bit = DAC2_PAUSE_BIT;break;
+ default: return EINVAL;
+ }
+ /* pause */
+ pci_outl(reg(CONC_bSERFMT_OFF),
+ pci_inl(reg(CONC_bSERFMT_OFF)) | pause_bit);
+ return OK;
+}
+
+
+int drv_resume(int sub_dev)
+{
+ u32_t pause_bit = 0;
+
+ /* todo: drv_reenable_int(sub_dev); *//* enable interrupts */
+
+ switch(sub_dev) {
+ case DAC1_CHAN: pause_bit = DAC1_PAUSE_BIT;break;
+ case DAC2_CHAN: pause_bit = DAC2_PAUSE_BIT;break;
+ default: return EINVAL;
+ }
+ /* clear pause bit */
+ pci_outl(reg(CONC_bSERFMT_OFF),
+ pci_inl(reg(CONC_bSERFMT_OFF)) & ~pause_bit);
+ return OK;
+}
+
+
+PRIVATE int set_bits(u32_t nr_of_bits, int sub_dev) {
+
+ /* set format bits for specified channel. */
+ u32_t size_16_bit, i;
+
+ switch(sub_dev) {
+ case ADC1_CHAN: size_16_bit = ADC1_16_8_BIT;break;
+ case DAC1_CHAN: size_16_bit = DAC1_16_8_BIT;break;
+ case DAC2_CHAN: size_16_bit = DAC2_16_8_BIT;break;
+ default: return EINVAL;
+ }
+ i = pci_inb(reg(CONC_bSERFMT_OFF));
+ i &= ~size_16_bit;
+ switch(nr_of_bits) {
+ case 16: i |= size_16_bit;break;
+ case 8: break;
+ default: return EINVAL;
+ }
+ pci_outb(reg(CONC_bSERFMT_OFF), i);
+ aud_conf[sub_dev].nr_of_bits = nr_of_bits;
+ return OK;
+}
+
+
+PRIVATE int set_stereo(u32_t stereo, int sub_dev) {
+
+ /* set format bits for specified channel. */
+ u32_t stereo_bit, i;
+ switch(sub_dev) {
+ case ADC1_CHAN: stereo_bit = ADC1_STEREO_BIT;break;
+ case DAC1_CHAN: stereo_bit = DAC1_STEREO_BIT;break;
+ case DAC2_CHAN: stereo_bit = DAC2_STEREO_BIT;break;
+ default: return EINVAL;
+ }
+ i = pci_inb(reg(CONC_bSERFMT_OFF));
+ i &= ~stereo_bit;
+ if( stereo == TRUE ) {
+ i |= stereo_bit;
+ }
+ pci_outb(reg(CONC_bSERFMT_OFF), i);
+ aud_conf[sub_dev].stereo = stereo;
+ return OK;
+}
+
+
+PRIVATE int set_sign(u32_t val, int sub_dev) {
+ return OK;
+}
+
+
+PRIVATE int set_frag_size(u32_t fragment_size, int sub_dev_nr) {
+ if (fragment_size > (sub_dev[sub_dev_nr].DmaSize / sub_dev[sub_dev_nr].NrOfDmaFragments) || fragment_size < sub_dev[sub_dev_nr].MinFragmentSize) {
+ return EINVAL;
+ }
+ aud_conf[sub_dev_nr].fragment_size = fragment_size;
+ return OK;
+}
+
+
+PRIVATE int set_sample_rate(u32_t rate, int sub_dev) {
+ u32_t SRCBaseReg;
+
+ if (rate > MAX_RATE || rate < MIN_RATE) {
+ return EINVAL;
+ }
+ /* set the sample rate for the specified channel*/
+ switch(sub_dev) {
+ case ADC1_CHAN: SRCBaseReg = SRC_ADC_BASE;break;
+ case DAC1_CHAN: SRCBaseReg = SRC_SYNTH_BASE;break;
+ case DAC2_CHAN: SRCBaseReg = SRC_DAC_BASE;break;
+ default: return EINVAL;
+ }
+ SRCSetRate(&dev, SRCBaseReg, rate);
+ aud_conf[sub_dev].sample_rate = rate;
+ return OK;
+}
+
+
+PRIVATE int set_int_cnt(int chan) {
+ /* Write interrupt count for specified channel.
+ After <DspFragmentSize> bytes, an interrupt will be generated */
+
+ int sample_count; u16_t int_cnt_reg;
+
+ if (aud_conf[chan].fragment_size > (sub_dev[chan].DmaSize / sub_dev[chan].NrOfDmaFragments)
+ || aud_conf[chan].fragment_size < sub_dev[chan].MinFragmentSize) {
+ return EINVAL;
+ }
+
+ switch(chan) {
+ case ADC1_CHAN: int_cnt_reg = CONC_wADCIC_OFF;break;
+ case DAC1_CHAN: int_cnt_reg = CONC_wSYNIC_OFF;break;
+ case DAC2_CHAN: int_cnt_reg = CONC_wDACIC_OFF;break;
+ default: return EINVAL;
+ }
+
+ sample_count = aud_conf[chan].fragment_size;
+
+ /* adjust sample count according to sample format */
+ if( aud_conf[chan].stereo == TRUE ) sample_count >>= 1;
+ switch(aud_conf[chan].nr_of_bits) {
+ case 16: sample_count >>= 1;break;
+ case 8: break;
+ default: return EINVAL;
+ }
+ /* set the sample count - 1 for the specified channel. */
+ pci_outw(reg(int_cnt_reg), sample_count - 1);
+ return OK;
+}
+
+
+PRIVATE int get_max_frag_size(u32_t * val, int * len, int sub_dev_nr) {
+ *len = sizeof(*val);
+ *val = (sub_dev[sub_dev_nr].DmaSize / sub_dev[sub_dev_nr].NrOfDmaFragments);
+ return OK;
+}
+
+
+PRIVATE int disable_int(int chan) {
+ u32_t i, int_en_bit;
+
+ switch(chan) {
+ case ADC1_CHAN: int_en_bit = ADC1_INT_EN_BIT;break;
+ case DAC1_CHAN: int_en_bit = DAC1_INT_EN_BIT;break;
+ case DAC2_CHAN: int_en_bit = DAC2_INT_EN_BIT;break;
+ default: EINVAL;
+ }
+ /* clear the interrupt */
+ i = pci_inl(reg(CONC_bSERFMT_OFF));
+ pci_outl(reg(CONC_bSERFMT_OFF), i & ~int_en_bit);
+}
+
+
+PRIVATE void set_nice_volume(void) {
+ /* goofy code to set the DAC1 channel to an audibe volume
+ to be able to test it without using the mixer */
+
+ AC97Write(AC97_PCM_OUT_VOLUME, 0x0808);/* the higher, the softer */
+ AC97Write(AC97_MASTER_VOLUME, 0x0101);
+ AC97Write(0x38, 0); /* not crucial */
+
+ AC97Write(AC97_LINE_IN_VOLUME, 0x0303);
+ AC97Write(AC97_MIC_VOLUME, 0x0303);
+
+ /* mute record gain */
+ AC97Write(AC97_RECORD_GAIN_VOLUME, 0xFFFF);
+
+ /* Also, to be able test recording without mixer:
+ select ONE channel as input below. */
+
+ /* select LINE IN */
+ /*CodecWrite(AC97_RECORD_SELECT, 0x0404);*/
+
+ /* select MIC */
+ AC97Write(AC97_RECORD_SELECT, 0x0000);
+
+ /* unmute record gain */
+ AC97Write(AC97_RECORD_GAIN_VOLUME, 0x0000);
+}
+
+
+/* The following two functions can be used by the mixer to
+ control and read volume settings. */
+PRIVATE int AC97Write (u16_t addr, u16_t data)
+{
+ /* todo: only allow volume control,
+ no serial data or dev ctl please*/
+ return CodecWriteUnsynced(&dev, addr, data);
+}
+
+
+PRIVATE int AC97Read (u16_t addr, u16_t *data)
+{
+ return CodecReadUnsynced(&dev, addr, data);
+}
--- /dev/null
+#ifndef ES1371_H
+#define ES1371_H
+
+#include <sys/types.h>
+#include "../../drivers.h"
+#include "../../libpci/pci.h"
+#include <sys/ioc_sound.h>
+
+#define DAC1_CHAN 0
+#define ADC1_CHAN 1
+#define MIXER 2
+#define DAC2_CHAN 3
+
+/* set your vendor and device ID's here */
+#define VENDOR_ID 0x1274
+#define DEVICE_ID 0x1371
+
+/* Concert97 direct register offset defines */
+#define CONC_bDEVCTL_OFF 0x00 /* Device control/enable */
+#define CONC_bMISCCTL_OFF 0x01 /* Miscellaneous control */
+#define CONC_bGPIO_OFF 0x02 /* General purpose I/O control */
+#define CONC_bJOYCTL_OFF 0x03 /* Joystick control (decode) */
+#define CONC_bINTSTAT_OFF 0x04 /* Device interrupt status */
+#define CONC_bCODECSTAT_OFF 0x05 /* CODEC interface status */
+#define CONC_bINTSUMM_OFF 0x07 /* Interrupt summary status */
+#define CONC_b4SPKR_OFF 0x07 /* Also 4 speaker config reg */
+#define CONC_bSPDIF_ROUTE_OFF 0x07 /* Also S/PDIF route control reg */
+#define CONC_bUARTDATA_OFF 0x08 /* UART data R/W - read clears RX int */
+#define CONC_bUARTCSTAT_OFF 0x09 /* UART control and status */
+#define CONC_bUARTTEST_OFF 0x0a /* UART test control reg */
+#define CONC_bMEMPAGE_OFF 0x0c /* Memory page select */
+#define CONC_dSRCIO_OFF 0x10 /* I/O ctl/stat/data for SRC RAM */
+#define CONC_dCODECCTL_OFF 0x14 /* CODEC control - u32_t read/write */
+#define CONC_wNMISTAT_OFF 0x18 /* Legacy NMI status */
+#define CONC_bNMIENA_OFF 0x1a /* Legacy NMI enable */
+#define CONC_bNMICTL_OFF 0x1b /* Legacy control */
+#define CONC_bSERFMT_OFF 0x20 /* Serial device format */
+#define CONC_bSERCTL_OFF 0x21 /* Serial device control */
+#define CONC_bSKIPC_OFF 0x22 /* DAC skip count reg */
+#define CONC_wSYNIC_OFF 0x24 /* Synth int count in sample frames */
+#define CONC_wSYNCIC_OFF 0x26 /* Synth current int count */
+#define CONC_wDACIC_OFF 0x28 /* DAC int count in sample frames */
+#define CONC_wDACCIC_OFF 0x2a /* DAC current int count */
+#define CONC_wADCIC_OFF 0x2c /* ADC int count in sample frames */
+#define CONC_wADCCIC_OFF 0x2e /* ADC current int count */
+#define CONC_MEMBASE_OFF 0x30 /* Memory window base - 16 byte window */
+
+/* Concert memory page-banked register offset defines */
+#define CONC_dSYNPADDR_OFF 0x30 /* Synth host frame PCI phys addr */
+#define CONC_wSYNFC_OFF 0x34 /* Synth host frame count in u32_t'S */
+#define CONC_wSYNCFC_OFF 0x36 /* Synth host current frame count */
+#define CONC_dDACPADDR_OFF 0x38 /* DAC host frame PCI phys addr */
+#define CONC_wDACFC_OFF 0x3c /* DAC host frame count in u32_t'S */
+#define CONC_wDACCFC_OFF 0x3e /* DAC host current frame count */
+#define CONC_dADCPADDR_OFF 0x30 /* ADC host frame PCI phys addr */
+#define CONC_wADCFC_OFF 0x34 /* ADC host frame count in u32_t'S */
+#define CONC_wADCCFC_OFF 0x36 /* ADC host current frame count */
+
+/* memory page number defines */
+#define CONC_SYNRAM_PAGE 0x00 /* Synth host/serial I/F RAM */
+#define CONC_DACRAM_PAGE 0x04 /* DAC host/serial I/F RAM */
+#define CONC_ADCRAM_PAGE 0x08 /* ADC host/serial I/F RAM */
+#define CONC_SYNCTL_PAGE 0x0c /* Page bank for synth host control */
+#define CONC_DACCTL_PAGE 0x0c /* Page bank for DAC host control */
+#define CONC_ADCCTL_PAGE 0x0d /* Page bank for ADC host control */
+#define CONC_FIFO0_PAGE 0x0e /* page 0 of UART "FIFO" (rx stash) */
+#define CONC_FIFO1_PAGE 0x0f /* page 1 of UART "FIFO" (rx stash) */
+
+
+
+/* bits for Interrupt/Chip Select Control Register (offset 0x00)*/
+#define DAC1_EN_BIT bit(6)
+#define DAC2_EN_BIT bit(5)
+#define ADC1_EN_BIT bit(4)
+#define SYNC_RES_BIT bit(14)
+
+/* bits for Interrupt/Chip Select Status Register (offset 0x04)*/
+#define DAC1_INT_STATUS_BIT bit(2)
+#define DAC2_INT_STATUS_BIT bit(1)
+#define ADC1_INT_STATUS_BIT bit(0)
+
+/* some bits for Serial Interface Control Register (CONC_bSERFMT_OFF 20H) */
+#define DAC1_STEREO_BIT bit(0) /* stereo or mono format */
+#define DAC1_16_8_BIT bit(1) /* 16 or 8 bit format */
+#define DAC2_STEREO_BIT bit(2)
+#define DAC2_16_8_BIT bit(3)
+#define ADC1_STEREO_BIT bit(4)
+#define ADC1_16_8_BIT bit(5)
+#define DAC1_INT_EN_BIT bit(8) /* interupt enable bits */
+#define DAC2_INT_EN_BIT bit(9)
+#define ADC1_INT_EN_BIT bit(10)
+#define DAC1_PAUSE_BIT bit(11)
+#define DAC2_PAUSE_BIT bit(12)
+
+/* Some return values */
+#define SRC_SUCCESS 0
+#define CONC_SUCCESS 0
+ /* Timeout waiting for: */
+#define SRC_ERR_NOT_BUSY_TIMEOUT -1 /* SRC not busy */
+#define CONC_ERR_NO_PCI_BIOS -2
+#define CONC_ERR_DEVICE_NOT_FOUND -3
+#define CONC_ERR_SPDIF_NOT_AVAIL -4
+#define CONC_ERR_SPDIF_ROUTING_NOT_AVAIL -5
+#define CONC_ERR_4SPEAKER_NOT_AVAIL -6
+#define CONC_ERR_ECHO_NOT_AVAIL -7
+
+typedef struct {
+ u32_t stereo;
+ u16_t sample_rate;
+ u32_t nr_of_bits;
+ u32_t sign;
+ u32_t busy;
+ u32_t fragment_size;
+} aud_sub_dev_conf_t;
+
+/* Some defaults for the aud_sub_dev_conf_t*/
+#define DEFAULT_RATE 44100 /* Sample rate */
+#define DEFAULT_NR_OF_BITS 16 /* Nr. of bits per sample per channel*/
+#define DEFAULT_SIGNED 0 /* 0 = unsigned, 1 = signed */
+#define DEFAULT_STEREO 1 /* 0 = mono, 1 = stereo */
+#define MAX_RATE 44100 /* Max sample speed in KHz */
+#define MIN_RATE 4000 /* Min sample speed in KHz */
+
+
+typedef struct DEVSTRUCT {
+ char* name;
+ u16_t v_id; /* vendor id */
+ u16_t d_id; /* device id */
+ u32_t devind; /* minix pci device id, for pci configuration space */
+ u32_t base; /* changed to 32 bits */
+ char irq;
+ char revision;/* version of the device */
+} DEV_STRUCT;
+
+#define bit(n) 1UL << n
+
+
+#endif /* ES1371_H */
--- /dev/null
+#include "../../drivers.h"
+#include <sys/types.h>
+#include <time.h>
+#include "../../libpci/pci.h"
+
+int WaitBitd (int paddr, int bitno, int state, long tmout)
+{
+unsigned long val, mask;
+
+ mask = 1UL << bitno;
+ tmout *= 5000;
+
+ if(state) {
+ while(tmout-- > 0) {
+ if((val = pci_inl(paddr)) & mask) {
+ return 0;
+ }
+ }
+ } else {
+ while(tmout-- > 0) {
+ if(!((val = pci_inl(paddr)) & mask)) {
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
--- /dev/null
+/* WAIT.H
+// General purpose waiting routines
+
+// Function prototypes
+*/
+int WaitBitb (int paddr, int bitno, int state, long tmout);
+int WaitBitw (int paddr, int bitno, int state, long tmout);
+int WaitBitd (int paddr, int bitno, int state, long tmout);
+int MemWaitw (unsigned int volatile *gaddr, int bitno, int state, long tmout);
+
--- /dev/null
+CC = exec cc
+
+all: audio_fw.o
+
+audio_fw.o:
+ $(CC) -c audio_fw.c
+
+clean:
+ rm -f a.out *.bak core errs audio_fw.o
--- /dev/null
+/* This file contains a standard driver for audio devices.
+ * It supports double dma buffering and can be configured to use
+ * extra buffer space beside the dma buffer.
+ * This driver also support sub devices, which can be independently
+ * opened and closed.
+ *
+ * The driver supports the following operations:
+ *
+ * m_type DEVICE PROC_NR COUNT POSITION ADRRESS
+ * -----------------------------------------------------------------
+ * | DEV_OPEN | device | proc nr | | | |
+ * |-------------+---------+---------+---------+---------+---------|
+ * | DEV_CLOSE | device | proc nr | | | |
+ * |-------------+---------+---------+---------+---------+---------|
+ * | DEV_READ | device | proc nr | bytes | | buf ptr |
+ * |-------------+---------+---------+---------+---------+---------|
+ * | DEV_WRITE | device | proc nr | bytes | | buf ptr |
+ * |-------------+---------+---------+---------+---------+---------|
+ * | DEV_IOCTL | device | proc nr |func code| | buf ptr |
+ * |-------------+---------+---------+---------+---------+---------|
+ * | DEV_STATUS | | | | | |
+ * |-------------+---------+---------+---------+---------+---------|
+ * | HARD_INT | | | | | |
+ * |-------------+---------+---------+---------+---------+---------|
+ * | SIG_STOP | | | | | |
+ * -----------------------------------------------------------------
+ *
+ * The file contains one entry point:
+ *
+ * main: main entry when driver is brought up
+ *
+ * February 2006 Updated audio framework, changed driver-framework relation (Peter Boonstoppel)
+ * November 2005 Created generic DMA driver framework (Laurens Bronwasser)
+ * August 24 2005 Ported audio driver to user space (only audio playback) (Peter Boonstoppel)
+ * May 20 1995 SB16 Driver: Michel R. Prevenier
+ */
+
+#include "audio_fw.h"
+
+FORWARD _PROTOTYPE( int msg_open, (int minor_dev_nr) );
+FORWARD _PROTOTYPE( int msg_close, (int minor_dev_nr) );
+FORWARD _PROTOTYPE( int msg_ioctl, (message *m_ptr) );
+FORWARD _PROTOTYPE( void msg_write, (message *m_ptr) );
+FORWARD _PROTOTYPE( void msg_read, (message *m_ptr) );
+FORWARD _PROTOTYPE( void msg_hardware, (void) );
+FORWARD _PROTOTYPE( void msg_sig_stop, (void) );
+FORWARD _PROTOTYPE( void msg_status, (message *m_ptr) );
+FORWARD _PROTOTYPE( int init_driver, (void) );
+FORWARD _PROTOTYPE( int open_sub_dev, (int sub_dev_nr, int operation) );
+FORWARD _PROTOTYPE( int close_sub_dev, (int sub_dev_nr) );
+FORWARD _PROTOTYPE( void handle_int_write,(int sub_dev_nr) );
+FORWARD _PROTOTYPE( void handle_int_read,(int sub_dev_nr) );
+FORWARD _PROTOTYPE( void data_to_user, (sub_dev_t *sub_dev_ptr) );
+FORWARD _PROTOTYPE( void data_from_user, (sub_dev_t *sub_dev_ptr) );
+FORWARD _PROTOTYPE( int init_buffers, (sub_dev_t *sub_dev_ptr) );
+FORWARD _PROTOTYPE( int get_started, (sub_dev_t *sub_dev_ptr) );
+FORWARD _PROTOTYPE( void reply,(int code, int replyee, int process,int status));
+FORWARD _PROTOTYPE( int io_ctl_length, (int io_request) );
+FORWARD _PROTOTYPE( special_file_t* get_special_file, (int minor_dev_nr) );
+
+
+PRIVATE char io_ctl_buf[_IOCPARM_MASK];
+PRIVATE int irq_hook_id = 0; /* id of irq hook at the kernel */
+PRIVATE int irq_hook_set = FALSE;
+PRIVATE device_available = 0;/*todo*/
+
+
+PUBLIC void main(void)
+{
+ int r, caller, proc_nr, chan;
+ message mess;
+
+ drv_init();
+
+ /* Here is the main loop of the dma driver. It waits for a message,
+ carries it out, and sends a reply. */
+ printf("%s up and running\n", drv.DriverName);
+
+ while(1) {
+ receive(ANY, &mess);
+ caller = mess.m_source;
+ proc_nr = mess.PROC_NR;
+
+ /* Now carry out the work. */
+ switch(mess.m_type) {
+ case DEV_OPEN: /* open the special file ( = parameter) */
+ r = msg_open(mess.DEVICE);break;
+
+ case DEV_CLOSE: /* close the special file ( = parameter) */
+ r = msg_close(mess.DEVICE); break;
+
+ case DEV_IOCTL:
+ r = msg_ioctl(&mess); break;
+
+ case DEV_READ:
+ msg_read(&mess); continue; /* don't reply */
+
+ case DEV_WRITE:
+ msg_write(&mess); continue; /* don't reply */
+
+ case DEV_STATUS:
+ msg_status(&mess);continue; /* don't reply */
+
+ case HARD_INT:
+ msg_hardware();continue; /* don't reply */
+
+ case SYS_SIG:
+ msg_sig_stop(); continue; /* don't reply */
+
+ default:
+ r = EINVAL; dprint("%s: %d uncaught msg!\n", mess.m_type ,drv.DriverName);
+ break;
+ }
+ /* Finally, prepare and send the reply message. */
+ reply(TASK_REPLY, caller, proc_nr, r);
+ }
+
+}
+
+PRIVATE int init_driver(void) {
+ u32_t i; char irq;
+ static int executed = 0;
+ sub_dev_t* sub_dev_ptr;
+
+ /* init variables, get dma buffers */
+ for (i = 0; i < drv.NrOfSubDevices; i++) {
+
+ sub_dev_ptr = &sub_dev[i];
+
+ sub_dev_ptr->Opened = FALSE;
+ sub_dev_ptr->DmaBusy = FALSE;
+ sub_dev_ptr->DmaMode = NO_DMA;
+ sub_dev_ptr->DmaReadNext = 0;
+ sub_dev_ptr->DmaFillNext = 0;
+ sub_dev_ptr->DmaLength = 0;
+ sub_dev_ptr->BufReadNext = 0;
+ sub_dev_ptr->BufFillNext = 0;
+ sub_dev_ptr->RevivePending = FALSE;
+ sub_dev_ptr->OutOfData = FALSE;
+ sub_dev_ptr->Nr = i;
+ }
+
+ /* initialize hardware*/
+ if (drv_init_hw() != OK) {
+ error("%s: Could not initialize hardware\n", drv.DriverName, 0);
+ return EIO;
+ }
+
+ /* get irq from device driver...*/
+ if (drv_get_irq(&irq) != OK) {
+ error("%s: init driver couldn't get IRQ", drv.DriverName, i);
+ return EIO;
+ }
+ /* todo: execute the rest of this function only once
+ we don't want to set irq policy twice */
+ if (executed) return OK;
+ executed = TRUE;
+
+ /* ...and register interrupt vector */
+ if ((i=sys_irqsetpolicy(irq, 0, &irq_hook_id )) != OK){
+ error("%s: init driver couldn't set IRQ policy", drv.DriverName, i);
+ return EIO;
+ }
+ irq_hook_set = TRUE; /* now msg_sig_stop knows it must unregister policy*/
+ return OK;
+}
+
+
+PRIVATE int msg_open (int minor_dev_nr) {
+ int r, read_chan, write_chan, io_ctl;
+ special_file_t* special_file_ptr;
+
+ dprint("%s: msg_open() special file %d\n", drv.DriverName, minor_dev_nr);
+
+ special_file_ptr = get_special_file(minor_dev_nr);
+ if(special_file_ptr == NULL) {
+ return EIO;
+ }
+
+ read_chan = special_file_ptr->read_chan;
+ write_chan = special_file_ptr->write_chan;
+ io_ctl = special_file_ptr->io_ctl;
+
+ if (read_chan==NO_CHANNEL && write_chan==NO_CHANNEL && io_ctl==NO_CHANNEL) {
+ error("%s: No channel specified for minor device!\n", drv.DriverName, minor_dev_nr);
+ return EIO;
+ }
+ if (read_chan == write_chan && read_chan != NO_CHANNEL) {
+ error("%s: Read and write channels are equal!\n", drv.DriverName,minor_dev_nr);
+ return EIO;
+ }
+ /* init driver */
+ if (!device_available) {
+ if (init_driver() != OK) {
+ error("%s: Couldn't init driver!\n", drv.DriverName, minor_dev_nr);
+ return EIO;
+ } else {
+ device_available = TRUE;
+ }
+ }
+ /* open the sub devices specified in the interface header file */
+ if (write_chan != NO_CHANNEL) {
+ /* open sub device for writing */
+ if (open_sub_dev(write_chan, DEV_WRITE) != OK) return EIO;
+ }
+ if (read_chan != NO_CHANNEL) {
+ if (open_sub_dev(read_chan, DEV_READ) != OK) return EIO;
+ }
+ if (read_chan == io_ctl || write_chan == io_ctl) {
+ /* io_ctl is already opened because it's the same as read or write */
+ return OK; /* we're done */
+ }
+ if (io_ctl != NO_CHANNEL) { /* Ioctl differs from read/write channels, */
+ r = open_sub_dev(io_ctl, NO_DMA); /* open it explicitly */
+ if (r != OK) return EIO;
+ }
+ return OK;
+}
+
+
+PRIVATE int open_sub_dev(int sub_dev_nr, int dma_mode) {
+ sub_dev_t* sub_dev_ptr; int i;
+ sub_dev_ptr = &sub_dev[sub_dev_nr];
+
+ /* Only one open at a time per sub device */
+ if (sub_dev_ptr->Opened) {
+ error("%s: Sub device %d is already opened\n", drv.DriverName, sub_dev_nr);
+ return EBUSY;
+ }
+ if (sub_dev_ptr->DmaBusy) {
+ error("%s: Sub device %d is still busy\n", drv.DriverName, sub_dev_nr);
+ return EBUSY;
+ }
+ /* Setup variables */
+ sub_dev_ptr->Opened = TRUE;
+ sub_dev_ptr->DmaReadNext = 0;
+ sub_dev_ptr->DmaFillNext = 0;
+ sub_dev_ptr->DmaLength = 0;
+ sub_dev_ptr->DmaMode = dma_mode;
+ sub_dev_ptr->BufReadNext = 0;
+ sub_dev_ptr->BufFillNext = 0;
+ sub_dev_ptr->BufLength = 0;
+ sub_dev_ptr->RevivePending = FALSE;
+ sub_dev_ptr->OutOfData = TRUE;
+
+ /* arrange DMA */
+ if (dma_mode != NO_DMA) { /* sub device uses DMA */
+ /* allocate dma buffer and extra buffer space
+ and configure sub device for dma */
+ if (init_buffers(sub_dev_ptr) != OK ) return EIO;
+ }
+ return OK;
+}
+
+
+PRIVATE int msg_close(int minor_dev_nr) {
+
+ int r, read_chan, write_chan, io_ctl;
+ special_file_t* special_file_ptr;
+
+ dprint("%s: msg_close() minor device %d\n", drv.DriverName, minor_dev_nr);
+
+ special_file_ptr = get_special_file(minor_dev_nr);
+ if(special_file_ptr == NULL) {
+ return EIO;
+ }
+
+ read_chan = special_file_ptr->read_chan;
+ write_chan = special_file_ptr->write_chan;
+ io_ctl = special_file_ptr->io_ctl;
+
+ /* close all sub devices */
+ if (write_chan != NO_CHANNEL) {
+ if (close_sub_dev(write_chan) != OK) r = EIO;
+ }
+ if (read_chan != NO_CHANNEL) {
+ if (close_sub_dev(read_chan) != OK) r = EIO;
+ }
+ if (read_chan == io_ctl || write_chan == io_ctl) {
+ /* io_ctl is already closed because it's the same as read or write */
+ return r; /* we're done */
+ }
+ /* Ioctl differs from read/write channels... */
+ if (io_ctl != NO_CHANNEL) {
+ if (close_sub_dev(io_ctl) != OK) r = EIO; /* ...close it explicitly */
+ }
+ return r;
+}
+
+
+PRIVATE int close_sub_dev(int sub_dev_nr) {
+ sub_dev_t *sub_dev_ptr;
+ sub_dev_ptr = &sub_dev[sub_dev_nr];
+
+ if (sub_dev_ptr->DmaMode == DEV_WRITE && !sub_dev_ptr->OutOfData) {
+ /* do nothing, still data in buffers that has to be transferred */
+ sub_dev_ptr->Opened = FALSE; /* keep DMA busy */
+ return OK;
+ }
+ if (sub_dev_ptr->DmaMode == NO_DMA) {
+ /* do nothing, there is no dma going on */
+ sub_dev_ptr->Opened = FALSE;
+ return OK;
+ }
+ sub_dev_ptr->Opened = FALSE;
+ sub_dev_ptr->DmaBusy = FALSE;
+ /* stop the device */
+ drv_stop(sub_dev_ptr->Nr);
+ /* free the buffers */
+ free(sub_dev_ptr->DmaBuf);
+ free(sub_dev_ptr->ExtraBuf);
+}
+
+
+PRIVATE int msg_ioctl(message *m_ptr)
+{
+ int status, len, chan;
+ phys_bytes user_phys;
+ sub_dev_t *sub_dev_ptr;
+ special_file_t* special_file_ptr;
+
+ dprint("%s: msg_ioctl() device %d\n", drv.DriverName, m_ptr->DEVICE);
+
+ special_file_ptr = get_special_file(m_ptr->DEVICE);
+ if(special_file_ptr == NULL) {
+ return EIO;
+ }
+
+ chan = special_file_ptr->io_ctl;
+
+ if (chan == NO_CHANNEL) {
+ error("%s: No io control channel specified!\n", drv.DriverName);
+ return EIO;
+ }
+ /* get pointer to sub device data */
+ sub_dev_ptr = &sub_dev[chan];
+
+ if(!sub_dev_ptr->Opened) {
+ error("%s: io control impossible - not opened!\n", drv.DriverName);
+ return EIO;
+ }
+
+ /* this is a hack...todo: may we intercept reset calls? */
+ if(m_ptr->REQUEST == DSPIORESET) {
+ device_available = FALSE;
+ }
+ /* this is confusing, _IOC_OUT bit means that there is incoming data */
+ if (m_ptr->REQUEST & _IOC_OUT) { /* if there is data for us, copy it */
+ len = io_ctl_length(m_ptr->REQUEST);
+ sys_vircopy(m_ptr->PROC_NR, D,
+ (vir_bytes)m_ptr->ADDRESS, SELF, D,
+ (vir_bytes)io_ctl_buf, len);
+ }
+
+ /* all ioctl's are passed to the device specific part of the driver */
+ status = drv_io_ctl(m_ptr->REQUEST, (void *)io_ctl_buf, &len, chan);
+
+ /* _IOC_IN bit -> user expects data */
+ if (status == OK && m_ptr->REQUEST & _IOC_IN) {
+ /* copy result back to user */
+ sys_vircopy(SELF, D, (vir_bytes)io_ctl_buf, m_ptr->PROC_NR, D, (vir_bytes)m_ptr->ADDRESS, len);
+ }
+ return status;
+}
+
+
+PRIVATE void msg_write(message *m_ptr)
+{
+ int s, chan; sub_dev_t *sub_dev_ptr;
+ special_file_t* special_file_ptr;
+
+ dprint("%s: msg_write() device %d\n", drv.DriverName, m_ptr->DEVICE);
+
+ special_file_ptr = get_special_file(m_ptr->DEVICE);
+ chan = special_file_ptr->write_chan;
+
+ if (chan == NO_CHANNEL) {
+ error("%s: No write channel specified!\n", drv.DriverName);
+ reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO);
+ return;
+ }
+ /* get pointer to sub device data */
+ sub_dev_ptr = &sub_dev[chan];
+
+ if (!sub_dev_ptr->DmaBusy) { /* get fragment size on first write */
+ if (drv_get_frag_size(&(sub_dev_ptr->FragSize), sub_dev_ptr->Nr) != OK){
+ error("%s; Failed to get fragment size!\n", drv.DriverName, 0);
+ return;
+ }
+ }
+ if(m_ptr->COUNT != sub_dev_ptr->FragSize) {
+ error("Fragment size does not match user's buffer length\n");
+ reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINVAL);
+ return;
+ }
+ /* if we are busy with something else than writing, return EBUSY */
+ if(sub_dev_ptr->DmaBusy && sub_dev_ptr->DmaMode != DEV_WRITE) {
+ error("Already busy with something else then writing\n");
+ reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EBUSY);
+ return;
+ }
+ /* unblock the FileSystem, but keep user process blocked until REVIVE*/
+ reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, SUSPEND);
+ sub_dev_ptr->RevivePending = TRUE;
+ sub_dev_ptr->ReviveProcNr = m_ptr->PROC_NR;
+ sub_dev_ptr->UserBuf = m_ptr->ADDRESS;
+ sub_dev_ptr->NotifyProcNr = m_ptr->m_source;
+
+ data_from_user(sub_dev_ptr);
+
+ if(!sub_dev_ptr->DmaBusy) { /* Dma tranfer not yet started */
+ dprint("starting audio device\n");
+ get_started(sub_dev_ptr);
+ sub_dev_ptr->DmaMode = DEV_WRITE; /* Dma mode is writing */
+ }
+}
+
+
+PRIVATE void msg_read(message *m_ptr)
+{
+ int s, chan; sub_dev_t *sub_dev_ptr;
+ special_file_t* special_file_ptr;
+
+ dprint("%s: msg_read() device %d\n", drv.DriverName, m_ptr->DEVICE);
+
+ special_file_ptr = get_special_file(m_ptr->DEVICE);
+ chan = special_file_ptr->read_chan;
+
+ if (chan == NO_CHANNEL) {
+ error("%s: No read channel specified!\n", drv.DriverName);
+ reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO);
+ return;
+ }
+ /* get pointer to sub device data */
+ sub_dev_ptr = &sub_dev[chan];
+
+ if (!sub_dev_ptr->DmaBusy) { /* get fragment size on first read */
+ if (drv_get_frag_size(&(sub_dev_ptr->FragSize), sub_dev_ptr->Nr) != OK){
+ error("%s: Could not retrieve fragment size!\n", drv.DriverName);
+ reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO);
+ return;
+ }
+ }
+ if(m_ptr->COUNT != sub_dev_ptr->FragSize) {
+ reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINVAL);
+ error("fragment size does not match message size\n");
+ return;
+ }
+ /* if we are busy with something else than reading, reply EBUSY */
+ if(sub_dev_ptr->DmaBusy && sub_dev_ptr->DmaMode != DEV_READ) {
+ reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EBUSY);
+ return;
+ }
+ /* unblock the FileSystem, but keep user process blocked until REVIVE*/
+ reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, SUSPEND);
+ sub_dev_ptr->RevivePending = TRUE;
+ sub_dev_ptr->ReviveProcNr = m_ptr->PROC_NR;
+ sub_dev_ptr->UserBuf = m_ptr->ADDRESS;
+ sub_dev_ptr->NotifyProcNr = m_ptr->m_source;
+
+ if(!sub_dev_ptr->DmaBusy) { /* Dma tranfer not yet started */
+ get_started(sub_dev_ptr);
+ sub_dev_ptr->DmaMode = DEV_READ; /* Dma mode is reading */
+ return; /* no need to get data from DMA buffer at this point */
+ }
+ /* check if data is available and possibly fill user's buffer */
+ data_to_user(sub_dev_ptr);
+}
+
+
+PRIVATE void msg_hardware(void) {
+
+ u32_t i;
+ int j = 0;
+
+ dprint("%s: handling hardware message\n", drv.DriverName);
+
+ /* while we have an interrupt */
+ while ( drv_int_sum()) {
+ /* loop over all sub devices */
+ for ( i = 0; i < drv.NrOfSubDevices; i++) {
+ /* if interrupt from sub device and Dma transfer
+ was actually busy, take care of business */
+ if( drv_int(i) && sub_dev[i].DmaBusy ) {
+ if (sub_dev[i].DmaMode == DEV_WRITE) handle_int_write(i);
+ if (sub_dev[i].DmaMode == DEV_READ) handle_int_read(i);
+ }
+ }
+ }
+}
+
+
+PRIVATE void msg_status(message *m_ptr)
+{
+ int i;
+
+ dprint("got a status message\n");
+ for (i = 0; i < drv.NrOfSubDevices; i++) {
+
+ if(sub_dev[i].ReadyToRevive)
+ {
+ m_ptr->m_type = DEV_REVIVE; /* build message */
+ m_ptr->REP_PROC_NR = sub_dev[i].ReviveProcNr;
+ m_ptr->REP_STATUS = sub_dev[i].ReviveStatus;
+ send(m_ptr->m_source, m_ptr); /* send the message */
+
+ /* reset variables */
+ sub_dev[i].ReadyToRevive = FALSE;
+ sub_dev[i].RevivePending = 0;
+
+ return; /* stop after one mess,
+ file system will get back for other processes */
+ }
+ }
+ m_ptr->m_type = DEV_NO_STATUS;
+ m_ptr->REP_STATUS = 0;
+ send(m_ptr->m_source, m_ptr); /* send DEV_NO_STATUS message */
+}
+
+
+PRIVATE void msg_sig_stop(void)
+{
+ int i; char irq;
+ for (i = 0; i < drv.NrOfSubDevices; i++) {
+ drv_stop(i); /* stop all sub devices */
+ }
+ if (irq_hook_set) {
+ if (sys_irqdisable(&irq_hook_id) != OK) {
+ error("Could not disable IRQ\n");
+ }
+ /* get irq from device driver*/
+ if (drv_get_irq(&irq) != OK) {
+ error("Msg SIG_STOP Couldn't get IRQ");
+ }
+ /* remove the policy */
+ if (sys_irqrmpolicy(irq, &irq_hook_id) != OK) {
+ error("%s: Could not disable IRQ\n",drv.DriverName);
+ }
+ }
+}
+
+
+/* handle interrupt for specified sub device; DmaMode == DEV_WRITE*/
+PRIVATE void handle_int_write(int sub_dev_nr)
+{
+ sub_dev_t *sub_dev_ptr;
+ int r;
+
+ sub_dev_ptr = &sub_dev[sub_dev_nr];
+
+ dprint("Finished playing dma[%d] ", sub_dev_ptr->DmaReadNext);
+ sub_dev_ptr->DmaReadNext = (sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
+ sub_dev_ptr->DmaLength -= 1;
+
+ if (sub_dev_ptr->BufLength != 0) { /* Data in extra buf, copy to Dma buf */
+
+ dprint(" buf[%d] -> dma[%d] ", sub_dev_ptr->BufReadNext, sub_dev_ptr->DmaFillNext);
+ memcpy(sub_dev_ptr->DmaPtr + sub_dev_ptr->DmaFillNext * sub_dev_ptr->FragSize,
+ sub_dev_ptr->ExtraBuf + sub_dev_ptr->BufReadNext * sub_dev_ptr->FragSize,
+ sub_dev_ptr->FragSize);
+
+ sub_dev_ptr->BufReadNext = (sub_dev_ptr->BufReadNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
+ sub_dev_ptr->DmaFillNext = (sub_dev_ptr->DmaFillNext + 1) % sub_dev_ptr->NrOfDmaFragments;
+
+ sub_dev_ptr->BufLength -= 1;
+ sub_dev_ptr->DmaLength += 1;
+ }
+
+ /* space became available, possibly copy new data from user */
+ data_from_user(sub_dev_ptr);
+
+ if(sub_dev_ptr->DmaLength == 0) { /* Dma buffer empty, stop Dma transfer */
+
+ sub_dev_ptr->OutOfData = TRUE; /* we're out of data */
+ dprint("No more work...!\n");
+ if (!sub_dev_ptr->Opened) {
+ close_sub_dev(sub_dev_ptr->Nr);
+ dprint("Stopping sub device %d\n", sub_dev_ptr->Nr);
+ return;
+ }
+ dprint("Pausing sub device %d\n",sub_dev_ptr->Nr);
+ drv_pause(sub_dev_ptr->Nr);
+ return;
+ }
+
+ dprint("\n");
+
+ /* confirm and reenable interrupt from this sub dev */
+ drv_reenable_int(sub_dev_nr);
+ /* reenable irq_hook*/
+ if ((r=sys_irqenable(&irq_hook_id)) != OK) {
+ error("%s Couldn't enable IRQ\n", drv.DriverName);
+ }
+}
+
+
+/* handle interrupt for specified sub device; DmaMode == DEV_READ */
+PRIVATE void handle_int_read(int sub_dev_nr)
+{
+ sub_dev_t *sub_dev_ptr; int r,i;
+
+ sub_dev_ptr = &sub_dev[sub_dev_nr];
+
+ dprint("Device filled dma[%d]\n", sub_dev_ptr->DmaFillNext);
+ sub_dev_ptr->DmaLength += 1;
+ sub_dev_ptr->DmaFillNext = (sub_dev_ptr->DmaFillNext + 1) % sub_dev_ptr->NrOfDmaFragments;
+
+ /* possibly copy data to user (if it is waiting for us) */
+ data_to_user(sub_dev_ptr);
+
+ if (sub_dev_ptr->DmaLength == sub_dev_ptr->NrOfDmaFragments) { /* if dma buffer full */
+
+ if (sub_dev_ptr->BufLength == sub_dev_ptr->NrOfExtraBuffers) {
+ error("All buffers full, we have a problem.\n");
+ drv_stop(sub_dev_nr); /* stop the sub device */
+ sub_dev_ptr->DmaBusy = FALSE;
+ sub_dev_ptr->ReviveStatus = 0; /* no data for user, this is a sad story */
+ sub_dev_ptr->ReadyToRevive = TRUE; /* wake user up */
+ return;
+ }
+ else { /* dma full, still room in extra buf; copy from dma to extra buf */
+ dprint("dma full: going to copy buf[%d] <- dma[%d]\n", sub_dev_ptr->BufFillNext,
+ sub_dev_ptr->DmaReadNext);
+ memcpy(sub_dev_ptr->ExtraBuf + sub_dev_ptr->BufFillNext * sub_dev_ptr->FragSize,
+ sub_dev_ptr->DmaPtr + sub_dev_ptr->DmaReadNext * sub_dev_ptr->FragSize,
+ sub_dev_ptr->FragSize);
+ sub_dev_ptr->DmaLength -= 1;
+ sub_dev_ptr->DmaReadNext = (sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
+ sub_dev_ptr->BufFillNext = (sub_dev_ptr->BufFillNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
+ }
+ }
+ /* confirm interrupt, and reenable interrupt from this sub dev*/
+ drv_reenable_int(sub_dev_ptr->Nr);
+
+ /* reenable irq_hook*/
+ if ((r=sys_irqenable(&irq_hook_id)) != OK) {
+ error("%s: Couldn't reenable IRQ", drv.DriverName);
+ }
+}
+
+
+PRIVATE int get_started(sub_dev_t *sub_dev_ptr) {
+ u32_t i;char c;
+
+ /* enable interrupt messages from MINIX */
+ if ((i=sys_irqenable(&irq_hook_id)) != OK) {
+ error("%s: Couldn't enable IRQs",drv.DriverName);
+ return EIO;
+ }
+ /* let the lower part of the driver start the device */
+ if (drv_start(sub_dev_ptr->Nr, sub_dev_ptr->DmaMode) != OK) {
+ error("%s: Could not start device %d\n", drv.DriverName, sub_dev_ptr->Nr);
+ }
+
+ sub_dev_ptr->DmaBusy = TRUE; /* Dma is busy from now on */
+ sub_dev_ptr->DmaReadNext = 0;
+ return OK;
+}
+
+
+PRIVATE void data_from_user(sub_dev_t *subdev) {
+
+ if (subdev->DmaLength == subdev->NrOfDmaFragments &&
+ subdev->BufLength == subdev->NrOfExtraBuffers) return; /* no space */
+
+ if (!subdev->RevivePending) return; /* no new data waiting to be copied */
+
+ if (subdev->RevivePending &&
+ subdev->ReadyToRevive) return; /* we already got this data */
+
+
+ if (subdev->DmaLength < subdev->NrOfDmaFragments) { /* room in dma buf */
+
+ sys_datacopy(subdev->ReviveProcNr,
+ (vir_bytes)subdev->UserBuf,
+ SELF,
+ (vir_bytes)subdev->DmaPtr + subdev->DmaFillNext * subdev->FragSize,
+ (phys_bytes)subdev->FragSize);
+
+ dprint(" user -> dma[%d]\n", subdev->DmaFillNext);
+ subdev->DmaLength += 1;
+ subdev->DmaFillNext = (subdev->DmaFillNext + 1) % subdev->NrOfDmaFragments;
+
+ } else { /* room in extra buf */
+
+ sys_datacopy(subdev->ReviveProcNr,
+ (vir_bytes)subdev->UserBuf,
+ SELF,
+ (vir_bytes)subdev->ExtraBuf + subdev->BufFillNext * subdev->FragSize,
+ (phys_bytes)subdev->FragSize);
+
+ dprint(" user -> buf[%d]\n", subdev->BufFillNext);
+ subdev->BufLength += 1;
+
+ subdev->BufFillNext = (subdev->BufFillNext + 1) % subdev->NrOfExtraBuffers;
+
+ }
+ if(subdev->OutOfData) { /* if device paused (because of lack of data) */
+ subdev->OutOfData = FALSE;
+ drv_reenable_int(subdev->Nr);
+ /* reenable irq_hook*/
+ if ((sys_irqenable(&irq_hook_id)) != OK) {
+ error("%s: Couldn't enable IRQ", drv.DriverName);
+ }
+ drv_resume(subdev->Nr); /* resume resume the sub device */
+
+ }
+
+ subdev->ReviveStatus = subdev->FragSize;
+ subdev->ReadyToRevive = TRUE;
+ notify(subdev->NotifyProcNr);
+}
+
+
+PRIVATE void data_to_user(sub_dev_t *sub_dev_ptr)
+{
+ if (!sub_dev_ptr->RevivePending) return; /* nobody is wating for data */
+ if (sub_dev_ptr->ReadyToRevive) return; /* we already filled user's buffer */
+ if (sub_dev_ptr->BufLength == 0 && sub_dev_ptr->DmaLength == 0) return;
+ /* no data for user */
+
+ if(sub_dev_ptr->BufLength != 0) { /* data in extra buffer available */
+
+ sys_datacopy(SELF,
+ (vir_bytes)sub_dev_ptr->ExtraBuf + sub_dev_ptr->BufReadNext * sub_dev_ptr->FragSize,
+ sub_dev_ptr->ReviveProcNr,
+ (vir_bytes)sub_dev_ptr->UserBuf,
+ (phys_bytes)sub_dev_ptr->FragSize);
+
+ dprint(" copied buf[%d] to user\n", sub_dev_ptr->BufReadNext);
+ /* adjust the buffer status variables */
+ sub_dev_ptr->BufReadNext = (sub_dev_ptr->BufReadNext + 1) % sub_dev_ptr->NrOfExtraBuffers;
+ sub_dev_ptr->BufLength -= 1;
+
+ } else { /* extra buf empty, but data in dma buf*/
+
+ sys_datacopy(SELF,
+ (vir_bytes)sub_dev_ptr->DmaPtr + sub_dev_ptr->DmaReadNext * sub_dev_ptr->FragSize,
+ sub_dev_ptr->ReviveProcNr,
+ (vir_bytes)sub_dev_ptr->UserBuf,
+ (phys_bytes)sub_dev_ptr->FragSize);
+
+ dprint(" copied dma[%d] to user\n", sub_dev_ptr->DmaReadNext);
+ /* adjust the buffer status variables */
+ sub_dev_ptr->DmaReadNext = (sub_dev_ptr->DmaReadNext + 1) % sub_dev_ptr->NrOfDmaFragments;
+ sub_dev_ptr->DmaLength -= 1;
+ }
+ sub_dev_ptr->ReviveStatus = sub_dev_ptr->FragSize;
+ sub_dev_ptr->ReadyToRevive = TRUE; /* drv_status will send REVIVE mess to FS*/
+ notify(sub_dev_ptr->NotifyProcNr); /* notify the File Systam to make it
+ send DEV_STATUS messages*/
+}
+
+
+PRIVATE int init_buffers(sub_dev_t *sub_dev_ptr) {
+#if (CHIP == INTEL)
+ unsigned left;
+ u32_t i;
+
+ /* allocate dma buffer space */
+ if (!(sub_dev_ptr->DmaBuf = malloc(sub_dev_ptr->DmaSize + 64 * 1024))) {
+ error("%s: failed to allocate dma buffer for channel %d\n", drv.DriverName,i);
+ return EIO;
+ }
+ /* allocate extra buffer space */
+ if (!(sub_dev_ptr->ExtraBuf = malloc(sub_dev_ptr->NrOfExtraBuffers * sub_dev_ptr->DmaSize / sub_dev_ptr->NrOfDmaFragments))) {
+ error("%s failed to allocate extra buffer for channel %d\n", drv.DriverName,i);
+ return EIO;
+ }
+
+ sub_dev_ptr->DmaPtr = sub_dev_ptr->DmaBuf;
+ i = sys_umap(SELF, D,
+ (vir_bytes) sub_dev_ptr->DmaBuf,
+ (phys_bytes) sizeof(sub_dev_ptr->DmaBuf),
+ &(sub_dev_ptr->DmaPhys));
+
+ if (i != OK) {
+ return EIO;
+ }
+ if((left = dma_bytes_left(sub_dev_ptr->DmaPhys)) < sub_dev_ptr->DmaSize) {
+ /* First half of buffer crosses a 64K boundary, can't DMA into that */
+ sub_dev_ptr->DmaPtr += left;
+ sub_dev_ptr->DmaPhys += left;
+ }
+ /* write the physical dma address and size to the device */
+ drv_set_dma(sub_dev_ptr->DmaPhys, sub_dev_ptr->DmaSize, sub_dev_ptr->Nr);
+ return OK;
+
+#else /* CHIP != INTEL */
+ error("%s: init_buffer() failed, CHIP != INTEL", drv.DriverName);
+ return EIO;
+#endif /* CHIP == INTEL */
+}
+
+
+PRIVATE void reply(int code, int replyee, int process, int status)
+{
+ message m;
+
+ m.m_type = code; /* TASK_REPLY or REVIVE */
+ m.REP_STATUS = status; /* result of device operation */
+ m.REP_PROC_NR = process; /* which user made the request */
+ send(replyee, &m);
+}
+
+
+PRIVATE int io_ctl_length(int io_request) {
+ io_request >>= 16;
+ return io_request & _IOCPARM_MASK;
+}
+
+
+PRIVATE special_file_t* get_special_file(int minor_dev_nr) {
+ int i;
+
+ for(i = 0; i < drv.NrOfSpecialFiles; i++) {
+ if(special_file[i].minor_dev_nr == minor_dev_nr) {
+ return &special_file[i];
+ }
+ }
+
+ error("%s: No subdevice specified for minor device %d!\n", drv.DriverName, minor_dev_nr);
+
+ return NULL;
+}
\ No newline at end of file
--- /dev/null
+#ifndef AUDIO_FW_H
+#define AUDIO_FW_H
+
+#include "../../drivers.h"
+#include <sys/ioc_sound.h>
+
+
+/* change '(void)' to 'printf' to print debug info and error messages */
+#define dprint (void)
+#define error printf
+
+
+
+_PROTOTYPE( void main, (void) );
+
+_PROTOTYPE( int drv_init, (void) );
+_PROTOTYPE( int drv_init_hw, (void) );
+_PROTOTYPE( int drv_reset, (void) );
+_PROTOTYPE( int drv_start, (int sub_dev, int DmaMode) );
+_PROTOTYPE( int drv_stop, (int sub_dev) );
+_PROTOTYPE( int drv_set_dma, (u32_t dma, u32_t length, int chan) );
+_PROTOTYPE( int drv_reenable_int, (int chan) );
+_PROTOTYPE( int drv_int_sum, (void) );
+_PROTOTYPE( int drv_int, (int sub_dev) );
+_PROTOTYPE( int drv_pause, (int chan) );
+_PROTOTYPE( int drv_resume, (int chan) );
+_PROTOTYPE( int drv_io_ctl, (int request, void * val, int * len, int sub_dev) );
+_PROTOTYPE( int drv_get_irq, (char *irq) );
+_PROTOTYPE( int drv_get_frag_size, (u32_t *frag_size, int sub_dev) );
+
+
+
+/* runtime status fields */
+typedef struct {
+ int readable;
+ int writable;
+ int DmaSize;
+ int NrOfDmaFragments;
+ int MinFragmentSize;
+ int NrOfExtraBuffers;
+ int Nr; /* sub device number */
+ int Opened; /* sub device opened */
+ int DmaBusy; /* is dma busy? */
+ int DmaMode; /* DEV_WRITE / DEV_READ */
+ int DmaReadNext; /* current dma buffer */
+ int DmaFillNext; /* next dma buffer to fill */
+ int DmaLength;
+ int BufReadNext; /* start of extra circular buffer */
+ int BufFillNext; /* end of extra circular buffer */
+ int BufLength;
+ int RevivePending; /* process waiting for this dev? */
+ int ReviveStatus; /* return val when proc unblocked */
+ int ReviveProcNr; /* the process to unblock */
+ void *UserBuf; /* address of user's data buffer */
+ int ReadyToRevive; /* are we ready to revive process?*/
+ int NotifyProcNr; /* process to send notify to (FS) */
+ u32_t FragSize; /* dma fragment size */
+ char *DmaBuf; /* the dma buffer; extra space for
+ page alignment */
+ phys_bytes DmaPhys; /* physical address of dma buffer */
+ char* DmaPtr; /* pointer to aligned dma buffer */
+ int OutOfData; /* all buffers empty? */
+ char *ExtraBuf; /* don't use extra buffer;just
+ declare a pointer to supress
+ error messages */
+} sub_dev_t;
+
+typedef struct {
+ int minor_dev_nr;
+ int read_chan;
+ int write_chan;
+ int io_ctl;
+} special_file_t;
+
+typedef struct {
+ char* DriverName;
+ int NrOfSubDevices;
+ int NrOfSpecialFiles;
+} drv_t;
+
+EXTERN drv_t drv;
+EXTERN sub_dev_t sub_dev[];
+EXTERN special_file_t special_file[];
+
+/* Number of bytes you can DMA before hitting a 64K boundary: */
+#define dma_bytes_left(phys) \
+ ((unsigned) (sizeof(int) == 2 ? 0 : 0x10000) - (unsigned) ((phys) & 0xFFFF))
+
+#define NO_CHANNEL -1
+
+#define TRUE 1
+#define FALSE 0
+#define NO_DMA 0
+
+
+#endif /* AUDIO_FW_H */
--- /dev/null
+
+mixer.o: ../framework/../../drivers.h
+mixer.o: ../framework/audio_fw.h
+mixer.o: /usr/include/ansi.h
+mixer.o: /usr/include/errno.h
+mixer.o: /usr/include/ibm/bios.h
+mixer.o: /usr/include/ibm/interrupt.h
+mixer.o: /usr/include/ibm/ports.h
+mixer.o: /usr/include/limits.h
+mixer.o: /usr/include/minix/bitmap.h
+mixer.o: /usr/include/minix/callnr.h
+mixer.o: /usr/include/minix/com.h
+mixer.o: /usr/include/minix/config.h
+mixer.o: /usr/include/minix/const.h
+mixer.o: /usr/include/minix/devio.h
+mixer.o: /usr/include/minix/dmap.h
+mixer.o: /usr/include/minix/ioctl.h
+mixer.o: /usr/include/minix/ipc.h
+mixer.o: /usr/include/minix/sound.h
+mixer.o: /usr/include/minix/sys_config.h
+mixer.o: /usr/include/minix/syslib.h
+mixer.o: /usr/include/minix/sysutil.h
+mixer.o: /usr/include/minix/type.h
+mixer.o: /usr/include/signal.h
+mixer.o: /usr/include/stddef.h
+mixer.o: /usr/include/stdlib.h
+mixer.o: /usr/include/string.h
+mixer.o: /usr/include/sys/dir.h
+mixer.o: /usr/include/sys/ioc_sound.h
+mixer.o: /usr/include/sys/types.h
+mixer.o: /usr/include/unistd.h
+mixer.o: mixer.c
+mixer.o: mixer.h
+mixer.o: sb16.h
+
+sb16.o: ../framework/../../drivers.h
+sb16.o: ../framework/audio_fw.h
+sb16.o: /usr/include/ansi.h
+sb16.o: /usr/include/errno.h
+sb16.o: /usr/include/ibm/bios.h
+sb16.o: /usr/include/ibm/interrupt.h
+sb16.o: /usr/include/ibm/ports.h
+sb16.o: /usr/include/limits.h
+sb16.o: /usr/include/minix/bitmap.h
+sb16.o: /usr/include/minix/callnr.h
+sb16.o: /usr/include/minix/com.h
+sb16.o: /usr/include/minix/config.h
+sb16.o: /usr/include/minix/const.h
+sb16.o: /usr/include/minix/devio.h
+sb16.o: /usr/include/minix/dmap.h
+sb16.o: /usr/include/minix/ioctl.h
+sb16.o: /usr/include/minix/ipc.h
+sb16.o: /usr/include/minix/sound.h
+sb16.o: /usr/include/minix/sys_config.h
+sb16.o: /usr/include/minix/syslib.h
+sb16.o: /usr/include/minix/sysutil.h
+sb16.o: /usr/include/minix/type.h
+sb16.o: /usr/include/signal.h
+sb16.o: /usr/include/stddef.h
+sb16.o: /usr/include/stdlib.h
+sb16.o: /usr/include/string.h
+sb16.o: /usr/include/sys/dir.h
+sb16.o: /usr/include/sys/ioc_sound.h
+sb16.o: /usr/include/sys/types.h
+sb16.o: /usr/include/unistd.h
+sb16.o: mixer.h
+sb16.o: sb16.c
+sb16.o: sb16.h
--- /dev/null
+# Makefile for the Sound Blaster 16 driver (SB16)
+
+# directories
+u = /usr
+i = $u/include
+s = $i/sys
+m = $i/minix
+b = $i/ibm
+d = ..
+
+# programs, flags, etc.
+CC = exec cc
+CFLAGS = -I$i
+LDFLAGS = -i
+LIBS = -lsys -lsysutil
+
+
+# build local binary
+all build: sb16
+
+sb16: sb16.o mixer.o audio_fw.o
+ $(CC) -o $@ $(LDFLAGS) sb16.o mixer.o audio_fw.o $(LIBS)
+
+audio_fw.o: ../framework/audio_fw.c ../framework/audio_fw.h
+ $(CC) -c ../framework/audio_fw.c
+
+# install with other drivers
+install: /usr/sbin/sb16
+/usr/sbin/sb16: sb16
+ install -o root -S 512k -c $? $@
+
+# clean up local files
+clean:
+ rm -f *.o *.bak sb16
+
+depend:
+ /usr/bin/mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
+
+# Include generated dependencies.
+include .depend
+
--- /dev/null
+#include "sb16.h"
+#include "mixer.h"
+
+
+
+FORWARD _PROTOTYPE( int get_set_volume, (struct volume_level *level, int flag));
+FORWARD _PROTOTYPE( int get_set_input, (struct inout_ctrl *input, int flag, int channel));
+FORWARD _PROTOTYPE( int get_set_output, (struct inout_ctrl *output, int flag));
+
+
+
+
+/*=========================================================================*
+ * mixer_ioctl
+ *=========================================================================*/
+PUBLIC int mixer_ioctl(int request, void *val, int *len) {
+ int status;
+
+ switch(request) {
+ case MIXIOGETVOLUME: status = get_set_volume(val, 0); break;
+ case MIXIOSETVOLUME: status = get_set_volume(val, 1); break;
+ case MIXIOGETINPUTLEFT: status = get_set_input(val, 0, 0); break;
+ case MIXIOGETINPUTRIGHT: status = get_set_input(val, 0, 1); break;
+ case MIXIOGETOUTPUT: status = get_set_output(val, 0); break;
+ case MIXIOSETINPUTLEFT: status = get_set_input(val, 1, 0); break;
+ case MIXIOSETINPUTRIGHT: status = get_set_input(val, 1, 1); break;
+ case MIXIOSETOUTPUT: status = get_set_output(val, 1); break;
+ default: status = ENOTTY;
+ }
+
+ return status;
+}
+
+
+/*=========================================================================*
+ * mixer_init
+ *=========================================================================*/
+PUBLIC int mixer_init() {
+ /* Try to detect the mixer by writing to MIXER_DAC_LEVEL if the
+ * value written can be read back the mixer is there
+ */
+
+ mixer_set(MIXER_DAC_LEVEL, 0x10); /* write something to it */
+ if(mixer_get(MIXER_DAC_LEVEL) != 0x10) {
+ dprint("sb16: Mixer not detected\n");
+ return EIO;
+ }
+
+ /* Enable Automatic Gain Control */
+ mixer_set(MIXER_AGC, 0x01);
+
+ dprint("Mixer detected\n");
+
+ return OK;
+}
+
+
+
+/*=========================================================================*
+ * get_set_volume *
+ *=========================================================================*/
+PRIVATE int get_set_volume(struct volume_level *level, int flag) {
+ int cmd_left, cmd_right, shift, max_level;
+
+ shift = 3;
+ max_level = 0x1F;
+ switch(level->device) {
+ case Master:
+ cmd_left = MIXER_MASTER_LEFT;
+ cmd_right = MIXER_MASTER_RIGHT;
+ break;
+ case Dac:
+ cmd_left = MIXER_DAC_LEFT;
+ cmd_right = MIXER_DAC_RIGHT;
+ break;
+ case Fm:
+ cmd_left = MIXER_FM_LEFT;
+ cmd_right = MIXER_FM_RIGHT;
+ break;
+ case Cd:
+ cmd_left = MIXER_CD_LEFT;
+ cmd_right = MIXER_CD_RIGHT;
+ break;
+ case Line:
+ cmd_left = MIXER_LINE_LEFT;
+ cmd_right = MIXER_LINE_RIGHT;
+ break;
+ case Mic:
+ cmd_left = cmd_right = MIXER_MIC_LEVEL;
+ break;
+ case Speaker:
+ cmd_left = cmd_right = MIXER_PC_LEVEL;
+ shift = 6;
+ max_level = 0x03;
+ break;
+ case Treble:
+ cmd_left = MIXER_TREBLE_LEFT;
+ cmd_right = MIXER_TREBLE_RIGHT;
+ shift = 4;
+ max_level = 0x0F;
+ break;
+ case Bass:
+ cmd_left = MIXER_BASS_LEFT;
+ cmd_right = MIXER_BASS_RIGHT;
+ shift = 4;
+ max_level = 0x0F;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if(flag) { /* Set volume level */
+ if(level->right < 0) level->right = 0;
+ else if(level->right > max_level) level->right = max_level;
+ if(level->left < 0) level->left = 0;
+ else if(level->left > max_level) level->left = max_level;
+
+ mixer_set(cmd_right, (level->right << shift));
+ mixer_set(cmd_left, (level->left << shift));
+ } else { /* Get volume level */
+ level->left = mixer_get(cmd_left);
+ level->right = mixer_get(cmd_right);
+
+ level->left >>= shift;
+ level->right >>= shift;
+ }
+
+ return OK;
+}
+
+
+/*=========================================================================*
+ * get_set_input *
+ *=========================================================================*/
+PRIVATE int get_set_input(struct inout_ctrl *input, int flag, int channel) {
+ int input_cmd, input_mask, mask, del_mask, shift;
+
+ input_cmd = (channel == 0 ? MIXER_IN_LEFT : MIXER_IN_RIGHT);
+
+ mask = mixer_get(input_cmd);
+
+ switch (input->device) {
+ case Fm:
+ shift = 5;
+ del_mask = 0x1F;
+ break;
+ case Cd:
+ shift = 1;
+ del_mask = 0x79;
+ break;
+ case Line:
+ shift = 3;
+ del_mask = 0x67;
+ break;
+ case Mic:
+ shift = 0;
+ del_mask = 0x7E;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (flag) { /* Set input */
+ input_mask = ((input->left == ON ? 1 : 0) << 1) | (input->right == ON ? 1 : 0);
+
+ if (shift > 0) input_mask <<= shift;
+ else input_mask >>= 1;
+
+ mask &= del_mask;
+ mask |= input_mask;
+
+ mixer_set(input_cmd, mask);
+ } else { /* Get input */
+ if (shift > 0) {
+ input->left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
+ input->right = ((mask >> shift) & 1 == 1 ? ON : OFF);
+ } else {
+ input->left = ((mask & 1) == 1 ? ON : OFF);
+ }
+ }
+
+ return OK;
+}
+
+
+/*=========================================================================*
+ * get_set_output *
+ *=========================================================================*/
+PRIVATE int get_set_output(struct inout_ctrl *output, int flag) {
+ int output_mask, mask, del_mask, shift;
+
+ mask = mixer_get(MIXER_OUTPUT_CTRL);
+
+ switch (output->device) {
+ case Cd:
+ shift = 1;
+ del_mask = 0x79;
+ break;
+ case Line:
+ shift = 3;
+ del_mask = 0x67;
+ break;
+ case Mic:
+ shift = 0;
+ del_mask = 0x7E;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ if (flag) { /* Set input */
+ output_mask = ((output->left == ON ? 1 : 0) << 1) | (output->right == ON ? 1 : 0);
+
+ if (shift > 0) output_mask <<= shift;
+ else output_mask >>= 1;
+
+ mask &= del_mask;
+ mask |= output_mask;
+
+ mixer_set(MIXER_OUTPUT_CTRL, mask);
+ } else { /* Get input */
+ if (shift > 0) {
+ output->left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
+ output->right = ((mask >> shift) & 1 == 1 ? ON : OFF);
+ } else {
+ output->left = ((mask & 1) == 1 ? ON : OFF);
+ }
+ }
+
+ return OK;
+}
+
+
+
+PUBLIC int mixer_set(int reg, int data) {
+ int i;
+
+ sb16_outb(MIXER_REG, reg);
+ for(i = 0; i < 100; i++);
+ sb16_outb(MIXER_DATA, data);
+
+ return OK;
+}
+
+
+
+PUBLIC int mixer_get(int reg) {
+ int i;
+
+ sb16_outb(MIXER_REG, reg);
+ for(i = 0; i < 100; i++);
+ return sb16_inb(MIXER_DATA) & 0xff;
+}
\ No newline at end of file
--- /dev/null
+#ifndef SB16_MIXER_H
+#define SB16_MIXER_H
+
+_PROTOTYPE( int mixer_init, (void));
+_PROTOTYPE( int mixer_ioctl, (int request, void *val, int *len));
+
+_PROTOTYPE( int mixer_set, (int reg, int data));
+_PROTOTYPE( int mixer_get, (int reg));
+
+#endif
--- /dev/null
+/* Driver for SB16 ISA card
+ * Implementing audio/audio_fw.h
+ *
+ * February 2006 Integrated standalone driver with audio framework (Peter Boonstoppel)
+ * August 24 2005 Ported audio driver to user space (only audio playback) (Peter Boonstoppel)
+ * May 20 1995 SB16 Driver: Michel R. Prevenier
+ */
+
+
+#include "sb16.h"
+#include "mixer.h"
+
+
+FORWARD _PROTOTYPE( void dsp_dma_setup, (phys_bytes address, int count, int sub_dev) );
+
+FORWARD _PROTOTYPE( int dsp_ioctl, (int request, void *val, int *len));
+FORWARD _PROTOTYPE( int dsp_set_size, (unsigned int size) );
+FORWARD _PROTOTYPE( int dsp_set_speed, (unsigned int speed) );
+FORWARD _PROTOTYPE( int dsp_set_stereo, (unsigned int stereo) );
+FORWARD _PROTOTYPE( int dsp_set_bits, (unsigned int bits) );
+FORWARD _PROTOTYPE( int dsp_set_sign, (unsigned int sign) );
+FORWARD _PROTOTYPE( int dsp_get_max_frag_size, (u32_t *val, int *len) );
+
+
+PRIVATE unsigned int DspStereo = DEFAULT_STEREO;
+PRIVATE unsigned int DspSpeed = DEFAULT_SPEED;
+PRIVATE unsigned int DspBits = DEFAULT_BITS;
+PRIVATE unsigned int DspSign = DEFAULT_SIGN;
+PRIVATE unsigned int DspFragmentSize;
+
+PRIVATE phys_bytes DmaPhys;
+PRIVATE int running = FALSE;
+
+
+PUBLIC sub_dev_t sub_dev[2];
+PUBLIC special_file_t special_file[3];
+PUBLIC drv_t drv;
+
+
+
+PUBLIC int drv_init(void) {
+ drv.DriverName = "SB16";
+ drv.NrOfSubDevices = 2;
+ drv.NrOfSpecialFiles = 3;
+
+ sub_dev[AUDIO].readable = 1;
+ sub_dev[AUDIO].writable = 1;
+ sub_dev[AUDIO].DmaSize = 64 * 1024;
+ sub_dev[AUDIO].NrOfDmaFragments = 2;
+ sub_dev[AUDIO].MinFragmentSize = 1024;
+ sub_dev[AUDIO].NrOfExtraBuffers = 4;
+
+ sub_dev[MIXER].writable = 0;
+ sub_dev[MIXER].readable = 0;
+
+ special_file[0].minor_dev_nr = 0;
+ special_file[0].write_chan = AUDIO;
+ special_file[0].read_chan = NO_CHANNEL;
+ special_file[0].io_ctl = AUDIO;
+
+ special_file[1].minor_dev_nr = 1;
+ special_file[1].write_chan = NO_CHANNEL;
+ special_file[1].read_chan = AUDIO;
+ special_file[1].io_ctl = AUDIO;
+
+ special_file[2].minor_dev_nr = 2;
+ special_file[2].write_chan = NO_CHANNEL;
+ special_file[2].read_chan = NO_CHANNEL;
+ special_file[2].io_ctl = MIXER;
+}
+
+
+PUBLIC int drv_init_hw(void) {
+ int i, s;
+ int DspVersion[2];
+ dprint("drv_init_hw():\n");
+
+ if(drv_reset () != OK) {
+ dprint("sb16: No SoundBlaster card detected\n");
+ return -1;
+ }
+
+ DspVersion[0] = DspVersion[1] = 0;
+ dsp_command(DSP_GET_VERSION); /* Get DSP version bytes */
+
+ for(i = 1000; i; i--) {
+ if(sb16_inb(DSP_DATA_AVL) & 0x80) {
+ if(DspVersion[0] == 0) {
+ DspVersion[0] = sb16_inb(DSP_READ);
+ } else {
+ DspVersion[1] = sb16_inb(DSP_READ);
+ break;
+ }
+ }
+ }
+
+ if(DspVersion[0] < 4) {
+ dprint("sb16: No SoundBlaster 16 compatible card detected\n");
+ return -1;
+ }
+
+ dprint("sb16: SoundBlaster DSP version %d.%d detected!\n", DspVersion[0], DspVersion[1]);
+
+ /* set SB to use our IRQ and DMA channels */
+ mixer_set(MIXER_SET_IRQ, (1 << (SB_IRQ / 2 - 1)));
+ mixer_set(MIXER_SET_DMA, (1 << SB_DMA_8 | 1 << SB_DMA_16));
+
+ DspFragmentSize = sub_dev[AUDIO].DmaSize / sub_dev[AUDIO].NrOfDmaFragments;
+
+ return OK;
+}
+
+
+
+PUBLIC int drv_reset(void) {
+ int i;
+ dprint("drv_reset():\n");
+
+ sb16_outb(DSP_RESET, 1);
+ for(i = 0; i < 1000; i++); /* wait a while */
+ sb16_outb(DSP_RESET, 0);
+
+ for(i = 0; i < 1000 && !(sb16_inb(DSP_DATA_AVL) & 0x80); i++);
+
+ if(sb16_inb(DSP_READ) != 0xAA) return EIO; /* No SoundBlaster */
+
+ return OK;
+}
+
+
+
+PUBLIC int drv_start(int channel, int DmaMode) {
+ dprint("drv_start():\n");
+
+ drv_reset();
+
+ dsp_dma_setup(DmaPhys, DspFragmentSize * sub_dev[channel].NrOfDmaFragments, DmaMode);
+
+ dsp_set_speed(DspSpeed);
+
+ /* Put the speaker on */
+ if(DmaMode == DEV_WRITE) {
+ dsp_command (DSP_CMD_SPKON); /* put speaker on */
+
+ /* Program DSP with dma mode */
+ dsp_command((DspBits == 8 ? DSP_CMD_8BITAUTO_OUT : DSP_CMD_16BITAUTO_OUT));
+ } else {
+ dsp_command (DSP_CMD_SPKOFF); /* put speaker off */
+
+ /* Program DSP with dma mode */
+ dsp_command((DspBits == 8 ? DSP_CMD_8BITAUTO_IN : DSP_CMD_16BITAUTO_IN));
+ }
+
+ /* Program DSP with transfer mode */
+ if (!DspSign) {
+ dsp_command((DspStereo == 1 ? DSP_MODE_STEREO_US : DSP_MODE_MONO_US));
+ } else {
+ dsp_command((DspStereo == 1 ? DSP_MODE_STEREO_S : DSP_MODE_MONO_S));
+ }
+
+ /* Give length of fragment to DSP */
+ if (DspBits == 8) { /* 8 bit transfer */
+ /* #bytes - 1 */
+ dsp_command((DspFragmentSize - 1) >> 0);
+ dsp_command((DspFragmentSize - 1) >> 8);
+ } else { /* 16 bit transfer */
+ /* #words - 1 */
+ dsp_command((DspFragmentSize - 1) >> 1);
+ dsp_command((DspFragmentSize - 1) >> 9);
+ }
+
+ running = TRUE;
+
+ return OK;
+}
+
+
+
+PUBLIC int drv_stop(int sub_dev) {
+ if(running) {
+ dprint("drv_stop():\n");
+ dsp_command((DspBits == 8 ? DSP_CMD_DMA8HALT : DSP_CMD_DMA16HALT));
+ running = FALSE;
+ drv_reenable_int(sub_dev);
+ }
+ return OK;
+}
+
+
+
+PUBLIC int drv_set_dma(u32_t dma, u32_t length, int chan) {
+ dprint("drv_set_dma():\n");
+ DmaPhys = dma;
+ return OK;
+}
+
+
+
+PUBLIC int drv_reenable_int(int chan) {
+ dprint("drv_reenable_int()\n");
+ sb16_inb((DspBits == 8 ? DSP_DATA_AVL : DSP_DATA16_AVL));
+ return OK;
+}
+
+
+
+PUBLIC int drv_int_sum(void) {
+ return mixer_get(MIXER_IRQ_STATUS) & 0x0F;
+}
+
+
+
+PUBLIC int drv_int(int sub_dev) {
+ return sub_dev == AUDIO && mixer_get(MIXER_IRQ_STATUS) & 0x03;
+}
+
+
+
+PUBLIC int drv_pause(int chan) {
+ drv_stop(chan);
+ return OK;
+}
+
+
+
+PUBLIC int drv_resume(int chan) {
+ dsp_command((DspBits == 8 ? DSP_CMD_DMA8CONT : DSP_CMD_DMA16CONT));
+ return OK;
+}
+
+
+
+PUBLIC int drv_io_ctl(int request, void *val, int *len, int sub_dev) {
+ dprint("dsp_ioctl: got ioctl %d, argument: %d sub_dev: %d\n", request, val, sub_dev);
+
+ if(sub_dev == AUDIO) {
+ return dsp_ioctl(request, val, len);
+ } else if(sub_dev == MIXER) {
+ return mixer_ioctl(request, val, len);
+ }
+
+ return EIO;
+}
+
+
+
+PUBLIC int drv_get_irq(char *irq) {
+ dprint("drv_get_irq():\n");
+ *irq = SB_IRQ;
+ return OK;
+}
+
+
+
+PUBLIC int drv_get_frag_size(u32_t *frag_size, int sub_dev) {
+ dprint("drv_get_frag_size():\n");
+ *frag_size = DspFragmentSize;
+ return OK;
+}
+
+
+
+PRIVATE int dsp_ioctl(int request, void *val, int *len) {
+ int status;
+
+ switch(request) {
+ case DSPIORATE: status = dsp_set_speed(*((u32_t*) val)); break;
+ case DSPIOSTEREO: status = dsp_set_stereo(*((u32_t*) val)); break;
+ case DSPIOBITS: status = dsp_set_bits(*((u32_t*) val)); break;
+ case DSPIOSIZE: status = dsp_set_size(*((u32_t*) val)); break;
+ case DSPIOSIGN: status = dsp_set_sign(*((u32_t*) val)); break;
+ case DSPIOMAX: status = dsp_get_max_frag_size(val, len); break;
+ case DSPIORESET: status = drv_reset(); break;
+ default: status = ENOTTY; break;
+ }
+
+ return status;
+}
+
+
+
+PRIVATE void dsp_dma_setup(phys_bytes address, int count, int DmaMode) {
+ pvb_pair_t pvb[9];
+
+ dprint("Setting up %d bit DMA\n", DspBits);
+
+ if(DspBits == 8) { /* 8 bit sound */
+ count--;
+
+ pv_set(pvb[0], DMA8_MASK, SB_DMA_8 | 0x04); /* Disable DMA channel */
+ pv_set(pvb[1], DMA8_CLEAR, 0x00); /* Clear flip flop */
+
+ /* set DMA mode */
+ pv_set(pvb[2], DMA8_MODE, (DmaMode == DEV_WRITE ? DMA8_AUTO_PLAY : DMA8_AUTO_REC));
+
+ pv_set(pvb[3], DMA8_ADDR, address >> 0); /* Low_byte of address */
+ pv_set(pvb[4], DMA8_ADDR, address >> 8); /* High byte of address */
+ pv_set(pvb[5], DMA8_PAGE, address >> 16); /* 64K page number */
+ pv_set(pvb[6], DMA8_COUNT, count >> 0); /* Low byte of count */
+ pv_set(pvb[7], DMA8_COUNT, count >> 8); /* High byte of count */
+ pv_set(pvb[8], DMA8_MASK, SB_DMA_8); /* Enable DMA channel */
+
+ sys_voutb(pvb, 9);
+ } else { /* 16 bit sound */
+ count -= 2;
+
+ pv_set(pvb[0], DMA16_MASK, (SB_DMA_16 & 3) | 0x04); /* Disable DMA channel */
+
+ pv_set(pvb[1], DMA16_CLEAR, 0x00); /* Clear flip flop */
+
+ /* Set dma mode */
+ pv_set(pvb[2], DMA16_MODE, (DmaMode == DEV_WRITE ? DMA16_AUTO_PLAY : DMA16_AUTO_REC));
+
+ pv_set(pvb[3], DMA16_ADDR, (address >> 1) & 0xFF); /* Low_byte of address */
+ pv_set(pvb[4], DMA16_ADDR, (address >> 9) & 0xFF); /* High byte of address */
+ pv_set(pvb[5], DMA16_PAGE, (address >> 16) & 0xFE); /* 128K page number */
+ pv_set(pvb[6], DMA16_COUNT, count >> 1); /* Low byte of count */
+ pv_set(pvb[7], DMA16_COUNT, count >> 9); /* High byte of count */
+ pv_set(pvb[8], DMA16_MASK, SB_DMA_16 & 3); /* Enable DMA channel */
+
+ sys_voutb(pvb, 9);
+ }
+}
+
+
+
+PRIVATE int dsp_set_size(unsigned int size) {
+ dprint("dsp_set_size(): set fragment size to %u\n", size);
+
+ /* Sanity checks */
+ if(size < sub_dev[AUDIO].MinFragmentSize || size > sub_dev[AUDIO].DmaSize / sub_dev[AUDIO].NrOfDmaFragments || size % 2 != 0) {
+ return EINVAL;
+ }
+
+ DspFragmentSize = size;
+
+ return OK;
+}
+
+
+
+PRIVATE int dsp_set_speed(unsigned int speed) {
+ dprint("sb16: setting speed to %u, stereo = %d\n", speed, DspStereo);
+
+ if(speed < DSP_MIN_SPEED || speed > DSP_MAX_SPEED) {
+ return EPERM;
+ }
+
+ /* Soundblaster 16 can be programmed with real sample rates
+ * instead of time constants
+ *
+ * Since you cannot sample and play at the same time
+ * we set in- and output rate to the same value
+ */
+
+ dsp_command(DSP_INPUT_RATE); /* set input rate */
+ dsp_command(speed >> 8); /* high byte of speed */
+ dsp_command(speed); /* low byte of speed */
+ dsp_command(DSP_OUTPUT_RATE); /* same for output rate */
+ dsp_command(speed >> 8);
+ dsp_command(speed);
+
+ DspSpeed = speed;
+
+ return OK;
+}
+
+
+
+PRIVATE int dsp_set_stereo(unsigned int stereo) {
+ if(stereo) {
+ DspStereo = 1;
+ } else {
+ DspStereo = 0;
+ }
+
+ return OK;
+}
+
+
+
+PRIVATE int dsp_set_bits(unsigned int bits) {
+ /* Sanity checks */
+ if(bits != 8 && bits != 16) {
+ return EINVAL;
+ }
+
+ DspBits = bits;
+
+ return OK;
+}
+
+
+
+PRIVATE int dsp_set_sign(unsigned int sign) {
+ dprint("sb16: set sign to %u\n", sign);
+
+ DspSign = (sign > 0 ? 1 : 0);
+
+ return OK;
+}
+
+
+
+PRIVATE int dsp_get_max_frag_size(u32_t *val, int *len) {
+ *len = sizeof(*val);
+ *val = sub_dev[AUDIO].DmaSize / sub_dev[AUDIO].NrOfDmaFragments;
+ return OK;
+}
+
+
+
+PUBLIC int dsp_command(int value) {
+ int i, status;
+
+ for (i = 0; i < SB_TIMEOUT; i++) {
+ if((sb16_inb(DSP_STATUS) & 0x80) == 0) {
+ sb16_outb(DSP_COMMAND, value);
+ return OK;
+ }
+ }
+
+ dprint("sb16: SoundBlaster: DSP Command(%x) timeout\n", value);
+ return -1;
+}
+
+
+
+PUBLIC int sb16_inb(int port) {
+ int s, value = -1;
+
+ if ((s=sys_inb(port, &value)) != OK)
+ panic("SB16DSP","sys_inb() failed", s);
+
+ return value;
+}
+
+
+
+PUBLIC void sb16_outb(int port, int value) {
+ int s;
+
+ if ((s=sys_outb(port, value)) != OK)
+ panic("SB16DSP","sys_outb() failed", s);
+}
\ No newline at end of file
--- /dev/null
+#ifndef SB16_H
+#define SB16_H
+
+#include <minix/sound.h>
+#include "../framework/audio_fw.h"
+
+#define AUDIO 0
+#define MIXER 1
+
+#define SB_TIMEOUT 32000 /* timeout count */
+
+/* IRQ, base address and DMA channels */
+#define SB_IRQ 7
+#define SB_BASE_ADDR 0x220 /* 0x210, 0x220, 0x230, 0x240,
+ * 0x250, 0x260, 0x280
+ */
+#define SB_DMA_8 1 /* 0, 1, 3 */
+#define SB_DMA_16 5 /* 5, 6, 7 */
+
+/* Some defaults for the DSP */
+#define DEFAULT_SPEED 22050 /* Sample rate */
+#define DEFAULT_BITS 8 /* Nr. of bits */
+#define DEFAULT_SIGN 0 /* 0 = unsigned, 1 = signed */
+#define DEFAULT_STEREO 0 /* 0 = mono, 1 = stereo */
+
+/* DMA port addresses */
+#define DMA8_ADDR ((SB_DMA_8 & 3) << 1) + 0x00
+#define DMA8_COUNT ((SB_DMA_8 & 3) << 1) + 0x01
+#define DMA8_MASK 0x0A
+#define DMA8_MODE 0x0B
+#define DMA8_CLEAR 0x0C
+
+
+/* If after this preprocessing stuff DMA8_PAGE is not defined
+ * the 8-bit DMA channel specified is not valid
+ */
+#if SB_DMA_8 == 0
+# define DMA8_PAGE 0x87
+#else
+# if SB_DMA_8 == 1
+# define DMA8_PAGE 0x83
+# else
+# if SB_DMA_8 == 3
+# define DMA8_PAGE 0x82
+# endif
+# endif
+#endif
+
+
+#define DMA16_ADDR ((SB_DMA_16 & 3) << 2) + 0xC0
+#define DMA16_COUNT ((SB_DMA_16 & 3) << 2) + 0xC2
+#define DMA16_MASK 0xD4
+#define DMA16_MODE 0xD6
+#define DMA16_CLEAR 0xD8
+
+
+/* If after this preprocessing stuff DMA16_PAGE is not defined
+ * the 16-bit DMA channel specified is not valid
+ */
+#if SB_DMA_16 == 5
+# define DMA16_PAGE 0x8B
+#else
+# if SB_DMA_16 == 6
+# define DMA16_PAGE 0x89
+# else
+# if SB_DMA_16 == 7
+# define DMA16_PAGE 0x8A
+# endif
+# endif
+#endif
+
+
+/* DMA modes */
+#define DMA16_AUTO_PLAY 0x58 + (SB_DMA_16 & 3)
+#define DMA16_AUTO_REC 0x54 + (SB_DMA_16 & 3)
+#define DMA8_AUTO_PLAY 0x58 + SB_DMA_8
+#define DMA8_AUTO_REC 0x54 + SB_DMA_8
+
+
+/* IO ports for soundblaster */
+#define DSP_RESET 0x6 + SB_BASE_ADDR
+#define DSP_READ 0xA + SB_BASE_ADDR
+#define DSP_WRITE 0xC + SB_BASE_ADDR
+#define DSP_COMMAND 0xC + SB_BASE_ADDR
+#define DSP_STATUS 0xC + SB_BASE_ADDR
+#define DSP_DATA_AVL 0xE + SB_BASE_ADDR
+#define DSP_DATA16_AVL 0xF + SB_BASE_ADDR
+#define MIXER_REG 0x4 + SB_BASE_ADDR
+#define MIXER_DATA 0x5 + SB_BASE_ADDR
+#define OPL3_LEFT 0x0 + SB_BASE_ADDR
+#define OPL3_RIGHT 0x2 + SB_BASE_ADDR
+#define OPL3_BOTH 0x8 + SB_BASE_ADDR
+
+
+/* DSP Commands */
+#define DSP_INPUT_RATE 0x42 /* set input sample rate */
+#define DSP_OUTPUT_RATE 0x41 /* set output sample rate */
+#define DSP_CMD_SPKON 0xD1 /* set speaker on */
+#define DSP_CMD_SPKOFF 0xD3 /* set speaker off */
+#define DSP_CMD_DMA8HALT 0xD0 /* halt DMA 8-bit operation */
+#define DSP_CMD_DMA8CONT 0xD4 /* continue DMA 8-bit operation */
+#define DSP_CMD_DMA16HALT 0xD5 /* halt DMA 16-bit operation */
+#define DSP_CMD_DMA16CONT 0xD6 /* continue DMA 16-bit operation */
+#define DSP_GET_VERSION 0xE1 /* get version number of DSP */
+#define DSP_CMD_8BITAUTO_IN 0xCE /* 8 bit auto-initialized input */
+#define DSP_CMD_8BITAUTO_OUT 0xC6 /* 8 bit auto-initialized output */
+#define DSP_CMD_16BITAUTO_IN 0xBE /* 16 bit auto-initialized input */
+#define DSP_CMD_16BITAUTO_OUT 0xB6 /* 16 bit auto-initialized output */
+#define DSP_CMD_IRQREQ8 0xF2 /* Interrupt request 8 bit */
+#define DSP_CMD_IRQREQ16 0xF3 /* Interrupt request 16 bit */
+
+
+/* DSP Modes */
+#define DSP_MODE_MONO_US 0x00 /* Mono unsigned */
+#define DSP_MODE_MONO_S 0x10 /* Mono signed */
+#define DSP_MODE_STEREO_US 0x20 /* Stereo unsigned */
+#define DSP_MODE_STEREO_S 0x30 /* Stereo signed */
+
+
+/* MIXER commands */
+#define MIXER_RESET 0x00 /* Reset */
+#define MIXER_DAC_LEVEL 0x04 /* Used for detection only */
+#define MIXER_MASTER_LEFT 0x30 /* Master volume left */
+#define MIXER_MASTER_RIGHT 0x31 /* Master volume right */
+#define MIXER_DAC_LEFT 0x32 /* Dac level left */
+#define MIXER_DAC_RIGHT 0x33 /* Dac level right */
+#define MIXER_FM_LEFT 0x34 /* Fm level left */
+#define MIXER_FM_RIGHT 0x35 /* Fm level right */
+#define MIXER_CD_LEFT 0x36 /* Cd audio level left */
+#define MIXER_CD_RIGHT 0x37 /* Cd audio level right */
+#define MIXER_LINE_LEFT 0x38 /* Line in level left */
+#define MIXER_LINE_RIGHT 0x39 /* Line in level right */
+#define MIXER_MIC_LEVEL 0x3A /* Microphone level */
+#define MIXER_PC_LEVEL 0x3B /* Pc speaker level */
+#define MIXER_OUTPUT_CTRL 0x3C /* Output control */
+#define MIXER_IN_LEFT 0x3D /* Input control left */
+#define MIXER_IN_RIGHT 0x3E /* Input control right */
+#define MIXER_GAIN_IN_LEFT 0x3F /* Input gain control left */
+#define MIXER_GAIN_IN_RIGHT 0x40 /* Input gain control right */
+#define MIXER_GAIN_OUT_LEFT 0x41 /* Output gain control left */
+#define MIXER_GAIN_OUT_RIGHT 0x42 /* Output gain control rigth */
+#define MIXER_AGC 0x43 /* Automatic gain control */
+#define MIXER_TREBLE_LEFT 0x44 /* Treble left */
+#define MIXER_TREBLE_RIGHT 0x45 /* Treble right */
+#define MIXER_BASS_LEFT 0x46 /* Bass left */
+#define MIXER_BASS_RIGHT 0x47 /* Bass right */
+#define MIXER_SET_IRQ 0x80 /* Set irq number */
+#define MIXER_SET_DMA 0x81 /* Set DMA channels */
+#define MIXER_IRQ_STATUS 0x82 /* Irq status */
+
+/* Mixer constants */
+#define MIC 0x01 /* Microphone */
+#define CD_RIGHT 0x02
+#define CD_LEFT 0x04
+#define LINE_RIGHT 0x08
+#define LINE_LEFT 0x10
+#define FM_RIGHT 0x20
+#define FM_LEFT 0x40
+
+/* DSP constants */
+#define DSP_MAX_SPEED 44100 /* Max sample speed in KHz */
+#define DSP_MIN_SPEED 4000 /* Min sample speed in KHz */
+
+
+/* Number of bytes you can DMA before hitting a 64K boundary: */
+#define dma_bytes_left(phys) \
+ ((unsigned) (sizeof(int) == 2 ? 0 : 0x10000) - (unsigned) ((phys) & 0xFFFF))
+
+_PROTOTYPE( int dsp_command, (int value) );
+_PROTOTYPE( int sb16_inb, (int port) );
+_PROTOTYPE( void sb16_outb, (int port, int value) );
+
+#endif /* SB16_H */
#define MIXIOSETINPUTRIGHT _IORW('s', 22, struct inout_ctrl)
#define MIXIOSETOUTPUT _IORW('s', 23, struct inout_ctrl)
+#define AC97READ _IOW('s', 8, u16_t[2])
+#define AC97WRITE _IOR('s', 9, u16_t[2])
+
#endif /* _S_I_SOUND_H */