# Makefile for the Sound Blaster 16 driver (SB16)
-DRIVER = sb16_dsp
# directories
u = /usr
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:
--- /dev/null
+
+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
+
--- /dev/null
+#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
#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 */
#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
*/
#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
#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 */
+/* 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) );
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) );
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 */
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;
#define dprint (void)
+
/*===========================================================================*
* main
*===========================================================================*/
}
+
/*===========================================================================*
* dsp_open
*===========================================================================*/
return OK;
}
+
/*===========================================================================*
* dsp_close
*===========================================================================*/
return OK;
}
+
/*===========================================================================*
* dsp_ioctl
*===========================================================================*/
return status;
}
+
/*===========================================================================*
* dsp_write
*===========================================================================*/
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);
notify(m_ptr->m_source);
}
+
/*===========================================================================*
* 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;
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 *
*===========================================================================*/
send(m_ptr->m_source, m_ptr); /* send the message */
}
+
/*===========================================================================*
* reply *
*===========================================================================*/
send(replyee, &m);
}
+
/*===========================================================================*
* 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
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;
}
}
return OK;
}
+
/*===========================================================================*
* 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
*===========================================================================*/
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;
}
}
return -1;
}
+
/*===========================================================================*
* dsp_set_size
*===========================================================================*/
return OK;
}
+
/*===========================================================================*
* dsp_set_speed
*===========================================================================*/
return OK;
}
+
/*===========================================================================*
* dsp_set_stereo
*===========================================================================*/
return OK;
}
+
/*===========================================================================*
* dsp_set_bits
*===========================================================================*/
return OK;
}
+
/*===========================================================================*
* dsp_set_sign
*===========================================================================*/
return OK;
}
+
/*===========================================================================*
* dsp_dma_setup
*===========================================================================*/
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()
*===========================================================================*/
}
}
-/*===========================================================================*
- * 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;
-}
+
--- /dev/null
+/* 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