home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyrighted as an unpublished work.
- * (c) Copyright 1991 Brian Smith
- * All rights reserved.
- *
- * Read the LICENSE file for details on distribution and use.
- *
- */
-
-
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/file.h>
- #include <sys/ioctl.h>
- #include <sys/dir.h>
- #include <sys/buf.h>
- #include <sys/iobuf.h>
- #include <sys/signal.h>
- #include <sys/user.h>
- #include <sys/sysmacros.h>
- #include <sys/dma.h>
- #include <sys/cmn_err.h>
- #include <sys/errno.h>
- #include <sys/inline.h>
- #include "sb.h"
-
- /* defs needed because of ommisions ISC! */
- #ifndef DMA_Rdmode
- #define DMA_Rdmode 0x44
- #endif
- #ifndef DMA_Wrmode
- #define DMA_Wrmode 0x48
- #endif
- #ifndef DMA_BLOCK
- #define DMA_BLOCK 0
- #endif
- #ifndef DMA_NBLOCK
- #define DMA_NBLOCK 1
- #endif
-
- extern ushort sb_configured;
- extern ushort sb_dma_chan;
- extern ushort sb_interrupt;
-
- /* GLOBALS */
- struct sb_stat_type sb_status; /* Soundblaster Status */
- extern time_t lbolt;
-
-
- /*
- * reset DSP chip, and return TRUE if successful
- */
- static int dsp_reset()
- {
- int i;
- register unsigned char rc;
-
- /* reset dsp */
- outb(DSP_RESET, 0x01);
- tenmicrosec();
- outb(DSP_RESET, 0x00);
- for (i=0; i<200; i++)
- {
- rc = (unsigned char)inb(DSP_RDAVAIL);
- if (rc & 128)
- {
- rc = inb(DSP_RDDATA);
- if (rc == 0xAA)
- break;
- }
-
- i--;
- continue;
- }
- if (i>=200)
- cmn_err(CE_WARN, "SoundBlaster(tm) DSP failed initialization\n");
-
- /* reset sampling speed */
- dsp_speed();
-
- return(TRUE);
- }
-
-
- /*
- * program the DSP's time constant: the sampling/output rate
- */
- int dsp_speed()
- {
- unchar time_constant;
- unchar dsp_return;
-
- time_constant = (char)(256 - (1000000/sb_status.dsp_speed));
-
- /* send command SET_TIME_CONSTANT (0x40) */
- do
- {
- dsp_return = inb(DSP_STATUS);
- } while (dsp_return & (1<<7));
- outb(DSP_COMMAND, 0x40);
-
- /* send one byte time_constant */
- do
- {
- dsp_return = inb(DSP_STATUS);
- } while (dsp_return & (1<<7));
- outb(DSP_COMMAND, time_constant);
-
- return(TRUE);
- }
-
-
- /*
- * called at OS startup time to initialize the SoundBlaster
- */
- int sbinit()
- {
- int failure = FALSE;
-
- if (!sb_configured)
- cmn_err(CE_WARN, "sbinit(): SoundBlaster(tm) not configured\n");
-
- sbtab[0].b_actf = NULL;
- sbtab[0].b_actl = NULL;
- sbtab[0].b_active = FALSE;
-
- /* init cms chips' state */
- sb_status.cms_open = NOT_OPEN;
- sb_status.cms_waiting = 0;
-
- /* init fm chips' state */
- sb_status.fm_open = NOT_OPEN;
- sb_status.fm_waiting = 0;
-
- /* init dsp chip(s)' state */
- sb_status.dsp_open = NOT_OPEN;
- sb_status.dsp_speed = 11000;
- sb_status.dsp_compression = ADCPM_8;
- if (!dsp_reset())
- failure = TRUE;
-
- /* all OK, send notification to console */
- if (!failure)
- {
- cmn_err(CE_CONT, "SoundBlaster(tm) is recognized and initialized\n");
- #ifdef DEBUG
- cmn_err(CE_CONT, "operating upon DMA channel %d\n", sb_dma_chan);
- cmn_err(CE_CONT, "operating upon interrupt vector %d\n", sb_interrupt);
- #endif
- }
- else
- {
- cmn_err(CE_WARN, "SoundBlaster(tm) initialization failed\n");
- sb_status.dsp_open = OPEN_READ | OPEN_WRITE;
- sb_status.fm_open = OPEN_READ | OPEN_WRITE;
- sb_status.cms_open = OPEN_READ | OPEN_WRITE;
- }
-
- return(0);
- }
-
-
- /*
- * turn the dsp voice on if param is true
- */
- void dsp_voice(on)
- int on;
- {
- unchar dsp_return;
-
- do
- {
- dsp_return = inb(DSP_STATUS);
- } while (dsp_return & (1<<7));
-
- if (on)
- {
- outb(DSP_COMMAND, 0xD1);
- #ifdef DEBUG
- cmn_err(CE_CONT, "dsp_voice(): voice on\n");
- #endif
- }
- else
- {
- outb(DSP_COMMAND, 0xD3);
- #ifdef DEBUG
- cmn_err(CE_CONT, "dsp_voice(): voice off\n");
- #endif
- }
-
- return;
- }
-
-
- /*
- * grabs the DSP chip for a process.
- * sets u.u_error to EBUSY if already opened by other device
- */
- static void dsp_open(flag)
- int flag;
- {
- int old_pri;
- int rc;
-
-
- /* check if already open */
- old_pri = spl6();
- if (sb_status.dsp_open)
- {
- u.u_error = EBUSY;
- splx(old_pri);
- return;
- }
-
- /* grab dma and device */
- dma_alloc(SB_DMA_CHAN, DMA_BLOCK);
- dsp_reset();
- dsp_voice(FALSE);
- if (flag & FWRITE)
- {
- if (flag & FREAD)
- {
- sb_status.dsp_open = OPEN_WRITE | OPEN_READ;
- #ifdef DEBUG
- cmn_err(CE_CONT, "open read/write %d\n", flag);
- #endif
- }
- else
- {
- sb_status.dsp_open = OPEN_WRITE;
- #ifdef DEBUG
- cmn_err(CE_CONT, "open write %d\n", flag);
- #endif
- dsp_voice(TRUE);
- }
- }
- else if (flag & FREAD)
- {
- sb_status.dsp_open = OPEN_READ;
- #ifdef DEBUG
- cmn_err(CE_CONT, "open read/write %d\n", flag);
- #endif
- }
- else
- {
- #ifdef DEBUG
- cmn_err(CE_CONT, "unknown flags on open %d\n", flag);
- #endif
- u.u_error = ENXIO;
- }
-
- /* END CRITICAL */
- splx(old_pri);
-
- return;
- }
-
-
- /*
- * grabs the FM chips for a process.
- * sets u.u_error to EBUSY if already opened by other device
- */
- static void fm_open(flag)
- int flag;
- {
- int old_pri;
- int rc;
-
-
- /* check if already open */
- old_pri = spl6();
- if (sb_status.fm_open != NOT_OPEN)
- {
- u.u_error = EBUSY;
- splx(old_pri);
- return;
- }
-
- /* grab device and affirm that only openable with write permission */
- fm_reset();
- if (flag & FWRITE)
- sb_status.fm_open = OPEN_WRITE;
- else
- u.u_error = ENXIO;
-
- /* END CRITICAL */
- splx(old_pri);
-
- return;
- }
-
-
-
- /*
- * multiplexes opens to dsp_open(), fm_open(), and cms_open()
- * depending upon which minor dev was used
- */
- int sbopen(dev, flag)
- int dev;
- int flag;
- {
- int minor_num;
-
- #ifdef DEBUG
- cmn_err(CE_CONT, "opened with flag %d\n", flag);
- #endif
-
- minor_num = minor(dev);
- switch (minor_num)
- {
- case SB_CMS_NUM:
- u.u_error = ENXIO;
- break;
- case SB_FM_NUM:
- fm_open(flag);
- break;
- case SB_DSP_NUM:
- dsp_open(flag);
- break;
- default:
- u.u_error = ENXIO;
- }
-
- return(0);
- }
-
-
- /*
- * Release and reset the dsp chip
- */
- void dsp_close()
- {
- int old_pri;
-
- old_pri = spl6();
- sb_status.dsp_open = NOT_OPEN;
- dsp_reset();
- dsp_voice(FALSE);
- dma_relse(SB_DMA_CHAN);
- splx(old_pri);
-
- return;
- }
-
-
- /*
- * Release and reset the fm chip
- */
- void fm_close()
- {
- int old_pri;
-
- old_pri = spl6();
- sb_status.fm_open = NOT_OPEN;
- fm_reset();
- splx(old_pri);
-
- return;
- }
-
-
- /*
- * Multiplexes between the closes for dsp, fm, and cms chips
- */
- int sbclose(dev)
- int dev;
- {
- int minor_num;
-
- minor_num = minor(dev);
- switch (minor_num)
- {
- case SB_CMS_NUM:
- break;
- case SB_FM_NUM:
- fm_close();
- break;
- case SB_DSP_NUM:
- dsp_close();
- break;
- default:
- u.u_error = ENXIO;
- }
-
- return(0);
- }
-
-
- /*
- * start DMA rolling for DSP
- * only used by DSP chips, so no need for multiplexing on minor num
- */
- int sb_iostart()
- {
- int old_pri;
- paddr_t phys_address;
- unsigned char tmp_byte;
- unsigned int length;
-
- /* begin critical */
- old_pri = spl6();
-
- /* check for invalid length of buf */
- if ((sbtab[0].b_actf->b_bcount < 1) || (sbtab[0].b_actf->b_bcount > 0xFFFF))
- {
- cmn_err(CE_WARN, "sb_iostart(): invalid length of buf, %d\n",
- sbtab[0].b_actf->b_bcount);
-
- /* artificially terminate */
- sbtab[0].b_actf->b_resid = 0;
- sbtab[0].b_actf = sbtab[0].b_actf->av_forw;
- iodone(sbtab[0].b_actf);
- splx(old_pri);
- return(0);
- }
-
- /* mark device in sbtab as active */
- sbtab[0].b_active = TRUE;
-
- /* prep DMA channel */
- phys_address = vtop(paddr(sbtab[0].b_actf), sbtab[0].b_actf->b_proc);
- if (sbtab[0].b_actf->b_flags & B_READ)
- dma_param(SB_DMA_CHAN, DMA_Rdmode, phys_address,
- sbtab[0].b_actf->b_bcount);
- else
- dma_param(SB_DMA_CHAN, DMA_Wrmode, phys_address,
- sbtab[0].b_actf->b_bcount);
- dma_enable(SB_DMA_CHAN);
-
- /* prep SoundBlaster for 8-bit DMA */
- do {
- tmp_byte = inb(DSP_STATUS);
- } while (tmp_byte & (1<<7));
- if (sbtab[0].b_actf->b_flags & B_READ)
- outb(DSP_COMMAND, 0x24);
- else
- outb(DSP_COMMAND, 0x14);
-
- /* prep SoundBlaster for length */
- length = sbtab[0].b_actf->b_bcount-1;
- do {
- tmp_byte = inb(DSP_STATUS);
- } while (tmp_byte & (1<<7));
- tmp_byte = length & 0xFF;
- outb(DSP_COMMAND, tmp_byte);
- do {
- tmp_byte = inb(DSP_STATUS);
- } while (tmp_byte & (1<<7));
- tmp_byte = (length & 0xFF00) >> 8;
- outb(DSP_COMMAND, tmp_byte);
-
- /* end critical */
- splx(old_pri);
-
- return(0);
- }
-
-
- /*
- * write sound samples to dsp
- */
- int sb_strategy(bufhead)
- register struct buf *bufhead;
- {
- int old_pri;
-
- /* start critical section */
- old_pri = spl6();
-
- /* prep buffer for addition to queue */
- bufhead->b_start = lbolt;
- bufhead->av_forw = NULL;
-
- /* add buffer to queue */
- if (sbtab[0].b_actf == NULL)
- {
- /* add to empty queue */
- sbtab[0].b_actf = bufhead;
- sbtab[0].b_actl = bufhead;
-
- /* start io */
- sb_iostart();
- }
- else
- {
- /* add to already started queue */
- sbtab[0].b_actl->av_forw = bufhead;
- sbtab[0].b_actl = bufhead;
- }
-
- /* end critical section */
- splx(old_pri);
-
- return(0);
- }
-
-
- /*
- * breaks up io requests so that DMA can be done
- */
- static int sb_breakup(bp)
- register struct buf *bp;
- {
- dma_breakup(sb_strategy, bp);
- return(0);
- }
-
-
- /*
- * Starts the DMA write to the Soundblaster
- */
- int dsp_write(dev)
- int dev;
- {
- physio(sb_breakup, 0, dev, B_WRITE);
- return(0);
- }
-
-
- /*
- * multiplexes writes to dsp, cm/s and fm chips
- */
- int sbwrite(dev)
- int dev;
- {
- int minor_num;
-
- minor_num = minor(dev);
- switch (minor_num)
- {
- case SB_CMS_NUM:
- cmn_err(CE_CONT, "sbwrite(): error, cms device accessed\n");
- u.u_error = ENXIO;
- break;
- case SB_FM_NUM:
- u.u_error = ENXIO;
- break;
- case SB_DSP_NUM:
- dsp_write(dev);
- break;
- default:
- cmn_err(CE_CONT, "sbwrite(): unknown minor device %d\n", minor_num);
- u.u_error = ENXIO;
- }
- return(0);
- }
-
-
- /*
- * Starts the DMA read from the Soundblaster
- */
- int dsp_read(dev)
- int dev;
- {
- physio(sb_breakup, 0, dev, B_READ);
- return(0);
- }
-
-
- /*
- * multiplexes read/writes to different functions
- */
- int sbread(dev)
- int dev;
- {
- int minor_num;
-
- minor_num = minor(dev);
- switch (minor_num)
- {
- case SB_CMS_NUM:
- cmn_err(CE_CONT, "sbread(): error, cms device accessed\n");
- u.u_error = ENXIO;
- break;
- case SB_FM_NUM:
- u.u_error = ENXIO;
- break;
- case SB_DSP_NUM:
- dsp_read(dev);
- break;
- default:
- cmn_err(CE_CONT, "sbread(): unknown minor device %d\n", minor_num);
- u.u_error = ENXIO;
- }
- return(0);
- }
-
-
- /*
- * minor control function for the dsp
- */
- void dsp_ioctl(cmd, arg1, arg2)
- int cmd;
- caddr_t arg1, arg2;
- {
- switch(cmd)
- {
- case DSP_IOCTL_RESET:
- dsp_reset();
- break;
- case DSP_IOCTL_SPEED:
- sb_status.dsp_speed = (int)arg1;
- dsp_speed();
- break;
- case DSP_IOCTL_VOICE:
- dsp_voice((int)arg1);
- break;
- default:
- break;
- }
-
- return;
- }
-
-
- /*
- * turns a note/key off
- */
- void fm_key_off(voice_num)
- int voice_num;
- {
- unsigned char reg_num;
-
- /* error checking to avoid munching kernel */
- if ((voice_num < 0) || (voice_num >= MAX_FM_NOTES))
- {
- u.u_error = EFAULT;
- return;
- }
-
- /* turn voice off */
- reg_num = (unsigned char)0xB0 + (unsigned char)voice_num;
- #ifdef DEBUG
- cmn_err(CE_CONT, "turning off voice for voice %d\n", voice_num);
- cmn_err(CE_CONT, "reg_num is %x\n", reg_num);
- #endif
- outb(FM_SELECT, reg_num);
- tenmicrosec();
- outb(FM_REG, 0);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- return;
- }
-
- /*
- * turns a key on, with the frequency and octave so indicated in the
- * low 2 bytes of the data integer
- */
- void fm_key_on(usr_note)
- int usr_note;
- {
- register unsigned char reg_num;
- int tmp_int;
-
- #ifdef DEBUG
- cmn_err(CE_CONT, "turning on voice for voice %d\n", note_num(usr_note));
- cmn_err(CE_CONT, "fnum_low (dec): %d\n", fnum_low(usr_note));
- cmn_err(CE_CONT, "fnum_low (hex): %x\n", fnum_low(usr_note));
- cmn_err(CE_CONT, "keyon_blk_fnum (dec): %d\n", keyon_blk_fnum(usr_note));
- cmn_err(CE_CONT, "keyon_blk_fnum (hex): %x\n", keyon_blk_fnum(usr_note));
- #endif
-
- /* put out first byte */
- reg_num = (unsigned char)0xA0 + note_num(usr_note);
- #ifdef DEBUG
- cmn_err(CE_CONT, "reg_num is %x\n", reg_num);
- #endif
- outb(FM_SELECT, reg_num);
- tenmicrosec();
- outb(FM_REG, fnum_low(usr_note));
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* put out second byte */
- reg_num = (unsigned char)0xB0 + note_num(usr_note);
- #ifdef DEBUG
- cmn_err(CE_CONT, "reg_num is %x\n", reg_num);
- #endif
- outb(FM_SELECT, reg_num);
- tenmicrosec();
- outb(FM_REG, keyon_blk_fnum(usr_note));
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- return;
- }
-
- /* at this point, it just turns all notes to off */
- int fm_reset()
- {
- int i;
-
- /* must be initialized? */
- outb(FM_SELECT, 1);
- tenmicrosec();
- outb(FM_REG, 0);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* dispense for time being */
- for (i=0; i<MAX_FM_NOTES; i++)
- fm_key_off(i);
-
- return(0);
- }
-
-
- /*
- * set characteristics on a voice
- */
- void fm_set_voice(usr_character)
- sb_fm_character *usr_character;
- {
- register unsigned char op_cell_num;
- int cell_offset;
- sb_fm_character voice_data;
- int i;
-
- /* copy in characteristics */
- if (copyin(usr_character, &voice_data, sizeof(sb_fm_character)) == -1)
- {
- #ifdef DEBUG
- cmn_err(CE_CONT, "fm_set_voice(): bad address\n");
- #endif
- u.u_error = EFAULT;
- return;
- }
-
- /* echo voice characteristics */
- #ifdef DEBUG
- cmn_err(CE_CONT, "setting voice number %d\n", voice_data.voice_num);
- cmn_err(CE_CONT, "setting voice number(hex) %x\n", voice_data.voice_num);
- cmn_err(CE_CONT, "data: ");
- for (i=0; i<16; i++)
- cmn_err(CE_CONT, "%x ", (unsigned int)voice_data.data[i]);
- cmn_err(CE_CONT, "\n");
- #endif
-
- /* check on voice_num range */
- if ((voice_data.voice_num >= MAX_FM_NOTES) || (voice_data.voice_num < 0))
- {
- cmn_err(CE_CONT, "fm_set_voice(): voice number out of range\n");
- u.u_error = EFAULT;
- }
- cell_offset = voice_data.voice_num%3 + ((voice_data.voice_num / 3) << 3);
-
- /* set sound characteristic */
- op_cell_num = 0x20 + (char)cell_offset;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 20-35 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[0]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
- op_cell_num += 3;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 20-35 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[1]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* set level/output */
- op_cell_num = 0x40 + (char)cell_offset;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 40-55 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[2]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
- op_cell_num += 3;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 40-55 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[3]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* set Attack/Decay */
- op_cell_num = 0x60 + (char)cell_offset;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 60-75 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[4]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
- op_cell_num += 3;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 60-75 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[5]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* set Sustain/Release */
- op_cell_num = 0x80 + (char)cell_offset;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 80-95 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[6]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
- op_cell_num += 3;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 80-95 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[7]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* set Wave Select */
- op_cell_num = 0xE0 + (char)cell_offset;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for E0-F5 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[8]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
- op_cell_num += 3;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for E0-F5 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[9]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* set Feedback/Selectivity */
- op_cell_num = (unsigned char)0xC0 + (unsigned char)voice_data.voice_num;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for C0-C8 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[10]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- return;
- }
-
-
- /*
- * set characteristics on an opcell
- */
- void fm_set_opcell(usr_character)
- sb_fm_character *usr_character;
- {
- register unsigned char op_cell_num;
- int cell_offset;
- sb_fm_character voice_data;
- int i;
-
- /* copy in characteristics */
- if (copyin(usr_character, &voice_data, sizeof(sb_fm_character)) == -1)
- {
- #ifdef DEBUG
- cmn_err(CE_CONT, "bad address\n");
- #endif
- u.u_error = EFAULT;
- return;
- }
-
- /* echo voice characteristics */
- #ifdef DEBUG
- cmn_err(CE_CONT, "setting opcell number %d\n", voice_data.voice_num);
- cmn_err(CE_CONT, "setting opcell number(hex) %x\n", voice_data.voice_num);
- cmn_err(CE_CONT, "data: ");
- for (i=0; i<8; i++)
- cmn_err(CE_CONT, "%x ", (unsigned int)voice_data.data[i]);
- cmn_err(CE_CONT, "\n");
- #endif
-
- /* check on opcell range */
- if ((voice_data.voice_num >= 2*MAX_FM_NOTES) || (voice_data.voice_num < 0))
- {
- cmn_err(CE_CONT, "opcell number out of range\n");
- u.u_error = EFAULT;
- }
-
- /* set sound characteristic */
- op_cell_num = 0x20 + (char)voice_data.voice_num;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 20-35 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[0]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* set level/output */
- op_cell_num = 0x40 + (char)voice_data.voice_num;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 40-55 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[1]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* set Attack/Decay */
- op_cell_num = 0x60 + (char)voice_data.voice_num;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 60-75 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[2]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* set Sustain/Release */
- op_cell_num = 0x80 + (char)voice_data.voice_num;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for 80-95 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[3]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* set Wave Select */
- op_cell_num = 0xE0 + (char)voice_data.voice_num;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for E0-F5 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[4]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- /* set Feedback/Selectivity */
- op_cell_num = (unsigned char)0xC0 + (unsigned char)voice_data.voice_num;
- #ifdef DEBUG
- cmn_err(CE_CONT, "op_cell for C0-C8 = %x\n", op_cell_num);
- #endif
- outb(FM_SELECT, op_cell_num);
- tenmicrosec();
- outb(FM_REG, voice_data.data[5]);
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- return;
- }
-
- /*
- * set the register which contains the keyon/off for rhythm, and depth flags
- */
- void fm_set_rhythm(new_rhythm)
- int new_rhythm;
- {
- /* herf it in */
- outb(FM_SELECT, 0xBD);
- tenmicrosec();
- outb(FM_REG, lobyte(new_rhythm));
- tenmicrosec();
- tenmicrosec();
- tenmicrosec();
-
- return;
- }
-
-
- /*
- * The only control for the FM chips. The rest belongs in user code.
- */
- void fm_ioctl(cmd, arg1, arg2)
- int cmd;
- caddr_t arg1, arg2;
- {
- switch(cmd)
- {
- case FM_IOCTL_RESET:
- fm_reset();
- break;
- case FM_IOCTL_NOTE_ON:
- fm_key_on((int)arg1);
- break;
- case FM_IOCTL_NOTE_OFF:
- fm_key_off((int)arg1);
- break;
- case FM_IOCTL_SET_VOICE:
- fm_set_voice((sb_fm_character *)arg1);
- break;
- case FM_IOCTL_SET_OPCELL:
- fm_set_opcell((sb_fm_character *)arg1);
- break;
- case FM_IOCTL_SET_RHYTHM:
- fm_set_rhythm((int)arg1);
- break;
- default:
- break;
- }
-
- return;
- }
-
-
- /*
- * multiplex ioctl to different sub-devices (minor numbers)
- */
- int sbioctl(dev, cmd, arg1, arg2)
- int dev;
- int cmd;
- caddr_t arg1, arg2;
- {
- int minor_num;
-
- minor_num = minor(dev);
- switch (minor_num)
- {
- case SB_CMS_NUM:
- cmn_err(CE_CONT, "sbioctl cms\n");
- break;
- case SB_FM_NUM:
- fm_ioctl(cmd, arg1, arg2);
- break;
- case SB_DSP_NUM:
- dsp_ioctl(cmd, arg1, arg2);
- break;
- default:
- cmn_err(CE_CONT, "sbioctl unknown minor %d\n", minor_num);
- u.u_error = ENXIO;
- }
- return(0);
- }
-
-
- /*
- * responds to interrupt sent by DSP chips
- * and starts the next block of DMA (if one is queued)
- */
- int sbintr(vect)
- int vect;
- {
- int old_pri;
- unsigned char tmp_byte;
-
- /* ASSUME CRITICAL PRIORITY */
- old_pri = spl6();
-
- /* check for validity of interrupt */
- if (! sbtab[0].b_active)
- {
- cmn_err(CE_CONT, "sbintr(): spurious interrupt\n");
- splx(old_pri);
- return(0);
- }
-
- /* aknowledge interrupt */
- tmp_byte = inb(DSP_RDAVAIL);
-
- /* acknowledge as done to waiting thread and remove buf from queue */
- sbtab[0].b_actf->b_resid = 0;
- iodone(sbtab[0].b_actf);
- sbtab[0].b_actf = sbtab[0].b_actf->av_forw;
-
- /* mark device as inactive and service rest of queue (if not empty) */
- sbtab[0].b_active = FALSE;
- if (sbtab[0].b_actf != NULL)
- sb_iostart();
-
- /* end of critical section */
- splx(old_pri);
-
- return(0);
- }
-