1 /* $OpenBSD: spkr.c,v 1.6 2001/07/31 22:08:14 pvalchev 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/proc.h> 54 #include <sys/ioctl.h> 55 #include <sys/conf.h> 56 57 #include <dev/isa/pcppivar.h> 58 59 #include <dev/isa/spkrio.h> 60 61 cdev_decl(spkr); 62 63 #define __BROKEN_INDIRECT_CONFIG /* XXX */ 64 #ifdef __BROKEN_INDIRECT_CONFIG 65 int spkrprobe __P((struct device *, void *, void *)); 66 #else 67 int spkrprobe __P((struct device *, struct cfdata *, void *)); 68 #endif 69 void spkrattach __P((struct device *, struct device *, void *)); 70 71 struct spkr_softc { 72 struct device sc_dev; 73 }; 74 75 struct cfattach spkr_ca = { 76 sizeof(struct spkr_softc), spkrprobe, spkrattach 77 }; 78 79 struct cfdriver spkr_cd = { 80 NULL, "spkr", DV_DULL 81 }; 82 83 static pcppi_tag_t ppicookie; 84 85 #define SPKRPRI (PZERO - 1) 86 87 static void tone __P((u_int, u_int)); 88 static void rest __P((int)); 89 static void playinit __P((void)); 90 static void playtone __P((int, int, int)); 91 static void playstring __P((char *, int)); 92 93 static 94 void tone(hz, ticks) 95 /* emit tone of frequency hz for given number of ticks */ 96 u_int hz, ticks; 97 { 98 pcppi_bell(ppicookie, hz, ticks, PCPPI_BELL_SLEEP); 99 } 100 101 static void 102 rest(ticks) 103 /* rest for given number of ticks */ 104 int ticks; 105 { 106 /* 107 * Set timeout to endrest function, then give up the timeslice. 108 * This is so other processes can execute while the rest is being 109 * waited out. 110 */ 111 #ifdef SPKRDEBUG 112 printf("rest: %d\n", ticks); 113 #endif /* SPKRDEBUG */ 114 if (ticks > 0) 115 tsleep(rest, SPKRPRI | PCATCH, "rest", ticks); 116 } 117 118 /**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 119 * 120 * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 121 * M[LNS] are missing and the ~ synonym and octave-tracking facility is added. 122 * Requires tone(), rest(), and endtone(). String play is not interruptible 123 * except possibly at physical block boundaries. 124 */ 125 126 typedef int bool; 127 #define TRUE 1 128 #define FALSE 0 129 130 #define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z'))) 131 #define isdigit(c) (((c) >= '0') && ((c) <= '9')) 132 #define dtoi(c) ((c) - '0') 133 134 static int octave; /* currently selected octave */ 135 static int whole; /* whole-note time at current tempo, in ticks */ 136 static int value; /* whole divisor for note time, quarter note = 1 */ 137 static int fill; /* controls spacing of notes */ 138 static bool octtrack; /* octave-tracking on? */ 139 static bool octprefix; /* override current octave-tracking state? */ 140 141 /* 142 * Magic number avoidance... 143 */ 144 #define SECS_PER_MIN 60 /* seconds per minute */ 145 #define WHOLE_NOTE 4 /* quarter notes per whole note */ 146 #define MIN_VALUE 64 /* the most we can divide a note by */ 147 #define DFLT_VALUE 4 /* default value (quarter-note) */ 148 #define FILLTIME 8 /* for articulation, break note in parts */ 149 #define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 150 #define NORMAL 7 /* 7/8ths of note interval is filled */ 151 #define LEGATO 8 /* all of note interval is filled */ 152 #define DFLT_OCTAVE 4 /* default octave */ 153 #define MIN_TEMPO 32 /* minimum tempo */ 154 #define DFLT_TEMPO 120 /* default tempo */ 155 #define MAX_TEMPO 255 /* max tempo */ 156 #define NUM_MULT 3 /* numerator of dot multiplier */ 157 #define DENOM_MULT 2 /* denominator of dot multiplier */ 158 159 /* letter to half-tone: A B C D E F G */ 160 static int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 161 162 /* 163 * This is the American Standard A440 Equal-Tempered scale with frequencies 164 * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 165 * our octave 0 is standard octave 2. 166 */ 167 #define OCTAVE_NOTES 12 /* semitones per octave */ 168 static int pitchtab[] = 169 { 170 /* C C# D D# E F F# G G# A A# B*/ 171 /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 172 /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 173 /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 174 /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 175 /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 176 /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 177 /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 178 }; 179 #define NOCTAVES (sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES) 180 181 static void 182 playinit() 183 { 184 octave = DFLT_OCTAVE; 185 whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 186 fill = NORMAL; 187 value = DFLT_VALUE; 188 octtrack = FALSE; 189 octprefix = TRUE; /* act as though there was an initial O(n) */ 190 } 191 192 static void 193 playtone(pitch, value, sustain) 194 /* play tone of proper duration for current rhythm signature */ 195 int pitch, value, sustain; 196 { 197 register int sound, silence, snum = 1, sdenom = 1; 198 199 /* this weirdness avoids floating-point arithmetic */ 200 for (; sustain; sustain--) 201 { 202 snum *= NUM_MULT; 203 sdenom *= DENOM_MULT; 204 } 205 206 if (pitch == -1) 207 rest(whole * snum / (value * sdenom)); 208 else if (pitch >= 0 && pitch < (sizeof(pitchtab) / sizeof(pitchtab[0]))) 209 { 210 sound = (whole * snum) / (value * sdenom) 211 - (whole * (FILLTIME - fill)) / (value * FILLTIME); 212 silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 213 214 #ifdef SPKRDEBUG 215 printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 216 pitch, sound, silence); 217 #endif /* SPKRDEBUG */ 218 219 tone(pitchtab[pitch], sound); 220 if (fill != LEGATO) 221 rest(silence); 222 } 223 } 224 225 static void 226 playstring(cp, slen) 227 /* interpret and play an item from a notation string */ 228 char *cp; 229 int slen; 230 { 231 int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 232 233 #define GETNUM(cp, v) for(v=0; slen > 0 && isdigit(cp[1]); ) \ 234 {v = v * 10 + (*++cp - '0'); slen--;} 235 for (; slen--; cp++) 236 { 237 int sustain, timeval, tempo; 238 register char c = toupper(*cp); 239 240 #ifdef SPKRDEBUG 241 printf("playstring: %c (%x)\n", c, c); 242 #endif /* SPKRDEBUG */ 243 244 switch (c) 245 { 246 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 247 248 /* compute pitch */ 249 pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 250 251 /* this may be followed by an accidental sign */ 252 if (slen > 0 && (cp[1] == '#' || cp[1] == '+')) 253 { 254 ++pitch; 255 ++cp; 256 slen--; 257 } 258 else if (slen > 0 && cp[1] == '-') 259 { 260 --pitch; 261 ++cp; 262 slen--; 263 } 264 265 /* 266 * If octave-tracking mode is on, and there has been no octave- 267 * setting prefix, find the version of the current letter note 268 * closest to the last regardless of octave. 269 */ 270 if (octtrack && !octprefix) 271 { 272 if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) 273 { 274 ++octave; 275 pitch += OCTAVE_NOTES; 276 } 277 278 if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) 279 { 280 --octave; 281 pitch -= OCTAVE_NOTES; 282 } 283 } 284 octprefix = FALSE; 285 lastpitch = pitch; 286 287 /* ...which may in turn be followed by an override time value */ 288 GETNUM(cp, timeval); 289 if (timeval <= 0 || timeval > MIN_VALUE) 290 timeval = value; 291 292 /* ...and/or sustain dots */ 293 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) 294 { 295 slen--; 296 sustain++; 297 } 298 299 /* time to emit the actual tone */ 300 playtone(pitch, timeval, sustain); 301 break; 302 303 case 'O': 304 if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) 305 { 306 octprefix = octtrack = FALSE; 307 ++cp; 308 slen--; 309 } 310 else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) 311 { 312 octtrack = TRUE; 313 ++cp; 314 slen--; 315 } 316 else 317 { 318 GETNUM(cp, octave); 319 if (octave >= NOCTAVES) 320 octave = DFLT_OCTAVE; 321 octprefix = TRUE; 322 } 323 break; 324 325 case '>': 326 if (octave < NOCTAVES - 1) 327 octave++; 328 octprefix = TRUE; 329 break; 330 331 case '<': 332 if (octave > 0) 333 octave--; 334 octprefix = TRUE; 335 break; 336 337 case 'N': 338 GETNUM(cp, pitch); 339 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) 340 { 341 slen--; 342 sustain++; 343 } 344 playtone(pitch - 1, value, sustain); 345 break; 346 347 case 'L': 348 GETNUM(cp, value); 349 if (value <= 0 || value > MIN_VALUE) 350 value = DFLT_VALUE; 351 break; 352 353 case 'P': 354 case '~': 355 /* this may be followed by an override time value */ 356 GETNUM(cp, timeval); 357 if (timeval <= 0 || timeval > MIN_VALUE) 358 timeval = value; 359 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) 360 { 361 slen--; 362 sustain++; 363 } 364 playtone(-1, timeval, sustain); 365 break; 366 367 case 'T': 368 GETNUM(cp, tempo); 369 if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 370 tempo = DFLT_TEMPO; 371 whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo; 372 break; 373 374 case 'M': 375 if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) 376 { 377 fill = NORMAL; 378 ++cp; 379 slen--; 380 } 381 else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) 382 { 383 fill = LEGATO; 384 ++cp; 385 slen--; 386 } 387 else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's')) 388 { 389 fill = STACCATO; 390 ++cp; 391 slen--; 392 } 393 break; 394 } 395 } 396 } 397 398 /******************* UNIX DRIVER HOOKS BEGIN HERE ************************** 399 * 400 * This section implements driver hooks to run playstring() and the tone(), 401 * endtone(), and rest() functions defined above. 402 */ 403 404 static int spkr_active; /* exclusion flag */ 405 static void *spkr_inbuf; 406 407 static int spkr_attached = 0; 408 409 int 410 spkrprobe (parent, match, aux) 411 struct device *parent; 412 #ifdef __BROKEN_INDIRECT_CONFIG 413 void *match; 414 #else 415 struct cfdata *match; 416 #endif 417 void *aux; 418 { 419 return (!spkr_attached); 420 } 421 422 void 423 spkrattach(parent, self, aux) 424 struct device *parent; 425 struct device *self; 426 void *aux; 427 { 428 printf("\n"); 429 ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie; 430 spkr_attached = 1; 431 } 432 433 int 434 spkropen(dev, flags, mode, p) 435 dev_t dev; 436 int flags; 437 int mode; 438 struct proc *p; 439 { 440 #ifdef SPKRDEBUG 441 printf("spkropen: entering with dev = %x\n", dev); 442 #endif /* SPKRDEBUG */ 443 444 if (minor(dev) != 0 || !spkr_attached) 445 return(ENXIO); 446 else if (spkr_active) 447 return(EBUSY); 448 else 449 { 450 playinit(); 451 spkr_inbuf = malloc(DEV_BSIZE, M_DEVBUF, M_WAITOK); 452 spkr_active = 1; 453 } 454 return(0); 455 } 456 457 int 458 spkrwrite(dev, uio, flags) 459 dev_t dev; 460 struct uio *uio; 461 int flags; 462 { 463 register int n; 464 int error; 465 #ifdef SPKRDEBUG 466 printf("spkrwrite: entering with dev = %x, count = %d\n", 467 dev, uio->uio_resid); 468 #endif /* SPKRDEBUG */ 469 470 if (minor(dev) != 0) 471 return(ENXIO); 472 else 473 { 474 n = min(DEV_BSIZE, uio->uio_resid); 475 error = uiomove(spkr_inbuf, n, uio); 476 if (!error) 477 playstring((char *)spkr_inbuf, n); 478 return(error); 479 } 480 } 481 482 int spkrclose(dev, flags, mode, p) 483 dev_t dev; 484 int flags; 485 int mode; 486 struct proc *p; 487 { 488 #ifdef SPKRDEBUG 489 printf("spkrclose: entering with dev = %x\n", dev); 490 #endif /* SPKRDEBUG */ 491 492 if (minor(dev) != 0) 493 return(ENXIO); 494 else 495 { 496 tone(0, 0); 497 free(spkr_inbuf, M_DEVBUF); 498 spkr_active = 0; 499 } 500 return(0); 501 } 502 503 int spkrioctl(dev, cmd, data, flag, p) 504 dev_t dev; 505 u_long cmd; 506 caddr_t data; 507 int flag; 508 struct proc *p; 509 { 510 #ifdef SPKRDEBUG 511 printf("spkrioctl: entering with dev = %x, cmd = %lx\n", dev, cmd); 512 #endif /* SPKRDEBUG */ 513 514 if (minor(dev) != 0) 515 return(ENXIO); 516 else if (cmd == SPKRTONE) 517 { 518 tone_t *tp = (tone_t *)data; 519 520 if (tp->frequency == 0) 521 rest(tp->duration); 522 else 523 tone(tp->frequency, tp->duration); 524 } 525 else if (cmd == SPKRTUNE) 526 { 527 tone_t *tp = (tone_t *)(*(caddr_t *)data); 528 tone_t ttp; 529 int error; 530 531 for (; ; tp++) { 532 error = copyin(tp, &ttp, sizeof(tone_t)); 533 if (error) 534 return(error); 535 if (ttp.duration == 0) 536 break; 537 if (ttp.frequency == 0) 538 rest(ttp.duration); 539 else 540 tone(ttp.frequency, ttp.duration); 541 } 542 } 543 else 544 return(EINVAL); 545 return(0); 546 } 547 548 /* spkr.c ends here */ 549