From: Ben Gras Date: Mon, 22 Aug 2005 11:46:06 +0000 (+0000) Subject: Imported initial version of user-space port of original sb16 driver. X-Git-Tag: v3.1.0~316 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/Bv9ARM.ch10.html?a=commitdiff_plain;h=07e149a1e33bdf7930e1413b90ab785880e5b137;p=minix.git Imported initial version of user-space port of original sb16 driver. User-space porting by Peter Boonstoppel. --- diff --git a/drivers/Makefile b/drivers/Makefile index eaefc09b1..0eb004a90 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -29,3 +29,4 @@ all install depend clean: cd ./cmos && $(MAKE) $@ cd ./random && $(MAKE) $@ cd ./dp8390 && $(MAKE) $@ + cd ./sb16 && $(MAKE) $@ diff --git a/drivers/sb16/Makefile b/drivers/sb16/Makefile new file mode 100644 index 000000000..71070d046 --- /dev/null +++ b/drivers/sb16/Makefile @@ -0,0 +1,41 @@ +# Makefile for the Sound Blaster 16 driver (SB16) +DRIVER = sb16_dsp + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +d = .. + +# programs, flags, etc. +CC = exec cc +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) + +# install with other drivers +install: /usr/sbin/$(DRIVER) +/usr/sbin/$(DRIVER): $(DRIVER) + install -o root -c $? $@ +# install -o root -cs $? $@ + +# clean up local files +clean: + rm -f *.o *.bak $(DRIVER) + +depend: + /usr/bin/mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend + +# Include generated dependencies. +include .depend + diff --git a/drivers/sb16/sb16.h b/drivers/sb16/sb16.h new file mode 100644 index 000000000..5afd21a04 --- /dev/null +++ b/drivers/sb16/sb16.h @@ -0,0 +1,176 @@ +#ifndef SB16_H +#define SB16_H + +#define SB_DEBUG 0 /* 1 = print debug info */ +#define SB_DEBUG_2 0 /* 1 = print more debug info */ + +#define SB_TIMEOUT 32000 /* timeout count */ + +/* IRQ, base address and DMA channels */ +#define SB_IRQ 7 +#define SB_BASE_ADDR 0x220 /* 0x210, 0x220, 0x230, 0x240, + * 0x250, 0x260, 0x280 + */ +#define SB_DMA_8 1 /* 0, 1, 3 */ +#define SB_DMA_16 5 /* 5, 6, 7 */ +#if _WORD_SIZE == 2 +#define DMA_SIZE 8192 /* Dma buffer MUST BE MULTIPLE OF 2 */ +#else +#define DMA_SIZE (64 * 1024) /* Dma buffer MUST BE MULTIPLE OF 2 */ +#endif + +/* Some defaults for the DSP */ +#define DEFAULT_SPEED 22050 /* Sample rate */ +#define DEFAULT_BITS 8 /* Nr. of bits */ +#define DEFAULT_SIGN 0 /* 0 = unsigned, 1 = signed */ +#define DEFAULT_STEREO 0 /* 0 = mono, 1 = stereo */ + +/* DMA port addresses */ +#define DMA8_ADDR ((SB_DMA_8 & 3) << 1) + 0x00 +#define DMA8_COUNT ((SB_DMA_8 & 3) << 1) + 0x01 +#define DMA8_MASK 0x0A +#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 + */ +#if SB_DMA_8 == 0 +# define DMA8_PAGE 0x87 +#else +# if SB_DMA_8 == 1 +# define DMA8_PAGE 0x83 +# else +# if SB_DMA_8 == 3 +# define DMA8_PAGE 0x82 +# endif +# endif +#endif + + +#define DMA16_ADDR ((SB_DMA_16 & 3) << 2) + 0xC0 +#define DMA16_COUNT ((SB_DMA_16 & 3) << 2) + 0xC2 +#define DMA16_MASK 0xD4 +#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 + */ +#if SB_DMA_16 == 5 +# define DMA16_PAGE 0x8B +#else +# if SB_DMA_16 == 6 +# define DMA16_PAGE 0x89 +# else +# if SB_DMA_16 == 7 +# define DMA16_PAGE 0x8A +# endif +# endif +#endif + + +/* DMA modes */ +#define DMA16_AUTO_PLAY 0x58 + (SB_DMA_16 & 3) +#define DMA16_AUTO_REC 0x54 + (SB_DMA_16 & 3) +#define DMA8_AUTO_PLAY 0x58 + SB_DMA_8 +#define DMA8_AUTO_REC 0x54 + SB_DMA_8 + + +/* IO ports for soundblaster */ +#define DSP_RESET 0x6 + SB_BASE_ADDR +#define DSP_READ 0xA + SB_BASE_ADDR +#define DSP_WRITE 0xC + SB_BASE_ADDR +#define DSP_COMMAND 0xC + SB_BASE_ADDR +#define DSP_STATUS 0xC + SB_BASE_ADDR +#define DSP_DATA_AVL 0xE + SB_BASE_ADDR +#define DSP_DATA16_AVL 0xF + SB_BASE_ADDR +#define MIXER_REG 0x4 + SB_BASE_ADDR +#define MIXER_DATA 0x5 + SB_BASE_ADDR +#define OPL3_LEFT 0x0 + SB_BASE_ADDR +#define OPL3_RIGHT 0x2 + SB_BASE_ADDR +#define OPL3_BOTH 0x8 + SB_BASE_ADDR + + +/* DSP Commands */ +#define DSP_INPUT_RATE 0x42 /* set input sample rate */ +#define DSP_OUTPUT_RATE 0x41 /* set output sample rate */ +#define DSP_CMD_SPKON 0xD1 /* set speaker on */ +#define DSP_CMD_SPKOFF 0xD3 /* set speaker off */ +#define DSP_CMD_DMA8HALT 0xD0 /* halt DMA 8-bit operation */ +#define DSP_CMD_DMA8CONT 0xD4 /* continue DMA 8-bit operation */ +#define DSP_CMD_DMA16HALT 0xD5 /* halt DMA 16-bit operation */ +#define DSP_CMD_DMA16CONT 0xD6 /* continue DMA 16-bit operation */ +#define DSP_GET_VERSION 0xE1 /* get version number of DSP */ +#define DSP_CMD_8BITAUTO_IN 0xCE /* 8 bit auto-initialized input */ +#define DSP_CMD_8BITAUTO_OUT 0xC6 /* 8 bit auto-initialized output */ +#define DSP_CMD_16BITAUTO_IN 0xBE /* 16 bit auto-initialized input */ +#define DSP_CMD_16BITAUTO_OUT 0xB6 /* 16 bit auto-initialized output */ +#define DSP_CMD_IRQREQ8 0xF2 /* Interrupt request 8 bit */ +#define DSP_CMD_IRQREQ16 0xF3 /* Interrupt request 16 bit */ + + +/* DSP Modes */ +#define DSP_MODE_MONO_US 0x00 /* Mono unsigned */ +#define DSP_MODE_MONO_S 0x10 /* Mono signed */ +#define DSP_MODE_STEREO_US 0x20 /* Stereo unsigned */ +#define DSP_MODE_STEREO_S 0x30 /* Stereo signed */ + + +/* MIXER commands */ +#define MIXER_RESET 0x00 /* Reset */ +#define MIXER_DAC_LEVEL 0x04 /* Used for detection only */ +#define MIXER_MASTER_LEFT 0x30 /* Master volume left */ +#define MIXER_MASTER_RIGHT 0x31 /* Master volume right */ +#define MIXER_DAC_LEFT 0x32 /* Dac level left */ +#define MIXER_DAC_RIGHT 0x33 /* Dac level right */ +#define MIXER_FM_LEFT 0x34 /* Fm level left */ +#define MIXER_FM_RIGHT 0x35 /* Fm level right */ +#define MIXER_CD_LEFT 0x36 /* Cd audio level left */ +#define MIXER_CD_RIGHT 0x37 /* Cd audio level right */ +#define MIXER_LINE_LEFT 0x38 /* Line in level left */ +#define MIXER_LINE_RIGHT 0x39 /* Line in level right */ +#define MIXER_MIC_LEVEL 0x3A /* Microphone level */ +#define MIXER_PC_LEVEL 0x3B /* Pc speaker level */ +#define MIXER_OUTPUT_CTRL 0x3C /* Output control */ +#define MIXER_IN_LEFT 0x3D /* Input control left */ +#define MIXER_IN_RIGHT 0x3E /* Input control right */ +#define MIXER_GAIN_IN_LEFT 0x3F /* Input gain control left */ +#define MIXER_GAIN_IN_RIGHT 0x40 /* Input gain control right */ +#define MIXER_GAIN_OUT_LEFT 0x41 /* Output gain control left */ +#define MIXER_GAIN_OUT_RIGHT 0x42 /* Output gain control rigth */ +#define MIXER_AGC 0x43 /* Automatic gain control */ +#define MIXER_TREBLE_LEFT 0x44 /* Treble left */ +#define MIXER_TREBLE_RIGHT 0x45 /* Treble right */ +#define MIXER_BASS_LEFT 0x46 /* Bass left */ +#define MIXER_BASS_RIGHT 0x47 /* Bass right */ +#define MIXER_SET_IRQ 0x80 /* Set irq number */ +#define MIXER_SET_DMA 0x81 /* Set DMA channels */ +#define MIXER_IRQ_STATUS 0x82 /* Irq status */ + +/* Mixer constants */ +#define MIC 0x01 /* Microphone */ +#define CD_RIGHT 0x02 +#define CD_LEFT 0x04 +#define LINE_RIGHT 0x08 +#define LINE_LEFT 0x10 +#define FM_RIGHT 0x20 +#define FM_LEFT 0x40 + +/* DSP constants */ +#define DMA_NR_OF_BUFFERS 2 +#define DSP_MAX_SPEED 44100 /* Max sample speed in KHz */ +#define DSP_MIN_SPEED 4000 /* Min sample speed in KHz */ +#define DSP_MAX_FRAGMENT_SIZE DMA_SIZE / DMA_NR_OF_BUFFERS /* Maximum fragment size */ +#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)) + + +#endif /* SB16_H */ diff --git a/drivers/sb16/sb16_dsp.c b/drivers/sb16/sb16_dsp.c new file mode 100644 index 000000000..8409ce0df --- /dev/null +++ b/drivers/sb16/sb16_dsp.c @@ -0,0 +1,656 @@ +#include "sb16.h" +#include "../drivers.h" +#include + +_PROTOTYPE(void main, (void)); +FORWARD _PROTOTYPE( int dsp_open, (void) ); +FORWARD _PROTOTYPE( int dsp_close, (void) ); +FORWARD _PROTOTYPE( int dsp_ioctl, (message *m_ptr) ); +FORWARD _PROTOTYPE( void dsp_write, (message *m_ptr) ); +FORWARD _PROTOTYPE( void dsp_hardware_msg, (void) ); +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) ); +FORWARD _PROTOTYPE( int dsp_set_size, (unsigned int size) ); +FORWARD _PROTOTYPE( int dsp_set_speed, (unsigned int speed) ); +FORWARD _PROTOTYPE( int dsp_set_stereo, (unsigned int stereo) ); +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 */ + +PRIVATE char DmaBuffer[DMA_SIZE + 64 * 1024]; +PRIVATE char* DmaPtr; +PRIVATE phys_bytes DmaPhys; + +PRIVATE char Buffer[DSP_MAX_FRAGMENT_SIZE * DSP_NR_OF_BUFFERS]; + +PRIVATE int DspVersion[2]; +PRIVATE unsigned int DspStereo = DEFAULT_STEREO; +PRIVATE unsigned int DspSpeed = DEFAULT_SPEED; +PRIVATE unsigned int DspBits = DEFAULT_BITS; +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; +PRIVATE int BufReadNext = -1; +PRIVATE int BufFillNext = 0; + +PRIVATE int revivePending = 0; +PRIVATE int reviveStatus; +PRIVATE int reviveProcNr; + +#define dprint (void) + + +/*===========================================================================* + * main + *===========================================================================*/ +PUBLIC void main() +{ + int r, caller, proc_nr, s; + message mess; + + dprint("sb16_dsp.c: main()\n"); + + /* Get a DMA buffer. */ + init_buffer(); + + while(TRUE) { + /* Wait for an incoming message */ + receive(ANY, &mess); + + caller = mess.m_source; + proc_nr = mess.PROC_NR; + + /* Now carry out the work. */ + switch(mess.m_type) { + case DEV_OPEN: r = dsp_open(); break; + case DEV_CLOSE: r = dsp_close(); break; + case DEV_IOCTL: r = dsp_ioctl(&mess); break; + + case DEV_READ: r = EINVAL; break; /* Not yet implemented */ + case DEV_WRITE: dsp_write(&mess); continue; /* don't reply */ + + case DEV_STATUS: dsp_status(&mess); continue; /* don't reply */ + case HARD_INT: dsp_hardware_msg(); continue; /* don't reply */ + case SYS_SIG: continue; /* don't reply */ + default: r = EINVAL; + } + + /* Finally, prepare and send the reply message. */ + reply(TASK_REPLY, caller, proc_nr, r); + } + +} + + +/*===========================================================================* + * dsp_open + *===========================================================================*/ +PRIVATE int dsp_open() +{ + dprint("sb16_dsp.c: dsp_open()\n"); + + /* try to detect SoundBlaster card */ + if(!DspAvail && dsp_init() != OK) return EIO; + + /* Only one open at a time with soundcards */ + if(DspBusy) return EBUSY; + + /* Start with a clean DSP */ + if(dsp_reset() != OK) return EIO; + + /* Setup default values */ + DspStereo = DEFAULT_STEREO; + DspSpeed = DEFAULT_SPEED; + DspBits = DEFAULT_BITS; + DspSign = DEFAULT_SIGN; + DspFragmentSize = DMA_SIZE / 2; + + DspBusy = 1; + + return OK; +} + + +/*===========================================================================* + * dsp_close + *===========================================================================*/ +PRIVATE int dsp_close() +{ + dprint("sb16_dsp.c: dsp_close()\n"); + + DspBusy = 0; /* soundcard available again */ + + return OK; +} + + +/*===========================================================================* + * dsp_ioctl + *===========================================================================*/ +PRIVATE int dsp_ioctl(m_ptr) +message *m_ptr; +{ + int status; + phys_bytes user_phys; + unsigned int val; + + dprint("sb16_dsp.c: dsp_ioctl()\n"); + + /* Cannot change parameters during play or recording */ + if(DmaBusy >= 0) return EBUSY; + + /* Get user data */ + if(m_ptr->REQUEST != DSPIORESET) { + sys_vircopy(m_ptr->PROC_NR, D, (vir_bytes)m_ptr->ADDRESS, SELF, D, (vir_bytes)&val, sizeof(val)); + } + + dprint("dsp_ioctl: got ioctl %d, argument: %d\n", m_ptr->REQUEST, val); + + switch(m_ptr->REQUEST) { + case DSPIORATE: status = dsp_set_speed(val); break; + case DSPIOSTEREO: status = dsp_set_stereo(val); break; + case DSPIOBITS: status = dsp_set_bits(val); break; + case DSPIOSIZE: status = dsp_set_size(val); break; + case DSPIOSIGN: status = dsp_set_sign(val); break; + case DSPIOMAX: + val = DSP_MAX_FRAGMENT_SIZE; + sys_vircopy(SELF, D, (vir_bytes)&val, m_ptr->PROC_NR, D, (vir_bytes)m_ptr->ADDRESS, sizeof(val)); + status = OK; + break; + case DSPIORESET: status = dsp_reset(); break; + default: status = ENOTTY; break; + } + + return status; +} + + +/*===========================================================================* + * dsp_write + *===========================================================================*/ +PRIVATE void dsp_write(m_ptr) +message *m_ptr; +{ + int s; + message mess; + + dprint("sb16_dsp.c: dsp_write()\n"); + + if(m_ptr->COUNT != DspFragmentSize) { + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINVAL); + return; + } + + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, SUSPEND); + + if(DmaBusy < 0) { /* Dma tranfer not yet started */ + + DmaMode = DEV_WRITE; /* Dma mode is writing */ + sys_datacopy(m_ptr->PROC_NR, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)DmaPtr, (phys_bytes)DspFragmentSize); + dsp_dma_setup(DmaPhys, DspFragmentSize * DMA_NR_OF_BUFFERS); + dsp_setup(); + DmaBusy = 0; /* Dma is busy */ + dprint(" filled dma[0]\n"); + DmaFillNext = 1; + + } else if(DmaBusy != DmaFillNext) { /* Dma transfer started, but Dma buffer not yet full */ + + sys_datacopy(m_ptr->PROC_NR, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)DmaPtr + DmaFillNext * DspFragmentSize, (phys_bytes)DspFragmentSize); + dprint(" filled dma[%d]\n", DmaFillNext); + DmaFillNext = (DmaFillNext + 1) % DMA_NR_OF_BUFFERS; + + } else if(BufReadNext < 0) { /* Dma buffer full, fill first element of second buffer */ + + sys_datacopy(m_ptr->PROC_NR, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)Buffer, (phys_bytes)DspFragmentSize); + dprint(" filled buf[0]\n"); + BufReadNext = 0; + BufFillNext = 1; + + } else { /* Dma buffer is full, filling second buffer */ + + while(BufReadNext == BufFillNext) { /* Second buffer also full, wait for space to become available */ + receive(HARDWARE, &mess); + dsp_hardware_msg(); + } + sys_datacopy(m_ptr->PROC_NR, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)Buffer + BufFillNext * DspFragmentSize, (phys_bytes)DspFragmentSize); + dprint(" filled buf[%d]\n", BufFillNext); + BufFillNext = (BufFillNext + 1) % DSP_NR_OF_BUFFERS; + + } + + revivePending = 1; + reviveStatus = DspFragmentSize; + reviveProcNr = m_ptr->PROC_NR; + notify(m_ptr->m_source); +} + + +/*===========================================================================* + * dsp_hardware_msg + *===========================================================================*/ +PRIVATE void dsp_hardware_msg() +{ + dprint("Interrupt: "); + if(DmaBusy >= 0) { /* Dma transfer was actually busy */ + 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 */ + memcpy(DmaPtr + DmaFillNext * DspFragmentSize, Buffer + BufReadNext * DspFragmentSize, DspFragmentSize); + dprint("copy buf[%d] -> dma[%d]; ", BufReadNext, DmaFillNext); + BufReadNext = (BufReadNext + 1) % DSP_NR_OF_BUFFERS; + DmaFillNext = (DmaFillNext + 1) % DMA_NR_OF_BUFFERS; + if(BufReadNext == BufFillNext) { + BufReadNext = -1; + } + dprint("Starting dma[%d]\n", DmaBusy); + } 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)); +} + + +/*===========================================================================* + * dsp_status * + *===========================================================================*/ +PRIVATE void dsp_status(m_ptr) +message *m_ptr; /* pointer to the newly arrived message */ +{ + if(revivePending) { + m_ptr->m_type = DEV_REVIVE; /* build message */ + m_ptr->REP_PROC_NR = reviveProcNr; + m_ptr->REP_STATUS = reviveStatus; + + revivePending = 0; /* unmark event */ + } else { + m_ptr->m_type = DEV_NO_STATUS; + } + + send(m_ptr->m_source, m_ptr); /* send the message */ +} + + +/*===========================================================================* + * reply * + *===========================================================================*/ +PRIVATE void reply(code, replyee, process, status) +int code; +int replyee; +int process; +int status; +{ + message m; + + m.m_type = code; /* TASK_REPLY or REVIVE */ + m.REP_STATUS = status; /* result of device operation */ + m.REP_PROC_NR = process; /* which user made the request */ + + send(replyee, &m); +} + + +/*===========================================================================* + * init_buffer + *===========================================================================*/ +PRIVATE void init_buffer() +{ +/* Select a buffer that can safely be used for dma transfers. + * Its absolute address is 'DmaPhys', the normal address is 'DmaPtr'. + */ + +#if (CHIP == INTEL) + unsigned left; + + DmaPtr = DmaBuffer; + sys_umap(SELF, D, (vir_bytes)DmaBuffer, (phys_bytes)sizeof(DmaBuffer), &DmaPhys); + + if((left = dma_bytes_left(DmaPhys)) < DMA_SIZE) { + /* First half of buffer crosses a 64K boundary, can't DMA into that */ + DmaPtr += left; + DmaPhys += left; + } +#else /* CHIP != INTEL */ + panic("SB16DSP","init_buffer() failed, CHIP != INTEL", 0); +#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 + *===========================================================================*/ +PRIVATE int dsp_init() +{ + int i, s; + + if(dsp_reset () != OK) { + dprint("sb16: No SoundBlaster card detected\n"); + return -1; + } + + DspVersion[0] = DspVersion[1] = 0; + dsp_command(DSP_GET_VERSION); /* Get DSP version bytes */ + + for(i = 1000; i; i--) { + if(dsp_inb(DSP_DATA_AVL) & 0x80) { + if(DspVersion[0] == 0) { + DspVersion[0] = dsp_inb(DSP_READ); + } else { + DspVersion[1] = dsp_inb(DSP_READ); + break; + } + } + } + + if(DspVersion[0] < 4) { + dprint("sb16: No SoundBlaster 16 compatible card detected\n"); + return -1; + } + + dprint("sb16: SoundBlaster DSP version %d.%d detected\n", DspVersion[0], DspVersion[1]); + + /* set SB to use our IRQ and DMA channels */ + mixer_set(MIXER_SET_IRQ, (1 << (SB_IRQ / 2 - 1))); + mixer_set(MIXER_SET_DMA, (1 << SB_DMA_8 | 1 << SB_DMA_16)); + + /* register interrupt vector and enable irq */ + if ((s=sys_irqsetpolicy(SB_IRQ, IRQ_REENABLE, &irq_hook_id )) != OK) + panic("SB16DSP", "Couldn't set IRQ policy", s); + if ((s=sys_irqenable(&irq_hook_id)) != OK) + panic("SB16DSP", "Couldn't enable IRQ", s); + + DspAvail = 1; + return OK; +} + + +/*===========================================================================* + * dsp_reset + *===========================================================================*/ +PRIVATE int dsp_reset() +{ + int i; + + dsp_outb(DSP_RESET, 1); + for(i = 0; i < 1000; i++); /* wait a while */ + dsp_outb(DSP_RESET, 0); + + for(i = 0; i < 1000 && !(dsp_inb(DSP_DATA_AVL) & 0x80); i++); + + if(dsp_inb(DSP_READ) != 0xAA) return EIO; /* No SoundBlaster */ + + DmaBusy = -1; + DmaDone = 1; + + return OK; +} + + +/*===========================================================================* + * dsp_command + *===========================================================================*/ +PRIVATE int dsp_command(value) +int value; +{ + int i, status; + + for (i = 0; i < SB_TIMEOUT; i++) { + if((dsp_inb(DSP_STATUS) & 0x80) == 0) { + dsp_outb(DSP_COMMAND, value); + return OK; + } + } + + dprint("sb16: SoundBlaster: DSP Command(%x) timeout\n", value); + return -1; +} + + +/*===========================================================================* + * dsp_set_size + *===========================================================================*/ +static int dsp_set_size(size) +unsigned int size; +{ + dprint("dsp_set_size(): set fragment size to %u\n", size); + + /* Sanity checks */ + if(size < DSP_MIN_FRAGMENT_SIZE || size > DSP_MAX_FRAGMENT_SIZE || size % 2 != 0) { + return EINVAL; + } + + DspFragmentSize = size; + + return OK; +} + + +/*===========================================================================* + * dsp_set_speed + *===========================================================================*/ +static int dsp_set_speed(speed) +unsigned int speed; +{ + dprint("sb16: setting speed to %u, stereo = %d\n", speed, DspStereo); + + if(speed < DSP_MIN_SPEED || speed > DSP_MAX_SPEED) { + return EPERM; + } + + /* Soundblaster 16 can be programmed with real sample rates + * instead of time constants + * + * Since you cannot sample and play at the same time + * we set in- and output rate to the same value + */ + + dsp_command(DSP_INPUT_RATE); /* set input rate */ + dsp_command(speed >> 8); /* high byte of speed */ + dsp_command(speed); /* low byte of speed */ + dsp_command(DSP_OUTPUT_RATE); /* same for output rate */ + dsp_command(speed >> 8); + dsp_command(speed); + + DspSpeed = speed; + + return OK; +} + + +/*===========================================================================* + * dsp_set_stereo + *===========================================================================*/ +static int dsp_set_stereo(stereo) +unsigned int stereo; +{ + if(stereo) { + DspStereo = 1; + } else { + DspStereo = 0; + } + + return OK; +} + + +/*===========================================================================* + * dsp_set_bits + *===========================================================================*/ +static int dsp_set_bits(bits) +unsigned int bits; +{ + /* Sanity checks */ + if(bits != 8 && bits != 16) { + return EINVAL; + } + + DspBits = bits; + + return OK; +} + + +/*===========================================================================* + * dsp_set_sign + *===========================================================================*/ +static int dsp_set_sign(sign) +unsigned int sign; +{ + dprint("sb16: set sign to %u\n", sign); + + DspSign = (sign > 0 ? 1 : 0); + + return OK; +} + + +/*===========================================================================* + * dsp_dma_setup + *===========================================================================*/ +PRIVATE void dsp_dma_setup(address, count) +phys_bytes address; +int count; +{ + 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 */ + + /* set DMA mode */ + dsp_outb(DMA8_MODE, (DmaMode == DEV_WRITE ? DMA8_AUTO_PLAY : DMA8_AUTO_REC)); + + dsp_outb(DMA8_ADDR, address >> 0); /* Low_byte of address */ + dsp_outb(DMA8_ADDR, address >> 8); /* High byte of address */ + dsp_outb(DMA8_PAGE, address >> 16); /* 64K page number */ + dsp_outb(DMA8_COUNT, count >> 0); /* Low byte of count */ + dsp_outb(DMA8_COUNT, count >> 8); /* High byte of count */ + dsp_outb(DMA8_MASK, SB_DMA_8); /* Enable DMA channel */ + } 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 */ + + /* Set dma mode */ + dsp_outb(DMA16_MODE, (DmaMode == DEV_WRITE ? DMA16_AUTO_PLAY : DMA16_AUTO_REC)); + + dsp_outb(DMA16_ADDR, (address >> 1) & 0xFF); /* Low_byte of address */ + dsp_outb(DMA16_ADDR, (address >> 9) & 0xFF); /* High byte of address */ + dsp_outb(DMA16_PAGE, (address >> 16) & 0xFE); /* 128K page number */ + dsp_outb(DMA16_COUNT, count >> 1); /* Low byte of count */ + dsp_outb(DMA16_COUNT, count >> 9); /* High byte of count */ + dsp_outb(DMA16_MASK, SB_DMA_16 & 3); /* Enable DMA channel */ + } +} + + +/*===========================================================================* + * dsp_setup() + *===========================================================================*/ +PRIVATE void dsp_setup() +{ + /* Set current sample speed */ + dsp_set_speed(DspSpeed); + + /* Put the speaker on */ + if(DmaMode == DEV_WRITE) { + dsp_command (DSP_CMD_SPKON); /* put speaker on */ + + /* Program DSP with dma mode */ + dsp_command((DspBits == 8 ? DSP_CMD_8BITAUTO_OUT : DSP_CMD_16BITAUTO_OUT)); + } else { + dsp_command (DSP_CMD_SPKOFF); /* put speaker off */ + + /* Program DSP with dma mode */ + dsp_command((DspBits == 8 ? DSP_CMD_8BITAUTO_IN : DSP_CMD_16BITAUTO_IN)); + } + + /* Program DSP with transfer mode */ + if (!DspSign) { + dsp_command((DspStereo == 1 ? DSP_MODE_STEREO_US : DSP_MODE_MONO_US)); + } else { + dsp_command((DspStereo == 1 ? DSP_MODE_STEREO_S : DSP_MODE_MONO_S)); + } + + /* Give length of fragment to DSP */ + if (DspBits == 8) { /* 8 bit transfer */ + /* #bytes - 1 */ + dsp_command((DspFragmentSize - 1) >> 0); + dsp_command((DspFragmentSize - 1) >> 8); + } else { /* 16 bit transfer */ + /* #words - 1 */ + dsp_command((DspFragmentSize - 1) >> 1); + dsp_command((DspFragmentSize - 1) >> 9); + } +} + + +/*===========================================================================* + * 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; +} \ No newline at end of file