Line data Source code
1 : /* $OpenBSD: spkr.c,v 1.22 2017/12/30 23:08:29 guenther Exp $ */
2 : /* $NetBSD: spkr.c,v 1.1 1998/04/15 20:26:18 drochner Exp $ */
3 :
4 : /*
5 : * Copyright (c) 1990 Eric S. Raymond (esr@snark.thyrsus.com)
6 : * Copyright (c) 1990 Andrew A. Chernov (ache@astral.msk.su)
7 : * Copyright (c) 1990 Lennart Augustsson (lennart@augustsson.net)
8 : * All rights reserved.
9 : *
10 : * Redistribution and use in source and binary forms, with or without
11 : * modification, are permitted provided that the following conditions
12 : * are met:
13 : * 1. Redistributions of source code must retain the above copyright
14 : * notice, this list of conditions and the following disclaimer.
15 : * 2. Redistributions in binary form must reproduce the above copyright
16 : * notice, this list of conditions and the following disclaimer in the
17 : * documentation and/or other materials provided with the distribution.
18 : * 3. All advertising materials mentioning features or use of this software
19 : * must display the following acknowledgement:
20 : * This product includes software developed by Eric S. Raymond
21 : * 4. The name of the author may not be used to endorse or promote products
22 : * derived from this software without specific prior written permission.
23 : *
24 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 : * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 : * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
28 : * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 : * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 : * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32 : * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 : * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 : * POSSIBILITY OF SUCH DAMAGE.
35 : */
36 :
37 : /*
38 : * spkr.c -- device driver for console speaker on 80386
39 : *
40 : * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
41 : * modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
42 : * 386bsd only clean version, all SYSV stuff removed
43 : * use hz value from param.c
44 : */
45 :
46 : #include <sys/param.h>
47 : #include <sys/systm.h>
48 : #include <sys/kernel.h>
49 : #include <sys/errno.h>
50 : #include <sys/device.h>
51 : #include <sys/malloc.h>
52 : #include <sys/uio.h>
53 : #include <sys/ioctl.h>
54 : #include <sys/conf.h>
55 : #include <sys/fcntl.h>
56 :
57 : #include <dev/isa/pcppivar.h>
58 :
59 : #include <dev/isa/spkrio.h>
60 :
61 : cdev_decl(spkr);
62 :
63 : int spkrprobe(struct device *, void *, void *);
64 : void spkrattach(struct device *, struct device *, void *);
65 :
66 : struct cfattach spkr_ca = {
67 : sizeof(struct device), spkrprobe, spkrattach
68 : };
69 :
70 : struct cfdriver spkr_cd = {
71 : NULL, "spkr", DV_DULL
72 : };
73 :
74 : static pcppi_tag_t ppicookie;
75 :
76 : #define SPKRPRI (PZERO - 1)
77 :
78 : static void tone(u_int, u_int);
79 : static void rest(int);
80 : static void playinit(void);
81 : static void playtone(int, int, int);
82 : static void playstring(char *, size_t);
83 :
84 : /* emit tone of frequency hz for given number of ticks */
85 : static void
86 0 : tone(hz, nticks)
87 : u_int hz, nticks;
88 : {
89 0 : pcppi_bell(ppicookie, hz, nticks, PCPPI_BELL_SLEEP);
90 0 : }
91 :
92 : /* rest for given number of ticks */
93 : static void
94 0 : rest(nticks)
95 : int nticks;
96 : {
97 : /*
98 : * Set timeout to endrest function, then give up the timeslice.
99 : * This is so other processes can execute while the rest is being
100 : * waited out.
101 : */
102 : #ifdef SPKRDEBUG
103 : printf("rest: %d\n", nticks);
104 : #endif /* SPKRDEBUG */
105 0 : if (nticks > 0)
106 0 : tsleep(rest, SPKRPRI | PCATCH, "rest", nticks);
107 0 : }
108 :
109 : /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
110 : *
111 : * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
112 : * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
113 : * Requires tone(), rest(), and endtone(). String play is not interruptible
114 : * except possibly at physical block boundaries.
115 : */
116 :
117 : #define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
118 : #define isdigit(c) (((c) >= '0') && ((c) <= '9'))
119 : #define dtoi(c) ((c) - '0')
120 :
121 : static int octave; /* currently selected octave */
122 : static int whole; /* whole-note time at current tempo, in ticks */
123 : static int value; /* whole divisor for note time, quarter note = 1 */
124 : static int fill; /* controls spacing of notes */
125 : static int octtrack; /* octave-tracking on? */
126 : static int octprefix; /* override current octave-tracking state? */
127 :
128 : /*
129 : * Magic number avoidance...
130 : */
131 : #define SECS_PER_MIN 60 /* seconds per minute */
132 : #define WHOLE_NOTE 4 /* quarter notes per whole note */
133 : #define MIN_VALUE 64 /* the most we can divide a note by */
134 : #define DFLT_VALUE 4 /* default value (quarter-note) */
135 : #define FILLTIME 8 /* for articulation, break note in parts */
136 : #define STACCATO 6 /* 6/8 = 3/4 of note is filled */
137 : #define NORMAL 7 /* 7/8ths of note interval is filled */
138 : #define LEGATO 8 /* all of note interval is filled */
139 : #define DFLT_OCTAVE 4 /* default octave */
140 : #define MIN_TEMPO 32 /* minimum tempo */
141 : #define DFLT_TEMPO 120 /* default tempo */
142 : #define MAX_TEMPO 255 /* max tempo */
143 : #define NUM_MULT 3 /* numerator of dot multiplier */
144 : #define DENOM_MULT 2 /* denominator of dot multiplier */
145 :
146 : /* letter to half-tone: A B C D E F G */
147 : static int notetab[8] = { 9, 11, 0, 2, 4, 5, 7 };
148 :
149 : /*
150 : * This is the American Standard A440 Equal-Tempered scale with frequencies
151 : * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
152 : * our octave 0 is standard octave 2.
153 : */
154 : #define OCTAVE_NOTES 12 /* semitones per octave */
155 : static int pitchtab[] =
156 : {
157 : /* C C# D D# E F F# G G# A A# B*/
158 : /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
159 : /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
160 : /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
161 : /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
162 : /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
163 : /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
164 : /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
165 : };
166 : #define NOCTAVES (sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
167 :
168 : static void
169 0 : playinit(void)
170 : {
171 0 : octave = DFLT_OCTAVE;
172 0 : whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
173 0 : fill = NORMAL;
174 0 : value = DFLT_VALUE;
175 0 : octtrack = 0;
176 0 : octprefix = 1; /* act as though there was an initial O(n) */
177 0 : }
178 :
179 : /* play tone of proper duration for current rhythm signature */
180 : static void
181 0 : playtone(int pitch, int value, int sustain)
182 : {
183 : int sound, silence, snum = 1, sdenom = 1;
184 :
185 : /* this weirdness avoids floating-point arithmetic */
186 0 : for (; sustain; sustain--) {
187 0 : snum *= NUM_MULT;
188 0 : sdenom *= DENOM_MULT;
189 : }
190 :
191 0 : if (pitch == -1)
192 0 : rest(whole * snum / (value * sdenom));
193 0 : else if (pitch >= 0 &&
194 0 : pitch < (sizeof(pitchtab) / sizeof(pitchtab[0]))) {
195 0 : sound = (whole * snum) / (value * sdenom) -
196 0 : (whole * (FILLTIME - fill)) / (value * FILLTIME);
197 0 : silence = whole * (FILLTIME-fill) * snum /
198 0 : (FILLTIME * value * sdenom);
199 :
200 : #ifdef SPKRDEBUG
201 : printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
202 : pitch, sound, silence);
203 : #endif /* SPKRDEBUG */
204 :
205 0 : tone(pitchtab[pitch], sound);
206 0 : if (fill != LEGATO)
207 0 : rest(silence);
208 : }
209 0 : }
210 :
211 : /* interpret and play an item from a notation string */
212 : static void
213 0 : playstring(char *cp, size_t slen)
214 : {
215 : int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
216 :
217 : #define GETNUM(cp, v) \
218 : do { \
219 : for (v = 0; slen > 0 && isdigit(cp[1]); ) { \
220 : v = v * 10 + (*++cp - '0'); \
221 : slen--; \
222 : } \
223 : } while (0)
224 :
225 0 : for (; slen--; cp++) {
226 : int sustain, timeval, tempo;
227 0 : char c = toupper(*cp);
228 :
229 : #ifdef SPKRDEBUG
230 : printf("playstring: %c (%x)\n", c, c);
231 : #endif /* SPKRDEBUG */
232 :
233 0 : switch (c) {
234 : case 'A':
235 : case 'B':
236 : case 'C':
237 : case 'D':
238 : case 'E':
239 : case 'F':
240 : case 'G':
241 : /* compute pitch */
242 0 : pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
243 :
244 : /* this may be followed by an accidental sign */
245 0 : if (slen > 0 && (cp[1] == '#' || cp[1] == '+')) {
246 0 : ++pitch;
247 0 : ++cp;
248 0 : slen--;
249 0 : } else if (slen > 0 && cp[1] == '-') {
250 0 : --pitch;
251 0 : ++cp;
252 0 : slen--;
253 0 : }
254 :
255 : /*
256 : * If octave-tracking mode is on, and there has been
257 : * no octave-setting prefix, find the version of the
258 : * current letter note closest to the last regardless
259 : * of octave.
260 : */
261 0 : if (octtrack && !octprefix) {
262 0 : if (abs(pitch - lastpitch) >
263 0 : abs(pitch + OCTAVE_NOTES - lastpitch)) {
264 0 : ++octave;
265 : pitch += OCTAVE_NOTES;
266 0 : }
267 :
268 0 : if (abs(pitch - lastpitch) >
269 0 : abs(pitch - OCTAVE_NOTES - lastpitch)) {
270 0 : --octave;
271 : pitch -= OCTAVE_NOTES;
272 0 : }
273 : }
274 0 : octprefix = 0;
275 : lastpitch = pitch;
276 :
277 : /*
278 : * ...which may in turn be followed by an override
279 : * time value
280 : */
281 0 : GETNUM(cp, timeval);
282 0 : if (timeval <= 0 || timeval > MIN_VALUE)
283 0 : timeval = value;
284 :
285 : /* ...and/or sustain dots */
286 0 : for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
287 0 : slen--;
288 0 : sustain++;
289 : }
290 :
291 : /* time to emit the actual tone */
292 0 : playtone(pitch, timeval, sustain);
293 0 : break;
294 :
295 : case 'O':
296 0 : if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
297 0 : octprefix = octtrack = 0;
298 0 : ++cp;
299 0 : slen--;
300 0 : } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
301 0 : octtrack = 1;
302 0 : ++cp;
303 0 : slen--;
304 0 : } else {
305 0 : GETNUM(cp, octave);
306 0 : if (octave >= NOCTAVES)
307 0 : octave = DFLT_OCTAVE;
308 0 : octprefix = 1;
309 : }
310 : break;
311 :
312 : case '>':
313 0 : if (octave < NOCTAVES - 1)
314 0 : octave++;
315 0 : octprefix = 1;
316 0 : break;
317 :
318 : case '<':
319 0 : if (octave > 0)
320 0 : octave--;
321 0 : octprefix = 1;
322 0 : break;
323 :
324 : case 'N':
325 0 : GETNUM(cp, pitch);
326 0 : for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
327 0 : slen--;
328 0 : sustain++;
329 : }
330 0 : playtone(pitch - 1, value, sustain);
331 0 : break;
332 :
333 : case 'L':
334 0 : GETNUM(cp, value);
335 0 : if (value <= 0 || value > MIN_VALUE)
336 0 : value = DFLT_VALUE;
337 : break;
338 :
339 : case 'P':
340 : case '~':
341 : /* this may be followed by an override time value */
342 0 : GETNUM(cp, timeval);
343 0 : if (timeval <= 0 || timeval > MIN_VALUE)
344 0 : timeval = value;
345 0 : for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
346 0 : slen--;
347 0 : sustain++;
348 : }
349 0 : playtone(-1, timeval, sustain);
350 0 : break;
351 :
352 : case 'T':
353 0 : GETNUM(cp, tempo);
354 0 : if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
355 0 : tempo = DFLT_TEMPO;
356 0 : whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
357 0 : break;
358 :
359 : case 'M':
360 0 : if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
361 0 : fill = NORMAL;
362 0 : ++cp;
363 0 : slen--;
364 0 : } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
365 0 : fill = LEGATO;
366 0 : ++cp;
367 0 : slen--;
368 0 : } else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's')) {
369 0 : fill = STACCATO;
370 0 : ++cp;
371 0 : slen--;
372 0 : }
373 : break;
374 : }
375 : }
376 0 : }
377 :
378 : /******************* UNIX DRIVER HOOKS BEGIN HERE **************************
379 : *
380 : * This section implements driver hooks to run playstring() and the tone(),
381 : * endtone(), and rest() functions defined above.
382 : */
383 :
384 : static int spkr_active; /* exclusion flag */
385 : static void *spkr_inbuf;
386 :
387 : static int spkr_attached = 0;
388 :
389 : int
390 0 : spkrprobe(struct device *parent, void *match, void *aux)
391 : {
392 0 : return (!spkr_attached);
393 : }
394 :
395 : void
396 0 : spkrattach(struct device *parent, struct device *self, void *aux)
397 : {
398 0 : printf("\n");
399 0 : ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
400 0 : spkr_attached = 1;
401 0 : }
402 :
403 : int
404 0 : spkropen(dev_t dev, int flags, int mode, struct proc *p)
405 : {
406 : #ifdef SPKRDEBUG
407 : printf("spkropen: entering with dev = %x\n", dev);
408 : #endif /* SPKRDEBUG */
409 :
410 0 : if (minor(dev) != 0 || !spkr_attached)
411 0 : return (ENXIO);
412 0 : else if (spkr_active)
413 0 : return (EBUSY);
414 : else {
415 0 : playinit();
416 0 : spkr_inbuf = malloc(DEV_BSIZE, M_DEVBUF, M_WAITOK);
417 0 : spkr_active = 1;
418 : }
419 0 : return (0);
420 0 : }
421 :
422 : int
423 0 : spkrwrite(dev_t dev, struct uio *uio, int flags)
424 : {
425 : size_t n;
426 : int error;
427 : #ifdef SPKRDEBUG
428 : printf("spkrwrite: entering with dev = %x, count = %d\n",
429 : dev, uio->uio_resid);
430 : #endif /* SPKRDEBUG */
431 :
432 0 : if (minor(dev) != 0)
433 0 : return (ENXIO);
434 : else {
435 0 : n = ulmin(DEV_BSIZE, uio->uio_resid);
436 0 : error = uiomove(spkr_inbuf, n, uio);
437 0 : if (!error)
438 0 : playstring((char *)spkr_inbuf, n);
439 0 : return (error);
440 : }
441 0 : }
442 :
443 : int
444 0 : spkrclose(dev_t dev, int flags, int mode, struct proc *p)
445 : {
446 : #ifdef SPKRDEBUG
447 : printf("spkrclose: entering with dev = %x\n", dev);
448 : #endif /* SPKRDEBUG */
449 :
450 0 : if (minor(dev) != 0)
451 0 : return (ENXIO);
452 : else {
453 0 : tone(0, 0);
454 0 : free(spkr_inbuf, M_DEVBUF, DEV_BSIZE);
455 0 : spkr_active = 0;
456 : }
457 0 : return (0);
458 0 : }
459 :
460 : int
461 0 : spkrioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
462 : {
463 0 : tone_t *tp, ttp;
464 : int error;
465 :
466 : #ifdef SPKRDEBUG
467 : printf("spkrioctl: entering with dev = %x, cmd = %lx\n", dev, cmd);
468 : #endif /* SPKRDEBUG */
469 :
470 0 : if (minor(dev) != 0)
471 0 : return (ENXIO);
472 :
473 0 : switch (cmd) {
474 : case SPKRTONE:
475 : case SPKRTUNE:
476 0 : if ((flag & FWRITE) == 0)
477 0 : return (EACCES);
478 : default:
479 : break;
480 : }
481 :
482 0 : switch (cmd) {
483 : case SPKRTONE:
484 0 : tp = (tone_t *)data;
485 :
486 0 : if (tp->frequency == 0)
487 0 : rest(tp->duration);
488 : else
489 0 : tone(tp->frequency, tp->duration);
490 : break;
491 : case SPKRTUNE:
492 0 : tp = (tone_t *)(*(caddr_t *)data);
493 :
494 0 : for (; ; tp++) {
495 0 : error = copyin(tp, &ttp, sizeof(tone_t));
496 0 : if (error)
497 0 : return (error);
498 0 : if (ttp.duration == 0)
499 : break;
500 0 : if (ttp.frequency == 0)
501 0 : rest(ttp.duration);
502 : else
503 0 : tone(ttp.frequency, ttp.duration);
504 : }
505 : break;
506 : default:
507 0 : return (ENOTTY);
508 : }
509 :
510 0 : return (0);
511 0 : }
|