spkr.c revision 809
1264790Sbapt/*
2264790Sbapt * spkr.c -- device driver for console speaker
3264790Sbapt *
4264790Sbapt * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
5264790Sbapt * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
6264790Sbapt *
7264790Sbapt *    $Id: spkr.c,v 1.5 1993/11/15 01:33:11 ache Exp $
8264790Sbapt */
9264790Sbapt
10264790Sbapt#include "speaker.h"
11264790Sbapt
12264790Sbapt#if NSPEAKER > 0
13264790Sbapt
14264790Sbapt#include "param.h"
15264790Sbapt#include "systm.h"
16264790Sbapt#include "kernel.h"
17264790Sbapt#include "errno.h"
18264790Sbapt#include "buf.h"
19264790Sbapt#include "uio.h"
20264790Sbapt
21264790Sbapt#include "machine/speaker.h"
22264790Sbapt
23264790Sbapt/**************** MACHINE DEPENDENT PART STARTS HERE *************************
24264790Sbapt *
25264790Sbapt * This section defines a function tone() which causes a tone of given
26264790Sbapt * frequency and duration from the 80x86's console speaker.
27264790Sbapt * Another function endtone() is defined to force sound off, and there is
28264790Sbapt * also a rest() entry point to do pauses.
29264790Sbapt *
30264790Sbapt * Audible sound is generated using the Programmable Interval Timer (PIT) and
31264790Sbapt * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The
32264790Sbapt * PPI controls whether sound is passed through at all; the PIT's channel 2 is
33264790Sbapt * used to generate clicks (a square wave) of whatever frequency is desired.
34264790Sbapt */
35264790Sbapt
36264790Sbapt/*
37264790Sbapt * PIT and PPI port addresses and control values
38264790Sbapt *
39264790Sbapt * Most of the magic is hidden in the TIMER_PREP value, which selects PIT
40264790Sbapt * channel 2, frequency LSB first, square-wave mode and binary encoding.
41264790Sbapt * The encoding is as follows:
42264790Sbapt *
43264790Sbapt * +----------+----------+---------------+-----+
44264790Sbapt * |  1    0  |  1    1  |  0    1    1  |  0  |
45264790Sbapt * | SC1  SC0 | RW1  RW0 | M2   M1   M0  | BCD |
46264790Sbapt * +----------+----------+---------------+-----+
47264790Sbapt *   Counter     Write        Mode 3      Binary
48264790Sbapt *  Channel 2  LSB first,  (Square Wave) Encoding
49264790Sbapt *             MSB second
50264790Sbapt */
51264790Sbapt#define PPI		0x61	/* port of Programmable Peripheral Interface */
52264790Sbapt#define PPI_SPKR	0x03	/* turn these PPI bits on to pass sound */
53264790Sbapt#define PIT_CTRL	0x43	/* PIT control address */
54264790Sbapt#define PIT_COUNT	0x42	/* PIT count address */
55264790Sbapt#define PIT_MODE	0xB6	/* set timer mode for sound generation */
56264790Sbapt
57264790Sbapt/*
58264790Sbapt * Magic numbers for timer control.
59264790Sbapt */
60264790Sbapt#define TIMER_CLK	1193180L	/* corresponds to 18.2 MHz tick rate */
61264790Sbapt
62264790Sbapt#define SPKRPRI PSOCK
63264790Sbaptstatic char endtone, endrest;
64264790Sbapt
65264790Sbaptstatic int tone(thz, ticks)
66264790Sbapt/* emit tone of frequency thz for given number of ticks */
67264790Sbaptunsigned int thz, ticks;
68264790Sbapt{
69264790Sbapt    unsigned int divisor = TIMER_CLK / thz;
70264790Sbapt    int sps, error;
71264790Sbapt
72264790Sbapt#ifdef DEBUG
73264790Sbapt    (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
74264790Sbapt#endif /* DEBUG */
75264790Sbapt
76264790Sbapt    /* set timer to generate clicks at given frequency in Hertz */
77264790Sbapt    sps = spltty();
78264790Sbapt    outb(PIT_CTRL, PIT_MODE);		/* prepare timer */
79264790Sbapt    outb(PIT_COUNT, (divisor & 0xff));	/* send lo byte */
80264790Sbapt    outb(PIT_COUNT, (divisor >> 8));	/* send hi byte */
81264790Sbapt    splx(sps);
82264790Sbapt
83264790Sbapt    /* turn the speaker on */
84264790Sbapt    outb(PPI, inb(PPI) | PPI_SPKR);
85264790Sbapt
86264790Sbapt    /*
87264790Sbapt     * Set timeout to endtone function, then give up the timeslice.
88264790Sbapt     * This is so other processes can execute while the tone is being
89264790Sbapt     * emitted.
90264790Sbapt     */
91264790Sbapt    while ((error = tsleep((caddr_t)&endtone,
92264790Sbapt		SPKRPRI | PCATCH, "spkrtn", ticks)) == ERESTART)
93264790Sbapt		;
94264790Sbapt    outb(PPI, inb(PPI) & ~PPI_SPKR);
95264790Sbapt
96264790Sbapt    if (error == EWOULDBLOCK)
97264790Sbapt	error = 0;
98264790Sbapt    return error;
99264790Sbapt}
100264790Sbapt
101264790Sbaptstatic int rest(ticks)
102264790Sbapt/* rest for given number of ticks */
103264790Sbaptint	ticks;
104264790Sbapt{
105264790Sbapt    int error;
106264790Sbapt    /*
107264790Sbapt     * Set timeout to endrest function, then give up the timeslice.
108264790Sbapt     * This is so other processes can execute while the rest is being
109264790Sbapt     * waited out.
110264790Sbapt     */
111264790Sbapt#ifdef DEBUG
112264790Sbapt    (void) printf("rest: %d\n", ticks);
113264790Sbapt#endif /* DEBUG */
114264790Sbapt    while ((error = tsleep((caddr_t)&endrest,
115264790Sbapt		SPKRPRI | PCATCH, "spkrrs", ticks)) == ERESTART)
116264790Sbapt		;
117264790Sbapt    if (error == EWOULDBLOCK)
118264790Sbapt	error = 0;
119264790Sbapt    return error;
120264790Sbapt}
121264790Sbapt
122264790Sbapt/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
123264790Sbapt *
124264790Sbapt * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
125264790Sbapt * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
126264790Sbapt * tracking facility are added.
127264790Sbapt * Requires tone(), rest(), and endtone(). String play is not interruptible
128264790Sbapt * except possibly at physical block boundaries.
129264790Sbapt */
130264790Sbapt
131264790Sbapttypedef int	bool;
132264790Sbapt#define TRUE	1
133264790Sbapt#define FALSE	0
134264790Sbapt
135264790Sbapt#define toupper(c)	((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
136264790Sbapt#define isdigit(c)	(((c) >= '0') && ((c) <= '9'))
137264790Sbapt#define dtoi(c)		((c) - '0')
138264790Sbapt
139264790Sbaptstatic int octave;	/* currently selected octave */
140264790Sbaptstatic int whole;	/* whole-note time at current tempo, in ticks */
141264790Sbaptstatic int value;	/* whole divisor for note time, quarter note = 1 */
142264790Sbaptstatic int fill;	/* controls spacing of notes */
143264790Sbaptstatic bool octtrack;	/* octave-tracking on? */
144264790Sbaptstatic bool octprefix;	/* override current octave-tracking state? */
145264790Sbapt
146264790Sbapt/*
147264790Sbapt * Magic number avoidance...
148264790Sbapt */
149264790Sbapt#define SECS_PER_MIN	60	/* seconds per minute */
150264790Sbapt#define WHOLE_NOTE	4	/* quarter notes per whole note */
151264790Sbapt#define MIN_VALUE	64	/* the most we can divide a note by */
152264790Sbapt#define DFLT_VALUE	4	/* default value (quarter-note) */
153264790Sbapt#define FILLTIME	8	/* for articulation, break note in parts */
154264790Sbapt#define STACCATO	6	/* 6/8 = 3/4 of note is filled */
155264790Sbapt#define NORMAL		7	/* 7/8ths of note interval is filled */
156264790Sbapt#define LEGATO		8	/* all of note interval is filled */
157264790Sbapt#define DFLT_OCTAVE	4	/* default octave */
158264790Sbapt#define MIN_TEMPO	32	/* minimum tempo */
159264790Sbapt#define DFLT_TEMPO	120	/* default tempo */
160264790Sbapt#define MAX_TEMPO	255	/* max tempo */
161264790Sbapt#define NUM_MULT	3	/* numerator of dot multiplier */
162264790Sbapt#define DENOM_MULT	2	/* denominator of dot multiplier */
163264790Sbapt
164264790Sbapt/* letter to half-tone:  A   B  C  D  E  F  G */
165264790Sbaptstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
166264790Sbapt
167264790Sbapt/*
168264790Sbapt * This is the American Standard A440 Equal-Tempered scale with frequencies
169264790Sbapt * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
170264790Sbapt * our octave 0 is standard octave 2.
171264790Sbapt */
172264790Sbapt#define OCTAVE_NOTES	12	/* semitones per octave */
173264790Sbaptstatic int pitchtab[] =
174264790Sbapt{
175264790Sbapt/*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
176264790Sbapt/* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
177264790Sbapt/* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
178264790Sbapt/* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
179264790Sbapt/* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
180264790Sbapt/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
181264790Sbapt/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
182264790Sbapt/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
183264790Sbapt};
184264790Sbapt
185264790Sbaptstatic void playinit()
186264790Sbapt{
187264790Sbapt    octave = DFLT_OCTAVE;
188264790Sbapt    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
189264790Sbapt    fill = NORMAL;
190264790Sbapt    value = DFLT_VALUE;
191264790Sbapt    octtrack = FALSE;
192264790Sbapt    octprefix = TRUE;	/* act as though there was an initial O(n) */
193264790Sbapt}
194264790Sbapt
195264790Sbaptstatic int playtone(pitch, value, sustain)
196264790Sbapt/* play tone of proper duration for current rhythm signature */
197264790Sbaptint	pitch, value, sustain;
198264790Sbapt{
199264790Sbapt    register int	sound, silence, snum = 1, sdenom = 1;
200264790Sbapt    int error;
201264790Sbapt
202264790Sbapt    /* this weirdness avoids floating-point arithmetic */
203264790Sbapt    for (; sustain; sustain--)
204264790Sbapt    {
205264790Sbapt	/* See the BUGS section in the man page for discussion */
206264790Sbapt	snum *= NUM_MULT;
207264790Sbapt	sdenom *= DENOM_MULT;
208264790Sbapt    }
209264790Sbapt
210264790Sbapt    if (pitch == -1)
211264790Sbapt	error = rest(whole * snum / (value * sdenom));
212264790Sbapt    else
213264790Sbapt    {
214264790Sbapt	sound = (whole * snum) / (value * sdenom)
215264790Sbapt		- (whole * (FILLTIME - fill)) / (value * FILLTIME);
216264790Sbapt	silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
217264790Sbapt
218264790Sbapt#ifdef DEBUG
219264790Sbapt	(void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
220264790Sbapt			pitch, sound, silence);
221264790Sbapt#endif /* DEBUG */
222264790Sbapt
223264790Sbapt	error = tone(pitchtab[pitch], sound);
224264790Sbapt	if (error) return error;
225264790Sbapt	if (fill != LEGATO)
226264790Sbapt	    error = rest(silence);
227264790Sbapt    }
228264790Sbapt    return error;
229264790Sbapt}
230264790Sbapt
231264790Sbaptstatic int abs(n)
232264790Sbaptint n;
233264790Sbapt{
234264790Sbapt    if (n < 0)
235264790Sbapt	return(-n);
236264790Sbapt    else
237264790Sbapt	return(n);
238264790Sbapt}
239264790Sbapt
240264790Sbaptstatic int playstring(cp, slen)
241264790Sbapt/* interpret and play an item from a notation string */
242264790Sbaptchar	*cp;
243264790Sbaptsize_t	slen;
244264790Sbapt{
245264790Sbapt    int		pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
246264790Sbapt    int error;
247264790Sbapt
248264790Sbapt#define GETNUM(cp, v)	for(v=0; isdigit(cp[1]) && slen > 0; ) \
249264790Sbapt				{v = v * 10 + (*++cp - '0'); slen--;}
250264790Sbapt    for (; slen--; cp++)
251264790Sbapt    {
252264790Sbapt	int		sustain, timeval, tempo;
253264790Sbapt	register char	c = toupper(*cp);
254264790Sbapt
255264790Sbapt#ifdef DEBUG
256264790Sbapt	(void) printf("playstring: %c (%x)\n", c, c);
257264790Sbapt#endif /* DEBUG */
258264790Sbapt
259264790Sbapt	switch (c)
260264790Sbapt	{
261264790Sbapt	case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
262264790Sbapt
263264790Sbapt	    /* compute pitch */
264264790Sbapt	    pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
265264790Sbapt
266264790Sbapt	    /* this may be followed by an accidental sign */
267264790Sbapt	    if (cp[1] == '#' || cp[1] == '+')
268264790Sbapt	    {
269264790Sbapt		++pitch;
270264790Sbapt		++cp;
271264790Sbapt		slen--;
272264790Sbapt	    }
273264790Sbapt	    else if (cp[1] == '-')
274264790Sbapt	    {
275264790Sbapt		--pitch;
276264790Sbapt		++cp;
277264790Sbapt		slen--;
278264790Sbapt	    }
279264790Sbapt
280264790Sbapt	    /*
281264790Sbapt	     * If octave-tracking mode is on, and there has been no octave-
282264790Sbapt	     * setting prefix, find the version of the current letter note
283264790Sbapt	     * closest to the last regardless of octave.
284264790Sbapt	     */
285264790Sbapt	    if (octtrack && !octprefix)
286264790Sbapt	    {
287264790Sbapt		if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
288264790Sbapt		{
289264790Sbapt		    ++octave;
290264790Sbapt		    pitch += OCTAVE_NOTES;
291264790Sbapt		}
292264790Sbapt
293264790Sbapt		if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
294264790Sbapt		{
295264790Sbapt		    --octave;
296264790Sbapt		    pitch -= OCTAVE_NOTES;
297264790Sbapt		}
298264790Sbapt	    }
299264790Sbapt	    octprefix = FALSE;
300264790Sbapt	    lastpitch = pitch;
301264790Sbapt
302264790Sbapt	    /* ...which may in turn be followed by an override time value */
303264790Sbapt	    GETNUM(cp, timeval);
304264790Sbapt	    if (timeval <= 0 || timeval > MIN_VALUE)
305264790Sbapt		timeval = value;
306264790Sbapt
307264790Sbapt	    /* ...and/or sustain dots */
308264790Sbapt	    for (sustain = 0; cp[1] == '.'; cp++)
309264790Sbapt	    {
310264790Sbapt		slen--;
311264790Sbapt		sustain++;
312264790Sbapt	    }
313264790Sbapt
314264790Sbapt	    /* ...and/or a slur mark */
315264790Sbapt	    oldfill = fill;
316264790Sbapt	    if (cp[1] == '_')
317264790Sbapt	    {
318264790Sbapt		fill = LEGATO;
319264790Sbapt		++cp;
320264790Sbapt		slen--;
321264790Sbapt	    }
322264790Sbapt
323264790Sbapt	    /* time to emit the actual tone */
324264790Sbapt	    error = playtone(pitch, timeval, sustain);
325264790Sbapt
326264790Sbapt	    fill = oldfill;
327264790Sbapt	    if (error) return error;
328264790Sbapt	    break;
329264790Sbapt
330264790Sbapt	case 'O':
331264790Sbapt	    if (cp[1] == 'N' || cp[1] == 'n')
332264790Sbapt	    {
333264790Sbapt		octprefix = octtrack = FALSE;
334264790Sbapt		++cp;
335264790Sbapt		slen--;
336264790Sbapt	    }
337264790Sbapt	    else if (cp[1] == 'L' || cp[1] == 'l')
338264790Sbapt	    {
339264790Sbapt		octtrack = TRUE;
340264790Sbapt		++cp;
341264790Sbapt		slen--;
342264790Sbapt	    }
343264790Sbapt	    else
344264790Sbapt	    {
345264790Sbapt		GETNUM(cp, octave);
346264790Sbapt		if (octave >= sizeof(pitchtab) / OCTAVE_NOTES)
347264790Sbapt		    octave = DFLT_OCTAVE;
348264790Sbapt		octprefix = TRUE;
349264790Sbapt	    }
350264790Sbapt	    break;
351264790Sbapt
352264790Sbapt	case '>':
353264790Sbapt	    if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1)
354264790Sbapt		octave++;
355264790Sbapt	    octprefix = TRUE;
356264790Sbapt	    break;
357264790Sbapt
358264790Sbapt	case '<':
359264790Sbapt	    if (octave > 0)
360264790Sbapt		octave--;
361264790Sbapt	    octprefix = TRUE;
362264790Sbapt	    break;
363264790Sbapt
364264790Sbapt	case 'N':
365264790Sbapt	    GETNUM(cp, pitch);
366264790Sbapt	    for (sustain = 0; cp[1] == '.'; cp++)
367264790Sbapt	    {
368264790Sbapt		slen--;
369264790Sbapt		sustain++;
370264790Sbapt	    }
371264790Sbapt	    oldfill = fill;
372264790Sbapt	    if (cp[1] == '_')
373264790Sbapt	    {
374264790Sbapt		fill = LEGATO;
375264790Sbapt		++cp;
376264790Sbapt		slen--;
377264790Sbapt	    }
378264790Sbapt	    error = playtone(pitch - 1, value, sustain);
379264790Sbapt	    fill = oldfill;
380264790Sbapt	    if (error) return error;
381264790Sbapt	    break;
382264790Sbapt
383264790Sbapt	case 'L':
384264790Sbapt	    GETNUM(cp, value);
385264790Sbapt	    if (value <= 0 || value > MIN_VALUE)
386264790Sbapt		value = DFLT_VALUE;
387264790Sbapt	    break;
388264790Sbapt
389264790Sbapt	case 'P':
390264790Sbapt	case '~':
391264790Sbapt	    /* this may be followed by an override time value */
392264790Sbapt	    GETNUM(cp, timeval);
393264790Sbapt	    if (timeval <= 0 || timeval > MIN_VALUE)
394264790Sbapt		timeval = value;
395264790Sbapt	    for (sustain = 0; cp[1] == '.'; cp++)
396264790Sbapt	    {
397264790Sbapt		slen--;
398264790Sbapt		sustain++;
399264790Sbapt	    }
400264790Sbapt	    error = playtone(-1, timeval, sustain);
401264790Sbapt	    if (error) return error;
402264790Sbapt	    break;
403264790Sbapt
404264790Sbapt	case 'T':
405264790Sbapt	    GETNUM(cp, tempo);
406264790Sbapt	    if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
407264790Sbapt		tempo = DFLT_TEMPO;
408264790Sbapt	    whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
409264790Sbapt	    break;
410264790Sbapt
411264790Sbapt	case 'M':
412264790Sbapt	    if (cp[1] == 'N' || cp[1] == 'n')
413264790Sbapt	    {
414264790Sbapt		fill = NORMAL;
415264790Sbapt		++cp;
416264790Sbapt		slen--;
417264790Sbapt	    }
418264790Sbapt	    else if (cp[1] == 'L' || cp[1] == 'l')
419264790Sbapt	    {
420264790Sbapt		fill = LEGATO;
421264790Sbapt		++cp;
422264790Sbapt		slen--;
423264790Sbapt	    }
424264790Sbapt	    else if (cp[1] == 'S' || cp[1] == 's')
425264790Sbapt	    {
426264790Sbapt		fill = STACCATO;
427264790Sbapt		++cp;
428264790Sbapt		slen--;
429264790Sbapt	    }
430264790Sbapt	    break;
431264790Sbapt	}
432264790Sbapt    }
433264790Sbapt    return 0;
434264790Sbapt}
435264790Sbapt
436264790Sbapt/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
437264790Sbapt *
438264790Sbapt * This section implements driver hooks to run playstring() and the tone(),
439264790Sbapt * endtone(), and rest() functions defined above.
440264790Sbapt */
441264790Sbapt
442264790Sbaptstatic int spkr_active = FALSE; /* exclusion flag */
443264790Sbaptstatic struct buf *spkr_inbuf;  /* incoming buf */
444264790Sbapt
445264790Sbaptint spkropen(dev)
446264790Sbaptdev_t	dev;
447264790Sbapt{
448264790Sbapt#ifdef DEBUG
449264790Sbapt    (void) printf("spkropen: entering with dev = %x\n", dev);
450264790Sbapt#endif /* DEBUG */
451264790Sbapt
452264790Sbapt    if (minor(dev) != 0)
453264790Sbapt	return(ENXIO);
454264790Sbapt    else if (spkr_active)
455264790Sbapt	return(EBUSY);
456264790Sbapt    else
457264790Sbapt    {
458264790Sbapt#ifdef DEBUG
459264790Sbapt	(void) printf("spkropen: about to perform play initialization\n");
460264790Sbapt#endif /* DEBUG */
461264790Sbapt	playinit();
462264790Sbapt	spkr_inbuf = geteblk(DEV_BSIZE);
463264790Sbapt	spkr_active = TRUE;
464264790Sbapt	return(0);
465264790Sbapt    }
466264790Sbapt}
467264790Sbapt
468264790Sbaptint spkrwrite(dev, uio)
469264790Sbaptdev_t		dev;
470264790Sbaptstruct uio	*uio;
471264790Sbapt{
472264790Sbapt#ifdef DEBUG
473264790Sbapt    printf("spkrwrite: entering with dev = %x, count = %d\n",
474264790Sbapt		dev, uio->uio_resid);
475264790Sbapt#endif /* DEBUG */
476264790Sbapt
477264790Sbapt    if (minor(dev) != 0)
478264790Sbapt	return(ENXIO);
479264790Sbapt    else if (uio->uio_resid > DEV_BSIZE)     /* prevent system crashes */
480264790Sbapt	return(E2BIG);
481264790Sbapt    else
482264790Sbapt    {
483264790Sbapt	unsigned n;
484264790Sbapt	char *cp;
485264790Sbapt	int error;
486264790Sbapt
487264790Sbapt	n = uio->uio_resid;
488264790Sbapt	cp = spkr_inbuf->b_un.b_addr;
489264790Sbapt	if (!(error = uiomove(cp, n, uio)))
490264790Sbapt		error = playstring(cp, n);
491264790Sbapt	return(error);
492264790Sbapt    }
493264790Sbapt}
494264790Sbapt
495264790Sbaptint spkrclose(dev)
496264790Sbaptdev_t	dev;
497264790Sbapt{
498264790Sbapt#ifdef DEBUG
499264790Sbapt    (void) printf("spkrclose: entering with dev = %x\n", dev);
500264790Sbapt#endif /* DEBUG */
501264790Sbapt
502264790Sbapt    if (minor(dev) != 0)
503264790Sbapt	return(ENXIO);
504264790Sbapt    else
505264790Sbapt    {
506264790Sbapt	wakeup((caddr_t)&endtone);
507264790Sbapt	wakeup((caddr_t)&endrest);
508264790Sbapt	brelse(spkr_inbuf);
509264790Sbapt	spkr_active = FALSE;
510264790Sbapt	return(0);
511264790Sbapt    }
512264790Sbapt}
513264790Sbapt
514264790Sbaptint spkrioctl(dev, cmd, cmdarg)
515264790Sbaptdev_t	dev;
516264790Sbaptint	cmd;
517264790Sbaptcaddr_t	cmdarg;
518264790Sbapt{
519264790Sbapt#ifdef DEBUG
520264790Sbapt    (void) printf("spkrioctl: entering with dev = %x, cmd = %x\n");
521264790Sbapt#endif /* DEBUG */
522264790Sbapt
523264790Sbapt    if (minor(dev) != 0)
524264790Sbapt	return(ENXIO);
525264790Sbapt    else if (cmd == SPKRTONE)
526264790Sbapt    {
527264790Sbapt	tone_t	*tp = (tone_t *)cmdarg;
528264790Sbapt
529264790Sbapt	if (tp->frequency == 0)
530264790Sbapt	    return rest(tp->duration);
531264790Sbapt	else
532264790Sbapt	    return tone(tp->frequency, tp->duration);
533264790Sbapt    }
534264790Sbapt    else if (cmd == SPKRTUNE)
535264790Sbapt    {
536264790Sbapt	tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
537264790Sbapt	tone_t ttp;
538264790Sbapt	int error;
539264790Sbapt
540264790Sbapt	for (; ; tp++) {
541264790Sbapt	    error = copyin(tp, &ttp, sizeof(tone_t));
542264790Sbapt	    if (error)
543264790Sbapt		    return(error);
544264790Sbapt	    if (ttp.duration == 0)
545264790Sbapt		    break;
546264790Sbapt	    if (ttp.frequency == 0)
547264790Sbapt		error = rest(ttp.duration);
548264790Sbapt	    else
549264790Sbapt		error = tone(ttp.frequency, ttp.duration);
550264790Sbapt	    if (error)
551264790Sbapt		    return(error);
552264790Sbapt	}
553264790Sbapt	return(0);
554264790Sbapt    }
555264790Sbapt    return(EINVAL);
556264790Sbapt}
557264790Sbapt
558264790Sbapt#endif  /* NSPEAKER > 0 */
559264790Sbapt/* spkr.c ends here */
560264790Sbapt