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

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