186cb007fSWarner Losh /*- 2e614aa8aSAndrey A. Chernov * spkr.c -- device driver for console speaker 35b81b6b3SRodney W. Grimes * 4e614aa8aSAndrey A. Chernov * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993 5e614aa8aSAndrey A. Chernov * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su> 630201b6cSMatthew N. Dodd * modified for PC98 by Kakefuda 75b81b6b3SRodney W. Grimes */ 85b81b6b3SRodney W. Grimes 9006124d8SDavid E. O'Brien #include <sys/cdefs.h> 10006124d8SDavid E. O'Brien __FBSDID("$FreeBSD$"); 11006124d8SDavid E. O'Brien 12f540b106SGarrett Wollman #include <sys/param.h> 13f540b106SGarrett Wollman #include <sys/systm.h> 14f540b106SGarrett Wollman #include <sys/kernel.h> 152a50a6d7SMike Smith #include <sys/module.h> 16f540b106SGarrett Wollman #include <sys/uio.h> 1787f6c662SJulian Elischer #include <sys/conf.h> 185b664c7cSPoul-Henning Kamp #include <sys/ctype.h> 19e4961fbfSPoul-Henning Kamp #include <sys/malloc.h> 20b5e8ce9fSBruce Evans #include <machine/clock.h> 216d8200ffSRuslan Ermilov #include <dev/speaker/speaker.h> 225b81b6b3SRodney W. Grimes 2387f6c662SJulian Elischer static d_open_t spkropen; 2487f6c662SJulian Elischer static d_close_t spkrclose; 2587f6c662SJulian Elischer static d_write_t spkrwrite; 2687f6c662SJulian Elischer static d_ioctl_t spkrioctl; 2787f6c662SJulian Elischer 284e2f199eSPoul-Henning Kamp static struct cdevsw spkr_cdevsw = { 29dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 30dc08ffecSPoul-Henning Kamp .d_flags = D_NEEDGIANT, 317ac40f5fSPoul-Henning Kamp .d_open = spkropen, 327ac40f5fSPoul-Henning Kamp .d_close = spkrclose, 337ac40f5fSPoul-Henning Kamp .d_write = spkrwrite, 347ac40f5fSPoul-Henning Kamp .d_ioctl = spkrioctl, 357ac40f5fSPoul-Henning Kamp .d_name = "spkr", 364e2f199eSPoul-Henning Kamp }; 378af5d536SJulian Elischer 38959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer"); 39e4961fbfSPoul-Henning Kamp 40338c585eSPoul-Henning Kamp /* 41338c585eSPoul-Henning Kamp **************** MACHINE DEPENDENT PART STARTS HERE ************************* 425b81b6b3SRodney W. Grimes * This section defines a function tone() which causes a tone of given 439275c7ccSBruce Evans * frequency and duration from the ISA console speaker. 445b81b6b3SRodney W. Grimes * Another function endtone() is defined to force sound off, and there is 455b81b6b3SRodney W. Grimes * also a rest() entry point to do pauses. 465b81b6b3SRodney W. Grimes * 475b81b6b3SRodney W. Grimes * Audible sound is generated using the Programmable Interval Timer (PIT) and 489275c7ccSBruce Evans * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The 495b81b6b3SRodney W. Grimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is 505b81b6b3SRodney W. Grimes * used to generate clicks (a square wave) of whatever frequency is desired. 515b81b6b3SRodney W. Grimes */ 525b81b6b3SRodney W. Grimes 531e064bd4SAndrey A. Chernov #define SPKRPRI PSOCK 541e064bd4SAndrey A. Chernov static char endtone, endrest; 555b81b6b3SRodney W. Grimes 56b2424ac0SBrian Somers static void tone(unsigned int thz, unsigned int centisecs); 57b2424ac0SBrian Somers static void rest(int centisecs); 5889c9a483SAlfred Perlstein static void playinit(void); 5989c9a483SAlfred Perlstein static void playtone(int pitch, int value, int sustain); 6089c9a483SAlfred Perlstein static void playstring(char *cp, size_t slen); 610dfe10a6SBruce Evans 62338c585eSPoul-Henning Kamp /* 63338c585eSPoul-Henning Kamp * Emit tone of frequency thz for given number of centisecs 64338c585eSPoul-Henning Kamp */ 65f4de22acSJoerg Wunsch static void 66338c585eSPoul-Henning Kamp tone(unsigned int thz, unsigned int centisecs) 675b81b6b3SRodney W. Grimes { 681f770560SWarner Losh int timo; 695b81b6b3SRodney W. Grimes 709714de6aSDavid Greenman if (thz <= 0) 719714de6aSDavid Greenman return; 729714de6aSDavid Greenman 735b81b6b3SRodney W. Grimes #ifdef DEBUG 74b2424ac0SBrian Somers (void) printf("tone: thz=%d centisecs=%d\n", thz, centisecs); 755b81b6b3SRodney W. Grimes #endif /* DEBUG */ 765b81b6b3SRodney W. Grimes 775b81b6b3SRodney W. Grimes /* set timer to generate clicks at given frequency in Hertz */ 7824072ca3SYoshihiro Takahashi if (timer_spkr_acquire()) { 79bc4ff6e3SSøren Schmidt /* enter list of waiting procs ??? */ 80bc4ff6e3SSøren Schmidt return; 81bc4ff6e3SSøren Schmidt } 82f4de22acSJoerg Wunsch disable_intr(); 83e4659858SPoul-Henning Kamp timer_spkr_setfreq(thz); 84f4de22acSJoerg Wunsch enable_intr(); 855b81b6b3SRodney W. Grimes 865b81b6b3SRodney W. Grimes /* 875b81b6b3SRodney W. Grimes * Set timeout to endtone function, then give up the timeslice. 885b81b6b3SRodney W. Grimes * This is so other processes can execute while the tone is being 895b81b6b3SRodney W. Grimes * emitted. 905b81b6b3SRodney W. Grimes */ 91b2424ac0SBrian Somers timo = centisecs * hz / 100; 92b2424ac0SBrian Somers if (timo > 0) 93b2424ac0SBrian Somers tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", timo); 9424072ca3SYoshihiro Takahashi timer_spkr_release(); 955b81b6b3SRodney W. Grimes } 965b81b6b3SRodney W. Grimes 97338c585eSPoul-Henning Kamp /* 98338c585eSPoul-Henning Kamp * Rest for given number of centisecs 99338c585eSPoul-Henning Kamp */ 100f4de22acSJoerg Wunsch static void 101338c585eSPoul-Henning Kamp rest(int centisecs) 1025b81b6b3SRodney W. Grimes { 103b2424ac0SBrian Somers int timo; 104b2424ac0SBrian Somers 1055b81b6b3SRodney W. Grimes /* 1065b81b6b3SRodney W. Grimes * Set timeout to endrest function, then give up the timeslice. 1075b81b6b3SRodney W. Grimes * This is so other processes can execute while the rest is being 1085b81b6b3SRodney W. Grimes * waited out. 1095b81b6b3SRodney W. Grimes */ 1105b81b6b3SRodney W. Grimes #ifdef DEBUG 111b2424ac0SBrian Somers (void) printf("rest: %d\n", centisecs); 1125b81b6b3SRodney W. Grimes #endif /* DEBUG */ 113b2424ac0SBrian Somers timo = centisecs * hz / 100; 114b2424ac0SBrian Somers if (timo > 0) 115b2424ac0SBrian Somers tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", timo); 1165b81b6b3SRodney W. Grimes } 1175b81b6b3SRodney W. Grimes 118338c585eSPoul-Henning Kamp /* 119338c585eSPoul-Henning Kamp **************** PLAY STRING INTERPRETER BEGINS HERE ********************** 1205b81b6b3SRodney W. Grimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 121e614aa8aSAndrey A. Chernov * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 122e614aa8aSAndrey A. Chernov * tracking facility are added. 1235b81b6b3SRodney W. Grimes * Requires tone(), rest(), and endtone(). String play is not interruptible 1245b81b6b3SRodney W. Grimes * except possibly at physical block boundaries. 1255b81b6b3SRodney W. Grimes */ 1265b81b6b3SRodney W. Grimes 127103af58fSMatthew D Fleming #ifndef __bool_true_false_are_defined 1285b81b6b3SRodney W. Grimes typedef int bool; 129103af58fSMatthew D Fleming #endif 1305b81b6b3SRodney W. Grimes #define TRUE 1 1315b81b6b3SRodney W. Grimes #define FALSE 0 1325b81b6b3SRodney W. Grimes 1335b81b6b3SRodney W. Grimes #define dtoi(c) ((c) - '0') 1345b81b6b3SRodney W. Grimes 1355b81b6b3SRodney W. Grimes static int octave; /* currently selected octave */ 1365b81b6b3SRodney W. Grimes static int whole; /* whole-note time at current tempo, in ticks */ 1375b81b6b3SRodney W. Grimes static int value; /* whole divisor for note time, quarter note = 1 */ 1385b81b6b3SRodney W. Grimes static int fill; /* controls spacing of notes */ 1395b81b6b3SRodney W. Grimes static bool octtrack; /* octave-tracking on? */ 1405b81b6b3SRodney W. Grimes static bool octprefix; /* override current octave-tracking state? */ 1415b81b6b3SRodney W. Grimes 1425b81b6b3SRodney W. Grimes /* 1435b81b6b3SRodney W. Grimes * Magic number avoidance... 1445b81b6b3SRodney W. Grimes */ 1455b81b6b3SRodney W. Grimes #define SECS_PER_MIN 60 /* seconds per minute */ 1465b81b6b3SRodney W. Grimes #define WHOLE_NOTE 4 /* quarter notes per whole note */ 1475b81b6b3SRodney W. Grimes #define MIN_VALUE 64 /* the most we can divide a note by */ 1485b81b6b3SRodney W. Grimes #define DFLT_VALUE 4 /* default value (quarter-note) */ 1495b81b6b3SRodney W. Grimes #define FILLTIME 8 /* for articulation, break note in parts */ 1505b81b6b3SRodney W. Grimes #define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 1515b81b6b3SRodney W. Grimes #define NORMAL 7 /* 7/8ths of note interval is filled */ 1525b81b6b3SRodney W. Grimes #define LEGATO 8 /* all of note interval is filled */ 1535b81b6b3SRodney W. Grimes #define DFLT_OCTAVE 4 /* default octave */ 1545b81b6b3SRodney W. Grimes #define MIN_TEMPO 32 /* minimum tempo */ 1555b81b6b3SRodney W. Grimes #define DFLT_TEMPO 120 /* default tempo */ 1565b81b6b3SRodney W. Grimes #define MAX_TEMPO 255 /* max tempo */ 1575b81b6b3SRodney W. Grimes #define NUM_MULT 3 /* numerator of dot multiplier */ 1585b81b6b3SRodney W. Grimes #define DENOM_MULT 2 /* denominator of dot multiplier */ 1595b81b6b3SRodney W. Grimes 1605b81b6b3SRodney W. Grimes /* letter to half-tone: A B C D E F G */ 1615b81b6b3SRodney W. Grimes static int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 1625b81b6b3SRodney W. Grimes 1635b81b6b3SRodney W. Grimes /* 1645b81b6b3SRodney W. Grimes * This is the American Standard A440 Equal-Tempered scale with frequencies 1655b81b6b3SRodney W. Grimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 1665b81b6b3SRodney W. Grimes * our octave 0 is standard octave 2. 1675b81b6b3SRodney W. Grimes */ 1685b81b6b3SRodney W. Grimes #define OCTAVE_NOTES 12 /* semitones per octave */ 1695b81b6b3SRodney W. Grimes static int pitchtab[] = 1705b81b6b3SRodney W. Grimes { 1715b81b6b3SRodney W. Grimes /* C C# D D# E F F# G G# A A# B*/ 1725b81b6b3SRodney W. Grimes /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 1735b81b6b3SRodney W. Grimes /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 1745b81b6b3SRodney W. Grimes /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 1755b81b6b3SRodney W. Grimes /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1765b81b6b3SRodney W. Grimes /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 1775b81b6b3SRodney W. Grimes /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 1785b81b6b3SRodney W. Grimes /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 1795b81b6b3SRodney W. Grimes }; 1805b81b6b3SRodney W. Grimes 181f4de22acSJoerg Wunsch static void 18257c46916SDimitry Andric playinit(void) 1835b81b6b3SRodney W. Grimes { 1845b81b6b3SRodney W. Grimes octave = DFLT_OCTAVE; 1857344a290SBrian Somers whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 1865b81b6b3SRodney W. Grimes fill = NORMAL; 1875b81b6b3SRodney W. Grimes value = DFLT_VALUE; 1885b81b6b3SRodney W. Grimes octtrack = FALSE; 1895b81b6b3SRodney W. Grimes octprefix = TRUE; /* act as though there was an initial O(n) */ 1905b81b6b3SRodney W. Grimes } 1915b81b6b3SRodney W. Grimes 192338c585eSPoul-Henning Kamp /* 193338c585eSPoul-Henning Kamp * Play tone of proper duration for current rhythm signature 194338c585eSPoul-Henning Kamp */ 195f4de22acSJoerg Wunsch static void 196338c585eSPoul-Henning Kamp playtone(int pitch, int value, int sustain) 1975b81b6b3SRodney W. Grimes { 1983e85b721SEd Maste int sound, silence, snum = 1, sdenom = 1; 1995b81b6b3SRodney W. Grimes 2005b81b6b3SRodney W. Grimes /* this weirdness avoids floating-point arithmetic */ 201338c585eSPoul-Henning Kamp for (; sustain; sustain--) { 202e614aa8aSAndrey A. Chernov /* See the BUGS section in the man page for discussion */ 2035b81b6b3SRodney W. Grimes snum *= NUM_MULT; 2045b81b6b3SRodney W. Grimes sdenom *= DENOM_MULT; 2055b81b6b3SRodney W. Grimes } 2065b81b6b3SRodney W. Grimes 2079714de6aSDavid Greenman if (value == 0 || sdenom == 0) 2089714de6aSDavid Greenman return; 2099714de6aSDavid Greenman 2105b81b6b3SRodney W. Grimes if (pitch == -1) 211424e2d04SAndrey A. Chernov rest(whole * snum / (value * sdenom)); 212338c585eSPoul-Henning Kamp else { 2135b81b6b3SRodney W. Grimes sound = (whole * snum) / (value * sdenom) 2145b81b6b3SRodney W. Grimes - (whole * (FILLTIME - fill)) / (value * FILLTIME); 2155b81b6b3SRodney W. Grimes silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 2165b81b6b3SRodney W. Grimes 2175b81b6b3SRodney W. Grimes #ifdef DEBUG 218e614aa8aSAndrey A. Chernov (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 2195b81b6b3SRodney W. Grimes pitch, sound, silence); 2205b81b6b3SRodney W. Grimes #endif /* DEBUG */ 2215b81b6b3SRodney W. Grimes 222424e2d04SAndrey A. Chernov tone(pitchtab[pitch], sound); 2235b81b6b3SRodney W. Grimes if (fill != LEGATO) 224424e2d04SAndrey A. Chernov rest(silence); 2255b81b6b3SRodney W. Grimes } 2265b81b6b3SRodney W. Grimes } 2275b81b6b3SRodney W. Grimes 228338c585eSPoul-Henning Kamp /* 229338c585eSPoul-Henning Kamp * Interpret and play an item from a notation string 230338c585eSPoul-Henning Kamp */ 231f4de22acSJoerg Wunsch static void 232338c585eSPoul-Henning Kamp playstring(char *cp, size_t slen) 2335b81b6b3SRodney W. Grimes { 234e614aa8aSAndrey A. Chernov int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 2355b81b6b3SRodney W. Grimes 2365b81b6b3SRodney W. Grimes #define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 2375b81b6b3SRodney W. Grimes {v = v * 10 + (*++cp - '0'); slen--;} 238338c585eSPoul-Henning Kamp for (; slen--; cp++) { 2395b81b6b3SRodney W. Grimes int sustain, timeval, tempo; 2403e85b721SEd Maste char c = toupper(*cp); 2415b81b6b3SRodney W. Grimes 2425b81b6b3SRodney W. Grimes #ifdef DEBUG 243e614aa8aSAndrey A. Chernov (void) printf("playstring: %c (%x)\n", c, c); 2445b81b6b3SRodney W. Grimes #endif /* DEBUG */ 2455b81b6b3SRodney W. Grimes 246338c585eSPoul-Henning Kamp switch (c) { 247338c585eSPoul-Henning Kamp case 'A': 248338c585eSPoul-Henning Kamp case 'B': 249338c585eSPoul-Henning Kamp case 'C': 250338c585eSPoul-Henning Kamp case 'D': 251338c585eSPoul-Henning Kamp case 'E': 252338c585eSPoul-Henning Kamp case 'F': 253338c585eSPoul-Henning Kamp case 'G': 2545b81b6b3SRodney W. Grimes /* compute pitch */ 2555b81b6b3SRodney W. Grimes pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 2565b81b6b3SRodney W. Grimes 2575b81b6b3SRodney W. Grimes /* this may be followed by an accidental sign */ 258338c585eSPoul-Henning Kamp if (cp[1] == '#' || cp[1] == '+') { 2595b81b6b3SRodney W. Grimes ++pitch; 2605b81b6b3SRodney W. Grimes ++cp; 2615b81b6b3SRodney W. Grimes slen--; 262338c585eSPoul-Henning Kamp } else if (cp[1] == '-') { 2635b81b6b3SRodney W. Grimes --pitch; 2645b81b6b3SRodney W. Grimes ++cp; 2655b81b6b3SRodney W. Grimes slen--; 2665b81b6b3SRodney W. Grimes } 2675b81b6b3SRodney W. Grimes 2685b81b6b3SRodney W. Grimes /* 2695b81b6b3SRodney W. Grimes * If octave-tracking mode is on, and there has been no octave- 2705b81b6b3SRodney W. Grimes * setting prefix, find the version of the current letter note 2715b81b6b3SRodney W. Grimes * closest to the last regardless of octave. 2725b81b6b3SRodney W. Grimes */ 273338c585eSPoul-Henning Kamp if (octtrack && !octprefix) { 274338c585eSPoul-Henning Kamp if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES - 275338c585eSPoul-Henning Kamp lastpitch)) { 2765b81b6b3SRodney W. Grimes ++octave; 2775b81b6b3SRodney W. Grimes pitch += OCTAVE_NOTES; 2785b81b6b3SRodney W. Grimes } 2795b81b6b3SRodney W. Grimes 280338c585eSPoul-Henning Kamp if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES) - 281338c585eSPoul-Henning Kamp lastpitch)) { 2825b81b6b3SRodney W. Grimes --octave; 2835b81b6b3SRodney W. Grimes pitch -= OCTAVE_NOTES; 2845b81b6b3SRodney W. Grimes } 2855b81b6b3SRodney W. Grimes } 2865b81b6b3SRodney W. Grimes octprefix = FALSE; 2875b81b6b3SRodney W. Grimes lastpitch = pitch; 2885b81b6b3SRodney W. Grimes 2895b81b6b3SRodney W. Grimes /* ...which may in turn be followed by an override time value */ 2905b81b6b3SRodney W. Grimes GETNUM(cp, timeval); 2915b81b6b3SRodney W. Grimes if (timeval <= 0 || timeval > MIN_VALUE) 2925b81b6b3SRodney W. Grimes timeval = value; 2935b81b6b3SRodney W. Grimes 2945b81b6b3SRodney W. Grimes /* ...and/or sustain dots */ 295338c585eSPoul-Henning Kamp for (sustain = 0; cp[1] == '.'; cp++) { 2965b81b6b3SRodney W. Grimes slen--; 2975b81b6b3SRodney W. Grimes sustain++; 2985b81b6b3SRodney W. Grimes } 2995b81b6b3SRodney W. Grimes 300e614aa8aSAndrey A. Chernov /* ...and/or a slur mark */ 301e614aa8aSAndrey A. Chernov oldfill = fill; 302338c585eSPoul-Henning Kamp if (cp[1] == '_') { 303e614aa8aSAndrey A. Chernov fill = LEGATO; 304e614aa8aSAndrey A. Chernov ++cp; 305e614aa8aSAndrey A. Chernov slen--; 306e614aa8aSAndrey A. Chernov } 307e614aa8aSAndrey A. Chernov 3085b81b6b3SRodney W. Grimes /* time to emit the actual tone */ 309424e2d04SAndrey A. Chernov playtone(pitch, timeval, sustain); 310e614aa8aSAndrey A. Chernov 311e614aa8aSAndrey A. Chernov fill = oldfill; 3125b81b6b3SRodney W. Grimes break; 3135b81b6b3SRodney W. Grimes case 'O': 314338c585eSPoul-Henning Kamp if (cp[1] == 'N' || cp[1] == 'n') { 3155b81b6b3SRodney W. Grimes octprefix = octtrack = FALSE; 3165b81b6b3SRodney W. Grimes ++cp; 3175b81b6b3SRodney W. Grimes slen--; 318338c585eSPoul-Henning Kamp } else if (cp[1] == 'L' || cp[1] == 'l') { 3195b81b6b3SRodney W. Grimes octtrack = TRUE; 3205b81b6b3SRodney W. Grimes ++cp; 3215b81b6b3SRodney W. Grimes slen--; 322338c585eSPoul-Henning Kamp } else { 3235b81b6b3SRodney W. Grimes GETNUM(cp, octave); 32473a1170aSPedro F. Giffuni if (octave >= nitems(pitchtab) / OCTAVE_NOTES) 3255b81b6b3SRodney W. Grimes octave = DFLT_OCTAVE; 3265b81b6b3SRodney W. Grimes octprefix = TRUE; 3275b81b6b3SRodney W. Grimes } 3285b81b6b3SRodney W. Grimes break; 3295b81b6b3SRodney W. Grimes case '>': 33073a1170aSPedro F. Giffuni if (octave < nitems(pitchtab) / OCTAVE_NOTES - 1) 3315b81b6b3SRodney W. Grimes octave++; 3325b81b6b3SRodney W. Grimes octprefix = TRUE; 3335b81b6b3SRodney W. Grimes break; 3345b81b6b3SRodney W. Grimes case '<': 3355b81b6b3SRodney W. Grimes if (octave > 0) 3365b81b6b3SRodney W. Grimes octave--; 3375b81b6b3SRodney W. Grimes octprefix = TRUE; 3385b81b6b3SRodney W. Grimes break; 3395b81b6b3SRodney W. Grimes case 'N': 3405b81b6b3SRodney W. Grimes GETNUM(cp, pitch); 341338c585eSPoul-Henning Kamp for (sustain = 0; cp[1] == '.'; cp++) { 3425b81b6b3SRodney W. Grimes slen--; 3435b81b6b3SRodney W. Grimes sustain++; 3445b81b6b3SRodney W. Grimes } 345e614aa8aSAndrey A. Chernov oldfill = fill; 346338c585eSPoul-Henning Kamp if (cp[1] == '_') { 347e614aa8aSAndrey A. Chernov fill = LEGATO; 348e614aa8aSAndrey A. Chernov ++cp; 349e614aa8aSAndrey A. Chernov slen--; 350e614aa8aSAndrey A. Chernov } 351424e2d04SAndrey A. Chernov playtone(pitch - 1, value, sustain); 352e614aa8aSAndrey A. Chernov fill = oldfill; 3535b81b6b3SRodney W. Grimes break; 3545b81b6b3SRodney W. Grimes case 'L': 3555b81b6b3SRodney W. Grimes GETNUM(cp, value); 3565b81b6b3SRodney W. Grimes if (value <= 0 || value > MIN_VALUE) 3575b81b6b3SRodney W. Grimes value = DFLT_VALUE; 3585b81b6b3SRodney W. Grimes break; 3595b81b6b3SRodney W. Grimes case 'P': 3605b81b6b3SRodney W. Grimes case '~': 3615b81b6b3SRodney W. Grimes /* this may be followed by an override time value */ 3625b81b6b3SRodney W. Grimes GETNUM(cp, timeval); 3635b81b6b3SRodney W. Grimes if (timeval <= 0 || timeval > MIN_VALUE) 3645b81b6b3SRodney W. Grimes timeval = value; 365338c585eSPoul-Henning Kamp for (sustain = 0; cp[1] == '.'; cp++) { 3665b81b6b3SRodney W. Grimes slen--; 3675b81b6b3SRodney W. Grimes sustain++; 3685b81b6b3SRodney W. Grimes } 369424e2d04SAndrey A. Chernov playtone(-1, timeval, sustain); 3705b81b6b3SRodney W. Grimes break; 3715b81b6b3SRodney W. Grimes case 'T': 3725b81b6b3SRodney W. Grimes GETNUM(cp, tempo); 3735b81b6b3SRodney W. Grimes if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 3745b81b6b3SRodney W. Grimes tempo = DFLT_TEMPO; 3757344a290SBrian Somers whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / tempo; 3765b81b6b3SRodney W. Grimes break; 3775b81b6b3SRodney W. Grimes case 'M': 378338c585eSPoul-Henning Kamp if (cp[1] == 'N' || cp[1] == 'n') { 3795b81b6b3SRodney W. Grimes fill = NORMAL; 3805b81b6b3SRodney W. Grimes ++cp; 3815b81b6b3SRodney W. Grimes slen--; 382338c585eSPoul-Henning Kamp } else if (cp[1] == 'L' || cp[1] == 'l') { 3835b81b6b3SRodney W. Grimes fill = LEGATO; 3845b81b6b3SRodney W. Grimes ++cp; 3855b81b6b3SRodney W. Grimes slen--; 386338c585eSPoul-Henning Kamp } else if (cp[1] == 'S' || cp[1] == 's') { 3875b81b6b3SRodney W. Grimes fill = STACCATO; 3885b81b6b3SRodney W. Grimes ++cp; 3895b81b6b3SRodney W. Grimes slen--; 3905b81b6b3SRodney W. Grimes } 3915b81b6b3SRodney W. Grimes break; 3925b81b6b3SRodney W. Grimes } 3935b81b6b3SRodney W. Grimes } 3945b81b6b3SRodney W. Grimes } 3955b81b6b3SRodney W. Grimes 396338c585eSPoul-Henning Kamp /* 397338c585eSPoul-Henning Kamp * ****************** UNIX DRIVER HOOKS BEGIN HERE ************************** 3985b81b6b3SRodney W. Grimes * This section implements driver hooks to run playstring() and the tone(), 3995b81b6b3SRodney W. Grimes * endtone(), and rest() functions defined above. 4005b81b6b3SRodney W. Grimes */ 4015b81b6b3SRodney W. Grimes 402e614aa8aSAndrey A. Chernov static int spkr_active = FALSE; /* exclusion flag */ 403e4961fbfSPoul-Henning Kamp static char *spkr_inbuf; /* incoming buf */ 4045b81b6b3SRodney W. Grimes 4053412120fSPoul-Henning Kamp static int 406*aa0b4694SMateusz Guzik spkropen(struct cdev *dev, int flags, int fmt, struct thread *td) 4075b81b6b3SRodney W. Grimes { 4085b81b6b3SRodney W. Grimes #ifdef DEBUG 409b8e49f68SBill Fumerola (void) printf("spkropen: entering with dev = %s\n", devtoname(dev)); 4105b81b6b3SRodney W. Grimes #endif /* DEBUG */ 4115b81b6b3SRodney W. Grimes 4128d456252SEd Schouten if (spkr_active) 4135b81b6b3SRodney W. Grimes return(EBUSY); 414338c585eSPoul-Henning Kamp else { 415e614aa8aSAndrey A. Chernov #ifdef DEBUG 416e614aa8aSAndrey A. Chernov (void) printf("spkropen: about to perform play initialization\n"); 417e614aa8aSAndrey A. Chernov #endif /* DEBUG */ 4185b81b6b3SRodney W. Grimes playinit(); 419a163d034SWarner Losh spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK); 420e614aa8aSAndrey A. Chernov spkr_active = TRUE; 4215b81b6b3SRodney W. Grimes return(0); 4225b81b6b3SRodney W. Grimes } 423e614aa8aSAndrey A. Chernov } 4245b81b6b3SRodney W. Grimes 4253412120fSPoul-Henning Kamp static int 426*aa0b4694SMateusz Guzik spkrwrite(struct cdev *dev, struct uio *uio, int ioflag) 4275b81b6b3SRodney W. Grimes { 4285b81b6b3SRodney W. Grimes #ifdef DEBUG 4299f80ce04SKonstantin Belousov printf("spkrwrite: entering with dev = %s, count = %zd\n", 430b8e49f68SBill Fumerola devtoname(dev), uio->uio_resid); 4315b81b6b3SRodney W. Grimes #endif /* DEBUG */ 4328d456252SEd Schouten 4338d456252SEd Schouten if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */ 434e614aa8aSAndrey A. Chernov return(E2BIG); 435338c585eSPoul-Henning Kamp else { 436e614aa8aSAndrey A. Chernov unsigned n; 437e614aa8aSAndrey A. Chernov char *cp; 438e614aa8aSAndrey A. Chernov int error; 439e614aa8aSAndrey A. Chernov 440e614aa8aSAndrey A. Chernov n = uio->uio_resid; 441e4961fbfSPoul-Henning Kamp cp = spkr_inbuf; 4423bd9f6dbSPeter Wemm error = uiomove(cp, n, uio); 4433bd9f6dbSPeter Wemm if (!error) { 4443bd9f6dbSPeter Wemm cp[n] = '\0'; 445424e2d04SAndrey A. Chernov playstring(cp, n); 4463bd9f6dbSPeter Wemm } 4475b81b6b3SRodney W. Grimes return(error); 4485b81b6b3SRodney W. Grimes } 4495b81b6b3SRodney W. Grimes } 4505b81b6b3SRodney W. Grimes 4513412120fSPoul-Henning Kamp static int 452*aa0b4694SMateusz Guzik spkrclose(struct cdev *dev, int flags, int fmt, struct thread *td) 4535b81b6b3SRodney W. Grimes { 4545b81b6b3SRodney W. Grimes #ifdef DEBUG 455b8e49f68SBill Fumerola (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev)); 4565b81b6b3SRodney W. Grimes #endif /* DEBUG */ 4575b81b6b3SRodney W. Grimes 458521f364bSDag-Erling Smørgrav wakeup(&endtone); 459521f364bSDag-Erling Smørgrav wakeup(&endrest); 460e4961fbfSPoul-Henning Kamp free(spkr_inbuf, M_SPKR); 461e614aa8aSAndrey A. Chernov spkr_active = FALSE; 4625b81b6b3SRodney W. Grimes return(0); 4635b81b6b3SRodney W. Grimes } 4645b81b6b3SRodney W. Grimes 4653412120fSPoul-Henning Kamp static int 466*aa0b4694SMateusz Guzik spkrioctl(struct cdev *dev, unsigned long cmd, caddr_t cmdarg, int flags, 467*aa0b4694SMateusz Guzik struct thread *td) 4685b81b6b3SRodney W. Grimes { 4695b81b6b3SRodney W. Grimes #ifdef DEBUG 470d9183205SBruce Evans (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n", 471d9183205SBruce Evans devtoname(dev), cmd); 4725b81b6b3SRodney W. Grimes #endif /* DEBUG */ 4735b81b6b3SRodney W. Grimes 4748d456252SEd Schouten if (cmd == SPKRTONE) { 4755b81b6b3SRodney W. Grimes tone_t *tp = (tone_t *)cmdarg; 4765b81b6b3SRodney W. Grimes 4775b81b6b3SRodney W. Grimes if (tp->frequency == 0) 478424e2d04SAndrey A. Chernov rest(tp->duration); 4795b81b6b3SRodney W. Grimes else 480424e2d04SAndrey A. Chernov tone(tp->frequency, tp->duration); 481424e2d04SAndrey A. Chernov return 0; 482338c585eSPoul-Henning Kamp } else if (cmd == SPKRTUNE) { 4835b81b6b3SRodney W. Grimes tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 4845b81b6b3SRodney W. Grimes tone_t ttp; 4855b81b6b3SRodney W. Grimes int error; 4865b81b6b3SRodney W. Grimes 4875b81b6b3SRodney W. Grimes for (; ; tp++) { 4885b81b6b3SRodney W. Grimes error = copyin(tp, &ttp, sizeof(tone_t)); 4895b81b6b3SRodney W. Grimes if (error) 4905b81b6b3SRodney W. Grimes return(error); 491338c585eSPoul-Henning Kamp 4925b81b6b3SRodney W. Grimes if (ttp.duration == 0) 4935b81b6b3SRodney W. Grimes break; 494338c585eSPoul-Henning Kamp 4955b81b6b3SRodney W. Grimes if (ttp.frequency == 0) 496424e2d04SAndrey A. Chernov rest(ttp.duration); 4975b81b6b3SRodney W. Grimes else 498424e2d04SAndrey A. Chernov tone(ttp.frequency, ttp.duration); 4995b81b6b3SRodney W. Grimes } 5005b81b6b3SRodney W. Grimes return(0); 5015b81b6b3SRodney W. Grimes } 502e614aa8aSAndrey A. Chernov return(EINVAL); 503e614aa8aSAndrey A. Chernov } 5045b81b6b3SRodney W. Grimes 50589c9c53dSPoul-Henning Kamp static struct cdev *speaker_dev; 506ce6d929bSWes Peters 50793f5134aSPoul-Henning Kamp /* 50893f5134aSPoul-Henning Kamp * Module handling 50993f5134aSPoul-Henning Kamp */ 5102a50a6d7SMike Smith static int 51193f5134aSPoul-Henning Kamp speaker_modevent(module_t mod, int type, void *data) 5122a50a6d7SMike Smith { 51393f5134aSPoul-Henning Kamp int error = 0; 514c7f718ecSMatthew N. Dodd 51593f5134aSPoul-Henning Kamp switch(type) { 51693f5134aSPoul-Henning Kamp case MOD_LOAD: 51793f5134aSPoul-Henning Kamp speaker_dev = make_dev(&spkr_cdevsw, 0, 51893f5134aSPoul-Henning Kamp UID_ROOT, GID_WHEEL, 0600, "speaker"); 51993f5134aSPoul-Henning Kamp break; 52093f5134aSPoul-Henning Kamp case MOD_SHUTDOWN: 52193f5134aSPoul-Henning Kamp case MOD_UNLOAD: 52230201b6cSMatthew N. Dodd destroy_dev(speaker_dev); 52393f5134aSPoul-Henning Kamp break; 52493f5134aSPoul-Henning Kamp default: 52593f5134aSPoul-Henning Kamp error = EOPNOTSUPP; 52693f5134aSPoul-Henning Kamp } 52793f5134aSPoul-Henning Kamp return (error); 5282a50a6d7SMike Smith } 5292a50a6d7SMike Smith 53093f5134aSPoul-Henning Kamp DEV_MODULE(speaker, speaker_modevent, NULL); 531