]> Zhao Yanbai Git Server - minix.git/commitdiff
audio drivers. (not updated for trunk.)
authorBen Gras <ben@minix3.org>
Fri, 23 Nov 2007 11:30:50 +0000 (11:30 +0000)
committerBen Gras <ben@minix3.org>
Fri, 23 Nov 2007 11:30:50 +0000 (11:30 +0000)
sb16: port of isa sb16 driver to user-space. Port by Peter Boonstoppel.
es1371: By Laurens Bronwasser.

22 files changed:
drivers/audio/AC97.h [new file with mode: 0755]
drivers/audio/README [new file with mode: 0644]
drivers/audio/es1371/.depend [new file with mode: 0755]
drivers/audio/es1371/Makefile [new file with mode: 0755]
drivers/audio/es1371/SRC.c [new file with mode: 0755]
drivers/audio/es1371/SRC.h [new file with mode: 0755]
drivers/audio/es1371/codec.c [new file with mode: 0755]
drivers/audio/es1371/codec.h [new file with mode: 0755]
drivers/audio/es1371/es1371.c [new file with mode: 0755]
drivers/audio/es1371/es1371.h [new file with mode: 0755]
drivers/audio/es1371/wait.c [new file with mode: 0755]
drivers/audio/es1371/wait.h [new file with mode: 0755]
drivers/audio/framework/Makefile [new file with mode: 0644]
drivers/audio/framework/audio_fw.c [new file with mode: 0755]
drivers/audio/framework/audio_fw.h [new file with mode: 0755]
drivers/audio/sb16/.depend [new file with mode: 0755]
drivers/audio/sb16/Makefile [new file with mode: 0755]
drivers/audio/sb16/mixer.c [new file with mode: 0755]
drivers/audio/sb16/mixer.h [new file with mode: 0644]
drivers/audio/sb16/sb16.c [new file with mode: 0755]
drivers/audio/sb16/sb16.h [new file with mode: 0755]
include/sys/ioc_sound.h

diff --git a/drivers/audio/AC97.h b/drivers/audio/AC97.h
new file mode 100755 (executable)
index 0000000..bcb8945
--- /dev/null
@@ -0,0 +1,74 @@
+#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 */
diff --git a/drivers/audio/README b/drivers/audio/README
new file mode 100644 (file)
index 0000000..b60763d
--- /dev/null
@@ -0,0 +1,34 @@
+
+***** 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)
diff --git a/drivers/audio/es1371/.depend b/drivers/audio/es1371/.depend
new file mode 100755 (executable)
index 0000000..deb8a33
--- /dev/null
@@ -0,0 +1,138 @@
+
+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
diff --git a/drivers/audio/es1371/Makefile b/drivers/audio/es1371/Makefile
new file mode 100755 (executable)
index 0000000..3d32211
--- /dev/null
@@ -0,0 +1,44 @@
+# 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
+
diff --git a/drivers/audio/es1371/SRC.c b/drivers/audio/es1371/SRC.c
new file mode 100755 (executable)
index 0000000..6fec22e
--- /dev/null
@@ -0,0 +1,196 @@
+#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;
+}
+
+
diff --git a/drivers/audio/es1371/SRC.h b/drivers/audio/es1371/SRC.h
new file mode 100755 (executable)
index 0000000..7faddbe
--- /dev/null
@@ -0,0 +1,43 @@
+#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 */
diff --git a/drivers/audio/es1371/codec.c b/drivers/audio/es1371/codec.c
new file mode 100755 (executable)
index 0000000..8a110c5
--- /dev/null
@@ -0,0 +1,264 @@
+#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;
+}
+
+
+
+
+
+  
diff --git a/drivers/audio/es1371/codec.h b/drivers/audio/es1371/codec.h
new file mode 100755 (executable)
index 0000000..beba385
--- /dev/null
@@ -0,0 +1,44 @@
+#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 */
diff --git a/drivers/audio/es1371/es1371.c b/drivers/audio/es1371/es1371.c
new file mode 100755 (executable)
index 0000000..f83d503
--- /dev/null
@@ -0,0 +1,588 @@
+/* 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);
+}
diff --git a/drivers/audio/es1371/es1371.h b/drivers/audio/es1371/es1371.h
new file mode 100755 (executable)
index 0000000..309b1c0
--- /dev/null
@@ -0,0 +1,138 @@
+#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 */
diff --git a/drivers/audio/es1371/wait.c b/drivers/audio/es1371/wait.c
new file mode 100755 (executable)
index 0000000..00db6bf
--- /dev/null
@@ -0,0 +1,27 @@
+#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;
+}
diff --git a/drivers/audio/es1371/wait.h b/drivers/audio/es1371/wait.h
new file mode 100755 (executable)
index 0000000..5df6a39
--- /dev/null
@@ -0,0 +1,10 @@
+/* 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);
+
diff --git a/drivers/audio/framework/Makefile b/drivers/audio/framework/Makefile
new file mode 100644 (file)
index 0000000..4273ada
--- /dev/null
@@ -0,0 +1,9 @@
+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
diff --git a/drivers/audio/framework/audio_fw.c b/drivers/audio/framework/audio_fw.c
new file mode 100755 (executable)
index 0000000..bd38099
--- /dev/null
@@ -0,0 +1,825 @@
+/* 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
diff --git a/drivers/audio/framework/audio_fw.h b/drivers/audio/framework/audio_fw.h
new file mode 100755 (executable)
index 0000000..bad3280
--- /dev/null
@@ -0,0 +1,96 @@
+#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 */
diff --git a/drivers/audio/sb16/.depend b/drivers/audio/sb16/.depend
new file mode 100755 (executable)
index 0000000..418f83b
--- /dev/null
@@ -0,0 +1,68 @@
+
+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
diff --git a/drivers/audio/sb16/Makefile b/drivers/audio/sb16/Makefile
new file mode 100755 (executable)
index 0000000..ffcdd39
--- /dev/null
@@ -0,0 +1,41 @@
+# 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
+
diff --git a/drivers/audio/sb16/mixer.c b/drivers/audio/sb16/mixer.c
new file mode 100755 (executable)
index 0000000..770af03
--- /dev/null
@@ -0,0 +1,253 @@
+#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
diff --git a/drivers/audio/sb16/mixer.h b/drivers/audio/sb16/mixer.h
new file mode 100644 (file)
index 0000000..687a8aa
--- /dev/null
@@ -0,0 +1,10 @@
+#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
diff --git a/drivers/audio/sb16/sb16.c b/drivers/audio/sb16/sb16.c
new file mode 100755 (executable)
index 0000000..0e2a0c6
--- /dev/null
@@ -0,0 +1,445 @@
+/*  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
diff --git a/drivers/audio/sb16/sb16.h b/drivers/audio/sb16/sb16.h
new file mode 100755 (executable)
index 0000000..75ae7e2
--- /dev/null
@@ -0,0 +1,173 @@
+#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 */
index eb033848811c8896b12ebd099ca7660c7c763f0e..59ed32a1d30a683312401e6a6b3585cc68c638e9 100755 (executable)
@@ -27,4 +27,7 @@
 #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 */