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