spkr.c revision 111815
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>
6106323Smdodd * modified for PC98 by Kakefuda
7619Srgrimes *
850477Speter * $FreeBSD: head/sys/dev/speaker/spkr.c 111815 2003-03-03 12:15:54Z phk $
94Srgrimes */
104Srgrimes
112056Swollman#include <sys/param.h>
122056Swollman#include <sys/systm.h>
1361994Smsmith#include <sys/bus.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>
2061994Smsmith#include <isa/isavar.h>
21106323Smdodd#ifdef PC98
22106323Smdodd#include <pc98/pc98/pc98.h>
23106323Smdodd#else
242056Swollman#include <i386/isa/isa.h>
25106323Smdodd#endif
262056Swollman#include <i386/isa/timerreg.h>
277090Sbde#include <machine/clock.h>
282056Swollman#include <machine/speaker.h>
294Srgrimes
3012675Sjulianstatic	d_open_t	spkropen;
3112675Sjulianstatic	d_close_t	spkrclose;
3212675Sjulianstatic	d_write_t	spkrwrite;
3312675Sjulianstatic	d_ioctl_t	spkrioctl;
3412502Sjulian
3512675Sjulian#define CDEV_MAJOR 26
3647625Sphkstatic struct cdevsw spkr_cdevsw = {
37111815Sphk	.d_open =	spkropen,
38111815Sphk	.d_close =	spkrclose,
39111815Sphk	.d_write =	spkrwrite,
40111815Sphk	.d_ioctl =	spkrioctl,
41111815Sphk	.d_name =	"spkr",
42111815Sphk	.d_maj =	CDEV_MAJOR,
4347625Sphk};
4412675Sjulian
4569774Sphkstatic MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer");
4660038Sphk
474Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE *************************
484Srgrimes *
494Srgrimes * This section defines a function tone() which causes a tone of given
5019174Sbde * frequency and duration from the ISA console speaker.
514Srgrimes * Another function endtone() is defined to force sound off, and there is
524Srgrimes * also a rest() entry point to do pauses.
534Srgrimes *
544Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and
5519174Sbde * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The
564Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is
574Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired.
584Srgrimes */
594Srgrimes
604Srgrimes/*
61106323Smdodd * XXX PPI control values should be in a header and used in clock.c.
624Srgrimes */
63106323Smdodd#ifdef PC98
64106323Smdodd#define	PPI_SPKR	0x08	/* turn these PPI bits on to pass sound */
65106323Smdodd#define	PIT_COUNT	0x3fdb	/* PIT count address */
66106323Smdodd
67106323Smdodd#define	SPEAKER_ON	outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR)
68106323Smdodd#define	SPEAKER_OFF	outb(IO_PPI, inb(IO_PPI) | PPI_SPKR)
69106323Smdodd#define	TIMER_ACQUIRE	acquire_timer1(TIMER_SEL1 | TIMER_SQWAVE | TIMER_16BIT)
70106323Smdodd#define	TIMER_RELEASE	release_timer1()
71106323Smdodd#define	SPEAKER_WRITE(val)	{ \
72106323Smdodd					outb(PIT_COUNT, (val & 0xff)); \
73106323Smdodd					outb(PIT_COUNT, (val >> 8)); \
74106323Smdodd				}
75106323Smdodd#else
764Srgrimes#define PPI_SPKR	0x03	/* turn these PPI bits on to pass sound */
774Srgrimes
78106323Smdodd#define	SPEAKER_ON	outb(IO_PPI, inb(IO_PPI) | PPI_SPKR)
79106323Smdodd#define	SPEAKER_OFF	outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR)
80106323Smdodd#define	TIMER_ACQUIRE	acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT)
81106323Smdodd#define	TIMER_RELEASE	release_timer2()
82106323Smdodd#define	SPEAKER_WRITE(val)	{ \
83106323Smdodd					outb(TIMER_CNTR2, (val & 0xff)); \
84106323Smdodd    					outb(TIMER_CNTR2, (val >> 8)); \
85106323Smdodd				}
86106323Smdodd#endif
87106323Smdodd
88766Sache#define SPKRPRI PSOCK
89766Sachestatic char endtone, endrest;
904Srgrimes
9192765Salfredstatic void tone(unsigned int thz, unsigned int ticks);
9292765Salfredstatic void rest(int ticks);
9392765Salfredstatic void playinit(void);
9492765Salfredstatic void playtone(int pitch, int value, int sustain);
9592765Salfredstatic void playstring(char *cp, size_t slen);
9612854Sbde
97766Sache/* emit tone of frequency thz for given number of ticks */
9817232Sjoergstatic void
9917232Sjoergtone(thz, ticks)
10017232Sjoerg	unsigned int thz, ticks;
1014Srgrimes{
1028288Sdg    unsigned int divisor;
1031016Sache    int sps;
1044Srgrimes
1058288Sdg    if (thz <= 0)
1068288Sdg	return;
1078288Sdg
10819174Sbde    divisor = timer_freq / thz;
1098288Sdg
1104Srgrimes#ifdef DEBUG
111766Sache    (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
1124Srgrimes#endif /* DEBUG */
1134Srgrimes
1144Srgrimes    /* set timer to generate clicks at given frequency in Hertz */
11517232Sjoerg    sps = splclock();
1161393Ssos
117106323Smdodd    if (TIMER_ACQUIRE) {
1181393Ssos	/* enter list of waiting procs ??? */
11917232Sjoerg	splx(sps);
1201393Ssos	return;
1211393Ssos    }
12217232Sjoerg    splx(sps);
12317232Sjoerg    disable_intr();
124106323Smdodd    SPEAKER_WRITE(divisor);
12517232Sjoerg    enable_intr();
1264Srgrimes
1274Srgrimes    /* turn the speaker on */
128106323Smdodd    SPEAKER_ON;
1294Srgrimes
1304Srgrimes    /*
1314Srgrimes     * Set timeout to endtone function, then give up the timeslice.
1324Srgrimes     * This is so other processes can execute while the tone is being
1334Srgrimes     * emitted.
1344Srgrimes     */
1356152Sache    if (ticks > 0)
136111748Sdes	tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
137106323Smdodd    SPEAKER_OFF;
13817232Sjoerg    sps = splclock();
139106323Smdodd    TIMER_RELEASE;
14017232Sjoerg    splx(sps);
1414Srgrimes}
1424Srgrimes
1434Srgrimes/* rest for given number of ticks */
14417232Sjoergstatic void
14517232Sjoergrest(ticks)
14617232Sjoerg	int	ticks;
1474Srgrimes{
1484Srgrimes    /*
1494Srgrimes     * Set timeout to endrest function, then give up the timeslice.
1504Srgrimes     * This is so other processes can execute while the rest is being
1514Srgrimes     * waited out.
1524Srgrimes     */
1534Srgrimes#ifdef DEBUG
154738Sache    (void) printf("rest: %d\n", ticks);
1554Srgrimes#endif /* DEBUG */
1566152Sache    if (ticks > 0)
157111748Sdes	tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
1584Srgrimes}
1594Srgrimes
1604Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
1614Srgrimes *
1624Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
163738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
164738Sache * tracking facility are added.
1654Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible
1664Srgrimes * except possibly at physical block boundaries.
1674Srgrimes */
1684Srgrimes
1694Srgrimestypedef int	bool;
1704Srgrimes#define TRUE	1
1714Srgrimes#define FALSE	0
1724Srgrimes
1734Srgrimes#define dtoi(c)		((c) - '0')
1744Srgrimes
1754Srgrimesstatic int octave;	/* currently selected octave */
1764Srgrimesstatic int whole;	/* whole-note time at current tempo, in ticks */
1774Srgrimesstatic int value;	/* whole divisor for note time, quarter note = 1 */
1784Srgrimesstatic int fill;	/* controls spacing of notes */
1794Srgrimesstatic bool octtrack;	/* octave-tracking on? */
1804Srgrimesstatic bool octprefix;	/* override current octave-tracking state? */
1814Srgrimes
1824Srgrimes/*
1834Srgrimes * Magic number avoidance...
1844Srgrimes */
1854Srgrimes#define SECS_PER_MIN	60	/* seconds per minute */
1864Srgrimes#define WHOLE_NOTE	4	/* quarter notes per whole note */
1874Srgrimes#define MIN_VALUE	64	/* the most we can divide a note by */
1884Srgrimes#define DFLT_VALUE	4	/* default value (quarter-note) */
1894Srgrimes#define FILLTIME	8	/* for articulation, break note in parts */
1904Srgrimes#define STACCATO	6	/* 6/8 = 3/4 of note is filled */
1914Srgrimes#define NORMAL		7	/* 7/8ths of note interval is filled */
1924Srgrimes#define LEGATO		8	/* all of note interval is filled */
1934Srgrimes#define DFLT_OCTAVE	4	/* default octave */
1944Srgrimes#define MIN_TEMPO	32	/* minimum tempo */
1954Srgrimes#define DFLT_TEMPO	120	/* default tempo */
1964Srgrimes#define MAX_TEMPO	255	/* max tempo */
1974Srgrimes#define NUM_MULT	3	/* numerator of dot multiplier */
1984Srgrimes#define DENOM_MULT	2	/* denominator of dot multiplier */
1994Srgrimes
2004Srgrimes/* letter to half-tone:  A   B  C  D  E  F  G */
2014Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
2024Srgrimes
2034Srgrimes/*
2044Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies
2054Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
2064Srgrimes * our octave 0 is standard octave 2.
2074Srgrimes */
2084Srgrimes#define OCTAVE_NOTES	12	/* semitones per octave */
2094Srgrimesstatic int pitchtab[] =
2104Srgrimes{
2114Srgrimes/*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
2124Srgrimes/* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
2134Srgrimes/* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
2144Srgrimes/* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
2154Srgrimes/* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
2164Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
2174Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
2184Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
2194Srgrimes};
2204Srgrimes
22117232Sjoergstatic void
22217232Sjoergplayinit()
2234Srgrimes{
2244Srgrimes    octave = DFLT_OCTAVE;
225766Sache    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
2264Srgrimes    fill = NORMAL;
2274Srgrimes    value = DFLT_VALUE;
2284Srgrimes    octtrack = FALSE;
2294Srgrimes    octprefix = TRUE;	/* act as though there was an initial O(n) */
2304Srgrimes}
2314Srgrimes
2324Srgrimes/* play tone of proper duration for current rhythm signature */
23317232Sjoergstatic void
23417232Sjoergplaytone(pitch, value, sustain)
23517232Sjoerg	int	pitch, value, sustain;
2364Srgrimes{
2374Srgrimes    register int	sound, silence, snum = 1, sdenom = 1;
2384Srgrimes
2394Srgrimes    /* this weirdness avoids floating-point arithmetic */
2404Srgrimes    for (; sustain; sustain--)
2414Srgrimes    {
242738Sache	/* See the BUGS section in the man page for discussion */
2434Srgrimes	snum *= NUM_MULT;
2444Srgrimes	sdenom *= DENOM_MULT;
2454Srgrimes    }
2464Srgrimes
2478288Sdg    if (value == 0 || sdenom == 0)
2488288Sdg	return;
2498288Sdg
2504Srgrimes    if (pitch == -1)
2511016Sache	rest(whole * snum / (value * sdenom));
2524Srgrimes    else
2534Srgrimes    {
2544Srgrimes	sound = (whole * snum) / (value * sdenom)
2554Srgrimes		- (whole * (FILLTIME - fill)) / (value * FILLTIME);
2564Srgrimes	silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
2574Srgrimes
2584Srgrimes#ifdef DEBUG
259738Sache	(void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
2604Srgrimes			pitch, sound, silence);
2614Srgrimes#endif /* DEBUG */
2624Srgrimes
2631016Sache	tone(pitchtab[pitch], sound);
2644Srgrimes	if (fill != LEGATO)
2651016Sache	    rest(silence);
2664Srgrimes    }
2674Srgrimes}
2684Srgrimes
2694Srgrimes/* interpret and play an item from a notation string */
27017232Sjoergstatic void
27117232Sjoergplaystring(cp, slen)
27217232Sjoerg	char	*cp;
27317232Sjoerg	size_t	slen;
2744Srgrimes{
275738Sache    int		pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
2764Srgrimes
2774Srgrimes#define GETNUM(cp, v)	for(v=0; isdigit(cp[1]) && slen > 0; ) \
2784Srgrimes				{v = v * 10 + (*++cp - '0'); slen--;}
2794Srgrimes    for (; slen--; cp++)
2804Srgrimes    {
2814Srgrimes	int		sustain, timeval, tempo;
2824Srgrimes	register char	c = toupper(*cp);
2834Srgrimes
2844Srgrimes#ifdef DEBUG
285738Sache	(void) printf("playstring: %c (%x)\n", c, c);
2864Srgrimes#endif /* DEBUG */
2874Srgrimes
2884Srgrimes	switch (c)
2894Srgrimes	{
2904Srgrimes	case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2914Srgrimes
2924Srgrimes	    /* compute pitch */
2934Srgrimes	    pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
2944Srgrimes
2954Srgrimes	    /* this may be followed by an accidental sign */
2964Srgrimes	    if (cp[1] == '#' || cp[1] == '+')
2974Srgrimes	    {
2984Srgrimes		++pitch;
2994Srgrimes		++cp;
3004Srgrimes		slen--;
3014Srgrimes	    }
3024Srgrimes	    else if (cp[1] == '-')
3034Srgrimes	    {
3044Srgrimes		--pitch;
3054Srgrimes		++cp;
3064Srgrimes		slen--;
3074Srgrimes	    }
3084Srgrimes
3094Srgrimes	    /*
3104Srgrimes	     * If octave-tracking mode is on, and there has been no octave-
3114Srgrimes	     * setting prefix, find the version of the current letter note
3124Srgrimes	     * closest to the last regardless of octave.
3134Srgrimes	     */
3144Srgrimes	    if (octtrack && !octprefix)
3154Srgrimes	    {
3164Srgrimes		if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
3174Srgrimes		{
3184Srgrimes		    ++octave;
3194Srgrimes		    pitch += OCTAVE_NOTES;
3204Srgrimes		}
3214Srgrimes
3224Srgrimes		if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
3234Srgrimes		{
3244Srgrimes		    --octave;
3254Srgrimes		    pitch -= OCTAVE_NOTES;
3264Srgrimes		}
3274Srgrimes	    }
3284Srgrimes	    octprefix = FALSE;
3294Srgrimes	    lastpitch = pitch;
3304Srgrimes
3314Srgrimes	    /* ...which may in turn be followed by an override time value */
3324Srgrimes	    GETNUM(cp, timeval);
3334Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
3344Srgrimes		timeval = value;
3354Srgrimes
3364Srgrimes	    /* ...and/or sustain dots */
3374Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3384Srgrimes	    {
3394Srgrimes		slen--;
3404Srgrimes		sustain++;
3414Srgrimes	    }
3424Srgrimes
343738Sache	    /* ...and/or a slur mark */
344738Sache	    oldfill = fill;
345738Sache	    if (cp[1] == '_')
346738Sache	    {
347738Sache		fill = LEGATO;
348738Sache		++cp;
349738Sache		slen--;
350738Sache	    }
351738Sache
3524Srgrimes	    /* time to emit the actual tone */
3531016Sache	    playtone(pitch, timeval, sustain);
354738Sache
355738Sache	    fill = oldfill;
3564Srgrimes	    break;
3574Srgrimes
3584Srgrimes	case 'O':
3594Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
3604Srgrimes	    {
3614Srgrimes		octprefix = octtrack = FALSE;
3624Srgrimes		++cp;
3634Srgrimes		slen--;
3644Srgrimes	    }
3654Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
3664Srgrimes	    {
3674Srgrimes		octtrack = TRUE;
3684Srgrimes		++cp;
3694Srgrimes		slen--;
3704Srgrimes	    }
3714Srgrimes	    else
3724Srgrimes	    {
3734Srgrimes		GETNUM(cp, octave);
3743593Sache		if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
3754Srgrimes		    octave = DFLT_OCTAVE;
3764Srgrimes		octprefix = TRUE;
3774Srgrimes	    }
3784Srgrimes	    break;
3794Srgrimes
3804Srgrimes	case '>':
3813593Sache	    if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
3824Srgrimes		octave++;
3834Srgrimes	    octprefix = TRUE;
3844Srgrimes	    break;
3854Srgrimes
3864Srgrimes	case '<':
3874Srgrimes	    if (octave > 0)
3884Srgrimes		octave--;
3894Srgrimes	    octprefix = TRUE;
3904Srgrimes	    break;
3914Srgrimes
3924Srgrimes	case 'N':
3934Srgrimes	    GETNUM(cp, pitch);
3944Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3954Srgrimes	    {
3964Srgrimes		slen--;
3974Srgrimes		sustain++;
3984Srgrimes	    }
399738Sache	    oldfill = fill;
400738Sache	    if (cp[1] == '_')
401738Sache	    {
402738Sache		fill = LEGATO;
403738Sache		++cp;
404738Sache		slen--;
405738Sache	    }
4061016Sache	    playtone(pitch - 1, value, sustain);
407738Sache	    fill = oldfill;
4084Srgrimes	    break;
4094Srgrimes
4104Srgrimes	case 'L':
4114Srgrimes	    GETNUM(cp, value);
4124Srgrimes	    if (value <= 0 || value > MIN_VALUE)
4134Srgrimes		value = DFLT_VALUE;
4144Srgrimes	    break;
4154Srgrimes
4164Srgrimes	case 'P':
4174Srgrimes	case '~':
4184Srgrimes	    /* this may be followed by an override time value */
4194Srgrimes	    GETNUM(cp, timeval);
4204Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
4214Srgrimes		timeval = value;
4224Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
4234Srgrimes	    {
4244Srgrimes		slen--;
4254Srgrimes		sustain++;
4264Srgrimes	    }
4271016Sache	    playtone(-1, timeval, sustain);
4284Srgrimes	    break;
4294Srgrimes
4304Srgrimes	case 'T':
4314Srgrimes	    GETNUM(cp, tempo);
4324Srgrimes	    if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
4334Srgrimes		tempo = DFLT_TEMPO;
434766Sache	    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
4354Srgrimes	    break;
4364Srgrimes
4374Srgrimes	case 'M':
4384Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
4394Srgrimes	    {
4404Srgrimes		fill = NORMAL;
4414Srgrimes		++cp;
4424Srgrimes		slen--;
4434Srgrimes	    }
4444Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
4454Srgrimes	    {
4464Srgrimes		fill = LEGATO;
4474Srgrimes		++cp;
4484Srgrimes		slen--;
4494Srgrimes	    }
4504Srgrimes	    else if (cp[1] == 'S' || cp[1] == 's')
4514Srgrimes	    {
4524Srgrimes		fill = STACCATO;
4534Srgrimes		++cp;
4544Srgrimes		slen--;
4554Srgrimes	    }
4564Srgrimes	    break;
4574Srgrimes	}
4584Srgrimes    }
4594Srgrimes}
4604Srgrimes
4614Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
4624Srgrimes *
4634Srgrimes * This section implements driver hooks to run playstring() and the tone(),
4644Srgrimes * endtone(), and rest() functions defined above.
4654Srgrimes */
4664Srgrimes
467738Sachestatic int spkr_active = FALSE; /* exclusion flag */
46860038Sphkstatic char *spkr_inbuf;  /* incoming buf */
4694Srgrimes
470105224Sphkstatic int
47183366Sjulianspkropen(dev, flags, fmt, td)
47217232Sjoerg	dev_t		dev;
47317232Sjoerg	int		flags;
47417232Sjoerg	int		fmt;
47583366Sjulian	struct thread	*td;
4764Srgrimes{
4774Srgrimes#ifdef DEBUG
47849982Sbillf    (void) printf("spkropen: entering with dev = %s\n", devtoname(dev));
4794Srgrimes#endif /* DEBUG */
4804Srgrimes
4814Srgrimes    if (minor(dev) != 0)
4824Srgrimes	return(ENXIO);
4834Srgrimes    else if (spkr_active)
4844Srgrimes	return(EBUSY);
4854Srgrimes    else
4864Srgrimes    {
487738Sache#ifdef DEBUG
488738Sache	(void) printf("spkropen: about to perform play initialization\n");
489738Sache#endif /* DEBUG */
4904Srgrimes	playinit();
491111119Simp	spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK);
492738Sache	spkr_active = TRUE;
493738Sache	return(0);
4944Srgrimes    }
4954Srgrimes}
4964Srgrimes
497105224Sphkstatic int
49817232Sjoergspkrwrite(dev, uio, ioflag)
49917232Sjoerg	dev_t		dev;
50017232Sjoerg	struct uio	*uio;
50117232Sjoerg	int		ioflag;
5024Srgrimes{
5034Srgrimes#ifdef DEBUG
50449982Sbillf    printf("spkrwrite: entering with dev = %s, count = %d\n",
50549982Sbillf		devtoname(dev), uio->uio_resid);
5064Srgrimes#endif /* DEBUG */
5074Srgrimes
5084Srgrimes    if (minor(dev) != 0)
5094Srgrimes	return(ENXIO);
51017803Speter    else if (uio->uio_resid > (DEV_BSIZE - 1))     /* prevent system crashes */
511738Sache	return(E2BIG);
5124Srgrimes    else
5134Srgrimes    {
514738Sache	unsigned n;
515738Sache	char *cp;
516738Sache	int error;
517738Sache
518738Sache	n = uio->uio_resid;
51960038Sphk	cp = spkr_inbuf;
52017803Speter	error = uiomove(cp, n, uio);
52117803Speter	if (!error) {
52217803Speter		cp[n] = '\0';
5231016Sache		playstring(cp, n);
52417803Speter	}
5254Srgrimes	return(error);
5264Srgrimes    }
5274Srgrimes}
5284Srgrimes
529105224Sphkstatic int
53083366Sjulianspkrclose(dev, flags, fmt, td)
53117232Sjoerg	dev_t		dev;
53217232Sjoerg	int		flags;
53317232Sjoerg	int		fmt;
53483366Sjulian	struct thread	*td;
5354Srgrimes{
5364Srgrimes#ifdef DEBUG
53749982Sbillf    (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev));
5384Srgrimes#endif /* DEBUG */
5394Srgrimes
5404Srgrimes    if (minor(dev) != 0)
5414Srgrimes	return(ENXIO);
5424Srgrimes    else
5434Srgrimes    {
544111748Sdes	wakeup(&endtone);
545111748Sdes	wakeup(&endrest);
54660038Sphk	free(spkr_inbuf, M_SPKR);
547738Sache	spkr_active = FALSE;
548738Sache	return(0);
5494Srgrimes    }
5504Srgrimes}
5514Srgrimes
552105224Sphkstatic int
55383366Sjulianspkrioctl(dev, cmd, cmdarg, flags, td)
55417232Sjoerg	dev_t		dev;
55538505Sbde	unsigned long	cmd;
55617232Sjoerg	caddr_t		cmdarg;
55717232Sjoerg	int		flags;
55883366Sjulian	struct thread	*td;
5594Srgrimes{
5604Srgrimes#ifdef DEBUG
56150253Sbde    (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n",
56250253Sbde    	devtoname(dev), cmd);
5634Srgrimes#endif /* DEBUG */
5644Srgrimes
5654Srgrimes    if (minor(dev) != 0)
5664Srgrimes	return(ENXIO);
5674Srgrimes    else if (cmd == SPKRTONE)
5684Srgrimes    {
5694Srgrimes	tone_t	*tp = (tone_t *)cmdarg;
5704Srgrimes
5714Srgrimes	if (tp->frequency == 0)
5721016Sache	    rest(tp->duration);
5734Srgrimes	else
5741016Sache	    tone(tp->frequency, tp->duration);
5751016Sache	return 0;
5764Srgrimes    }
5774Srgrimes    else if (cmd == SPKRTUNE)
5784Srgrimes    {
5794Srgrimes	tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
5804Srgrimes	tone_t ttp;
5814Srgrimes	int error;
5824Srgrimes
5834Srgrimes	for (; ; tp++) {
5844Srgrimes	    error = copyin(tp, &ttp, sizeof(tone_t));
5854Srgrimes	    if (error)
5864Srgrimes		    return(error);
5874Srgrimes	    if (ttp.duration == 0)
5884Srgrimes		    break;
5894Srgrimes	    if (ttp.frequency == 0)
5901016Sache		 rest(ttp.duration);
5914Srgrimes	    else
5921016Sache		 tone(ttp.frequency, ttp.duration);
5934Srgrimes	}
594738Sache	return(0);
5954Srgrimes    }
596738Sache    return(EINVAL);
5974Srgrimes}
5984Srgrimes
59961994Smsmith/*
60061994Smsmith * Install placeholder to claim the resources owned by the
60161994Smsmith * AT tone generator.
60261994Smsmith */
603106323Smdoddstatic struct isa_pnp_id speaker_ids[] = {
604106323Smdodd#ifndef PC98
605106323Smdodd	{ 0x0008d041 /* PNP0800 */, "PC speaker" },
606106323Smdodd#endif
60761994Smsmith	{ 0 }
60861994Smsmith};
60912517Sjulian
610106323Smdoddstatic dev_t speaker_dev;
61189679Swes
61261994Smsmithstatic int
613106323Smdoddspeaker_probe(device_t dev)
61461994Smsmith{
615106070Smdodd	int	error;
616106070Smdodd
617106323Smdodd	error = ISA_PNP_PROBE(device_get_parent(dev), dev, speaker_ids);
618106070Smdodd
619106070Smdodd	/* PnP match */
620106070Smdodd	if (error == 0)
621106070Smdodd		return (0);
622106070Smdodd
623106070Smdodd	/* No match */
624106070Smdodd	if (error == ENXIO)
625106070Smdodd		return (ENXIO);
626106070Smdodd
627106070Smdodd	/* Not configured by hints. */
628106323Smdodd	if (strncmp(device_get_name(dev), "speaker", 9))
629106070Smdodd		return (ENXIO);
630106070Smdodd
631106323Smdodd	device_set_desc(dev, "PC speaker");
632106070Smdodd
633106070Smdodd	return (0);
63461994Smsmith}
63561994Smsmith
63661994Smsmithstatic int
637106323Smdoddspeaker_attach(device_t dev)
63861994Smsmith{
639106070Smdodd
640106323Smdodd	if (speaker_dev) {
641106070Smdodd		device_printf(dev, "Already attached!\n");
642106070Smdodd		return (ENXIO);
643106070Smdodd	}
644106070Smdodd
645106323Smdodd	speaker_dev = make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
64689679Swes	    "speaker");
64789679Swes	return (0);
64861994Smsmith}
64961994Smsmith
65089679Swesstatic int
651106323Smdoddspeaker_detach(device_t dev)
65289679Swes{
653106323Smdodd	destroy_dev(speaker_dev);
65489679Swes	return (0);
65589679Swes}
65689679Swes
657106323Smdoddstatic device_method_t speaker_methods[] = {
65861994Smsmith	/* Device interface */
659106323Smdodd	DEVMETHOD(device_probe,		speaker_probe),
660106323Smdodd	DEVMETHOD(device_attach,	speaker_attach),
661106323Smdodd	DEVMETHOD(device_detach,	speaker_detach),
66261994Smsmith	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
66361994Smsmith	DEVMETHOD(device_suspend,	bus_generic_suspend),
66461994Smsmith	DEVMETHOD(device_resume,	bus_generic_resume),
66561994Smsmith	{ 0, 0 }
66661994Smsmith};
66761994Smsmith
668106323Smdoddstatic driver_t speaker_driver = {
669106323Smdodd	"speaker",
670106323Smdodd	speaker_methods,
67161994Smsmith	1,		/* no softc */
67261994Smsmith};
67361994Smsmith
674106323Smdoddstatic devclass_t speaker_devclass;
67561994Smsmith
676106323SmdoddDRIVER_MODULE(speaker, isa, speaker_driver, speaker_devclass, 0, 0);
677106323Smdodd#ifndef PC98
678106323SmdoddDRIVER_MODULE(speaker, acpi, speaker_driver, speaker_devclass, 0, 0);
679106323Smdodd#endif
68061994Smsmith
6814Srgrimes/* spkr.c ends here */
682