spkr.c revision 12854
14Srgrimes/*
2738Sache * spkr.c -- device driver for console speaker
34Srgrimes *
4738Sache * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
5738Sache * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
6619Srgrimes *
712854Sbde *    $Id: spkr.c,v 1.22 1995/12/08 23:20:48 phk Exp $
84Srgrimes */
94Srgrimes
104Srgrimes#include "speaker.h"
114Srgrimes
124Srgrimes#if NSPEAKER > 0
134Srgrimes
142056Swollman#include <sys/param.h>
152056Swollman#include <sys/systm.h>
162056Swollman#include <sys/kernel.h>
172056Swollman#include <sys/errno.h>
182056Swollman#include <sys/buf.h>
197090Sbde#include <sys/proc.h>
202056Swollman#include <sys/uio.h>
2112675Sjulian#include <sys/conf.h>
222056Swollman#include <i386/isa/isa.h>
232056Swollman#include <i386/isa/timerreg.h>
247090Sbde#include <machine/clock.h>
252056Swollman#include <machine/speaker.h>
264Srgrimes
2712502Sjulian
2812502Sjulian
2910537Sjulian#ifdef	DEVFS
3010537Sjulian#include <sys/devfsext.h>
3112675Sjulianvoid	*devfs_token;
3212502Sjulian#endif
3310537Sjulian
3412675Sjulianstatic	d_open_t	spkropen;
3512675Sjulianstatic	d_close_t	spkrclose;
3612675Sjulianstatic	d_write_t	spkrwrite;
3712675Sjulianstatic	d_ioctl_t	spkrioctl;
3812502Sjulian
3912675Sjulian#define CDEV_MAJOR 26
4012678Sphkstatic struct cdevsw spkr_cdevsw =
4112675Sjulian	{ spkropen,     spkrclose,      noread,         spkrwrite,      /*26*/
4212675Sjulian	  spkrioctl,    nostop,         nullreset,      nodevtotty,/* spkr */
4312675Sjulian	  seltrue,	nommap,		NULL,	"spkr",	NULL,	-1 };
4412675Sjulian
454Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE *************************
464Srgrimes *
474Srgrimes * This section defines a function tone() which causes a tone of given
484Srgrimes * frequency and duration from the 80x86's console speaker.
494Srgrimes * Another function endtone() is defined to force sound off, and there is
504Srgrimes * also a rest() entry point to do pauses.
514Srgrimes *
524Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and
534Srgrimes * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The
544Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is
554Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired.
564Srgrimes */
574Srgrimes
584Srgrimes/*
594Srgrimes * PIT and PPI port addresses and control values
604Srgrimes *
614Srgrimes * Most of the magic is hidden in the TIMER_PREP value, which selects PIT
624Srgrimes * channel 2, frequency LSB first, square-wave mode and binary encoding.
634Srgrimes * The encoding is as follows:
644Srgrimes *
654Srgrimes * +----------+----------+---------------+-----+
664Srgrimes * |  1    0  |  1    1  |  0    1    1  |  0  |
674Srgrimes * | SC1  SC0 | RW1  RW0 | M2   M1   M0  | BCD |
684Srgrimes * +----------+----------+---------------+-----+
694Srgrimes *   Counter     Write        Mode 3      Binary
708876Srgrimes *  Channel 2  LSB first,  (Square Wave) Encoding
714Srgrimes *             MSB second
724Srgrimes */
734Srgrimes#define PPI_SPKR	0x03	/* turn these PPI bits on to pass sound */
744Srgrimes#define PIT_MODE	0xB6	/* set timer mode for sound generation */
754Srgrimes
764Srgrimes/*
778876Srgrimes * Magic numbers for timer control.
784Srgrimes */
794Srgrimes#define TIMER_CLK	1193180L	/* corresponds to 18.2 MHz tick rate */
804Srgrimes
81766Sache#define SPKRPRI PSOCK
82766Sachestatic char endtone, endrest;
834Srgrimes
8412854Sbdestatic void tone __P((unsigned int thz, unsigned int ticks));
8512854Sbdestatic void rest __P((int ticks));
8612854Sbdestatic void playinit __P((void));
8712854Sbdestatic void playtone __P((int pitch, int value, int sustain));
8812854Sbdestatic int abs __P((int n));
8912854Sbdestatic void playstring __P((char *cp, size_t slen));
9012854Sbde
911016Sachestatic void tone(thz, ticks)
92766Sache/* emit tone of frequency thz for given number of ticks */
93766Sacheunsigned int thz, ticks;
944Srgrimes{
958288Sdg    unsigned int divisor;
961016Sache    int sps;
974Srgrimes
988288Sdg    if (thz <= 0)
998288Sdg	return;
1008288Sdg
1018288Sdg    divisor = TIMER_CLK / thz;
1028288Sdg
1034Srgrimes#ifdef DEBUG
104766Sache    (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
1054Srgrimes#endif /* DEBUG */
1064Srgrimes
1074Srgrimes    /* set timer to generate clicks at given frequency in Hertz */
1084Srgrimes    sps = spltty();
1091393Ssos
1101393Ssos    if (acquire_timer2(PIT_MODE)) {
1111393Ssos	/* enter list of waiting procs ??? */
1121393Ssos	return;
1131393Ssos    }
1141393Ssos    outb(TIMER_CNTR2, (divisor & 0xff));	/* send lo byte */
1151393Ssos    outb(TIMER_CNTR2, (divisor >> 8));	/* send hi byte */
1164Srgrimes    splx(sps);
1174Srgrimes
1184Srgrimes    /* turn the speaker on */
1191393Ssos    outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
1204Srgrimes
1214Srgrimes    /*
1224Srgrimes     * Set timeout to endtone function, then give up the timeslice.
1234Srgrimes     * This is so other processes can execute while the tone is being
1244Srgrimes     * emitted.
1254Srgrimes     */
1266152Sache    if (ticks > 0)
1276152Sache	tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
1281393Ssos    outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
1291393Ssos    release_timer2();
1304Srgrimes}
1314Srgrimes
1321016Sachestatic void rest(ticks)
1334Srgrimes/* rest for given number of ticks */
1344Srgrimesint	ticks;
1354Srgrimes{
1364Srgrimes    /*
1374Srgrimes     * Set timeout to endrest function, then give up the timeslice.
1384Srgrimes     * This is so other processes can execute while the rest is being
1394Srgrimes     * waited out.
1404Srgrimes     */
1414Srgrimes#ifdef DEBUG
142738Sache    (void) printf("rest: %d\n", ticks);
1434Srgrimes#endif /* DEBUG */
1446152Sache    if (ticks > 0)
1456152Sache	tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
1464Srgrimes}
1474Srgrimes
1484Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
1494Srgrimes *
1504Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
151738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
152738Sache * tracking facility are added.
1534Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible
1544Srgrimes * except possibly at physical block boundaries.
1554Srgrimes */
1564Srgrimes
1574Srgrimestypedef int	bool;
1584Srgrimes#define TRUE	1
1594Srgrimes#define FALSE	0
1604Srgrimes
1614Srgrimes#define toupper(c)	((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
1624Srgrimes#define isdigit(c)	(((c) >= '0') && ((c) <= '9'))
1634Srgrimes#define dtoi(c)		((c) - '0')
1644Srgrimes
1654Srgrimesstatic int octave;	/* currently selected octave */
1664Srgrimesstatic int whole;	/* whole-note time at current tempo, in ticks */
1674Srgrimesstatic int value;	/* whole divisor for note time, quarter note = 1 */
1684Srgrimesstatic int fill;	/* controls spacing of notes */
1694Srgrimesstatic bool octtrack;	/* octave-tracking on? */
1704Srgrimesstatic bool octprefix;	/* override current octave-tracking state? */
1714Srgrimes
1724Srgrimes/*
1734Srgrimes * Magic number avoidance...
1744Srgrimes */
1754Srgrimes#define SECS_PER_MIN	60	/* seconds per minute */
1764Srgrimes#define WHOLE_NOTE	4	/* quarter notes per whole note */
1774Srgrimes#define MIN_VALUE	64	/* the most we can divide a note by */
1784Srgrimes#define DFLT_VALUE	4	/* default value (quarter-note) */
1794Srgrimes#define FILLTIME	8	/* for articulation, break note in parts */
1804Srgrimes#define STACCATO	6	/* 6/8 = 3/4 of note is filled */
1814Srgrimes#define NORMAL		7	/* 7/8ths of note interval is filled */
1824Srgrimes#define LEGATO		8	/* all of note interval is filled */
1834Srgrimes#define DFLT_OCTAVE	4	/* default octave */
1844Srgrimes#define MIN_TEMPO	32	/* minimum tempo */
1854Srgrimes#define DFLT_TEMPO	120	/* default tempo */
1864Srgrimes#define MAX_TEMPO	255	/* max tempo */
1874Srgrimes#define NUM_MULT	3	/* numerator of dot multiplier */
1884Srgrimes#define DENOM_MULT	2	/* denominator of dot multiplier */
1894Srgrimes
1904Srgrimes/* letter to half-tone:  A   B  C  D  E  F  G */
1914Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
1924Srgrimes
1934Srgrimes/*
1944Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies
1954Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
1964Srgrimes * our octave 0 is standard octave 2.
1974Srgrimes */
1984Srgrimes#define OCTAVE_NOTES	12	/* semitones per octave */
1994Srgrimesstatic int pitchtab[] =
2004Srgrimes{
2014Srgrimes/*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
2024Srgrimes/* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
2034Srgrimes/* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
2044Srgrimes/* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
2054Srgrimes/* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
2064Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
2074Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
2084Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
2094Srgrimes};
2104Srgrimes
2114Srgrimesstatic void playinit()
2124Srgrimes{
2134Srgrimes    octave = DFLT_OCTAVE;
214766Sache    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
2154Srgrimes    fill = NORMAL;
2164Srgrimes    value = DFLT_VALUE;
2174Srgrimes    octtrack = FALSE;
2184Srgrimes    octprefix = TRUE;	/* act as though there was an initial O(n) */
2194Srgrimes}
2204Srgrimes
2211016Sachestatic void playtone(pitch, value, sustain)
2224Srgrimes/* play tone of proper duration for current rhythm signature */
2234Srgrimesint	pitch, value, sustain;
2244Srgrimes{
2254Srgrimes    register int	sound, silence, snum = 1, sdenom = 1;
2264Srgrimes
2274Srgrimes    /* this weirdness avoids floating-point arithmetic */
2284Srgrimes    for (; sustain; sustain--)
2294Srgrimes    {
230738Sache	/* See the BUGS section in the man page for discussion */
2314Srgrimes	snum *= NUM_MULT;
2324Srgrimes	sdenom *= DENOM_MULT;
2334Srgrimes    }
2344Srgrimes
2358288Sdg    if (value == 0 || sdenom == 0)
2368288Sdg	return;
2378288Sdg
2384Srgrimes    if (pitch == -1)
2391016Sache	rest(whole * snum / (value * sdenom));
2404Srgrimes    else
2414Srgrimes    {
2424Srgrimes	sound = (whole * snum) / (value * sdenom)
2434Srgrimes		- (whole * (FILLTIME - fill)) / (value * FILLTIME);
2444Srgrimes	silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
2454Srgrimes
2464Srgrimes#ifdef DEBUG
247738Sache	(void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
2484Srgrimes			pitch, sound, silence);
2494Srgrimes#endif /* DEBUG */
2504Srgrimes
2511016Sache	tone(pitchtab[pitch], sound);
2524Srgrimes	if (fill != LEGATO)
2531016Sache	    rest(silence);
2544Srgrimes    }
2554Srgrimes}
2564Srgrimes
2574Srgrimesstatic int abs(n)
2584Srgrimesint n;
2594Srgrimes{
2604Srgrimes    if (n < 0)
2614Srgrimes	return(-n);
2624Srgrimes    else
2634Srgrimes	return(n);
2644Srgrimes}
2654Srgrimes
2661016Sachestatic void playstring(cp, slen)
2674Srgrimes/* interpret and play an item from a notation string */
2684Srgrimeschar	*cp;
2694Srgrimessize_t	slen;
2704Srgrimes{
271738Sache    int		pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
2724Srgrimes
2734Srgrimes#define GETNUM(cp, v)	for(v=0; isdigit(cp[1]) && slen > 0; ) \
2744Srgrimes				{v = v * 10 + (*++cp - '0'); slen--;}
2754Srgrimes    for (; slen--; cp++)
2764Srgrimes    {
2774Srgrimes	int		sustain, timeval, tempo;
2784Srgrimes	register char	c = toupper(*cp);
2794Srgrimes
2804Srgrimes#ifdef DEBUG
281738Sache	(void) printf("playstring: %c (%x)\n", c, c);
2824Srgrimes#endif /* DEBUG */
2834Srgrimes
2844Srgrimes	switch (c)
2854Srgrimes	{
2864Srgrimes	case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2874Srgrimes
2884Srgrimes	    /* compute pitch */
2894Srgrimes	    pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
2904Srgrimes
2914Srgrimes	    /* this may be followed by an accidental sign */
2924Srgrimes	    if (cp[1] == '#' || cp[1] == '+')
2934Srgrimes	    {
2944Srgrimes		++pitch;
2954Srgrimes		++cp;
2964Srgrimes		slen--;
2974Srgrimes	    }
2984Srgrimes	    else if (cp[1] == '-')
2994Srgrimes	    {
3004Srgrimes		--pitch;
3014Srgrimes		++cp;
3024Srgrimes		slen--;
3034Srgrimes	    }
3044Srgrimes
3054Srgrimes	    /*
3064Srgrimes	     * If octave-tracking mode is on, and there has been no octave-
3074Srgrimes	     * setting prefix, find the version of the current letter note
3084Srgrimes	     * closest to the last regardless of octave.
3094Srgrimes	     */
3104Srgrimes	    if (octtrack && !octprefix)
3114Srgrimes	    {
3124Srgrimes		if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
3134Srgrimes		{
3144Srgrimes		    ++octave;
3154Srgrimes		    pitch += OCTAVE_NOTES;
3164Srgrimes		}
3174Srgrimes
3184Srgrimes		if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
3194Srgrimes		{
3204Srgrimes		    --octave;
3214Srgrimes		    pitch -= OCTAVE_NOTES;
3224Srgrimes		}
3234Srgrimes	    }
3244Srgrimes	    octprefix = FALSE;
3254Srgrimes	    lastpitch = pitch;
3264Srgrimes
3274Srgrimes	    /* ...which may in turn be followed by an override time value */
3284Srgrimes	    GETNUM(cp, timeval);
3294Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
3304Srgrimes		timeval = value;
3314Srgrimes
3324Srgrimes	    /* ...and/or sustain dots */
3334Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3344Srgrimes	    {
3354Srgrimes		slen--;
3364Srgrimes		sustain++;
3374Srgrimes	    }
3384Srgrimes
339738Sache	    /* ...and/or a slur mark */
340738Sache	    oldfill = fill;
341738Sache	    if (cp[1] == '_')
342738Sache	    {
343738Sache		fill = LEGATO;
344738Sache		++cp;
345738Sache		slen--;
346738Sache	    }
347738Sache
3484Srgrimes	    /* time to emit the actual tone */
3491016Sache	    playtone(pitch, timeval, sustain);
350738Sache
351738Sache	    fill = oldfill;
3524Srgrimes	    break;
3534Srgrimes
3544Srgrimes	case 'O':
3554Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
3564Srgrimes	    {
3574Srgrimes		octprefix = octtrack = FALSE;
3584Srgrimes		++cp;
3594Srgrimes		slen--;
3604Srgrimes	    }
3614Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
3624Srgrimes	    {
3634Srgrimes		octtrack = TRUE;
3644Srgrimes		++cp;
3654Srgrimes		slen--;
3664Srgrimes	    }
3674Srgrimes	    else
3684Srgrimes	    {
3694Srgrimes		GETNUM(cp, octave);
3703593Sache		if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
3714Srgrimes		    octave = DFLT_OCTAVE;
3724Srgrimes		octprefix = TRUE;
3734Srgrimes	    }
3744Srgrimes	    break;
3754Srgrimes
3764Srgrimes	case '>':
3773593Sache	    if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
3784Srgrimes		octave++;
3794Srgrimes	    octprefix = TRUE;
3804Srgrimes	    break;
3814Srgrimes
3824Srgrimes	case '<':
3834Srgrimes	    if (octave > 0)
3844Srgrimes		octave--;
3854Srgrimes	    octprefix = TRUE;
3864Srgrimes	    break;
3874Srgrimes
3884Srgrimes	case 'N':
3894Srgrimes	    GETNUM(cp, pitch);
3904Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3914Srgrimes	    {
3924Srgrimes		slen--;
3934Srgrimes		sustain++;
3944Srgrimes	    }
395738Sache	    oldfill = fill;
396738Sache	    if (cp[1] == '_')
397738Sache	    {
398738Sache		fill = LEGATO;
399738Sache		++cp;
400738Sache		slen--;
401738Sache	    }
4021016Sache	    playtone(pitch - 1, value, sustain);
403738Sache	    fill = oldfill;
4044Srgrimes	    break;
4054Srgrimes
4064Srgrimes	case 'L':
4074Srgrimes	    GETNUM(cp, value);
4084Srgrimes	    if (value <= 0 || value > MIN_VALUE)
4094Srgrimes		value = DFLT_VALUE;
4104Srgrimes	    break;
4114Srgrimes
4124Srgrimes	case 'P':
4134Srgrimes	case '~':
4144Srgrimes	    /* this may be followed by an override time value */
4154Srgrimes	    GETNUM(cp, timeval);
4164Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
4174Srgrimes		timeval = value;
4184Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
4194Srgrimes	    {
4204Srgrimes		slen--;
4214Srgrimes		sustain++;
4224Srgrimes	    }
4231016Sache	    playtone(-1, timeval, sustain);
4244Srgrimes	    break;
4254Srgrimes
4264Srgrimes	case 'T':
4274Srgrimes	    GETNUM(cp, tempo);
4284Srgrimes	    if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
4294Srgrimes		tempo = DFLT_TEMPO;
430766Sache	    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
4314Srgrimes	    break;
4324Srgrimes
4334Srgrimes	case 'M':
4344Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
4354Srgrimes	    {
4364Srgrimes		fill = NORMAL;
4374Srgrimes		++cp;
4384Srgrimes		slen--;
4394Srgrimes	    }
4404Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
4414Srgrimes	    {
4424Srgrimes		fill = LEGATO;
4434Srgrimes		++cp;
4444Srgrimes		slen--;
4454Srgrimes	    }
4464Srgrimes	    else if (cp[1] == 'S' || cp[1] == 's')
4474Srgrimes	    {
4484Srgrimes		fill = STACCATO;
4494Srgrimes		++cp;
4504Srgrimes		slen--;
4514Srgrimes	    }
4524Srgrimes	    break;
4534Srgrimes	}
4544Srgrimes    }
4554Srgrimes}
4564Srgrimes
4574Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
4584Srgrimes *
4594Srgrimes * This section implements driver hooks to run playstring() and the tone(),
4604Srgrimes * endtone(), and rest() functions defined above.
4614Srgrimes */
4624Srgrimes
463738Sachestatic int spkr_active = FALSE; /* exclusion flag */
464738Sachestatic struct buf *spkr_inbuf;  /* incoming buf */
4654Srgrimes
46610624Sbdeint spkropen(dev, flags, fmt, p)
46710624Sbdedev_t		dev;
46810624Sbdeint		flags;
46910624Sbdeint		fmt;
47010624Sbdestruct proc	*p;
4714Srgrimes{
4724Srgrimes#ifdef DEBUG
473738Sache    (void) printf("spkropen: entering with dev = %x\n", dev);
4744Srgrimes#endif /* DEBUG */
4754Srgrimes
4764Srgrimes    if (minor(dev) != 0)
4774Srgrimes	return(ENXIO);
4784Srgrimes    else if (spkr_active)
4794Srgrimes	return(EBUSY);
4804Srgrimes    else
4814Srgrimes    {
482738Sache#ifdef DEBUG
483738Sache	(void) printf("spkropen: about to perform play initialization\n");
484738Sache#endif /* DEBUG */
4854Srgrimes	playinit();
4864Srgrimes	spkr_inbuf = geteblk(DEV_BSIZE);
487738Sache	spkr_active = TRUE;
488738Sache	return(0);
4894Srgrimes    }
4904Srgrimes}
4914Srgrimes
49210624Sbdeint spkrwrite(dev, uio, ioflag)
493738Sachedev_t		dev;
494738Sachestruct uio	*uio;
49510624Sbdeint		ioflag;
4964Srgrimes{
4974Srgrimes#ifdef DEBUG
4984Srgrimes    printf("spkrwrite: entering with dev = %x, count = %d\n",
4994Srgrimes		dev, uio->uio_resid);
5004Srgrimes#endif /* DEBUG */
5014Srgrimes
5024Srgrimes    if (minor(dev) != 0)
5034Srgrimes	return(ENXIO);
504738Sache    else if (uio->uio_resid > DEV_BSIZE)     /* prevent system crashes */
505738Sache	return(E2BIG);
5064Srgrimes    else
5074Srgrimes    {
508738Sache	unsigned n;
509738Sache	char *cp;
510738Sache	int error;
511738Sache
512738Sache	n = uio->uio_resid;
5134Srgrimes	cp = spkr_inbuf->b_un.b_addr;
514738Sache	if (!(error = uiomove(cp, n, uio)))
5151016Sache		playstring(cp, n);
5164Srgrimes	return(error);
5174Srgrimes    }
5184Srgrimes}
5194Srgrimes
52010624Sbdeint spkrclose(dev, flags, fmt, p)
52110624Sbdedev_t		dev;
52210624Sbdeint		flags;
52310624Sbdeint		fmt;
52410624Sbdestruct proc	*p;
5254Srgrimes{
5264Srgrimes#ifdef DEBUG
527738Sache    (void) printf("spkrclose: entering with dev = %x\n", dev);
5284Srgrimes#endif /* DEBUG */
5294Srgrimes
5304Srgrimes    if (minor(dev) != 0)
5314Srgrimes	return(ENXIO);
5324Srgrimes    else
5334Srgrimes    {
534766Sache	wakeup((caddr_t)&endtone);
535766Sache	wakeup((caddr_t)&endrest);
5364Srgrimes	brelse(spkr_inbuf);
537738Sache	spkr_active = FALSE;
538738Sache	return(0);
5394Srgrimes    }
5404Srgrimes}
5414Srgrimes
54210624Sbdeint spkrioctl(dev, cmd, cmdarg, flags, p)
54310624Sbdedev_t		dev;
54410624Sbdeint		cmd;
54510624Sbdecaddr_t		cmdarg;
54610624Sbdeint		flags;
54710624Sbdestruct proc	*p;
5484Srgrimes{
5494Srgrimes#ifdef DEBUG
550738Sache    (void) printf("spkrioctl: entering with dev = %x, cmd = %x\n");
5514Srgrimes#endif /* DEBUG */
5524Srgrimes
5534Srgrimes    if (minor(dev) != 0)
5544Srgrimes	return(ENXIO);
5554Srgrimes    else if (cmd == SPKRTONE)
5564Srgrimes    {
5574Srgrimes	tone_t	*tp = (tone_t *)cmdarg;
5584Srgrimes
5594Srgrimes	if (tp->frequency == 0)
5601016Sache	    rest(tp->duration);
5614Srgrimes	else
5621016Sache	    tone(tp->frequency, tp->duration);
5631016Sache	return 0;
5644Srgrimes    }
5654Srgrimes    else if (cmd == SPKRTUNE)
5664Srgrimes    {
5674Srgrimes	tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
5684Srgrimes	tone_t ttp;
5694Srgrimes	int error;
5704Srgrimes
5714Srgrimes	for (; ; tp++) {
5724Srgrimes	    error = copyin(tp, &ttp, sizeof(tone_t));
5734Srgrimes	    if (error)
5744Srgrimes		    return(error);
5754Srgrimes	    if (ttp.duration == 0)
5764Srgrimes		    break;
5774Srgrimes	    if (ttp.frequency == 0)
5781016Sache		 rest(ttp.duration);
5794Srgrimes	    else
5801016Sache		 tone(ttp.frequency, ttp.duration);
5814Srgrimes	}
582738Sache	return(0);
5834Srgrimes    }
584738Sache    return(EINVAL);
5854Srgrimes}
5864Srgrimes
58712502Sjulian
58812502Sjulianstatic spkr_devsw_installed = 0;
58912502Sjulian
59012517Sjulianstatic void 	spkr_drvinit(void *unused)
59112502Sjulian{
59212517Sjulian	dev_t dev;
59312517Sjulian
59412502Sjulian	if( ! spkr_devsw_installed ) {
59512675Sjulian		dev = makedev(CDEV_MAJOR, 0);
59612675Sjulian		cdevsw_add(&dev,&spkr_cdevsw, NULL);
59712502Sjulian		spkr_devsw_installed = 1;
59812517Sjulian#ifdef DEVFS
59912675Sjulian			devfs_token = devfs_add_devsw("/", "spkr",
60012675Sjulian						&spkr_cdevsw, 0,
60112675Sjulian						DV_CHR, 0,  0, 0600);
60212521Sjulian#endif
60312517Sjulian    	}
60412502Sjulian}
60512517Sjulian
60612517SjulianSYSINIT(spkrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,spkr_drvinit,NULL)
60712517Sjulian
60812517Sjulian
6094Srgrimes#endif  /* NSPEAKER > 0 */
6104Srgrimes/* spkr.c ends here */
611