Update by paboonst@cs.vu.nl
This commit is contained in:
parent
c10b7c719f
commit
c4e4398043
7 changed files with 576 additions and 87 deletions
|
@ -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:
|
||||
|
|
21
drivers/sb16/README
Normal file
21
drivers/sb16/README
Normal 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
46
drivers/sb16/sb16.c
Normal 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);
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
|
@ -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 */
|
||||
|
|
BIN
drivers/sb16/sb16.tar
Normal file
BIN
drivers/sb16/sb16.tar
Normal file
Binary file not shown.
|
@ -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));
|
||||
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;
|
||||
}
|
||||
|
|
387
drivers/sb16/sb16_mixer.c
Normal file
387
drivers/sb16/sb16_mixer.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue