Fixed 2 compiler warnings, two functions prototyped non-static, then later
[unix-history] / sys / i386 / isa / spkr.c
CommitLineData
15637ed4
RG
1/*
2 * spkr.c -- device driver for console speaker on 80386
3 *
4 * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
5 * modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
6 * 386bsd only clean version, all SYSV stuff removed
7 * use hz value from param.c
8 */
9
10#include "speaker.h"
11
12#if NSPEAKER > 0
13
14#include "param.h"
dd18dc33 15#include "systm.h"
15637ed4
RG
16#include "kernel.h"
17#include "errno.h"
18#include "buf.h"
19#include "uio.h"
20#include "spkr.h"
21
22/**************** MACHINE DEPENDENT PART STARTS HERE *************************
23 *
24 * This section defines a function tone() which causes a tone of given
25 * frequency and duration from the 80x86's console speaker.
26 * Another function endtone() is defined to force sound off, and there is
27 * also a rest() entry point to do pauses.
28 *
29 * Audible sound is generated using the Programmable Interval Timer (PIT) and
30 * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The
31 * PPI controls whether sound is passed through at all; the PIT's channel 2 is
32 * used to generate clicks (a square wave) of whatever frequency is desired.
33 */
34
35/*
36 * PIT and PPI port addresses and control values
37 *
38 * Most of the magic is hidden in the TIMER_PREP value, which selects PIT
39 * channel 2, frequency LSB first, square-wave mode and binary encoding.
40 * The encoding is as follows:
41 *
42 * +----------+----------+---------------+-----+
43 * | 1 0 | 1 1 | 0 1 1 | 0 |
44 * | SC1 SC0 | RW1 RW0 | M2 M1 M0 | BCD |
45 * +----------+----------+---------------+-----+
46 * Counter Write Mode 3 Binary
47 * Channel 2 LSB first, (Square Wave) Encoding
48 * MSB second
49 */
50#define PPI 0x61 /* port of Programmable Peripheral Interface */
51#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */
52#define PIT_CTRL 0x43 /* PIT control address */
53#define PIT_COUNT 0x42 /* PIT count address */
54#define PIT_MODE 0xB6 /* set timer mode for sound generation */
55
56/*
57 * Magic numbers for timer control.
58 */
59#define TIMER_CLK 1193180L /* corresponds to 18.2 MHz tick rate */
60
61static int endtone()
62/* turn off the speaker, ending current tone */
63{
64 wakeup((caddr_t)endtone);
65 outb(PPI, inb(PPI) & ~PPI_SPKR);
66}
67
68static void tone(hz, ticks)
69/* emit tone of frequency hz for given number of ticks */
70unsigned int hz, ticks;
71{
72 unsigned int divisor = TIMER_CLK / hz;
73 int sps;
74
75#ifdef DEBUG
76 printf("tone: hz=%d ticks=%d\n", hz, ticks);
77#endif /* DEBUG */
78
79 /* set timer to generate clicks at given frequency in Hertz */
80 sps = spltty();
81 outb(PIT_CTRL, PIT_MODE); /* prepare timer */
82 outb(PIT_COUNT, (unsigned char) divisor); /* send lo byte */
83 outb(PIT_COUNT, (divisor >> 8)); /* send hi byte */
84 splx(sps);
85
86 /* turn the speaker on */
87 outb(PPI, inb(PPI) | PPI_SPKR);
88
89 /*
90 * Set timeout to endtone function, then give up the timeslice.
91 * This is so other processes can execute while the tone is being
92 * emitted.
93 */
94 timeout((caddr_t)endtone, (caddr_t)NULL, ticks);
95 sleep((caddr_t)endtone, PZERO - 1);
96}
97
98static int endrest()
99/* end a rest */
100{
101 wakeup((caddr_t)endrest);
102}
103
104static void rest(ticks)
105/* rest for given number of ticks */
106int ticks;
107{
108 /*
109 * Set timeout to endrest function, then give up the timeslice.
110 * This is so other processes can execute while the rest is being
111 * waited out.
112 */
113#ifdef DEBUG
114 printf("rest: %d\n", ticks);
115#endif /* DEBUG */
116 timeout((caddr_t)endrest, (caddr_t)NULL, ticks);
117 sleep((caddr_t)endrest, PZERO - 1);
118}
119
120/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
121 *
122 * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
123 * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
124 * Requires tone(), rest(), and endtone(). String play is not interruptible
125 * except possibly at physical block boundaries.
126 */
127
128typedef int bool;
129#define TRUE 1
130#define FALSE 0
131
132#define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
133#define isdigit(c) (((c) >= '0') && ((c) <= '9'))
134#define dtoi(c) ((c) - '0')
135
136static int octave; /* currently selected octave */
137static int whole; /* whole-note time at current tempo, in ticks */
138static int value; /* whole divisor for note time, quarter note = 1 */
139static int fill; /* controls spacing of notes */
140static bool octtrack; /* octave-tracking on? */
141static bool octprefix; /* override current octave-tracking state? */
142
143/*
144 * Magic number avoidance...
145 */
146#define SECS_PER_MIN 60 /* seconds per minute */
147#define WHOLE_NOTE 4 /* quarter notes per whole note */
148#define MIN_VALUE 64 /* the most we can divide a note by */
149#define DFLT_VALUE 4 /* default value (quarter-note) */
150#define FILLTIME 8 /* for articulation, break note in parts */
151#define STACCATO 6 /* 6/8 = 3/4 of note is filled */
152#define NORMAL 7 /* 7/8ths of note interval is filled */
153#define LEGATO 8 /* all of note interval is filled */
154#define DFLT_OCTAVE 4 /* default octave */
155#define MIN_TEMPO 32 /* minimum tempo */
156#define DFLT_TEMPO 120 /* default tempo */
157#define MAX_TEMPO 255 /* max tempo */
158#define NUM_MULT 3 /* numerator of dot multiplier */
159#define DENOM_MULT 2 /* denominator of dot multiplier */
160
161/* letter to half-tone: A B C D E F G */
162static int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
163
164/*
165 * This is the American Standard A440 Equal-Tempered scale with frequencies
166 * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
167 * our octave 0 is standard octave 2.
168 */
169#define OCTAVE_NOTES 12 /* semitones per octave */
170static int pitchtab[] =
171{
172/* C C# D D# E F F# G G# A A# B*/
173/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
174/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
175/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
176/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
177/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
178/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
179/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
180};
181
182static void playinit()
183{
184 octave = DFLT_OCTAVE;
185 whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
186 fill = NORMAL;
187 value = DFLT_VALUE;
188 octtrack = FALSE;
189 octprefix = TRUE; /* act as though there was an initial O(n) */
190}
191
192static void playtone(pitch, value, sustain)
193/* play tone of proper duration for current rhythm signature */
194int pitch, value, sustain;
195{
196 register int sound, silence, snum = 1, sdenom = 1;
197
198 /* this weirdness avoids floating-point arithmetic */
199 for (; sustain; sustain--)
200 {
201 snum *= NUM_MULT;
202 sdenom *= DENOM_MULT;
203 }
204
205 if (pitch == -1)
206 rest(whole * snum / (value * sdenom));
207 else
208 {
209 sound = (whole * snum) / (value * sdenom)
210 - (whole * (FILLTIME - fill)) / (value * FILLTIME);
211 silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
212
213#ifdef DEBUG
214 printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
215 pitch, sound, silence);
216#endif /* DEBUG */
217
218 tone(pitchtab[pitch], sound);
219 if (fill != LEGATO)
220 rest(silence);
221 }
222}
223
224static int abs(n)
225int n;
226{
227 if (n < 0)
228 return(-n);
229 else
230 return(n);
231}
232
233static void playstring(cp, slen)
234/* interpret and play an item from a notation string */
235char *cp;
236size_t slen;
237{
238 int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
239
240#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \
241 {v = v * 10 + (*++cp - '0'); slen--;}
242 for (; slen--; cp++)
243 {
244 int sustain, timeval, tempo;
245 register char c = toupper(*cp);
246
247#ifdef DEBUG
248 printf("playstring: %c (%x)\n", c, c);
249#endif /* DEBUG */
250
251 switch (c)
252 {
253 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
254
255 /* compute pitch */
256 pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
257
258 /* this may be followed by an accidental sign */
259 if (cp[1] == '#' || cp[1] == '+')
260 {
261 ++pitch;
262 ++cp;
263 slen--;
264 }
265 else if (cp[1] == '-')
266 {
267 --pitch;
268 ++cp;
269 slen--;
270 }
271
272 /*
273 * If octave-tracking mode is on, and there has been no octave-
274 * setting prefix, find the version of the current letter note
275 * closest to the last regardless of octave.
276 */
277 if (octtrack && !octprefix)
278 {
279 if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
280 {
281 ++octave;
282 pitch += OCTAVE_NOTES;
283 }
284
285 if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
286 {
287 --octave;
288 pitch -= OCTAVE_NOTES;
289 }
290 }
291 octprefix = FALSE;
292 lastpitch = pitch;
293
294 /* ...which may in turn be followed by an override time value */
295 GETNUM(cp, timeval);
296 if (timeval <= 0 || timeval > MIN_VALUE)
297 timeval = value;
298
299 /* ...and/or sustain dots */
300 for (sustain = 0; cp[1] == '.'; cp++)
301 {
302 slen--;
303 sustain++;
304 }
305
306 /* time to emit the actual tone */
307 playtone(pitch, timeval, sustain);
308 break;
309
310 case 'O':
311 if (cp[1] == 'N' || cp[1] == 'n')
312 {
313 octprefix = octtrack = FALSE;
314 ++cp;
315 slen--;
316 }
317 else if (cp[1] == 'L' || cp[1] == 'l')
318 {
319 octtrack = TRUE;
320 ++cp;
321 slen--;
322 }
323 else
324 {
325 GETNUM(cp, octave);
326 if (octave >= sizeof(pitchtab) / OCTAVE_NOTES)
327 octave = DFLT_OCTAVE;
328 octprefix = TRUE;
329 }
330 break;
331
332 case '>':
333 if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1)
334 octave++;
335 octprefix = TRUE;
336 break;
337
338 case '<':
339 if (octave > 0)
340 octave--;
341 octprefix = TRUE;
342 break;
343
344 case 'N':
345 GETNUM(cp, pitch);
346 for (sustain = 0; cp[1] == '.'; cp++)
347 {
348 slen--;
349 sustain++;
350 }
351 playtone(pitch - 1, value, sustain);
352 break;
353
354 case 'L':
355 GETNUM(cp, value);
356 if (value <= 0 || value > MIN_VALUE)
357 value = DFLT_VALUE;
358 break;
359
360 case 'P':
361 case '~':
362 /* this may be followed by an override time value */
363 GETNUM(cp, timeval);
364 if (timeval <= 0 || timeval > MIN_VALUE)
365 timeval = value;
366 for (sustain = 0; cp[1] == '.'; cp++)
367 {
368 slen--;
369 sustain++;
370 }
371 playtone(-1, timeval, sustain);
372 break;
373
374 case 'T':
375 GETNUM(cp, tempo);
376 if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
377 tempo = DFLT_TEMPO;
378 whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
379 break;
380
381 case 'M':
382 if (cp[1] == 'N' || cp[1] == 'n')
383 {
384 fill = NORMAL;
385 ++cp;
386 slen--;
387 }
388 else if (cp[1] == 'L' || cp[1] == 'l')
389 {
390 fill = LEGATO;
391 ++cp;
392 slen--;
393 }
394 else if (cp[1] == 'S' || cp[1] == 's')
395 {
396 fill = STACCATO;
397 ++cp;
398 slen--;
399 }
400 break;
401 }
402 }
403}
404
405/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
406 *
407 * This section implements driver hooks to run playstring() and the tone(),
408 * endtone(), and rest() functions defined above.
409 */
410
411static int spkr_active; /* exclusion flag */
412static struct buf *spkr_inbuf; /* incoming buf */
413
414int spkropen(dev)
415dev_t dev;
416{
417#ifdef DEBUG
418 printf("spkropen: entering with dev = %x\n", dev);
419#endif /* DEBUG */
420
421 if (minor(dev) != 0)
422 return(ENXIO);
423 else if (spkr_active)
424 return(EBUSY);
425 else
426 {
427 playinit();
428 spkr_inbuf = geteblk(DEV_BSIZE);
429 spkr_active = 1;
430 }
431 return(0);
432}
433
434int spkrwrite(dev, uio)
435dev_t dev;
436struct uio *uio;
437{
438 register unsigned n;
439 char *cp;
440 int error;
441#ifdef DEBUG
442 printf("spkrwrite: entering with dev = %x, count = %d\n",
443 dev, uio->uio_resid);
444#endif /* DEBUG */
445
446 if (minor(dev) != 0)
447 return(ENXIO);
448 else
449 {
450 n = MIN(DEV_BSIZE, uio->uio_resid);
451 cp = spkr_inbuf->b_un.b_addr;
452 error = uiomove(cp, n, uio);
453 if (!error)
454 playstring(cp, n);
455 return(error);
456 }
457}
458
459int spkrclose(dev)
460dev_t dev;
461{
462#ifdef DEBUG
463 printf("spkrclose: entering with dev = %x\n", dev);
464#endif /* DEBUG */
465
466 if (minor(dev) != 0)
467 return(ENXIO);
468 else
469 {
470 endtone();
471 brelse(spkr_inbuf);
472 spkr_active = 0;
473 }
474 return(0);
475}
476
477int spkrioctl(dev, cmd, cmdarg)
478dev_t dev;
479int cmd;
480caddr_t cmdarg;
481{
482#ifdef DEBUG
483 printf("spkrioctl: entering with dev = %x, cmd = %x\n", dev, cmd);
484#endif /* DEBUG */
485
486 if (minor(dev) != 0)
487 return(ENXIO);
488 else if (cmd == SPKRTONE)
489 {
490 tone_t *tp = (tone_t *)cmdarg;
491
492 if (tp->frequency == 0)
493 rest(tp->duration);
494 else
495 tone(tp->frequency, tp->duration);
496 }
497 else if (cmd == SPKRTUNE)
498 {
499 tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg);
500 tone_t ttp;
501 int error;
502
503 for (; ; tp++) {
504 error = copyin(tp, &ttp, sizeof(tone_t));
505 if (error)
506 return(error);
507 if (ttp.duration == 0)
508 break;
509 if (ttp.frequency == 0)
510 rest(ttp.duration);
511 else
512 tone(ttp.frequency, ttp.duration);
513 }
514 }
515 else
516 return(EINVAL);
517 return(0);
518}
519
520#endif /* NSPEAKER > 0 */
521/* spkr.c ends here */