This commit is contained in:
Ben Gras 2005-09-16 11:09:08 +00:00
parent c10b7c719f
commit c4e4398043
7 changed files with 576 additions and 87 deletions

View file

@ -1,5 +1,4 @@
# Makefile for the Sound Blaster 16 driver (SB16) # Makefile for the Sound Blaster 16 driver (SB16)
DRIVER = sb16_dsp
# directories # directories
u = /usr u = /usr
@ -15,19 +14,20 @@ CFLAGS = -I$i
LDFLAGS = -i LDFLAGS = -i
LIBS = -lsys -lsysutil LIBS = -lsys -lsysutil
OBJ = sb16_dsp.o
# build local binary # build local binary
all build: $(DRIVER) all build: sb16_dsp sb16_mixer
$(DRIVER): $(OBJ) sb16_dsp: sb16.o sb16_dsp.o
$(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS) $(CC) -o $@ $(LDFLAGS) sb16.o sb16_dsp.o $(LIBS)
# install -S 256w $(DRIVER) sb16_mixer: sb16.o sb16_mixer.o
$(CC) -o $@ $(LDFLAGS) sb16.o sb16_mixer.o $(LIBS)
# install with other drivers # install with other drivers
install: /usr/sbin/$(DRIVER) install: /usr/sbin/sb16_dsp /usr/sbin/sb16_mixer
/usr/sbin/$(DRIVER): $(DRIVER) /usr/sbin/sb16_dsp: sb16_dsp
install -o root -c $? $@
/usr/sbin/sb16_mixer: sb16_mixer
install -o root -c $? $@ install -o root -c $? $@
# install -o root -cs $? $@
# clean up local files # clean up local files
clean: clean:

21
drivers/sb16/README Normal file
View file

@ -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

46
drivers/sb16/sb16.c Normal file
View file

@ -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);
}

View file

@ -1,8 +1,10 @@
#ifndef SB16_H #ifndef SB16_H
#define SB16_H #define SB16_H
#define SB_DEBUG 0 /* 1 = print debug info */ #include "../drivers.h"
#define SB_DEBUG_2 0 /* 1 = print more debug info */ #include <sys/ioc_sound.h>
#include <minix/sound.h>
#define SB_TIMEOUT 32000 /* timeout count */ #define SB_TIMEOUT 32000 /* timeout count */
@ -32,6 +34,7 @@
#define DMA8_MODE 0x0B #define DMA8_MODE 0x0B
#define DMA8_CLEAR 0x0C #define DMA8_CLEAR 0x0C
/* If after this preprocessing stuff DMA8_PAGE is not defined /* If after this preprocessing stuff DMA8_PAGE is not defined
* the 8-bit DMA channel specified is not valid * the 8-bit DMA channel specified is not valid
*/ */
@ -54,6 +57,7 @@
#define DMA16_MODE 0xD6 #define DMA16_MODE 0xD6
#define DMA16_CLEAR 0xD8 #define DMA16_CLEAR 0xD8
/* If after this preprocessing stuff DMA16_PAGE is not defined /* If after this preprocessing stuff DMA16_PAGE is not defined
* the 16-bit DMA channel specified is not valid * the 16-bit DMA channel specified is not valid
*/ */
@ -69,12 +73,14 @@
# endif # endif
#endif #endif
/* DMA modes */ /* DMA modes */
#define DMA16_AUTO_PLAY 0x58 + (SB_DMA_16 & 3) #define DMA16_AUTO_PLAY 0x58 + (SB_DMA_16 & 3)
#define DMA16_AUTO_REC 0x54 + (SB_DMA_16 & 3) #define DMA16_AUTO_REC 0x54 + (SB_DMA_16 & 3)
#define DMA8_AUTO_PLAY 0x58 + SB_DMA_8 #define DMA8_AUTO_PLAY 0x58 + SB_DMA_8
#define DMA8_AUTO_REC 0x54 + SB_DMA_8 #define DMA8_AUTO_REC 0x54 + SB_DMA_8
/* IO ports for soundblaster */ /* IO ports for soundblaster */
#define DSP_RESET 0x6 + SB_BASE_ADDR #define DSP_RESET 0x6 + SB_BASE_ADDR
#define DSP_READ 0xA + SB_BASE_ADDR #define DSP_READ 0xA + SB_BASE_ADDR
@ -89,6 +95,7 @@
#define OPL3_RIGHT 0x2 + SB_BASE_ADDR #define OPL3_RIGHT 0x2 + SB_BASE_ADDR
#define OPL3_BOTH 0x8 + SB_BASE_ADDR #define OPL3_BOTH 0x8 + SB_BASE_ADDR
/* DSP Commands */ /* DSP Commands */
#define DSP_INPUT_RATE 0x42 /* set input sample rate */ #define DSP_INPUT_RATE 0x42 /* set input sample rate */
#define DSP_OUTPUT_RATE 0x41 /* set output 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_IRQREQ8 0xF2 /* Interrupt request 8 bit */
#define DSP_CMD_IRQREQ16 0xF3 /* Interrupt request 16 bit */ #define DSP_CMD_IRQREQ16 0xF3 /* Interrupt request 16 bit */
/* DSP Modes */ /* DSP Modes */
#define DSP_MODE_MONO_US 0x00 /* Mono unsigned */ #define DSP_MODE_MONO_US 0x00 /* Mono unsigned */
#define DSP_MODE_MONO_S 0x10 /* Mono signed */ #define DSP_MODE_MONO_S 0x10 /* Mono signed */
#define DSP_MODE_STEREO_US 0x20 /* Stereo unsigned */ #define DSP_MODE_STEREO_US 0x20 /* Stereo unsigned */
#define DSP_MODE_STEREO_S 0x30 /* Stereo signed */ #define DSP_MODE_STEREO_S 0x30 /* Stereo signed */
/* MIXER commands */ /* MIXER commands */
#define MIXER_RESET 0x00 /* Reset */ #define MIXER_RESET 0x00 /* Reset */
#define MIXER_DAC_LEVEL 0x04 /* Used for detection only */ #define MIXER_DAC_LEVEL 0x04 /* Used for detection only */
@ -160,8 +169,15 @@
#define DSP_MIN_FRAGMENT_SIZE 1024 /* Minimum fragment size */ #define DSP_MIN_FRAGMENT_SIZE 1024 /* Minimum fragment size */
#define DSP_NR_OF_BUFFERS 8 #define DSP_NR_OF_BUFFERS 8
/* Number of bytes you can DMA before hitting a 64K boundary: */ /* Number of bytes you can DMA before hitting a 64K boundary: */
#define dma_bytes_left(phys) \ #define dma_bytes_left(phys) \
((unsigned) (sizeof(int) == 2 ? 0 : 0x10000) - (unsigned) ((phys) & 0xFFFF)) ((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 */ #endif /* SB16_H */

BIN
drivers/sb16/sb16.tar Normal file

Binary file not shown.

View 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 "sb16.h"
#include "../drivers.h"
#include <sys/ioc_sound.h>
_PROTOTYPE(void main, (void)); _PROTOTYPE(void main, (void));
FORWARD _PROTOTYPE( int dsp_open, (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 reply, (int code, int replyee, int process, int status) );
FORWARD _PROTOTYPE( void init_buffer, (void) ); 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_init, (void) );
FORWARD _PROTOTYPE( int dsp_reset, (void) ); FORWARD _PROTOTYPE( int dsp_reset, (void) );
FORWARD _PROTOTYPE( int dsp_command, (int value) ); 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( int dsp_set_sign, (unsigned int sign) );
FORWARD _PROTOTYPE( void dsp_dma_setup, (phys_bytes address, int count) ); FORWARD _PROTOTYPE( void dsp_dma_setup, (phys_bytes address, int count) );
FORWARD _PROTOTYPE( void dsp_setup, (void) ); 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 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 unsigned int DspFragmentSize = DSP_MAX_FRAGMENT_SIZE;
PRIVATE int DspAvail = 0; PRIVATE int DspAvail = 0;
PRIVATE int DspBusy = 0; PRIVATE int DspBusy = 0;
PRIVATE int DmaDone = 1;
PRIVATE int DmaMode = 0; PRIVATE int DmaMode = 0;
PRIVATE int DmaBusy = -1; PRIVATE int DmaBusy = -1;
PRIVATE int DmaFillNext = 0; PRIVATE int DmaFillNext = 0;
@ -55,6 +76,7 @@ PRIVATE int reviveProcNr;
#define dprint (void) #define dprint (void)
/*===========================================================================* /*===========================================================================*
* main * main
*===========================================================================*/ *===========================================================================*/
@ -96,6 +118,7 @@ PUBLIC void main()
} }
/*===========================================================================* /*===========================================================================*
* dsp_open * dsp_open
*===========================================================================*/ *===========================================================================*/
@ -124,6 +147,7 @@ PRIVATE int dsp_open()
return OK; return OK;
} }
/*===========================================================================* /*===========================================================================*
* dsp_close * dsp_close
*===========================================================================*/ *===========================================================================*/
@ -136,6 +160,7 @@ PRIVATE int dsp_close()
return OK; return OK;
} }
/*===========================================================================* /*===========================================================================*
* dsp_ioctl * dsp_ioctl
*===========================================================================*/ *===========================================================================*/
@ -176,6 +201,7 @@ message *m_ptr;
return status; return status;
} }
/*===========================================================================* /*===========================================================================*
* dsp_write * dsp_write
*===========================================================================*/ *===========================================================================*/
@ -191,6 +217,10 @@ message *m_ptr;
reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINVAL); reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINVAL);
return; 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); reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, SUSPEND);
@ -235,6 +265,7 @@ message *m_ptr;
notify(m_ptr->m_source); notify(m_ptr->m_source);
} }
/*===========================================================================* /*===========================================================================*
* dsp_hardware_msg * dsp_hardware_msg
*===========================================================================*/ *===========================================================================*/
@ -245,10 +276,16 @@ PRIVATE void dsp_hardware_msg()
dprint("Finished playing dma[%d]; ", DmaBusy); dprint("Finished playing dma[%d]; ", DmaBusy);
DmaBusy = (DmaBusy + 1) % DMA_NR_OF_BUFFERS; DmaBusy = (DmaBusy + 1) % DMA_NR_OF_BUFFERS;
if(DmaBusy == DmaFillNext) { /* Dma buffer empty, stop Dma transfer */ if(DmaBusy == DmaFillNext) { /* Dma buffer empty, stop Dma transfer */
dsp_command((DspBits == 8 ? DSP_CMD_DMA8HALT : DSP_CMD_DMA16HALT)); dsp_command((DspBits == 8 ? DSP_CMD_DMA8HALT : DSP_CMD_DMA16HALT));
dprint("No more work...!\n"); dprint("No more work...!\n");
DmaBusy = -1; DmaBusy = -1;
} else if(BufReadNext >= 0) { /* Data in second buffer, copy one fragment to Dma buffer */ } 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); memcpy(DmaPtr + DmaFillNext * DspFragmentSize, Buffer + BufReadNext * DspFragmentSize, DspFragmentSize);
dprint("copy buf[%d] -> dma[%d]; ", BufReadNext, DmaFillNext); dprint("copy buf[%d] -> dma[%d]; ", BufReadNext, DmaFillNext);
BufReadNext = (BufReadNext + 1) % DSP_NR_OF_BUFFERS; BufReadNext = (BufReadNext + 1) % DSP_NR_OF_BUFFERS;
@ -257,15 +294,19 @@ PRIVATE void dsp_hardware_msg()
BufReadNext = -1; BufReadNext = -1;
} }
dprint("Starting dma[%d]\n", DmaBusy); dprint("Starting dma[%d]\n", DmaBusy);
return;
} else { /* Second buffer empty, still data in Dma buffer, continue playback */ } else { /* Second buffer empty, still data in Dma buffer, continue playback */
dprint("Starting dma[%d]\n", DmaBusy); dprint("Starting dma[%d]\n", DmaBusy);
} }
} }
/* Acknowledge the interrupt on the DSP */ /* 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 * * 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 */ send(m_ptr->m_source, m_ptr); /* send the message */
} }
/*===========================================================================* /*===========================================================================*
* reply * * reply *
*===========================================================================*/ *===========================================================================*/
@ -303,6 +345,7 @@ int status;
send(replyee, &m); send(replyee, &m);
} }
/*===========================================================================* /*===========================================================================*
* init_buffer * init_buffer
*===========================================================================*/ *===========================================================================*/
@ -328,32 +371,6 @@ PRIVATE void init_buffer()
#endif /* CHIP == INTEL */ #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_init
@ -371,11 +388,11 @@ PRIVATE int dsp_init()
dsp_command(DSP_GET_VERSION); /* Get DSP version bytes */ dsp_command(DSP_GET_VERSION); /* Get DSP version bytes */
for(i = 1000; i; i--) { for(i = 1000; i; i--) {
if(dsp_inb(DSP_DATA_AVL) & 0x80) { if(sb16_inb(DSP_DATA_AVL) & 0x80) {
if(DspVersion[0] == 0) { if(DspVersion[0] == 0) {
DspVersion[0] = dsp_inb(DSP_READ); DspVersion[0] = sb16_inb(DSP_READ);
} else { } else {
DspVersion[1] = dsp_inb(DSP_READ); DspVersion[1] = sb16_inb(DSP_READ);
break; break;
} }
} }
@ -402,6 +419,7 @@ PRIVATE int dsp_init()
return OK; return OK;
} }
/*===========================================================================* /*===========================================================================*
* dsp_reset * dsp_reset
*===========================================================================*/ *===========================================================================*/
@ -409,20 +427,20 @@ PRIVATE int dsp_reset()
{ {
int i; int i;
dsp_outb(DSP_RESET, 1); sb16_outb(DSP_RESET, 1);
for(i = 0; i < 1000; i++); /* wait a while */ 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; DmaBusy = -1;
DmaDone = 1;
return OK; return OK;
} }
/*===========================================================================* /*===========================================================================*
* dsp_command * dsp_command
*===========================================================================*/ *===========================================================================*/
@ -432,8 +450,8 @@ int value;
int i, status; int i, status;
for (i = 0; i < SB_TIMEOUT; i++) { for (i = 0; i < SB_TIMEOUT; i++) {
if((dsp_inb(DSP_STATUS) & 0x80) == 0) { if((sb16_inb(DSP_STATUS) & 0x80) == 0) {
dsp_outb(DSP_COMMAND, value); sb16_outb(DSP_COMMAND, value);
return OK; return OK;
} }
} }
@ -442,6 +460,7 @@ int value;
return -1; return -1;
} }
/*===========================================================================* /*===========================================================================*
* dsp_set_size * dsp_set_size
*===========================================================================*/ *===========================================================================*/
@ -460,6 +479,7 @@ unsigned int size;
return OK; return OK;
} }
/*===========================================================================* /*===========================================================================*
* dsp_set_speed * dsp_set_speed
*===========================================================================*/ *===========================================================================*/
@ -491,6 +511,7 @@ unsigned int speed;
return OK; return OK;
} }
/*===========================================================================* /*===========================================================================*
* dsp_set_stereo * dsp_set_stereo
*===========================================================================*/ *===========================================================================*/
@ -506,6 +527,7 @@ unsigned int stereo;
return OK; return OK;
} }
/*===========================================================================* /*===========================================================================*
* dsp_set_bits * dsp_set_bits
*===========================================================================*/ *===========================================================================*/
@ -522,6 +544,7 @@ unsigned int bits;
return OK; return OK;
} }
/*===========================================================================* /*===========================================================================*
* dsp_set_sign * dsp_set_sign
*===========================================================================*/ *===========================================================================*/
@ -535,6 +558,7 @@ unsigned int sign;
return OK; return OK;
} }
/*===========================================================================* /*===========================================================================*
* dsp_dma_setup * dsp_dma_setup
*===========================================================================*/ *===========================================================================*/
@ -542,41 +566,50 @@ PRIVATE void dsp_dma_setup(address, count)
phys_bytes address; phys_bytes address;
int count; int count;
{ {
pvb_pair_t pvb[9];
dprint("Setting up %d bit DMA\n", DspBits); dprint("Setting up %d bit DMA\n", DspBits);
if(DspBits == 8) { /* 8 bit sound */ if(DspBits == 8) { /* 8 bit sound */
count--; count--;
dsp_outb(DMA8_MASK, SB_DMA_8 | 0x04); /* Disable DMA channel */ pv_set(pvb[0], DMA8_MASK, SB_DMA_8 | 0x04); /* Disable DMA channel */
dsp_outb(DMA8_CLEAR, 0x00); /* Clear flip flop */ pv_set(pvb[1], DMA8_CLEAR, 0x00); /* Clear flip flop */
/* set DMA mode */ /* 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 */ pv_set(pvb[3], DMA8_ADDR, address >> 0); /* Low_byte of address */
dsp_outb(DMA8_ADDR, address >> 8); /* High byte of address */ pv_set(pvb[4], DMA8_ADDR, address >> 8); /* High byte of address */
dsp_outb(DMA8_PAGE, address >> 16); /* 64K page number */ pv_set(pvb[5], DMA8_PAGE, address >> 16); /* 64K page number */
dsp_outb(DMA8_COUNT, count >> 0); /* Low byte of count */ pv_set(pvb[6], DMA8_COUNT, count >> 0); /* Low byte of count */
dsp_outb(DMA8_COUNT, count >> 8); /* High byte of count */ pv_set(pvb[7], DMA8_COUNT, count >> 8); /* High byte of count */
dsp_outb(DMA8_MASK, SB_DMA_8); /* Enable DMA channel */ pv_set(pvb[8], DMA8_MASK, SB_DMA_8); /* Enable DMA channel */
sys_voutb(pvb, 9);
} else { /* 16 bit sound */ } else { /* 16 bit sound */
count-= 2; count-= 2;
dsp_outb(DMA16_MASK, (SB_DMA_16 & 3) | 0x04); /* Disable DMA channel */ pv_set(pvb[0], DMA16_MASK, (SB_DMA_16 & 3) | 0x04); /* Disable DMA channel */
dsp_outb(DMA16_CLEAR, 0x00); /* Clear flip flop */
pv_set(pvb[1], DMA16_CLEAR, 0x00); /* Clear flip flop */
/* Set dma mode */ /* 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 */ pv_set(pvb[3], DMA16_ADDR, (address >> 1) & 0xFF); /* Low_byte of address */
dsp_outb(DMA16_ADDR, (address >> 9) & 0xFF); /* High byte of address */ pv_set(pvb[4], DMA16_ADDR, (address >> 9) & 0xFF); /* High byte of address */
dsp_outb(DMA16_PAGE, (address >> 16) & 0xFE); /* 128K page number */ pv_set(pvb[5], DMA16_PAGE, (address >> 16) & 0xFE); /* 128K page number */
dsp_outb(DMA16_COUNT, count >> 1); /* Low byte of count */ pv_set(pvb[6], DMA16_COUNT, count >> 1); /* Low byte of count */
dsp_outb(DMA16_COUNT, count >> 9); /* High byte of count */ pv_set(pvb[7], DMA16_COUNT, count >> 9); /* High byte of count */
dsp_outb(DMA16_MASK, SB_DMA_16 & 3); /* Enable DMA channel */ pv_set(pvb[8], DMA16_MASK, SB_DMA_16 & 3); /* Enable DMA channel */
sys_voutb(pvb, 9);
} }
} }
/*===========================================================================* /*===========================================================================*
* dsp_setup() * 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;
}

387
drivers/sb16/sb16_mixer.c Normal file
View file

@ -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;
}