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

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