spkr.c revision 83366
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 83366 2001-09-12 08:38:13Z julian $
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};
4612675Sjulian
4769774Sphkstatic MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer");
4860038Sphk
494Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE *************************
504Srgrimes *
514Srgrimes * This section defines a function tone() which causes a tone of given
5219174Sbde * frequency and duration from the ISA console speaker.
534Srgrimes * Another function endtone() is defined to force sound off, and there is
544Srgrimes * also a rest() entry point to do pauses.
554Srgrimes *
564Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and
5719174Sbde * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The
584Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is
594Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired.
604Srgrimes */
614Srgrimes
624Srgrimes/*
6319174Sbde * PPI control values.
6419174Sbde * XXX should be in a header and used in clock.c.
654Srgrimes */
664Srgrimes#define PPI_SPKR	0x03	/* turn these PPI bits on to pass sound */
674Srgrimes
68766Sache#define SPKRPRI PSOCK
69766Sachestatic char endtone, endrest;
704Srgrimes
7112854Sbdestatic void tone __P((unsigned int thz, unsigned int ticks));
7212854Sbdestatic void rest __P((int ticks));
7312854Sbdestatic void playinit __P((void));
7412854Sbdestatic void playtone __P((int pitch, int value, int sustain));
7512854Sbdestatic int abs __P((int n));
7612854Sbdestatic void playstring __P((char *cp, size_t slen));
7712854Sbde
78766Sache/* emit tone of frequency thz for given number of ticks */
7917232Sjoergstatic void
8017232Sjoergtone(thz, ticks)
8117232Sjoerg	unsigned int thz, ticks;
824Srgrimes{
838288Sdg    unsigned int divisor;
841016Sache    int sps;
854Srgrimes
868288Sdg    if (thz <= 0)
878288Sdg	return;
888288Sdg
8919174Sbde    divisor = timer_freq / thz;
908288Sdg
914Srgrimes#ifdef DEBUG
92766Sache    (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
934Srgrimes#endif /* DEBUG */
944Srgrimes
954Srgrimes    /* set timer to generate clicks at given frequency in Hertz */
9617232Sjoerg    sps = splclock();
971393Ssos
9819174Sbde    if (acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT)) {
991393Ssos	/* enter list of waiting procs ??? */
10017232Sjoerg	splx(sps);
1011393Ssos	return;
1021393Ssos    }
10317232Sjoerg    splx(sps);
10417232Sjoerg    disable_intr();
1051393Ssos    outb(TIMER_CNTR2, (divisor & 0xff));	/* send lo byte */
1061393Ssos    outb(TIMER_CNTR2, (divisor >> 8));	/* send hi byte */
10717232Sjoerg    enable_intr();
1084Srgrimes
1094Srgrimes    /* turn the speaker on */
1101393Ssos    outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
1114Srgrimes
1124Srgrimes    /*
1134Srgrimes     * Set timeout to endtone function, then give up the timeslice.
1144Srgrimes     * This is so other processes can execute while the tone is being
1154Srgrimes     * emitted.
1164Srgrimes     */
1176152Sache    if (ticks > 0)
1186152Sache	tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
1191393Ssos    outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
12017232Sjoerg    sps = splclock();
1211393Ssos    release_timer2();
12217232Sjoerg    splx(sps);
1234Srgrimes}
1244Srgrimes
1254Srgrimes/* rest for given number of ticks */
12617232Sjoergstatic void
12717232Sjoergrest(ticks)
12817232Sjoerg	int	ticks;
1294Srgrimes{
1304Srgrimes    /*
1314Srgrimes     * Set timeout to endrest function, then give up the timeslice.
1324Srgrimes     * This is so other processes can execute while the rest is being
1334Srgrimes     * waited out.
1344Srgrimes     */
1354Srgrimes#ifdef DEBUG
136738Sache    (void) printf("rest: %d\n", ticks);
1374Srgrimes#endif /* DEBUG */
1386152Sache    if (ticks > 0)
1396152Sache	tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
1404Srgrimes}
1414Srgrimes
1424Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
1434Srgrimes *
1444Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
145738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
146738Sache * tracking facility are added.
1474Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible
1484Srgrimes * except possibly at physical block boundaries.
1494Srgrimes */
1504Srgrimes
1514Srgrimestypedef int	bool;
1524Srgrimes#define TRUE	1
1534Srgrimes#define FALSE	0
1544Srgrimes
1554Srgrimes#define dtoi(c)		((c) - '0')
1564Srgrimes
1574Srgrimesstatic int octave;	/* currently selected octave */
1584Srgrimesstatic int whole;	/* whole-note time at current tempo, in ticks */
1594Srgrimesstatic int value;	/* whole divisor for note time, quarter note = 1 */
1604Srgrimesstatic int fill;	/* controls spacing of notes */
1614Srgrimesstatic bool octtrack;	/* octave-tracking on? */
1624Srgrimesstatic bool octprefix;	/* override current octave-tracking state? */
1634Srgrimes
1644Srgrimes/*
1654Srgrimes * Magic number avoidance...
1664Srgrimes */
1674Srgrimes#define SECS_PER_MIN	60	/* seconds per minute */
1684Srgrimes#define WHOLE_NOTE	4	/* quarter notes per whole note */
1694Srgrimes#define MIN_VALUE	64	/* the most we can divide a note by */
1704Srgrimes#define DFLT_VALUE	4	/* default value (quarter-note) */
1714Srgrimes#define FILLTIME	8	/* for articulation, break note in parts */
1724Srgrimes#define STACCATO	6	/* 6/8 = 3/4 of note is filled */
1734Srgrimes#define NORMAL		7	/* 7/8ths of note interval is filled */
1744Srgrimes#define LEGATO		8	/* all of note interval is filled */
1754Srgrimes#define DFLT_OCTAVE	4	/* default octave */
1764Srgrimes#define MIN_TEMPO	32	/* minimum tempo */
1774Srgrimes#define DFLT_TEMPO	120	/* default tempo */
1784Srgrimes#define MAX_TEMPO	255	/* max tempo */
1794Srgrimes#define NUM_MULT	3	/* numerator of dot multiplier */
1804Srgrimes#define DENOM_MULT	2	/* denominator of dot multiplier */
1814Srgrimes
1824Srgrimes/* letter to half-tone:  A   B  C  D  E  F  G */
1834Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
1844Srgrimes
1854Srgrimes/*
1864Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies
1874Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
1884Srgrimes * our octave 0 is standard octave 2.
1894Srgrimes */
1904Srgrimes#define OCTAVE_NOTES	12	/* semitones per octave */
1914Srgrimesstatic int pitchtab[] =
1924Srgrimes{
1934Srgrimes/*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
1944Srgrimes/* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
1954Srgrimes/* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
1964Srgrimes/* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
1974Srgrimes/* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
1984Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
1994Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
2004Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
2014Srgrimes};
2024Srgrimes
20317232Sjoergstatic void
20417232Sjoergplayinit()
2054Srgrimes{
2064Srgrimes    octave = DFLT_OCTAVE;
207766Sache    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
2084Srgrimes    fill = NORMAL;
2094Srgrimes    value = DFLT_VALUE;
2104Srgrimes    octtrack = FALSE;
2114Srgrimes    octprefix = TRUE;	/* act as though there was an initial O(n) */
2124Srgrimes}
2134Srgrimes
2144Srgrimes/* play tone of proper duration for current rhythm signature */
21517232Sjoergstatic void
21617232Sjoergplaytone(pitch, value, sustain)
21717232Sjoerg	int	pitch, value, sustain;
2184Srgrimes{
2194Srgrimes    register int	sound, silence, snum = 1, sdenom = 1;
2204Srgrimes
2214Srgrimes    /* this weirdness avoids floating-point arithmetic */
2224Srgrimes    for (; sustain; sustain--)
2234Srgrimes    {
224738Sache	/* See the BUGS section in the man page for discussion */
2254Srgrimes	snum *= NUM_MULT;
2264Srgrimes	sdenom *= DENOM_MULT;
2274Srgrimes    }
2284Srgrimes
2298288Sdg    if (value == 0 || sdenom == 0)
2308288Sdg	return;
2318288Sdg
2324Srgrimes    if (pitch == -1)
2331016Sache	rest(whole * snum / (value * sdenom));
2344Srgrimes    else
2354Srgrimes    {
2364Srgrimes	sound = (whole * snum) / (value * sdenom)
2374Srgrimes		- (whole * (FILLTIME - fill)) / (value * FILLTIME);
2384Srgrimes	silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
2394Srgrimes
2404Srgrimes#ifdef DEBUG
241738Sache	(void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
2424Srgrimes			pitch, sound, silence);
2434Srgrimes#endif /* DEBUG */
2444Srgrimes
2451016Sache	tone(pitchtab[pitch], sound);
2464Srgrimes	if (fill != LEGATO)
2471016Sache	    rest(silence);
2484Srgrimes    }
2494Srgrimes}
2504Srgrimes
25117232Sjoergstatic int
25217232Sjoergabs(n)
25317232Sjoerg	int n;
2544Srgrimes{
2554Srgrimes    if (n < 0)
2564Srgrimes	return(-n);
2574Srgrimes    else
2584Srgrimes	return(n);
2594Srgrimes}
2604Srgrimes
2614Srgrimes/* interpret and play an item from a notation string */
26217232Sjoergstatic void
26317232Sjoergplaystring(cp, slen)
26417232Sjoerg	char	*cp;
26517232Sjoerg	size_t	slen;
2664Srgrimes{
267738Sache    int		pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
2684Srgrimes
2694Srgrimes#define GETNUM(cp, v)	for(v=0; isdigit(cp[1]) && slen > 0; ) \
2704Srgrimes				{v = v * 10 + (*++cp - '0'); slen--;}
2714Srgrimes    for (; slen--; cp++)
2724Srgrimes    {
2734Srgrimes	int		sustain, timeval, tempo;
2744Srgrimes	register char	c = toupper(*cp);
2754Srgrimes
2764Srgrimes#ifdef DEBUG
277738Sache	(void) printf("playstring: %c (%x)\n", c, c);
2784Srgrimes#endif /* DEBUG */
2794Srgrimes
2804Srgrimes	switch (c)
2814Srgrimes	{
2824Srgrimes	case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2834Srgrimes
2844Srgrimes	    /* compute pitch */
2854Srgrimes	    pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
2864Srgrimes
2874Srgrimes	    /* this may be followed by an accidental sign */
2884Srgrimes	    if (cp[1] == '#' || cp[1] == '+')
2894Srgrimes	    {
2904Srgrimes		++pitch;
2914Srgrimes		++cp;
2924Srgrimes		slen--;
2934Srgrimes	    }
2944Srgrimes	    else if (cp[1] == '-')
2954Srgrimes	    {
2964Srgrimes		--pitch;
2974Srgrimes		++cp;
2984Srgrimes		slen--;
2994Srgrimes	    }
3004Srgrimes
3014Srgrimes	    /*
3024Srgrimes	     * If octave-tracking mode is on, and there has been no octave-
3034Srgrimes	     * setting prefix, find the version of the current letter note
3044Srgrimes	     * closest to the last regardless of octave.
3054Srgrimes	     */
3064Srgrimes	    if (octtrack && !octprefix)
3074Srgrimes	    {
3084Srgrimes		if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
3094Srgrimes		{
3104Srgrimes		    ++octave;
3114Srgrimes		    pitch += OCTAVE_NOTES;
3124Srgrimes		}
3134Srgrimes
3144Srgrimes		if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
3154Srgrimes		{
3164Srgrimes		    --octave;
3174Srgrimes		    pitch -= OCTAVE_NOTES;
3184Srgrimes		}
3194Srgrimes	    }
3204Srgrimes	    octprefix = FALSE;
3214Srgrimes	    lastpitch = pitch;
3224Srgrimes
3234Srgrimes	    /* ...which may in turn be followed by an override time value */
3244Srgrimes	    GETNUM(cp, timeval);
3254Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
3264Srgrimes		timeval = value;
3274Srgrimes
3284Srgrimes	    /* ...and/or sustain dots */
3294Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3304Srgrimes	    {
3314Srgrimes		slen--;
3324Srgrimes		sustain++;
3334Srgrimes	    }
3344Srgrimes
335738Sache	    /* ...and/or a slur mark */
336738Sache	    oldfill = fill;
337738Sache	    if (cp[1] == '_')
338738Sache	    {
339738Sache		fill = LEGATO;
340738Sache		++cp;
341738Sache		slen--;
342738Sache	    }
343738Sache
3444Srgrimes	    /* time to emit the actual tone */
3451016Sache	    playtone(pitch, timeval, sustain);
346738Sache
347738Sache	    fill = oldfill;
3484Srgrimes	    break;
3494Srgrimes
3504Srgrimes	case 'O':
3514Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
3524Srgrimes	    {
3534Srgrimes		octprefix = octtrack = FALSE;
3544Srgrimes		++cp;
3554Srgrimes		slen--;
3564Srgrimes	    }
3574Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
3584Srgrimes	    {
3594Srgrimes		octtrack = TRUE;
3604Srgrimes		++cp;
3614Srgrimes		slen--;
3624Srgrimes	    }
3634Srgrimes	    else
3644Srgrimes	    {
3654Srgrimes		GETNUM(cp, octave);
3663593Sache		if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
3674Srgrimes		    octave = DFLT_OCTAVE;
3684Srgrimes		octprefix = TRUE;
3694Srgrimes	    }
3704Srgrimes	    break;
3714Srgrimes
3724Srgrimes	case '>':
3733593Sache	    if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
3744Srgrimes		octave++;
3754Srgrimes	    octprefix = TRUE;
3764Srgrimes	    break;
3774Srgrimes
3784Srgrimes	case '<':
3794Srgrimes	    if (octave > 0)
3804Srgrimes		octave--;
3814Srgrimes	    octprefix = TRUE;
3824Srgrimes	    break;
3834Srgrimes
3844Srgrimes	case 'N':
3854Srgrimes	    GETNUM(cp, pitch);
3864Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
3874Srgrimes	    {
3884Srgrimes		slen--;
3894Srgrimes		sustain++;
3904Srgrimes	    }
391738Sache	    oldfill = fill;
392738Sache	    if (cp[1] == '_')
393738Sache	    {
394738Sache		fill = LEGATO;
395738Sache		++cp;
396738Sache		slen--;
397738Sache	    }
3981016Sache	    playtone(pitch - 1, value, sustain);
399738Sache	    fill = oldfill;
4004Srgrimes	    break;
4014Srgrimes
4024Srgrimes	case 'L':
4034Srgrimes	    GETNUM(cp, value);
4044Srgrimes	    if (value <= 0 || value > MIN_VALUE)
4054Srgrimes		value = DFLT_VALUE;
4064Srgrimes	    break;
4074Srgrimes
4084Srgrimes	case 'P':
4094Srgrimes	case '~':
4104Srgrimes	    /* this may be followed by an override time value */
4114Srgrimes	    GETNUM(cp, timeval);
4124Srgrimes	    if (timeval <= 0 || timeval > MIN_VALUE)
4134Srgrimes		timeval = value;
4144Srgrimes	    for (sustain = 0; cp[1] == '.'; cp++)
4154Srgrimes	    {
4164Srgrimes		slen--;
4174Srgrimes		sustain++;
4184Srgrimes	    }
4191016Sache	    playtone(-1, timeval, sustain);
4204Srgrimes	    break;
4214Srgrimes
4224Srgrimes	case 'T':
4234Srgrimes	    GETNUM(cp, tempo);
4244Srgrimes	    if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
4254Srgrimes		tempo = DFLT_TEMPO;
426766Sache	    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
4274Srgrimes	    break;
4284Srgrimes
4294Srgrimes	case 'M':
4304Srgrimes	    if (cp[1] == 'N' || cp[1] == 'n')
4314Srgrimes	    {
4324Srgrimes		fill = NORMAL;
4334Srgrimes		++cp;
4344Srgrimes		slen--;
4354Srgrimes	    }
4364Srgrimes	    else if (cp[1] == 'L' || cp[1] == 'l')
4374Srgrimes	    {
4384Srgrimes		fill = LEGATO;
4394Srgrimes		++cp;
4404Srgrimes		slen--;
4414Srgrimes	    }
4424Srgrimes	    else if (cp[1] == 'S' || cp[1] == 's')
4434Srgrimes	    {
4444Srgrimes		fill = STACCATO;
4454Srgrimes		++cp;
4464Srgrimes		slen--;
4474Srgrimes	    }
4484Srgrimes	    break;
4494Srgrimes	}
4504Srgrimes    }
4514Srgrimes}
4524Srgrimes
4534Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
4544Srgrimes *
4554Srgrimes * This section implements driver hooks to run playstring() and the tone(),
4564Srgrimes * endtone(), and rest() functions defined above.
4574Srgrimes */
4584Srgrimes
459738Sachestatic int spkr_active = FALSE; /* exclusion flag */
46060038Sphkstatic char *spkr_inbuf;  /* incoming buf */
4614Srgrimes
46217232Sjoergint
46383366Sjulianspkropen(dev, flags, fmt, td)
46417232Sjoerg	dev_t		dev;
46517232Sjoerg	int		flags;
46617232Sjoerg	int		fmt;
46783366Sjulian	struct thread	*td;
4684Srgrimes{
4694Srgrimes#ifdef DEBUG
47049982Sbillf    (void) printf("spkropen: entering with dev = %s\n", devtoname(dev));
4714Srgrimes#endif /* DEBUG */
4724Srgrimes
4734Srgrimes    if (minor(dev) != 0)
4744Srgrimes	return(ENXIO);
4754Srgrimes    else if (spkr_active)
4764Srgrimes	return(EBUSY);
4774Srgrimes    else
4784Srgrimes    {
479738Sache#ifdef DEBUG
480738Sache	(void) printf("spkropen: about to perform play initialization\n");
481738Sache#endif /* DEBUG */
4824Srgrimes	playinit();
48360038Sphk	spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK);
484738Sache	spkr_active = TRUE;
485738Sache	return(0);
4864Srgrimes    }
4874Srgrimes}
4884Srgrimes
48917232Sjoergint
49017232Sjoergspkrwrite(dev, uio, ioflag)
49117232Sjoerg	dev_t		dev;
49217232Sjoerg	struct uio	*uio;
49317232Sjoerg	int		ioflag;
4944Srgrimes{
4954Srgrimes#ifdef DEBUG
49649982Sbillf    printf("spkrwrite: entering with dev = %s, count = %d\n",
49749982Sbillf		devtoname(dev), uio->uio_resid);
4984Srgrimes#endif /* DEBUG */
4994Srgrimes
5004Srgrimes    if (minor(dev) != 0)
5014Srgrimes	return(ENXIO);
50217803Speter    else if (uio->uio_resid > (DEV_BSIZE - 1))     /* prevent system crashes */
503738Sache	return(E2BIG);
5044Srgrimes    else
5054Srgrimes    {
506738Sache	unsigned n;
507738Sache	char *cp;
508738Sache	int error;
509738Sache
510738Sache	n = uio->uio_resid;
51160038Sphk	cp = spkr_inbuf;
51217803Speter	error = uiomove(cp, n, uio);
51317803Speter	if (!error) {
51417803Speter		cp[n] = '\0';
5151016Sache		playstring(cp, n);
51617803Speter	}
5174Srgrimes	return(error);
5184Srgrimes    }
5194Srgrimes}
5204Srgrimes
52117232Sjoergint
52283366Sjulianspkrclose(dev, flags, fmt, td)
52317232Sjoerg	dev_t		dev;
52417232Sjoerg	int		flags;
52517232Sjoerg	int		fmt;
52683366Sjulian	struct thread	*td;
5274Srgrimes{
5284Srgrimes#ifdef DEBUG
52949982Sbillf    (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev));
5304Srgrimes#endif /* DEBUG */
5314Srgrimes
5324Srgrimes    if (minor(dev) != 0)
5334Srgrimes	return(ENXIO);
5344Srgrimes    else
5354Srgrimes    {
536766Sache	wakeup((caddr_t)&endtone);
537766Sache	wakeup((caddr_t)&endrest);
53860038Sphk	free(spkr_inbuf, M_SPKR);
539738Sache	spkr_active = FALSE;
540738Sache	return(0);
5414Srgrimes    }
5424Srgrimes}
5434Srgrimes
54417232Sjoergint
54583366Sjulianspkrioctl(dev, cmd, cmdarg, flags, td)
54617232Sjoerg	dev_t		dev;
54738505Sbde	unsigned long	cmd;
54817232Sjoerg	caddr_t		cmdarg;
54917232Sjoerg	int		flags;
55083366Sjulian	struct thread	*td;
5514Srgrimes{
5524Srgrimes#ifdef DEBUG
55350253Sbde    (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n",
55450253Sbde    	devtoname(dev), cmd);
5554Srgrimes#endif /* DEBUG */
5564Srgrimes
5574Srgrimes    if (minor(dev) != 0)
5584Srgrimes	return(ENXIO);
5594Srgrimes    else if (cmd == SPKRTONE)
5604Srgrimes    {
5614Srgrimes	tone_t	*tp = (tone_t *)cmdarg;
5624Srgrimes
5634Srgrimes	if (tp->frequency == 0)
5641016Sache	    rest(tp->duration);
5654Srgrimes	else
5661016Sache	    tone(tp->frequency, tp->duration);
5671016Sache	return 0;
5684Srgrimes    }
5694Srgrimes    else if (cmd == SPKRTUNE)
5704Srgrimes    {
5714Srgrimes	tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
5724Srgrimes	tone_t ttp;
5734Srgrimes	int error;
5744Srgrimes
5754Srgrimes	for (; ; tp++) {
5764Srgrimes	    error = copyin(tp, &ttp, sizeof(tone_t));
5774Srgrimes	    if (error)
5784Srgrimes		    return(error);
5794Srgrimes	    if (ttp.duration == 0)
5804Srgrimes		    break;
5814Srgrimes	    if (ttp.frequency == 0)
5821016Sache		 rest(ttp.duration);
5834Srgrimes	    else
5841016Sache		 tone(ttp.frequency, ttp.duration);
5854Srgrimes	}
586738Sache	return(0);
5874Srgrimes    }
588738Sache    return(EINVAL);
5894Srgrimes}
5904Srgrimes
59117232Sjoergstatic void
59217232Sjoergspkr_drvinit(void *unused)
59312502Sjulian{
59450254Sphk	make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "speaker");
59512502Sjulian}
59612517Sjulian
59712517SjulianSYSINIT(spkrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,spkr_drvinit,NULL)
59812517Sjulian
59961994Smsmith/*
60061994Smsmith * Install placeholder to claim the resources owned by the
60161994Smsmith * AT tone generator.
60261994Smsmith */
60361994Smsmithstatic struct isa_pnp_id atspeaker_ids[] = {
60461994Smsmith	{ 0x0008d041 /* PNP0800 */, "AT speaker" },
60561994Smsmith	{ 0 }
60661994Smsmith};
60712517Sjulian
60861994Smsmithstatic int
60961994Smsmithatspeaker_probe(device_t dev)
61061994Smsmith{
61161994Smsmith	return(ISA_PNP_PROBE(device_get_parent(dev), dev, atspeaker_ids));
61261994Smsmith}
61361994Smsmith
61461994Smsmithstatic int
61561994Smsmithatspeaker_attach(device_t dev)
61661994Smsmith{
61761994Smsmith	return(0);
61861994Smsmith}
61961994Smsmith
62061994Smsmithstatic device_method_t atspeaker_methods[] = {
62161994Smsmith	/* Device interface */
62261994Smsmith	DEVMETHOD(device_probe,		atspeaker_probe),
62361994Smsmith	DEVMETHOD(device_attach,	atspeaker_attach),
62461994Smsmith	DEVMETHOD(device_detach,	bus_generic_detach),
62561994Smsmith	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
62661994Smsmith	DEVMETHOD(device_suspend,	bus_generic_suspend),
62761994Smsmith	DEVMETHOD(device_resume,	bus_generic_resume),
62861994Smsmith	{ 0, 0 }
62961994Smsmith};
63061994Smsmith
63161994Smsmithstatic driver_t atspeaker_driver = {
63261994Smsmith	"atspeaker",
63361994Smsmith	atspeaker_methods,
63461994Smsmith	1,		/* no softc */
63561994Smsmith};
63661994Smsmith
63761994Smsmithstatic devclass_t atspeaker_devclass;
63861994Smsmith
63961994SmsmithDRIVER_MODULE(atspeaker, isa, atspeaker_driver, atspeaker_devclass, 0, 0);
64082555SmsmithDRIVER_MODULE(atspeaker, acpi, atspeaker_driver, atspeaker_devclass, 0, 0);
64161994Smsmith
6424Srgrimes/* spkr.c ends here */
643