|
|
1.1 ! root 1: /* ! 2: * spkr.c -- device driver for console speaker on 80386 ! 3: * ! 4: * v1.1 by Eric S. Raymond ([email protected]) Feb 1990 ! 5: * modified for 386bsd by Andrew A. Chernov <[email protected]> ! 6: * 386bsd only clean version, all SYSV stuff removed ! 7: * use hz value from param.c ! 8: * ! 9: * spkr.c,v 1.5 1993/06/16 19:41:54 brezak Exp ! 10: */ ! 11: ! 12: #include "speaker.h" ! 13: ! 14: #if NSPEAKER > 0 ! 15: ! 16: #include "param.h" ! 17: #include "kernel.h" ! 18: #include "errno.h" ! 19: #include "buf.h" ! 20: #include "uio.h" ! 21: #include "i386/include/spkr.h" ! 22: #include "isa.h" ! 23: #include "timerreg.h" ! 24: #include "spkr_reg.h" ! 25: ! 26: /**************** MACHINE DEPENDENT PART STARTS HERE ************************* ! 27: * ! 28: * This section defines a function tone() which causes a tone of given ! 29: * frequency and duration from the 80x86's console speaker. ! 30: * Another function endtone() is defined to force sound off, and there is ! 31: * also a rest() entry point to do pauses. ! 32: * ! 33: * Audible sound is generated using the Programmable Interval Timer (PIT) and ! 34: * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The ! 35: * PPI controls whether sound is passed through at all; the PIT's channel 2 is ! 36: * used to generate clicks (a square wave) of whatever frequency is desired. ! 37: */ ! 38: ! 39: /* ! 40: * Magic numbers for timer control. ! 41: */ ! 42: #define PIT_MODE (TIMER_SEL2|TIMER_MSB|TIMER_SQWAVE) ! 43: ! 44: static int endtone() ! 45: /* turn off the speaker, ending current tone */ ! 46: { ! 47: wakeup((caddr_t)endtone); ! 48: outb(PITAUX_PORT, inb(PITAUX_PORT) & ~PIT_SPKR); ! 49: } ! 50: ! 51: static void tone(hz, ticks) ! 52: /* emit tone of frequency hz for given number of ticks */ ! 53: unsigned int hz, ticks; ! 54: { ! 55: unsigned int divisor = TIMER_DIV(hz); ! 56: int sps; ! 57: ! 58: #ifdef DEBUG ! 59: printf("tone: hz=%d ticks=%d\n", hz, ticks); ! 60: #endif /* DEBUG */ ! 61: ! 62: /* set timer to generate clicks at given frequency in Hertz */ ! 63: sps = spltty(); ! 64: outb(TIMER_MODE, PIT_MODE); /* prepare timer */ ! 65: outb(TIMER_CNTR2, (unsigned char) divisor); /* send lo byte */ ! 66: outb(TIMER_CNTR2, (divisor >> 8)); /* send hi byte */ ! 67: splx(sps); ! 68: ! 69: /* turn the speaker on */ ! 70: outb(PITAUX_PORT, inb(PITAUX_PORT) | PIT_SPKR); ! 71: ! 72: /* ! 73: * Set timeout to endtone function, then give up the timeslice. ! 74: * This is so other processes can execute while the tone is being ! 75: * emitted. ! 76: */ ! 77: timeout((caddr_t)endtone, (caddr_t)NULL, ticks); ! 78: sleep((caddr_t)endtone, PZERO - 1); ! 79: } ! 80: ! 81: static int endrest() ! 82: /* end a rest */ ! 83: { ! 84: wakeup((caddr_t)endrest); ! 85: } ! 86: ! 87: static void rest(ticks) ! 88: /* rest for given number of ticks */ ! 89: int ticks; ! 90: { ! 91: /* ! 92: * Set timeout to endrest function, then give up the timeslice. ! 93: * This is so other processes can execute while the rest is being ! 94: * waited out. ! 95: */ ! 96: #ifdef DEBUG ! 97: printf("rest: %d\n", ticks); ! 98: #endif /* DEBUG */ ! 99: timeout((caddr_t)endrest, (caddr_t)NULL, ticks); ! 100: sleep((caddr_t)endrest, PZERO - 1); ! 101: } ! 102: ! 103: /**************** PLAY STRING INTERPRETER BEGINS HERE ********************** ! 104: * ! 105: * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; ! 106: * M[LNS] are missing and the ~ synonym and octave-tracking facility is added. ! 107: * Requires tone(), rest(), and endtone(). String play is not interruptible ! 108: * except possibly at physical block boundaries. ! 109: */ ! 110: ! 111: typedef int bool; ! 112: #define TRUE 1 ! 113: #define FALSE 0 ! 114: ! 115: #define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z'))) ! 116: #define isdigit(c) (((c) >= '0') && ((c) <= '9')) ! 117: #define dtoi(c) ((c) - '0') ! 118: ! 119: static int octave; /* currently selected octave */ ! 120: static int whole; /* whole-note time at current tempo, in ticks */ ! 121: static int value; /* whole divisor for note time, quarter note = 1 */ ! 122: static int fill; /* controls spacing of notes */ ! 123: static bool octtrack; /* octave-tracking on? */ ! 124: static bool octprefix; /* override current octave-tracking state? */ ! 125: ! 126: /* ! 127: * Magic number avoidance... ! 128: */ ! 129: #define SECS_PER_MIN 60 /* seconds per minute */ ! 130: #define WHOLE_NOTE 4 /* quarter notes per whole note */ ! 131: #define MIN_VALUE 64 /* the most we can divide a note by */ ! 132: #define DFLT_VALUE 4 /* default value (quarter-note) */ ! 133: #define FILLTIME 8 /* for articulation, break note in parts */ ! 134: #define STACCATO 6 /* 6/8 = 3/4 of note is filled */ ! 135: #define NORMAL 7 /* 7/8ths of note interval is filled */ ! 136: #define LEGATO 8 /* all of note interval is filled */ ! 137: #define DFLT_OCTAVE 4 /* default octave */ ! 138: #define MIN_TEMPO 32 /* minimum tempo */ ! 139: #define DFLT_TEMPO 120 /* default tempo */ ! 140: #define MAX_TEMPO 255 /* max tempo */ ! 141: #define NUM_MULT 3 /* numerator of dot multiplier */ ! 142: #define DENOM_MULT 2 /* denominator of dot multiplier */ ! 143: ! 144: /* letter to half-tone: A B C D E F G */ ! 145: static int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; ! 146: ! 147: /* ! 148: * This is the American Standard A440 Equal-Tempered scale with frequencies ! 149: * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... ! 150: * our octave 0 is standard octave 2. ! 151: */ ! 152: #define OCTAVE_NOTES 12 /* semitones per octave */ ! 153: static int pitchtab[] = ! 154: { ! 155: /* C C# D D# E F F# G G# A A# B*/ ! 156: /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, ! 157: /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, ! 158: /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, ! 159: /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, ! 160: /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, ! 161: /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, ! 162: /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, ! 163: }; ! 164: ! 165: static void playinit() ! 166: { ! 167: octave = DFLT_OCTAVE; ! 168: whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; ! 169: fill = NORMAL; ! 170: value = DFLT_VALUE; ! 171: octtrack = FALSE; ! 172: octprefix = TRUE; /* act as though there was an initial O(n) */ ! 173: } ! 174: ! 175: static void playtone(pitch, value, sustain) ! 176: /* play tone of proper duration for current rhythm signature */ ! 177: int pitch, value, sustain; ! 178: { ! 179: register int sound, silence, snum = 1, sdenom = 1; ! 180: ! 181: /* this weirdness avoids floating-point arithmetic */ ! 182: for (; sustain; sustain--) ! 183: { ! 184: snum *= NUM_MULT; ! 185: sdenom *= DENOM_MULT; ! 186: } ! 187: ! 188: if (pitch == -1) ! 189: rest(whole * snum / (value * sdenom)); ! 190: else ! 191: { ! 192: sound = (whole * snum) / (value * sdenom) ! 193: - (whole * (FILLTIME - fill)) / (value * FILLTIME); ! 194: silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); ! 195: ! 196: #ifdef DEBUG ! 197: printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", ! 198: pitch, sound, silence); ! 199: #endif /* DEBUG */ ! 200: ! 201: tone(pitchtab[pitch], sound); ! 202: if (fill != LEGATO) ! 203: rest(silence); ! 204: } ! 205: } ! 206: ! 207: static int abs(n) ! 208: int n; ! 209: { ! 210: if (n < 0) ! 211: return(-n); ! 212: else ! 213: return(n); ! 214: } ! 215: ! 216: static void playstring(cp, slen) ! 217: /* interpret and play an item from a notation string */ ! 218: char *cp; ! 219: size_t slen; ! 220: { ! 221: int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; ! 222: ! 223: #define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ ! 224: {v = v * 10 + (*++cp - '0'); slen--;} ! 225: for (; slen--; cp++) ! 226: { ! 227: int sustain, timeval, tempo; ! 228: register char c = toupper(*cp); ! 229: ! 230: #ifdef DEBUG ! 231: printf("playstring: %c (%x)\n", c, c); ! 232: #endif /* DEBUG */ ! 233: ! 234: switch (c) ! 235: { ! 236: case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': ! 237: ! 238: /* compute pitch */ ! 239: pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; ! 240: ! 241: /* this may be followed by an accidental sign */ ! 242: if (cp[1] == '#' || cp[1] == '+') ! 243: { ! 244: ++pitch; ! 245: ++cp; ! 246: slen--; ! 247: } ! 248: else if (cp[1] == '-') ! 249: { ! 250: --pitch; ! 251: ++cp; ! 252: slen--; ! 253: } ! 254: ! 255: /* ! 256: * If octave-tracking mode is on, and there has been no octave- ! 257: * setting prefix, find the version of the current letter note ! 258: * closest to the last regardless of octave. ! 259: */ ! 260: if (octtrack && !octprefix) ! 261: { ! 262: if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) ! 263: { ! 264: ++octave; ! 265: pitch += OCTAVE_NOTES; ! 266: } ! 267: ! 268: if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) ! 269: { ! 270: --octave; ! 271: pitch -= OCTAVE_NOTES; ! 272: } ! 273: } ! 274: octprefix = FALSE; ! 275: lastpitch = pitch; ! 276: ! 277: /* ...which may in turn be followed by an override time value */ ! 278: GETNUM(cp, timeval); ! 279: if (timeval <= 0 || timeval > MIN_VALUE) ! 280: timeval = value; ! 281: ! 282: /* ...and/or sustain dots */ ! 283: for (sustain = 0; cp[1] == '.'; cp++) ! 284: { ! 285: slen--; ! 286: sustain++; ! 287: } ! 288: ! 289: /* time to emit the actual tone */ ! 290: playtone(pitch, timeval, sustain); ! 291: break; ! 292: ! 293: case 'O': ! 294: if (cp[1] == 'N' || cp[1] == 'n') ! 295: { ! 296: octprefix = octtrack = FALSE; ! 297: ++cp; ! 298: slen--; ! 299: } ! 300: else if (cp[1] == 'L' || cp[1] == 'l') ! 301: { ! 302: octtrack = TRUE; ! 303: ++cp; ! 304: slen--; ! 305: } ! 306: else ! 307: { ! 308: GETNUM(cp, octave); ! 309: if (octave >= sizeof(pitchtab) / OCTAVE_NOTES) ! 310: octave = DFLT_OCTAVE; ! 311: octprefix = TRUE; ! 312: } ! 313: break; ! 314: ! 315: case '>': ! 316: if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1) ! 317: octave++; ! 318: octprefix = TRUE; ! 319: break; ! 320: ! 321: case '<': ! 322: if (octave > 0) ! 323: octave--; ! 324: octprefix = TRUE; ! 325: break; ! 326: ! 327: case 'N': ! 328: GETNUM(cp, pitch); ! 329: for (sustain = 0; cp[1] == '.'; cp++) ! 330: { ! 331: slen--; ! 332: sustain++; ! 333: } ! 334: playtone(pitch - 1, value, sustain); ! 335: break; ! 336: ! 337: case 'L': ! 338: GETNUM(cp, value); ! 339: if (value <= 0 || value > MIN_VALUE) ! 340: value = DFLT_VALUE; ! 341: break; ! 342: ! 343: case 'P': ! 344: case '~': ! 345: /* this may be followed by an override time value */ ! 346: GETNUM(cp, timeval); ! 347: if (timeval <= 0 || timeval > MIN_VALUE) ! 348: timeval = value; ! 349: for (sustain = 0; cp[1] == '.'; cp++) ! 350: { ! 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 (cp[1] == 'N' || cp[1] == 'n') ! 366: { ! 367: fill = NORMAL; ! 368: ++cp; ! 369: slen--; ! 370: } ! 371: else if (cp[1] == 'L' || cp[1] == 'l') ! 372: { ! 373: fill = LEGATO; ! 374: ++cp; ! 375: slen--; ! 376: } ! 377: else if (cp[1] == 'S' || cp[1] == 's') ! 378: { ! 379: fill = STACCATO; ! 380: ++cp; ! 381: slen--; ! 382: } ! 383: break; ! 384: } ! 385: } ! 386: } ! 387: ! 388: /******************* UNIX DRIVER HOOKS BEGIN HERE ************************** ! 389: * ! 390: * This section implements driver hooks to run playstring() and the tone(), ! 391: * endtone(), and rest() functions defined above. ! 392: */ ! 393: ! 394: static int spkr_active; /* exclusion flag */ ! 395: static struct buf *spkr_inbuf; /* incoming buf */ ! 396: ! 397: int spkropen(dev) ! 398: dev_t dev; ! 399: { ! 400: #ifdef DEBUG ! 401: printf("spkropen: entering with dev = %x\n", dev); ! 402: #endif /* DEBUG */ ! 403: ! 404: if (minor(dev) != 0) ! 405: return(ENXIO); ! 406: else if (spkr_active) ! 407: return(EBUSY); ! 408: else ! 409: { ! 410: playinit(); ! 411: spkr_inbuf = geteblk(DEV_BSIZE); ! 412: spkr_active = 1; ! 413: } ! 414: return(0); ! 415: } ! 416: ! 417: int spkrwrite(dev, uio) ! 418: dev_t dev; ! 419: struct uio *uio; ! 420: { ! 421: register unsigned n; ! 422: char *cp; ! 423: int error; ! 424: #ifdef DEBUG ! 425: printf("spkrwrite: entering with dev = %x, count = %d\n", ! 426: dev, uio->uio_resid); ! 427: #endif /* DEBUG */ ! 428: ! 429: if (minor(dev) != 0) ! 430: return(ENXIO); ! 431: else ! 432: { ! 433: n = MIN(DEV_BSIZE, uio->uio_resid); ! 434: cp = spkr_inbuf->b_un.b_addr; ! 435: error = uiomove(cp, n, uio); ! 436: if (!error) ! 437: playstring(cp, n); ! 438: return(error); ! 439: } ! 440: } ! 441: ! 442: int spkrclose(dev) ! 443: dev_t dev; ! 444: { ! 445: #ifdef DEBUG ! 446: printf("spkrclose: entering with dev = %x\n", dev); ! 447: #endif /* DEBUG */ ! 448: ! 449: if (minor(dev) != 0) ! 450: return(ENXIO); ! 451: else ! 452: { ! 453: endtone(); ! 454: brelse(spkr_inbuf); ! 455: spkr_active = 0; ! 456: } ! 457: return(0); ! 458: } ! 459: ! 460: int spkrioctl(dev, cmd, cmdarg) ! 461: dev_t dev; ! 462: int cmd; ! 463: caddr_t cmdarg; ! 464: { ! 465: #ifdef DEBUG ! 466: printf("spkrioctl: entering with dev = %x, cmd = %x\n", dev, cmd); ! 467: #endif /* DEBUG */ ! 468: ! 469: if (minor(dev) != 0) ! 470: return(ENXIO); ! 471: else if (cmd == SPKRTONE) ! 472: { ! 473: tone_t *tp = (tone_t *)cmdarg; ! 474: ! 475: if (tp->frequency == 0) ! 476: rest(tp->duration); ! 477: else ! 478: tone(tp->frequency, tp->duration); ! 479: } ! 480: else if (cmd == SPKRTUNE) ! 481: { ! 482: tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); ! 483: tone_t ttp; ! 484: int error; ! 485: ! 486: for (; ; tp++) { ! 487: error = copyin(tp, &ttp, sizeof(tone_t)); ! 488: if (error) ! 489: return(error); ! 490: if (ttp.duration == 0) ! 491: break; ! 492: if (ttp.frequency == 0) ! 493: rest(ttp.duration); ! 494: else ! 495: tone(ttp.frequency, ttp.duration); ! 496: } ! 497: } ! 498: else ! 499: return(EINVAL); ! 500: return(0); ! 501: } ! 502: ! 503: #endif /* NSPEAKER > 0 */ ! 504: /* spkr.c ends here */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.