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