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

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.3     ! dillon      8:  * $DragonFly: src/sys/i386/isa/spkr.c,v 1.2 2003/06/17 04:28:37 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
1.3     ! dillon    460: spkropen(dev_t dev, int        flags, int fmt, struct thread *td)
1.1       dillon    461: {
                    462: #ifdef DEBUG
                    463:     (void) printf("spkropen: entering with dev = %s\n", devtoname(dev));
                    464: #endif /* DEBUG */
                    465: 
                    466:     if (minor(dev) != 0)
                    467:        return(ENXIO);
                    468:     else if (spkr_active)
                    469:        return(EBUSY);
                    470:     else
                    471:     {
                    472: #ifdef DEBUG
                    473:        (void) printf("spkropen: about to perform play initialization\n");
                    474: #endif /* DEBUG */
                    475:        playinit();
                    476:        spkr_inbuf = geteblk(DEV_BSIZE);
                    477:        spkr_active = TRUE;
                    478:        return(0);
                    479:     }
                    480: }
                    481: 
                    482: int
                    483: spkrwrite(dev, uio, ioflag)
                    484:        dev_t           dev;
                    485:        struct uio      *uio;
                    486:        int             ioflag;
                    487: {
                    488: #ifdef DEBUG
                    489:     printf("spkrwrite: entering with dev = %s, count = %d\n",
                    490:                devtoname(dev), uio->uio_resid);
                    491: #endif /* DEBUG */
                    492: 
                    493:     if (minor(dev) != 0)
                    494:        return(ENXIO);
                    495:     else if (uio->uio_resid > (DEV_BSIZE - 1))     /* prevent system crashes */
                    496:        return(E2BIG);
                    497:     else
                    498:     {
                    499:        unsigned n;
                    500:        char *cp;
                    501:        int error;
                    502: 
                    503:        n = uio->uio_resid;
                    504:        cp = spkr_inbuf->b_data;
                    505:        error = uiomove(cp, n, uio);
                    506:        if (!error) {
                    507:                cp[n] = '\0';
                    508:                playstring(cp, n);
                    509:        }
                    510:        return(error);
                    511:     }
                    512: }
                    513: 
                    514: int
1.3     ! dillon    515: spkrclose(dev_t        dev, int flags, int fmt, struct thread *td)
1.1       dillon    516: {
                    517: #ifdef DEBUG
                    518:     (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev));
                    519: #endif /* DEBUG */
                    520: 
                    521:     if (minor(dev) != 0)
                    522:        return(ENXIO);
                    523:     else
                    524:     {
                    525:        wakeup((caddr_t)&endtone);
                    526:        wakeup((caddr_t)&endrest);
                    527:        brelse(spkr_inbuf);
                    528:        spkr_active = FALSE;
                    529:        return(0);
                    530:     }
                    531: }
                    532: 
                    533: int
1.3     ! dillon    534: spkrioctl(dev_t        dev, unsigned long cmd, caddr_t cmdarg, int flags, struct thread*td)
1.1       dillon    535: {
                    536: #ifdef DEBUG
                    537:     (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n",
                    538:        devtoname(dev), cmd);
                    539: #endif /* DEBUG */
                    540: 
                    541:     if (minor(dev) != 0)
                    542:        return(ENXIO);
                    543:     else if (cmd == SPKRTONE)
                    544:     {
                    545:        tone_t  *tp = (tone_t *)cmdarg;
                    546: 
                    547:        if (tp->frequency == 0)
                    548:            rest(tp->duration);
                    549:        else
                    550:            tone(tp->frequency, tp->duration);
                    551:        return 0;
                    552:     }
                    553:     else if (cmd == SPKRTUNE)
                    554:     {
                    555:        tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
                    556:        tone_t ttp;
                    557:        int error;
                    558: 
                    559:        for (; ; tp++) {
                    560:            error = copyin(tp, &ttp, sizeof(tone_t));
                    561:            if (error)
                    562:                    return(error);
                    563:            if (ttp.duration == 0)
                    564:                    break;
                    565:            if (ttp.frequency == 0)
                    566:                 rest(ttp.duration);
                    567:            else
                    568:                 tone(ttp.frequency, ttp.duration);
                    569:        }
                    570:        return(0);
                    571:     }
                    572:     return(EINVAL);
                    573: }
                    574: 
                    575: static void
                    576: spkr_drvinit(void *unused)
                    577: {
                    578:        make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "speaker");
                    579: }
                    580: 
                    581: SYSINIT(spkrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,spkr_drvinit,NULL)
                    582: 
                    583: 
                    584: /* spkr.c ends here */