spkr.c revision 61994
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 *
750477Speter * $FreeBSD: head/sys/dev/speaker/spkr.c 61994 2000-06-23 07:44:33Z msmith $
84Srgrimes */
94Srgrimes
102056Swollman#include <sys/param.h>
112056Swollman#include <sys/systm.h>
1261994Smsmith#include <sys/bus.h>
132056Swollman#include <sys/kernel.h>
1461994Smsmith#include <sys/module.h>
152056Swollman#include <sys/uio.h>
1612675Sjulian#include <sys/conf.h>
1752843Sphk#include <sys/ctype.h>
1860038Sphk#include <sys/malloc.h>
1961994Smsmith#include <isa/isavar.h>
202056Swollman#include <i386/isa/isa.h>
212056Swollman#include <i386/isa/timerreg.h>
227090Sbde#include <machine/clock.h>
232056Swollman#include <machine/speaker.h>
244Srgrimes
2512675Sjulianstatic	d_open_t	spkropen;
2612675Sjulianstatic	d_close_t	spkrclose;
2712675Sjulianstatic	d_write_t	spkrwrite;
2812675Sjulianstatic	d_ioctl_t	spkrioctl;
2912502Sjulian
3012675Sjulian#define CDEV_MAJOR 26
3147625Sphkstatic struct cdevsw spkr_cdevsw = {
3247625Sphk	/* open */	spkropen,
3347625Sphk	/* close */	spkrclose,
3447625Sphk	/* read */	noread,
3547625Sphk	/* write */	spkrwrite,
3647625Sphk	/* ioctl */	spkrioctl,
3747625Sphk	/* poll */	nopoll,
3847625Sphk	/* mmap */	nommap,
3947625Sphk	/* strategy */	nostrategy,
4047625Sphk	/* name */	"spkr",
4147625Sphk	/* maj */	CDEV_MAJOR,
4247625Sphk	/* dump */	nodump,
4347625Sphk	/* psize */	nopsize,
4447625Sphk	/* flags */	0,
4547625Sphk	/* bmaj */	-1
4647625Sphk};
4712675Sjulian
4860038SphkMALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer");
4960038Sphk
504Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE *************************
514Srgrimes *
524Srgrimes * This section defines a function tone() which causes a tone of given
5319174Sbde * frequency and duration from the ISA console speaker.
544Srgrimes * Another function endtone() is defined to force sound off, and there is
554Srgrimes * also a rest() entry point to do pauses.
564Srgrimes *
574Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and
5819174Sbde * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The
594Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is
604Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired.
614Srgrimes */
624Srgrimes
634Srgrimes/*
6419174Sbde * PPI control values.
6519174Sbde * XXX should be in a header and used in clock.c.
664Srgrimes */
674Srgrimes#define PPI_SPKR	0x03	/* turn these PPI bits on to pass sound */
684Srgrimes
69766Sache#define SPKRPRI PSOCK
70766Sachestatic char endtone, endrest;
714Srgrimes
7212854Sbdestatic void tone __P((unsigned int thz, unsigned int ticks));
7312854Sbdestatic void rest __P((int ticks));
7412854Sbdestatic void playinit __P((void));
7512854Sbdestatic void playtone __P((int pitch, int value, int sustain));
7612854Sbdestatic int abs __P((int n));
7712854Sbdestatic void playstring __P((char *cp, size_t slen));
7812854Sbde
79766Sache/* emit tone of frequency thz for given number of ticks */
8017232Sjoergstatic void
8117232Sjoergtone(thz, ticks)
8217232Sjoerg	unsigned int thz, ticks;
834Srgrimes{
848288Sdg    unsigned int divisor;
851016Sache    int sps;
864Srgrimes
878288Sdg    if (thz <= 0)
888288Sdg	return;
898288Sdg
9019174Sbde    divisor = timer_freq / thz;
918288Sdg
924Srgrimes#ifdef DEBUG
93766Sache    (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
944Srgrimes#endif /* DEBUG */
954Srgrimes
964Srgrimes    /* set timer to generate clicks at given frequency in Hertz */
9717232Sjoerg    sps = splclock();
981393Ssos
9919174Sbde    if (acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT)) {
1001393Ssos	/* enter list of waiting procs ??? */
10117232Sjoerg	splx(sps);
1021393Ssos	return;
1031393Ssos    }
10417232Sjoerg    splx(sps);
10517232Sjoerg    disable_intr();
1061393Ssos    outb(TIMER_CNTR2, (divisor & 0xff));	/* send lo byte */
1071393Ssos    outb(TIMER_CNTR2, (divisor >> 8));	/* send hi byte */
10817232Sjoerg    enable_intr();
1094Srgrimes
1104Srgrimes    /* turn the speaker on */
1111393Ssos    outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
1124Srgrimes
1134Srgrimes    /*
1144Srgrimes     * Set timeout to endtone function, then give up the timeslice.
1154Srgrimes     * This is so other processes can execute while the tone is being
1164Srgrimes     * emitted.
1174Srgrimes     */
1186152Sache    if (ticks > 0)
1196152Sache	tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
1201393Ssos    outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
12117232Sjoerg    sps = splclock();
1221393Ssos    release_timer2();
12317232Sjoerg    splx(sps);
1244Srgrimes}
1254Srgrimes
1264Srgrimes/* rest for given number of ticks */
12717232Sjoergstatic void
12817232Sjoergrest(ticks)
12917232Sjoerg	int	ticks;
1304Srgrimes{
1314Srgrimes    /*
1324Srgrimes     * Set timeout to endrest function, then give up the timeslice.
1334Srgrimes     * This is so other processes can execute while the rest is being
1344Srgrimes     * waited out.
1354Srgrimes     */
1364Srgrimes#ifdef DEBUG
137738Sache    (void) printf("rest: %d\n", ticks);
1384Srgrimes#endif /* DEBUG */
1396152Sache    if (ticks > 0)
1406152Sache	tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
1414Srgrimes}
1424Srgrimes
1434Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
1444Srgrimes *
1454Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
146738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
147738Sache * tracking facility are added.
1484Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible
1494Srgrimes * except possibly at physical block boundaries.
1504Srgrimes */
1514Srgrimes
1524Srgrimestypedef int	bool;
1534Srgrimes#define TRUE	1
1544Srgrimes#define FALSE	0
1554Srgrimes
1564Srgrimes#define dtoi(c)		((c) - '0')
1574Srgrimes
1584Srgrimesstatic int octave;	/* currently selected octave */
1594Srgrimesstatic int whole;	/* whole-note time at current tempo, in ticks */
1604Srgrimesstatic int value;	/* whole divisor for note time, quarter note = 1 */
1614Srgrimesstatic int fill;	/* controls spacing of notes */
1624Srgrimesstatic bool octtrack;	/* octave-tracking on? */
1634Srgrimesstatic bool octprefix;	/* override current octave-tracking state? */
1644Srgrimes
1654Srgrimes/*
1664Srgrimes * Magic number avoidance...
1674Srgrimes */
1684Srgrimes#define SECS_PER_MIN	60	/* seconds per minute */
1694Srgrimes#define WHOLE_NOTE	4	/* quarter notes per whole note */
1704Srgrimes#define MIN_VALUE	64	/* the most we can divide a note by */
1714Srgrimes#define DFLT_VALUE	4	/* default value (quarter-note) */
1724Srgrimes#define FILLTIME	8	/* for articulation, break note in parts */
1734Srgrimes#define STACCATO	6	/* 6/8 = 3/4 of note is filled */
1744Srgrimes#define NORMAL		7	/* 7/8ths of note interval is filled */
1754Srgrimes#define LEGATO		8	/* all of note interval is filled */
1764Srgrimes#define DFLT_OCTAVE	4	/* default octave */
1774Srgrimes#define MIN_TEMPO	32	/* minimum tempo */
1784Srgrimes#define DFLT_TEMPO	120	/* default tempo */
1794Srgrimes#define MAX_TEMPO	255	/* max tempo */
1804Srgrimes#define NUM_MULT	3	/* numerator of dot multiplier */
1814Srgrimes#define DENOM_MULT	2	/* denominator of dot multiplier */
1824Srgrimes
1834Srgrimes/* letter to half-tone:  A   B  C  D  E  F  G */
1844Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
1854Srgrimes
1864Srgrimes/*
1874Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies
1884Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
1894Srgrimes * our octave 0 is standard octave 2.
1904Srgrimes */
1914Srgrimes#define OCTAVE_NOTES	12	/* semitones per octave */
1924Srgrimesstatic int pitchtab[] =
1934Srgrimes{
1944Srgrimes/*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
1954Srgrimes/* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
1964Srgrimes/* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
1974Srgrimes/* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
1984Srgrimes/* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
1994Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
2004Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
2014Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
2024Srgrimes};
2034Srgrimes
20417232Sjoergstatic void
20517232Sjoergplayinit()
2064Srgrimes{
2074Srgrimes    octave = DFLT_OCTAVE;
208766Sache    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
2094Srgrimes    fill = NORMAL;
2104Srgrimes    value = DFLT_VALUE;
2114Srgrimes    octtrack = FALSE;
2124Srgrimes    octprefix = TRUE;	/* act as though there was an initial O(n) */
2134Srgrimes}
2144Srgrimes
2154Srgrimes/* play tone of proper duration for current rhythm signature */
21617232Sjoergstatic void
21717232Sjoergplaytone(pitch, value, sustain)
21817232Sjoerg	int	pitch, value, sustain;
2194Srgrimes{
2204Srgrimes    register int	sound, silence, snum = 1, sdenom = 1;
2214Srgrimes
2224Srgrimes    /* this weirdness avoids floating-point arithmetic */
2234Srgrimes    for (; sustain; sustain--)
2244Srgrimes    {
225738Sache	/* See the BUGS section in the man page for discussion */
2264Srgrimes	snum *= NUM_MULT;
2274Srgrimes	sdenom *= DENOM_MULT;
2284Srgrimes    }
2294Srgrimes
2308288Sdg    if (value == 0 || sdenom == 0)
2318288Sdg	return;
2328288Sdg
2334Srgrimes    if (pitch == -1)
2341016Sache	rest(whole * snum / (value * sdenom));
2354Srgrimes    else
2364Srgrimes    {
2374Srgrimes	sound = (whole * snum) / (value * sdenom)
2384Srgrimes		- (whole * (FILLTIME - fill)) / (value * FILLTIME);
2394Srgrimes	silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
2404Srgrimes
2414Srgrimes#ifdef DEBUG
242738Sache	(void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
2434Srgrimes			pitch, sound, silence);
2444Srgrimes#endif /* DEBUG */
2454Srgrimes
2461016Sache	tone(pitchtab[pitch], sound);
2474Srgrimes	if (fill != LEGATO)
2481016Sache	    rest(silence);
2494Srgrimes    }
2504Srgrimes}
2514Srgrimes
25217232Sjoergstatic int
25317232Sjoergabs(n)
25417232Sjoerg	int n;
2554Srgrimes{
2564Srgrimes    if (n < 0)
2574Srgrimes	return(-n);
2584Srgrimes    else
2594Srgrimes	return(n);
2604Srgrimes}
2614Srgrimes
2624Srgrimes/* interpret and play an item from a notation string */
26317232Sjoergstatic void
26417232Sjoergplaystring(cp, slen)
26517232Sjoerg	char	*cp;
26617232Sjoerg	size_t	slen;
2674Srgrimes{
268738Sache    int		pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
2694Srgrimes
2704Srgrimes#define GETNUM(cp, v)	for(v=0; isdigit(cp[1]) && slen > 0; ) \
2714Srgrimes				{v = v * 10 + (*++cp - '0'); slen--;}
2724Srgrimes    for (; slen--; cp++)
2734Srgrimes    {
2744Srgrimes	int		sustain, timeval, tempo;
2754Srgrimes	register char	c = toupper(*cp);
2764Srgrimes
2774Srgrimes#ifdef DEBUG
278738Sache	(void) printf("playstring: %c (%x)\n", c, c);
2794Srgrimes#endif /* DEBUG */
2804Srgrimes
2814Srgrimes	switch (c)
2824Srgrimes	{
2834Srgrimes	case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2844Srgrimes
2854Srgrimes	    /* compute pitch */
2864Srgrimes	    pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
2874Srgrimes
2884Srgrimes	    /* this may be followed by an accidental sign */
2894Srgrimes	    if (cp[1] == '#' || cp[1] == '+')
2904Srgrimes	    {
2914Srgrimes		++pitch;
2924Srgrimes		++cp;
2934Srgrimes		slen--;
2944Srgrimes	    }
2954Srgrimes	    else if (cp[1] == '-')
2964Srgrimes	    {
2974Srgrimes		--pitch;
2984Srgrimes		++cp;
2994Srgrimes		slen--;
3004Srgrimes	    }
3014Srgrimes
3024Srgrimes	    /*
3034Srgrimes	     * If octave-tracking mode is on, and there has been no octave-
3044Srgrimes	     * setting prefix, find the version of the current letter note
3054Srgrimes	     * closest to the last regardless of octave.
3064Srgrimes	     */
3074Srgrimes	    if (octtrack && !octprefix)
3084Srgrimes	    {
3094Srgrimes		if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
3104Srgrimes		{
3114Srgrimes		    ++octave;
3124Srgrimes		    pitch += OCTAVE_NOTES;
3134Srgrimes		}
3144Srgrimes
3154Srgrimes		if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
3164Srgrimes		{
3174Srgrimes		    --octave;
3184Srgrimes		    pitch -= OCTAVE_NOTES;
3194Srgrimes		}
3204Srgrimes	    }
3214Srgrimes	    octprefix = FALSE;
3224Srgrimes	    lastpitch = pitch;
3234Srgrimes
3244Srgrimes	    /* ...which may in turn be followed by an override time value */
3254Srgrimes	    GETNUM(cp, timeval);
3264Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
3274Srgrimes		timeval = value;
3284Srgrimes
3294Srgrimes	    /* ...and/or sustain dots */
3304Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3314Srgrimes	    {
3324Srgrimes		slen--;
3334Srgrimes		sustain++;
3344Srgrimes	    }
3354Srgrimes
336738Sache	    /* ...and/or a slur mark */
337738Sache	    oldfill = fill;
338738Sache	    if (cp[1] == '_')
339738Sache	    {
340738Sache		fill = LEGATO;
341738Sache		++cp;
342738Sache		slen--;
343738Sache	    }
344738Sache
3454Srgrimes	    /* time to emit the actual tone */
3461016Sache	    playtone(pitch, timeval, sustain);
347738Sache
348738Sache	    fill = oldfill;
3494Srgrimes	    break;
3504Srgrimes
3514Srgrimes	case 'O':
3524Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
3534Srgrimes	    {
3544Srgrimes		octprefix = octtrack = FALSE;
3554Srgrimes		++cp;
3564Srgrimes		slen--;
3574Srgrimes	    }
3584Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
3594Srgrimes	    {
3604Srgrimes		octtrack = TRUE;
3614Srgrimes		++cp;
3624Srgrimes		slen--;
3634Srgrimes	    }
3644Srgrimes	    else
3654Srgrimes	    {
3664Srgrimes		GETNUM(cp, octave);
3673593Sache		if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
3684Srgrimes		    octave = DFLT_OCTAVE;
3694Srgrimes		octprefix = TRUE;
3704Srgrimes	    }
3714Srgrimes	    break;
3724Srgrimes
3734Srgrimes	case '>':
3743593Sache	    if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
3754Srgrimes		octave++;
3764Srgrimes	    octprefix = TRUE;
3774Srgrimes	    break;
3784Srgrimes
3794Srgrimes	case '<':
3804Srgrimes	    if (octave > 0)
3814Srgrimes		octave--;
3824Srgrimes	    octprefix = TRUE;
3834Srgrimes	    break;
3844Srgrimes
3854Srgrimes	case 'N':
3864Srgrimes	    GETNUM(cp, pitch);
3874Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3884Srgrimes	    {
3894Srgrimes		slen--;
3904Srgrimes		sustain++;
3914Srgrimes	    }
392738Sache	    oldfill = fill;
393738Sache	    if (cp[1] == '_')
394738Sache	    {
395738Sache		fill = LEGATO;
396738Sache		++cp;
397738Sache		slen--;
398738Sache	    }
3991016Sache	    playtone(pitch - 1, value, sustain);
400738Sache	    fill = oldfill;
4014Srgrimes	    break;
4024Srgrimes
4034Srgrimes	case 'L':
4044Srgrimes	    GETNUM(cp, value);
4054Srgrimes	    if (value <= 0 || value > MIN_VALUE)
4064Srgrimes		value = DFLT_VALUE;
4074Srgrimes	    break;
4084Srgrimes
4094Srgrimes	case 'P':
4104Srgrimes	case '~':
4114Srgrimes	    /* this may be followed by an override time value */
4124Srgrimes	    GETNUM(cp, timeval);
4134Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
4144Srgrimes		timeval = value;
4154Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
4164Srgrimes	    {
4174Srgrimes		slen--;
4184Srgrimes		sustain++;
4194Srgrimes	    }
4201016Sache	    playtone(-1, timeval, sustain);
4214Srgrimes	    break;
4224Srgrimes
4234Srgrimes	case 'T':
4244Srgrimes	    GETNUM(cp, tempo);
4254Srgrimes	    if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
4264Srgrimes		tempo = DFLT_TEMPO;
427766Sache	    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
4284Srgrimes	    break;
4294Srgrimes
4304Srgrimes	case 'M':
4314Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
4324Srgrimes	    {
4334Srgrimes		fill = NORMAL;
4344Srgrimes		++cp;
4354Srgrimes		slen--;
4364Srgrimes	    }
4374Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
4384Srgrimes	    {
4394Srgrimes		fill = LEGATO;
4404Srgrimes		++cp;
4414Srgrimes		slen--;
4424Srgrimes	    }
4434Srgrimes	    else if (cp[1] == 'S' || cp[1] == 's')
4444Srgrimes	    {
4454Srgrimes		fill = STACCATO;
4464Srgrimes		++cp;
4474Srgrimes		slen--;
4484Srgrimes	    }
4494Srgrimes	    break;
4504Srgrimes	}
4514Srgrimes    }
4524Srgrimes}
4534Srgrimes
4544Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
4554Srgrimes *
4564Srgrimes * This section implements driver hooks to run playstring() and the tone(),
4574Srgrimes * endtone(), and rest() functions defined above.
4584Srgrimes */
4594Srgrimes
460738Sachestatic int spkr_active = FALSE; /* exclusion flag */
46160038Sphkstatic char *spkr_inbuf;  /* incoming buf */
4624Srgrimes
46317232Sjoergint
46417232Sjoergspkropen(dev, flags, fmt, p)
46517232Sjoerg	dev_t		dev;
46617232Sjoerg	int		flags;
46717232Sjoerg	int		fmt;
46817232Sjoerg	struct proc	*p;
4694Srgrimes{
4704Srgrimes#ifdef DEBUG
47149982Sbillf    (void) printf("spkropen: entering with dev = %s\n", devtoname(dev));
4724Srgrimes#endif /* DEBUG */
4734Srgrimes
4744Srgrimes    if (minor(dev) != 0)
4754Srgrimes	return(ENXIO);
4764Srgrimes    else if (spkr_active)
4774Srgrimes	return(EBUSY);
4784Srgrimes    else
4794Srgrimes    {
480738Sache#ifdef DEBUG
481738Sache	(void) printf("spkropen: about to perform play initialization\n");
482738Sache#endif /* DEBUG */
4834Srgrimes	playinit();
48460038Sphk	spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK);
485738Sache	spkr_active = TRUE;
486738Sache	return(0);
4874Srgrimes    }
4884Srgrimes}
4894Srgrimes
49017232Sjoergint
49117232Sjoergspkrwrite(dev, uio, ioflag)
49217232Sjoerg	dev_t		dev;
49317232Sjoerg	struct uio	*uio;
49417232Sjoerg	int		ioflag;
4954Srgrimes{
4964Srgrimes#ifdef DEBUG
49749982Sbillf    printf("spkrwrite: entering with dev = %s, count = %d\n",
49849982Sbillf		devtoname(dev), uio->uio_resid);
4994Srgrimes#endif /* DEBUG */
5004Srgrimes
5014Srgrimes    if (minor(dev) != 0)
5024Srgrimes	return(ENXIO);
50317803Speter    else if (uio->uio_resid > (DEV_BSIZE - 1))     /* prevent system crashes */
504738Sache	return(E2BIG);
5054Srgrimes    else
5064Srgrimes    {
507738Sache	unsigned n;
508738Sache	char *cp;
509738Sache	int error;
510738Sache
511738Sache	n = uio->uio_resid;
51260038Sphk	cp = spkr_inbuf;
51317803Speter	error = uiomove(cp, n, uio);
51417803Speter	if (!error) {
51517803Speter		cp[n] = '\0';
5161016Sache		playstring(cp, n);
51717803Speter	}
5184Srgrimes	return(error);
5194Srgrimes    }
5204Srgrimes}
5214Srgrimes
52217232Sjoergint
52317232Sjoergspkrclose(dev, flags, fmt, p)
52417232Sjoerg	dev_t		dev;
52517232Sjoerg	int		flags;
52617232Sjoerg	int		fmt;
52717232Sjoerg	struct proc	*p;
5284Srgrimes{
5294Srgrimes#ifdef DEBUG
53049982Sbillf    (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev));
5314Srgrimes#endif /* DEBUG */
5324Srgrimes
5334Srgrimes    if (minor(dev) != 0)
5344Srgrimes	return(ENXIO);
5354Srgrimes    else
5364Srgrimes    {
537766Sache	wakeup((caddr_t)&endtone);
538766Sache	wakeup((caddr_t)&endrest);
53960038Sphk	free(spkr_inbuf, M_SPKR);
540738Sache	spkr_active = FALSE;
541738Sache	return(0);
5424Srgrimes    }
5434Srgrimes}
5444Srgrimes
54517232Sjoergint
54617232Sjoergspkrioctl(dev, cmd, cmdarg, flags, p)
54717232Sjoerg	dev_t		dev;
54838505Sbde	unsigned long	cmd;
54917232Sjoerg	caddr_t		cmdarg;
55017232Sjoerg	int		flags;
55117232Sjoerg	struct proc	*p;
5524Srgrimes{
5534Srgrimes#ifdef DEBUG
55450253Sbde    (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n",
55550253Sbde    	devtoname(dev), cmd);
5564Srgrimes#endif /* DEBUG */
5574Srgrimes
5584Srgrimes    if (minor(dev) != 0)
5594Srgrimes	return(ENXIO);
5604Srgrimes    else if (cmd == SPKRTONE)
5614Srgrimes    {
5624Srgrimes	tone_t	*tp = (tone_t *)cmdarg;
5634Srgrimes
5644Srgrimes	if (tp->frequency == 0)
5651016Sache	    rest(tp->duration);
5664Srgrimes	else
5671016Sache	    tone(tp->frequency, tp->duration);
5681016Sache	return 0;
5694Srgrimes    }
5704Srgrimes    else if (cmd == SPKRTUNE)
5714Srgrimes    {
5724Srgrimes	tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
5734Srgrimes	tone_t ttp;
5744Srgrimes	int error;
5754Srgrimes
5764Srgrimes	for (; ; tp++) {
5774Srgrimes	    error = copyin(tp, &ttp, sizeof(tone_t));
5784Srgrimes	    if (error)
5794Srgrimes		    return(error);
5804Srgrimes	    if (ttp.duration == 0)
5814Srgrimes		    break;
5824Srgrimes	    if (ttp.frequency == 0)
5831016Sache		 rest(ttp.duration);
5844Srgrimes	    else
5851016Sache		 tone(ttp.frequency, ttp.duration);
5864Srgrimes	}
587738Sache	return(0);
5884Srgrimes    }
589738Sache    return(EINVAL);
5904Srgrimes}
5914Srgrimes
59217232Sjoergstatic void
59317232Sjoergspkr_drvinit(void *unused)
59412502Sjulian{
59550254Sphk	make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "speaker");
59612502Sjulian}
59712517Sjulian
59812517SjulianSYSINIT(spkrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,spkr_drvinit,NULL)
59912517Sjulian
60061994Smsmith/*
60161994Smsmith * Install placeholder to claim the resources owned by the
60261994Smsmith * AT tone generator.
60361994Smsmith */
60461994Smsmithstatic struct isa_pnp_id atspeaker_ids[] = {
60561994Smsmith	{ 0x0008d041 /* PNP0800 */, "AT speaker" },
60661994Smsmith	{ 0 }
60761994Smsmith};
60812517Sjulian
60961994Smsmithstatic int
61061994Smsmithatspeaker_probe(device_t dev)
61161994Smsmith{
61261994Smsmith	return(ISA_PNP_PROBE(device_get_parent(dev), dev, atspeaker_ids));
61361994Smsmith}
61461994Smsmith
61561994Smsmithstatic int
61661994Smsmithatspeaker_attach(device_t dev)
61761994Smsmith{
61861994Smsmith	return(0);
61961994Smsmith}
62061994Smsmith
62161994Smsmithstatic device_method_t atspeaker_methods[] = {
62261994Smsmith	/* Device interface */
62361994Smsmith	DEVMETHOD(device_probe,		atspeaker_probe),
62461994Smsmith	DEVMETHOD(device_attach,	atspeaker_attach),
62561994Smsmith	DEVMETHOD(device_detach,	bus_generic_detach),
62661994Smsmith	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
62761994Smsmith	DEVMETHOD(device_suspend,	bus_generic_suspend),
62861994Smsmith	DEVMETHOD(device_resume,	bus_generic_resume),
62961994Smsmith	{ 0, 0 }
63061994Smsmith};
63161994Smsmith
63261994Smsmithstatic driver_t atspeaker_driver = {
63361994Smsmith	"atspeaker",
63461994Smsmith	atspeaker_methods,
63561994Smsmith	1,		/* no softc */
63661994Smsmith};
63761994Smsmith
63861994Smsmithstatic devclass_t atspeaker_devclass;
63961994Smsmith
64061994SmsmithDRIVER_MODULE(atspeaker, isa, atspeaker_driver, atspeaker_devclass, 0, 0);
64161994Smsmith
6424Srgrimes/* spkr.c ends here */
643