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