*BSD News Article 6368


Return to BSD News archive

Newsgroups: comp.unix.bsd
Path: sserve!manuel.anu.edu.au!munnari.oz.au!hp9000.csc.cuhk.hk!uakari.primate.wisc.edu!zaphod.mps.ohio-state.edu!darwin.sura.net!mojo.eng.umd.edu!pandora.pix.com!lidl
From: lidl@pix.com (Kurt J. Lidl)
Subject: Sound Blaster Pro driver (re-post)
Message-ID: <BvzMx7.95B@pix.com>
Summary: beep, beep, whiz, boing!
Sender: news@pix.com (The News Subsystem)
Nntp-Posting-Host: pandora.pix.com
Organization: Pix -- The company with no adult supervision.
Date: Mon, 12 Oct 1992 02:58:17 GMT
Lines: 3186

This driver falls under the GNU copyleft.  Since I'm only including
the driver sources, you should get a copy of the Sound Blaster
Driver for the full details of the copyleft that this driver falls under.
The 1.5 release of the driver should be available from saffron.inset.com.
Obviously, you should check with Archie first for a closer ftp site.
(Search for SBlast-BSD-1.5)

The original author, Steve Haehnichen <shaehnic@ucsd.edu>, deserves a
round of applause for makeing his excellent driver available in source
format!

A word of warning.  Josh didn't finish whacking together a
"play" command (I just hack on things, Josh *programs* :-)  We also
haven't started with the rplayd stuff yet.  Josh is convinced that we
can smack some sense into rplayd, such that we can have that available
for the next Xtank release.  (We hope.)  In summary, all you get is
the hacked on driver and include files.

Also, the port of the driver, or something that it interfaces to in the 386BSD
system, seems to be pretty unstable.  Both playback of FM voices and
digitized sounds seem to work pretty good.  Recording frequently
crashes the system.  You gets what you pays for.

You would be best off to consider this an alpha quality port.

We're releasing this so others won't have to burn the same caffine and
lose sleep like we did to get a basic driver working.  Hopefully
someone else can get some of the bugs in the system tracked down...  I
will be out of town for the next week, so you can mail me, but I won't
respond :-)

To install the Sound Blaster driver into your 386BSD system, you will
need to re-build your kernel.  If you are not familar with this, don't
try adding this driver.  Don't ask me to build a kernel for you, I don't
have time for it, and my kernel will not boot on your system.

(Why you ask?  I have lots of hardware at non-standard enough places that
my kernel is hacked up quite a bit.)

So, the following instructions ought to get you up and running.

-Kurt

Directory /sys/i386/conf:

In your system configuration file, you need to put in the following lines:

device          sb0     at isa? port "IO_SBP" irq 5 vector sb_intr
pseudo-device   sb

In your files.i386 file, you should add the following:

i386/isa/sb_driver.c            optional sb device-driver

Directory /usr/i386/isa:

In the isa.h file, you should add the following to the correct place in
the file.  (You can't miss it!)  If you don't run your SBP at the
default I/O port, you will need to change this.

#define IO_SBP                0x220           /* Sound Blaster Pro */
                                              /* 0x238 - 0x277 Open */

Directory /sys/i386/i386:

You will need to add the following to the conf.c file.
(I put mine right after the block of code for the line printer)

#include "sb.h"
#if NSB > 0
int   sbopen(), sbclose(), sbread(), sbwrite(), sbioctl(), sbselect();
#else
#define       sbopen          enxio
#define       sbclose         enxio
#define       sbread          enxio
#define       sbwrite         enxio
#define       sbioctl         enxio
#define       sbselect        enxio
#endif

And at the end of the cdevtab initialization, you should add:

	{ sbopen,       sbclose,        sbread,         sbwrite,        /*10*/
	sbioctl,      enodev,         nullop,         NULL,
	sbselect,     enodev,         NULL },

The number in comments to the right is the next higher number in the
table than the preceding number, in HEX.  Remember this number, you will
need it later.

Add the following to your /dev/MAKEDEV script:

sb)
        major=16
        umask 0
        mknod sb_fm c $major 1
        mknod sb_dsp c $major 2
        mknod sb_midi c $major 3
        mknod sb_mixer c $major 4
        umask 77
        ;;

You should set "major" equal to the number you had in the cdevtab entry.
This should be a *decimal* number, not a hex number.

Unpack the following shar file, then copy the contents into the
/sys/i386/isa direcory.  "config" your kernel, "make depend", and
rebuild.

Good luck.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# Contents:  sb_driver.c sb_regs.h sblast.h
# Wrapped by lidl@pandora.pix.com on Sun Oct 11 22:24:47 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo '          "shar: End of archive 1 (of 1)."'
if test -f 'sb_driver.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sb_driver.c'\"
else
  echo shar: Extracting \"'sb_driver.c'\" \(66644 characters\)
  sed "s/^X//" >'sb_driver.c' <<'END_OF_FILE'
X/*======================================================================
X
X   Device driver for the Creative Labs Sound Blaster card.
X   [ This file is a part of SBlast-BSD-1.5 ]
X
X   Steve Haehnichen <shaehnic@ucsd.edu>
X 
X   sb_driver.c,v 2.2 1992/09/14 03:09:22 steve Exp
X
X   Copyright (C) 1992 Steve Haehnichen.
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X * sb_driver.c,v
X * Revision 2.2  1992/09/14  03:09:22  steve
X * Released as distribution v1.5.
X * Mostly stable, still lacking stereo recording.
X *
X * Revision 2.1  1992/08/02  21:54:59  steve
X * Added select handler and streamlined a few things.
X * Now allows read/write on a single open.
X *
X * Revision 2.0  1992/07/13  01:25:14  steve
X * Promoting to v2.0 with start of CVS management.
X *
X * Revision 1.6  1992/07/13  01:14:21  steve
X * Fixed version printing
X *
X * Revision 1.5.1.1  1992/07/13  00:39:30  steve
X * Stable double-buffering Sound Blaster driver.
X *
X * Revision 1.30  1992/06/20  04:41:29  steve
X * About to re-write buffering.
X *
X * Revision 1.29  1992/06/13  01:46:43  steve
X * Released in SBlast-BSD-1.5
X
X======================================================================*/
X
X#include <sb.h>
X#if NSB > 0
X
X#if defined(__bsdi__) || defined(__386BSD__)
X#include "param.h"
X#include "systm.h"
X#include "ioctl.h"
X#include "tty.h"
X#include "proc.h"
X#include "user.h"
X#include "conf.h"
X#include "file.h"
X#include "uio.h"
X#include "kernel.h"
X#include "syslog.h"
X#include "malloc.h"
X#include "vm/vm.h"
X#include "vm/vm_kern.h"
X
X#include "i386/isa/isa_device.h"
X#include "i386/isa/sblast.h"
X#include "i386/isa/sb_regs.h"
X#define isa_dev isa_device
X#define ESUCCESS 0
X#define PSLEP PPAUSE|PCATCH
X#define tenmicrosec() DELAY(10)
X#define kvtophys kvtop
X#ifdef KOLVIR
X#undef DEBUG		/* I turn on DEBUG for other files,
X			but don't want it here--too verbose. */
X#endif
X#else
X
X#include <sys/user.h>
X#include <sys/file.h>
X#include <i386/ipl.h>
X#include <i386/pio.h>
X#include <i386at/atbus.h>
X
X#include <i386at/sblast.h>	/* User-level structures and defs */
X#include "sb_regs.h"		/* Register and command values */
X#endif
X
X/*
X * Defining DEBUG will print out lots of useful kernel activity
X * information.  If there are problems, it's the first thing you
X * should do.  You can also define FULL_DEBUG for more info than
X * you probably want.  (If you consider any DEBUG useless, just
X * turn it into a FULL_DEBUG.)
X */
X/* #define DEBUG */
X/* #define FULL_DEBUG */
X#if defined (FULL_DEBUG) && !defined (DEBUG)
X#  define DEBUG
X#endif
X
X/*
X * The Mixer is an odd deal.  I think Mixer ioctls should be
X * allowed on any device.  It's very safe.
X * If all your users complain that other users are messing up the
X * mixer while they are playing, well, undef this and make
X * mixer open's exclusive.
X */
X#define 	ALWAYS_ALLOW_MIXER_IOCTLS
X
X/*
X * This is the highest DSP speed that will use "Low-Speed" mode.
X * Anything greater than this will use the "High-Speed" mode instead.
X */
X#define		MAX_LOW_SPEED	22222
X
X/*
X * This is the speed the DSP will start at when you reboot the system.
X * If I can read the speed registers, then maybe I should just use
X * the current card setting.  Does it matter?
X * (43478 is the closest we can get to 44 KHz)
X * Make sure this is a valid speed for your card!
X */
X#define		INITIAL_DSP_SPEED	43478
X
X/*
X * SB Device minor numbers
X * I have copied the numbers used in Brian Smith's ISC Unix driver,
X * with the faint hope of future compatibility.
X * Oh, make sure this is a valid speed for your card!
X * The listed device names are simply the ones that I am using.
X * Note that there is absolutely no CMS support in this driver.
X */
X#define SB_CMS_NUM  	0	/* /dev/sb_cms   CMS square wave chips   */
X#define SB_FM_NUM    	1	/* /dev/sb_fm    FM synthesis chips      */
X#define SB_DSP_NUM    	2	/* /dev/sb_dsp   DSP DAC/ADC             */
X#define SB_MIDI_NUM   	3	/* /dev/sb_midi  MIDI port               */
X#define SB_MIXER_NUM  	4	/* /dev/sb_mixer Non-exclusive-use Mixer */
X#define SB_BIGGEST_MINOR 4	/* Highest minor number used.            */
X
X/*
X * This is the "channel" and IRQ used for the FM timer functions.
X * Since I can't seem to get one to trigger yet,
X * I don't know what they should be.  (see fm_delay() comments)
X */
X#define TIMER_CHANNEL		FM_BOTH
X#define FM_TIMER_IRQ		8
X
X/*
X * Here's the debugging macro I use here and there, so I can turn
X * them all on or off in one place.
X */
X#ifdef DEBUG
X#  define DPRINTF(x)	printf x
X#else
X#  define DPRINTF(x)
X#endif
X
X/* Unix interrupt priority.
X   I set it to just above Hard Disk priority to keep things smooth. */
X#define INTR_PRIORITY	(PRIBIO - 5)
X
X#define GOOD 1
X#define FAIL 0
X#define ON   1
X#define OFF  0
X
X/* These are used to indicate what the DSP last did,
X   and can safely do again without preparation */
X#define NONE_READY	0
X#define READ_READY	1
X#define WRITE_READY	2
X
X/* These are the possible values for argument 'rw' in the select handlers.
X   Shouldn't they be in an include file somewhere? */
X#define SELECT_READ	1
X#define SELECT_WRITE	2
X
X/* Number of "cycles" in one second.  Shouldn't this be somewhere else? */
X#define HZ 		100
X
X#define TIMEOUT		(10 * HZ) /* FM interrupt patience in clock ticks */
X
X/* This sets dsp.timeout to an appropriate number of ticks */
X#define COMPUTE_DSP_TIMEOUT \
X  { dsp.timeout = HZ + (HZ * dsp.bufsize / dsp.speed); }
X
X/* Semaphore return codes.  This tells the reason for the wakeup(). */
X#define WOKEN_BY_INTERRUPT	1
X#define WOKEN_BY_TIMEOUT	2
X
X/* How many times should it cycle to wait for a DSP acknowledge? */
X#define DSP_LOOP_MAX		1000
X
X/* Interrupt-generating device units.  Just arbitrary numbers. */
X#define DSP_UNIT		0
X#define FM_TIMER_UNIT		1
X#define NUM_UNITS		2 /* How many units above */
X
Xenum { PLAY, RECORD };		/* DSP sampling directions */
X
X#define LOWBYTE(x)	((x) & 0x00ff)
X#define HIBYTE(x)	(((x) & 0xff00) >> 8)
X
X/* Handy macro from Brian Smith's/Lance Norskog's driver */
X#define crosses_64k_boundary(a,b) ((((int)(a) & 0xffff) + (b)) > 0x10000)
X
X/*
X * This is the DSP memory buffer size.  Choose wisely. :-)
X * I started with 21K buffers, and then later changed to 64K buffers.
X * This hogs more kernel memory, but gives fewer Pops and
X * needs servicing less often.  Adjust to taste.
X * Note that it must be an EVEN number if you want stereo to work.
X * (See dsp_find_buffers() for a better explanation
X * Note that the same amount of memory (192K) is hogged, regardless of
X * the buffer size you choose.  This makes it easier to allocate page-safe
X * buffers, but should probably be changed to attempt to pack more into
X * one 64K page.  Be really sure you understand what you are doing if
X * you change the size of memory_chunk[].
X * 64K is the biggest DSP_BUF_SIZE you can have.
X * Smaller buffer sizes make aborts and small reads more responsive.
X * For real-time stuff, perhaps ~10K would be better.
X * Note that you can now change this to any smaller value with the ioctl
X * DSP_IOCTL_BUFSIZE, so you are probably best off with a 64K limit.
X */
X#define DSP_BUF_SIZE	(64 * 1024)
X#define DSP_CHUNK_SIZE	(2*DSP_BUF_SIZE+(64*1024))
X#if !defined(__bsdi__) && !defined(__386BSD__)
Xstatic char memory_chunk[DSP_CHUNK_SIZE]; /* Be careful!  See above */
X#endif
X
X
X/* This structure will be instantiated as a single global holder
X   for all DSP-related variables and flags. */
Xstruct sb_dsp_type
X{
X  int	speed;			/* DSP sampling rate */
X  int	timeout;		/* Timeout for one DSP buffer */
X  sbBYTE	compression;		/* Current DAC decompression mode */
X  sbFLAG	hispeed;		/* 1 = High Speed DSP mode, 0 = low speed */
X  sbFLAG  in_stereo;		/* 1 = currently in stereo, 0 = mono */
X  sbBYTE	start_command;		/* current DSP start command */
X  int	error;			/* Current error status on read/write */
X  int	semaphore;		/* Silly place-holder int for dsp_dma_start */
X  void  (*cont_xfer)(void);	/* Function to call to continue a DMA xfer */
X  char	*buf[2];		/* Two pointers to mono-page buffers */
X  int	phys_buf[2];		/* Physical addresses for dsp buffers */
X  sbFLAG	full[2];		/* True when that buffer is full */
X  int	used[2];		/* Buffer bytes used by read/write */
X  sbBYTE  active;			/* The buffer currently engaged in DMA */
X  sbBYTE  hi;			/* The buffer being filled/emptied by user */
X  int	bufsize;		/* DMA transfer buffer size */
X  int	last_xfer_size;		/* How many bytes we last transfered */
X  int   overrun_errno;		/* errno code for a DSP overrun (0 to ignore)*/
X  int	ver_major, ver_minor;	/* Major and Minor version number */
X  sbBYTE	state;			/* Either READ_READY, WRITE_READY, or 0 */
X};
Xstatic struct sb_dsp_type dsp;	/* 'dsp' structure used everywhere */
X
X/* This will be used as a global holder for the current Sound Blaster
X   general status values. */
Xstruct sb_status_type
X{
X  sbFLAG	dsp_in_use;		/* DSP or MIDI open for reading or writing */
X  sbFLAG  fm_in_use;		/* FM open for ioctls */
X  sbFLAG  cms_in_use;		/* CMS open for ioctls */
X  
X  sbFLAG  alive;                  /* Card present? */
X  unsigned int addr;		/* Sound Blaster card address */
X  int  *wake[NUM_UNITS];	/* What to wakeup on interrupt */
X
X  /* Select handler stuff, one entry for each "device" */
X  struct proc *selects[SB_BIGGEST_MINOR];
X  sbFLAG sel_coll[SB_BIGGEST_MINOR];
X  struct proc *sig_proc[SB_BIGGEST_MINOR];
X  int flags[SB_BIGGEST_MINOR];	/* fcntl flags */
X};
Xstatic struct sb_status_type status; /* Global current status */
X
X/*
X * Here is the screwey register ordering for the first operator
X * of each FM cell.  The second operators are all +3 from the first
X * operator of the same cell.
X */
Xstatic const char fm_op_order[] =
X{ 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 };
X
X#if defined(__bsdi__) || defined(__386BSD__)
X#define sb_probe sbprobe
X#define sb_attach sbattach
X#define sb_open sbopen
X#define sb_close sbclose
X#define sb_read sbread
X#define sb_write sbwrite
X#define sb_ioctl sbioctl
X#define sb_select sbselect
X#endif
X
X/*
X * Forward declarations galore!
X */
Xint sb_probe (struct isa_dev *dev);
Xint sb_attach (struct isa_dev *dev);
Xint sb_intr (int unit);
Xint sb_open (dev_t dev, int flags);
Xint sb_close (dev_t dev, int flags);
Xint sb_ioctl (dev_t dev, int cmd, caddr_t arg, int mode);
Xint sb_read (int dev, struct uio *uio);
Xint sb_write (int dev, struct uio *uio);
Xint sb_select (int dev, int rw);
Xstatic void sb_sendb (unsigned select_addr, sbBYTE reg,
X		      unsigned data_addr, sbBYTE value);
Xstatic int dsp_reset (void);
Xstatic int dsp_get_version (void);
Xstatic int dsp_open (void);
Xstatic int dsp_close (void);
Xstatic int dsp_set_speed (int *speed);
Xstatic int dsp_command (sbBYTE val);
Xstatic int dsp_ioctl (int cmd, caddr_t arg);
Xstatic int dsp_set_voice (int on);
Xstatic int dsp_set_bufsize (int *newsize);
Xstatic int dsp_flush_dac (void);
Xstatic int dsp_set_compression (int mode);
Xstatic int dsp_set_stereo (sbFLAG on);
Xstatic int dsp_write (struct uio *uio);
Xstatic int dsp_read (struct uio *uio);
Xstatic int dsp_select (int rw);
Xstatic void dsp_find_buffers (void);
Xinline static void dsp_dma_start (int dir);
Xinline static void dsp_next_write (void);
Xinline static void dsp_next_read (void);
Xstatic int fm_open (void);
Xstatic int fm_close (void);
Xstatic int fm_reset (void);
Xstatic int fm_send (int channel, unsigned char reg, unsigned char val);
Xstatic int fm_ioctl (int cmd, caddr_t arg);
Xstatic int fm_delay (int pause);
Xstatic int fm_play_note (struct sb_fm_note *n);
Xstatic int fm_set_params (struct sb_fm_params *p);
Xstatic int fm_write (struct uio *uio);
Xstatic int mixer_read (struct uio *uio);
Xstatic int mixer_write (struct uio *uio);
Xstatic void mixer_send (sbBYTE reg, sbBYTE val);
Xstatic sbBYTE mixer_read_reg (sbBYTE reg);
Xstatic int mixer_ioctl (int cmd, caddr_t arg);
Xstatic int mixer_reset (void);
Xstatic int mixer_set_levels (struct sb_mixer_levels *l);
Xstatic int mixer_set_params (struct sb_mixer_params *p);
Xstatic int mixer_get_levels (struct sb_mixer_levels *l);
Xstatic int mixer_get_params (struct sb_mixer_params *params);
Xstatic int midi_open (void);
Xstatic int midi_close (void);
Xstatic int midi_read (struct uio *uio);
Xstatic int midi_write (struct uio *uio);
Xstatic int midi_ioctl (int cmd, caddr_t arg);
Xstatic int midi_select (int rw);
X
X
X/*
X * This page has the required driver functions for the kernel.
X *
X * This is your basic set of functions that the BSD kernel needs.
X * Nothing really Sound Blaster specific here.
X */
Xint (*sb_intrs[])() = { sb_intr, 0 };
X#if defined(__bsdi__) || defined(__386BSD__)
Xstruct isa_driver sbdriver = { sbprobe, sbattach, "sb" };
Xextern        long sb0mask;
X#else
Xstruct isa_driver sb_driver = { sb_probe, 0, sb_attach, "sb", 0, 0, 0 };
X#endif
X
X/* 
X * Probing sets dev->dev_alive, status.alive, and returns 1 if the
X * card is detected.  Note that we are not using the "official" Adlib
X * of checking for the presence of the card because it's tacky and
X * takes too much mucking about.  We just attempt to reset the DSP and
X * assume that a DSP-READY signal means the card is there.  If you
X * have another card that fools this probe, I'd really like to hear
X * about it. :-) Future versions may query the DSP version number
X * instead.
X */
Xint
Xsb_probe (struct isa_dev *dev)
X{
X#ifdef FULL_DEBUG
X  printf ("sb: sb_probe() called.\n");
X  printf ("sb: unit=%d, ctlr=%d, slave=%d, alive=%d, addr=0x%x, spl=%d\n",
X	  dev->dev_unit, dev->dev_ctlr, dev->dev_slave, dev->dev_alive,
X	  dev->dev_addr, dev->dev_spl);
X  printf ("sb: pic=%d  dk=%d  flags=%d  start=0x%x  len=%d  type=%d\n",
X	  dev->dev_pic, dev->dev_dk, dev->dev_flags, dev->dev_start,
X	  dev->dev_len, dev->dev_type);
X#endif
X
X#if defined(__bsdi__) || defined(__386BSD__)
X  status.addr = (unsigned int) dev->id_iobase;
X#else
X  status.addr = (unsigned int) dev->dev_addr;
X#endif
X
X  if (dsp_reset () == GOOD)
X    status.alive = 1;
X  else
X    {
X      status.alive = 0;
X      printf ("sb: Sound Blaster Not found!  Driver not installed.\n");
X    }
X#if !defined(__bsdi__) && !defined(__386BSD__)
X  dev->dev_alive = status.alive;
X#endif
X  return (status.alive);
X}
X
X/* This is called by the kernel if probe() is successful. */
Xint
Xsb_attach (struct isa_dev *dev)
X{
X  int i;
X
X#if !defined(__bsdi__) && !defined(__386BSD__)
X  take_dev_irq (dev);		/* Set up DSP interrupt */
X#endif
X
X  dsp_get_version ();
X#if !defined(__bsdi__) && !defined(__386BSD__)
X  printf ("sb: Sound Blaster v%d.%02d installed. ", /* %02d is hosed? */
X	  dsp.ver_major, dsp.ver_minor);
X  printf ("(Port 0x%x, IRQ %d, DMA %d)\n",
X	  status.addr, dev->dev_pic, SB_DMA_CHAN);
X#endif
X  
X  if (dsp.ver_major != EXPECTED_DSP_VER)
X    {
X      printf ("sb: SB Driver is MISCONFIGURED.\n"
X	      "sb: You do not have a DSP version %d.x.\n", EXPECTED_DSP_VER);
X      status.alive = FALSE;
X    }
X
X  status.fm_in_use = 0;
X  status.dsp_in_use = 0;
X
X#if !defined(__bsdi__)
X	/* under BSDI, we allocate buffers later from memory pool. */
X	/* same idea for 386BSD */
X	/* see init_main.c and sb_meminit() */
X  dsp_find_buffers();
X#endif
X  for (i = 0; i < NUM_UNITS; i++)
X    status.wake[i] = NULL;
X
X  for (i = 0; i <= SB_BIGGEST_MINOR; i++)
X    {
X      status.sig_proc[i] = NULL;
X      status.selects[i] = NULL;
X      status.sel_coll[i] = 0;
X    }
X
X  /*
X   * These are default startup settings.
X   * I'm wondering if I should just leave the card in the same
X   * speed/stereo state that it is in.  I decided to leave the mixer
X   * alone, and like that better.  Ideas?
X   */
X  dsp.compression = PCM_8;
X  dsp.speed = INITIAL_DSP_SPEED;
X  dsp_set_stereo (FALSE);
X  dsp.bufsize = DSP_MAX_BUFSIZE;
X  /* mixer_reset(); */
X
X#if defined(__bsdi__) || defined(__386BSD__)
X  sb0mask = dev->id_irq;	/* stolen from npx.c (??) */
X  return (1);
X#else
X  return (ESUCCESS);
X#endif
X}
X
X/*
X * This gets called anytime there is an interrupt on the SB IRQ.
X * Great effort is made to service DSP interrupts immediately
X * to keep the buffers flowing and pops small.  I wish the card
X * had a bigger internal buffer so we didn't have to worry.
X */ 
Xint
Xsb_intr (int unit)
X{
X  int i;
X  DPRINTF (("sb_intr(): Got interrupt on unit %d\n", unit));
X
X  /*
X   * If the DSP is doing a transfer, and the interrupt was on the
X   * DSP unit (as opposed to, say, the FM unit), then hurry up and
X   * call the function to continue the DSP transfer.
X   */
X  if (dsp.cont_xfer && unit == DSP_UNIT)
X    (*dsp.cont_xfer)();
X
X  /*
X   * If any selects are pending on any devices, wake them up
X   * and have them check for data ready.  Also send out ASYNC
X   * signals to anyone who asked for them.
X   * Of course, this makes the blatant assumption that only one
X   * interrupt-generating device will be open at any time, since
X   * we can't figure out what caused the SB to interrupt.
X   * (Ahhh, I love that ISA bus..)
X   */
X  for (i = 0; i <= SB_BIGGEST_MINOR; i++)
X    {
X      if (status.selects[i])
X	{
X	  DPRINTF (("Waking up selecters..\n"));
X	  selwakeup (status.selects[i], status.sel_coll[i]);
X	  status.selects[i] = 0;
X	  status.sel_coll[i] = FALSE;
X	}
X      /*
X       * I'm not sure if this is the right time to send out signals.
X       * Perhaps more checking should be done to make sure the process
X       * is really interested in this interrupt.  I don't use ASYNC much.
X       */
X      if (status.sig_proc[i])
X	psignal (status.sig_proc[i], SIGIO);
X    }
X  
X  /*
X   * If this Unit is expecting an interrupt, then set the semaphore
X   * and wake up the sleeper.  Otherwise, we probably weren't expecting
X   * this interrupt, so just ignore it.  This also happens when two
X   * interrupts are acked in a row, without sleeping in between.
X   * Not really a problem, but there should be a cleaner way to fix it.
X   */
X  if (status.wake[unit] != NULL)
X    {
X      DPRINTF(("sb_intr semaphore %x\n", status.wake[unit]));
X      *status.wake[unit] = WOKEN_BY_INTERRUPT;
X      wakeup (status.wake[unit]);
X      status.wake[unit] = NULL;
X    }
X  else
X    {
X      DPRINTF (("sb_intr(): An ignored interrupt.\n"));
X    }
X#if defined(__bsdi__) || defined(__386BSD__)
X  return (1);
X#else
X  return (ESUCCESS);
X#endif
X}
X
X/* Multiplexes the open between all the different minor numbers. */
Xint
Xsb_open (dev_t dev, int flags)
X{
X  if (!status.alive)
X    return (ENODEV);
X
X  DPRINTF (("open(): dev = 0x%x, flags = 0x%x\n", dev, flags));
X
X  /* Remember the flags for future reference */
X  status.flags[minor(dev)] = flags;
X  status.sig_proc[minor(dev)] = NULL;
X  status.selects[minor(dev)] = NULL;
X  status.sel_coll[minor(dev)] = 0;
X
X  switch (minor (dev))
X    {
X    case SB_DSP_NUM:
X      return (dsp_open ());
X    case SB_FM_NUM:
X      return (fm_open ());
X    case SB_MIDI_NUM:
X      return (midi_open ());
X    case SB_MIXER_NUM:
X      return (ESUCCESS);
X      
X    case SB_CMS_NUM:		/* No CMS support (yet) */
X    default:
X      return (EINVAL);
X    }
X}
X
Xint
Xsb_close (dev_t dev, int flags)
X{
X#ifdef FULL_DEBUG
X  printf ("close(): dev = 0x%x, flags = 0x%x\n", dev, flags);
X#endif
X
X  switch (minor (dev))
X    {
X    case SB_DSP_NUM:
X      return (dsp_close ());
X    case SB_FM_NUM:
X      return (fm_close ());
X    case SB_MIDI_NUM:
X      return (midi_close ());
X    case SB_MIXER_NUM:
X      return (ESUCCESS);
X
X    case SB_CMS_NUM:
X    default:
X      printf ("sb: How did you close() a device you can't open()??");
X      return (ESRCH);
X    }
X}
X
X/*
X * This routes all the ioctl calls to the right place.
X * In general, Mixer ioctls should be allowed on any of the devices,
X * because it makes things easier and is guaranteed hardware-safe.
X */
Xint
Xsb_ioctl (dev_t dev, int cmd, caddr_t arg, int mode)
X{
X  if (!status.alive)
X    return (ENODEV);
X
X  switch (cmd)
X    {
X#ifdef ALWAYS_ALLOW_MIXER_IOCTLS
X    case MIXER_IOCTL_RESET:
X    case MIXER_IOCTL_SET_LEVELS:
X    case MIXER_IOCTL_SET_PARAMS:
X    case MIXER_IOCTL_READ_LEVELS:
X    case MIXER_IOCTL_READ_PARAMS:
X      return (mixer_ioctl (cmd, arg));
X#endif
X      /*
X       * No support for these yet.  Soon..
X       * For now, you just have to open the device with the right flags.
X       * As soon as I figure out fcntl() calls, this will work.
X       */
X    case F_SETFL:
X      DPRINTF (("Got F_SETFL ioctl.\n"));
X      return (EINVAL);
X    case F_GETFL:
X      DPRINTF (("Got F_GETFL ioctl.\n"));
X      return (EINVAL);
X      
X    default:
X      switch (minor (dev))
X	{
X	case SB_DSP_NUM:
X	  return (dsp_ioctl (cmd, arg));
X	case SB_FM_NUM:
X	  return (fm_ioctl (cmd, arg));
X	case SB_MIXER_NUM:
X	  return (mixer_ioctl (cmd, arg));
X	case SB_MIDI_NUM:
X	  return (midi_ioctl (cmd, arg));
X	case SB_CMS_NUM:
X	default:
X	  printf ("sb: sb_ioctl(): Invalid minor number!\n");
X	  return (EINVAL);
X	}
X    }      
X  return (ESUCCESS);
X}
X
X/* Multiplexes a read to the different devices */
Xint
Xsb_read (int dev, struct uio *uio)
X{
X  if (!status.alive)
X    return (ENODEV);
X
X  switch (minor(dev))
X    {
X    case SB_DSP_NUM:
X      return (dsp_read (uio));
X    case SB_MIDI_NUM:
X      return (midi_read (uio));
X    case SB_MIXER_NUM:
X      return (mixer_read (uio));
X
X    case SB_FM_NUM:		/* No reading from FM, right? */
X      return (ENXIO);
X    case SB_CMS_NUM:
X    default:
X      printf ("sb: sb_read() got bad dev!\n");
X      return (ENXIO);
X    }
X}
X
Xint
Xsb_write (int dev, struct uio *uio)
X{
X  if (!status.alive)
X    return (ENODEV);
X
X  DPRINTF (("sb: sb_write(): dev = 0x%x\n", dev));
X  switch (minor (dev))
X    {
X    case SB_DSP_NUM:
X      return (dsp_write (uio));
X    case SB_FM_NUM:
X      return (fm_write (uio));
X    case SB_MIXER_NUM:
X      return (mixer_write (uio));
X    case SB_MIDI_NUM:
X      return (midi_write (uio));
X
X    case SB_CMS_NUM:
X    default:
X      printf ("sb: sb_write() on bad dev.  Serious probs.\n"); /* ??? */
X      return (0);
X    }
X}
X
Xint
Xsb_select (int dev, int rw)
X{
X  if (!status.alive)
X    return (ENODEV);
X  DPRINTF (("sb: sb_select: dev = 0x%X\n", dev));
X  switch (minor (dev))
X    {
X    case SB_DSP_NUM:
X      return (dsp_select (rw));
X    case SB_MIDI_NUM:
X      return (midi_select (rw));
X    default:
X      printf ("sb: sb_select() on bad dev.\n");
X      return (1);
X    }
X}
X
X/*
X * Send one byte to the named Sound Blaster register.
X * The SBDK recommends 3.3 microsecs after an address write,
X * and 23 after a data write.  What they don't tell you is that
X * you also can't READ from the ports too soon, or crash! (Same timing?)
X * Anyway, 10 usecs is as close as we can get, so..
X *
X * NOTE:  This function is wicked.  It ties up the entire machine for
X * over forty microseconds.  This is unacceptable, but I'm not sure how
X * to make it better.  Look for a re-write in future versions.
X * Does 30 microsecs merit a full timeout() proceedure?
X */
Xstatic void
Xsb_sendb (unsigned int select_addr, sbBYTE reg,
X	  unsigned int data_addr, sbBYTE value)
X{
X  outb (select_addr, reg);
X  tenmicrosec();
X  outb (data_addr, value);
X  tenmicrosec();
X  tenmicrosec();
X  tenmicrosec();
X}
X
X#if !defined(__bsdi__) && !defined(__386BSD__)
X/*
X * This gets called when a sleep() times out,
X * and sets the semaphore appropriately.
X */
Xstatic void
Xtimeout_wakeup (int *arg)
X{
X  *arg = WOKEN_BY_TIMEOUT;
X  wakeup (arg);
X}
X#endif
X
X/*
X * This waits for an interrupt on the named Unit.
X * The 'patience' is how long to wait (in cycles) before a timeout
X *
X * Always have interrupts masked while calling! (like w/ splhigh())
X */
Xstatic int
Xwait_for_intr (int unit, int patience, int priority)
X{
X  static int sem[NUM_UNITS] = {0};	/* implicitly all are zero */
X  int result;
X
X  DPRINTF (("Waiting for interrupt on unit %d.\n", unit));
X/*
X	we CANNOT have the wakeup semaphore be on our local stack,
X	since the interrupt may arise in another process context.
X	However, since multiple sleepers on the same unit would have collided
X	in the original code (and only one would be awoken), we just make it
X	static, with one entry per unit.
X*/
X  status.wake[unit] = &sem[unit];
X
X#if defined(__bsdi__) || defined(__386BSD__)
X	/* use new tsleep interface--not really necessary, but it makes it
X	easier to figure out what processes are waiting on. */
X  result = tsleep(&sem, priority|PCATCH, "sbintrwait", patience);
X  if (result == EWOULDBLOCK)
X#else
X  if (patience)
X    timeout (timeout_wakeup, &sem, patience);
X  sleep (&sem, priority);
X
X  if (sem == WOKEN_BY_TIMEOUT)
X#endif
X    {
X      status.wake[unit] = NULL;		/* clean up */
X      printf ("sb: Interrupt time out.\n");
X      if (unit == DSP_UNIT)
X	dsp.error = EIO;
X      result = FAIL;
X    }
X  else
X    {
X      DPRINTF (("Got it!\n"));
X#if defined(__bsdi__) || defined(__386BSD__)
X      if (result == ERESTART)
X	return ERESTART;
X#else
X      untimeout (timeout_wakeup, &sem);
X#endif
X      result = GOOD;
X    }
X  return (result);
X}
X
X
X/*
X * DSP functions.
X */
Xstatic int
Xdsp_select (int rw)
X{
X  int s = splhigh();
X  DPRINTF (("sb: dsp_select() called with rw = %d.\n", rw));
X  if ((dsp.cont_xfer == NULL)
X      || ((rw == SELECT_WRITE) && (dsp.active == dsp.hi))
X      || ((rw == SELECT_READ) && (dsp.full[dsp.hi] == TRUE)))
X    {
X      splx (s);
X      return (1);
X    }
X  if (status.selects[SB_DSP_NUM])
X    status.sel_coll[SB_DSP_NUM] = TRUE;
X  else
X    status.selects[SB_DSP_NUM] = (struct proc *) curproc; /* eeyuck */
X  
X  splx (s);
X  return 0;
X}
X
Xstatic int
Xdsp_ioctl (int cmd, caddr_t arg)
X{
X  switch (cmd)
X    {
X    case DSP_IOCTL_RESET:
X      if (dsp_reset () == GOOD)
X	return (ESUCCESS);
X      else
X	return (EIO);
X    case DSP_IOCTL_OVERRUN_ERRNO:
X      dsp.overrun_errno = (*(int *)arg);
X      return (ESUCCESS);
X    case DSP_IOCTL_BUFSIZE:
X      return (dsp_set_bufsize ((int *)arg));
X    case DSP_IOCTL_SPEED:
X      return (dsp_set_speed ((int *)arg));
X    case DSP_IOCTL_VOICE:
X      return (dsp_set_voice (*(sbFLAG *)arg));
X    case DSP_IOCTL_FLUSH:
X      return (dsp_flush_dac ());
X    case DSP_IOCTL_COMPRESS:
X      return (dsp_set_compression (*(sbBYTE *)arg));
X    case DSP_IOCTL_STEREO:
X      return (dsp_set_stereo (*(sbFLAG *)arg));
X    default:
X      return (EINVAL);
X    }
X}
X
X/*
X * Reset the DSP chip, and initialize all DSP variables back
X * to square one.  This can be done at any time to abort
X * a transfer and break out of locked modes. (Like MIDI UART mode!)
X * Note that resetting the DSP puts the speed back to 8196, but
X * it shouldn't matter because we set the speed in dsp_open.
X * Keep this in mind, though, if you use DSP_IOCTL_RESET from
X * inside a program.
X */
Xstatic int
Xdsp_reset (void)
X{
X  int i, s;
X
X  DPRINTF (("Resetting DSP.\n"));
X  s = splhigh();
X  dsp.used[0] = dsp.used[1] = 0; /* This is only for write; see dsp_read() */
X  dsp.full[0] = dsp.full[1] = FALSE;
X  dsp.hi = dsp.active = 0;
X  dsp.state = NONE_READY;
X  dsp.error = ESUCCESS;
X  dsp.cont_xfer = NULL;
X  dsp.last_xfer_size = -1;
X  status.wake[DSP_UNIT] = NULL;
X
X  /*
X   * This is how you reset the DSP, according to the SBDK:
X   * Send 0x01 to DSP_RESET (0x226) and wait for three microseconds.
X   * Then send a 0x00 to the same port.
X   * Poll until DSP_RDAVAIL's most significant bit is set, indicating
X   * data ready, then read a byte from DSP_RDDATA.  It should be 0xAA.
X   * Allow 100 microseconds for the reset.
X   */
X  tenmicrosec();		/* Lets things settle down. (necessary?) */
X  outb (DSP_RESET, 0x01);
X  tenmicrosec();
X  outb (DSP_RESET, 0x00);
X
X  dsp.error = EIO;
X  for (i = DSP_LOOP_MAX; i; i--)
X    {
X      tenmicrosec();
X      if ((inb (DSP_RDAVAIL) & DSP_DATA_AVAIL)
X	  && ((inb (DSP_RDDATA) & 0xFF) == DSP_READY))
X	{
X	  dsp.error = ESUCCESS;
X	  break;
X	}
X    }
X  splx (s);
X
X  if (dsp.error != ESUCCESS)
X    return (FAIL);
X  else
X    return (GOOD);
X}
X
Xstatic int
Xdsp_get_version (void)
X{
X  int i;
X  
X  dsp.ver_major = dsp.ver_minor = 0;
X  dsp_command (GET_DSP_VERSION); /* Request DSP version number */
X  for (i = 10 * DSP_LOOP_MAX; i; i--)
X    {
X      if (inb (DSP_RDAVAIL) & DSP_DATA_AVAIL) /* wait for Data Ready */
X	{
X	  if (dsp.ver_major == 0)
X	    dsp.ver_major = inb (DSP_RDDATA);
X	  else
X	    {
X	      dsp.ver_minor = inb (DSP_RDDATA);
X	      break;		/* All done */
X	    }
X	}
X      tenmicrosec ();
X    }
X
X  if (i)			/* finished in time */
X    return (GOOD);
X  else
X    {
X      printf ("sb: Error reading DSP version number.\n");
X      return (FAIL);
X    }
X}
X
X#if defined(__bsdi__)
Xvoid
Xsb_meminit(void)
X{
X	dsp_find_buffers();
X}
X#endif
X
X/* 
X * This finds page-safe buffers for the DSP DMA to use.  A single DMA
X * transfer can never cross a 64K page boundary, so we have to get
X * aligned buffers for DMA.  The current method is wasteful, but
X * allows any buffer size up to the full 64K (which is nice).  We grab
X * 3 * 64K in the static global memory_chunk, and find the first 64K
X * alignment in it for the first buffer.  The second buffer starts 64K
X * later, at the next alignment.  Yeah, it's gross, but it's flexible.
X * I'm certainly open to ideas!  (Using cool kernel memory alloc is tricky
X * and not real portable.)
X */
Xstatic void
Xdsp_find_buffers (void)
X{
X#if defined(__bsdi__) || defined(__386BSD__)
X	char *memory_chunk;
X	register char *pagecheck;
X	vm_offset_t realaddr;
X	/*
X	* to avoid problems with static buffers causing the boot loader
X	* some heartburn, we attempt to allocate the buffers here,
X	* hoping that they're physically contiguous.  This is called pretty
X	* early in the memory allocator's life, so there's hopefully a
X	* good chance.
X	*/
X	memory_chunk = malloc(DSP_CHUNK_SIZE, M_DEVBUF, M_WAITOK);
X	printf("sb: memory_chunk at %x\n", memory_chunk);
X	/* check that it's contiguous by stepping by pages */
X
X	for (pagecheck = memory_chunk, realaddr = kvtophys(pagecheck);
X	pagecheck - memory_chunk < DSP_CHUNK_SIZE;
X	pagecheck += (page_mask+1)) {
X		if (realaddr + (pagecheck - memory_chunk)
X			!= kvtophys(pagecheck)) {
X			printf("sb: noncontiguous memory\n");
X		errout:
X			free(memory_chunk, M_DEVBUF);
X			dsp.buf[0] = 0;
X			dsp.buf[1] = 0;
X			return;
X		}
X	}
X#endif
X
X  dsp.buf[0] = memory_chunk + 0x10000 - (kvtophys (memory_chunk)
X					 - (kvtophys (memory_chunk)
X					    & ~0xFFFF));
X  dsp.buf[1] = dsp.buf[0] + DSP_BUF_SIZE;
X  
X#if defined(__bsdi__) || defined (__386BSD__)
X  if (crosses_64k_boundary (kvtophys(dsp.buf[0]), DSP_BUF_SIZE)
X      || crosses_64k_boundary (kvtophys(dsp.buf[1]), DSP_BUF_SIZE)) {
X      printf ("sb: Couldn't get good DSP buffers! (v: %x, %x;p: %x, %x)\n",
X	dsp.buf[0], dsp.buf[1],
X	kvtophys(dsp.buf[0]), kvtophys(dsp.buf[1]));
X      goto errout;
X  }
X#else
X  if (crosses_64k_boundary (dsp.buf[0], DSP_MAX_BUFSIZE)
X      || crosses_64k_boundary (dsp.buf[1], DSP_MAX_BUFSIZE))
X    panic ("sb: Couldn't get good DSP buffers!\n");
X#endif
X
X  dsp.phys_buf[0] = kvtophys(dsp.buf[0]);
X  dsp.phys_buf[1] = kvtophys(dsp.buf[1]);
X  printf("sb: b0 0x%x pb0 0x%x b1 0x%x pb1 0x%x\n",
X	dsp.buf[0], dsp.phys_buf[0], dsp.buf[1], dsp.phys_buf[1]);
X}
X
X
X/*
X * Start a single-buffer DMA transfer to/from the DSP.
X * This one needs more explaining than I would like to put in comments,
X * so look at the accompanying documentation, which, of course, hasn't
X * been written yet. :-)
X *
X * This function takes one argument, 'dir' which is either PLAY or
X * RECORD, and starts a DMA transfer of as many bytes as are in
X * dsp.buf[dsp.active].  This allows for partial-buffers.
X *
X * Always call this with interrupts masked.
X */
Xinline static void
Xdsp_dma_start (int dir)
X{
X  int count = dsp.used[dsp.active] - 1;
X
X  /* Prepare the DMAC.  See sb_regs for defs and more info. */
X  outb (DMA_MASK_REG, DMA_MASK);
X  outb (DMA_CLEAR, 0);
X  outb (DMA_MODE, (dir == RECORD) ? DMA_MODE_READ : DMA_MODE_WRITE);
X  outb (DMA_PAGE, (dsp.phys_buf[dsp.active] & 0xff0000) >> 16);	/* Page */
X  outb (DMA_ADDRESS, LOWBYTE (dsp.phys_buf[dsp.active]));
X  outb (DMA_ADDRESS, HIBYTE (dsp.phys_buf[dsp.active]));
X  outb (DMA_COUNT, LOWBYTE(count));
X  outb (DMA_COUNT, HIBYTE(count));
X  outb (DMA_MASK_REG, DMA_UNMASK);
X
X  /*
X   * The DMAC is ready, now send the commands to the DSP.
X   * Notice that there are two entirely different operations for
X   * Low and High speed DSP.  With HS, You only have to send the
X   * byte-count when it changes, and that requires an extra command
X   * (Checking if it's a new size is quicker than always sending it.)
X   */
X  if (dsp.hispeed)
X    {
X      DPRINTF (("Starting High-Speed DMA of %d bytes to/from buffer %d.\n",
X		dsp.used[dsp.active], dsp.active));
X
X      if (count != dsp.last_xfer_size)
X	{
X	  dsp_command (TRANSFER_SIZE);
X	  dsp_command (LOWBYTE (count));
X	  dsp_command (HIBYTE (count));
X	  dsp.last_xfer_size = count;
X	}
X      dsp_command (dsp.start_command); /* GO! */
X    }
X  else				/* Low Speed transfer */
X    {
X      DPRINTF (("Starting Low-Speed DMA xfer of %d bytes to/from buffer %d.\n",
X		dsp.used[dsp.active], dsp.active));
X      dsp_command (dsp.start_command);
X      dsp_command (LOWBYTE (count));
X      dsp_command (HIBYTE (count)); /* GO! */
X    }
X
X  /* This sets up the function to call at the next interrupt: */
X  dsp.cont_xfer = (dir == RECORD) ? dsp_next_read : dsp_next_write;
X}
X
X/*
X * This is basically the interrupt handler for Playback DMA interrupts.
X * Our first priority is to get the other buffer playing, and worry
X * about the rest after.
X */
Xinline static void
Xdsp_next_write (void)
X{
X#if defined(__bsdi__) || defined(__386BSD__)
X  int s = splhigh ();
X#else
X  int s = sploff ();		/* Faster than splhigh */
X#endif
X
X  inb (DSP_RDAVAIL);		/* ack interrupt */
X  DPRINTF (("Got interrupt.  DMA done on buffer %d.\n", dsp.active));
X  dsp.full[dsp.active] = FALSE; /* Just-played buffer is not full */
X  dsp.used[dsp.active] = 0; /* it's actually totally empty! */
X  dsp.active ^= 1;
X  if (dsp.full[dsp.active])
X    {
X      DPRINTF (("Starting next buffer off..\n"));
X      dsp_dma_start (PLAY);
X    }
X  else
X    {
X      DPRINTF (("Other buffer is not full.  Clearing cont_xfer..\n"));
X      dsp.cont_xfer = NULL;
X    }
X#if defined(__bsdi__) || defined(__386BSD__)
X  splx(s);
X#else
X  splon (s);
X#endif
X}
X
X/*
X * This is similar to dsp_next_write(), but for Sampling instead of playback.
X */
Xinline static void
Xdsp_next_read (void)
X{
X#if defined(__bsdi__) || defined(__386BSD__)
X  int s = splhigh ();
X#else
X  int s = sploff ();
X#endif
X
X  inb (DSP_RDAVAIL);		/* ack interrupt */
X  /* The active buffer is currently full of samples */
X  dsp.full[dsp.active] = TRUE;
X  dsp.used[dsp.active] = 0;
X
X  /* Flop to the next buffer and fill it up too, unless it's already full */
X  dsp.active ^= 1;
X  if (dsp.full[dsp.active])
X    {
X      /*
X       * An overrun occurs when we fill up two buffers faster than the
X       * user is reading them.  Lossage has to occur.  This may or
X       * may not be bad.  For recording, it's bad.  For real-time
X       * FFTs and such, it's not a real big deal.
X       */
X      dsp.error = dsp.overrun_errno;
X      dsp.cont_xfer = NULL;
X    }
X  else
X    dsp_dma_start (RECORD);
X#if defined(__bsdi__) || defined(__386BSD__)
X  splx(s);
X#else
X  splon (s);
X#endif
X}
X
X/*
X * This is the main recording function.  Program flow is tricky with
X * all the double-buffering and interrupts, but such are drivers.
X * To summarize, it starts filling the first buffer when the user
X * requests the first read().  (Filling on the open() call would be silly.)
X * When one buffer is done filling, we fill the other one while copying
X * the fresh data to the user.
X * If the user doesn't read fast enough for that DSP speed, we have an
X * overrun.  See above concerning overrun_errno.
X */
Xstatic int
Xdsp_read (struct uio *uio)
X{
X  int bytecount, hunk_size, s, chunk;
X
X  if (dsp.state != READ_READY)
X    {
X      if (!(status.flags[SB_DSP_NUM] & FREAD))
X	return (EINVAL);
X      dsp_flush_dac ();		/* In case we were writing stuff */
X
X      s = splhigh ();
X      dsp_set_voice (OFF);
X      dsp_set_speed (&dsp.speed); /* Set SB to the current speed. */
X      dsp.state = READ_READY;
X      DPRINTF (("Kicking in first DSP read..\n"));
X      dsp.start_command = dsp.hispeed ? HS_ADC_8 : ADC_8;
X
X      /* A DSP state reset, without the hardware reset. */
X      dsp.used[0] = dsp.used[1] = dsp.bufsize; /* Start with both empty */
X      dsp.full[0] = dsp.full[1] = FALSE;
X      dsp.hi = dsp.active = 0;
X      dsp.error = ESUCCESS;
X      dsp.last_xfer_size = -1;
X      status.wake[DSP_UNIT] = NULL;
X      dsp_dma_start (RECORD);
X      splx (s);
X    }
X
X  /* For each uio_iov[] entry... */
X  for (chunk = 0; chunk < uio->uio_iovcnt; chunk++)
X    {
X      bytecount = 0;
X
X      /* While there is still data to read, and data in this chunk.. */
X      while (uio->uio_resid && bytecount < uio->uio_iov[chunk].iov_len)
X	{
X	  if (dsp.error != ESUCCESS)
X	    return (dsp.error);
X	  
X	  s = splhigh ();
X	  while (dsp.full[dsp.hi] == FALSE)
X	    {
X	      DPRINTF (("Waiting for buffer %d to fill..\n", dsp.hi));
X	      wait_for_intr (DSP_UNIT, dsp.timeout, INTR_PRIORITY);
X	    }
X	  splx (s);
X
X	  /* Now we give a piece of the buffer to the user */
X	  hunk_size = MIN (uio->uio_iov[chunk].iov_len - bytecount,
X			   dsp.bufsize - dsp.used[dsp.hi]);
X
X	  DPRINTF (("Copying %d bytes from buffer %d.\n", hunk_size, dsp.hi));
X	  if (copyout (dsp.buf[dsp.hi] + dsp.used[dsp.hi], 
X		       uio->uio_iov[chunk].iov_base + bytecount, hunk_size))
X	    {
X	      printf ("sb: Bad copyout()!\n");
X	      return (ENOTTY);
X	    }
X	  dsp.used[dsp.hi] += hunk_size;
X
X	  if (dsp.used[dsp.hi] == dsp.bufsize)
X	    {
X	      DPRINTF (("Drained all of buffer %d.\n", dsp.hi));
X	      dsp.full[dsp.hi] = FALSE;
X	      dsp.hi ^= 1;
X	      dsp.used[dsp.hi] = 0;
X	      if (dsp.cont_xfer == NULL) /* We had an overrun recently */
X		{
X		  DPRINTF (("Caught up after overrun, starting DMA..\n"));
X		  s = splhigh ();
X		  dsp_dma_start (RECORD);
X		  splx (s);
X		}
X	    }
X	  bytecount += hunk_size;
X	}			/* While there are bytes left in chunk */
X      uio->uio_resid -= bytecount;
X    }				/* for each chunk */
X  return (dsp.error);
X}
X
X/* 
X * Main function for DSP sampling.
X * No such thing as an overrun here, but if the user writes too
X * slowly, we have to restart the DSP buffer ping-pong manually.
X * There will then be gaps, of course.
X */
Xstatic int
Xdsp_write (struct uio *uio)
X{
X  int bytecount, hunk_size, s, chunk;
X
X  if (dsp.state != WRITE_READY)
X    {
X      if (!(status.flags[SB_DSP_NUM] & FWRITE))
X	return (EINVAL);
X      dsp_reset ();
X      dsp_set_speed (&dsp.speed); /* Set SB back to the current speed. */
X      dsp_set_voice (ON);
X      dsp.state = WRITE_READY;
X    
X      /*
X       * If this is the first write() operation for this open(),
X       * then figure out the DSP command to use.
X       * Have to check if it is High Speed, or one of the compressed modes.
X       * If this is the first block of a Compressed sound file,
X       * then we have to set the "Reference Bit" in the dsp.command for the
X       * first block transfer.
X       */
X      if (dsp.hispeed)
X	{
X	  dsp.start_command = HS_DAC_8;
X	}
X      else
X	{
X	  dsp.start_command = dsp.compression;
X	  if (dsp.compression == ADPCM_4
X	      || dsp.compression == ADPCM_3
X	      || dsp.compression == ADPCM_2)
X	    dsp.start_command |= 1;
X	}
X    }
X
X  /* For each uio_iov[] entry... */
X  for (chunk = 0; chunk < uio->uio_iovcnt; chunk++)
X    {
X      bytecount = 0;
X
X      /* While there is still data to write, and data in this chunk.. */
X      while (uio->uio_resid && bytecount < uio->uio_iov[chunk].iov_len)
X	{
X	  if (dsp.error != ESUCCESS) /* Shouldn't happen */
X	    return (dsp.error);
X
X	  hunk_size = MIN (uio->uio_iov[chunk].iov_len - bytecount,
X			   dsp.bufsize - dsp.used[dsp.hi]);
X	  DPRINTF (("Adding %d bytes (%d) to buffer %d.\n",
X		    hunk_size, dsp.used[dsp.hi], dsp.hi));
X	  if (copyin (uio->uio_iov[chunk].iov_base + bytecount,
X		      dsp.buf[dsp.hi] + dsp.used[dsp.hi], hunk_size)) 
X	    {
X	      printf ("sb: Bad copyin()!\n");
X	      return (ENOTTY);
X	    }
X	  dsp.used[dsp.hi] += hunk_size;
X
X	  if (dsp.used[dsp.hi] == dsp.bufsize)
X	    {
X	      s = splhigh ();
X	      dsp.full[dsp.hi] = TRUE;
X	      DPRINTF (("Just finished filling buffer %d.\n", dsp.hi));
X
X	      /*
X	       * This is true when there are no DMA's currently
X	       * active.  This is either the first block, or the
X	       * user is slow about writing.  Start the chain reaction.
X	       */
X	      if (dsp.cont_xfer == NULL)
X		{
X		  DPRINTF (("Jump-Starting a fresh DMA...\n"));
X		  dsp.active = dsp.hi;
X		  dsp_dma_start (PLAY);
X		  if (!dsp.hispeed)
X		    dsp.start_command &= ~1; /* Clear reference bit */
X		  status.wake[DSP_UNIT] = &dsp.semaphore;
X		}
X	      
X	      /* If the OTHER buffer is playing, wait for it to finish. */
X	      if (dsp.active == dsp.hi ^ 1)
X		{
X		  DPRINTF (("Waiting for buffer %d to empty.\n", dsp.active));
X		  wait_for_intr (DSP_UNIT, dsp.timeout, INTR_PRIORITY);
X		}
X	      dsp.hi ^= 1;	/* Switch to other buffer */
X	      dsp.used[dsp.hi] = 0; /* Mark it as empty */
X	      splx (s);
X	      DPRINTF (("Other buffer (%d) is empty, continuing..\n", dsp.hi));
X	    }			/* if filled hi buffer */
X	  bytecount += hunk_size;
X	}			/* While there are bytes left in chunk */
X      uio->uio_resid -= bytecount;
X    }				/* for each chunk */
X  return (dsp.error);
X}
X
X/*
X * Play any bytes in the last waiting write() buffer and wait
X * for all buffers to finish transferring.
X * An even number of bytes is forced to keep the stereo channels
X * straight.  I don't think you'll miss one sample.
X */
Xstatic int
Xdsp_flush_dac (void)
X{
X  int s;
X  
X  DPRINTF (("Flushing last buffer(s).\n"));
X  s = splhigh ();
X  if (dsp.used[dsp.hi] != 0)
X    {
X      DPRINTF (("Playing the last %d bytes.\n", dsp.used[dsp.hi]));
X      if (dsp.in_stereo)
X	dsp.used[dsp.hi] &= ~1;	/* Have to have even number of bytes. */
X      dsp.full[dsp.hi] = TRUE; 
X      if (dsp.cont_xfer == NULL)
X	{
X	  dsp.active = dsp.hi;
X	  dsp_dma_start (PLAY);
X	}
X    }
X
X  /* Now wait for any transfers to finish up. */
X  while (dsp.cont_xfer)
X    {
X      DPRINTF (("Waiting for last DMA(s) to finish.\n"));
X      wait_for_intr (DSP_UNIT, dsp.timeout, INTR_PRIORITY);
X    }
X  dsp.used[0] = dsp.used[1] = 0;
X  splx (s);
X  return (ESUCCESS);
X}
X
X
Xstatic int
Xdsp_open (void)
X{
X  if (status.dsp_in_use)
X    return (EBUSY);
X
X  if (!dsp.buf[0])
X	return(ENOMEM);	/* couldn't allocate memory; don't let it be used. */
X
X  status.selects[SB_DSP_NUM] = NULL;
X  status.sel_coll[SB_DSP_NUM] = FALSE;
X  status.dsp_in_use = TRUE;
X
X  /* Take note of the process if they want asynchronous signals */
X  if (status.flags[SB_DSP_NUM] & FASYNC)
X    status.sig_proc[SB_DSP_NUM] = curproc;
X  
X  dsp_reset ();			/* Resets card and inits variables */
X  dsp.overrun_errno = EIO;	/* Until we are told otherwise */
X  dsp.state = NONE_READY;	/* Not ready for either reading or writing */
X
X#ifdef FULL_DEBUG
X  /* Stuff buffers with loud garbage so we can hear/see leaks. */
X  for (i = 0; i < dsp.bufsize; i++)
X    dsp.buf[0][i] = dsp.buf[1][i] = i & 0xff;
X#endif
X  return (ESUCCESS);
X}
X
X
Xstatic int
Xdsp_close (void)
X{
X  if (status.dsp_in_use)
X    {
X      /* Wait for any last write buffers to empty  */
X      if (dsp.state == WRITE_READY)
X	dsp_flush_dac ();
X      dsp_reset ();
X      status.dsp_in_use = FALSE;
X      return (ESUCCESS);
X    }
X  else
X    return (ESRCH);		/* Does this ever happen? */
X}
X
X
X/*
X * Set the playback/recording speed of the DSP.
X * This takes a pointer to an integer between DSP_MIN_SPEED
X * and DSP_MAX_SPEED and changes that value to the actual speed
X * you got. (Since the speed is so darn granular.)
X * This also sets the dsp.hispeed flag appropriately.
X * Note that Hi-Speed and compression are mutually exclusive!
X * I also don't check all the different range limits that
X * compression imposes.  Supposedly, the DSP can't play compressed
X * data as fast, but that's your problem.  It will just run slower.
X * Hmmm.. that could cause interrupt timeouts, I suppose.
X */
Xstatic int
Xdsp_set_speed (int *speed)
X{
X  sbBYTE time_constant;
X
X  if (*speed == -1)		/* Requesting current speed */
X    {
X      *speed = dsp.speed;
X      return (ESUCCESS);
X    }
X  
X  if (*speed < DSP_MIN_SPEED)
X    *speed = DSP_MIN_SPEED;
X  if (*speed > DSP_MAX_SPEED)
X    *speed = DSP_MAX_SPEED;
X
X  if (*speed > MAX_LOW_SPEED)
X    {
X      if (dsp.compression != PCM_8)
X	return (EINVAL);
X      DPRINTF (("Using HiSpeed mode.\n"));
X      time_constant = (sbBYTE) ((65536 - (256000000 / *speed)) >> 8);
X      dsp.speed = (256000000 / (65536 - (time_constant << 8)));
X      dsp.hispeed = TRUE;
X    }
X  else
X    {
X      DPRINTF (("Using LowSpeed mode.\n"));
X      time_constant = (sbBYTE) (256 - (1000000 / *speed));
X      dsp.speed = 1000000 / (256 - time_constant);
X      dsp.hispeed = FALSE;
X    }
X
X  /* Here is where we actually set the card's speed */
X  if (dsp_command (SET_TIME_CONSTANT) == FAIL
X      || dsp_command (time_constant) == FAIL)
X    return (EIO);
X  /*
X   * Replace speed with the speed we actually got.
X   * Set the DSP timeout to be twice as long as a full
X   * buffer should take.
X   */
X  *speed = dsp.speed;
X  COMPUTE_DSP_TIMEOUT;
X  DPRINTF (("Speed set to %d.\n", dsp.speed));
X  return (ESUCCESS);
X}
X
X
X/*
X * Turn the DSP output speaker on and off.
X * Argument of zero turns it off, on otherwise
X */
Xstatic int
Xdsp_set_voice (int on)
X{
X  if (dsp_command (on ? SPEAKER_ON : SPEAKER_OFF) == GOOD)
X    return (ESUCCESS);
X  else
X    return (EIO);
X}
X
X/*
X * Set the DAC hardware decompression mode.
X * No compression allowed for Hi-Speed mode, of course.
X */
Xstatic int
Xdsp_set_compression (int mode)
X{
X  switch (mode)
X    {
X    case ADPCM_4:
X    case ADPCM_3:
X    case ADPCM_2:
X      if (dsp.hispeed)
X	return (EINVAL);      /* Fall through.. */
X    case PCM_8:
X      dsp.compression = mode;
X      return (ESUCCESS);
X      
X    default:
X      return (EINVAL);
X    }
X}
X
X/*
X * Send a command byte to the DSP port.
X * First poll the DSP_STATUS port until the BUSY bit clears,
X * then send the byte to the DSP_COMMAND port.
X */
Xstatic int
Xdsp_command (sbBYTE val)
X{
X  int i;
X
X#ifdef FULL_DEBUG
X  printf ("Sending DSP command 0x%x\n", val);
X#endif
X  for (i = DSP_LOOP_MAX; i; i--)
X    {
X#ifdef SB20			/* SB DSP 2.0 needs these? */
X      tenmicrosec ();
X      tenmicrosec ();
X#endif
X      if ((inb (DSP_STATUS) & DSP_BUSY) == 0)
X	{
X	  outb(DSP_COMMAND, val);
X	  return (GOOD);
X	}
X      tenmicrosec ();
X    }
X  printf ("sb: dsp_command (%2X) failed!\n", val);
X  return (FAIL);
X}
X
X/*
X * Sets the DSP buffer size.
X * Smaller sizes make the DSP more responsive,
X * but require more CPU attention.
X * Note that the size must be an even value for stereo to work.
X * We just reject odd sizes.
X * -1 is magic, it is replaced with the current buffer size.
X */
Xstatic int
Xdsp_set_bufsize (int *newsize)
X{
X  if (*newsize == -1)
X    {
X      *newsize = dsp.bufsize;
X      return (ESUCCESS);
X    }
X  if (*newsize < DSP_MIN_BUFSIZE || *newsize > DSP_MAX_BUFSIZE
X      || *newsize & 1)
X    {
X      DPRINTF (("sb: Attempt to set dsp.bufsize to %d!\n", *newsize));
X      return (EINVAL);
X    }
X  
X  dsp.bufsize = *newsize;
X  COMPUTE_DSP_TIMEOUT;
X  return (ESUCCESS);
X}
X
X/*
X * This turns stereo playback/recording on and off.
X * For playback, it seems to only be a bit in the mixer.
X * I don't know the secret to stereo sampling, so this may
X * need reworking.  If YOU know how to sample in stereo, send Email!
X * Maybe this should be a Mixer parameter, but I really don't think so.
X * It's a DSP Thing, isn't it?  Hmm..
X */
Xstatic int
Xdsp_set_stereo (sbFLAG select)
X{
X  dsp.in_stereo = !!select;
X  /* Have to preserve DNFI bit from OUT_FILTER */
X  mixer_send (CHANNELS, ((mixer_read_reg (OUT_FILTER) & ~STEREO_DAC)
X			 | (dsp.in_stereo ? STEREO_DAC : MONO_DAC)));
X  return (ESUCCESS);
X}
X
X
X/*
X * FM support functions
X */
Xstatic int
Xfm_ioctl (int cmd, caddr_t arg)
X{
X  switch (cmd)
X    {
X    case FM_IOCTL_RESET:
X      return (fm_reset ());
X    case FM_IOCTL_PLAY_NOTE:
X      return (fm_play_note ((struct sb_fm_note*) arg));
X    case FM_IOCTL_SET_PARAMS:
X      return (fm_set_params ((struct sb_fm_params*) arg));
X    default:
X      return (EINVAL);
X    }
X}
X
Xstatic int
Xfm_open (void)
X{
X  if (status.fm_in_use)
X    return (EBUSY);
X
X  fm_reset();
X  status.fm_in_use = 1;
X  return (ESUCCESS);
X}
X
Xstatic int
Xfm_close (void)
X{
X  if (status.fm_in_use)
X    {
X      status.fm_in_use = 0;
X      return (fm_reset ());
X    }
X  else
X    return (ESRCH);
X}
X
X/*
X * This really is the only way to reset the FM chips.
X * Just write zeros everywhere.
X */
Xstatic int
Xfm_reset (void)
X{
X  int reg;
X  for (reg = 1; reg <= 0xf5; reg++)
X    fm_send (FM_BOTH, reg, 0);
X
X  return (ESUCCESS);;
X}
X
X/*
X * Send one byte to the FM chips.  'channel' specifies which
X * of the two SB Pro FM chip-sets to use.
X * SB (non-Pro) and Adlib owners should set both the Left and
X * Right addresses to the same (and only) FM set.
X * See sb_regs.h for more info.
X */
Xstatic int
Xfm_send (int channel, unsigned char reg, unsigned char val)
X{
X#ifdef FULL_DEBUG
X  printf ("(%d) %02x: %02x\n", channel, reg, val);
X#endif
X  switch (channel)
X    {
X    case FM_LEFT:
X      sb_sendb (FM_ADDR_L, reg, FM_DATA_L, val);
X      break;
X    case FM_RIGHT:
X      sb_sendb (FM_ADDR_R, reg, FM_DATA_R, val);
X      break;
X    case FM_BOTH:
X      sb_sendb (FM_ADDR_B, reg, FM_DATA_B, val);
X      break;
X    default:
X      printf ("sb: Hey, fm_send to which channel?!\n");
X      return (FAIL);
X    }
X  return (GOOD);
X}
X
X/*
X * Play one FM note, as defined by the sb_fm_note structure.
X * See sblast.h for info on the fields.
X * Now, these functions don't do any validation of the parameters,
X * but it just uses the lowest bits, so you can't hurt anything
X * with garbage.  Checking all those fields would take a while.
X */
Xstatic int
Xfm_play_note (struct sb_fm_note *n)
X{
X  int op, voice, ch;
X  
X  op = n->operator;		/* Only operator 0 or 1 allowed */
X  if (op & ~1)
X    return (EINVAL);
X
X  voice = n->voice;		/* Voices numbered 0 through 8 */
X  if (voice < 0 || voice > 8)
X    return (EINVAL);
X
X  ch = n->channel;		/* FM_LEFT, FM_RIGHT, or FM_BOTH */
X  if (ch != FM_LEFT && ch != FM_RIGHT && ch != FM_BOTH)
X    return (EINVAL);
X
X  /*
X   * See sb_regs.h for a detailed explanation of each of these
X   * registers and their bit fields.  Here it is, packed dense.
X   */
X  fm_send (ch, MODULATION(voice, op),
X           F(n->am) << 7
X           | F(n->vibrato) << 6
X           | F(n->do_sustain) << 5
X           | F(n->kbd_scale) << 4
X           | B4(n->harmonic));
X  fm_send (ch, LEVEL(voice, op),
X           B2(n->scale_level) << 6
X           | B6(~n->volume));
X  fm_send (ch, ENV_AD(voice, op),
X           B4(n->attack) << 4
X           | B4(n->decay));
X  fm_send (ch, ENV_SR(voice, op),
X           B4(~n->sustain) << 4 /* Make sustain level intuitive */
X           | B4(n->release));
X  fm_send (ch, FNUM_LO(voice), B8(n->fnum));
X  fm_send (ch, FEED_ALG(voice),
X           B3(n->feedback) << 1
X           | F(n->indep_op));
X  fm_send (ch, WAVEFORM(voice, op), B2(n->waveform));
X  fm_send (ch, OCT_SET(voice),
X           F(n->key_on) << 5
X           | B3(n->octave) << 2
X           | B2(n->fnum >> 8));
X  return (ESUCCESS);
X}
X
Xstatic int
Xfm_set_params (struct sb_fm_params *p)
X{
X  if (p->channel != FM_LEFT && p->channel != FM_RIGHT && p->channel != FM_BOTH)
X    return (EINVAL);
X  
X  fm_send (p->channel, DEPTHS,
X           F(p->am_depth) << 7
X           | F(p->vib_depth) << 6
X           | F(p->rhythm) << 5
X           | F(p->bass) << 4
X           | F(p->snare) << 3
X           | F(p->tomtom) << 2
X           | F(p->cymbal) << 1
X           | F(p->hihat));
X  fm_send (p->channel, WAVE_CTL, F(p->wave_ctl) << 5);
X  fm_send (p->channel, CSM_KEYSPL, F(p->speech) << 7
X	   | F(p->kbd_split) << 6);
X  return (ESUCCESS);
X}
X
X/*
X * This is to write a stream of timed FM notes and parameters
X * to the FM device.  I'm not sure how useful it will be, since
X * it's totally non-standard.  Most FM should stick to the ioctls.
X * See the docs for an explanation of the expected data format.
X * Consider this experimental.
X */
Xstatic int
Xfm_write (struct uio *uio)
X{
X  int pause, bytecount, chunk;
X  char type;
X  char *chunk_addr;
X  struct sb_fm_note note;
X  struct sb_fm_params params;
X
X  for (chunk = 0; chunk < uio->uio_iovcnt; chunk++)
X    {
X      chunk_addr = uio->uio_iov[chunk].iov_base;
X      bytecount = 0;
X      while (uio->uio_resid
X	     && bytecount < uio->uio_iov[chunk].iov_len)
X	{
X	  /* First read the one-character event type */
X	  if (copyin (chunk_addr + bytecount, &type, sizeof (type)))
X	    return (ENOTTY);
X	  bytecount += sizeof (type);
X
X	  switch (type)
X	    {
X	    case 'd':		/* Delay */
X	    case 'D':
X	      DPRINTF (("FM event: delay\n"));
X	      if (copyin (chunk_addr + bytecount, &pause, sizeof (pause)))
X		return (ENOTTY);
X	      bytecount += sizeof (pause);
X	      fm_delay (pause);
X	      break;
X
X	    case 'n':		/* Note */
X	    case 'N':
X	      DPRINTF (("FM event: note\n"));
X	      if (copyin (chunk_addr + bytecount, &note, sizeof (note)))
X		return (ENOTTY);
X	      bytecount += sizeof (note);
X
X	      if (fm_play_note (&note) != ESUCCESS)
X		return (FAIL);
X	      break;
X
X	    case 'p':		/* Params */
X	    case 'P':
X	      DPRINTF (("FM event: params\n"));
X	      if (copyin (chunk_addr + bytecount, &params, sizeof (params)))
X		return (ENOTTY);
X	      bytecount += sizeof (params);
X
X	      if (fm_set_params (&params) != ESUCCESS)
X		return (FAIL);
X	      break;
X
X	    default:
X	      printf ("sb: Unrecognized FM event type: %c\n", type);
X	      return (FAIL);	/* FAIL: Bad event type */
X	    }
X	}			/* while data left to write */
X      uio->uio_resid -= bytecount;
X    }				/* for each chunk */
X  return (ESUCCESS);
X}
X
X/*
X * I am not happy with this function!
X * The FM chips are supposed to give an interrupt when a timer
X * overflows, and they DON'T as far as I can tell.  Without
X * the interrupt, the timers are useless to Unix, since polling
X * is altogether unreasonable.
X *
X * I am still looking for information on this timer facility, so
X * if you know more about how to use it, and specifically how to
X * make it generate an interrupt, I'd love to hear from you.
X * I'm beginning to think that they can't trigger a bus interrupt,
X * and all the DOS software just polls it.
X * If this is the case, expect this function to disappear in future
X * releases.
X */
Xstatic int
Xfm_delay (int pause)
X{
X  int ticks, s;
X  
X  DPRINTF (("fm_delay() pausing for %d microsecs... ", pause));
X
X  s = splhigh();		/* before calling wait_for_intr */
X#ifndef USE_BROKEN_FM_TIMER	/**** Kludge! ****/
X  wait_for_intr (SB_FM_NUM, HZ * pause / 1000000, INTR_PRIORITY); 
X#else
X
X  fm_send (TIMER_CHANNEL, TIMER_CTL, FM_TIMER_MASK1 | FM_TIMER_MASK2);
X  fm_send (TIMER_CHANNEL, TIMER_CTL, FM_TIMER_RESET);
X  while (pause >= 20000)	/* The 80us timer can do 20,400 usecs */
X    {
X      ticks = MIN((pause / 320), 255);
X      fm_send (TIMER_CHANNEL, TIMER2, (256 - ticks));
X      printf ("%d ticks (320 us)\n", ticks);
X      fm_send (TIMER_CHANNEL, TIMER_CTL, (FM_TIMER_MASK1 | FM_TIMER_START2));
X      wait_for_intr (SB_FM_NUM, TIMEOUT, INTR_PRIORITY);
X      fm_send (TIMER_CHANNEL, TIMER_CTL, FM_TIMER_MASK1 | FM_TIMER_MASK2);
X      fm_send (TIMER_CHANNEL, TIMER_CTL, FM_TIMER_RESET);
X      pause -= ticks * 320;
X    }
X
X  while (pause >= 80)
X    {
X      ticks = MIN((pause / 80), 255);
X      fm_send (TIMER_CHANNEL, TIMER1, (256 - ticks));
X      printf ("%d ticks (80 us)\n", ticks);
X      fm_send (TIMER_CHANNEL, TIMER_CTL, (FM_TIMER_START1 | FM_TIMER_MASK2));
X      wait_for_intr (SB_FM_NUM, TIMEOUT, INTR_PRIORITY);
X      fm_send (TIMER_CHANNEL, TIMER_CTL, FM_TIMER_MASK1 | FM_TIMER_MASK2);
X      fm_send (TIMER_CHANNEL, TIMER_CTL, FM_TIMER_RESET);
X      pause -= ticks * 80;
X    }
X  DPRINTF (("done.\n"));
X#endif
X  splx(s);
X  return (pause);		/* Return unspent time (< 80 usecs) */
X}
X
X
X/*
X * Mixer functions
X */
Xstatic int
Xmixer_ioctl (int cmd, caddr_t arg)
X{
X  switch (cmd)
X    {
X    case DSP_IOCTL_STEREO:	/* Mixer can control DSP stereo */
X      return (dsp_ioctl (cmd, arg));
X    case MIXER_IOCTL_SET_LEVELS:
X      return (mixer_set_levels ((struct sb_mixer_levels*) arg));
X    case MIXER_IOCTL_SET_PARAMS:
X      return (mixer_set_params ((struct sb_mixer_params*) arg));
X    case MIXER_IOCTL_READ_LEVELS:
X      return (mixer_get_levels ((struct sb_mixer_levels*) arg));
X    case MIXER_IOCTL_READ_PARAMS:
X      return (mixer_get_params ((struct sb_mixer_params*) arg));
X    case MIXER_IOCTL_RESET:
X      return (mixer_reset ());
X    default:
X      return (EINVAL);
X    }
X}
X
X/*
X * Sets mixer volume levels.
X * All levels except mic are 0 to 15, mic is 7.  See sbinfo.doc
X * for details on granularity and such.
X * Basically, the mixer forces the lowest bit high, effectively
X * reducing the possible settings by one half.  Yes, that's right,
X * volume levels have 8 settings, and microphone has four.  Sucks.
X */
Xstatic int
Xmixer_set_levels (struct sb_mixer_levels *l)
X{
X  if (l->master.l & ~0xF || l->master.r & ~0xF
X      || l->line.l & ~0xF || l->line.r & ~0xF
X      || l->voc.l & ~0xF || l->voc.r & ~0xF
X      || l->fm.l & ~0xF || l->fm.r & ~0xF
X      || l->cd.l & ~0xF || l->cd.r & ~0xF
X      || l->mic & ~0x7)
X    return (EINVAL);
X
X  mixer_send (VOL_MASTER, (l->master.l << 4) | l->master.r);
X  mixer_send (VOL_LINE, (l->line.l << 4) | l->line.r);
X  mixer_send (VOL_VOC, (l->voc.l << 4) | l->voc.r);
X  mixer_send (VOL_FM, (l->fm.l << 4) | l->fm.r);
X  mixer_send (VOL_CD, (l->cd.l << 4) | l->cd.r);
X  mixer_send (VOL_MIC, l->mic);
X  return (ESUCCESS);
X}
X
X/*
X * This sets aspects of the Mixer that are not volume levels.
X * (Recording source, filter level, I/O filtering, and stereo.)
X */
Xstatic int
Xmixer_set_params (struct sb_mixer_params *p)
X{
X  if (p->record_source != SRC_MIC
X      && p->record_source != SRC_CD
X      && p->record_source != SRC_LINE)
X    return (EINVAL);
X
X  /*
X   * I'm not sure if this is The Right Thing.  Should stereo
X   * be entirely under control of DSP?  I like being able to toggle
X   * it while a sound is playing, so I do this... because I can.
X   */
X  dsp.in_stereo = !!p->dsp_stereo;
X
X  mixer_send (RECORD_SRC, (p->record_source
X			   | (p->hifreq_filter ? FREQ_HI : FREQ_LOW)
X			   | (p->filter_input ? FILT_ON : FILT_OFF)));
X
X  mixer_send (OUT_FILTER, ((dsp.in_stereo ? STEREO_DAC : MONO_DAC)
X			   | (p->filter_output ? FILT_ON : FILT_OFF)));
X  return (ESUCCESS);
X}
X
X/*
X * Read the current mixer level settings into the user's struct.
X */
Xstatic int
Xmixer_get_levels (struct sb_mixer_levels *l)
X{
X  sbBYTE val;
X
X  val = mixer_read_reg (VOL_MASTER); /* Master */
X  l->master.l = B4(val >> 4);
X  l->master.r = B4(val);
X
X  val = mixer_read_reg (VOL_LINE); /* FM */
X  l->line.l = B4(val >> 4);
X  l->line.r = B4(val);
X  
X  val = mixer_read_reg (VOL_VOC); /* DAC */
X  l->voc.l = B4(val >> 4);
X  l->voc.r = B4(val);
X
X  val = mixer_read_reg (VOL_FM); /* FM */
X  l->fm.l = B4(val >> 4);
X  l->fm.r = B4(val);
X  
X  val = mixer_read_reg (VOL_CD); /* CD */
X  l->cd.l = B4(val >> 4);
X  l->cd.r = B4(val);
X
X  val = mixer_read_reg (VOL_MIC); /* Microphone */
X  l->mic = B3(val);
X
X  return (ESUCCESS);
X}
X
X/*
X * Read the current mixer parameters into the user's struct.
X */
Xstatic int
Xmixer_get_params (struct sb_mixer_params *params)
X{
X  sbBYTE val;
X
X  val = mixer_read_reg (RECORD_SRC);
X  params->record_source = val & 0x07;
X  params->hifreq_filter = !!(val & FREQ_HI);
X  params->filter_input = (val & FILT_OFF) ? OFF : ON;
X  params->filter_output = (mixer_read_reg (OUT_FILTER) & FILT_OFF) ? OFF : ON;
X  params->dsp_stereo = dsp.in_stereo;
X  return (ESUCCESS);
X}
X
X/*
X * Silly little function to read the entire mixer levels & params
X * as raw binary data from the device.  Hey, don't laugh, I set
X * my mixer with:
X * bash$ cat good-settings > /dev/sb_mixer
X * and take snapshots in the same way.
X * Of questionable utility, I admit.
X */
Xstatic int
Xmixer_read (struct uio *uio)
X{
X  int bytecount;
X  struct sb_mixer_params params;
X  struct sb_mixer_levels levels;
X
X  if (uio->uio_offset != 0)
X    return (ESUCCESS);
X
X  /* There needs to be enough space for both structures. */ 
X  if (uio->uio_iov->iov_len < (sizeof (params) + sizeof (levels)))
X    return (ENOMEM);
X
X  mixer_get_params (&params);
X  mixer_get_levels (&levels);
X
X  bytecount = 0;
X
X  if (copyout (&params, uio->uio_iov->iov_base, sizeof (params)))
X    return (EINVAL);
X  bytecount += sizeof (params);
X  if (copyout (&levels, uio->uio_iov->iov_base + bytecount, sizeof (levels)))
X    return (EINVAL);
X  bytecount += sizeof (levels);
X
X  uio->uio_resid -= bytecount;
X  return (ESUCCESS);
X}
X
X/*
X * Same deal as mixer_read().
X * Just read binary data for mixer params and levels and set the mixer.
X */
Xstatic int
Xmixer_write (struct uio *uio)
X{
X  int bytecount;
X  struct sb_mixer_params params;
X  struct sb_mixer_levels levels;
X
X  if (uio->uio_offset != 0)
X    return (ESUCCESS);
X  
X  if (uio->uio_iov->iov_len < (sizeof (params) + sizeof (levels)))
X    return (ENOMEM);
X
X  bytecount = 0;
X  if (copyin (uio->uio_iov->iov_base, &params, sizeof (params)))
X    return (EINVAL);
X  bytecount += sizeof (params);
X  if (copyin (uio->uio_iov->iov_base + bytecount, &levels, sizeof (levels)))
X    return (EINVAL);
X  bytecount += sizeof (levels);
X
X  if (mixer_set_params (&params) == ESUCCESS
X      && mixer_set_levels (&levels) == ESUCCESS)
X    uio->uio_resid -= bytecount;
X
X  return (ESUCCESS);
X}
X
X#ifdef KOLVIR
Xstatic int
Xmixer_reset (void)
X{
X	mixer_send(MIXER_RESET, MIXER_RESET);
X}
X#else
X/*
X * This is supposed to reset the mixer.
X * Technically, a reset is performed by sending a byte to the MIXER_RESET
X * register, but I don't like the default power-up settings, so I use
X * these.  Adjust to taste, and you have your own personalized mixer_reset
X * ioctl.
X */
Xstatic int
Xmixer_reset (void)
X{
X  struct sb_mixer_levels l;
X  struct sb_mixer_params p;
X
X  p.filter_input  = OFF;
X  p.filter_output = OFF;
X  p.hifreq_filter = TRUE;
X  p.record_source = SRC_LINE;
X
X  l.cd.l = l.cd.r = 0;
X  l.mic = 1;
X  l.master.l = l.master.r = 11;
X  l.line.l = l.line.r = 15;
X  l.fm.l = l.fm.r = 15;
X  l.voc.l = l.voc.r = 15;
X
X  if (mixer_set_levels (&l) == ESUCCESS
X      && mixer_set_params (&p) == ESUCCESS)
X    return (ESUCCESS);
X  else
X    return (EIO);
X}
X#endif
X
X/*
X * Send a byte 'val' to the Mixer register 'reg'.
X */
Xstatic void
Xmixer_send (sbBYTE reg, sbBYTE val)
X{
X#if FULL_DEBUG
X  printf ("%02x: %02x\n", reg, val);
X#endif
X  sb_sendb (MIXER_ADDR, reg, MIXER_DATA, val);
X}
X
X/*
X * Returns the contents of the mixer register 'reg'.
X */
Xstatic sbBYTE
Xmixer_read_reg (sbBYTE reg)
X{
X  outb (MIXER_ADDR, reg);
X  tenmicrosec();		/* To make sure nobody reads too soon */
X  return (inb (MIXER_DATA));
X}
X
X
X/*
X * MIDI functions
X */
Xstatic int
Xmidi_open (void)
X{
X  if (status.dsp_in_use)
X    return (EBUSY);
X
X  status.dsp_in_use = TRUE;
X  status.selects[SB_MIDI_NUM] = NULL;
X  status.sel_coll[SB_MIDI_NUM] = FALSE;
X  dsp_reset ();			/* Resets card and inits variables */
X
X  /*
X   * This DSP command requests interrupts on MIDI bytes, but allows
X   * writing to the MIDI port at the same time.  This is only
X   * available in SB DSP's v2.0 or greater.
X   * Any byte sent to the DSP while in this mode will be sent out the
X   * MIDI OUT lines.  The only way to exit this mode is to reset the DSP.
X   */
X  dsp_command (MIDI_UART_READ);
X  return (ESUCCESS);
X}
X
Xstatic int
Xmidi_close ()
X{
X  if (status.dsp_in_use)
X    {
X      status.dsp_in_use = FALSE;
X      if (dsp_reset() == GOOD)	/* Exits MIDI UART mode */
X	return (ESUCCESS);
X      else
X	return (EIO);
X    }
X  else
X    return (ESRCH);
X}
X
Xstatic int
Xmidi_select (int rw)
X{
X  int s = splhigh();
X  DPRINTF (("sb: midi_select() called with rw = %d.\n", rw));
X  if (((rw == SELECT_WRITE))
X      || ((rw == SELECT_READ) && (inb (DSP_RDAVAIL) & DSP_DATA_AVAIL)))
X    {
X      splx (s);
X      return (1);		/* Give the go-ahead. */
X    }
X
X  /* It's not ready yet, so put them on the list of procs to notify */
X  if (status.selects[SB_MIDI_NUM])
X    status.sel_coll[SB_MIDI_NUM] = TRUE;
X  else
X    status.selects[SB_MIDI_NUM] = (struct proc *) curproc;
X  
X  splx (s);
X  return 0;
X}
X
Xstatic int
Xmidi_read (struct uio *uio)
X{
X  int bytecount, s, chunk;
X  sbBYTE data;
X
X  s = splhigh ();
X  /* For each uio_iov[] entry... */
X  for (chunk = 0; chunk < uio->uio_iovcnt; chunk++)
X    {
X      bytecount = 0;
X
X      /* While there is still data to read, and data in this chunk.. */
X      while (uio->uio_resid
X	     && bytecount < uio->uio_iov[chunk].iov_len)
X	{
X	  while (!(inb (DSP_RDAVAIL) & DSP_DATA_AVAIL))
X	    {
X	      /* Maybe they don't want to wait.. */
X	      if (status.flags[SB_MIDI_NUM] & FNDELAY)
X		{
X		  splx (s);
X		  return (EWOULDBLOCK);
X		}
X              /* Maybe they do. */
X#if 0
X	      if (wait_for_intr (DSP_UNIT, 0, PSLEP) == ERESTART) {
X		splx (s);
X		return EINTR;
X              }
X#endif
X	    }
X	  /*
X	   * In any case, data is ready for reading by this point.
X	   */
X	  DPRINTF (("Getting one MIDI byte.."));
X	  data = inb (DSP_RDDATA);
X	  if (copyout (&data, uio->uio_iov[chunk].iov_base + bytecount, 1))
X	    {
X	      printf ("sb: Bad copyout()!\n");
X	      return (ENOTTY);
X	    }
X	  bytecount ++;
X	  uio->uio_resid --;
X	}
X    }			/* for each chunk */
X  splx (s);
X  return (ESUCCESS);
X}
X
Xstatic int
Xmidi_write (struct uio *uio)
X{
X  int bytecount, chunk, s;
X  char *chunk_addr;
X  sbBYTE data;
X
X  for (chunk = 0; chunk < uio->uio_iovcnt; chunk++)
X    {
X      chunk_addr = uio->uio_iov[chunk].iov_base;
X      bytecount = 0;
X      while (uio->uio_resid
X	     && bytecount < uio->uio_iov[chunk].iov_len)
X	{
X	  if (copyin (chunk_addr + bytecount, &data, sizeof (data)))
X	    return (ENOTTY);
X	  bytecount++;
X	  s = splhigh ();
X	  /*
X	   * In MIDI UART mode, data bytes are sent as DSP commands.
X	   */
X	  if (dsp_command (data) == FAIL)
X	    {
X	      splx (s);
X	      return (EIO);
X	    }
X	  splx (s);
X	}			/* while data left to write */
X      uio->uio_resid -= bytecount;
X    }				/* for each chunk */
X  return (ESUCCESS);
X}
X
X/*
X * No valid ioctls for MIDI yet, but I have some ideas.
X */
Xstatic int
Xmidi_ioctl (int cmd, caddr_t arg)
X{
X#if 0
X  switch (cmd)
X    {
X    default:
X      return (EINVAL);
X    }
X#endif
X      /*
X       * Shoot.  I can't figure this out, and I don't have any examples.
X       * This is supposed to support the fcntl() call.
X       * The only flags now are FNDELAY for non-blocking reads.
X       * You can currently open the midi device with this flag, but
X       * you can't set it with fcntl().
X       * Stay tuned...
X       */
X#if 0				/* sorry */
X    case TIOCGETP:
X      arg->sg_flags = dsp.dont_block;
X      break;
X    case TIOCSETP:
X      dsp.dont_block = arg->sg_flags & FNDELAY;
X      break;
X#endif
X  return (ESUCCESS);
X}
X
X#endif NSB > 0
X
X/* ---- Emacs kernel recompile command ----
XLocal Variables:
Xcompile-command: "/bin/sh -c \"cd ../../..; kmake\""
XEnd:
X*/
END_OF_FILE
  if test 66644 -ne `wc -c <'sb_driver.c'`; then
    echo shar: \"'sb_driver.c'\" unpacked with wrong size!
  fi
  # end of 'sb_driver.c'
fi
if test -f 'sb_regs.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sb_regs.h'\"
else
  echo shar: Extracting \"'sb_regs.h'\" \(14536 characters\)
  sed "s/^X//" >'sb_regs.h' <<'END_OF_FILE'
X/*======================================================================
X
X   Huge collection of SoundBlaster registers, flags, and such.
X   [ This file is a part of SBlast-BSD-1.5 ]
X
X   Steve Haehnichen <shaehnic@ucsd.edu>
X
X   sb_regs.h,v 2.1 1992/09/14 03:09:23 steve Exp
X 
X   Copyright (C) 1992 Steve Haehnichen.
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X * sb_regs.h,v
X * Revision 2.1  1992/09/14  03:09:23  steve
X * Released as distribution v1.5.
X * Mostly stable, still lacking stereo recording.
X *
X * Revision 2.0  1992/07/13  01:25:20  steve
X * Promoting to v2.0 with start of CVS management.
X *
X * Revision 1.5.1.1  1992/07/13  00:39:31  steve
X * Stable double-buffering Sound Blaster driver.
X *
X * Revision 1.10  1992/06/20  04:41:43  steve
X * About to re-write buffering.
X *
X * Revision 1.9  1992/06/13  01:47:23  steve
X * Released in SBlast-BSD-1.5
X
X======================================================================*/
X
X/*
X * This is where you set the Sound Blaster DMA channel.
X * Possible settings are 0, 1, and 3.  The card defaults to
X * channel 1, which works just fine for me.
X *
X * The IRQ and I/O Port must be set in autoconf.c to match your card
X * settings.  See the directions in INSTALL for details.
X */
X#define SB_DMA_CHAN 	1	/* 0, 1, or 3  (1 is factory default) */
X
X/* Convenient byte masks */
X#define B1(x)	((x) & 0x01)
X#define B2(x)	((x) & 0x03)
X#define B3(x)	((x) & 0x07)
X#define B4(x)	((x) & 0x0f)
X#define B5(x)	((x) & 0x1f)
X#define B6(x)	((x) & 0x3f)
X#define B7(x)	((x) & 0x7f)
X#define B8(x)	((x) & 0xff)
X#define F(x)	(!!(x))		/* 0 or 1 only */
X
X/*
X * DMA registers & values. (Intel 8237: Direct Memory Access Controller)
X */
X
X/* Mask register: Send DMA_MASK to this register to suspend DMA on that
X * channel, then DMA_UNMASK to allow transfers.  Generally, you
X * send DMA_MASK as the first thing in the DMA setup, then UNMASK it last */
X#define DMA_MASK_REG 	0x0A	/* Mask register */
X#define DMA_UNMASK	SB_DMA_CHAN
X#define DMA_MASK	(DMA_UNMASK | 0x04)
X
X/* The DMA_MODE register selects a Read or Write DMA transfer. */
X#define DMA_MODE 	0x0B	/* Mode register (read/write to card) */
X#define DMA_MODE_WRITE	(0x48 + SB_DMA_CHAN) /* DAC */
X#define DMA_MODE_READ   (0x44 + SB_DMA_CHAN) /* ADC */
X
X/* Use these MODE selects for auto-looping DMA transfers. */
X#define DMA_AUTO_WRITE	(DMA_MODE_WRITE | 0x10)
X#define DMA_AUTO_READ	(DMA_MODE_READ | 0x10)
X
X/* This is a strange register.  Basically you just have to send
X * a byte to it before writing the address.  (It sets the address
X * state to LSB.)  Always send a 0x00 or anything else before 
X * writing the address. */
X#define DMA_CLEAR	0x0C /* Send one byte before writing the address. */
X
X/* This is where you send the address of the block of memory involved
X * in the transfer.  This block may start anywhere in the Page, but
X * the Count must not cross a 64K page boundary.   This is very important!
X * Write 2 bytes (low byte first, followed by the high byte) for the
X * least-significant 16 bits of the address. */
X#define DMA_ADDRESS     (SB_DMA_CHAN << 1)
X
X/* Write 2 bytes, low byte first for the 16 bits of the count.
X   Note that one greater than this value will be copied!
X   (i.e. the biggest size is 0xFFFF, and 0x0000 would transfer one byte. */
X#define DMA_COUNT       (DMA_ADDRESS + 1)
X
X/* The DMA_PAGE register gets bits 16-23 of the address.
X * Combined with the LSB and MSB sent to the address register,
X * This allows for a 16Meg addressing range.
X * Note that the Page registers are not in any logical order, so you
X * just have to pick the right one out of some table. */
X#ifndef SB_DMA_CHAN
X#  error "You must define SB_DMA_CHAN to be 0, 1, or 3!"
X#endif
X
X#if SB_DMA_CHAN == 0
X#  define	DMA_PAGE	0x87
X#elif SB_DMA_CHAN == 1
X#  define	DMA_PAGE	0x83
X#elif SB_DMA_CHAN == 2
X#  define	DMA_PAGE	0x81 /* Not available to SB! */
X#elif SB_DMA_CHAN == 3
X#  define	DMA_PAGE	0x82
X#else
X#  error "You must define SB_DMA_CHAN to be 0, 1, or 3!"
X#endif
X
X#ifdef SBPRO
X#  define EXPECTED_DSP_VER	3
X#endif
X#ifdef SB20
X#  define EXPECTED_DSP_VER	2
X#endif
X#ifdef SB10
X#  define EXPECTED_DSP_VER	1
X#endif
X
X  
X/*  ---- Sound Blaster DSP values ---
X *
X * These are the card addresses where data may be read or written.
X * status.addr is the base address of the card.
X * The typical settings are 0x220 or 0x240  (0x220 is the default)
X * See the docs for an explanation of each register.
X */
X#define DSP_RESET   		(status.addr + 0x06) /* Pulse to reset DSP */
X#define DSP_RDDATA  		(status.addr + 0x0A) /* Read data */
X#define DSP_WRDATA  		(status.addr + 0x0C) /* Write data */
X#define DSP_COMMAND 		(status.addr + 0x0C) /* Send command */
X#define DSP_STATUS  		(status.addr + 0x0C) /* Read status */
X#define DSP_RDAVAIL 		(status.addr + 0x0E) /* Data available */
X
X/* Status bits:
X * These are bits within the named readable registers that mean something. */
X#define DSP_BUSY		(1 << 7) /* STATUS flag indicates not ready */
X#define DSP_DATA_AVAIL		(1 << 7) /* RDAVAIL flag for data ready */
X#define DSP_READY		0xAA     /* RDDATA generated by a good reset */
X
X/* DSP commands:
X * These are the possible commands to send to the DSP via the DSP_COMMAND
X * register.  See the docs for more detailed explanations. */
X#define SET_TIME_CONSTANT	0x40 /* Set the sampling rate (1 byte) */
X#define SPEAKER_ON		0xD1 /* Turn on DSP sound */
X#define SPEAKER_OFF		0xD3 /* Mute DSP voice output */
X#define READ_SPEAKER		0xD8 /* Read speaker status (00:OFF FF:ON) */
X#define HALT_DMA		0xD0 /* Pause the DMA transfer */
X#define CONT_DMA		0xD4 /* Continue paused DMA transfer */
X#define GET_DSP_VERSION		0xE1 /* Get the DSP major & minor versions */
X
X#define TRANSFER_SIZE		0x48 /* Set the HS block size. (LSB & MSB) */
X#define HS_DAC_8		0x91 /* Start High-Speed 8-bit DMA DAC */
X#define HS_ADC_8		0x99 /* Start High-Speed 8-bit DMA ADC */
X#define AUTO_HS_DAC_8		0x90 /* Start High-Speed auto-repeat DAC */
X#define AUTO_HS_ADC_8		0x98 /* Start High-Speed auto-repeat ADC */
X
X#define SILENCE			0x80 /* Send a block of silence */
X#define DIRECT_DAC_8		0x10 /* For sending a single sample byte */
X#define DIRECT_ADC_8		0x20 /* For sampling a single byte */
X
X/* Commands to start Low-Speed DMA transfers.
X   For the decompression modes, add one to get the command for
X   data with reference byte (first block of sample) */
X#define ADC_8			0x24 /* Start 8-bit DMA ADC */
X#define DAC_8			0x14 /* Start 8-bit DMA DAC */
X#define DAC_4			0x74 /* Start 4-bit DMA DAC */
X#define DAC_3			0x76 /* Start 2.6-bit DMA DAC */
X#define DAC_2			0x16 /* Start 2-bit DMA DAC */
X
X#define DMA_SUSPEND		0x04 /* ??? */
X#define AUTO_DAC_2BIT 		0x1e
X#define AUTO_DAC_3BIT 		0x7e
X#define AUTO_DAC_4BIT 		0x7c
X#define AUTO_DAC_8BIT		0x1c /* Start low-speed auto-repeat DAC */
X#define AUTO_ADC_8BIT		0x2c /* Start low-speed auto-repeat ADC */
X
X/* MIDI DSP commands. */
X#define MIDI_POLL		0x30 /* Poll a byte from the MIDI port */
X#define MIDI_READ		0x31 /* Initiate a MIDI read with interrupts */
X#define MIDI_UART_POLL		0x34 /* Poll for a byte while allowing write */
X#define MIDI_UART_READ		0x35 /* Initiate a read while allowing write */
X#define MIDI_WRITE		0x38 /* Write a single MIDI byte */ 
X
X
X/*
X * Sound Blaster on-board Mixer chip addresses:
X */
X#define MIXER_ADDR		(status.addr + 0x04)
X#define MIXER_DATA		(status.addr + 0x05)
X
X/* To reset the Mixer to power-up state, send a 0x00 to both the
X * ADDR and DATA ports.  (Actually, you can send any value to DATA after
X * sending 0x00 to the ADDR port.) */
X#define MIXER_RESET		0x00 /* Send this to both ADDR & DATA */
X
X/*
X * Mixer chip registers:
X */
X#define RECORD_SRC      0x0C
X/*  bit:  7 6 5 4 3 2 1 0           F=frequency (0=low, 1=high)
X          x x T x F S S x           SS=source (00=MIC, 01=CD, 11=LINE)
X	                            T=input filter switch (ANFI) */
X
X/* Recording sources (SRC_MIC, SRC_CD, SRC_LINE)
X   are defined in i386at/sblast.h */
X
X#define IN_FILTER       0x0C	/* "ANFI" analog input filter reg */
X#define OUT_FILTER      0x0E	/* "DNFI" output filter reg */
X#define FREQ_HI         (1 << 3) /* Use High-frequency ANFI filters */
X#define FREQ_LOW        0	/* Use Low-frequency ANFI filters */
X#define FILT_ON         0	/* Yes, 0 to turn it on, 1 for off */
X#define FILT_OFF        (1 << 5)
X
X#define CHANNELS	0x0E	/* Stereo/Mono output select */
X#define MONO_DAC	0	/* Send to OUT_FILTER for mono output */
X#define STEREO_DAC	2	/* Send to OUT_FILTER for stereo output */
X
X#define VOL_MASTER      0x22	/* High nibble is left, low is right */
X#define VOL_FM          0x26
X#define VOL_CD          0x28
X#define VOL_LINE        0x2E
X#define VOL_VOC         0x04
X#define VOL_MIC         0x0A	/* Only the lowest three bits. (0-7) */
X
X
X/*
X * Soundblaster FM sythesizer definitions
X */
X
X/* FM synthesizer chip addresses for both, left, and right channels. */
X#define FM_ADDR_B		0x388 /* Adlib addresses */
X#define FM_DATA_B		0x389 /* Could also use addr + 0x08 & 0x09 */
X
X#ifdef SBPRO
X/* The Sound Blaster Pro has stereo FM channels */
X#define FM_ADDR_L		(status.addr + 0x00) /* Left only */
X#define FM_DATA_L		(status.addr + 0x01)
X#define FM_ADDR_R		(status.addr + 0x02) /* Right only */
X#define FM_DATA_R		(status.addr + 0x03)
X#else
X/* Original Sound Blaster cards do not have stereo */
X#define FM_ADDR_L		FM_ADDR_B
X#define FM_DATA_L		FM_DATA_B
X#define FM_ADDR_R		FM_ADDR_B
X#define FM_DATA_R		FM_DATA_B
X#endif
X
X#define FM_STATUS_L		FM_ADDR_L /* Status Read Register (left) */
X#define FM_STATUS_R		FM_ADDR_R /* Status Read Register (right) */
X/* Bit 7: IRQ flag.  FM synth has raised an interrupt.
X * Bit 6: Timer 1 overflow flag.  Set when Timer1 expires.
X * Bit 5: Timer 2 overflow flag.  Set when Timer2 expires. */
X
X/*
X * Offset from register for a given voice (0 to 8) and operator cell (0 or 1)
X * (see also const char fm_op_order[] in sb_driver.c)
X */
X#define FM_OFFSET(voice, op)		(fm_op_order[voice] + ((op) ? 3 : 0))
Xextern const char fm_op_order[];
X/* = { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 }; */
X
X/*
X * Parameters applied to all voices
X */
X#define WAVE_CTL	0x01
X/* Bit 5 set allows FM chips to "control the waveform" of each operator. */
X
X#define CSM_KEYSPL	0x08
X/* Bit 7: Set for composite sine-wave speech mode.
X          All KEY-ON bits must be clear first.
X   Bit 6: Selects keyboard split point (with F-Number data??)  */
X
X#define DEPTHS	     	0xbd
X/* Bit 7: 1 - AM depth is 4.8 dB
X          0 - AM depth is 1 dB
X   Bit 6: 1 - Vibrato depth is 14 cent
X          0 - Vibrato depth is 7 cent
X   Bit 5: 1 - Rhythm enabled  (w/ 6 music voices)
X          0 - Rhtthm disabled (w/ 9 music voices)
X   Bit 4: Bass drum on/off
X   Bit 3: Snare drum on/off
X   Bit 2: Tom-tom on/off
X   Bit 1: Cymbal on/off
X   Bit 0: Hi-hat on/off
X
X   Note: KEY-ON bits for voices 0x06, 0x07, and 0x08 must be
X   OFF to use rhythm  */
X
X/*
X * Parameters applied to individual voices
X */
X#define WAVEFORM(voice, op)	(0xe0 + FM_OFFSET((voice), (op)))
X/* When Bit 5 of address 0x01 is set, output will be distorted
X   according to Bits 0-1 of this register.
X   00 - Normal sine wave
X   01 - Sine with all negative values removed
X   10 - abs(Sine wave)  i.e. two positive half-cycles
X   11 - sortof sawtooth.  (like 10, with second half of each hum removed) */
X
X#define MODULATION(voice, op)	(0x20 + FM_OFFSET((voice), (op)))
X/* Bit 7: Set for amplitude modulation (depth controlled by DEPTH)
X   Bit 6: Set for vibrato (depth controlled by DEPTH)
X   Bit 5: Set to have a Sustain in the envelope.  Clear for decay
X          immediately after hitting Sustain phase.
X   Bit 4: Keyboard scaling rate. ???
X   Bit 3-0: Which harmonic of the specified frequency to generate.
X            0: -1 8ve
X            1: at specified freq.
X	    2: +1 8ve
X	    3: +1 8ve and a fifth
X	    4: +2 8ve
X	    etc.. */
X
X#define LEVEL(voice, op)	(0x40 + FM_OFFSET ((voice), (op)))
X/* Bits 7-6: Output decrease as freq rises.
X             00 - none
X	     10 - 1.5 dB/8ve
X	     01 - 3 dB/8ve
X	     11 - 6 dB/8ve
X   Bits 5-0: Volume attenuation
X             All bits 0 is the LOUDEST, all bits 1 for softest.
X	     (Attenuation = value * .75 dB) */
X
X#define ENV_AD(voice, op)	(0x60 + FM_OFFSET ((voice), (op)))
X/* Bits 7-4: Attack rate  (0 slowest, F fastest)
X   Bits 3-0: Decay rate   (0 slowest, F fastest) */
X
X#define ENV_SR(voice, op)	(0x80 + FM_OFFSET ((voice), (op)))
X/* Bits 7-4: Sustain level (0 loudest, F softest)
X   Bits 3-0: Release rate  (0 slowest, F fastest) */
X
X#define FNUM_LO(voice)		(0xa0 + (voice))
X/* Least significant byte of frequency control.
X   8 bits of the 10-bit F-Number.
X   Freq = 50000 * FNUM * 2^(octave - 20) */
X
X#define OCT_SET(voice)		(0xb0 + (voice))
X/* Bit 5:    Channel on when set, silent when clear.
X   Bits 4-2: Octave (0 lowest, 7 highest)
X   Bits 1-0: Most significant bytes of frequency (FNUM_HI)*/
X
X#define FEED_ALG(voice)	(0xc0 + (voice))
X/* Bits 3-1: Feedback strength.  0 = none, 7 = max
X   Bit 0:    If Set, both operators output directly.
X             If Clear, operator 1 modulates operator 2.  */
X
X/*
X * Sound Blaster dual-Timer controls
X */
X#define TIMER1		0x02
X/* If Timer1 is enabled, this register will ++ every 80 uSec until
X   it overflows.  Upon overflow, card will signal a Timer interrupt (INT 08)
X   and set bits 7 and 6 in status byte.  */
X
X#define TIMER2		0x03
X/* Second timer same as Timer1 except it sets status bits 7 and 5
X   on overflow, and increments every 320 uSec.  */
X
X#define TIMER_CTL	0x04
X/* Bit 7: Resets flags for Timer 1 & 2.  (All other bits ignored)
X   Bit 6: Masks Timer1.  (Bit 0 is ignored)
X   Bit 5: Masks Timer2.  (Bit 1 is ignored)
X   Bit 1: When clear, Timer2 not running.
X          When set, value from TIMER2 is loaded into Timer2 and incr begins.
X   Bit 0: Same as Bit 1, but for Timer1.  */
X
X/* Timer control bits as explained in TIMER_CTL */
X#define FM_TIMER_RESET		(1 << 7)
X#define FM_TIMER_MASK1		(1 << 6)
X#define FM_TIMER_MASK2		(1 << 5)
X#define FM_TIMER_START2  	(1 << 1)
X#define FM_TIMER_START1  	(1 << 0)
END_OF_FILE
  if test 14536 -ne `wc -c <'sb_regs.h'`; then
    echo shar: \"'sb_regs.h'\" unpacked with wrong size!
  fi
  # end of 'sb_regs.h'
fi
if test -f 'sblast.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sblast.h'\"
else
  echo shar: Extracting \"'sblast.h'\" \(7916 characters\)
  sed "s/^X//" >'sblast.h' <<'END_OF_FILE'
X/*======================================================================
X
X   Header file for Sound Blaster user programs.
X   [ This file is a part of SBlast-BSD-1.5 ]
X
X   Steve Haehnichen <shaehnic@ucsd.edu>
X
X   sblast.h,v 2.2 1992/09/14 03:09:25 steve Exp
X
X   Copyright (C) 1992 Steve Haehnichen.
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X * sblast.h,v
X * Revision 2.2  1992/09/14  03:09:25  steve
X * Released as distribution v1.5.
X * Mostly stable, still lacking stereo recording.
X *
X * Revision 2.1  1992/08/02  21:55:03  steve
X * Added select handler and streamlined a few things.
X * Now allows read/write on a single open.
X *
X * Revision 2.0  1992/07/13  01:25:23  steve
X * Promoting to v2.0 with start of CVS management.
X *
X * Revision 1.5.1.1  1992/07/13  00:39:32  steve
X * Stable double-buffering Sound Blaster driver.
X *
X * Revision 1.5  1992/06/13  01:48:15  steve
X * Released in SBlast-BSD-1.5
X
X======================================================================*/
X
X#ifndef SBLAST_H
X#define SBLAST_H
X#include <sys/ioctl.h>
X
X/*
X * Here is where you select which card you have.
X * Define only one of these.
X *
X * NOTE: Version 1.5 supports ONLY the SBPRO selection.
X *       Feel free to add support for the rest. :-) 
X */
X
X/* Define this if you have a Sound Blaster Pro  (DSP v3.x) */
X#define SBPRO
X/* Define this if you have a Sound Blaster with a DSP version 2.x */
X/* #define SB20 */
X/* Define this if you have an older Sound Blaster (DSP v1.x) */
X/* #define SB10 */
X
Xtypedef unsigned char sbBYTE;
Xtypedef unsigned char sbFLAG;
X
X/*
X * Available compression modes:
X * (These are actually DSP DMA-start commands, but you don't care)
X */
Xenum
X{
X  PCM_8 	= 0x14,		/* Straight 8-bit PCM */
X  ADPCM_4 	= 0x74,		/* 4-bit ADPCM compression */
X  ADPCM_3	= 0x76,		/* 2.5-bit ADPCM compression */
X  ADPCM_2 	= 0x16,		/* 2-bit ADPCM compression */
X};
X
Xenum { FM_LEFT, FM_RIGHT, FM_BOTH };	/* Stereo channel choices */
X
X
X
X/*   IOCTLs for FM, Mixer, and DSP control.   */
X
X/*
X * FM: Reset chips to silence
X *     Play a note as defined by struct sb_fm_note
X *     Set global FM parameters as defined by struct sb_fm_params
X */
X#define FM_IOCTL_RESET      	_IO('s', 10)
X#define FM_IOCTL_PLAY_NOTE  	_IOW('s', 11, struct sb_fm_note)
X#define FM_IOCTL_SET_PARAMS 	_IOW('s', 13, struct sb_fm_params)
X
X/* Mixer: Set mixer volume levels
X *        Set mixer control parameters (non-volume stuff)
X *	  Read current mixer volume levels
X *	  Read current mixer parameters
X *	  Reset the mixer to a default state
X */	  
X#define MIXER_IOCTL_SET_LEVELS 	_IOW('s', 20, struct sb_mixer_levels)
X#define MIXER_IOCTL_SET_PARAMS 	_IOW('s', 21, struct sb_mixer_params)
X#define MIXER_IOCTL_READ_LEVELS	_IOR('s', 22, struct sb_mixer_levels)
X#define MIXER_IOCTL_READ_PARAMS _IOR('s', 23, struct sb_mixer_params)
X#define MIXER_IOCTL_RESET 	_IO('s', 24)
X
X/* DSP: Reset the DSP and terminate any transfers.
X *      Set the speed (in Hz) of DSP playback/record.
X *      (Note that the speed parameter is modified to be the actual speed.)
X *      Turn the DSP voice output on (non-zero) or off (0)
X *      Flush any pending written data.
X *      Set the DSP decompression mode to one of the above modes.
X *      Set Stereo playback/record on (non-zero) or off (0)
X *	Set the DMA transfer buffer size.
X *	Errno generated when there is recording overrun.  (ESUCCESS for ignore)
X */
X#define DSP_IOCTL_RESET 	_IO('s', 0)
X#define DSP_IOCTL_SPEED 	_IOWR('s', 1, int)
X#define DSP_IOCTL_VOICE 	_IOW('s', 2, sbFLAG)
X#define DSP_IOCTL_FLUSH 	_IO('s', 3)
X#define DSP_IOCTL_COMPRESS	_IOW('s', 4, sbBYTE)
X#define DSP_IOCTL_STEREO	_IOW('s', 5, sbFLAG) /* Can go to mixer too */
X#define DSP_IOCTL_BUFSIZE	_IOWR('s', 6, int)
X#define DSP_IOCTL_OVERRUN_ERRNO	_IOW('s', 7, int)
X
X/*
X * DSP legal speed range:
X * For cards other than the SBPRO, there are separate limits
X * on recording and playback speed.  Future driver versions will
X * address this.  Also, compression effects the valid range.  Whew..
X */
X# define DSP_MIN_SPEED	3906
X#ifdef SBPRO
X#  define DSP_MAX_SPEED	47619
X#endif
X#ifdef SB20
X#  define DSP_MAX_SPEED 43478
X#endif
X#ifdef SB10
X#  define DSP_MAX_SPEED 22222
X#endif
X
X/* The smallest and largest allowable DSP transfer buffer size.
X   Note that the size must also be divisible by two */
X#define DSP_MIN_BUFSIZE 256
X#define DSP_MAX_BUFSIZE (64 * 1024)
X
Xstruct stereo_vol
X{
X  sbBYTE l;			/* Left volume */
X  sbBYTE r;			/* Right volume */
X};
X
X
X/*
X * Mixer volume levels for MIXER_IOCTL_SET_VOL & MIXER_IOCTL_READ_VOL
X */
Xstruct sb_mixer_levels
X{
X  struct stereo_vol master;	/* Master volume */
X  struct stereo_vol voc;	/* DSP Voice volume */
X  struct stereo_vol fm;		/* FM volume */
X  struct stereo_vol line;	/* Line-in volume */
X  struct stereo_vol cd;		/* CD audio */
X  sbBYTE mic;			/* Microphone level */
X};
X
X/*
X * Mixer parameters for MIXER_IOCTL_SET_PARAMS & MIXER_IOCTL_READ_PARAMS
X */
Xstruct sb_mixer_params
X{
X  sbBYTE record_source;		/* Recording source (See SRC_xxx below) */
X  sbFLAG hifreq_filter;		/* Filter frequency (hi/low) */
X  sbFLAG filter_input;		/* ANFI input filter */
X  sbFLAG filter_output;		/* DNFI output filter */
X  sbFLAG dsp_stereo;		/* 1 if DSP is in Stereo mode */
X};
X#define SRC_MIC         1	/* Select Microphone recording source */
X#define SRC_CD          3	/* Select CD recording source */
X#define SRC_LINE        7	/* Use Line-in for recording source */
X
X
X/*
X * Data structure composing an FM "note" or sound event.
X */
Xstruct sb_fm_note
X{
X  sbBYTE channel;			/* LEFT, RIGHT, or BOTH */
X  sbBYTE operator;		/* Operator cell (0 or 1) */
X  sbBYTE voice;			/* FM voice (0 to 8) */
X
X  /* Per operator: */
X  sbBYTE waveform;		/* 2 bits: Select wave shape (see WAVEFORM) */
X  sbFLAG am;			/* Amplitude modulation */
X  sbFLAG vibrato;			/* Vibrato effect */
X  sbFLAG do_sustain;		/* Do sustain phase, or skip it */
X  sbFLAG kbd_scale;		/* Keyboard scaling? */
X
X  sbBYTE harmonic;		/* 4 bits: Which harmonic to generate */
X  sbBYTE scale_level;		/* 2 bits: Decrease output as freq rises*/
X  sbBYTE volume;			/* 6 bits: Intuitive volume */
X
X  sbBYTE attack;			/* 4 bits: Attack rate */
X  sbBYTE decay;			/* 4 bits: Decay rate */
X  sbBYTE sustain;			/* 4 bits: Sustain level */
X  sbBYTE release;			/* 4 bits: Release rate */
X
X  /* Apply to both operators of one voice: */
X  sbBYTE feedback;		/* 3 bits: How much feedback for op0 */
X  sbFLAG key_on;			/* Set for active, Clear for silent */
X  sbFLAG indep_op;		/* Clear for op0 to modulate op1,
X				   Set for parallel tones. */
X
X  /* Freq = 50000 * Fnumber * 2^(octave - 20) */
X  sbBYTE octave;			/* 3 bits: What octave to play (0 = low) */
X  unsigned int fnum;		/* 10 bits: Frequency "number" */
X};
X
X/*
X * FM parameters that apply globally to all voices, and thus are not "notes"
X */
Xstruct sb_fm_params
X{
X  sbBYTE channel;			/* LEFT, RIGHT, or BOTH, as defined above */
X
X  sbFLAG am_depth;		/* Amplitude Modulation depth (1=hi) */
X  sbFLAG vib_depth;		/* Vibrato depth (1=hi) */
X  sbFLAG wave_ctl;		/* Let voices select their waveform */
X  sbFLAG speech;			/* Composite "Speech" mode (?) */
X  sbFLAG kbd_split;		/* Keyboard split */
X  sbFLAG rhythm;			/* Percussion mode select */
X
X  /* Percussion instruments */
X  sbFLAG bass;
X  sbFLAG snare;
X  sbFLAG tomtom;
X  sbFLAG cymbal;
X  sbFLAG hihat;
X};
X
X#endif				/* !def SBLAST_H */
END_OF_FILE
  if test 7916 -ne `wc -c <'sblast.h'`; then
    echo shar: \"'sblast.h'\" unpacked with wrong size!
  fi
  # end of 'sblast.h'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still must unpack the following archives:
    echo "        " ${MISSING}
fi
exit 0
-- 
/* Kurt J. Lidl (lidl@pix.com)    | Unix is the answer, but only if you */
/* UUCP: uunet!mimsy!pixcom!lidl  | phrase the question very carefully. */