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/dev/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 /*      $OpenBSD: spkr.c,v 1.27 2022/04/06 18:59:29 naddy 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/ioctl.h>
   54 #include <sys/conf.h>
   55 #include <sys/fcntl.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 const 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 *, size_t);
   83 
   84 /* emit tone of frequency freq for given number of milliseconds */
   85 static void
   86 tone(u_int freq, u_int ms)
   87 {
   88         pcppi_bell(ppicookie, freq, ms, PCPPI_BELL_SLEEP);
   89 }
   90 
   91 /* rest for given number of milliseconds */
   92 static void
   93 rest(int ms)
   94 {
   95         /*
   96          * Set timeout to endrest function, then give up the timeslice.
   97          * This is so other processes can execute while the rest is being
   98          * waited out.
   99          */
  100 #ifdef SPKRDEBUG
  101         printf("rest: %dms\n", ms);
  102 #endif /* SPKRDEBUG */
  103         if (ms > 0)
  104                 tsleep_nsec(rest, SPKRPRI | PCATCH, "rest", MSEC_TO_NSEC(ms));
  105 }
  106 
  107 /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
  108  *
  109  * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
  110  * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
  111  * Requires tone(), rest(), and endtone(). String play is not interruptible
  112  * except possibly at physical block boundaries.
  113  */
  114 
  115 #define toupper(c)      ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
  116 #define isdigit(c)      (((c) >= '') && ((c) <= '9'))
  117 #define dtoi(c)         ((c) - '')
  118 
  119 static int octave;      /* currently selected octave */
  120 static int whole;       /* whole-note time at current tempo, in milliseconds */
  121 static int value;       /* whole divisor for note time, quarter note = 1 */
  122 static int fill;        /* controls spacing of notes */
  123 static int octtrack;    /* octave-tracking on? */
  124 static int octprefix;   /* override current octave-tracking state? */
  125 
  126 /*
  127  * Magic number avoidance...
  128  */
  129 #define SECS_PER_MIN    60      /* seconds per minute */
  130 #define WHOLE_NOTE      4       /* quarter notes per whole note */
  131 #define MIN_VALUE       64      /* the most we can divide a note by */
  132 #define DFLT_VALUE      4       /* default value (quarter-note) */
  133 #define FILLTIME        8       /* for articulation, break note in parts */
  134 #define STACCATO        6       /* 6/8 = 3/4 of note is filled */
  135 #define NORMAL          7       /* 7/8ths of note interval is filled */
  136 #define LEGATO          8       /* all of note interval is filled */
  137 #define DFLT_OCTAVE     4       /* default octave */
  138 #define MIN_TEMPO       32      /* minimum tempo */
  139 #define DFLT_TEMPO      120     /* default tempo */
  140 #define MAX_TEMPO       255     /* max tempo */
  141 #define NUM_MULT        3       /* numerator of dot multiplier */
  142 #define DENOM_MULT      2       /* denominator of dot multiplier */
  143 
  144 /* letter to half-tone:  A   B  C  D  E  F  G */
  145 static int notetab[8] = { 9, 11, 0, 2, 4, 5, 7 };
  146 
  147 /*
  148  * This is the American Standard A440 Equal-Tempered scale with frequencies
  149  * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
  150  * our octave 0 is standard octave 2.
  151  */
  152 #define OCTAVE_NOTES    12      /* semitones per octave */
  153 static int pitchtab[] =
  154 {
  155 /*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
  156 /* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
  157 /* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
  158 /* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
  159 /* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
  160 /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
  161 /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
  162 /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
  163 };
  164 #define NOCTAVES (sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
  165 
  166 static void
  167 playinit(void)
  168 {
  169         octave = DFLT_OCTAVE;
  170         whole = (1000 * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
  171         fill = NORMAL;
  172         value = DFLT_VALUE;
  173         octtrack = 0;
  174         octprefix = 1;  /* act as though there was an initial O(n) */
  175 }
  176 
  177 /* play tone of proper duration for current rhythm signature */
  178 static void
  179 playtone(int pitch, int value, int sustain)
  180 {
  181         int sound, silence, snum = 1, sdenom = 1;
  182 
  183         /* this weirdness avoids floating-point arithmetic */
  184         for (; sustain; sustain--) {
  185                 snum *= NUM_MULT;
  186                 sdenom *= DENOM_MULT;
  187         }
  188 
  189         if (pitch == -1)
  190                 rest(whole * snum / (value * sdenom));
  191         else if (pitch >= 0 &&
  192             pitch < (sizeof(pitchtab) / sizeof(pitchtab[0]))) {
  193                 sound = (whole * snum) / (value * sdenom) -
  194                     (whole * (FILLTIME - fill)) / (value * FILLTIME);
  195                 silence = whole * (FILLTIME-fill) * snum /
  196                     (FILLTIME * value * sdenom);
  197 
  198 #ifdef SPKRDEBUG
  199                 printf("playtone: pitch %d for %dms, rest for %dms\n",
  200                     pitch, sound, silence);
  201 #endif /* SPKRDEBUG */
  202 
  203                 tone(pitchtab[pitch], sound);
  204                 if (fill != LEGATO)
  205                         rest(silence);
  206         }
  207 }
  208 
  209 /* interpret and play an item from a notation string */
  210 static void
  211 playstring(char *cp, size_t slen)
  212 {
  213         int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
  214 
  215 #define GETNUM(cp, v) \
  216 do { \
  217         for (v = 0; slen > 0 && isdigit(cp[1]); ) { \
  218                 v = v * 10 + (*++cp - ''); \
  219                 slen--; \
  220         } \
  221 } while (0)
  222 
  223         for (; slen--; cp++) {
  224                 int sustain, timeval, tempo;
  225                 char c = toupper(*cp);
  226 
  227 #ifdef SPKRDEBUG
  228                 printf("playstring: %c (%x)\n", c, c);
  229 #endif /* SPKRDEBUG */
  230 
  231                 switch (c) {
  232                 case 'A':
  233                 case 'B':
  234                 case 'C':
  235                 case 'D':
  236                 case 'E':
  237                 case 'F':
  238                 case 'G':
  239                         /* compute pitch */
  240                         pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
  241 
  242                         /* this may be followed by an accidental sign */
  243                         if (slen > 0 && (cp[1] == '#' || cp[1] == '+')) {
  244                                 ++pitch;
  245                                 ++cp;
  246                                 slen--;
  247                         } else if (slen > 0 && cp[1] == '-') {
  248                                 --pitch;
  249                                 ++cp;
  250                                 slen--;
  251                         }
  252 
  253                         /*
  254                          * If octave-tracking mode is on, and there has been
  255                          * no octave-setting prefix, find the version of the
  256                          * current letter note closest to the last regardless
  257                          * of octave.
  258                          */
  259                         if (octtrack && !octprefix) {
  260                                 if (abs(pitch - lastpitch) >
  261                                     abs(pitch + OCTAVE_NOTES - lastpitch)) {
  262                                         ++octave;
  263                                         pitch += OCTAVE_NOTES;
  264                                 }
  265 
  266                                 if (abs(pitch - lastpitch) >
  267                                     abs(pitch - OCTAVE_NOTES - lastpitch)) {
  268                                         --octave;
  269                                         pitch -= OCTAVE_NOTES;
  270                                 }
  271                         }
  272                         octprefix = 0;
  273                         lastpitch = pitch;
  274 
  275                         /*
  276                          * ...which may in turn be followed by an override
  277                          * time value
  278                          */
  279                         GETNUM(cp, timeval);
  280                         if (timeval <= 0 || timeval > MIN_VALUE)
  281                                 timeval = value;
  282 
  283                         /* ...and/or sustain dots */
  284                         for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
  285                                 slen--;
  286                                 sustain++;
  287                         }
  288 
  289                         /* time to emit the actual tone */
  290                         playtone(pitch, timeval, sustain);
  291                         break;
  292 
  293                 case 'O':
  294                         if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
  295                                 octprefix = octtrack = 0;
  296                                 ++cp;
  297                                 slen--;
  298                         } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
  299                                 octtrack = 1;
  300                                 ++cp;
  301                                 slen--;
  302                         } else {
  303                                 GETNUM(cp, octave);
  304                                 if (octave >= NOCTAVES)
  305                                         octave = DFLT_OCTAVE;
  306                                 octprefix = 1;
  307                         }
  308                         break;
  309 
  310                 case '>':
  311                         if (octave < NOCTAVES - 1)
  312                                 octave++;
  313                         octprefix = 1;
  314                         break;
  315 
  316                 case '<':
  317                         if (octave > 0)
  318                                 octave--;
  319                         octprefix = 1;
  320                         break;
  321 
  322                 case 'N':
  323                         GETNUM(cp, pitch);
  324                         for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
  325                                 slen--;
  326                                 sustain++;
  327                         }
  328                         playtone(pitch - 1, value, sustain);
  329                         break;
  330 
  331                 case 'L':
  332                         GETNUM(cp, value);
  333                         if (value <= 0 || value > MIN_VALUE)
  334                                 value = DFLT_VALUE;
  335                         break;
  336 
  337                 case 'P':
  338                 case '~':
  339                         /* this may be followed by an override time value */
  340                         GETNUM(cp, timeval);
  341                         if (timeval <= 0 || timeval > MIN_VALUE)
  342                                 timeval = value;
  343                         for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
  344                                 slen--;
  345                                 sustain++;
  346                         }
  347                         playtone(-1, timeval, sustain);
  348                         break;
  349 
  350                 case 'T':
  351                         GETNUM(cp, tempo);
  352                         if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
  353                                 tempo = DFLT_TEMPO;
  354                         whole = (1000 * SECS_PER_MIN * WHOLE_NOTE) / tempo;
  355                         break;
  356 
  357                 case 'M':
  358                         if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
  359                                 fill = NORMAL;
  360                                 ++cp;
  361                                 slen--;
  362                         } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
  363                                 fill = LEGATO;
  364                                 ++cp;
  365                                 slen--;
  366                         } else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's')) {
  367                                 fill = STACCATO;
  368                                 ++cp;
  369                                 slen--;
  370                         }
  371                         break;
  372                 }
  373         }
  374 }
  375 
  376 /******************* UNIX DRIVER HOOKS BEGIN HERE **************************
  377  *
  378  * This section implements driver hooks to run playstring() and the tone(),
  379  * endtone(), and rest() functions defined above.
  380  */
  381 
  382 static int spkr_active; /* exclusion flag */
  383 static void *spkr_inbuf;
  384 
  385 static int spkr_attached = 0;
  386 
  387 int
  388 spkrprobe(struct device *parent, void *match, void *aux)
  389 {
  390         return (!spkr_attached);
  391 }
  392 
  393 void
  394 spkrattach(struct device *parent, struct device *self, void *aux)
  395 {
  396         printf("\n");
  397         ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
  398         spkr_attached = 1;
  399 }
  400 
  401 int
  402 spkropen(dev_t dev, int flags, int mode, struct proc *p)
  403 {
  404 #ifdef SPKRDEBUG
  405         printf("spkropen: entering with dev = %x\n", dev);
  406 #endif /* SPKRDEBUG */
  407 
  408         if (minor(dev) != 0 || !spkr_attached)
  409                 return (ENXIO);
  410         else if (spkr_active)
  411                 return (EBUSY);
  412         else {
  413                 playinit();
  414                 spkr_inbuf = malloc(DEV_BSIZE, M_DEVBUF, M_WAITOK);
  415                 spkr_active = 1;
  416         }
  417         return (0);
  418 }
  419 
  420 int
  421 spkrwrite(dev_t dev, struct uio *uio, int flags)
  422 {
  423         size_t n;
  424         int error;
  425 #ifdef SPKRDEBUG
  426         printf("spkrwrite: entering with dev = %x, count = %zu\n",
  427             dev, uio->uio_resid);
  428 #endif /* SPKRDEBUG */
  429 
  430         if (minor(dev) != 0)
  431                 return (ENXIO);
  432         else {
  433                 n = ulmin(DEV_BSIZE, uio->uio_resid);
  434                 error = uiomove(spkr_inbuf, n, uio);
  435                 if (!error)
  436                         playstring((char *)spkr_inbuf, n);
  437                 return (error);
  438         }
  439 }
  440 
  441 int
  442 spkrclose(dev_t dev, int flags, int mode, struct proc *p)
  443 {
  444 #ifdef SPKRDEBUG
  445         printf("spkrclose: entering with dev = %x\n", dev);
  446 #endif /* SPKRDEBUG */
  447 
  448         if (minor(dev) != 0)
  449                 return (ENXIO);
  450         else {
  451                 tone(0, 0);
  452                 free(spkr_inbuf, M_DEVBUF, DEV_BSIZE);
  453                 spkr_active = 0;
  454         }
  455         return (0);
  456 }
  457 
  458 int
  459 spkrioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
  460 {
  461         tone_t *tp, ttp;
  462         int error;
  463 
  464 #ifdef SPKRDEBUG
  465         printf("spkrioctl: entering with dev = %x, cmd = %lx\n", dev, cmd);
  466 #endif /* SPKRDEBUG */
  467 
  468         if (minor(dev) != 0)
  469                 return (ENXIO);
  470 
  471         switch (cmd) {
  472         case SPKRTONE:
  473         case SPKRTUNE:
  474                 if ((flag & FWRITE) == 0)
  475                         return (EACCES);
  476         default:
  477                 break;
  478         }
  479 
  480         switch (cmd) {
  481         case SPKRTONE:
  482                 tp = (tone_t *)data;
  483 
  484                 if (tp->duration < 0 || tp->frequency < 0)
  485                         return (EINVAL);
  486                 if (tp->frequency == 0)
  487                         rest(tp->duration);
  488                 else
  489                         tone(tp->frequency, tp->duration);
  490                 break;
  491         case SPKRTUNE:
  492                 tp = (tone_t *)(*(caddr_t *)data);
  493 
  494                 for (; ; tp++) {
  495                         error = copyin(tp, &ttp, sizeof(tone_t));
  496                         if (error)
  497                                 return (error);
  498                         if (ttp.duration < 0 || ttp.frequency < 0)
  499                                 return (EINVAL);
  500                         if (ttp.duration == 0)
  501                                 break;
  502                         if (ttp.frequency == 0)
  503                                 rest(ttp.duration);
  504                         else
  505                                 tone(ttp.frequency, ttp.duration);
  506                 }
  507                 break;
  508         default:
  509                 return (ENOTTY);
  510         }
  511 
  512         return (0);
  513 }

Cache object: 5c1305e7b90b3d202e2eb5963d5106c0


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