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 "speaker.h"
   11 
   12 #if NSPEAKER > 0
   13 
   14 #include "opt_devfs.h"
   15 
   16 #include <sys/param.h>
   17 #include <sys/systm.h>
   18 #include <sys/kernel.h>
   19 #include <sys/buf.h>
   20 #include <sys/uio.h>
   21 #include <sys/conf.h>
   22 #include <i386/isa/isa.h>
   23 #include <i386/isa/timerreg.h>
   24 #include <machine/clock.h>
   25 #include <machine/speaker.h>
   26 
   27 #ifdef  DEVFS
   28 #include <sys/devfsext.h>
   29 static void     *devfs_token;
   30 #endif
   31 
   32 static  d_open_t        spkropen;
   33 static  d_close_t       spkrclose;
   34 static  d_write_t       spkrwrite;
   35 static  d_ioctl_t       spkrioctl;
   36 
   37 #define CDEV_MAJOR 26
   38 static struct cdevsw spkr_cdevsw = 
   39         { spkropen,     spkrclose,      noread,         spkrwrite,      /*26*/
   40           spkrioctl,    nostop,         nullreset,      nodevtotty,/* spkr */
   41           seltrue,      nommap,         NULL,   "spkr", NULL,   -1 };
   42 
   43 /**************** MACHINE DEPENDENT PART STARTS HERE *************************
   44  *
   45  * This section defines a function tone() which causes a tone of given
   46  * frequency and duration from the ISA console speaker.
   47  * Another function endtone() is defined to force sound off, and there is
   48  * also a rest() entry point to do pauses.
   49  *
   50  * Audible sound is generated using the Programmable Interval Timer (PIT) and
   51  * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The
   52  * PPI controls whether sound is passed through at all; the PIT's channel 2 is
   53  * used to generate clicks (a square wave) of whatever frequency is desired.
   54  */
   55 
   56 /*
   57  * PPI control values.
   58  * XXX should be in a header and used in clock.c.
   59  */
   60 #define PPI_SPKR        0x03    /* turn these PPI bits on to pass sound */
   61 
   62 #define SPKRPRI PSOCK
   63 static char endtone, endrest;
   64 
   65 static void tone __P((unsigned int thz, unsigned int ticks));
   66 static void rest __P((int ticks));
   67 static void playinit __P((void));
   68 static void playtone __P((int pitch, int value, int sustain));
   69 static int abs __P((int n));
   70 static void playstring __P((char *cp, size_t slen));
   71 
   72 /* emit tone of frequency thz for given number of ticks */
   73 static void
   74 tone(thz, ticks)
   75         unsigned int thz, ticks;
   76 {
   77     unsigned int divisor;
   78     int sps;
   79 
   80     if (thz <= 0)
   81         return;
   82 
   83     divisor = timer_freq / thz;
   84 
   85 #ifdef DEBUG
   86     (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
   87 #endif /* DEBUG */
   88 
   89     /* set timer to generate clicks at given frequency in Hertz */
   90     sps = splclock();
   91 
   92     if (acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT)) {
   93         /* enter list of waiting procs ??? */
   94         splx(sps);
   95         return;
   96     }
   97     splx(sps);
   98     disable_intr();
   99     outb(TIMER_CNTR2, (divisor & 0xff));        /* send lo byte */
  100     outb(TIMER_CNTR2, (divisor >> 8));  /* send hi byte */
  101     enable_intr();
  102 
  103     /* turn the speaker on */
  104     outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
  105 
  106     /*
  107      * Set timeout to endtone function, then give up the timeslice.
  108      * This is so other processes can execute while the tone is being
  109      * emitted.
  110      */
  111     if (ticks > 0)
  112         tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
  113     outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
  114     sps = splclock();
  115     release_timer2();
  116     splx(sps);
  117 }
  118 
  119 /* rest for given number of ticks */
  120 static void
  121 rest(ticks)
  122         int     ticks;
  123 {
  124     /*
  125      * Set timeout to endrest function, then give up the timeslice.
  126      * This is so other processes can execute while the rest is being
  127      * waited out.
  128      */
  129 #ifdef DEBUG
  130     (void) printf("rest: %d\n", ticks);
  131 #endif /* DEBUG */
  132     if (ticks > 0)
  133         tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
  134 }
  135 
  136 /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
  137  *
  138  * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
  139  * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
  140  * tracking facility are added.
  141  * Requires tone(), rest(), and endtone(). String play is not interruptible
  142  * except possibly at physical block boundaries.
  143  */
  144 
  145 typedef int     bool;
  146 #define TRUE    1
  147 #define FALSE   0
  148 
  149 #define toupper(c)      ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
  150 #define isdigit(c)      (((c) >= '') && ((c) <= '9'))
  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 = %x\n", 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 = %x, count = %d\n",
  493                 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 = %x\n", 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 = %lx, cmd = %lx\n",
  550         (unsigned long)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 
  588 static int spkr_devsw_installed;
  589 
  590 static void
  591 spkr_drvinit(void *unused)
  592 {
  593         dev_t dev;
  594 
  595         if( ! spkr_devsw_installed ) {
  596                 dev = makedev(CDEV_MAJOR, 0);
  597                 cdevsw_add(&dev,&spkr_cdevsw, NULL);
  598                 spkr_devsw_installed = 1;
  599 #ifdef DEVFS
  600                 devfs_token = devfs_add_devswf(&spkr_cdevsw, 0, DV_CHR,
  601                                                UID_ROOT, GID_WHEEL, 0600,
  602                                                "speaker");
  603 #endif
  604         }
  605 }
  606 
  607 SYSINIT(spkrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,spkr_drvinit,NULL)
  608 
  609 
  610 #endif  /* NSPEAKER > 0 */
  611 /* spkr.c ends here */

Cache object: e140df7944760758a0dc02a98ac17019


[ 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.