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

Cache object: 416ee13d10d85a3b7f0ae65bae491cca


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