diff --git a/drivers/sb16/Makefile b/drivers/sb16/Makefile index 71070d046..24390b971 100644 --- a/drivers/sb16/Makefile +++ b/drivers/sb16/Makefile @@ -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 index 000000000..08e62a231 --- /dev/null +++ b/drivers/sb16/README @@ -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 index 000000000..3257fa976 --- /dev/null +++ b/drivers/sb16/sb16.c @@ -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 diff --git a/drivers/sb16/sb16.h b/drivers/sb16/sb16.h index ae1becd64..3396743c1 100644 --- a/drivers/sb16/sb16.h +++ b/drivers/sb16/sb16.h @@ -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 +#include + #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 */ @@ -69,12 +73,14 @@ # 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 */ @@ -106,12 +113,14 @@ #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 */ @@ -160,8 +169,15 @@ #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 index 000000000..854c41f95 Binary files /dev/null and b/drivers/sb16/sb16.tar differ diff --git a/drivers/sb16/sb16_dsp.c b/drivers/sb16/sb16_dsp.c index 4e82d8b18..e378acbf5 100644 --- a/drivers/sb16/sb16_dsp.c +++ b/drivers/sb16/sb16_dsp.c @@ -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 + _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)); + pv_set(pvb[2], 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[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)); + pv_set(pvb[2], 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[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 index 000000000..3ec309775 --- /dev/null +++ b/drivers/sb16/sb16_mixer.c @@ -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