📄 sb16_dsp.c
字号:
/* 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"_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_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) );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 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; } 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); 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 */ /* 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; DmaFillNext = (DmaFillNext + 1) % DMA_NR_OF_BUFFERS; if(BufReadNext == BufFillNext) { 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 */ sb16_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 */}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -