root/dev/isa/spkr.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tone
  2. rest
  3. playinit
  4. playtone
  5. playstring
  6. spkrprobe
  7. spkrattach
  8. spkropen
  9. spkrwrite
  10. spkrclose
  11. spkrioctl

    1 /*      $OpenBSD: spkr.c,v 1.10 2006/03/09 22:35:23 miod Exp $  */
    2 /*      $NetBSD: spkr.c,v 1.1 1998/04/15 20:26:18 drochner Exp $        */
    3 
    4 /*
    5  * Copyright (c) 1990 Eric S. Raymond (esr@snark.thyrsus.com)
    6  * Copyright (c) 1990 Andrew A. Chernov (ache@astral.msk.su)
    7  * Copyright (c) 1990 Lennart Augustsson (lennart@augustsson.net)
    8  * All rights reserved.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  * 3. All advertising materials mentioning features or use of this software
   19  *    must display the following acknowledgement:
   20  *      This product includes software developed by Eric S. Raymond
   21  * 4. The name of the author may not be used to endorse or promote products
   22  *    derived from this software without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   27  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
   28  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   29  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   30  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
   32  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34  * POSSIBILITY OF SUCH DAMAGE.
   35  */
   36 
   37 /*
   38  * spkr.c -- device driver for console speaker on 80386
   39  *
   40  * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
   41  *      modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
   42  *      386bsd only clean version, all SYSV stuff removed
   43  *      use hz value from param.c
   44  */
   45 
   46 #include <sys/param.h>
   47 #include <sys/systm.h>
   48 #include <sys/kernel.h>
   49 #include <sys/errno.h>
   50 #include <sys/device.h>
   51 #include <sys/malloc.h>
   52 #include <sys/uio.h>
   53 #include <sys/proc.h>
   54 #include <sys/ioctl.h>
   55 #include <sys/conf.h>
   56 
   57 #include <dev/isa/pcppivar.h>
   58 
   59 #include <dev/isa/spkrio.h>
   60 
   61 cdev_decl(spkr);
   62 
   63 int spkrprobe(struct device *, void *, void *);
   64 void spkrattach(struct device *, struct device *, void *);
   65 
   66 struct cfattach spkr_ca = {
   67         sizeof(struct device), spkrprobe, spkrattach
   68 };
   69 
   70 struct cfdriver spkr_cd = {
   71         NULL, "spkr", DV_DULL
   72 };
   73 
   74 static pcppi_tag_t ppicookie;
   75 
   76 #define SPKRPRI (PZERO - 1)
   77 
   78 static void tone(u_int, u_int);
   79 static void rest(int);
   80 static void playinit(void);
   81 static void playtone(int, int, int);
   82 static void playstring(char *, int);
   83 
   84 /* emit tone of frequency hz for given number of ticks */
   85 static void
   86 tone(hz, ticks)
   87         u_int hz, ticks;
   88 {
   89         pcppi_bell(ppicookie, hz, ticks, PCPPI_BELL_SLEEP);
   90 }
   91 
   92 /* rest for given number of ticks */
   93 static void
   94 rest(ticks)
   95         int ticks;
   96 {
   97         /*
   98          * Set timeout to endrest function, then give up the timeslice.
   99          * This is so other processes can execute while the rest is being
  100          * waited out.
  101          */
  102 #ifdef SPKRDEBUG
  103         printf("rest: %d\n", ticks);
  104 #endif /* SPKRDEBUG */
  105         if (ticks > 0)
  106                 tsleep(rest, SPKRPRI | PCATCH, "rest", ticks);
  107 }
  108 
  109 /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
  110  *
  111  * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
  112  * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
  113  * Requires tone(), rest(), and endtone(). String play is not interruptible
  114  * except possibly at physical block boundaries.
  115  */
  116 
  117 typedef int     bool;
  118 #define TRUE    1
  119 #define FALSE   0
  120 
  121 #define toupper(c)      ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
  122 #define isdigit(c)      (((c) >= '0') && ((c) <= '9'))
  123 #define dtoi(c)         ((c) - '0')
  124 
  125 static int octave;      /* currently selected octave */
  126 static int whole;       /* whole-note time at current tempo, in ticks */
  127 static int value;       /* whole divisor for note time, quarter note = 1 */
  128 static int fill;        /* controls spacing of notes */
  129 static bool octtrack;   /* octave-tracking on? */
  130 static bool octprefix;  /* override current octave-tracking state? */
  131 
  132 /*
  133  * Magic number avoidance...
  134  */
  135 #define SECS_PER_MIN    60      /* seconds per minute */
  136 #define WHOLE_NOTE      4       /* quarter notes per whole note */
  137 #define MIN_VALUE       64      /* the most we can divide a note by */
  138 #define DFLT_VALUE      4       /* default value (quarter-note) */
  139 #define FILLTIME        8       /* for articulation, break note in parts */
  140 #define STACCATO        6       /* 6/8 = 3/4 of note is filled */
  141 #define NORMAL          7       /* 7/8ths of note interval is filled */
  142 #define LEGATO          8       /* all of note interval is filled */
  143 #define DFLT_OCTAVE     4       /* default octave */
  144 #define MIN_TEMPO       32      /* minimum tempo */
  145 #define DFLT_TEMPO      120     /* default tempo */
  146 #define MAX_TEMPO       255     /* max tempo */
  147 #define NUM_MULT        3       /* numerator of dot multiplier */
  148 #define DENOM_MULT      2       /* denominator of dot multiplier */
  149 
  150 /* letter to half-tone:  A   B  C  D  E  F  G */
  151 static int notetab[8] = { 9, 11, 0, 2, 4, 5, 7 };
  152 
  153 /*
  154  * This is the American Standard A440 Equal-Tempered scale with frequencies
  155  * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
  156  * our octave 0 is standard octave 2.
  157  */
  158 #define OCTAVE_NOTES    12      /* semitones per octave */
  159 static int pitchtab[] =
  160 {
  161 /*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
  162 /* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
  163 /* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
  164 /* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
  165 /* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
  166 /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
  167 /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
  168 /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
  169 };
  170 #define NOCTAVES (sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
  171 
  172 static void
  173 playinit()
  174 {
  175         octave = DFLT_OCTAVE;
  176         whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
  177         fill = NORMAL;
  178         value = DFLT_VALUE;
  179         octtrack = FALSE;
  180         octprefix = TRUE;       /* act as though there was an initial O(n) */
  181 }
  182 
  183 /* play tone of proper duration for current rhythm signature */
  184 static void
  185 playtone(pitch, value, sustain)
  186         int pitch, value, sustain;
  187 {
  188         int sound, silence, snum = 1, sdenom = 1;
  189 
  190         /* this weirdness avoids floating-point arithmetic */
  191         for (; sustain; sustain--) {
  192                 snum *= NUM_MULT;
  193                 sdenom *= DENOM_MULT;
  194         }
  195 
  196         if (pitch == -1)
  197                 rest(whole * snum / (value * sdenom));
  198         else if (pitch >= 0 &&
  199             pitch < (sizeof(pitchtab) / sizeof(pitchtab[0]))) {
  200                 sound = (whole * snum) / (value * sdenom) -
  201                     (whole * (FILLTIME - fill)) / (value * FILLTIME);
  202                 silence = whole * (FILLTIME-fill) * snum /
  203                     (FILLTIME * value * sdenom);
  204 
  205 #ifdef SPKRDEBUG
  206                 printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
  207                     pitch, sound, silence);
  208 #endif /* SPKRDEBUG */
  209 
  210                 tone(pitchtab[pitch], sound);
  211                 if (fill != LEGATO)
  212                         rest(silence);
  213         }
  214 }
  215 
  216 /* interpret and play an item from a notation string */
  217 static void
  218 playstring(cp, slen)
  219         char *cp;
  220         int slen;
  221 {
  222         int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
  223 
  224 #define GETNUM(cp, v) \
  225 do { \
  226         for (v = 0; slen > 0 && isdigit(cp[1]); ) { \
  227                 v = v * 10 + (*++cp - '0'); \
  228                 slen--; \
  229         } \
  230 } while (0)
  231 
  232         for (; slen--; cp++) {
  233                 int sustain, timeval, tempo;
  234                 char c = toupper(*cp);
  235 
  236 #ifdef SPKRDEBUG
  237                 printf("playstring: %c (%x)\n", c, c);
  238 #endif /* SPKRDEBUG */
  239 
  240                 switch (c) {
  241                 case 'A':
  242                 case 'B':
  243                 case 'C':
  244                 case 'D':
  245                 case 'E':
  246                 case 'F':
  247                 case 'G':
  248                         /* compute pitch */
  249                         pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
  250 
  251                         /* this may be followed by an accidental sign */
  252                         if (slen > 0 && (cp[1] == '#' || cp[1] == '+')) {
  253                                 ++pitch;
  254                                 ++cp;
  255                                 slen--;
  256                         } else if (slen > 0 && cp[1] == '-') {
  257                                 --pitch;
  258                                 ++cp;
  259                                 slen--;
  260                         }
  261 
  262                         /*
  263                          * If octave-tracking mode is on, and there has been
  264                          * no octave-setting prefix, find the version of the
  265                          * current letter note closest to the last regardless
  266                          * of octave.
  267                          */
  268                         if (octtrack && !octprefix) {
  269                                 if (abs(pitch - lastpitch) >
  270                                     abs(pitch + OCTAVE_NOTES - lastpitch)) {
  271                                         ++octave;
  272                                         pitch += OCTAVE_NOTES;
  273                                 }
  274 
  275                                 if (abs(pitch - lastpitch) >
  276                                     abs(pitch - OCTAVE_NOTES - lastpitch)) {
  277                                         --octave;
  278                                         pitch -= OCTAVE_NOTES;
  279                                 }
  280                         }
  281                         octprefix = FALSE;
  282                         lastpitch = pitch;
  283 
  284                         /*
  285                          * ...which may in turn be followed by an override
  286                          * time value
  287                          */
  288                         GETNUM(cp, timeval);
  289                         if (timeval <= 0 || timeval > MIN_VALUE)
  290                                 timeval = value;
  291 
  292                         /* ...and/or sustain dots */
  293                         for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
  294                                 slen--;
  295                                 sustain++;
  296                         }
  297 
  298                         /* time to emit the actual tone */
  299                         playtone(pitch, timeval, sustain);
  300                         break;
  301 
  302                 case 'O':
  303                         if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
  304                                 octprefix = octtrack = FALSE;
  305                                 ++cp;
  306                                 slen--;
  307                         } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
  308                                 octtrack = TRUE;
  309                                 ++cp;
  310                                 slen--;
  311                         } else {
  312                                 GETNUM(cp, octave);
  313                                 if (octave >= NOCTAVES)
  314                                         octave = DFLT_OCTAVE;
  315                                 octprefix = TRUE;
  316                         }
  317                         break;
  318 
  319                 case '>':
  320                         if (octave < NOCTAVES - 1)
  321                                 octave++;
  322                         octprefix = TRUE;
  323                         break;
  324 
  325                 case '<':
  326                         if (octave > 0)
  327                                 octave--;
  328                         octprefix = TRUE;
  329                         break;
  330 
  331                 case 'N':
  332                         GETNUM(cp, pitch);
  333                         for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
  334                                 slen--;
  335                                 sustain++;
  336                         }
  337                         playtone(pitch - 1, value, sustain);
  338                         break;
  339 
  340                 case 'L':
  341                         GETNUM(cp, value);
  342                         if (value <= 0 || value > MIN_VALUE)
  343                                 value = DFLT_VALUE;
  344                         break;
  345 
  346                 case 'P':
  347                 case '~':
  348                         /* this may be followed by an override time value */
  349                         GETNUM(cp, timeval);
  350                         if (timeval <= 0 || timeval > MIN_VALUE)
  351                                 timeval = value;
  352                         for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
  353                                 slen--;
  354                                 sustain++;
  355                         }
  356                         playtone(-1, timeval, sustain);
  357                         break;
  358 
  359                 case 'T':
  360                         GETNUM(cp, tempo);
  361                         if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
  362                                 tempo = DFLT_TEMPO;
  363                         whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
  364                         break;
  365 
  366                 case 'M':
  367                         if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
  368                                 fill = NORMAL;
  369                                 ++cp;
  370                                 slen--;
  371                         } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
  372                                 fill = LEGATO;
  373                                 ++cp;
  374                                 slen--;
  375                         } else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's')) {
  376                                 fill = STACCATO;
  377                                 ++cp;
  378                                 slen--;
  379                         }
  380                         break;
  381                 }
  382         }
  383 }
  384 
  385 /******************* UNIX DRIVER HOOKS BEGIN HERE **************************
  386  *
  387  * This section implements driver hooks to run playstring() and the tone(),
  388  * endtone(), and rest() functions defined above.
  389  */
  390 
  391 static int spkr_active; /* exclusion flag */
  392 static void *spkr_inbuf;
  393 
  394 static int spkr_attached = 0;
  395 
  396 int
  397 spkrprobe(parent, match, aux)
  398         struct device *parent;
  399         void *match;
  400         void *aux;
  401 {
  402         return (!spkr_attached);
  403 }
  404 
  405 void
  406 spkrattach(parent, self, aux)
  407         struct device *parent;
  408         struct device *self;
  409         void *aux;
  410 {
  411         printf("\n");
  412         ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
  413         spkr_attached = 1;
  414 }
  415 
  416 int
  417 spkropen(dev, flags, mode, p)
  418         dev_t dev;
  419         int flags;
  420         int mode;
  421         struct proc *p;
  422 {
  423 #ifdef SPKRDEBUG
  424         printf("spkropen: entering with dev = %x\n", dev);
  425 #endif /* SPKRDEBUG */
  426 
  427         if (minor(dev) != 0 || !spkr_attached)
  428                 return (ENXIO);
  429         else if (spkr_active)
  430                 return (EBUSY);
  431         else {
  432                 playinit();
  433                 spkr_inbuf = malloc(DEV_BSIZE, M_DEVBUF, M_WAITOK);
  434                 spkr_active = 1;
  435         }
  436         return (0);
  437 }
  438 
  439 int
  440 spkrwrite(dev, uio, flags)
  441         dev_t dev;
  442         struct uio *uio;
  443         int flags;
  444 {
  445         int n;
  446         int error;
  447 #ifdef SPKRDEBUG
  448         printf("spkrwrite: entering with dev = %x, count = %d\n",
  449             dev, uio->uio_resid);
  450 #endif /* SPKRDEBUG */
  451 
  452         if (minor(dev) != 0)
  453                 return (ENXIO);
  454         else {
  455                 n = min(DEV_BSIZE, uio->uio_resid);
  456                 error = uiomove(spkr_inbuf, n, uio);
  457                 if (!error)
  458                         playstring((char *)spkr_inbuf, n);
  459                 return (error);
  460         }
  461 }
  462 
  463 int
  464 spkrclose(dev, flags, mode, p)
  465         dev_t dev;
  466         int flags;
  467         int mode;
  468         struct proc *p;
  469 {
  470 #ifdef SPKRDEBUG
  471         printf("spkrclose: entering with dev = %x\n", dev);
  472 #endif /* SPKRDEBUG */
  473 
  474         if (minor(dev) != 0)
  475                 return (ENXIO);
  476         else {
  477                 tone(0, 0);
  478                 free(spkr_inbuf, M_DEVBUF);
  479                 spkr_active = 0;
  480         }
  481         return (0);
  482 }
  483 
  484 int
  485 spkrioctl(dev, cmd, data, flag, p)
  486         dev_t dev;
  487         u_long cmd;
  488         caddr_t data;
  489         int flag;
  490         struct proc *p;
  491 {
  492         tone_t *tp, ttp;
  493         int error;
  494 
  495 #ifdef SPKRDEBUG
  496         printf("spkrioctl: entering with dev = %x, cmd = %lx\n", dev, cmd);
  497 #endif /* SPKRDEBUG */
  498 
  499         if (minor(dev) != 0)
  500                 return (ENXIO);
  501 
  502         switch (cmd) {
  503         case SPKRTONE:
  504                 tp = (tone_t *)data;
  505 
  506                 if (tp->frequency == 0)
  507                         rest(tp->duration);
  508                 else
  509                         tone(tp->frequency, tp->duration);
  510                 break;
  511         case SPKRTUNE:
  512                 tp = (tone_t *)(*(caddr_t *)data);
  513 
  514                 for (; ; tp++) {
  515                         error = copyin(tp, &ttp, sizeof(tone_t));
  516                         if (error)
  517                                 return (error);
  518                         if (ttp.duration == 0)
  519                                 break;
  520                         if (ttp.frequency == 0)
  521                                 rest(ttp.duration);
  522                         else
  523                                 tone(ttp.frequency, ttp.duration);
  524                 }
  525                 break;
  526         default:
  527                 return (ENOTTY);
  528         }
  529 
  530         return (0);
  531 }

/* [<][>][^][v][top][bottom][index][help] */