The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/spkr.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 9c6e35c018b7118f58a69016ec7dbcd4


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.