spkr.c revision 177648
1139790Simp/*-
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>
6106323Smdodd * modified for PC98 by Kakefuda
74Srgrimes */
84Srgrimes
9115703Sobrien#include <sys/cdefs.h>
10115703Sobrien__FBSDID("$FreeBSD: head/sys/dev/speaker/spkr.c 177648 2008-03-26 21:33:41Z phk $");
11115703Sobrien
122056Swollman#include <sys/param.h>
132056Swollman#include <sys/systm.h>
142056Swollman#include <sys/kernel.h>
1561994Smsmith#include <sys/module.h>
162056Swollman#include <sys/uio.h>
1712675Sjulian#include <sys/conf.h>
1852843Sphk#include <sys/ctype.h>
1960038Sphk#include <sys/malloc.h>
207090Sbde#include <machine/clock.h>
21152306Sru#include <dev/speaker/speaker.h>
224Srgrimes
2312675Sjulianstatic	d_open_t	spkropen;
2412675Sjulianstatic	d_close_t	spkrclose;
2512675Sjulianstatic	d_write_t	spkrwrite;
2612675Sjulianstatic	d_ioctl_t	spkrioctl;
2712502Sjulian
2847625Sphkstatic struct cdevsw spkr_cdevsw = {
29126080Sphk	.d_version =	D_VERSION,
30126080Sphk	.d_flags =	D_NEEDGIANT,
31111815Sphk	.d_open =	spkropen,
32111815Sphk	.d_close =	spkrclose,
33111815Sphk	.d_write =	spkrwrite,
34111815Sphk	.d_ioctl =	spkrioctl,
35111815Sphk	.d_name =	"spkr",
3647625Sphk};
3712675Sjulian
3869774Sphkstatic MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer");
3960038Sphk
404Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE *************************
414Srgrimes *
424Srgrimes * This section defines a function tone() which causes a tone of given
4319174Sbde * frequency and duration from the ISA console speaker.
444Srgrimes * Another function endtone() is defined to force sound off, and there is
454Srgrimes * also a rest() entry point to do pauses.
464Srgrimes *
474Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and
4819174Sbde * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The
494Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is
504Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired.
514Srgrimes */
524Srgrimes
53766Sache#define SPKRPRI PSOCK
54766Sachestatic char endtone, endrest;
554Srgrimes
56170278Sbrianstatic void tone(unsigned int thz, unsigned int centisecs);
57170278Sbrianstatic void rest(int centisecs);
5892765Salfredstatic void playinit(void);
5992765Salfredstatic void playtone(int pitch, int value, int sustain);
6092765Salfredstatic void playstring(char *cp, size_t slen);
6112854Sbde
62170278Sbrian/* emit tone of frequency thz for given number of centisecs */
6317232Sjoergstatic void
64170278Sbriantone(thz, centisecs)
65170278Sbrian	unsigned int thz, centisecs;
664Srgrimes{
67170278Sbrian    int sps, timo;
684Srgrimes
698288Sdg    if (thz <= 0)
708288Sdg	return;
718288Sdg
724Srgrimes#ifdef DEBUG
73170278Sbrian    (void) printf("tone: thz=%d centisecs=%d\n", thz, centisecs);
744Srgrimes#endif /* DEBUG */
754Srgrimes
764Srgrimes    /* set timer to generate clicks at given frequency in Hertz */
7717232Sjoerg    sps = splclock();
781393Ssos
79146211Snyan    if (timer_spkr_acquire()) {
801393Ssos	/* enter list of waiting procs ??? */
8117232Sjoerg	splx(sps);
821393Ssos	return;
831393Ssos    }
8417232Sjoerg    splx(sps);
8517232Sjoerg    disable_intr();
86177642Sphk    timer_spkr_setfreq(thz);
8717232Sjoerg    enable_intr();
884Srgrimes
894Srgrimes    /*
904Srgrimes     * Set timeout to endtone function, then give up the timeslice.
914Srgrimes     * This is so other processes can execute while the tone is being
924Srgrimes     * emitted.
934Srgrimes     */
94170278Sbrian    timo = centisecs * hz / 100;
95170278Sbrian    if (timo > 0)
96170278Sbrian	tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", timo);
9717232Sjoerg    sps = splclock();
98146211Snyan    timer_spkr_release();
9917232Sjoerg    splx(sps);
1004Srgrimes}
1014Srgrimes
102170278Sbrian/* rest for given number of centisecs */
10317232Sjoergstatic void
104170278Sbrianrest(centisecs)
105170278Sbrian	int	centisecs;
1064Srgrimes{
107170278Sbrian    int timo;
108170278Sbrian
1094Srgrimes    /*
1104Srgrimes     * Set timeout to endrest function, then give up the timeslice.
1114Srgrimes     * This is so other processes can execute while the rest is being
1124Srgrimes     * waited out.
1134Srgrimes     */
1144Srgrimes#ifdef DEBUG
115170278Sbrian    (void) printf("rest: %d\n", centisecs);
1164Srgrimes#endif /* DEBUG */
117170278Sbrian    timo = centisecs * hz / 100;
118170278Sbrian    if (timo > 0)
119170278Sbrian	tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", timo);
1204Srgrimes}
1214Srgrimes
1224Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
1234Srgrimes *
1244Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
125738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
126738Sache * tracking facility are added.
1274Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible
1284Srgrimes * except possibly at physical block boundaries.
1294Srgrimes */
1304Srgrimes
1314Srgrimestypedef int	bool;
1324Srgrimes#define TRUE	1
1334Srgrimes#define FALSE	0
1344Srgrimes
1354Srgrimes#define dtoi(c)		((c) - '0')
1364Srgrimes
1374Srgrimesstatic int octave;	/* currently selected octave */
1384Srgrimesstatic int whole;	/* whole-note time at current tempo, in ticks */
1394Srgrimesstatic int value;	/* whole divisor for note time, quarter note = 1 */
1404Srgrimesstatic int fill;	/* controls spacing of notes */
1414Srgrimesstatic bool octtrack;	/* octave-tracking on? */
1424Srgrimesstatic bool octprefix;	/* override current octave-tracking state? */
1434Srgrimes
1444Srgrimes/*
1454Srgrimes * Magic number avoidance...
1464Srgrimes */
1474Srgrimes#define SECS_PER_MIN	60	/* seconds per minute */
1484Srgrimes#define WHOLE_NOTE	4	/* quarter notes per whole note */
1494Srgrimes#define MIN_VALUE	64	/* the most we can divide a note by */
1504Srgrimes#define DFLT_VALUE	4	/* default value (quarter-note) */
1514Srgrimes#define FILLTIME	8	/* for articulation, break note in parts */
1524Srgrimes#define STACCATO	6	/* 6/8 = 3/4 of note is filled */
1534Srgrimes#define NORMAL		7	/* 7/8ths of note interval is filled */
1544Srgrimes#define LEGATO		8	/* all of note interval is filled */
1554Srgrimes#define DFLT_OCTAVE	4	/* default octave */
1564Srgrimes#define MIN_TEMPO	32	/* minimum tempo */
1574Srgrimes#define DFLT_TEMPO	120	/* default tempo */
1584Srgrimes#define MAX_TEMPO	255	/* max tempo */
1594Srgrimes#define NUM_MULT	3	/* numerator of dot multiplier */
1604Srgrimes#define DENOM_MULT	2	/* denominator of dot multiplier */
1614Srgrimes
1624Srgrimes/* letter to half-tone:  A   B  C  D  E  F  G */
1634Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
1644Srgrimes
1654Srgrimes/*
1664Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies
1674Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
1684Srgrimes * our octave 0 is standard octave 2.
1694Srgrimes */
1704Srgrimes#define OCTAVE_NOTES	12	/* semitones per octave */
1714Srgrimesstatic int pitchtab[] =
1724Srgrimes{
1734Srgrimes/*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
1744Srgrimes/* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
1754Srgrimes/* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
1764Srgrimes/* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
1774Srgrimes/* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
1784Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
1794Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
1804Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
1814Srgrimes};
1824Srgrimes
18317232Sjoergstatic void
18417232Sjoergplayinit()
1854Srgrimes{
1864Srgrimes    octave = DFLT_OCTAVE;
187170280Sbrian    whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
1884Srgrimes    fill = NORMAL;
1894Srgrimes    value = DFLT_VALUE;
1904Srgrimes    octtrack = FALSE;
1914Srgrimes    octprefix = TRUE;	/* act as though there was an initial O(n) */
1924Srgrimes}
1934Srgrimes
1944Srgrimes/* play tone of proper duration for current rhythm signature */
19517232Sjoergstatic void
19617232Sjoergplaytone(pitch, value, sustain)
19717232Sjoerg	int	pitch, value, sustain;
1984Srgrimes{
1994Srgrimes    register int	sound, silence, snum = 1, sdenom = 1;
2004Srgrimes
2014Srgrimes    /* this weirdness avoids floating-point arithmetic */
2024Srgrimes    for (; sustain; sustain--)
2034Srgrimes    {
204738Sache	/* See the BUGS section in the man page for discussion */
2054Srgrimes	snum *= NUM_MULT;
2064Srgrimes	sdenom *= DENOM_MULT;
2074Srgrimes    }
2084Srgrimes
2098288Sdg    if (value == 0 || sdenom == 0)
2108288Sdg	return;
2118288Sdg
2124Srgrimes    if (pitch == -1)
2131016Sache	rest(whole * snum / (value * sdenom));
2144Srgrimes    else
2154Srgrimes    {
2164Srgrimes	sound = (whole * snum) / (value * sdenom)
2174Srgrimes		- (whole * (FILLTIME - fill)) / (value * FILLTIME);
2184Srgrimes	silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
2194Srgrimes
2204Srgrimes#ifdef DEBUG
221738Sache	(void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
2224Srgrimes			pitch, sound, silence);
2234Srgrimes#endif /* DEBUG */
2244Srgrimes
2251016Sache	tone(pitchtab[pitch], sound);
2264Srgrimes	if (fill != LEGATO)
2271016Sache	    rest(silence);
2284Srgrimes    }
2294Srgrimes}
2304Srgrimes
2314Srgrimes/* interpret and play an item from a notation string */
23217232Sjoergstatic void
23317232Sjoergplaystring(cp, slen)
23417232Sjoerg	char	*cp;
23517232Sjoerg	size_t	slen;
2364Srgrimes{
237738Sache    int		pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
2384Srgrimes
2394Srgrimes#define GETNUM(cp, v)	for(v=0; isdigit(cp[1]) && slen > 0; ) \
2404Srgrimes				{v = v * 10 + (*++cp - '0'); slen--;}
2414Srgrimes    for (; slen--; cp++)
2424Srgrimes    {
2434Srgrimes	int		sustain, timeval, tempo;
2444Srgrimes	register char	c = toupper(*cp);
2454Srgrimes
2464Srgrimes#ifdef DEBUG
247738Sache	(void) printf("playstring: %c (%x)\n", c, c);
2484Srgrimes#endif /* DEBUG */
2494Srgrimes
2504Srgrimes	switch (c)
2514Srgrimes	{
2524Srgrimes	case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2534Srgrimes
2544Srgrimes	    /* compute pitch */
2554Srgrimes	    pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
2564Srgrimes
2574Srgrimes	    /* this may be followed by an accidental sign */
2584Srgrimes	    if (cp[1] == '#' || cp[1] == '+')
2594Srgrimes	    {
2604Srgrimes		++pitch;
2614Srgrimes		++cp;
2624Srgrimes		slen--;
2634Srgrimes	    }
2644Srgrimes	    else if (cp[1] == '-')
2654Srgrimes	    {
2664Srgrimes		--pitch;
2674Srgrimes		++cp;
2684Srgrimes		slen--;
2694Srgrimes	    }
2704Srgrimes
2714Srgrimes	    /*
2724Srgrimes	     * If octave-tracking mode is on, and there has been no octave-
2734Srgrimes	     * setting prefix, find the version of the current letter note
2744Srgrimes	     * closest to the last regardless of octave.
2754Srgrimes	     */
2764Srgrimes	    if (octtrack && !octprefix)
2774Srgrimes	    {
2784Srgrimes		if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
2794Srgrimes		{
2804Srgrimes		    ++octave;
2814Srgrimes		    pitch += OCTAVE_NOTES;
2824Srgrimes		}
2834Srgrimes
2844Srgrimes		if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
2854Srgrimes		{
2864Srgrimes		    --octave;
2874Srgrimes		    pitch -= OCTAVE_NOTES;
2884Srgrimes		}
2894Srgrimes	    }
2904Srgrimes	    octprefix = FALSE;
2914Srgrimes	    lastpitch = pitch;
2924Srgrimes
2934Srgrimes	    /* ...which may in turn be followed by an override time value */
2944Srgrimes	    GETNUM(cp, timeval);
2954Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
2964Srgrimes		timeval = value;
2974Srgrimes
2984Srgrimes	    /* ...and/or sustain dots */
2994Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3004Srgrimes	    {
3014Srgrimes		slen--;
3024Srgrimes		sustain++;
3034Srgrimes	    }
3044Srgrimes
305738Sache	    /* ...and/or a slur mark */
306738Sache	    oldfill = fill;
307738Sache	    if (cp[1] == '_')
308738Sache	    {
309738Sache		fill = LEGATO;
310738Sache		++cp;
311738Sache		slen--;
312738Sache	    }
313738Sache
3144Srgrimes	    /* time to emit the actual tone */
3151016Sache	    playtone(pitch, timeval, sustain);
316738Sache
317738Sache	    fill = oldfill;
3184Srgrimes	    break;
3194Srgrimes
3204Srgrimes	case 'O':
3214Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
3224Srgrimes	    {
3234Srgrimes		octprefix = octtrack = FALSE;
3244Srgrimes		++cp;
3254Srgrimes		slen--;
3264Srgrimes	    }
3274Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
3284Srgrimes	    {
3294Srgrimes		octtrack = TRUE;
3304Srgrimes		++cp;
3314Srgrimes		slen--;
3324Srgrimes	    }
3334Srgrimes	    else
3344Srgrimes	    {
3354Srgrimes		GETNUM(cp, octave);
3363593Sache		if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
3374Srgrimes		    octave = DFLT_OCTAVE;
3384Srgrimes		octprefix = TRUE;
3394Srgrimes	    }
3404Srgrimes	    break;
3414Srgrimes
3424Srgrimes	case '>':
3433593Sache	    if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
3444Srgrimes		octave++;
3454Srgrimes	    octprefix = TRUE;
3464Srgrimes	    break;
3474Srgrimes
3484Srgrimes	case '<':
3494Srgrimes	    if (octave > 0)
3504Srgrimes		octave--;
3514Srgrimes	    octprefix = TRUE;
3524Srgrimes	    break;
3534Srgrimes
3544Srgrimes	case 'N':
3554Srgrimes	    GETNUM(cp, pitch);
3564Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3574Srgrimes	    {
3584Srgrimes		slen--;
3594Srgrimes		sustain++;
3604Srgrimes	    }
361738Sache	    oldfill = fill;
362738Sache	    if (cp[1] == '_')
363738Sache	    {
364738Sache		fill = LEGATO;
365738Sache		++cp;
366738Sache		slen--;
367738Sache	    }
3681016Sache	    playtone(pitch - 1, value, sustain);
369738Sache	    fill = oldfill;
3704Srgrimes	    break;
3714Srgrimes
3724Srgrimes	case 'L':
3734Srgrimes	    GETNUM(cp, value);
3744Srgrimes	    if (value <= 0 || value > MIN_VALUE)
3754Srgrimes		value = DFLT_VALUE;
3764Srgrimes	    break;
3774Srgrimes
3784Srgrimes	case 'P':
3794Srgrimes	case '~':
3804Srgrimes	    /* this may be followed by an override time value */
3814Srgrimes	    GETNUM(cp, timeval);
3824Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
3834Srgrimes		timeval = value;
3844Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3854Srgrimes	    {
3864Srgrimes		slen--;
3874Srgrimes		sustain++;
3884Srgrimes	    }
3891016Sache	    playtone(-1, timeval, sustain);
3904Srgrimes	    break;
3914Srgrimes
3924Srgrimes	case 'T':
3934Srgrimes	    GETNUM(cp, tempo);
3944Srgrimes	    if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
3954Srgrimes		tempo = DFLT_TEMPO;
396170280Sbrian	    whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / tempo;
3974Srgrimes	    break;
3984Srgrimes
3994Srgrimes	case 'M':
4004Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
4014Srgrimes	    {
4024Srgrimes		fill = NORMAL;
4034Srgrimes		++cp;
4044Srgrimes		slen--;
4054Srgrimes	    }
4064Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
4074Srgrimes	    {
4084Srgrimes		fill = LEGATO;
4094Srgrimes		++cp;
4104Srgrimes		slen--;
4114Srgrimes	    }
4124Srgrimes	    else if (cp[1] == 'S' || cp[1] == 's')
4134Srgrimes	    {
4144Srgrimes		fill = STACCATO;
4154Srgrimes		++cp;
4164Srgrimes		slen--;
4174Srgrimes	    }
4184Srgrimes	    break;
4194Srgrimes	}
4204Srgrimes    }
4214Srgrimes}
4224Srgrimes
4234Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
4244Srgrimes *
4254Srgrimes * This section implements driver hooks to run playstring() and the tone(),
4264Srgrimes * endtone(), and rest() functions defined above.
4274Srgrimes */
4284Srgrimes
429738Sachestatic int spkr_active = FALSE; /* exclusion flag */
43060038Sphkstatic char *spkr_inbuf;  /* incoming buf */
4314Srgrimes
432105224Sphkstatic int
43383366Sjulianspkropen(dev, flags, fmt, td)
434130585Sphk	struct cdev *dev;
43517232Sjoerg	int		flags;
43617232Sjoerg	int		fmt;
43783366Sjulian	struct thread	*td;
4384Srgrimes{
4394Srgrimes#ifdef DEBUG
44049982Sbillf    (void) printf("spkropen: entering with dev = %s\n", devtoname(dev));
4414Srgrimes#endif /* DEBUG */
4424Srgrimes
4434Srgrimes    if (minor(dev) != 0)
4444Srgrimes	return(ENXIO);
4454Srgrimes    else if (spkr_active)
4464Srgrimes	return(EBUSY);
4474Srgrimes    else
4484Srgrimes    {
449738Sache#ifdef DEBUG
450738Sache	(void) printf("spkropen: about to perform play initialization\n");
451738Sache#endif /* DEBUG */
4524Srgrimes	playinit();
453111119Simp	spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK);
454738Sache	spkr_active = TRUE;
455738Sache	return(0);
4564Srgrimes    }
4574Srgrimes}
4584Srgrimes
459105224Sphkstatic int
46017232Sjoergspkrwrite(dev, uio, ioflag)
461130585Sphk	struct cdev *dev;
46217232Sjoerg	struct uio	*uio;
46317232Sjoerg	int		ioflag;
4644Srgrimes{
4654Srgrimes#ifdef DEBUG
46649982Sbillf    printf("spkrwrite: entering with dev = %s, count = %d\n",
46749982Sbillf		devtoname(dev), uio->uio_resid);
4684Srgrimes#endif /* DEBUG */
4694Srgrimes
4704Srgrimes    if (minor(dev) != 0)
4714Srgrimes	return(ENXIO);
47217803Speter    else if (uio->uio_resid > (DEV_BSIZE - 1))     /* prevent system crashes */
473738Sache	return(E2BIG);
4744Srgrimes    else
4754Srgrimes    {
476738Sache	unsigned n;
477738Sache	char *cp;
478738Sache	int error;
479738Sache
480738Sache	n = uio->uio_resid;
48160038Sphk	cp = spkr_inbuf;
48217803Speter	error = uiomove(cp, n, uio);
48317803Speter	if (!error) {
48417803Speter		cp[n] = '\0';
4851016Sache		playstring(cp, n);
48617803Speter	}
4874Srgrimes	return(error);
4884Srgrimes    }
4894Srgrimes}
4904Srgrimes
491105224Sphkstatic int
49283366Sjulianspkrclose(dev, flags, fmt, td)
493130585Sphk	struct cdev *dev;
49417232Sjoerg	int		flags;
49517232Sjoerg	int		fmt;
49683366Sjulian	struct thread	*td;
4974Srgrimes{
4984Srgrimes#ifdef DEBUG
49949982Sbillf    (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev));
5004Srgrimes#endif /* DEBUG */
5014Srgrimes
5024Srgrimes    if (minor(dev) != 0)
5034Srgrimes	return(ENXIO);
5044Srgrimes    else
5054Srgrimes    {
506111748Sdes	wakeup(&endtone);
507111748Sdes	wakeup(&endrest);
50860038Sphk	free(spkr_inbuf, M_SPKR);
509738Sache	spkr_active = FALSE;
510738Sache	return(0);
5114Srgrimes    }
5124Srgrimes}
5134Srgrimes
514105224Sphkstatic int
51583366Sjulianspkrioctl(dev, cmd, cmdarg, flags, td)
516130585Sphk	struct cdev *dev;
51738505Sbde	unsigned long	cmd;
51817232Sjoerg	caddr_t		cmdarg;
51917232Sjoerg	int		flags;
52083366Sjulian	struct thread	*td;
5214Srgrimes{
5224Srgrimes#ifdef DEBUG
52350253Sbde    (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n",
52450253Sbde    	devtoname(dev), cmd);
5254Srgrimes#endif /* DEBUG */
5264Srgrimes
5274Srgrimes    if (minor(dev) != 0)
5284Srgrimes	return(ENXIO);
5294Srgrimes    else if (cmd == SPKRTONE)
5304Srgrimes    {
5314Srgrimes	tone_t	*tp = (tone_t *)cmdarg;
5324Srgrimes
5334Srgrimes	if (tp->frequency == 0)
5341016Sache	    rest(tp->duration);
5354Srgrimes	else
5361016Sache	    tone(tp->frequency, tp->duration);
5371016Sache	return 0;
5384Srgrimes    }
5394Srgrimes    else if (cmd == SPKRTUNE)
5404Srgrimes    {
5414Srgrimes	tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
5424Srgrimes	tone_t ttp;
5434Srgrimes	int error;
5444Srgrimes
5454Srgrimes	for (; ; tp++) {
5464Srgrimes	    error = copyin(tp, &ttp, sizeof(tone_t));
5474Srgrimes	    if (error)
5484Srgrimes		    return(error);
5494Srgrimes	    if (ttp.duration == 0)
5504Srgrimes		    break;
5514Srgrimes	    if (ttp.frequency == 0)
5521016Sache		 rest(ttp.duration);
5534Srgrimes	    else
5541016Sache		 tone(ttp.frequency, ttp.duration);
5554Srgrimes	}
556738Sache	return(0);
5574Srgrimes    }
558738Sache    return(EINVAL);
5594Srgrimes}
5604Srgrimes
561177648Sphkstatic struct cdev *speaker_dev;
562177648Sphk
56361994Smsmith/*
564177648Sphk * Module handling
56561994Smsmith */
56661994Smsmithstatic int
567177648Sphkspeaker_modevent(module_t mod, int type, void *data)
56861994Smsmith{
569177648Sphk	int error = 0;
570106070Smdodd
571177648Sphk	switch(type) {
572177648Sphk	case MOD_LOAD:
573177648Sphk		speaker_dev = make_dev(&spkr_cdevsw, 0,
574177648Sphk		    UID_ROOT, GID_WHEEL, 0600, "speaker");
575177648Sphk		break;
576177648Sphk	case MOD_SHUTDOWN:
577177648Sphk	case MOD_UNLOAD:
578177648Sphk		destroy_dev(speaker_dev);
579177648Sphk		break;
580177648Sphk	default:
581177648Sphk		error = EOPNOTSUPP;
582106070Smdodd	}
583177648Sphk	return (error);
58461994Smsmith}
58561994Smsmith
586177648SphkDEV_MODULE(speaker, speaker_modevent, NULL);
587