LCOV - code coverage report
Current view: top level - dev/isa - spkr.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 178 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13