Annotation of src/sys/dev/sound/isa/i386/spkr/spkr.c, revision 1.2

1.1       dillon      1: /*
                      2:  * spkr.c -- device driver for console speaker
                      3:  *
                      4:  * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
                      5:  * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
                      6:  *
                      7:  * $FreeBSD: src/sys/i386/isa/spkr.c,v 1.45 2000/01/29 16:00:32 peter Exp $
1.2     ! dillon      8:  * $DragonFly: src/sys/i386/isa/spkr.c,v 1.1 2003/06/16 06:19:40 dillon Exp $
1.1       dillon      9:  */
                     10: 
                     11: #include <sys/param.h>
                     12: #include <sys/systm.h>
                     13: #include <sys/kernel.h>
                     14: #include <sys/buf.h>
                     15: #include <sys/uio.h>
                     16: #include <sys/conf.h>
                     17: #include <sys/ctype.h>
                     18: #include <i386/isa/isa.h>
                     19: #include <i386/isa/timerreg.h>
                     20: #include <machine/clock.h>
                     21: #include <machine/speaker.h>
                     22: 
                     23: static d_open_t        spkropen;
                     24: static d_close_t       spkrclose;
                     25: static d_write_t       spkrwrite;
                     26: static d_ioctl_t       spkrioctl;
                     27: 
                     28: #define CDEV_MAJOR 26
                     29: static struct cdevsw spkr_cdevsw = {
                     30:        /* open */      spkropen,
                     31:        /* close */     spkrclose,
                     32:        /* read */      noread,
                     33:        /* write */     spkrwrite,
                     34:        /* ioctl */     spkrioctl,
                     35:        /* poll */      nopoll,
                     36:        /* mmap */      nommap,
                     37:        /* strategy */  nostrategy,
                     38:        /* name */      "spkr",
                     39:        /* maj */       CDEV_MAJOR,
                     40:        /* dump */      nodump,
                     41:        /* psize */     nopsize,
                     42:        /* flags */     0,
                     43:        /* bmaj */      -1
                     44: };
                     45: 
                     46: /**************** MACHINE DEPENDENT PART STARTS HERE *************************
                     47:  *
                     48:  * This section defines a function tone() which causes a tone of given
                     49:  * frequency and duration from the ISA console speaker.
                     50:  * Another function endtone() is defined to force sound off, and there is
                     51:  * also a rest() entry point to do pauses.
                     52:  *
                     53:  * Audible sound is generated using the Programmable Interval Timer (PIT) and
                     54:  * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The
                     55:  * PPI controls whether sound is passed through at all; the PIT's channel 2 is
                     56:  * used to generate clicks (a square wave) of whatever frequency is desired.
                     57:  */
                     58: 
                     59: /*
                     60:  * PPI control values.
                     61:  * XXX should be in a header and used in clock.c.
                     62:  */
                     63: #define PPI_SPKR       0x03    /* turn these PPI bits on to pass sound */
                     64: 
                     65: #define SPKRPRI PSOCK
                     66: static char endtone, endrest;
                     67: 
                     68: static void tone __P((unsigned int thz, unsigned int ticks));
                     69: static void rest __P((int ticks));
                     70: static void playinit __P((void));
                     71: static void playtone __P((int pitch, int value, int sustain));
                     72: static int abs __P((int n));
                     73: static void playstring __P((char *cp, size_t slen));
                     74: 
                     75: /* emit tone of frequency thz for given number of ticks */
                     76: static void
                     77: tone(thz, ticks)
                     78:        unsigned int thz, ticks;
                     79: {
                     80:     unsigned int divisor;
                     81:     int sps;
                     82: 
                     83:     if (thz <= 0)
                     84:        return;
                     85: 
                     86:     divisor = timer_freq / thz;
                     87: 
                     88: #ifdef DEBUG
                     89:     (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
                     90: #endif /* DEBUG */
                     91: 
                     92:     /* set timer to generate clicks at given frequency in Hertz */
                     93:     sps = splclock();
                     94: 
                     95:     if (acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT)) {
                     96:        /* enter list of waiting procs ??? */
                     97:        splx(sps);
                     98:        return;
                     99:     }
                    100:     splx(sps);
                    101:     disable_intr();
                    102:     outb(TIMER_CNTR2, (divisor & 0xff));       /* send lo byte */
                    103:     outb(TIMER_CNTR2, (divisor >> 8)); /* send hi byte */
                    104:     enable_intr();
                    105: 
                    106:     /* turn the speaker on */
                    107:     outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
                    108: 
                    109:     /*
                    110:      * Set timeout to endtone function, then give up the timeslice.
                    111:      * This is so other processes can execute while the tone is being
                    112:      * emitted.
                    113:      */
                    114:     if (ticks > 0)
                    115:        tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
                    116:     outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
                    117:     sps = splclock();
                    118:     release_timer2();
                    119:     splx(sps);
                    120: }
                    121: 
                    122: /* rest for given number of ticks */
                    123: static void
                    124: rest(ticks)
                    125:        int     ticks;
                    126: {
                    127:     /*
                    128:      * Set timeout to endrest function, then give up the timeslice.
                    129:      * This is so other processes can execute while the rest is being
                    130:      * waited out.
                    131:      */
                    132: #ifdef DEBUG
                    133:     (void) printf("rest: %d\n", ticks);
                    134: #endif /* DEBUG */
                    135:     if (ticks > 0)
                    136:        tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
                    137: }
                    138: 
                    139: /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
                    140:  *
                    141:  * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
                    142:  * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
                    143:  * tracking facility are added.
                    144:  * Requires tone(), rest(), and endtone(). String play is not interruptible
                    145:  * except possibly at physical block boundaries.
                    146:  */
                    147: 
                    148: typedef int    bool;
                    149: #define TRUE   1
                    150: #define FALSE  0
                    151: 
                    152: #define dtoi(c)                ((c) - '0')
                    153: 
                    154: static int octave;     /* currently selected octave */
                    155: static int whole;      /* whole-note time at current tempo, in ticks */
                    156: static int value;      /* whole divisor for note time, quarter note = 1 */
                    157: static int fill;       /* controls spacing of notes */
                    158: static bool octtrack;  /* octave-tracking on? */
                    159: static bool octprefix; /* override current octave-tracking state? */
                    160: 
                    161: /*
                    162:  * Magic number avoidance...
                    163:  */
                    164: #define SECS_PER_MIN   60      /* seconds per minute */
                    165: #define WHOLE_NOTE     4       /* quarter notes per whole note */
                    166: #define MIN_VALUE      64      /* the most we can divide a note by */
                    167: #define DFLT_VALUE     4       /* default value (quarter-note) */
                    168: #define FILLTIME       8       /* for articulation, break note in parts */
                    169: #define STACCATO       6       /* 6/8 = 3/4 of note is filled */
                    170: #define NORMAL         7       /* 7/8ths of note interval is filled */
                    171: #define LEGATO         8       /* all of note interval is filled */
                    172: #define DFLT_OCTAVE    4       /* default octave */
                    173: #define MIN_TEMPO      32      /* minimum tempo */
                    174: #define DFLT_TEMPO     120     /* default tempo */
                    175: #define MAX_TEMPO      255     /* max tempo */
                    176: #define NUM_MULT       3       /* numerator of dot multiplier */
                    177: #define DENOM_MULT     2       /* denominator of dot multiplier */
                    178: 
                    179: /* letter to half-tone:  A   B  C  D  E  F  G */
                    180: static int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
                    181: 
                    182: /*
                    183:  * This is the American Standard A440 Equal-Tempered scale with frequencies
                    184:  * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
                    185:  * our octave 0 is standard octave 2.
                    186:  */
                    187: #define OCTAVE_NOTES   12      /* semitones per octave */
                    188: static int pitchtab[] =
                    189: {
                    190: /*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
                    191: /* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
                    192: /* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
                    193: /* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
                    194: /* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
                    195: /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
                    196: /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
                    197: /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
                    198: };
                    199: 
                    200: static void
                    201: playinit()
                    202: {
                    203:     octave = DFLT_OCTAVE;
                    204:     whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
                    205:     fill = NORMAL;
                    206:     value = DFLT_VALUE;
                    207:     octtrack = FALSE;
                    208:     octprefix = TRUE;  /* act as though there was an initial O(n) */
                    209: }
                    210: 
                    211: /* play tone of proper duration for current rhythm signature */
                    212: static void
                    213: playtone(pitch, value, sustain)
                    214:        int     pitch, value, sustain;
                    215: {
                    216:     register int       sound, silence, snum = 1, sdenom = 1;
                    217: 
                    218:     /* this weirdness avoids floating-point arithmetic */
                    219:     for (; sustain; sustain--)
                    220:     {
                    221:        /* See the BUGS section in the man page for discussion */
                    222:        snum *= NUM_MULT;
                    223:        sdenom *= DENOM_MULT;
                    224:     }
                    225: 
                    226:     if (value == 0 || sdenom == 0)
                    227:        return;
                    228: 
                    229:     if (pitch == -1)
                    230:        rest(whole * snum / (value * sdenom));
                    231:     else
                    232:     {
                    233:        sound = (whole * snum) / (value * sdenom)
                    234:                - (whole * (FILLTIME - fill)) / (value * FILLTIME);
                    235:        silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
                    236: 
                    237: #ifdef DEBUG
                    238:        (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
                    239:                        pitch, sound, silence);
                    240: #endif /* DEBUG */
                    241: 
                    242:        tone(pitchtab[pitch], sound);
                    243:        if (fill != LEGATO)
                    244:            rest(silence);
                    245:     }
                    246: }
                    247: 
                    248: static int
                    249: abs(n)
                    250:        int n;
                    251: {
                    252:     if (n < 0)
                    253:        return(-n);
                    254:     else
                    255:        return(n);
                    256: }
                    257: 
                    258: /* interpret and play an item from a notation string */
                    259: static void
                    260: playstring(cp, slen)
                    261:        char    *cp;
                    262:        size_t  slen;
                    263: {
                    264:     int                pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
                    265: 
                    266: #define GETNUM(cp, v)  for(v=0; isdigit(cp[1]) && slen > 0; ) \
                    267:                                {v = v * 10 + (*++cp - '0'); slen--;}
                    268:     for (; slen--; cp++)
                    269:     {
                    270:        int             sustain, timeval, tempo;
                    271:        register char   c = toupper(*cp);
                    272: 
                    273: #ifdef DEBUG
                    274:        (void) printf("playstring: %c (%x)\n", c, c);
                    275: #endif /* DEBUG */
                    276: 
                    277:        switch (c)
                    278:        {
                    279:        case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
                    280: 
                    281:            /* compute pitch */
                    282:            pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
                    283: 
                    284:            /* this may be followed by an accidental sign */
                    285:            if (cp[1] == '#' || cp[1] == '+')
                    286:            {
                    287:                ++pitch;
                    288:                ++cp;
                    289:                slen--;
                    290:            }
                    291:            else if (cp[1] == '-')
                    292:            {
                    293:                --pitch;
                    294:                ++cp;
                    295:                slen--;
                    296:            }
                    297: 
                    298:            /*
                    299:             * If octave-tracking mode is on, and there has been no octave-
                    300:             * setting prefix, find the version of the current letter note
                    301:             * closest to the last regardless of octave.
                    302:             */
                    303:            if (octtrack && !octprefix)
                    304:            {
                    305:                if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
                    306:                {
                    307:                    ++octave;
                    308:                    pitch += OCTAVE_NOTES;
                    309:                }
                    310: 
                    311:                if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
                    312:                {
                    313:                    --octave;
                    314:                    pitch -= OCTAVE_NOTES;
                    315:                }
                    316:            }
                    317:            octprefix = FALSE;
                    318:            lastpitch = pitch;
                    319: 
                    320:            /* ...which may in turn be followed by an override time value */
                    321:            GETNUM(cp, timeval);
                    322:            if (timeval <= 0 || timeval > MIN_VALUE)
                    323:                timeval = value;
                    324: 
                    325:            /* ...and/or sustain dots */
                    326:            for (sustain = 0; cp[1] == '.'; cp++)
                    327:            {
                    328:                slen--;
                    329:                sustain++;
                    330:            }
                    331: 
                    332:            /* ...and/or a slur mark */
                    333:            oldfill = fill;
                    334:            if (cp[1] == '_')
                    335:            {
                    336:                fill = LEGATO;
                    337:                ++cp;
                    338:                slen--;
                    339:            }
                    340: 
                    341:            /* time to emit the actual tone */
                    342:            playtone(pitch, timeval, sustain);
                    343: 
                    344:            fill = oldfill;
                    345:            break;
                    346: 
                    347:        case 'O':
                    348:            if (cp[1] == 'N' || cp[1] == 'n')
                    349:            {
                    350:                octprefix = octtrack = FALSE;
                    351:                ++cp;
                    352:                slen--;
                    353:            }
                    354:            else if (cp[1] == 'L' || cp[1] == 'l')
                    355:            {
                    356:                octtrack = TRUE;
                    357:                ++cp;
                    358:                slen--;
                    359:            }
                    360:            else
                    361:            {
                    362:                GETNUM(cp, octave);
                    363:                if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
                    364:                    octave = DFLT_OCTAVE;
                    365:                octprefix = TRUE;
                    366:            }
                    367:            break;
                    368: 
                    369:        case '>':
                    370:            if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
                    371:                octave++;
                    372:            octprefix = TRUE;
                    373:            break;
                    374: 
                    375:        case '<':
                    376:            if (octave > 0)
                    377:                octave--;
                    378:            octprefix = TRUE;
                    379:            break;
                    380: 
                    381:        case 'N':
                    382:            GETNUM(cp, pitch);
                    383:            for (sustain = 0; cp[1] == '.'; cp++)
                    384:            {
                    385:                slen--;
                    386:                sustain++;
                    387:            }
                    388:            oldfill = fill;
                    389:            if (cp[1] == '_')
                    390:            {
                    391:                fill = LEGATO;
                    392:                ++cp;
                    393:                slen--;
                    394:            }
                    395:            playtone(pitch - 1, value, sustain);
                    396:            fill = oldfill;
                    397:            break;
                    398: 
                    399:        case 'L':
                    400:            GETNUM(cp, value);
                    401:            if (value <= 0 || value > MIN_VALUE)
                    402:                value = DFLT_VALUE;
                    403:            break;
                    404: 
                    405:        case 'P':
                    406:        case '~':
                    407:            /* this may be followed by an override time value */
                    408:            GETNUM(cp, timeval);
                    409:            if (timeval <= 0 || timeval > MIN_VALUE)
                    410:                timeval = value;
                    411:            for (sustain = 0; cp[1] == '.'; cp++)
                    412:            {
                    413:                slen--;
                    414:                sustain++;
                    415:            }
                    416:            playtone(-1, timeval, sustain);
                    417:            break;
                    418: 
                    419:        case 'T':
                    420:            GETNUM(cp, tempo);
                    421:            if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
                    422:                tempo = DFLT_TEMPO;
                    423:            whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
                    424:            break;
                    425: 
                    426:        case 'M':
                    427:            if (cp[1] == 'N' || cp[1] == 'n')
                    428:            {
                    429:                fill = NORMAL;
                    430:                ++cp;
                    431:                slen--;
                    432:            }
                    433:            else if (cp[1] == 'L' || cp[1] == 'l')
                    434:            {
                    435:                fill = LEGATO;
                    436:                ++cp;
                    437:                slen--;
                    438:            }
                    439:            else if (cp[1] == 'S' || cp[1] == 's')
                    440:            {
                    441:                fill = STACCATO;
                    442:                ++cp;
                    443:                slen--;
                    444:            }
                    445:            break;
                    446:        }
                    447:     }
                    448: }
                    449: 
                    450: /******************* UNIX DRIVER HOOKS BEGIN HERE **************************
                    451:  *
                    452:  * This section implements driver hooks to run playstring() and the tone(),
                    453:  * endtone(), and rest() functions defined above.
                    454:  */
                    455: 
                    456: static int spkr_active = FALSE; /* exclusion flag */
                    457: static struct buf *spkr_inbuf;  /* incoming buf */
                    458: 
                    459: int
                    460: spkropen(dev, flags, fmt, p)
                    461:        dev_t           dev;
                    462:        int             flags;
                    463:        int             fmt;
                    464:        struct proc     *p;
                    465: {
                    466: #ifdef DEBUG
                    467:     (void) printf("spkropen: entering with dev = %s\n", devtoname(dev));
                    468: #endif /* DEBUG */
                    469: 
                    470:     if (minor(dev) != 0)
                    471:        return(ENXIO);
                    472:     else if (spkr_active)
                    473:        return(EBUSY);
                    474:     else
                    475:     {
                    476: #ifdef DEBUG
                    477:        (void) printf("spkropen: about to perform play initialization\n");
                    478: #endif /* DEBUG */
                    479:        playinit();
                    480:        spkr_inbuf = geteblk(DEV_BSIZE);
                    481:        spkr_active = TRUE;
                    482:        return(0);
                    483:     }
                    484: }
                    485: 
                    486: int
                    487: spkrwrite(dev, uio, ioflag)
                    488:        dev_t           dev;
                    489:        struct uio      *uio;
                    490:        int             ioflag;
                    491: {
                    492: #ifdef DEBUG
                    493:     printf("spkrwrite: entering with dev = %s, count = %d\n",
                    494:                devtoname(dev), uio->uio_resid);
                    495: #endif /* DEBUG */
                    496: 
                    497:     if (minor(dev) != 0)
                    498:        return(ENXIO);
                    499:     else if (uio->uio_resid > (DEV_BSIZE - 1))     /* prevent system crashes */
                    500:        return(E2BIG);
                    501:     else
                    502:     {
                    503:        unsigned n;
                    504:        char *cp;
                    505:        int error;
                    506: 
                    507:        n = uio->uio_resid;
                    508:        cp = spkr_inbuf->b_data;
                    509:        error = uiomove(cp, n, uio);
                    510:        if (!error) {
                    511:                cp[n] = '\0';
                    512:                playstring(cp, n);
                    513:        }
                    514:        return(error);
                    515:     }
                    516: }
                    517: 
                    518: int
                    519: spkrclose(dev, flags, fmt, p)
                    520:        dev_t           dev;
                    521:        int             flags;
                    522:        int             fmt;
                    523:        struct proc     *p;
                    524: {
                    525: #ifdef DEBUG
                    526:     (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev));
                    527: #endif /* DEBUG */
                    528: 
                    529:     if (minor(dev) != 0)
                    530:        return(ENXIO);
                    531:     else
                    532:     {
                    533:        wakeup((caddr_t)&endtone);
                    534:        wakeup((caddr_t)&endrest);
                    535:        brelse(spkr_inbuf);
                    536:        spkr_active = FALSE;
                    537:        return(0);
                    538:     }
                    539: }
                    540: 
                    541: int
                    542: spkrioctl(dev, cmd, cmdarg, flags, p)
                    543:        dev_t           dev;
                    544:        unsigned long   cmd;
                    545:        caddr_t         cmdarg;
                    546:        int             flags;
                    547:        struct proc     *p;
                    548: {
                    549: #ifdef DEBUG
                    550:     (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n",
                    551:        devtoname(dev), cmd);
                    552: #endif /* DEBUG */
                    553: 
                    554:     if (minor(dev) != 0)
                    555:        return(ENXIO);
                    556:     else if (cmd == SPKRTONE)
                    557:     {
                    558:        tone_t  *tp = (tone_t *)cmdarg;
                    559: 
                    560:        if (tp->frequency == 0)
                    561:            rest(tp->duration);
                    562:        else
                    563:            tone(tp->frequency, tp->duration);
                    564:        return 0;
                    565:     }
                    566:     else if (cmd == SPKRTUNE)
                    567:     {
                    568:        tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
                    569:        tone_t ttp;
                    570:        int error;
                    571: 
                    572:        for (; ; tp++) {
                    573:            error = copyin(tp, &ttp, sizeof(tone_t));
                    574:            if (error)
                    575:                    return(error);
                    576:            if (ttp.duration == 0)
                    577:                    break;
                    578:            if (ttp.frequency == 0)
                    579:                 rest(ttp.duration);
                    580:            else
                    581:                 tone(ttp.frequency, ttp.duration);
                    582:        }
                    583:        return(0);
                    584:     }
                    585:     return(EINVAL);
                    586: }
                    587: 
                    588: static void
                    589: spkr_drvinit(void *unused)
                    590: {
                    591:        make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "speaker");
                    592: }
                    593: 
                    594: SYSINIT(spkrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,spkr_drvinit,NULL)
                    595: 
                    596: 
                    597: /* spkr.c ends here */