[BACK]Return to spkr.c CVS log [TXT][DIR] Up to [local] / sys / dev / isa

Annotation of sys/dev/isa/spkr.c, Revision 1.1.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