]> Zhao Yanbai Git Server - minix.git/commitdiff
Update by paboonst@cs.vu.nl
authorBen Gras <ben@minix3.org>
Fri, 16 Sep 2005 11:09:08 +0000 (11:09 +0000)
committerBen Gras <ben@minix3.org>
Fri, 16 Sep 2005 11:09:08 +0000 (11:09 +0000)
drivers/sb16/Makefile
drivers/sb16/README [new file with mode: 0644]
drivers/sb16/sb16.c [new file with mode: 0644]
drivers/sb16/sb16.h
drivers/sb16/sb16.tar [new file with mode: 0644]
drivers/sb16/sb16_dsp.c
drivers/sb16/sb16_mixer.c [new file with mode: 0644]

index 71070d04608b8df251cebfc9f3911bfc1bee2317..24390b971868b05482f4b84d159c505d364e19b4 100644 (file)
@@ -1,5 +1,4 @@
 # Makefile for the Sound Blaster 16 driver (SB16)
-DRIVER = sb16_dsp
 
 # directories
 u = /usr
@@ -15,19 +14,20 @@ CFLAGS = -I$i
 LDFLAGS = -i
 LIBS = -lsys -lsysutil
 
-OBJ = sb16_dsp.o
 
 # build local binary
-all build:     $(DRIVER)
-$(DRIVER):     $(OBJ)
-       $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS)
-#      install -S 256w $(DRIVER)
+all build:     sb16_dsp sb16_mixer
+sb16_dsp:      sb16.o sb16_dsp.o
+       $(CC) -o $@ $(LDFLAGS) sb16.o sb16_dsp.o $(LIBS)
+sb16_mixer:    sb16.o sb16_mixer.o
+       $(CC) -o $@ $(LDFLAGS) sb16.o sb16_mixer.o $(LIBS)
 
 # install with other drivers
-install:       /usr/sbin/$(DRIVER)
-/usr/sbin/$(DRIVER):   $(DRIVER)
+install:       /usr/sbin/sb16_dsp /usr/sbin/sb16_mixer
+/usr/sbin/sb16_dsp:    sb16_dsp
+       install -o root -c $? $@
+/usr/sbin/sb16_mixer:  sb16_mixer
        install -o root -c $? $@
-#      install -o root -cs $? $@
 
 # clean up local files
 clean:
diff --git a/drivers/sb16/README b/drivers/sb16/README
new file mode 100644 (file)
index 0000000..08e62a2
--- /dev/null
@@ -0,0 +1,21 @@
+
+Sound Blaster 16 ISA driver for Minix 3
+
+Note: supports audio playback and volume control (mixer),
+recording is not yet supported
+
+
+Installation instructions SB16 driver Minix >= 3.0.7
+
+- set IRQ and I/O address in sb16.h
+(default 7 and 220)
+- make install
+- MAKEDEV /dev/audio (if /dev/audio doesn't allready exist)
+- service up /usr/sbin/sb16_dsp -dev /dev/audio
+- service up /usr/sbin/sb16_mixer -dev /dev/mixer
+done... (you can include the last 2 lines in /usr/etc/rc)
+
+
+Peter Boonstoppel - September 2005
+paboonst@cs.vu.nl
+
diff --git a/drivers/sb16/sb16.c b/drivers/sb16/sb16.c
new file mode 100644 (file)
index 0000000..3257fa9
--- /dev/null
@@ -0,0 +1,46 @@
+#include "sb16.h"
+
+/*===========================================================================*
+ *                             mixer_set
+ *===========================================================================*/
+PUBLIC int mixer_set(reg, data) 
+int reg;
+int data;
+{
+       int i;
+
+       sb16_outb(MIXER_REG, reg);
+       for(i = 0; i < 100; i++);
+       sb16_outb(MIXER_DATA, data);
+
+       return OK;
+}
+
+
+/*===========================================================================*
+ *                             sb16_inb
+ *===========================================================================*/
+PUBLIC int sb16_inb(port)
+int port;
+{      
+       int s, value = -1;
+
+       if ((s=sys_inb(port, &value)) != OK)
+               panic("SB16DSP","sys_inb() failed", s);
+       
+       return value;
+}
+
+
+/*===========================================================================*
+ *                             sb16_outb
+ *===========================================================================*/
+PUBLIC void sb16_outb(port, value)
+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
index ae1becd64fa1a3c958b6e2bd07ffd50a88b0dc06..3396743c1b6611d5631257b856b5202283b2263c 100644 (file)
@@ -1,8 +1,10 @@
 #ifndef SB16_H
 #define SB16_H
 
-#define SB_DEBUG               0       /* 1 = print debug info */
-#define SB_DEBUG_2             0       /* 1 = print more debug info */
+#include "../drivers.h"
+#include <sys/ioc_sound.h>
+#include <minix/sound.h>
+
 
 #define SB_TIMEOUT             32000   /* timeout count */
 
@@ -32,6 +34,7 @@
 #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
  */
@@ -54,6 +57,7 @@
 #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
  */
 #  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
@@ -89,6 +95,7 @@
 #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_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 DSP_MIN_FRAGMENT_SIZE  1024       /* Minimum fragment size */
 #define DSP_NR_OF_BUFFERS              8
 
+
 /* 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 mixer_set, (int reg, int data));
+_PROTOTYPE( int sb16_inb, (int port) );
+_PROTOTYPE( void sb16_outb, (int port, int value) );
+
+
 #endif /* SB16_H */
diff --git a/drivers/sb16/sb16.tar b/drivers/sb16/sb16.tar
new file mode 100644 (file)
index 0000000..854c41f
Binary files /dev/null and b/drivers/sb16/sb16.tar differ
index 4e82d8b18dbdd145fd7421b7cf61b71e0d2dd81d..e378acbf539889c3f9ed7af1ad8584f5ff8764fa 100644 (file)
@@ -1,6 +1,31 @@
+/* This file contains the driver for a DSP (Digital Sound Processor) on
+ * a SoundBlaster 16 soundcard.
+ *
+ * The driver supports the following operations (using message format m2):
+ *
+ *    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 |
+ * ----------------------------------------------------------------
+ *
+ * The file contains one entry point:
+ *
+ *   main:     main entry when driver is brought up
+ *     
+ *  August 24 2005             Ported driver to user space (only audio playback) (Peter Boonstoppel)
+ *  May 20 1995                        Author: Michel R. Prevenier 
+ */
+
 #include "sb16.h"
-#include "../drivers.h"
-#include <sys/ioc_sound.h>
+
 
 _PROTOTYPE(void main, (void));
 FORWARD _PROTOTYPE( int dsp_open, (void) );
@@ -12,8 +37,6 @@ FORWARD _PROTOTYPE( void dsp_status, (message *m_ptr) );
 
 FORWARD _PROTOTYPE( void reply, (int code, int replyee, int process, int status) );
 FORWARD _PROTOTYPE( void init_buffer, (void) );
-FORWARD _PROTOTYPE( int dsp_inb, (int port) );
-FORWARD _PROTOTYPE( void dsp_outb, (int port, int value) );
 FORWARD _PROTOTYPE( int dsp_init, (void) );
 FORWARD _PROTOTYPE( int dsp_reset, (void) );
 FORWARD _PROTOTYPE( int dsp_command, (int value) );
@@ -24,7 +47,6 @@ FORWARD _PROTOTYPE( int dsp_set_bits, (unsigned int bits) );
 FORWARD _PROTOTYPE( int dsp_set_sign, (unsigned int sign) );
 FORWARD _PROTOTYPE( void dsp_dma_setup, (phys_bytes address, int count) );
 FORWARD _PROTOTYPE( void dsp_setup, (void) );
-_PROTOTYPE(int mixer_set, (int reg, int data));
 
 PRIVATE int irq_hook_id;       /* id of irq hook at the kernel */
 
@@ -42,7 +64,6 @@ PRIVATE unsigned int DspSign = DEFAULT_SIGN;
 PRIVATE unsigned int DspFragmentSize = DSP_MAX_FRAGMENT_SIZE;
 PRIVATE int DspAvail = 0;
 PRIVATE int DspBusy = 0;
-PRIVATE int DmaDone = 1;
 PRIVATE int DmaMode = 0;
 PRIVATE int DmaBusy = -1;
 PRIVATE int DmaFillNext = 0;
@@ -55,6 +76,7 @@ PRIVATE int reviveProcNr;
 
 #define dprint (void)
 
+
 /*===========================================================================*
  *                             main
  *===========================================================================*/
@@ -96,6 +118,7 @@ PUBLIC void main()
 
 }
 
+
 /*===========================================================================*
  *                             dsp_open
  *===========================================================================*/
@@ -124,6 +147,7 @@ PRIVATE int dsp_open()
        return OK;
 }
 
+
 /*===========================================================================*
  *                             dsp_close
  *===========================================================================*/
@@ -136,6 +160,7 @@ PRIVATE int dsp_close()
        return OK;
 }
 
+
 /*===========================================================================*
  *                             dsp_ioctl
  *===========================================================================*/
@@ -176,6 +201,7 @@ message *m_ptr;
        return status;
 }
 
+
 /*===========================================================================*
  *                             dsp_write
  *===========================================================================*/
@@ -191,6 +217,10 @@ message *m_ptr;
                reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINVAL);
                return;
        }
+       if(m_ptr->m_type != DmaMode && DmaBusy >= 0) {
+               reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EBUSY);
+               return;
+       }
        
        reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, SUSPEND);
 
@@ -235,6 +265,7 @@ message *m_ptr;
        notify(m_ptr->m_source);
 }
 
+
 /*===========================================================================*
  *                             dsp_hardware_msg
  *===========================================================================*/
@@ -245,10 +276,16 @@ PRIVATE void dsp_hardware_msg()
                dprint("Finished playing dma[%d]; ", DmaBusy);
                DmaBusy = (DmaBusy + 1) % DMA_NR_OF_BUFFERS;
                if(DmaBusy == DmaFillNext) { /* Dma buffer empty, stop Dma transfer */
+
                        dsp_command((DspBits == 8 ? DSP_CMD_DMA8HALT : DSP_CMD_DMA16HALT));
                        dprint("No more work...!\n");
                        DmaBusy = -1;
+
                } else if(BufReadNext >= 0) { /* Data in second buffer, copy one fragment to Dma buffer */
+                       
+                       /* Acknowledge the interrupt on the DSP */
+                       sb16_inb((DspBits == 8 ? DSP_DATA_AVL : DSP_DATA16_AVL));
+
                        memcpy(DmaPtr + DmaFillNext * DspFragmentSize, Buffer + BufReadNext * DspFragmentSize, DspFragmentSize);
                        dprint("copy buf[%d] -> dma[%d]; ", BufReadNext, DmaFillNext);
                        BufReadNext = (BufReadNext + 1) % DSP_NR_OF_BUFFERS;
@@ -257,15 +294,19 @@ PRIVATE void dsp_hardware_msg()
                                BufReadNext = -1;
                        } 
                        dprint("Starting dma[%d]\n", DmaBusy);
+                       
+                       return;
+
                } else { /* Second buffer empty, still data in Dma buffer, continue playback */
                        dprint("Starting dma[%d]\n", DmaBusy);
                }
        }
 
        /* Acknowledge the interrupt on the DSP */
-       dsp_inb((DspBits == 8 ? DSP_DATA_AVL : DSP_DATA16_AVL));
+       sb16_inb((DspBits == 8 ? DSP_DATA_AVL : DSP_DATA16_AVL));
 }
 
+
 /*===========================================================================*
  *                             dsp_status                                   *
  *===========================================================================*/
@@ -285,6 +326,7 @@ message *m_ptr;     /* pointer to the newly arrived message */
        send(m_ptr->m_source, m_ptr);                   /* send the message */
 }
 
+
 /*===========================================================================*
  *                             reply                                        *
  *===========================================================================*/
@@ -303,6 +345,7 @@ int status;
        send(replyee, &m);
 }
 
+
 /*===========================================================================*
  *                             init_buffer
  *===========================================================================*/
@@ -328,32 +371,6 @@ PRIVATE void init_buffer()
 #endif /* CHIP == INTEL */
 }
 
-/*===========================================================================*
- *                             dsp_inb
- *===========================================================================*/
-PRIVATE int dsp_inb(port)
-int port;
-{      
-       int s, value = -1;
-
-       if ((s=sys_inb(port, &value)) != OK)
-               panic("SB16DSP","sys_inb() failed", s);
-       
-       return value;
-}
-
-/*===========================================================================*
- *                             dsp_outb
- *===========================================================================*/
-PRIVATE void dsp_outb(port, value)
-int port;
-int value;
-{
-       int s;
-       
-       if ((s=sys_outb(port, value)) != OK)
-               panic("SB16DSP","sys_outb() failed", s);
-}
 
 /*===========================================================================*
  *                             dsp_init
@@ -371,11 +388,11 @@ PRIVATE int dsp_init()
        dsp_command(DSP_GET_VERSION);   /* Get DSP version bytes */
 
        for(i = 1000; i; i--) {
-               if(dsp_inb(DSP_DATA_AVL) & 0x80) {              
+               if(sb16_inb(DSP_DATA_AVL) & 0x80) {             
                        if(DspVersion[0] == 0) {
-                               DspVersion[0] = dsp_inb(DSP_READ);
+                               DspVersion[0] = sb16_inb(DSP_READ);
                        } else {
-                               DspVersion[1] = dsp_inb(DSP_READ);
+                               DspVersion[1] = sb16_inb(DSP_READ);
                                break;
                        }
                }
@@ -402,6 +419,7 @@ PRIVATE int dsp_init()
        return OK;
 }
 
+
 /*===========================================================================*
  *                             dsp_reset
  *===========================================================================*/
@@ -409,20 +427,20 @@ PRIVATE int dsp_reset()
 {
        int i;
 
-       dsp_outb(DSP_RESET, 1);
+       sb16_outb(DSP_RESET, 1);
        for(i = 0; i < 1000; i++); /* wait a while */
-       dsp_outb(DSP_RESET, 0);
+       sb16_outb(DSP_RESET, 0);
 
-       for(i = 0; i < 1000 && !(dsp_inb(DSP_DATA_AVL) & 0x80); i++);   
+       for(i = 0; i < 1000 && !(sb16_inb(DSP_DATA_AVL) & 0x80); i++);  
        
-       if(dsp_inb(DSP_READ) != 0xAA) return EIO; /* No SoundBlaster */
+       if(sb16_inb(DSP_READ) != 0xAA) return EIO; /* No SoundBlaster */
 
        DmaBusy = -1;
-       DmaDone = 1;
 
        return OK;
 }
 
+
 /*===========================================================================*
  *                             dsp_command
  *===========================================================================*/
@@ -432,8 +450,8 @@ int value;
        int i, status;
 
        for (i = 0; i < SB_TIMEOUT; i++) {
-               if((dsp_inb(DSP_STATUS) & 0x80) == 0) {
-                       dsp_outb(DSP_COMMAND, value);
+               if((sb16_inb(DSP_STATUS) & 0x80) == 0) {
+                       sb16_outb(DSP_COMMAND, value);
                        return OK;
                }
        }
@@ -442,6 +460,7 @@ int value;
        return -1;
 }
 
+
 /*===========================================================================*
  *                             dsp_set_size
  *===========================================================================*/
@@ -460,6 +479,7 @@ unsigned int size;
        return OK;
 }
 
+
 /*===========================================================================*
  *                             dsp_set_speed
  *===========================================================================*/
@@ -491,6 +511,7 @@ unsigned int speed;
        return OK;
 }
 
+
 /*===========================================================================*
  *                             dsp_set_stereo
  *===========================================================================*/
@@ -506,6 +527,7 @@ unsigned int stereo;
        return OK;
 }
 
+
 /*===========================================================================*
  *                             dsp_set_bits
  *===========================================================================*/
@@ -522,6 +544,7 @@ unsigned int bits;
        return OK;
 }
 
+
 /*===========================================================================*
  *                             dsp_set_sign
  *===========================================================================*/
@@ -535,6 +558,7 @@ unsigned int sign;
        return OK;
 }
 
+
 /*===========================================================================*
  *                             dsp_dma_setup
  *===========================================================================*/
@@ -542,41 +566,50 @@ PRIVATE void dsp_dma_setup(address, count)
 phys_bytes address;
 int count;
 {
+       pvb_pair_t pvb[9];
+
+
        dprint("Setting up %d bit DMA\n", DspBits);
 
        if(DspBits == 8) {   /* 8 bit sound */
                count--;     
 
-               dsp_outb(DMA8_MASK, SB_DMA_8 | 0x04);      /* Disable DMA channel */
-               dsp_outb(DMA8_CLEAR, 0x00);                    /* Clear flip flop */
+               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 */
-               dsp_outb(DMA8_MODE, (DmaMode == DEV_WRITE ? DMA8_AUTO_PLAY : DMA8_AUTO_REC)); 
-
-               dsp_outb(DMA8_ADDR, address >>  0);        /* Low_byte of address */
-               dsp_outb(DMA8_ADDR, address >>  8);        /* High byte of address */
-               dsp_outb(DMA8_PAGE, address >> 16);        /* 64K page number */
-               dsp_outb(DMA8_COUNT, count >> 0);          /* Low byte of count */
-               dsp_outb(DMA8_COUNT, count >> 8);          /* High byte of count */
-               dsp_outb(DMA8_MASK, SB_DMA_8);         /* Enable DMA channel */
+               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;
 
-               dsp_outb(DMA16_MASK, (SB_DMA_16 & 3) | 0x04); /* Disable DMA channel */
-               dsp_outb(DMA16_CLEAR, 0x00);                  /* Clear flip flop */
+               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 */
-               dsp_outb(DMA16_MODE, (DmaMode == DEV_WRITE ? DMA16_AUTO_PLAY : DMA16_AUTO_REC));        
-
-               dsp_outb(DMA16_ADDR, (address >> 1) & 0xFF);  /* Low_byte of address */
-               dsp_outb(DMA16_ADDR, (address >> 9) & 0xFF);  /* High byte of address */
-               dsp_outb(DMA16_PAGE, (address >> 16) & 0xFE); /* 128K page number */
-               dsp_outb(DMA16_COUNT, count >> 1);            /* Low byte of count */
-               dsp_outb(DMA16_COUNT, count >> 9);            /* High byte of count */
-               dsp_outb(DMA16_MASK, SB_DMA_16 & 3);          /* Enable DMA channel */
+               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);
        }
 }
 
+
 /*===========================================================================*
  *                             dsp_setup()
  *===========================================================================*/
@@ -617,18 +650,4 @@ PRIVATE void dsp_setup()
        }
 }
 
-/*===========================================================================*
- *                             mixer_set
- *===========================================================================*/
-PUBLIC int mixer_set(reg, data) 
-int reg;
-int data;
-{
-       int i;
-
-       dsp_outb(MIXER_REG, reg);
-       for(i = 0; i < 100; i++);
-       dsp_outb(MIXER_DATA, data);
-
-       return OK;
-}  
+  
diff --git a/drivers/sb16/sb16_mixer.c b/drivers/sb16/sb16_mixer.c
new file mode 100644 (file)
index 0000000..3ec3097
--- /dev/null
@@ -0,0 +1,387 @@
+/* This file contains the driver for the mixer on
+ * a SoundBlaster 16 soundcard.
+ *
+ * The driver supports the following operations (using message format m2):
+ *
+ *    m_type      DEVICE    PROC_NR     COUNT    POSITION  ADRRESS
+ * ----------------------------------------------------------------
+ * |  DEV_OPEN  | device  | proc nr |         |         |         |
+ * |------------+---------+---------+---------+---------+---------|
+ * |  DEV_CLOSE | device  | proc nr |         |         |         |
+ * |------------+---------+---------+---------+---------+---------|
+ * |  DEV_IOCTL | device  | proc nr |func code|         | buf_ptr |
+ * ----------------------------------------------------------------
+ *
+ * The file contains one entry point:
+ *
+ *   sb16mixer_task:  main entry when system is brought up
+ *
+ *     August 24 2005          Ported driver to user space (Peter Boonstoppel)
+ *  May 20 1995                        Author: Michel R. Prevenier 
+ */
+
+
+#include "sb16.h"
+
+
+_PROTOTYPE(void main, (void));
+FORWARD _PROTOTYPE( int mixer_init, (void)); 
+FORWARD _PROTOTYPE( int mixer_open, (message *m_ptr));
+FORWARD _PROTOTYPE( int mixer_close, (message *m_ptr));
+FORWARD _PROTOTYPE( int mixer_ioctl, (message *m_ptr));
+FORWARD _PROTOTYPE( int mixer_get, (int reg));
+FORWARD _PROTOTYPE( int get_set_volume, (message *m_ptr, int flag));
+FORWARD _PROTOTYPE( int get_set_input, (message *m_ptr, int flag, int channel));
+FORWARD _PROTOTYPE( int get_set_output, (message *m_ptr, int flag));
+
+
+PRIVATE int mixer_avail = 0;   /* Mixer exists? */
+
+
+#define dprint (void)
+
+
+/*===========================================================================*
+ *                             main
+ *===========================================================================*/
+PUBLIC void main() {
+message mess;
+       int err, caller, proc_nr;
+
+       /* Here is the main loop of the mixer task. It waits for a message, carries
+       * it out, and sends a reply.
+       */
+       while (TRUE) {
+               receive(ANY, &mess);
+
+               caller = mess.m_source;
+               proc_nr = mess.PROC_NR;
+
+               switch (caller) {
+                       case HARDWARE: /* Leftover interrupt. */
+                               continue;
+                       case FS_PROC_NR: /* The only legitimate caller. */
+                               break;
+                       default:
+                               dprint("sb16: got message from %d\n", caller);
+                               continue;
+               }
+
+               /* Now carry out the work. */
+               switch(mess.m_type) {
+                       case DEV_OPEN:      err = mixer_open(&mess); break;     
+                       case DEV_CLOSE:     err = mixer_close(&mess); break; 
+                       case DEV_IOCTL:     err = mixer_ioctl(&mess); break;
+                       default:                err = EINVAL; break;
+               }
+
+               /* Finally, prepare and send the reply message. */
+               mess.m_type = TASK_REPLY;
+               mess.REP_PROC_NR = proc_nr;
+       
+               dprint("%d %d", err, OK);
+               
+               mess.REP_STATUS = err;  /* error code */
+               send(caller, &mess);    /* send reply to caller */
+       }
+}
+
+
+/*=========================================================================*
+ *                             mixer_open                                      
+ *=========================================================================*/
+PRIVATE int mixer_open(m_ptr)
+message *m_ptr;
+{
+       dprint("mixer_open\n");
+
+       /* try to detect the mixer type */
+       if (!mixer_avail && mixer_init() != OK) return EIO;
+
+       return OK;
+}
+
+
+/*=========================================================================*
+ *                             mixer_close                                     
+ *=========================================================================*/
+PRIVATE int mixer_close(m_ptr)
+message *m_ptr;
+{
+       dprint("mixer_close\n");
+
+       return OK;
+}
+
+
+/*=========================================================================*
+ *                             mixer_ioctl                                     
+ *=========================================================================*/
+PRIVATE int mixer_ioctl(m_ptr)
+message *m_ptr;
+{
+       int status;
+
+       dprint("mixer: got ioctl %d\n", m_ptr->REQUEST);
+
+
+       switch(m_ptr->REQUEST) {
+               case MIXIOGETVOLUME:      status = get_set_volume(m_ptr, 0); break;
+               case MIXIOSETVOLUME:      status = get_set_volume(m_ptr, 1); break;
+               case MIXIOGETINPUTLEFT:   status = get_set_input(m_ptr, 0, 0); break;
+               case MIXIOGETINPUTRIGHT:  status = get_set_input(m_ptr, 0, 1); break;
+               case MIXIOGETOUTPUT:      status = get_set_output(m_ptr, 0); break;
+               case MIXIOSETINPUTLEFT:   status = get_set_input(m_ptr, 1, 0); break;
+               case MIXIOSETINPUTRIGHT:  status = get_set_input(m_ptr, 1, 1); break;
+               case MIXIOSETOUTPUT:      status = get_set_output(m_ptr, 1); break;
+               default:                  status = ENOTTY;
+       }
+
+       return status;
+}
+
+
+/*=========================================================================*
+ *                             mixer_init                                 
+ *=========================================================================*/
+PRIVATE 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");
+
+       mixer_avail = 1;
+       return OK;
+}
+
+
+/*=========================================================================*
+ *                             mixer_get                                 
+ *=========================================================================*/
+PRIVATE int mixer_get(reg)
+int reg;
+{
+       int i;
+
+       sb16_outb(MIXER_REG, reg);
+       for(i = 0; i < 100; i++);
+       return sb16_inb(MIXER_DATA) & 0xff;
+}  
+
+
+/*=========================================================================*
+ *                             get_set_volume                             *
+ *=========================================================================*/
+PRIVATE int get_set_volume(m_ptr, flag)
+message *m_ptr;
+int flag;      /* 0 = get, 1 = set */
+{
+       phys_bytes user_phys;
+       struct volume_level level;
+       int cmd_left, cmd_right, shift, max_level;
+
+       sys_datacopy(m_ptr->PROC_NR, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&level, (phys_bytes)sizeof(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;
+
+               /* Copy back to user */
+               sys_datacopy(SELF, (vir_bytes)&level, m_ptr->PROC_NR, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(level));
+       }
+
+       return OK;
+}
+
+
+/*=========================================================================*
+ *                             get_set_input                              *
+ *=========================================================================*/
+PRIVATE int get_set_input(m_ptr, flag, channel)
+message *m_ptr;
+int flag;      /* 0 = get, 1 = set */
+int channel;    /* 0 = left, 1 = right */
+{
+       phys_bytes user_phys;
+       struct inout_ctrl input;
+       int input_cmd, input_mask, mask, del_mask, shift;
+
+       sys_datacopy(m_ptr->PROC_NR, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&input, (phys_bytes)sizeof(input));
+
+       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);
+               }
+
+               /* Copy back to user */
+               sys_datacopy(SELF, (vir_bytes)&input, m_ptr->PROC_NR, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(input));
+       }
+
+       return OK;
+}
+
+
+/*=========================================================================*
+ *                             get_set_output                             *
+ *=========================================================================*/
+PRIVATE int get_set_output(m_ptr, flag)
+message *m_ptr;
+int flag;      /* 0 = get, 1 = set */
+{
+       phys_bytes user_phys;
+       struct inout_ctrl output;
+       int output_mask, mask, del_mask, shift;
+
+       sys_datacopy(m_ptr->PROC_NR, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&output, (phys_bytes)sizeof(output));
+
+       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);
+               }
+
+               /* Copy back to user */
+               sys_datacopy(SELF, (vir_bytes)&output, m_ptr->PROC_NR, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(output));
+       }
+
+       return OK;
+}
\ No newline at end of file