spkr.c revision 1.6.6.3 1 /* $NetBSD: spkr.c,v 1.6.6.3 2017/08/28 17:52:00 skrll Exp $ */
2
3 /*
4 * Copyright (c) 1990 Eric S. Raymond (esr (at) snark.thyrsus.com)
5 * Copyright (c) 1990 Andrew A. Chernov (ache (at) astral.msk.su)
6 * Copyright (c) 1990 Lennart Augustsson (lennart (at) augustsson.net)
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Eric S. Raymond
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /*
37 * spkr.c -- device driver for console speaker on 80386
38 *
39 * v1.1 by Eric S. Raymond (esr (at) snark.thyrsus.com) Feb 1990
40 * modified for 386bsd by Andrew A. Chernov <ache (at) astral.msk.su>
41 * 386bsd only clean version, all SYSV stuff removed
42 * use hz value from param.c
43 */
44
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: spkr.c,v 1.6.6.3 2017/08/28 17:52:00 skrll Exp $");
47 #if defined(_KERNEL_OPT)
48 #include "wsmux.h"
49 #endif
50
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/kernel.h>
54 #include <sys/errno.h>
55 #include <sys/device.h>
56 #include <sys/malloc.h>
57 #include <sys/module.h>
58 #include <sys/uio.h>
59 #include <sys/proc.h>
60 #include <sys/ioctl.h>
61 #include <sys/conf.h>
62
63 #include <sys/bus.h>
64
65 #include <dev/spkrio.h>
66 #include <dev/spkrvar.h>
67 #include <dev/wscons/wsconsio.h>
68 #include <dev/wscons/wsbellvar.h>
69 #include <dev/wscons/wsbellmuxvar.h>
70
71 dev_type_open(spkropen);
72 dev_type_close(spkrclose);
73 dev_type_write(spkrwrite);
74 dev_type_ioctl(spkrioctl);
75
76 const struct cdevsw spkr_cdevsw = {
77 .d_open = spkropen,
78 .d_close = spkrclose,
79 .d_read = noread,
80 .d_write = spkrwrite,
81 .d_ioctl = spkrioctl,
82 .d_stop = nostop,
83 .d_tty = notty,
84 .d_poll = nopoll,
85 .d_mmap = nommap,
86 .d_kqfilter = nokqfilter,
87 .d_discard = nodiscard,
88 .d_flag = D_OTHER
89 };
90
91 static void playinit(struct spkr_softc *);
92 static void playtone(struct spkr_softc *, int, int, int);
93 static void playstring(struct spkr_softc *, const char *, size_t);
94
95 /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
96 *
97 * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
98 * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
99 * Requires spkr_tone(), spkr_rest(). String play is not interruptible
100 * except possibly at physical block boundaries.
101 */
102
103 #define dtoi(c) ((c) - '0')
104
105 /*
106 * Magic number avoidance...
107 */
108 #define SECS_PER_MIN 60 /* seconds per minute */
109 #define WHOLE_NOTE 4 /* quarter notes per whole note */
110 #define MIN_VALUE 64 /* the most we can divide a note by */
111 #define DFLT_VALUE 4 /* default value (quarter-note) */
112 #define FILLTIME 8 /* for articulation, break note in parts */
113 #define STACCATO 6 /* 6/8 = 3/4 of note is filled */
114 #define NORMAL 7 /* 7/8ths of note interval is filled */
115 #define LEGATO 8 /* all of note interval is filled */
116 #define DFLT_OCTAVE 4 /* default octave */
117 #define MIN_TEMPO 32 /* minimum tempo */
118 #define DFLT_TEMPO 120 /* default tempo */
119 #define MAX_TEMPO 255 /* max tempo */
120 #define NUM_MULT 3 /* numerator of dot multiplier */
121 #define DENOM_MULT 2 /* denominator of dot multiplier */
122
123 /* letter to half-tone: A B C D E F G */
124 static const int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
125
126 /*
127 * This is the American Standard A440 Equal-Tempered scale with frequencies
128 * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
129 * our octave 0 is standard octave 2.
130 */
131 #define OCTAVE_NOTES 12 /* semitones per octave */
132 static const int pitchtab[] =
133 {
134 /* C C# D D# E F F# G G# A A# B*/
135 /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
136 /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
137 /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
138 /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
139 /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
140 /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
141 /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
142 };
143 #define NOCTAVES (int)(__arraycount(pitchtab) / OCTAVE_NOTES)
144
145 static void
146 playinit(struct spkr_softc *sc)
147 {
148 sc->sc_octave = DFLT_OCTAVE;
149 sc->sc_whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
150 sc->sc_fill = NORMAL;
151 sc->sc_value = DFLT_VALUE;
152 sc->sc_octtrack = false;
153 sc->sc_octprefix = true;/* act as though there was an initial O(n) */
154 }
155
156 /* play tone of proper duration for current rhythm signature */
157 static void
158 playtone(struct spkr_softc *sc, int pitch, int val, int sustain)
159 {
160 int sound, silence, snum = 1, sdenom = 1;
161
162 /* this weirdness avoids floating-point arithmetic */
163 for (; sustain; sustain--) {
164 snum *= NUM_MULT;
165 sdenom *= DENOM_MULT;
166 }
167
168 if (pitch == -1) {
169 (*sc->sc_rest)(sc->sc_dev, sc->sc_whole
170 * snum / (val * sdenom));
171 return;
172 }
173
174 int fac = sc->sc_whole * (FILLTIME - sc->sc_fill);
175 int fval = FILLTIME * val;
176 sound = (sc->sc_whole * snum) / (val * sdenom) - fac / fval;
177 silence = fac * snum / (fval * sdenom);
178
179 #ifdef SPKRDEBUG
180 aprint_debug_dev(sc->sc_dev,
181 "%s: pitch %d for %d ticks, rest for %d ticks\n", __func__,
182 pitch, sound, silence);
183 #endif /* SPKRDEBUG */
184
185 (*sc->sc_tone)(sc->sc_dev, pitchtab[pitch], sound);
186 if (sc->sc_fill != LEGATO)
187 (*sc->sc_rest)(sc->sc_dev, silence);
188 }
189
190 /* interpret and play an item from a notation string */
191 static void
192 playstring(struct spkr_softc *sc, const char *cp, size_t slen)
193 {
194 int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
195
196 #define GETNUM(cp, v) \
197 for (v = 0; slen > 0 && isdigit((unsigned char)cp[1]); ) { \
198 v = v * 10 + (*++cp - '0'); \
199 slen--; \
200 }
201
202 for (; slen--; cp++) {
203 int sustain, timeval, tempo;
204 char c = toupper((unsigned char)*cp);
205
206 #ifdef SPKRDEBUG
207 aprint_debug_dev(sc->sc_dev, "%s: %c (%x)\n", __func__, c, c);
208 #endif /* SPKRDEBUG */
209
210 switch (c) {
211 case 'A': case 'B': case 'C': case 'D':
212 case 'E': case 'F': case 'G':
213 /* compute pitch */
214 pitch = notetab[c - 'A'] + sc->sc_octave * OCTAVE_NOTES;
215
216 /* this may be followed by an accidental sign */
217 if (slen > 0 && (cp[1] == '#' || cp[1] == '+')) {
218 ++pitch;
219 ++cp;
220 slen--;
221 } else if (slen > 0 && cp[1] == '-') {
222 --pitch;
223 ++cp;
224 slen--;
225 }
226
227 /*
228 * If octave-tracking mode is on, and there has been no
229 * octave- setting prefix, find the version of the
230 * current letter note * closest to the last
231 * regardless of octave.
232 */
233 if (sc->sc_octtrack && !sc->sc_octprefix) {
234 int d = abs(pitch - lastpitch);
235 if (d > abs(pitch + OCTAVE_NOTES - lastpitch)) {
236 if (sc->sc_octave < NOCTAVES - 1) {
237 ++sc->sc_octave;
238 pitch += OCTAVE_NOTES;
239 }
240 }
241
242 if (d > abs(pitch - OCTAVE_NOTES - lastpitch)) {
243 if (sc->sc_octave > 0) {
244 --sc->sc_octave;
245 pitch -= OCTAVE_NOTES;
246 }
247 }
248 }
249 sc->sc_octprefix = false;
250 lastpitch = pitch;
251
252 /*
253 * ...which may in turn be followed by an override
254 * time value
255 */
256 GETNUM(cp, timeval);
257 if (timeval <= 0 || timeval > MIN_VALUE)
258 timeval = sc->sc_value;
259
260 /* ...and/or sustain dots */
261 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
262 slen--;
263 sustain++;
264 }
265
266 /* time to emit the actual tone */
267 playtone(sc, pitch, timeval, sustain);
268 break;
269
270 case 'O':
271 if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
272 sc->sc_octprefix = sc->sc_octtrack = false;
273 ++cp;
274 slen--;
275 } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
276 sc->sc_octtrack = true;
277 ++cp;
278 slen--;
279 } else {
280 GETNUM(cp, sc->sc_octave);
281 if (sc->sc_octave >= NOCTAVES)
282 sc->sc_octave = DFLT_OCTAVE;
283 sc->sc_octprefix = true;
284 }
285 break;
286
287 case '>':
288 if (sc->sc_octave < NOCTAVES - 1)
289 sc->sc_octave++;
290 sc->sc_octprefix = true;
291 break;
292
293 case '<':
294 if (sc->sc_octave > 0)
295 sc->sc_octave--;
296 sc->sc_octprefix = true;
297 break;
298
299 case 'N':
300 GETNUM(cp, pitch);
301 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
302 slen--;
303 sustain++;
304 }
305 playtone(sc, pitch - 1, sc->sc_value, sustain);
306 break;
307
308 case 'L':
309 GETNUM(cp, sc->sc_value);
310 if (sc->sc_value <= 0 || sc->sc_value > MIN_VALUE)
311 sc->sc_value = DFLT_VALUE;
312 break;
313
314 case 'P':
315 case '~':
316 /* this may be followed by an override time value */
317 GETNUM(cp, timeval);
318 if (timeval <= 0 || timeval > MIN_VALUE)
319 timeval = sc->sc_value;
320 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
321 slen--;
322 sustain++;
323 }
324 playtone(sc, -1, timeval, sustain);
325 break;
326
327 case 'T':
328 GETNUM(cp, tempo);
329 if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
330 tempo = DFLT_TEMPO;
331 sc->sc_whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
332 break;
333
334 case 'M':
335 if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
336 sc->sc_fill = NORMAL;
337 ++cp;
338 slen--;
339 } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
340 sc->sc_fill = LEGATO;
341 ++cp;
342 slen--;
343 } else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's')) {
344 sc->sc_fill = STACCATO;
345 ++cp;
346 slen--;
347 }
348 break;
349 }
350 }
351 }
352
353 /******************* UNIX DRIVER HOOKS BEGIN HERE **************************
354 *
355 * This section implements driver hooks to run playstring() and the spkr_tone()
356 * and spkr_rest() functions defined above.
357 */
358 extern struct cfdriver spkr_cd;
359 #define spkrenter(d) device_lookup_private(&spkr_cd, d)
360
361 void
362 spkr_attach(device_t self, void (*tone)(device_t, u_int, u_int),
363 void (*rest)(device_t, int))
364 {
365 struct spkr_softc *sc = device_private(self);
366
367 #ifdef SPKRDEBUG
368 aprint_debug("%s: entering for unit %d\n", __func__, self->dv_unit);
369 #endif /* SPKRDEBUG */
370 sc->sc_dev = self;
371 sc->sc_tone = tone;
372 sc->sc_rest = rest;
373 sc->sc_inbuf = NULL;
374 sc->sc_wsbelldev = NULL;
375
376 spkr_rescan(self, "", NULL);
377 }
378
379 int
380 spkr_detach(device_t self, int flags)
381 {
382 struct spkr_softc *sc = device_private(self);
383 int rc;
384
385 #ifdef SPKRDEBUG
386 aprint_debug("%s: entering for unit %d\n", __func__, self->dv_unit);
387 #endif /* SPKRDEBUG */
388 if (sc == NULL)
389 return ENXIO;
390
391 /* XXXNS If speaker never closes, we cannot complete the detach. */
392 while ((flags & DETACH_FORCE) != 0 && sc->sc_inbuf != NULL)
393 kpause("spkrwait", TRUE, 1, NULL);
394 if (sc->sc_inbuf != NULL)
395 return EBUSY;
396
397 rc = config_detach_children(self, flags);
398
399 return rc;
400 }
401
402 /* ARGSUSED */
403 int
404 spkr_rescan(device_t self, const char *iattr, const int *locators)
405 {
406 #if NWSMUX > 0
407 struct spkr_softc *sc = device_private(self);
408 struct wsbelldev_attach_args a;
409
410 if (sc->sc_wsbelldev == NULL) {
411 a.accesscookie = sc;
412 sc->sc_wsbelldev = config_found(self, &a, wsbelldevprint);
413 }
414 #endif
415 return 0;
416 }
417
418 int
419 spkr_childdet(device_t self, device_t child)
420 {
421 struct spkr_softc *sc = device_private(self);
422
423 if (sc->sc_wsbelldev == child)
424 sc->sc_wsbelldev = NULL;
425
426 return 0;
427 }
428
429 int
430 spkropen(dev_t dev, int flags, int mode, struct lwp *l)
431 {
432 #ifdef SPKRDEBUG
433 aprint_debug("%s: entering with dev = %"PRIx64"\n", __func__, dev);
434 #endif /* SPKRDEBUG */
435 struct spkr_softc *sc = spkrenter(minor(dev));
436
437 if (sc == NULL)
438 return ENXIO;
439 if (sc->sc_inbuf != NULL)
440 return EBUSY;
441
442 sc->sc_inbuf = malloc(DEV_BSIZE, M_DEVBUF, M_WAITOK);
443 playinit(sc);
444 return 0;
445 }
446
447 int
448 spkrwrite(dev_t dev, struct uio *uio, int flags)
449 {
450 #ifdef SPKRDEBUG
451 aprint_debug("%s: entering with dev = %"PRIx64", count = %zu\n",
452 __func__, dev, uio->uio_resid);
453 #endif /* SPKRDEBUG */
454 struct spkr_softc *sc = spkrenter(minor(dev));
455
456 if (sc == NULL)
457 return ENXIO;
458 if (sc->sc_inbuf == NULL)
459 return EINVAL;
460
461 size_t n = min(DEV_BSIZE, uio->uio_resid);
462 int error = uiomove(sc->sc_inbuf, n, uio);
463 if (error)
464 return error;
465 playstring(sc, sc->sc_inbuf, n);
466 return 0;
467 }
468
469 int
470 spkrclose(dev_t dev, int flags, int mode, struct lwp *l)
471 {
472 #ifdef SPKRDEBUG
473 aprint_debug("%s: entering with dev = %"PRIx64"\n", __func__, dev);
474 #endif /* SPKRDEBUG */
475 struct spkr_softc *sc = spkrenter(minor(dev));
476
477 if (sc == NULL)
478 return ENXIO;
479 if (sc->sc_inbuf == NULL)
480 return EINVAL;
481
482 sc->sc_tone(sc->sc_dev, 0, 0);
483 free(sc->sc_inbuf, M_DEVBUF);
484 sc->sc_inbuf = NULL;
485
486 return 0;
487 }
488
489 static void
490 playonetone(struct spkr_softc *sc, tone_t *tp)
491 {
492 if (tp->frequency == 0)
493 (*sc->sc_rest)(sc->sc_dev, tp->duration);
494 else
495 (*sc->sc_tone)(sc->sc_dev, tp->frequency, tp->duration);
496 }
497
498 int
499 spkrioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
500 {
501 tone_t *tp;
502 tone_t ttp;
503 int error;
504 #ifdef SPKRDEBUG
505 aprint_debug("%s: entering with dev = %"PRIx64", cmd = %lx\n",
506 __func__, dev, cmd);
507 #endif /* SPKRDEBUG */
508
509 struct spkr_softc *sc = spkrenter(minor(dev));
510
511 if (sc == NULL)
512 return ENXIO;
513 if (sc->sc_inbuf == NULL)
514 return EINVAL;
515
516 switch (cmd) {
517 case SPKRTONE:
518 playonetone(sc, data);
519 return 0;
520 case SPKRTUNE:
521 for (tp = *(void **)data;; tp++) {
522 error = copyin(tp, &ttp, sizeof(tone_t));
523 if (error)
524 return(error);
525 if (ttp.duration == 0)
526 break;
527 playonetone(sc, &ttp);
528 }
529 return 0;
530 case SPKRGETVOL:
531 if (data != NULL)
532 *(u_int *)data = sc->sc_vol;
533 return 0;
534 case SPKRSETVOL:
535 if (data != NULL && *(u_int *)data <= 100)
536 sc->sc_vol = *(u_int *)data;
537 return 0;
538 default:
539 return ENOTTY;
540 }
541 }
542
543 #ifdef _MODULE
544 extern struct cfdriver spkr_cd;
545 #include "ioconf.c"
546 #endif
547
548 MODULE(MODULE_CLASS_DRIVER, spkr, "audio" /* and/or pcppi */ );
549
550 int
551 spkr_modcmd(modcmd_t cmd, void *arg)
552 {
553 int error = 0;
554 #ifdef _MODULE
555 devmajor_t bmajor, cmajor;
556 #endif
557
558 switch(cmd) {
559 case MODULE_CMD_INIT:
560 #ifdef _MODULE
561 bmajor = cmajor = -1;
562 error = devsw_attach(spkr_cd.cd_name, NULL, &bmajor,
563 &spkr_cdevsw, &cmajor);
564 if (error)
565 break;
566
567 error = config_init_component(cfdriver_ioconf_spkr,
568 cfattach_ioconf_spkr, cfdata_ioconf_spkr);
569 if (error) {
570 devsw_detach(NULL, &spkr_cdevsw);
571 }
572 #endif
573 break;
574
575 case MODULE_CMD_FINI:
576 #ifdef _MODULE
577 devsw_detach(NULL, &spkr_cdevsw);
578 error = config_fini_component(cfdriver_ioconf_spkr,
579 cfattach_ioconf_spkr, cfdata_ioconf_spkr);
580 if (error)
581 devsw_attach(spkr_cd.cd_name, NULL, &bmajor,
582 &spkr_cdevsw, &cmajor);
583 #endif
584 break;
585
586 default:
587 error = ENOTTY;
588 break;
589 }
590
591 return error;
592 }
593