1 /* $NetBSD: spkr.c,v 1.25 2023/03/31 15:00:26 riastradh 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.25 2023/03/31 15:00:26 riastradh 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/kmem.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 * String play is not interruptible except possibly at physical block 103 * boundaries. 104 */ 105 106 /* 107 * Magic number avoidance... 108 */ 109 #define SECS_PER_MIN 60 /* seconds per minute */ 110 #define WHOLE_NOTE 4 /* quarter notes per whole note */ 111 #define MIN_VALUE 64 /* the most we can divide a note by */ 112 #define DFLT_VALUE 4 /* default value (quarter-note) */ 113 #define FILLTIME 8 /* for articulation, break note in parts */ 114 #define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 115 #define NORMAL 7 /* 7/8ths of note interval is filled */ 116 #define LEGATO 8 /* all of note interval is filled */ 117 #define DFLT_OCTAVE 4 /* default octave */ 118 #define MIN_TEMPO 32 /* minimum tempo */ 119 #define DFLT_TEMPO 120 /* default tempo */ 120 #define MAX_TEMPO 255 /* max tempo */ 121 #define NUM_MULT 3 /* numerator of dot multiplier */ 122 #define DENOM_MULT 2 /* denominator of dot multiplier */ 123 124 /* letter to half-tone: A B C D E F G */ 125 static const int notetab[8] = { 9, 11, 0, 2, 4, 5, 7 }; 126 127 /* 128 * This is the American Standard A440 Equal-Tempered scale with frequencies 129 * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 130 * our octave 0 is standard octave 2. 131 */ 132 #define OCTAVE_NOTES 12 /* semitones per octave */ 133 static const int pitchtab[] = 134 { 135 /* C C# D D# E F F# G G# A A# B*/ 136 /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 137 /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 138 /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 139 /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 140 /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 141 /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 142 /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 143 }; 144 #define NOCTAVES (int)(__arraycount(pitchtab) / OCTAVE_NOTES) 145 146 static void 147 playinit(struct spkr_softc *sc) 148 { 149 sc->sc_octave = DFLT_OCTAVE; 150 sc->sc_whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 151 sc->sc_fill = NORMAL; 152 sc->sc_value = DFLT_VALUE; 153 sc->sc_octtrack = false; 154 sc->sc_octprefix = true;/* act as though there was an initial O(n) */ 155 } 156 157 #define SPKRPRI (PZERO - 1) 158 159 /* Rest for given number of ticks */ 160 static void 161 rest(struct spkr_softc *sc, int ticks) 162 { 163 164 #ifdef SPKRDEBUG 165 device_printf(sc->sc_dev, "%s: rest for %d ticks\n", __func__, ticks); 166 #endif /* SPKRDEBUG */ 167 KASSERT(ticks > 0); 168 169 tsleep(sc->sc_dev, SPKRPRI | PCATCH, device_xname(sc->sc_dev), ticks); 170 } 171 172 /* 173 * Play tone of proper duration for current rhythm signature. 174 * note indicates "O0C" = 0, "O0C#" = 1, "O0D" = 2, ... , and 175 * -1 indiacates a rest. 176 * val indicates the length, "L4" = 4, "L8" = 8. 177 * sustain indicates the number of subsequent dots that extend the sound 178 * by one a half. 179 */ 180 static void 181 playtone(struct spkr_softc *sc, int note, int val, int sustain) 182 { 183 int whole; 184 int total; 185 int sound; 186 int silence; 187 188 /* this weirdness avoids floating-point arithmetic */ 189 whole = sc->sc_whole; 190 for (; sustain; sustain--) { 191 whole *= NUM_MULT; 192 val *= DENOM_MULT; 193 } 194 195 /* Total length in tick */ 196 total = whole / val; 197 198 if (note == -1) { 199 #ifdef SPKRDEBUG 200 device_printf(sc->sc_dev, "%s: rest for %d ticks\n", 201 __func__, total); 202 #endif /* SPKRDEBUG */ 203 if (total != 0) 204 rest(sc, total); 205 return; 206 } 207 KASSERTMSG(note < __arraycount(pitchtab), "note=%d", note); 208 209 /* 210 * Rest 1/8 (if NORMAL) or 3/8 (if STACCATO) in tick. 211 * silence should be rounded down. 212 */ 213 silence = total * (FILLTIME - sc->sc_fill) / FILLTIME; 214 sound = total - silence; 215 216 #ifdef SPKRDEBUG 217 device_printf(sc->sc_dev, 218 "%s: note %d for %d ticks, rest for %d ticks\n", __func__, 219 note, sound, silence); 220 #endif /* SPKRDEBUG */ 221 222 if (sound != 0) 223 (*sc->sc_tone)(sc->sc_dev, pitchtab[note], sound); 224 if (silence != 0) 225 rest(sc, silence); 226 } 227 228 /* interpret and play an item from a notation string */ 229 static void 230 playstring(struct spkr_softc *sc, const char *cp, size_t slen) 231 { 232 int pitch; 233 int lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 234 235 #define GETNUM(cp, v) \ 236 for (v = 0; slen > 0 && isdigit((unsigned char)cp[1]); ) { \ 237 if (v > INT_MAX/10 - (cp[1] - '0')) { \ 238 v = INT_MAX; \ 239 continue; \ 240 } \ 241 v = v * 10 + (*++cp - '0'); \ 242 slen--; \ 243 } 244 245 for (; slen--; cp++) { 246 int sustain, timeval, tempo; 247 char c = toupper((unsigned char)*cp); 248 249 #ifdef SPKRDEBUG 250 if (0x20 <= c && c < 0x7f) { 251 device_printf(sc->sc_dev, "%s: '%c'\n", __func__, c); 252 } else { 253 device_printf(sc->sc_dev, "%s: (0x%x)\n", __func__, c); 254 } 255 #endif /* SPKRDEBUG */ 256 257 switch (c) { 258 case 'A': case 'B': case 'C': case 'D': 259 case 'E': case 'F': case 'G': 260 /* compute pitch */ 261 pitch = notetab[c - 'A'] + sc->sc_octave * OCTAVE_NOTES; 262 263 /* this may be followed by an accidental sign */ 264 if (slen > 0 && (cp[1] == '#' || cp[1] == '+')) { 265 ++pitch; 266 ++cp; 267 slen--; 268 } else if (slen > 0 && cp[1] == '-') { 269 --pitch; 270 ++cp; 271 slen--; 272 } 273 274 /* 275 * If octave-tracking mode is on, and there has been no 276 * octave- setting prefix, find the version of the 277 * current letter note * closest to the last 278 * regardless of octave. 279 */ 280 if (sc->sc_octtrack && !sc->sc_octprefix) { 281 int d = abs(pitch - lastpitch); 282 if (d > abs(pitch + OCTAVE_NOTES - lastpitch)) { 283 if (sc->sc_octave < NOCTAVES - 1) { 284 ++sc->sc_octave; 285 pitch += OCTAVE_NOTES; 286 } 287 } 288 289 if (d > abs(pitch - OCTAVE_NOTES - lastpitch)) { 290 if (sc->sc_octave > 0) { 291 --sc->sc_octave; 292 pitch -= OCTAVE_NOTES; 293 } 294 } 295 } 296 sc->sc_octprefix = false; 297 lastpitch = pitch; 298 299 /* 300 * ...which may in turn be followed by an override 301 * time value 302 */ 303 GETNUM(cp, timeval); 304 if (timeval <= 0 || timeval > MIN_VALUE) 305 timeval = sc->sc_value; 306 307 /* ...and/or sustain dots */ 308 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) { 309 slen--; 310 sustain++; 311 } 312 313 /* time to emit the actual tone */ 314 playtone(sc, pitch, timeval, sustain); 315 break; 316 317 case 'O': 318 if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) { 319 sc->sc_octprefix = sc->sc_octtrack = false; 320 ++cp; 321 slen--; 322 } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) { 323 sc->sc_octtrack = true; 324 ++cp; 325 slen--; 326 } else { 327 GETNUM(cp, sc->sc_octave); 328 KASSERTMSG(sc->sc_octave >= 0, "%d", 329 sc->sc_octave); 330 if (sc->sc_octave >= NOCTAVES) 331 sc->sc_octave = DFLT_OCTAVE; 332 sc->sc_octprefix = true; 333 } 334 break; 335 336 case '>': 337 if (sc->sc_octave < NOCTAVES - 1) 338 sc->sc_octave++; 339 sc->sc_octprefix = true; 340 break; 341 342 case '<': 343 if (sc->sc_octave > 0) 344 sc->sc_octave--; 345 sc->sc_octprefix = true; 346 break; 347 348 case 'N': 349 GETNUM(cp, pitch); 350 KASSERTMSG(pitch >= 0, "pitch=%d", pitch); 351 if (pitch >= __arraycount(pitchtab)) 352 break; 353 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) { 354 slen--; 355 sustain++; 356 } 357 playtone(sc, pitch - 1, sc->sc_value, sustain); 358 break; 359 360 case 'L': 361 GETNUM(cp, sc->sc_value); 362 if (sc->sc_value <= 0 || sc->sc_value > MIN_VALUE) 363 sc->sc_value = DFLT_VALUE; 364 break; 365 366 case 'P': 367 case '~': 368 /* this may be followed by an override time value */ 369 GETNUM(cp, timeval); 370 if (timeval <= 0 || timeval > MIN_VALUE) 371 timeval = sc->sc_value; 372 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) { 373 slen--; 374 sustain++; 375 } 376 playtone(sc, -1, timeval, sustain); 377 break; 378 379 case 'T': 380 GETNUM(cp, tempo); 381 if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 382 tempo = DFLT_TEMPO; 383 sc->sc_whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo; 384 break; 385 386 case 'M': 387 if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) { 388 sc->sc_fill = NORMAL; 389 ++cp; 390 slen--; 391 } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) { 392 sc->sc_fill = LEGATO; 393 ++cp; 394 slen--; 395 } else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's')) { 396 sc->sc_fill = STACCATO; 397 ++cp; 398 slen--; 399 } 400 break; 401 } 402 } 403 } 404 405 /******************* UNIX DRIVER HOOKS BEGIN HERE **************************/ 406 #define spkrenter(d) device_lookup_private(&spkr_cd, d) 407 408 /* 409 * Attaches spkr. Specify tone function with the following specification: 410 * 411 * void 412 * tone(device_t self, u_int pitch, u_int tick) 413 * plays a beep with specified parameters. 414 * The argument 'pitch' specifies the pitch of a beep in Hz. The argument 415 * 'tick' specifies the period of a beep in tick(9). This function waits 416 * to finish playing the beep and then halts it. 417 * If the pitch is zero, it halts all sound if any (for compatibility 418 * with the past confused specifications, but there should be no sound at 419 * this point). And it returns immediately, without waiting ticks. So 420 * you cannot use this as a rest. 421 * If the tick is zero, it returns immediately. 422 */ 423 void 424 spkr_attach(device_t self, void (*tone)(device_t, u_int, u_int)) 425 { 426 struct spkr_softc *sc = device_private(self); 427 428 #ifdef SPKRDEBUG 429 aprint_debug("%s: entering for unit %d\n", __func__, 430 device_unit(self)); 431 #endif /* SPKRDEBUG */ 432 sc->sc_dev = self; 433 sc->sc_tone = tone; 434 sc->sc_inbuf = NULL; 435 sc->sc_wsbelldev = NULL; 436 437 spkr_rescan(self, NULL, NULL); 438 } 439 440 int 441 spkr_detach(device_t self, int flags) 442 { 443 struct spkr_softc *sc = device_private(self); 444 int rc; 445 446 #ifdef SPKRDEBUG 447 aprint_debug("%s: entering for unit %d\n", __func__, 448 device_unit(self)); 449 #endif /* SPKRDEBUG */ 450 if (sc == NULL) 451 return ENXIO; 452 453 /* XXXNS If speaker never closes, we cannot complete the detach. */ 454 while ((flags & DETACH_FORCE) != 0 && sc->sc_inbuf != NULL) 455 kpause("spkrwait", TRUE, 1, NULL); 456 if (sc->sc_inbuf != NULL) 457 return EBUSY; 458 459 rc = config_detach_children(self, flags); 460 461 return rc; 462 } 463 464 /* ARGSUSED */ 465 int 466 spkr_rescan(device_t self, const char *iattr, const int *locators) 467 { 468 #if NWSMUX > 0 469 struct spkr_softc *sc = device_private(self); 470 struct wsbelldev_attach_args a; 471 472 if (sc->sc_wsbelldev == NULL) { 473 a.accesscookie = sc; 474 sc->sc_wsbelldev = config_found(self, &a, wsbelldevprint, 475 CFARGS_NONE); 476 } 477 #endif 478 return 0; 479 } 480 481 int 482 spkr_childdet(device_t self, device_t child) 483 { 484 struct spkr_softc *sc = device_private(self); 485 486 if (sc->sc_wsbelldev == child) 487 sc->sc_wsbelldev = NULL; 488 489 return 0; 490 } 491 492 int 493 spkropen(dev_t dev, int flags, int mode, struct lwp *l) 494 { 495 struct spkr_softc *sc = spkrenter(minor(dev)); 496 497 #ifdef SPKRDEBUG 498 device_printf(sc->sc_dev, "%s: entering\n", __func__); 499 #endif /* SPKRDEBUG */ 500 if (sc == NULL) 501 return ENXIO; 502 if (sc->sc_inbuf != NULL) 503 return EBUSY; 504 505 sc->sc_inbuf = kmem_alloc(DEV_BSIZE, KM_SLEEP); 506 playinit(sc); 507 return 0; 508 } 509 510 int 511 spkrwrite(dev_t dev, struct uio *uio, int flags) 512 { 513 struct spkr_softc *sc = spkrenter(minor(dev)); 514 515 #ifdef SPKRDEBUG 516 device_printf(sc->sc_dev, "%s: entering with length = %zu\n", 517 __func__, uio->uio_resid); 518 #endif /* SPKRDEBUG */ 519 if (sc == NULL) 520 return ENXIO; 521 if (sc->sc_inbuf == NULL) 522 return EINVAL; 523 524 size_t n = uimin(DEV_BSIZE, uio->uio_resid); 525 int error = uiomove(sc->sc_inbuf, n, uio); 526 if (error) 527 return error; 528 playstring(sc, sc->sc_inbuf, n); 529 return 0; 530 } 531 532 int 533 spkrclose(dev_t dev, int flags, int mode, struct lwp *l) 534 { 535 struct spkr_softc *sc = spkrenter(minor(dev)); 536 537 #ifdef SPKRDEBUG 538 device_printf(sc->sc_dev, "%s: entering\n", __func__); 539 #endif /* SPKRDEBUG */ 540 if (sc == NULL) 541 return ENXIO; 542 if (sc->sc_inbuf == NULL) 543 return EINVAL; 544 545 sc->sc_tone(sc->sc_dev, 0, 0); 546 kmem_free(sc->sc_inbuf, DEV_BSIZE); 547 sc->sc_inbuf = NULL; 548 549 return 0; 550 } 551 552 /* 553 * Play tone specified by tp. 554 * tp->frequency is the frequency (0 means a rest). 555 * tp->duration is the length in tick (returns immediately if 0). 556 */ 557 static void 558 playonetone(struct spkr_softc *sc, tone_t *tp) 559 { 560 if (tp->duration <= 0) 561 return; 562 563 if (tp->frequency == 0) 564 rest(sc, tp->duration); 565 else 566 (*sc->sc_tone)(sc->sc_dev, tp->frequency, tp->duration); 567 } 568 569 int 570 spkrioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 571 { 572 struct spkr_softc *sc = spkrenter(minor(dev)); 573 tone_t *tp; 574 tone_t ttp; 575 int error; 576 577 #ifdef SPKRDEBUG 578 device_printf(sc->sc_dev, "%s: entering with cmd = %lx\n", 579 __func__, cmd); 580 #endif /* SPKRDEBUG */ 581 if (sc == NULL) 582 return ENXIO; 583 if (sc->sc_inbuf == NULL) 584 return EINVAL; 585 586 switch (cmd) { 587 case SPKRTONE: 588 playonetone(sc, data); 589 return 0; 590 case SPKRTUNE: 591 for (tp = *(void **)data;; tp++) { 592 error = copyin(tp, &ttp, sizeof(tone_t)); 593 if (error) 594 return(error); 595 if (ttp.duration == 0) 596 break; 597 playonetone(sc, &ttp); 598 } 599 return 0; 600 case SPKRGETVOL: 601 if (data != NULL) 602 *(u_int *)data = sc->sc_vol; 603 return 0; 604 case SPKRSETVOL: 605 if (data != NULL && *(u_int *)data <= 100) 606 sc->sc_vol = *(u_int *)data; 607 return 0; 608 default: 609 return ENOTTY; 610 } 611 } 612 613 #ifdef _MODULE 614 #include "ioconf.c" 615 #endif 616 617 MODULE(MODULE_CLASS_DRIVER, spkr, "audio" /* and/or pcppi */ ); 618 619 int 620 spkr_modcmd(modcmd_t cmd, void *arg) 621 { 622 int error = 0; 623 #ifdef _MODULE 624 devmajor_t bmajor, cmajor; 625 #endif 626 627 switch(cmd) { 628 case MODULE_CMD_INIT: 629 #ifdef _MODULE 630 bmajor = cmajor = -1; 631 error = devsw_attach(spkr_cd.cd_name, NULL, &bmajor, 632 &spkr_cdevsw, &cmajor); 633 if (error) 634 break; 635 636 error = config_init_component(cfdriver_ioconf_spkr, 637 cfattach_ioconf_spkr, cfdata_ioconf_spkr); 638 if (error) { 639 devsw_detach(NULL, &spkr_cdevsw); 640 } 641 #endif 642 break; 643 644 case MODULE_CMD_FINI: 645 #ifdef _MODULE 646 error = config_fini_component(cfdriver_ioconf_spkr, 647 cfattach_ioconf_spkr, cfdata_ioconf_spkr); 648 if (error == 0) 649 devsw_detach(NULL, &spkr_cdevsw); 650 #endif 651 break; 652 653 default: 654 error = ENOTTY; 655 break; 656 } 657 658 return error; 659 } 660