spkr.c revision 170280
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 170280 2007-06-04 09:27:13Z brian $");
11115703Sobrien
122056Swollman#include <sys/param.h>
132056Swollman#include <sys/systm.h>
1461994Smsmith#include <sys/bus.h>
152056Swollman#include <sys/kernel.h>
1661994Smsmith#include <sys/module.h>
172056Swollman#include <sys/uio.h>
1812675Sjulian#include <sys/conf.h>
1952843Sphk#include <sys/ctype.h>
2060038Sphk#include <sys/malloc.h>
2161994Smsmith#include <isa/isavar.h>
227090Sbde#include <machine/clock.h>
23146211Snyan#include <machine/ppireg.h>
24146211Snyan#include <machine/timerreg.h>
25152306Sru#include <dev/speaker/speaker.h>
264Srgrimes
2712675Sjulianstatic	d_open_t	spkropen;
2812675Sjulianstatic	d_close_t	spkrclose;
2912675Sjulianstatic	d_write_t	spkrwrite;
3012675Sjulianstatic	d_ioctl_t	spkrioctl;
3112502Sjulian
3247625Sphkstatic struct cdevsw spkr_cdevsw = {
33126080Sphk	.d_version =	D_VERSION,
34126080Sphk	.d_flags =	D_NEEDGIANT,
35111815Sphk	.d_open =	spkropen,
36111815Sphk	.d_close =	spkrclose,
37111815Sphk	.d_write =	spkrwrite,
38111815Sphk	.d_ioctl =	spkrioctl,
39111815Sphk	.d_name =	"spkr",
4047625Sphk};
4112675Sjulian
4269774Sphkstatic MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer");
4360038Sphk
444Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE *************************
454Srgrimes *
464Srgrimes * This section defines a function tone() which causes a tone of given
4719174Sbde * frequency and duration from the ISA console speaker.
484Srgrimes * Another function endtone() is defined to force sound off, and there is
494Srgrimes * also a rest() entry point to do pauses.
504Srgrimes *
514Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and
5219174Sbde * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The
534Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is
544Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired.
554Srgrimes */
564Srgrimes
57106323Smdodd#ifdef PC98
58112561Smdodd#define	SPKR_DESC	"PC98 speaker"
59106323Smdodd#else
60112561Smdodd#define	SPKR_DESC	"PC speaker"
61106323Smdodd#endif
62106323Smdodd
63766Sache#define SPKRPRI PSOCK
64766Sachestatic char endtone, endrest;
654Srgrimes
66170278Sbrianstatic void tone(unsigned int thz, unsigned int centisecs);
67170278Sbrianstatic void rest(int centisecs);
6892765Salfredstatic void playinit(void);
6992765Salfredstatic void playtone(int pitch, int value, int sustain);
7092765Salfredstatic void playstring(char *cp, size_t slen);
7112854Sbde
72170278Sbrian/* emit tone of frequency thz for given number of centisecs */
7317232Sjoergstatic void
74170278Sbriantone(thz, centisecs)
75170278Sbrian	unsigned int thz, centisecs;
764Srgrimes{
778288Sdg    unsigned int divisor;
78170278Sbrian    int sps, timo;
794Srgrimes
808288Sdg    if (thz <= 0)
818288Sdg	return;
828288Sdg
8319174Sbde    divisor = timer_freq / thz;
848288Sdg
854Srgrimes#ifdef DEBUG
86170278Sbrian    (void) printf("tone: thz=%d centisecs=%d\n", thz, centisecs);
874Srgrimes#endif /* DEBUG */
884Srgrimes
894Srgrimes    /* set timer to generate clicks at given frequency in Hertz */
9017232Sjoerg    sps = splclock();
911393Ssos
92146211Snyan    if (timer_spkr_acquire()) {
931393Ssos	/* enter list of waiting procs ??? */
9417232Sjoerg	splx(sps);
951393Ssos	return;
961393Ssos    }
9717232Sjoerg    splx(sps);
9817232Sjoerg    disable_intr();
99146211Snyan    spkr_set_pitch(divisor);
10017232Sjoerg    enable_intr();
1014Srgrimes
1024Srgrimes    /* turn the speaker on */
103146211Snyan    ppi_spkr_on();
1044Srgrimes
1054Srgrimes    /*
1064Srgrimes     * Set timeout to endtone function, then give up the timeslice.
1074Srgrimes     * This is so other processes can execute while the tone is being
1084Srgrimes     * emitted.
1094Srgrimes     */
110170278Sbrian    timo = centisecs * hz / 100;
111170278Sbrian    if (timo > 0)
112170278Sbrian	tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", timo);
113146211Snyan    ppi_spkr_off();
11417232Sjoerg    sps = splclock();
115146211Snyan    timer_spkr_release();
11617232Sjoerg    splx(sps);
1174Srgrimes}
1184Srgrimes
119170278Sbrian/* rest for given number of centisecs */
12017232Sjoergstatic void
121170278Sbrianrest(centisecs)
122170278Sbrian	int	centisecs;
1234Srgrimes{
124170278Sbrian    int timo;
125170278Sbrian
1264Srgrimes    /*
1274Srgrimes     * Set timeout to endrest function, then give up the timeslice.
1284Srgrimes     * This is so other processes can execute while the rest is being
1294Srgrimes     * waited out.
1304Srgrimes     */
1314Srgrimes#ifdef DEBUG
132170278Sbrian    (void) printf("rest: %d\n", centisecs);
1334Srgrimes#endif /* DEBUG */
134170278Sbrian    timo = centisecs * hz / 100;
135170278Sbrian    if (timo > 0)
136170278Sbrian	tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", timo);
1374Srgrimes}
1384Srgrimes
1394Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
1404Srgrimes *
1414Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
142738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
143738Sache * tracking facility are added.
1444Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible
1454Srgrimes * except possibly at physical block boundaries.
1464Srgrimes */
1474Srgrimes
1484Srgrimestypedef int	bool;
1494Srgrimes#define TRUE	1
1504Srgrimes#define FALSE	0
1514Srgrimes
1524Srgrimes#define dtoi(c)		((c) - '0')
1534Srgrimes
1544Srgrimesstatic int octave;	/* currently selected octave */
1554Srgrimesstatic int whole;	/* whole-note time at current tempo, in ticks */
1564Srgrimesstatic int value;	/* whole divisor for note time, quarter note = 1 */
1574Srgrimesstatic int fill;	/* controls spacing of notes */
1584Srgrimesstatic bool octtrack;	/* octave-tracking on? */
1594Srgrimesstatic bool octprefix;	/* override current octave-tracking state? */
1604Srgrimes
1614Srgrimes/*
1624Srgrimes * Magic number avoidance...
1634Srgrimes */
1644Srgrimes#define SECS_PER_MIN	60	/* seconds per minute */
1654Srgrimes#define WHOLE_NOTE	4	/* quarter notes per whole note */
1664Srgrimes#define MIN_VALUE	64	/* the most we can divide a note by */
1674Srgrimes#define DFLT_VALUE	4	/* default value (quarter-note) */
1684Srgrimes#define FILLTIME	8	/* for articulation, break note in parts */
1694Srgrimes#define STACCATO	6	/* 6/8 = 3/4 of note is filled */
1704Srgrimes#define NORMAL		7	/* 7/8ths of note interval is filled */
1714Srgrimes#define LEGATO		8	/* all of note interval is filled */
1724Srgrimes#define DFLT_OCTAVE	4	/* default octave */
1734Srgrimes#define MIN_TEMPO	32	/* minimum tempo */
1744Srgrimes#define DFLT_TEMPO	120	/* default tempo */
1754Srgrimes#define MAX_TEMPO	255	/* max tempo */
1764Srgrimes#define NUM_MULT	3	/* numerator of dot multiplier */
1774Srgrimes#define DENOM_MULT	2	/* denominator of dot multiplier */
1784Srgrimes
1794Srgrimes/* letter to half-tone:  A   B  C  D  E  F  G */
1804Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
1814Srgrimes
1824Srgrimes/*
1834Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies
1844Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
1854Srgrimes * our octave 0 is standard octave 2.
1864Srgrimes */
1874Srgrimes#define OCTAVE_NOTES	12	/* semitones per octave */
1884Srgrimesstatic int pitchtab[] =
1894Srgrimes{
1904Srgrimes/*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
1914Srgrimes/* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
1924Srgrimes/* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
1934Srgrimes/* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
1944Srgrimes/* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
1954Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
1964Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
1974Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
1984Srgrimes};
1994Srgrimes
20017232Sjoergstatic void
20117232Sjoergplayinit()
2024Srgrimes{
2034Srgrimes    octave = DFLT_OCTAVE;
204170280Sbrian    whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
2054Srgrimes    fill = NORMAL;
2064Srgrimes    value = DFLT_VALUE;
2074Srgrimes    octtrack = FALSE;
2084Srgrimes    octprefix = TRUE;	/* act as though there was an initial O(n) */
2094Srgrimes}
2104Srgrimes
2114Srgrimes/* play tone of proper duration for current rhythm signature */
21217232Sjoergstatic void
21317232Sjoergplaytone(pitch, value, sustain)
21417232Sjoerg	int	pitch, value, sustain;
2154Srgrimes{
2164Srgrimes    register int	sound, silence, snum = 1, sdenom = 1;
2174Srgrimes
2184Srgrimes    /* this weirdness avoids floating-point arithmetic */
2194Srgrimes    for (; sustain; sustain--)
2204Srgrimes    {
221738Sache	/* See the BUGS section in the man page for discussion */
2224Srgrimes	snum *= NUM_MULT;
2234Srgrimes	sdenom *= DENOM_MULT;
2244Srgrimes    }
2254Srgrimes
2268288Sdg    if (value == 0 || sdenom == 0)
2278288Sdg	return;
2288288Sdg
2294Srgrimes    if (pitch == -1)
2301016Sache	rest(whole * snum / (value * sdenom));
2314Srgrimes    else
2324Srgrimes    {
2334Srgrimes	sound = (whole * snum) / (value * sdenom)
2344Srgrimes		- (whole * (FILLTIME - fill)) / (value * FILLTIME);
2354Srgrimes	silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
2364Srgrimes
2374Srgrimes#ifdef DEBUG
238738Sache	(void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
2394Srgrimes			pitch, sound, silence);
2404Srgrimes#endif /* DEBUG */
2414Srgrimes
2421016Sache	tone(pitchtab[pitch], sound);
2434Srgrimes	if (fill != LEGATO)
2441016Sache	    rest(silence);
2454Srgrimes    }
2464Srgrimes}
2474Srgrimes
2484Srgrimes/* interpret and play an item from a notation string */
24917232Sjoergstatic void
25017232Sjoergplaystring(cp, slen)
25117232Sjoerg	char	*cp;
25217232Sjoerg	size_t	slen;
2534Srgrimes{
254738Sache    int		pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
2554Srgrimes
2564Srgrimes#define GETNUM(cp, v)	for(v=0; isdigit(cp[1]) && slen > 0; ) \
2574Srgrimes				{v = v * 10 + (*++cp - '0'); slen--;}
2584Srgrimes    for (; slen--; cp++)
2594Srgrimes    {
2604Srgrimes	int		sustain, timeval, tempo;
2614Srgrimes	register char	c = toupper(*cp);
2624Srgrimes
2634Srgrimes#ifdef DEBUG
264738Sache	(void) printf("playstring: %c (%x)\n", c, c);
2654Srgrimes#endif /* DEBUG */
2664Srgrimes
2674Srgrimes	switch (c)
2684Srgrimes	{
2694Srgrimes	case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2704Srgrimes
2714Srgrimes	    /* compute pitch */
2724Srgrimes	    pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
2734Srgrimes
2744Srgrimes	    /* this may be followed by an accidental sign */
2754Srgrimes	    if (cp[1] == '#' || cp[1] == '+')
2764Srgrimes	    {
2774Srgrimes		++pitch;
2784Srgrimes		++cp;
2794Srgrimes		slen--;
2804Srgrimes	    }
2814Srgrimes	    else if (cp[1] == '-')
2824Srgrimes	    {
2834Srgrimes		--pitch;
2844Srgrimes		++cp;
2854Srgrimes		slen--;
2864Srgrimes	    }
2874Srgrimes
2884Srgrimes	    /*
2894Srgrimes	     * If octave-tracking mode is on, and there has been no octave-
2904Srgrimes	     * setting prefix, find the version of the current letter note
2914Srgrimes	     * closest to the last regardless of octave.
2924Srgrimes	     */
2934Srgrimes	    if (octtrack && !octprefix)
2944Srgrimes	    {
2954Srgrimes		if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
2964Srgrimes		{
2974Srgrimes		    ++octave;
2984Srgrimes		    pitch += OCTAVE_NOTES;
2994Srgrimes		}
3004Srgrimes
3014Srgrimes		if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
3024Srgrimes		{
3034Srgrimes		    --octave;
3044Srgrimes		    pitch -= OCTAVE_NOTES;
3054Srgrimes		}
3064Srgrimes	    }
3074Srgrimes	    octprefix = FALSE;
3084Srgrimes	    lastpitch = pitch;
3094Srgrimes
3104Srgrimes	    /* ...which may in turn be followed by an override time value */
3114Srgrimes	    GETNUM(cp, timeval);
3124Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
3134Srgrimes		timeval = value;
3144Srgrimes
3154Srgrimes	    /* ...and/or sustain dots */
3164Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3174Srgrimes	    {
3184Srgrimes		slen--;
3194Srgrimes		sustain++;
3204Srgrimes	    }
3214Srgrimes
322738Sache	    /* ...and/or a slur mark */
323738Sache	    oldfill = fill;
324738Sache	    if (cp[1] == '_')
325738Sache	    {
326738Sache		fill = LEGATO;
327738Sache		++cp;
328738Sache		slen--;
329738Sache	    }
330738Sache
3314Srgrimes	    /* time to emit the actual tone */
3321016Sache	    playtone(pitch, timeval, sustain);
333738Sache
334738Sache	    fill = oldfill;
3354Srgrimes	    break;
3364Srgrimes
3374Srgrimes	case 'O':
3384Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
3394Srgrimes	    {
3404Srgrimes		octprefix = octtrack = FALSE;
3414Srgrimes		++cp;
3424Srgrimes		slen--;
3434Srgrimes	    }
3444Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
3454Srgrimes	    {
3464Srgrimes		octtrack = TRUE;
3474Srgrimes		++cp;
3484Srgrimes		slen--;
3494Srgrimes	    }
3504Srgrimes	    else
3514Srgrimes	    {
3524Srgrimes		GETNUM(cp, octave);
3533593Sache		if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
3544Srgrimes		    octave = DFLT_OCTAVE;
3554Srgrimes		octprefix = TRUE;
3564Srgrimes	    }
3574Srgrimes	    break;
3584Srgrimes
3594Srgrimes	case '>':
3603593Sache	    if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
3614Srgrimes		octave++;
3624Srgrimes	    octprefix = TRUE;
3634Srgrimes	    break;
3644Srgrimes
3654Srgrimes	case '<':
3664Srgrimes	    if (octave > 0)
3674Srgrimes		octave--;
3684Srgrimes	    octprefix = TRUE;
3694Srgrimes	    break;
3704Srgrimes
3714Srgrimes	case 'N':
3724Srgrimes	    GETNUM(cp, pitch);
3734Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3744Srgrimes	    {
3754Srgrimes		slen--;
3764Srgrimes		sustain++;
3774Srgrimes	    }
378738Sache	    oldfill = fill;
379738Sache	    if (cp[1] == '_')
380738Sache	    {
381738Sache		fill = LEGATO;
382738Sache		++cp;
383738Sache		slen--;
384738Sache	    }
3851016Sache	    playtone(pitch - 1, value, sustain);
386738Sache	    fill = oldfill;
3874Srgrimes	    break;
3884Srgrimes
3894Srgrimes	case 'L':
3904Srgrimes	    GETNUM(cp, value);
3914Srgrimes	    if (value <= 0 || value > MIN_VALUE)
3924Srgrimes		value = DFLT_VALUE;
3934Srgrimes	    break;
3944Srgrimes
3954Srgrimes	case 'P':
3964Srgrimes	case '~':
3974Srgrimes	    /* this may be followed by an override time value */
3984Srgrimes	    GETNUM(cp, timeval);
3994Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
4004Srgrimes		timeval = value;
4014Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
4024Srgrimes	    {
4034Srgrimes		slen--;
4044Srgrimes		sustain++;
4054Srgrimes	    }
4061016Sache	    playtone(-1, timeval, sustain);
4074Srgrimes	    break;
4084Srgrimes
4094Srgrimes	case 'T':
4104Srgrimes	    GETNUM(cp, tempo);
4114Srgrimes	    if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
4124Srgrimes		tempo = DFLT_TEMPO;
413170280Sbrian	    whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / tempo;
4144Srgrimes	    break;
4154Srgrimes
4164Srgrimes	case 'M':
4174Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
4184Srgrimes	    {
4194Srgrimes		fill = NORMAL;
4204Srgrimes		++cp;
4214Srgrimes		slen--;
4224Srgrimes	    }
4234Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
4244Srgrimes	    {
4254Srgrimes		fill = LEGATO;
4264Srgrimes		++cp;
4274Srgrimes		slen--;
4284Srgrimes	    }
4294Srgrimes	    else if (cp[1] == 'S' || cp[1] == 's')
4304Srgrimes	    {
4314Srgrimes		fill = STACCATO;
4324Srgrimes		++cp;
4334Srgrimes		slen--;
4344Srgrimes	    }
4354Srgrimes	    break;
4364Srgrimes	}
4374Srgrimes    }
4384Srgrimes}
4394Srgrimes
4404Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
4414Srgrimes *
4424Srgrimes * This section implements driver hooks to run playstring() and the tone(),
4434Srgrimes * endtone(), and rest() functions defined above.
4444Srgrimes */
4454Srgrimes
446738Sachestatic int spkr_active = FALSE; /* exclusion flag */
44760038Sphkstatic char *spkr_inbuf;  /* incoming buf */
4484Srgrimes
449105224Sphkstatic int
45083366Sjulianspkropen(dev, flags, fmt, td)
451130585Sphk	struct cdev *dev;
45217232Sjoerg	int		flags;
45317232Sjoerg	int		fmt;
45483366Sjulian	struct thread	*td;
4554Srgrimes{
4564Srgrimes#ifdef DEBUG
45749982Sbillf    (void) printf("spkropen: entering with dev = %s\n", devtoname(dev));
4584Srgrimes#endif /* DEBUG */
4594Srgrimes
4604Srgrimes    if (minor(dev) != 0)
4614Srgrimes	return(ENXIO);
4624Srgrimes    else if (spkr_active)
4634Srgrimes	return(EBUSY);
4644Srgrimes    else
4654Srgrimes    {
466738Sache#ifdef DEBUG
467738Sache	(void) printf("spkropen: about to perform play initialization\n");
468738Sache#endif /* DEBUG */
4694Srgrimes	playinit();
470111119Simp	spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK);
471738Sache	spkr_active = TRUE;
472738Sache	return(0);
4734Srgrimes    }
4744Srgrimes}
4754Srgrimes
476105224Sphkstatic int
47717232Sjoergspkrwrite(dev, uio, ioflag)
478130585Sphk	struct cdev *dev;
47917232Sjoerg	struct uio	*uio;
48017232Sjoerg	int		ioflag;
4814Srgrimes{
4824Srgrimes#ifdef DEBUG
48349982Sbillf    printf("spkrwrite: entering with dev = %s, count = %d\n",
48449982Sbillf		devtoname(dev), uio->uio_resid);
4854Srgrimes#endif /* DEBUG */
4864Srgrimes
4874Srgrimes    if (minor(dev) != 0)
4884Srgrimes	return(ENXIO);
48917803Speter    else if (uio->uio_resid > (DEV_BSIZE - 1))     /* prevent system crashes */
490738Sache	return(E2BIG);
4914Srgrimes    else
4924Srgrimes    {
493738Sache	unsigned n;
494738Sache	char *cp;
495738Sache	int error;
496738Sache
497738Sache	n = uio->uio_resid;
49860038Sphk	cp = spkr_inbuf;
49917803Speter	error = uiomove(cp, n, uio);
50017803Speter	if (!error) {
50117803Speter		cp[n] = '\0';
5021016Sache		playstring(cp, n);
50317803Speter	}
5044Srgrimes	return(error);
5054Srgrimes    }
5064Srgrimes}
5074Srgrimes
508105224Sphkstatic int
50983366Sjulianspkrclose(dev, flags, fmt, td)
510130585Sphk	struct cdev *dev;
51117232Sjoerg	int		flags;
51217232Sjoerg	int		fmt;
51383366Sjulian	struct thread	*td;
5144Srgrimes{
5154Srgrimes#ifdef DEBUG
51649982Sbillf    (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev));
5174Srgrimes#endif /* DEBUG */
5184Srgrimes
5194Srgrimes    if (minor(dev) != 0)
5204Srgrimes	return(ENXIO);
5214Srgrimes    else
5224Srgrimes    {
523111748Sdes	wakeup(&endtone);
524111748Sdes	wakeup(&endrest);
52560038Sphk	free(spkr_inbuf, M_SPKR);
526738Sache	spkr_active = FALSE;
527738Sache	return(0);
5284Srgrimes    }
5294Srgrimes}
5304Srgrimes
531105224Sphkstatic int
53283366Sjulianspkrioctl(dev, cmd, cmdarg, flags, td)
533130585Sphk	struct cdev *dev;
53438505Sbde	unsigned long	cmd;
53517232Sjoerg	caddr_t		cmdarg;
53617232Sjoerg	int		flags;
53783366Sjulian	struct thread	*td;
5384Srgrimes{
5394Srgrimes#ifdef DEBUG
54050253Sbde    (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n",
54150253Sbde    	devtoname(dev), cmd);
5424Srgrimes#endif /* DEBUG */
5434Srgrimes
5444Srgrimes    if (minor(dev) != 0)
5454Srgrimes	return(ENXIO);
5464Srgrimes    else if (cmd == SPKRTONE)
5474Srgrimes    {
5484Srgrimes	tone_t	*tp = (tone_t *)cmdarg;
5494Srgrimes
5504Srgrimes	if (tp->frequency == 0)
5511016Sache	    rest(tp->duration);
5524Srgrimes	else
5531016Sache	    tone(tp->frequency, tp->duration);
5541016Sache	return 0;
5554Srgrimes    }
5564Srgrimes    else if (cmd == SPKRTUNE)
5574Srgrimes    {
5584Srgrimes	tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
5594Srgrimes	tone_t ttp;
5604Srgrimes	int error;
5614Srgrimes
5624Srgrimes	for (; ; tp++) {
5634Srgrimes	    error = copyin(tp, &ttp, sizeof(tone_t));
5644Srgrimes	    if (error)
5654Srgrimes		    return(error);
5664Srgrimes	    if (ttp.duration == 0)
5674Srgrimes		    break;
5684Srgrimes	    if (ttp.frequency == 0)
5691016Sache		 rest(ttp.duration);
5704Srgrimes	    else
5711016Sache		 tone(ttp.frequency, ttp.duration);
5724Srgrimes	}
573738Sache	return(0);
5744Srgrimes    }
575738Sache    return(EINVAL);
5764Srgrimes}
5774Srgrimes
57861994Smsmith/*
57961994Smsmith * Install placeholder to claim the resources owned by the
58061994Smsmith * AT tone generator.
58161994Smsmith */
582106323Smdoddstatic struct isa_pnp_id speaker_ids[] = {
583106323Smdodd#ifndef PC98
584112561Smdodd	{ 0x0008d041 /* PNP0800 */, SPKR_DESC },
585106323Smdodd#endif
58661994Smsmith	{ 0 }
58761994Smsmith};
58812517Sjulian
589130585Sphkstatic struct cdev *speaker_dev;
59089679Swes
59161994Smsmithstatic int
592106323Smdoddspeaker_probe(device_t dev)
59361994Smsmith{
594106070Smdodd	int	error;
595106070Smdodd
596106323Smdodd	error = ISA_PNP_PROBE(device_get_parent(dev), dev, speaker_ids);
597106070Smdodd
598106070Smdodd	/* PnP match */
599106070Smdodd	if (error == 0)
600106070Smdodd		return (0);
601106070Smdodd
602106070Smdodd	/* No match */
603106070Smdodd	if (error == ENXIO)
604106070Smdodd		return (ENXIO);
605106070Smdodd
606106070Smdodd	/* Not configured by hints. */
607106323Smdodd	if (strncmp(device_get_name(dev), "speaker", 9))
608106070Smdodd		return (ENXIO);
609106070Smdodd
610112561Smdodd	device_set_desc(dev, SPKR_DESC);
611106070Smdodd
612106070Smdodd	return (0);
61361994Smsmith}
61461994Smsmith
61561994Smsmithstatic int
616106323Smdoddspeaker_attach(device_t dev)
61761994Smsmith{
618106070Smdodd
619106323Smdodd	if (speaker_dev) {
620106070Smdodd		device_printf(dev, "Already attached!\n");
621106070Smdodd		return (ENXIO);
622106070Smdodd	}
623106070Smdodd
624106323Smdodd	speaker_dev = make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
62589679Swes	    "speaker");
62689679Swes	return (0);
62761994Smsmith}
62861994Smsmith
62989679Swesstatic int
630106323Smdoddspeaker_detach(device_t dev)
63189679Swes{
632106323Smdodd	destroy_dev(speaker_dev);
63389679Swes	return (0);
63489679Swes}
63589679Swes
636106323Smdoddstatic device_method_t speaker_methods[] = {
63761994Smsmith	/* Device interface */
638106323Smdodd	DEVMETHOD(device_probe,		speaker_probe),
639106323Smdodd	DEVMETHOD(device_attach,	speaker_attach),
640106323Smdodd	DEVMETHOD(device_detach,	speaker_detach),
64161994Smsmith	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
64261994Smsmith	DEVMETHOD(device_suspend,	bus_generic_suspend),
64361994Smsmith	DEVMETHOD(device_resume,	bus_generic_resume),
64461994Smsmith	{ 0, 0 }
64561994Smsmith};
64661994Smsmith
647106323Smdoddstatic driver_t speaker_driver = {
648106323Smdodd	"speaker",
649106323Smdodd	speaker_methods,
65061994Smsmith	1,		/* no softc */
65161994Smsmith};
65261994Smsmith
653106323Smdoddstatic devclass_t speaker_devclass;
65461994Smsmith
655106323SmdoddDRIVER_MODULE(speaker, isa, speaker_driver, speaker_devclass, 0, 0);
656106323Smdodd#ifndef PC98
657106323SmdoddDRIVER_MODULE(speaker, acpi, speaker_driver, speaker_devclass, 0, 0);
658106323Smdodd#endif
65961994Smsmith
6604Srgrimes/* spkr.c ends here */
661