spkr.c revision 17232
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 *
717232Sjoerg *    $Id: spkr.c,v 1.24 1996/03/27 19:07:33 bde 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
91766Sache/* emit tone of frequency thz for given number of ticks */
9217232Sjoergstatic void
9317232Sjoergtone(thz, ticks)
9417232Sjoerg	unsigned int thz, ticks;
954Srgrimes{
968288Sdg    unsigned int divisor;
971016Sache    int sps;
984Srgrimes
998288Sdg    if (thz <= 0)
1008288Sdg	return;
1018288Sdg
1028288Sdg    divisor = TIMER_CLK / thz;
1038288Sdg
1044Srgrimes#ifdef DEBUG
105766Sache    (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
1064Srgrimes#endif /* DEBUG */
1074Srgrimes
1084Srgrimes    /* set timer to generate clicks at given frequency in Hertz */
10917232Sjoerg    sps = splclock();
1101393Ssos
1111393Ssos    if (acquire_timer2(PIT_MODE)) {
1121393Ssos	/* enter list of waiting procs ??? */
11317232Sjoerg	splx(sps);
1141393Ssos	return;
1151393Ssos    }
11617232Sjoerg    splx(sps);
11717232Sjoerg    disable_intr();
1181393Ssos    outb(TIMER_CNTR2, (divisor & 0xff));	/* send lo byte */
1191393Ssos    outb(TIMER_CNTR2, (divisor >> 8));	/* send hi byte */
12017232Sjoerg    enable_intr();
1214Srgrimes
1224Srgrimes    /* turn the speaker on */
1231393Ssos    outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
1244Srgrimes
1254Srgrimes    /*
1264Srgrimes     * Set timeout to endtone function, then give up the timeslice.
1274Srgrimes     * This is so other processes can execute while the tone is being
1284Srgrimes     * emitted.
1294Srgrimes     */
1306152Sache    if (ticks > 0)
1316152Sache	tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
1321393Ssos    outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
13317232Sjoerg    sps = splclock();
1341393Ssos    release_timer2();
13517232Sjoerg    splx(sps);
1364Srgrimes}
1374Srgrimes
1384Srgrimes/* rest for given number of ticks */
13917232Sjoergstatic void
14017232Sjoergrest(ticks)
14117232Sjoerg	int	ticks;
1424Srgrimes{
1434Srgrimes    /*
1444Srgrimes     * Set timeout to endrest function, then give up the timeslice.
1454Srgrimes     * This is so other processes can execute while the rest is being
1464Srgrimes     * waited out.
1474Srgrimes     */
1484Srgrimes#ifdef DEBUG
149738Sache    (void) printf("rest: %d\n", ticks);
1504Srgrimes#endif /* DEBUG */
1516152Sache    if (ticks > 0)
1526152Sache	tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
1534Srgrimes}
1544Srgrimes
1554Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
1564Srgrimes *
1574Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
158738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
159738Sache * tracking facility are added.
1604Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible
1614Srgrimes * except possibly at physical block boundaries.
1624Srgrimes */
1634Srgrimes
1644Srgrimestypedef int	bool;
1654Srgrimes#define TRUE	1
1664Srgrimes#define FALSE	0
1674Srgrimes
1684Srgrimes#define toupper(c)	((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
1694Srgrimes#define isdigit(c)	(((c) >= '0') && ((c) <= '9'))
1704Srgrimes#define dtoi(c)		((c) - '0')
1714Srgrimes
1724Srgrimesstatic int octave;	/* currently selected octave */
1734Srgrimesstatic int whole;	/* whole-note time at current tempo, in ticks */
1744Srgrimesstatic int value;	/* whole divisor for note time, quarter note = 1 */
1754Srgrimesstatic int fill;	/* controls spacing of notes */
1764Srgrimesstatic bool octtrack;	/* octave-tracking on? */
1774Srgrimesstatic bool octprefix;	/* override current octave-tracking state? */
1784Srgrimes
1794Srgrimes/*
1804Srgrimes * Magic number avoidance...
1814Srgrimes */
1824Srgrimes#define SECS_PER_MIN	60	/* seconds per minute */
1834Srgrimes#define WHOLE_NOTE	4	/* quarter notes per whole note */
1844Srgrimes#define MIN_VALUE	64	/* the most we can divide a note by */
1854Srgrimes#define DFLT_VALUE	4	/* default value (quarter-note) */
1864Srgrimes#define FILLTIME	8	/* for articulation, break note in parts */
1874Srgrimes#define STACCATO	6	/* 6/8 = 3/4 of note is filled */
1884Srgrimes#define NORMAL		7	/* 7/8ths of note interval is filled */
1894Srgrimes#define LEGATO		8	/* all of note interval is filled */
1904Srgrimes#define DFLT_OCTAVE	4	/* default octave */
1914Srgrimes#define MIN_TEMPO	32	/* minimum tempo */
1924Srgrimes#define DFLT_TEMPO	120	/* default tempo */
1934Srgrimes#define MAX_TEMPO	255	/* max tempo */
1944Srgrimes#define NUM_MULT	3	/* numerator of dot multiplier */
1954Srgrimes#define DENOM_MULT	2	/* denominator of dot multiplier */
1964Srgrimes
1974Srgrimes/* letter to half-tone:  A   B  C  D  E  F  G */
1984Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
1994Srgrimes
2004Srgrimes/*
2014Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies
2024Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
2034Srgrimes * our octave 0 is standard octave 2.
2044Srgrimes */
2054Srgrimes#define OCTAVE_NOTES	12	/* semitones per octave */
2064Srgrimesstatic int pitchtab[] =
2074Srgrimes{
2084Srgrimes/*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
2094Srgrimes/* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
2104Srgrimes/* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
2114Srgrimes/* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
2124Srgrimes/* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
2134Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
2144Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
2154Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
2164Srgrimes};
2174Srgrimes
21817232Sjoergstatic void
21917232Sjoergplayinit()
2204Srgrimes{
2214Srgrimes    octave = DFLT_OCTAVE;
222766Sache    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
2234Srgrimes    fill = NORMAL;
2244Srgrimes    value = DFLT_VALUE;
2254Srgrimes    octtrack = FALSE;
2264Srgrimes    octprefix = TRUE;	/* act as though there was an initial O(n) */
2274Srgrimes}
2284Srgrimes
2294Srgrimes/* play tone of proper duration for current rhythm signature */
23017232Sjoergstatic void
23117232Sjoergplaytone(pitch, value, sustain)
23217232Sjoerg	int	pitch, value, sustain;
2334Srgrimes{
2344Srgrimes    register int	sound, silence, snum = 1, sdenom = 1;
2354Srgrimes
2364Srgrimes    /* this weirdness avoids floating-point arithmetic */
2374Srgrimes    for (; sustain; sustain--)
2384Srgrimes    {
239738Sache	/* See the BUGS section in the man page for discussion */
2404Srgrimes	snum *= NUM_MULT;
2414Srgrimes	sdenom *= DENOM_MULT;
2424Srgrimes    }
2434Srgrimes
2448288Sdg    if (value == 0 || sdenom == 0)
2458288Sdg	return;
2468288Sdg
2474Srgrimes    if (pitch == -1)
2481016Sache	rest(whole * snum / (value * sdenom));
2494Srgrimes    else
2504Srgrimes    {
2514Srgrimes	sound = (whole * snum) / (value * sdenom)
2524Srgrimes		- (whole * (FILLTIME - fill)) / (value * FILLTIME);
2534Srgrimes	silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
2544Srgrimes
2554Srgrimes#ifdef DEBUG
256738Sache	(void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
2574Srgrimes			pitch, sound, silence);
2584Srgrimes#endif /* DEBUG */
2594Srgrimes
2601016Sache	tone(pitchtab[pitch], sound);
2614Srgrimes	if (fill != LEGATO)
2621016Sache	    rest(silence);
2634Srgrimes    }
2644Srgrimes}
2654Srgrimes
26617232Sjoergstatic int
26717232Sjoergabs(n)
26817232Sjoerg	int n;
2694Srgrimes{
2704Srgrimes    if (n < 0)
2714Srgrimes	return(-n);
2724Srgrimes    else
2734Srgrimes	return(n);
2744Srgrimes}
2754Srgrimes
2764Srgrimes/* interpret and play an item from a notation string */
27717232Sjoergstatic void
27817232Sjoergplaystring(cp, slen)
27917232Sjoerg	char	*cp;
28017232Sjoerg	size_t	slen;
2814Srgrimes{
282738Sache    int		pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
2834Srgrimes
2844Srgrimes#define GETNUM(cp, v)	for(v=0; isdigit(cp[1]) && slen > 0; ) \
2854Srgrimes				{v = v * 10 + (*++cp - '0'); slen--;}
2864Srgrimes    for (; slen--; cp++)
2874Srgrimes    {
2884Srgrimes	int		sustain, timeval, tempo;
2894Srgrimes	register char	c = toupper(*cp);
2904Srgrimes
2914Srgrimes#ifdef DEBUG
292738Sache	(void) printf("playstring: %c (%x)\n", c, c);
2934Srgrimes#endif /* DEBUG */
2944Srgrimes
2954Srgrimes	switch (c)
2964Srgrimes	{
2974Srgrimes	case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2984Srgrimes
2994Srgrimes	    /* compute pitch */
3004Srgrimes	    pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
3014Srgrimes
3024Srgrimes	    /* this may be followed by an accidental sign */
3034Srgrimes	    if (cp[1] == '#' || cp[1] == '+')
3044Srgrimes	    {
3054Srgrimes		++pitch;
3064Srgrimes		++cp;
3074Srgrimes		slen--;
3084Srgrimes	    }
3094Srgrimes	    else if (cp[1] == '-')
3104Srgrimes	    {
3114Srgrimes		--pitch;
3124Srgrimes		++cp;
3134Srgrimes		slen--;
3144Srgrimes	    }
3154Srgrimes
3164Srgrimes	    /*
3174Srgrimes	     * If octave-tracking mode is on, and there has been no octave-
3184Srgrimes	     * setting prefix, find the version of the current letter note
3194Srgrimes	     * closest to the last regardless of octave.
3204Srgrimes	     */
3214Srgrimes	    if (octtrack && !octprefix)
3224Srgrimes	    {
3234Srgrimes		if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
3244Srgrimes		{
3254Srgrimes		    ++octave;
3264Srgrimes		    pitch += OCTAVE_NOTES;
3274Srgrimes		}
3284Srgrimes
3294Srgrimes		if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
3304Srgrimes		{
3314Srgrimes		    --octave;
3324Srgrimes		    pitch -= OCTAVE_NOTES;
3334Srgrimes		}
3344Srgrimes	    }
3354Srgrimes	    octprefix = FALSE;
3364Srgrimes	    lastpitch = pitch;
3374Srgrimes
3384Srgrimes	    /* ...which may in turn be followed by an override time value */
3394Srgrimes	    GETNUM(cp, timeval);
3404Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
3414Srgrimes		timeval = value;
3424Srgrimes
3434Srgrimes	    /* ...and/or sustain dots */
3444Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3454Srgrimes	    {
3464Srgrimes		slen--;
3474Srgrimes		sustain++;
3484Srgrimes	    }
3494Srgrimes
350738Sache	    /* ...and/or a slur mark */
351738Sache	    oldfill = fill;
352738Sache	    if (cp[1] == '_')
353738Sache	    {
354738Sache		fill = LEGATO;
355738Sache		++cp;
356738Sache		slen--;
357738Sache	    }
358738Sache
3594Srgrimes	    /* time to emit the actual tone */
3601016Sache	    playtone(pitch, timeval, sustain);
361738Sache
362738Sache	    fill = oldfill;
3634Srgrimes	    break;
3644Srgrimes
3654Srgrimes	case 'O':
3664Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
3674Srgrimes	    {
3684Srgrimes		octprefix = octtrack = FALSE;
3694Srgrimes		++cp;
3704Srgrimes		slen--;
3714Srgrimes	    }
3724Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
3734Srgrimes	    {
3744Srgrimes		octtrack = TRUE;
3754Srgrimes		++cp;
3764Srgrimes		slen--;
3774Srgrimes	    }
3784Srgrimes	    else
3794Srgrimes	    {
3804Srgrimes		GETNUM(cp, octave);
3813593Sache		if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
3824Srgrimes		    octave = DFLT_OCTAVE;
3834Srgrimes		octprefix = TRUE;
3844Srgrimes	    }
3854Srgrimes	    break;
3864Srgrimes
3874Srgrimes	case '>':
3883593Sache	    if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
3894Srgrimes		octave++;
3904Srgrimes	    octprefix = TRUE;
3914Srgrimes	    break;
3924Srgrimes
3934Srgrimes	case '<':
3944Srgrimes	    if (octave > 0)
3954Srgrimes		octave--;
3964Srgrimes	    octprefix = TRUE;
3974Srgrimes	    break;
3984Srgrimes
3994Srgrimes	case 'N':
4004Srgrimes	    GETNUM(cp, pitch);
4014Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
4024Srgrimes	    {
4034Srgrimes		slen--;
4044Srgrimes		sustain++;
4054Srgrimes	    }
406738Sache	    oldfill = fill;
407738Sache	    if (cp[1] == '_')
408738Sache	    {
409738Sache		fill = LEGATO;
410738Sache		++cp;
411738Sache		slen--;
412738Sache	    }
4131016Sache	    playtone(pitch - 1, value, sustain);
414738Sache	    fill = oldfill;
4154Srgrimes	    break;
4164Srgrimes
4174Srgrimes	case 'L':
4184Srgrimes	    GETNUM(cp, value);
4194Srgrimes	    if (value <= 0 || value > MIN_VALUE)
4204Srgrimes		value = DFLT_VALUE;
4214Srgrimes	    break;
4224Srgrimes
4234Srgrimes	case 'P':
4244Srgrimes	case '~':
4254Srgrimes	    /* this may be followed by an override time value */
4264Srgrimes	    GETNUM(cp, timeval);
4274Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
4284Srgrimes		timeval = value;
4294Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
4304Srgrimes	    {
4314Srgrimes		slen--;
4324Srgrimes		sustain++;
4334Srgrimes	    }
4341016Sache	    playtone(-1, timeval, sustain);
4354Srgrimes	    break;
4364Srgrimes
4374Srgrimes	case 'T':
4384Srgrimes	    GETNUM(cp, tempo);
4394Srgrimes	    if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
4404Srgrimes		tempo = DFLT_TEMPO;
441766Sache	    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
4424Srgrimes	    break;
4434Srgrimes
4444Srgrimes	case 'M':
4454Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
4464Srgrimes	    {
4474Srgrimes		fill = NORMAL;
4484Srgrimes		++cp;
4494Srgrimes		slen--;
4504Srgrimes	    }
4514Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
4524Srgrimes	    {
4534Srgrimes		fill = LEGATO;
4544Srgrimes		++cp;
4554Srgrimes		slen--;
4564Srgrimes	    }
4574Srgrimes	    else if (cp[1] == 'S' || cp[1] == 's')
4584Srgrimes	    {
4594Srgrimes		fill = STACCATO;
4604Srgrimes		++cp;
4614Srgrimes		slen--;
4624Srgrimes	    }
4634Srgrimes	    break;
4644Srgrimes	}
4654Srgrimes    }
4664Srgrimes}
4674Srgrimes
4684Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
4694Srgrimes *
4704Srgrimes * This section implements driver hooks to run playstring() and the tone(),
4714Srgrimes * endtone(), and rest() functions defined above.
4724Srgrimes */
4734Srgrimes
474738Sachestatic int spkr_active = FALSE; /* exclusion flag */
475738Sachestatic struct buf *spkr_inbuf;  /* incoming buf */
4764Srgrimes
47717232Sjoergint
47817232Sjoergspkropen(dev, flags, fmt, p)
47917232Sjoerg	dev_t		dev;
48017232Sjoerg	int		flags;
48117232Sjoerg	int		fmt;
48217232Sjoerg	struct proc	*p;
4834Srgrimes{
4844Srgrimes#ifdef DEBUG
485738Sache    (void) printf("spkropen: entering with dev = %x\n", dev);
4864Srgrimes#endif /* DEBUG */
4874Srgrimes
4884Srgrimes    if (minor(dev) != 0)
4894Srgrimes	return(ENXIO);
4904Srgrimes    else if (spkr_active)
4914Srgrimes	return(EBUSY);
4924Srgrimes    else
4934Srgrimes    {
494738Sache#ifdef DEBUG
495738Sache	(void) printf("spkropen: about to perform play initialization\n");
496738Sache#endif /* DEBUG */
4974Srgrimes	playinit();
4984Srgrimes	spkr_inbuf = geteblk(DEV_BSIZE);
499738Sache	spkr_active = TRUE;
500738Sache	return(0);
5014Srgrimes    }
5024Srgrimes}
5034Srgrimes
50417232Sjoergint
50517232Sjoergspkrwrite(dev, uio, ioflag)
50617232Sjoerg	dev_t		dev;
50717232Sjoerg	struct uio	*uio;
50817232Sjoerg	int		ioflag;
5094Srgrimes{
5104Srgrimes#ifdef DEBUG
5114Srgrimes    printf("spkrwrite: entering with dev = %x, count = %d\n",
5124Srgrimes		dev, uio->uio_resid);
5134Srgrimes#endif /* DEBUG */
5144Srgrimes
5154Srgrimes    if (minor(dev) != 0)
5164Srgrimes	return(ENXIO);
517738Sache    else if (uio->uio_resid > DEV_BSIZE)     /* prevent system crashes */
518738Sache	return(E2BIG);
5194Srgrimes    else
5204Srgrimes    {
521738Sache	unsigned n;
522738Sache	char *cp;
523738Sache	int error;
524738Sache
525738Sache	n = uio->uio_resid;
5264Srgrimes	cp = spkr_inbuf->b_un.b_addr;
527738Sache	if (!(error = uiomove(cp, n, uio)))
5281016Sache		playstring(cp, n);
5294Srgrimes	return(error);
5304Srgrimes    }
5314Srgrimes}
5324Srgrimes
53317232Sjoergint
53417232Sjoergspkrclose(dev, flags, fmt, p)
53517232Sjoerg	dev_t		dev;
53617232Sjoerg	int		flags;
53717232Sjoerg	int		fmt;
53817232Sjoerg	struct proc	*p;
5394Srgrimes{
5404Srgrimes#ifdef DEBUG
541738Sache    (void) printf("spkrclose: entering with dev = %x\n", dev);
5424Srgrimes#endif /* DEBUG */
5434Srgrimes
5444Srgrimes    if (minor(dev) != 0)
5454Srgrimes	return(ENXIO);
5464Srgrimes    else
5474Srgrimes    {
548766Sache	wakeup((caddr_t)&endtone);
549766Sache	wakeup((caddr_t)&endrest);
5504Srgrimes	brelse(spkr_inbuf);
551738Sache	spkr_active = FALSE;
552738Sache	return(0);
5534Srgrimes    }
5544Srgrimes}
5554Srgrimes
55617232Sjoergint
55717232Sjoergspkrioctl(dev, cmd, cmdarg, flags, p)
55817232Sjoerg	dev_t		dev;
55917232Sjoerg	int		cmd;
56017232Sjoerg	caddr_t		cmdarg;
56117232Sjoerg	int		flags;
56217232Sjoerg	struct proc	*p;
5634Srgrimes{
5644Srgrimes#ifdef DEBUG
565738Sache    (void) printf("spkrioctl: entering with dev = %x, cmd = %x\n");
5664Srgrimes#endif /* DEBUG */
5674Srgrimes
5684Srgrimes    if (minor(dev) != 0)
5694Srgrimes	return(ENXIO);
5704Srgrimes    else if (cmd == SPKRTONE)
5714Srgrimes    {
5724Srgrimes	tone_t	*tp = (tone_t *)cmdarg;
5734Srgrimes
5744Srgrimes	if (tp->frequency == 0)
5751016Sache	    rest(tp->duration);
5764Srgrimes	else
5771016Sache	    tone(tp->frequency, tp->duration);
5781016Sache	return 0;
5794Srgrimes    }
5804Srgrimes    else if (cmd == SPKRTUNE)
5814Srgrimes    {
5824Srgrimes	tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
5834Srgrimes	tone_t ttp;
5844Srgrimes	int error;
5854Srgrimes
5864Srgrimes	for (; ; tp++) {
5874Srgrimes	    error = copyin(tp, &ttp, sizeof(tone_t));
5884Srgrimes	    if (error)
5894Srgrimes		    return(error);
5904Srgrimes	    if (ttp.duration == 0)
5914Srgrimes		    break;
5924Srgrimes	    if (ttp.frequency == 0)
5931016Sache		 rest(ttp.duration);
5944Srgrimes	    else
5951016Sache		 tone(ttp.frequency, ttp.duration);
5964Srgrimes	}
597738Sache	return(0);
5984Srgrimes    }
599738Sache    return(EINVAL);
6004Srgrimes}
6014Srgrimes
60212502Sjulian
60312502Sjulianstatic spkr_devsw_installed = 0;
60412502Sjulian
60517232Sjoergstatic void
60617232Sjoergspkr_drvinit(void *unused)
60712502Sjulian{
60812517Sjulian	dev_t dev;
60912517Sjulian
61012502Sjulian	if( ! spkr_devsw_installed ) {
61112675Sjulian		dev = makedev(CDEV_MAJOR, 0);
61212675Sjulian		cdevsw_add(&dev,&spkr_cdevsw, NULL);
61312502Sjulian		spkr_devsw_installed = 1;
61412517Sjulian#ifdef DEVFS
61514847Sbde		devfs_token = devfs_add_devswf(&spkr_cdevsw, 0, DV_CHR,
61614847Sbde					       UID_ROOT, GID_WHEEL, 0600,
61714847Sbde					       "speaker");
61812521Sjulian#endif
61912517Sjulian    	}
62012502Sjulian}
62112517Sjulian
62212517SjulianSYSINIT(spkrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,spkr_drvinit,NULL)
62312517Sjulian
62412517Sjulian
6254Srgrimes#endif  /* NSPEAKER > 0 */
6264Srgrimes/* spkr.c ends here */
627