1 /*- 2 * spkr.c -- device driver for console speaker 3 * 4 * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993 5 * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su> 6 * modified for PC98 by Kakefuda 7 */ 8 9 #include <sys/cdefs.h> 10 __FBSDID("$FreeBSD$"); 11 12 #include <sys/param.h> 13 #include <sys/systm.h> 14 #include <sys/bus.h> 15 #include <sys/kernel.h> 16 #include <sys/module.h> 17 #include <sys/uio.h> 18 #include <sys/conf.h> 19 #include <sys/ctype.h> 20 #include <sys/malloc.h> 21 #include <isa/isavar.h> 22 #include <machine/clock.h> 23 #include <machine/ppireg.h> 24 #include <machine/timerreg.h> 25 #include <dev/speaker/speaker.h> 26 27 static d_open_t spkropen; 28 static d_close_t spkrclose; 29 static d_write_t spkrwrite; 30 static d_ioctl_t spkrioctl; 31 32 static struct cdevsw spkr_cdevsw = { 33 .d_version = D_VERSION, 34 .d_flags = D_NEEDGIANT, 35 .d_open = spkropen, 36 .d_close = spkrclose, 37 .d_write = spkrwrite, 38 .d_ioctl = spkrioctl, 39 .d_name = "spkr", 40 }; 41 42 static MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer"); 43 44 /**************** MACHINE DEPENDENT PART STARTS HERE ************************* 45 * 46 * This section defines a function tone() which causes a tone of given 47 * frequency and duration from the ISA console speaker. 48 * Another function endtone() is defined to force sound off, and there is 49 * also a rest() entry point to do pauses. 50 * 51 * Audible sound is generated using the Programmable Interval Timer (PIT) and 52 * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The 53 * PPI controls whether sound is passed through at all; the PIT's channel 2 is 54 * used to generate clicks (a square wave) of whatever frequency is desired. 55 */ 56 57 #ifdef PC98 58 #define SPKR_DESC "PC98 speaker" 59 #else 60 #define SPKR_DESC "PC speaker" 61 #endif 62 63 #define SPKRPRI PSOCK 64 static char endtone, endrest; 65 66 static void tone(unsigned int thz, unsigned int centisecs); 67 static void rest(int centisecs); 68 static void playinit(void); 69 static void playtone(int pitch, int value, int sustain); 70 static void playstring(char *cp, size_t slen); 71 72 /* emit tone of frequency thz for given number of centisecs */ 73 static void 74 tone(thz, centisecs) 75 unsigned int thz, centisecs; 76 { 77 unsigned int divisor; 78 int sps, timo; 79 80 if (thz <= 0) 81 return; 82 83 divisor = timer_freq / thz; 84 85 #ifdef DEBUG 86 (void) printf("tone: thz=%d centisecs=%d\n", thz, centisecs); 87 #endif /* DEBUG */ 88 89 /* set timer to generate clicks at given frequency in Hertz */ 90 sps = splclock(); 91 92 if (timer_spkr_acquire()) { 93 /* enter list of waiting procs ??? */ 94 splx(sps); 95 return; 96 } 97 splx(sps); 98 disable_intr(); 99 spkr_set_pitch(divisor); 100 enable_intr(); 101 102 /* turn the speaker on */ 103 ppi_spkr_on(); 104 105 /* 106 * Set timeout to endtone function, then give up the timeslice. 107 * This is so other processes can execute while the tone is being 108 * emitted. 109 */ 110 timo = centisecs * hz / 100; 111 if (timo > 0) 112 tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", timo); 113 ppi_spkr_off(); 114 sps = splclock(); 115 timer_spkr_release(); 116 splx(sps); 117 } 118 119 /* rest for given number of centisecs */ 120 static void 121 rest(centisecs) 122 int centisecs; 123 { 124 int timo; 125 126 /* 127 * Set timeout to endrest function, then give up the timeslice. 128 * This is so other processes can execute while the rest is being 129 * waited out. 130 */ 131 #ifdef DEBUG 132 (void) printf("rest: %d\n", centisecs); 133 #endif /* DEBUG */ 134 timo = centisecs * hz / 100; 135 if (timo > 0) 136 tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", timo); 137 } 138 139 /**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 140 * 141 * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 142 * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 143 * tracking facility are added. 144 * Requires tone(), rest(), and endtone(). String play is not interruptible 145 * except possibly at physical block boundaries. 146 */ 147 148 typedef int bool; 149 #define TRUE 1 150 #define FALSE 0 151 152 #define dtoi(c) ((c) - '0') 153 154 static int octave; /* currently selected octave */ 155 static int whole; /* whole-note time at current tempo, in ticks */ 156 static int value; /* whole divisor for note time, quarter note = 1 */ 157 static int fill; /* controls spacing of notes */ 158 static bool octtrack; /* octave-tracking on? */ 159 static bool octprefix; /* override current octave-tracking state? */ 160 161 /* 162 * Magic number avoidance... 163 */ 164 #define SECS_PER_MIN 60 /* seconds per minute */ 165 #define WHOLE_NOTE 4 /* quarter notes per whole note */ 166 #define MIN_VALUE 64 /* the most we can divide a note by */ 167 #define DFLT_VALUE 4 /* default value (quarter-note) */ 168 #define FILLTIME 8 /* for articulation, break note in parts */ 169 #define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 170 #define NORMAL 7 /* 7/8ths of note interval is filled */ 171 #define LEGATO 8 /* all of note interval is filled */ 172 #define DFLT_OCTAVE 4 /* default octave */ 173 #define MIN_TEMPO 32 /* minimum tempo */ 174 #define DFLT_TEMPO 120 /* default tempo */ 175 #define MAX_TEMPO 255 /* max tempo */ 176 #define NUM_MULT 3 /* numerator of dot multiplier */ 177 #define DENOM_MULT 2 /* denominator of dot multiplier */ 178 179 /* letter to half-tone: A B C D E F G */ 180 static int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 181 182 /* 183 * This is the American Standard A440 Equal-Tempered scale with frequencies 184 * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 185 * our octave 0 is standard octave 2. 186 */ 187 #define OCTAVE_NOTES 12 /* semitones per octave */ 188 static int pitchtab[] = 189 { 190 /* C C# D D# E F F# G G# A A# B*/ 191 /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 192 /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 193 /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 194 /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 195 /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 196 /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 197 /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 198 }; 199 200 static void 201 playinit() 202 { 203 octave = DFLT_OCTAVE; 204 whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 205 fill = NORMAL; 206 value = DFLT_VALUE; 207 octtrack = FALSE; 208 octprefix = TRUE; /* act as though there was an initial O(n) */ 209 } 210 211 /* play tone of proper duration for current rhythm signature */ 212 static void 213 playtone(pitch, value, sustain) 214 int pitch, value, sustain; 215 { 216 register int sound, silence, snum = 1, sdenom = 1; 217 218 /* this weirdness avoids floating-point arithmetic */ 219 for (; sustain; sustain--) 220 { 221 /* See the BUGS section in the man page for discussion */ 222 snum *= NUM_MULT; 223 sdenom *= DENOM_MULT; 224 } 225 226 if (value == 0 || sdenom == 0) 227 return; 228 229 if (pitch == -1) 230 rest(whole * snum / (value * sdenom)); 231 else 232 { 233 sound = (whole * snum) / (value * sdenom) 234 - (whole * (FILLTIME - fill)) / (value * FILLTIME); 235 silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 236 237 #ifdef DEBUG 238 (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 239 pitch, sound, silence); 240 #endif /* DEBUG */ 241 242 tone(pitchtab[pitch], sound); 243 if (fill != LEGATO) 244 rest(silence); 245 } 246 } 247 248 /* interpret and play an item from a notation string */ 249 static void 250 playstring(cp, slen) 251 char *cp; 252 size_t slen; 253 { 254 int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 255 256 #define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 257 {v = v * 10 + (*++cp - '0'); slen--;} 258 for (; slen--; cp++) 259 { 260 int sustain, timeval, tempo; 261 register char c = toupper(*cp); 262 263 #ifdef DEBUG 264 (void) printf("playstring: %c (%x)\n", c, c); 265 #endif /* DEBUG */ 266 267 switch (c) 268 { 269 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 270 271 /* compute pitch */ 272 pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 273 274 /* this may be followed by an accidental sign */ 275 if (cp[1] == '#' || cp[1] == '+') 276 { 277 ++pitch; 278 ++cp; 279 slen--; 280 } 281 else if (cp[1] == '-') 282 { 283 --pitch; 284 ++cp; 285 slen--; 286 } 287 288 /* 289 * If octave-tracking mode is on, and there has been no octave- 290 * setting prefix, find the version of the current letter note 291 * closest to the last regardless of octave. 292 */ 293 if (octtrack && !octprefix) 294 { 295 if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) 296 { 297 ++octave; 298 pitch += OCTAVE_NOTES; 299 } 300 301 if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) 302 { 303 --octave; 304 pitch -= OCTAVE_NOTES; 305 } 306 } 307 octprefix = FALSE; 308 lastpitch = pitch; 309 310 /* ...which may in turn be followed by an override time value */ 311 GETNUM(cp, timeval); 312 if (timeval <= 0 || timeval > MIN_VALUE) 313 timeval = value; 314 315 /* ...and/or sustain dots */ 316 for (sustain = 0; cp[1] == '.'; cp++) 317 { 318 slen--; 319 sustain++; 320 } 321 322 /* ...and/or a slur mark */ 323 oldfill = fill; 324 if (cp[1] == '_') 325 { 326 fill = LEGATO; 327 ++cp; 328 slen--; 329 } 330 331 /* time to emit the actual tone */ 332 playtone(pitch, timeval, sustain); 333 334 fill = oldfill; 335 break; 336 337 case 'O': 338 if (cp[1] == 'N' || cp[1] == 'n') 339 { 340 octprefix = octtrack = FALSE; 341 ++cp; 342 slen--; 343 } 344 else if (cp[1] == 'L' || cp[1] == 'l') 345 { 346 octtrack = TRUE; 347 ++cp; 348 slen--; 349 } 350 else 351 { 352 GETNUM(cp, octave); 353 if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES) 354 octave = DFLT_OCTAVE; 355 octprefix = TRUE; 356 } 357 break; 358 359 case '>': 360 if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1) 361 octave++; 362 octprefix = TRUE; 363 break; 364 365 case '<': 366 if (octave > 0) 367 octave--; 368 octprefix = TRUE; 369 break; 370 371 case 'N': 372 GETNUM(cp, pitch); 373 for (sustain = 0; cp[1] == '.'; cp++) 374 { 375 slen--; 376 sustain++; 377 } 378 oldfill = fill; 379 if (cp[1] == '_') 380 { 381 fill = LEGATO; 382 ++cp; 383 slen--; 384 } 385 playtone(pitch - 1, value, sustain); 386 fill = oldfill; 387 break; 388 389 case 'L': 390 GETNUM(cp, value); 391 if (value <= 0 || value > MIN_VALUE) 392 value = DFLT_VALUE; 393 break; 394 395 case 'P': 396 case '~': 397 /* this may be followed by an override time value */ 398 GETNUM(cp, timeval); 399 if (timeval <= 0 || timeval > MIN_VALUE) 400 timeval = value; 401 for (sustain = 0; cp[1] == '.'; cp++) 402 { 403 slen--; 404 sustain++; 405 } 406 playtone(-1, timeval, sustain); 407 break; 408 409 case 'T': 410 GETNUM(cp, tempo); 411 if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 412 tempo = DFLT_TEMPO; 413 whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / tempo; 414 break; 415 416 case 'M': 417 if (cp[1] == 'N' || cp[1] == 'n') 418 { 419 fill = NORMAL; 420 ++cp; 421 slen--; 422 } 423 else if (cp[1] == 'L' || cp[1] == 'l') 424 { 425 fill = LEGATO; 426 ++cp; 427 slen--; 428 } 429 else if (cp[1] == 'S' || cp[1] == 's') 430 { 431 fill = STACCATO; 432 ++cp; 433 slen--; 434 } 435 break; 436 } 437 } 438 } 439 440 /******************* UNIX DRIVER HOOKS BEGIN HERE ************************** 441 * 442 * This section implements driver hooks to run playstring() and the tone(), 443 * endtone(), and rest() functions defined above. 444 */ 445 446 static int spkr_active = FALSE; /* exclusion flag */ 447 static char *spkr_inbuf; /* incoming buf */ 448 449 static int 450 spkropen(dev, flags, fmt, td) 451 struct cdev *dev; 452 int flags; 453 int fmt; 454 struct thread *td; 455 { 456 #ifdef DEBUG 457 (void) printf("spkropen: entering with dev = %s\n", devtoname(dev)); 458 #endif /* DEBUG */ 459 460 if (minor(dev) != 0) 461 return(ENXIO); 462 else if (spkr_active) 463 return(EBUSY); 464 else 465 { 466 #ifdef DEBUG 467 (void) printf("spkropen: about to perform play initialization\n"); 468 #endif /* DEBUG */ 469 playinit(); 470 spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK); 471 spkr_active = TRUE; 472 return(0); 473 } 474 } 475 476 static int 477 spkrwrite(dev, uio, ioflag) 478 struct cdev *dev; 479 struct uio *uio; 480 int ioflag; 481 { 482 #ifdef DEBUG 483 printf("spkrwrite: entering with dev = %s, count = %d\n", 484 devtoname(dev), uio->uio_resid); 485 #endif /* DEBUG */ 486 487 if (minor(dev) != 0) 488 return(ENXIO); 489 else if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */ 490 return(E2BIG); 491 else 492 { 493 unsigned n; 494 char *cp; 495 int error; 496 497 n = uio->uio_resid; 498 cp = spkr_inbuf; 499 error = uiomove(cp, n, uio); 500 if (!error) { 501 cp[n] = '\0'; 502 playstring(cp, n); 503 } 504 return(error); 505 } 506 } 507 508 static int 509 spkrclose(dev, flags, fmt, td) 510 struct cdev *dev; 511 int flags; 512 int fmt; 513 struct thread *td; 514 { 515 #ifdef DEBUG 516 (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev)); 517 #endif /* DEBUG */ 518 519 if (minor(dev) != 0) 520 return(ENXIO); 521 else 522 { 523 wakeup(&endtone); 524 wakeup(&endrest); 525 free(spkr_inbuf, M_SPKR); 526 spkr_active = FALSE; 527 return(0); 528 } 529 } 530 531 static int 532 spkrioctl(dev, cmd, cmdarg, flags, td) 533 struct cdev *dev; 534 unsigned long cmd; 535 caddr_t cmdarg; 536 int flags; 537 struct thread *td; 538 { 539 #ifdef DEBUG 540 (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n", 541 devtoname(dev), cmd); 542 #endif /* DEBUG */ 543 544 if (minor(dev) != 0) 545 return(ENXIO); 546 else if (cmd == SPKRTONE) 547 { 548 tone_t *tp = (tone_t *)cmdarg; 549 550 if (tp->frequency == 0) 551 rest(tp->duration); 552 else 553 tone(tp->frequency, tp->duration); 554 return 0; 555 } 556 else if (cmd == SPKRTUNE) 557 { 558 tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 559 tone_t ttp; 560 int error; 561 562 for (; ; tp++) { 563 error = copyin(tp, &ttp, sizeof(tone_t)); 564 if (error) 565 return(error); 566 if (ttp.duration == 0) 567 break; 568 if (ttp.frequency == 0) 569 rest(ttp.duration); 570 else 571 tone(ttp.frequency, ttp.duration); 572 } 573 return(0); 574 } 575 return(EINVAL); 576 } 577 578 /* 579 * Install placeholder to claim the resources owned by the 580 * AT tone generator. 581 */ 582 static struct isa_pnp_id speaker_ids[] = { 583 #ifndef PC98 584 { 0x0008d041 /* PNP0800 */, SPKR_DESC }, 585 #endif 586 { 0 } 587 }; 588 589 static struct cdev *speaker_dev; 590 591 static int 592 speaker_probe(device_t dev) 593 { 594 int error; 595 596 error = ISA_PNP_PROBE(device_get_parent(dev), dev, speaker_ids); 597 598 /* PnP match */ 599 if (error == 0) 600 return (0); 601 602 /* No match */ 603 if (error == ENXIO) 604 return (ENXIO); 605 606 /* Not configured by hints. */ 607 if (strncmp(device_get_name(dev), "speaker", 9)) 608 return (ENXIO); 609 610 device_set_desc(dev, SPKR_DESC); 611 612 return (0); 613 } 614 615 static int 616 speaker_attach(device_t dev) 617 { 618 619 if (speaker_dev) { 620 device_printf(dev, "Already attached!\n"); 621 return (ENXIO); 622 } 623 624 speaker_dev = make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 625 "speaker"); 626 return (0); 627 } 628 629 static int 630 speaker_detach(device_t dev) 631 { 632 destroy_dev(speaker_dev); 633 return (0); 634 } 635 636 static device_method_t speaker_methods[] = { 637 /* Device interface */ 638 DEVMETHOD(device_probe, speaker_probe), 639 DEVMETHOD(device_attach, speaker_attach), 640 DEVMETHOD(device_detach, speaker_detach), 641 DEVMETHOD(device_shutdown, bus_generic_shutdown), 642 DEVMETHOD(device_suspend, bus_generic_suspend), 643 DEVMETHOD(device_resume, bus_generic_resume), 644 { 0, 0 } 645 }; 646 647 static driver_t speaker_driver = { 648 "speaker", 649 speaker_methods, 650 1, /* no softc */ 651 }; 652 653 static devclass_t speaker_devclass; 654 655 DRIVER_MODULE(speaker, isa, speaker_driver, speaker_devclass, 0, 0); 656 #ifndef PC98 657 DRIVER_MODULE(speaker, acpi, speaker_driver, speaker_devclass, 0, 0); 658 #endif 659 660 /* spkr.c ends here */ 661