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> 142a50a6d7SMike Smith #include <sys/bus.h> 15f540b106SGarrett Wollman #include <sys/kernel.h> 162a50a6d7SMike Smith #include <sys/module.h> 17f540b106SGarrett Wollman #include <sys/uio.h> 1887f6c662SJulian Elischer #include <sys/conf.h> 195b664c7cSPoul-Henning Kamp #include <sys/ctype.h> 20e4961fbfSPoul-Henning Kamp #include <sys/malloc.h> 212a50a6d7SMike Smith #include <isa/isavar.h> 22b5e8ce9fSBruce Evans #include <machine/clock.h> 2324072ca3SYoshihiro Takahashi #include <machine/ppireg.h> 2424072ca3SYoshihiro Takahashi #include <machine/timerreg.h> 256d8200ffSRuslan Ermilov #include <dev/speaker/speaker.h> 265b81b6b3SRodney W. Grimes 2787f6c662SJulian Elischer static d_open_t spkropen; 2887f6c662SJulian Elischer static d_close_t spkrclose; 2987f6c662SJulian Elischer static d_write_t spkrwrite; 3087f6c662SJulian Elischer static d_ioctl_t spkrioctl; 3187f6c662SJulian Elischer 324e2f199eSPoul-Henning Kamp static struct cdevsw spkr_cdevsw = { 33dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 34dc08ffecSPoul-Henning Kamp .d_flags = D_NEEDGIANT, 357ac40f5fSPoul-Henning Kamp .d_open = spkropen, 367ac40f5fSPoul-Henning Kamp .d_close = spkrclose, 377ac40f5fSPoul-Henning Kamp .d_write = spkrwrite, 387ac40f5fSPoul-Henning Kamp .d_ioctl = spkrioctl, 397ac40f5fSPoul-Henning Kamp .d_name = "spkr", 404e2f199eSPoul-Henning Kamp }; 418af5d536SJulian Elischer 42959b7375SPoul-Henning Kamp static MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer"); 43e4961fbfSPoul-Henning Kamp 445b81b6b3SRodney W. Grimes /**************** MACHINE DEPENDENT PART STARTS HERE ************************* 455b81b6b3SRodney W. Grimes * 465b81b6b3SRodney W. Grimes * This section defines a function tone() which causes a tone of given 479275c7ccSBruce Evans * frequency and duration from the ISA console speaker. 485b81b6b3SRodney W. Grimes * Another function endtone() is defined to force sound off, and there is 495b81b6b3SRodney W. Grimes * also a rest() entry point to do pauses. 505b81b6b3SRodney W. Grimes * 515b81b6b3SRodney W. Grimes * Audible sound is generated using the Programmable Interval Timer (PIT) and 529275c7ccSBruce Evans * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The 535b81b6b3SRodney W. Grimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is 545b81b6b3SRodney W. Grimes * used to generate clicks (a square wave) of whatever frequency is desired. 555b81b6b3SRodney W. Grimes */ 565b81b6b3SRodney W. Grimes 5730201b6cSMatthew N. Dodd #ifdef PC98 58257427efSMatthew N. Dodd #define SPKR_DESC "PC98 speaker" 5930201b6cSMatthew N. Dodd #else 60257427efSMatthew N. Dodd #define SPKR_DESC "PC speaker" 6130201b6cSMatthew N. Dodd #endif 6230201b6cSMatthew N. Dodd 631e064bd4SAndrey A. Chernov #define SPKRPRI PSOCK 641e064bd4SAndrey A. Chernov static char endtone, endrest; 655b81b6b3SRodney W. Grimes 66b2424ac0SBrian Somers static void tone(unsigned int thz, unsigned int centisecs); 67b2424ac0SBrian Somers static void rest(int centisecs); 6889c9a483SAlfred Perlstein static void playinit(void); 6989c9a483SAlfred Perlstein static void playtone(int pitch, int value, int sustain); 7089c9a483SAlfred Perlstein static void playstring(char *cp, size_t slen); 710dfe10a6SBruce Evans 72b2424ac0SBrian Somers /* emit tone of frequency thz for given number of centisecs */ 73f4de22acSJoerg Wunsch static void 74b2424ac0SBrian Somers tone(thz, centisecs) 75b2424ac0SBrian Somers unsigned int thz, centisecs; 765b81b6b3SRodney W. Grimes { 779714de6aSDavid Greenman unsigned int divisor; 78b2424ac0SBrian Somers int sps, timo; 795b81b6b3SRodney W. Grimes 809714de6aSDavid Greenman if (thz <= 0) 819714de6aSDavid Greenman return; 829714de6aSDavid Greenman 839275c7ccSBruce Evans divisor = timer_freq / thz; 849714de6aSDavid Greenman 855b81b6b3SRodney W. Grimes #ifdef DEBUG 86b2424ac0SBrian Somers (void) printf("tone: thz=%d centisecs=%d\n", thz, centisecs); 875b81b6b3SRodney W. Grimes #endif /* DEBUG */ 885b81b6b3SRodney W. Grimes 895b81b6b3SRodney W. Grimes /* set timer to generate clicks at given frequency in Hertz */ 90f4de22acSJoerg Wunsch sps = splclock(); 91bc4ff6e3SSøren Schmidt 9224072ca3SYoshihiro Takahashi if (timer_spkr_acquire()) { 93bc4ff6e3SSøren Schmidt /* enter list of waiting procs ??? */ 94f4de22acSJoerg Wunsch splx(sps); 95bc4ff6e3SSøren Schmidt return; 96bc4ff6e3SSøren Schmidt } 97f4de22acSJoerg Wunsch splx(sps); 98f4de22acSJoerg Wunsch disable_intr(); 9924072ca3SYoshihiro Takahashi spkr_set_pitch(divisor); 100f4de22acSJoerg Wunsch enable_intr(); 1015b81b6b3SRodney W. Grimes 1025b81b6b3SRodney W. Grimes /* turn the speaker on */ 10324072ca3SYoshihiro Takahashi ppi_spkr_on(); 1045b81b6b3SRodney W. Grimes 1055b81b6b3SRodney W. Grimes /* 1065b81b6b3SRodney W. Grimes * Set timeout to endtone function, then give up the timeslice. 1075b81b6b3SRodney W. Grimes * This is so other processes can execute while the tone is being 1085b81b6b3SRodney W. Grimes * emitted. 1095b81b6b3SRodney W. Grimes */ 110b2424ac0SBrian Somers timo = centisecs * hz / 100; 111b2424ac0SBrian Somers if (timo > 0) 112b2424ac0SBrian Somers tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", timo); 11324072ca3SYoshihiro Takahashi ppi_spkr_off(); 114f4de22acSJoerg Wunsch sps = splclock(); 11524072ca3SYoshihiro Takahashi timer_spkr_release(); 116f4de22acSJoerg Wunsch splx(sps); 1175b81b6b3SRodney W. Grimes } 1185b81b6b3SRodney W. Grimes 119b2424ac0SBrian Somers /* rest for given number of centisecs */ 120f4de22acSJoerg Wunsch static void 121b2424ac0SBrian Somers rest(centisecs) 122b2424ac0SBrian Somers int centisecs; 1235b81b6b3SRodney W. Grimes { 124b2424ac0SBrian Somers int timo; 125b2424ac0SBrian Somers 1265b81b6b3SRodney W. Grimes /* 1275b81b6b3SRodney W. Grimes * Set timeout to endrest function, then give up the timeslice. 1285b81b6b3SRodney W. Grimes * This is so other processes can execute while the rest is being 1295b81b6b3SRodney W. Grimes * waited out. 1305b81b6b3SRodney W. Grimes */ 1315b81b6b3SRodney W. Grimes #ifdef DEBUG 132b2424ac0SBrian Somers (void) printf("rest: %d\n", centisecs); 1335b81b6b3SRodney W. Grimes #endif /* DEBUG */ 134b2424ac0SBrian Somers timo = centisecs * hz / 100; 135b2424ac0SBrian Somers if (timo > 0) 136b2424ac0SBrian Somers tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", timo); 1375b81b6b3SRodney W. Grimes } 1385b81b6b3SRodney W. Grimes 1395b81b6b3SRodney W. Grimes /**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 1405b81b6b3SRodney W. Grimes * 1415b81b6b3SRodney W. Grimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 142e614aa8aSAndrey A. Chernov * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 143e614aa8aSAndrey A. Chernov * tracking facility are added. 1445b81b6b3SRodney W. Grimes * Requires tone(), rest(), and endtone(). String play is not interruptible 1455b81b6b3SRodney W. Grimes * except possibly at physical block boundaries. 1465b81b6b3SRodney W. Grimes */ 1475b81b6b3SRodney W. Grimes 1485b81b6b3SRodney W. Grimes typedef int bool; 1495b81b6b3SRodney W. Grimes #define TRUE 1 1505b81b6b3SRodney W. Grimes #define FALSE 0 1515b81b6b3SRodney W. Grimes 1525b81b6b3SRodney W. Grimes #define dtoi(c) ((c) - '0') 1535b81b6b3SRodney W. Grimes 1545b81b6b3SRodney W. Grimes static int octave; /* currently selected octave */ 1555b81b6b3SRodney W. Grimes static int whole; /* whole-note time at current tempo, in ticks */ 1565b81b6b3SRodney W. Grimes static int value; /* whole divisor for note time, quarter note = 1 */ 1575b81b6b3SRodney W. Grimes static int fill; /* controls spacing of notes */ 1585b81b6b3SRodney W. Grimes static bool octtrack; /* octave-tracking on? */ 1595b81b6b3SRodney W. Grimes static bool octprefix; /* override current octave-tracking state? */ 1605b81b6b3SRodney W. Grimes 1615b81b6b3SRodney W. Grimes /* 1625b81b6b3SRodney W. Grimes * Magic number avoidance... 1635b81b6b3SRodney W. Grimes */ 1645b81b6b3SRodney W. Grimes #define SECS_PER_MIN 60 /* seconds per minute */ 1655b81b6b3SRodney W. Grimes #define WHOLE_NOTE 4 /* quarter notes per whole note */ 1665b81b6b3SRodney W. Grimes #define MIN_VALUE 64 /* the most we can divide a note by */ 1675b81b6b3SRodney W. Grimes #define DFLT_VALUE 4 /* default value (quarter-note) */ 1685b81b6b3SRodney W. Grimes #define FILLTIME 8 /* for articulation, break note in parts */ 1695b81b6b3SRodney W. Grimes #define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 1705b81b6b3SRodney W. Grimes #define NORMAL 7 /* 7/8ths of note interval is filled */ 1715b81b6b3SRodney W. Grimes #define LEGATO 8 /* all of note interval is filled */ 1725b81b6b3SRodney W. Grimes #define DFLT_OCTAVE 4 /* default octave */ 1735b81b6b3SRodney W. Grimes #define MIN_TEMPO 32 /* minimum tempo */ 1745b81b6b3SRodney W. Grimes #define DFLT_TEMPO 120 /* default tempo */ 1755b81b6b3SRodney W. Grimes #define MAX_TEMPO 255 /* max tempo */ 1765b81b6b3SRodney W. Grimes #define NUM_MULT 3 /* numerator of dot multiplier */ 1775b81b6b3SRodney W. Grimes #define DENOM_MULT 2 /* denominator of dot multiplier */ 1785b81b6b3SRodney W. Grimes 1795b81b6b3SRodney W. Grimes /* letter to half-tone: A B C D E F G */ 1805b81b6b3SRodney W. Grimes static int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 1815b81b6b3SRodney W. Grimes 1825b81b6b3SRodney W. Grimes /* 1835b81b6b3SRodney W. Grimes * This is the American Standard A440 Equal-Tempered scale with frequencies 1845b81b6b3SRodney W. Grimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 1855b81b6b3SRodney W. Grimes * our octave 0 is standard octave 2. 1865b81b6b3SRodney W. Grimes */ 1875b81b6b3SRodney W. Grimes #define OCTAVE_NOTES 12 /* semitones per octave */ 1885b81b6b3SRodney W. Grimes static int pitchtab[] = 1895b81b6b3SRodney W. Grimes { 1905b81b6b3SRodney W. Grimes /* C C# D D# E F F# G G# A A# B*/ 1915b81b6b3SRodney W. Grimes /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 1925b81b6b3SRodney W. Grimes /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 1935b81b6b3SRodney W. Grimes /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 1945b81b6b3SRodney W. Grimes /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1955b81b6b3SRodney W. Grimes /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 1965b81b6b3SRodney W. Grimes /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 1975b81b6b3SRodney W. Grimes /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 1985b81b6b3SRodney W. Grimes }; 1995b81b6b3SRodney W. Grimes 200f4de22acSJoerg Wunsch static void 201f4de22acSJoerg Wunsch playinit() 2025b81b6b3SRodney W. Grimes { 2035b81b6b3SRodney W. Grimes octave = DFLT_OCTAVE; 2041e064bd4SAndrey A. Chernov whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 2055b81b6b3SRodney W. Grimes fill = NORMAL; 2065b81b6b3SRodney W. Grimes value = DFLT_VALUE; 2075b81b6b3SRodney W. Grimes octtrack = FALSE; 2085b81b6b3SRodney W. Grimes octprefix = TRUE; /* act as though there was an initial O(n) */ 2095b81b6b3SRodney W. Grimes } 2105b81b6b3SRodney W. Grimes 2115b81b6b3SRodney W. Grimes /* play tone of proper duration for current rhythm signature */ 212f4de22acSJoerg Wunsch static void 213f4de22acSJoerg Wunsch playtone(pitch, value, sustain) 2145b81b6b3SRodney W. Grimes int pitch, value, sustain; 2155b81b6b3SRodney W. Grimes { 2165b81b6b3SRodney W. Grimes register int sound, silence, snum = 1, sdenom = 1; 2175b81b6b3SRodney W. Grimes 2185b81b6b3SRodney W. Grimes /* this weirdness avoids floating-point arithmetic */ 2195b81b6b3SRodney W. Grimes for (; sustain; sustain--) 2205b81b6b3SRodney W. Grimes { 221e614aa8aSAndrey A. Chernov /* See the BUGS section in the man page for discussion */ 2225b81b6b3SRodney W. Grimes snum *= NUM_MULT; 2235b81b6b3SRodney W. Grimes sdenom *= DENOM_MULT; 2245b81b6b3SRodney W. Grimes } 2255b81b6b3SRodney W. Grimes 2269714de6aSDavid Greenman if (value == 0 || sdenom == 0) 2279714de6aSDavid Greenman return; 2289714de6aSDavid Greenman 2295b81b6b3SRodney W. Grimes if (pitch == -1) 230424e2d04SAndrey A. Chernov rest(whole * snum / (value * sdenom)); 2315b81b6b3SRodney W. Grimes else 2325b81b6b3SRodney W. Grimes { 2335b81b6b3SRodney W. Grimes sound = (whole * snum) / (value * sdenom) 2345b81b6b3SRodney W. Grimes - (whole * (FILLTIME - fill)) / (value * FILLTIME); 2355b81b6b3SRodney W. Grimes silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 2365b81b6b3SRodney W. Grimes 2375b81b6b3SRodney W. Grimes #ifdef DEBUG 238e614aa8aSAndrey A. Chernov (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 2395b81b6b3SRodney W. Grimes pitch, sound, silence); 2405b81b6b3SRodney W. Grimes #endif /* DEBUG */ 2415b81b6b3SRodney W. Grimes 242424e2d04SAndrey A. Chernov tone(pitchtab[pitch], sound); 2435b81b6b3SRodney W. Grimes if (fill != LEGATO) 244424e2d04SAndrey A. Chernov rest(silence); 2455b81b6b3SRodney W. Grimes } 2465b81b6b3SRodney W. Grimes } 2475b81b6b3SRodney W. Grimes 2485b81b6b3SRodney W. Grimes /* interpret and play an item from a notation string */ 249f4de22acSJoerg Wunsch static void 250f4de22acSJoerg Wunsch playstring(cp, slen) 2515b81b6b3SRodney W. Grimes char *cp; 2525b81b6b3SRodney W. Grimes size_t slen; 2535b81b6b3SRodney W. Grimes { 254e614aa8aSAndrey A. Chernov int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 2555b81b6b3SRodney W. Grimes 2565b81b6b3SRodney W. Grimes #define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 2575b81b6b3SRodney W. Grimes {v = v * 10 + (*++cp - '0'); slen--;} 2585b81b6b3SRodney W. Grimes for (; slen--; cp++) 2595b81b6b3SRodney W. Grimes { 2605b81b6b3SRodney W. Grimes int sustain, timeval, tempo; 2615b81b6b3SRodney W. Grimes register char c = toupper(*cp); 2625b81b6b3SRodney W. Grimes 2635b81b6b3SRodney W. Grimes #ifdef DEBUG 264e614aa8aSAndrey A. Chernov (void) printf("playstring: %c (%x)\n", c, c); 2655b81b6b3SRodney W. Grimes #endif /* DEBUG */ 2665b81b6b3SRodney W. Grimes 2675b81b6b3SRodney W. Grimes switch (c) 2685b81b6b3SRodney W. Grimes { 2695b81b6b3SRodney W. Grimes case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 2705b81b6b3SRodney W. Grimes 2715b81b6b3SRodney W. Grimes /* compute pitch */ 2725b81b6b3SRodney W. Grimes pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 2735b81b6b3SRodney W. Grimes 2745b81b6b3SRodney W. Grimes /* this may be followed by an accidental sign */ 2755b81b6b3SRodney W. Grimes if (cp[1] == '#' || cp[1] == '+') 2765b81b6b3SRodney W. Grimes { 2775b81b6b3SRodney W. Grimes ++pitch; 2785b81b6b3SRodney W. Grimes ++cp; 2795b81b6b3SRodney W. Grimes slen--; 2805b81b6b3SRodney W. Grimes } 2815b81b6b3SRodney W. Grimes else if (cp[1] == '-') 2825b81b6b3SRodney W. Grimes { 2835b81b6b3SRodney W. Grimes --pitch; 2845b81b6b3SRodney W. Grimes ++cp; 2855b81b6b3SRodney W. Grimes slen--; 2865b81b6b3SRodney W. Grimes } 2875b81b6b3SRodney W. Grimes 2885b81b6b3SRodney W. Grimes /* 2895b81b6b3SRodney W. Grimes * If octave-tracking mode is on, and there has been no octave- 2905b81b6b3SRodney W. Grimes * setting prefix, find the version of the current letter note 2915b81b6b3SRodney W. Grimes * closest to the last regardless of octave. 2925b81b6b3SRodney W. Grimes */ 2935b81b6b3SRodney W. Grimes if (octtrack && !octprefix) 2945b81b6b3SRodney W. Grimes { 2955b81b6b3SRodney W. Grimes if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) 2965b81b6b3SRodney W. Grimes { 2975b81b6b3SRodney W. Grimes ++octave; 2985b81b6b3SRodney W. Grimes pitch += OCTAVE_NOTES; 2995b81b6b3SRodney W. Grimes } 3005b81b6b3SRodney W. Grimes 3015b81b6b3SRodney W. Grimes if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) 3025b81b6b3SRodney W. Grimes { 3035b81b6b3SRodney W. Grimes --octave; 3045b81b6b3SRodney W. Grimes pitch -= OCTAVE_NOTES; 3055b81b6b3SRodney W. Grimes } 3065b81b6b3SRodney W. Grimes } 3075b81b6b3SRodney W. Grimes octprefix = FALSE; 3085b81b6b3SRodney W. Grimes lastpitch = pitch; 3095b81b6b3SRodney W. Grimes 3105b81b6b3SRodney W. Grimes /* ...which may in turn be followed by an override time value */ 3115b81b6b3SRodney W. Grimes GETNUM(cp, timeval); 3125b81b6b3SRodney W. Grimes if (timeval <= 0 || timeval > MIN_VALUE) 3135b81b6b3SRodney W. Grimes timeval = value; 3145b81b6b3SRodney W. Grimes 3155b81b6b3SRodney W. Grimes /* ...and/or sustain dots */ 3165b81b6b3SRodney W. Grimes for (sustain = 0; cp[1] == '.'; cp++) 3175b81b6b3SRodney W. Grimes { 3185b81b6b3SRodney W. Grimes slen--; 3195b81b6b3SRodney W. Grimes sustain++; 3205b81b6b3SRodney W. Grimes } 3215b81b6b3SRodney W. Grimes 322e614aa8aSAndrey A. Chernov /* ...and/or a slur mark */ 323e614aa8aSAndrey A. Chernov oldfill = fill; 324e614aa8aSAndrey A. Chernov if (cp[1] == '_') 325e614aa8aSAndrey A. Chernov { 326e614aa8aSAndrey A. Chernov fill = LEGATO; 327e614aa8aSAndrey A. Chernov ++cp; 328e614aa8aSAndrey A. Chernov slen--; 329e614aa8aSAndrey A. Chernov } 330e614aa8aSAndrey A. Chernov 3315b81b6b3SRodney W. Grimes /* time to emit the actual tone */ 332424e2d04SAndrey A. Chernov playtone(pitch, timeval, sustain); 333e614aa8aSAndrey A. Chernov 334e614aa8aSAndrey A. Chernov fill = oldfill; 3355b81b6b3SRodney W. Grimes break; 3365b81b6b3SRodney W. Grimes 3375b81b6b3SRodney W. Grimes case 'O': 3385b81b6b3SRodney W. Grimes if (cp[1] == 'N' || cp[1] == 'n') 3395b81b6b3SRodney W. Grimes { 3405b81b6b3SRodney W. Grimes octprefix = octtrack = FALSE; 3415b81b6b3SRodney W. Grimes ++cp; 3425b81b6b3SRodney W. Grimes slen--; 3435b81b6b3SRodney W. Grimes } 3445b81b6b3SRodney W. Grimes else if (cp[1] == 'L' || cp[1] == 'l') 3455b81b6b3SRodney W. Grimes { 3465b81b6b3SRodney W. Grimes octtrack = TRUE; 3475b81b6b3SRodney W. Grimes ++cp; 3485b81b6b3SRodney W. Grimes slen--; 3495b81b6b3SRodney W. Grimes } 3505b81b6b3SRodney W. Grimes else 3515b81b6b3SRodney W. Grimes { 3525b81b6b3SRodney W. Grimes GETNUM(cp, octave); 3530a97fb59SAndrey A. Chernov if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES) 3545b81b6b3SRodney W. Grimes octave = DFLT_OCTAVE; 3555b81b6b3SRodney W. Grimes octprefix = TRUE; 3565b81b6b3SRodney W. Grimes } 3575b81b6b3SRodney W. Grimes break; 3585b81b6b3SRodney W. Grimes 3595b81b6b3SRodney W. Grimes case '>': 3600a97fb59SAndrey A. Chernov if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1) 3615b81b6b3SRodney W. Grimes octave++; 3625b81b6b3SRodney W. Grimes octprefix = TRUE; 3635b81b6b3SRodney W. Grimes break; 3645b81b6b3SRodney W. Grimes 3655b81b6b3SRodney W. Grimes case '<': 3665b81b6b3SRodney W. Grimes if (octave > 0) 3675b81b6b3SRodney W. Grimes octave--; 3685b81b6b3SRodney W. Grimes octprefix = TRUE; 3695b81b6b3SRodney W. Grimes break; 3705b81b6b3SRodney W. Grimes 3715b81b6b3SRodney W. Grimes case 'N': 3725b81b6b3SRodney W. Grimes GETNUM(cp, pitch); 3735b81b6b3SRodney W. Grimes for (sustain = 0; cp[1] == '.'; cp++) 3745b81b6b3SRodney W. Grimes { 3755b81b6b3SRodney W. Grimes slen--; 3765b81b6b3SRodney W. Grimes sustain++; 3775b81b6b3SRodney W. Grimes } 378e614aa8aSAndrey A. Chernov oldfill = fill; 379e614aa8aSAndrey A. Chernov if (cp[1] == '_') 380e614aa8aSAndrey A. Chernov { 381e614aa8aSAndrey A. Chernov fill = LEGATO; 382e614aa8aSAndrey A. Chernov ++cp; 383e614aa8aSAndrey A. Chernov slen--; 384e614aa8aSAndrey A. Chernov } 385424e2d04SAndrey A. Chernov playtone(pitch - 1, value, sustain); 386e614aa8aSAndrey A. Chernov fill = oldfill; 3875b81b6b3SRodney W. Grimes break; 3885b81b6b3SRodney W. Grimes 3895b81b6b3SRodney W. Grimes case 'L': 3905b81b6b3SRodney W. Grimes GETNUM(cp, value); 3915b81b6b3SRodney W. Grimes if (value <= 0 || value > MIN_VALUE) 3925b81b6b3SRodney W. Grimes value = DFLT_VALUE; 3935b81b6b3SRodney W. Grimes break; 3945b81b6b3SRodney W. Grimes 3955b81b6b3SRodney W. Grimes case 'P': 3965b81b6b3SRodney W. Grimes case '~': 3975b81b6b3SRodney W. Grimes /* this may be followed by an override time value */ 3985b81b6b3SRodney W. Grimes GETNUM(cp, timeval); 3995b81b6b3SRodney W. Grimes if (timeval <= 0 || timeval > MIN_VALUE) 4005b81b6b3SRodney W. Grimes timeval = value; 4015b81b6b3SRodney W. Grimes for (sustain = 0; cp[1] == '.'; cp++) 4025b81b6b3SRodney W. Grimes { 4035b81b6b3SRodney W. Grimes slen--; 4045b81b6b3SRodney W. Grimes sustain++; 4055b81b6b3SRodney W. Grimes } 406424e2d04SAndrey A. Chernov playtone(-1, timeval, sustain); 4075b81b6b3SRodney W. Grimes break; 4085b81b6b3SRodney W. Grimes 4095b81b6b3SRodney W. Grimes case 'T': 4105b81b6b3SRodney W. Grimes GETNUM(cp, tempo); 4115b81b6b3SRodney W. Grimes if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 4125b81b6b3SRodney W. Grimes tempo = DFLT_TEMPO; 4131e064bd4SAndrey A. Chernov whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo; 4145b81b6b3SRodney W. Grimes break; 4155b81b6b3SRodney W. Grimes 4165b81b6b3SRodney W. Grimes case 'M': 4175b81b6b3SRodney W. Grimes if (cp[1] == 'N' || cp[1] == 'n') 4185b81b6b3SRodney W. Grimes { 4195b81b6b3SRodney W. Grimes fill = NORMAL; 4205b81b6b3SRodney W. Grimes ++cp; 4215b81b6b3SRodney W. Grimes slen--; 4225b81b6b3SRodney W. Grimes } 4235b81b6b3SRodney W. Grimes else if (cp[1] == 'L' || cp[1] == 'l') 4245b81b6b3SRodney W. Grimes { 4255b81b6b3SRodney W. Grimes fill = LEGATO; 4265b81b6b3SRodney W. Grimes ++cp; 4275b81b6b3SRodney W. Grimes slen--; 4285b81b6b3SRodney W. Grimes } 4295b81b6b3SRodney W. Grimes else if (cp[1] == 'S' || cp[1] == 's') 4305b81b6b3SRodney W. Grimes { 4315b81b6b3SRodney W. Grimes fill = STACCATO; 4325b81b6b3SRodney W. Grimes ++cp; 4335b81b6b3SRodney W. Grimes slen--; 4345b81b6b3SRodney W. Grimes } 4355b81b6b3SRodney W. Grimes break; 4365b81b6b3SRodney W. Grimes } 4375b81b6b3SRodney W. Grimes } 4385b81b6b3SRodney W. Grimes } 4395b81b6b3SRodney W. Grimes 4405b81b6b3SRodney W. Grimes /******************* UNIX DRIVER HOOKS BEGIN HERE ************************** 4415b81b6b3SRodney W. Grimes * 4425b81b6b3SRodney W. Grimes * This section implements driver hooks to run playstring() and the tone(), 4435b81b6b3SRodney W. Grimes * endtone(), and rest() functions defined above. 4445b81b6b3SRodney W. Grimes */ 4455b81b6b3SRodney W. Grimes 446e614aa8aSAndrey A. Chernov static int spkr_active = FALSE; /* exclusion flag */ 447e4961fbfSPoul-Henning Kamp static char *spkr_inbuf; /* incoming buf */ 4485b81b6b3SRodney W. Grimes 4493412120fSPoul-Henning Kamp static int 450b40ce416SJulian Elischer spkropen(dev, flags, fmt, td) 45189c9c53dSPoul-Henning Kamp struct cdev *dev; 45260039670SBruce Evans int flags; 45360039670SBruce Evans int fmt; 454b40ce416SJulian Elischer struct thread *td; 4555b81b6b3SRodney W. Grimes { 4565b81b6b3SRodney W. Grimes #ifdef DEBUG 457b8e49f68SBill Fumerola (void) printf("spkropen: entering with dev = %s\n", devtoname(dev)); 4585b81b6b3SRodney W. Grimes #endif /* DEBUG */ 4595b81b6b3SRodney W. Grimes 4605b81b6b3SRodney W. Grimes if (minor(dev) != 0) 4615b81b6b3SRodney W. Grimes return(ENXIO); 4625b81b6b3SRodney W. Grimes else if (spkr_active) 4635b81b6b3SRodney W. Grimes return(EBUSY); 4645b81b6b3SRodney W. Grimes else 4655b81b6b3SRodney W. Grimes { 466e614aa8aSAndrey A. Chernov #ifdef DEBUG 467e614aa8aSAndrey A. Chernov (void) printf("spkropen: about to perform play initialization\n"); 468e614aa8aSAndrey A. Chernov #endif /* DEBUG */ 4695b81b6b3SRodney W. Grimes playinit(); 470a163d034SWarner Losh spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK); 471e614aa8aSAndrey A. Chernov spkr_active = TRUE; 4725b81b6b3SRodney W. Grimes return(0); 4735b81b6b3SRodney W. Grimes } 474e614aa8aSAndrey A. Chernov } 4755b81b6b3SRodney W. Grimes 4763412120fSPoul-Henning Kamp static int 477f4de22acSJoerg Wunsch spkrwrite(dev, uio, ioflag) 47889c9c53dSPoul-Henning Kamp struct cdev *dev; 4795b81b6b3SRodney W. Grimes struct uio *uio; 48060039670SBruce Evans int ioflag; 4815b81b6b3SRodney W. Grimes { 4825b81b6b3SRodney W. Grimes #ifdef DEBUG 483b8e49f68SBill Fumerola printf("spkrwrite: entering with dev = %s, count = %d\n", 484b8e49f68SBill Fumerola devtoname(dev), uio->uio_resid); 4855b81b6b3SRodney W. Grimes #endif /* DEBUG */ 4865b81b6b3SRodney W. Grimes 4875b81b6b3SRodney W. Grimes if (minor(dev) != 0) 4885b81b6b3SRodney W. Grimes return(ENXIO); 4893bd9f6dbSPeter Wemm else if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */ 490e614aa8aSAndrey A. Chernov return(E2BIG); 4915b81b6b3SRodney W. Grimes else 4925b81b6b3SRodney W. Grimes { 493e614aa8aSAndrey A. Chernov unsigned n; 494e614aa8aSAndrey A. Chernov char *cp; 495e614aa8aSAndrey A. Chernov int error; 496e614aa8aSAndrey A. Chernov 497e614aa8aSAndrey A. Chernov n = uio->uio_resid; 498e4961fbfSPoul-Henning Kamp cp = spkr_inbuf; 4993bd9f6dbSPeter Wemm error = uiomove(cp, n, uio); 5003bd9f6dbSPeter Wemm if (!error) { 5013bd9f6dbSPeter Wemm cp[n] = '\0'; 502424e2d04SAndrey A. Chernov playstring(cp, n); 5033bd9f6dbSPeter Wemm } 5045b81b6b3SRodney W. Grimes return(error); 5055b81b6b3SRodney W. Grimes } 5065b81b6b3SRodney W. Grimes } 5075b81b6b3SRodney W. Grimes 5083412120fSPoul-Henning Kamp static int 509b40ce416SJulian Elischer spkrclose(dev, flags, fmt, td) 51089c9c53dSPoul-Henning Kamp struct cdev *dev; 51160039670SBruce Evans int flags; 51260039670SBruce Evans int fmt; 513b40ce416SJulian Elischer struct thread *td; 5145b81b6b3SRodney W. Grimes { 5155b81b6b3SRodney W. Grimes #ifdef DEBUG 516b8e49f68SBill Fumerola (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev)); 5175b81b6b3SRodney W. Grimes #endif /* DEBUG */ 5185b81b6b3SRodney W. Grimes 5195b81b6b3SRodney W. Grimes if (minor(dev) != 0) 5205b81b6b3SRodney W. Grimes return(ENXIO); 5215b81b6b3SRodney W. Grimes else 5225b81b6b3SRodney W. Grimes { 523521f364bSDag-Erling Smørgrav wakeup(&endtone); 524521f364bSDag-Erling Smørgrav wakeup(&endrest); 525e4961fbfSPoul-Henning Kamp free(spkr_inbuf, M_SPKR); 526e614aa8aSAndrey A. Chernov spkr_active = FALSE; 5275b81b6b3SRodney W. Grimes return(0); 5285b81b6b3SRodney W. Grimes } 529e614aa8aSAndrey A. Chernov } 5305b81b6b3SRodney W. Grimes 5313412120fSPoul-Henning Kamp static int 532b40ce416SJulian Elischer spkrioctl(dev, cmd, cmdarg, flags, td) 53389c9c53dSPoul-Henning Kamp struct cdev *dev; 53400671271SBruce Evans unsigned long cmd; 5355b81b6b3SRodney W. Grimes caddr_t cmdarg; 53660039670SBruce Evans int flags; 537b40ce416SJulian Elischer struct thread *td; 5385b81b6b3SRodney W. Grimes { 5395b81b6b3SRodney W. Grimes #ifdef DEBUG 540d9183205SBruce Evans (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n", 541d9183205SBruce Evans devtoname(dev), cmd); 5425b81b6b3SRodney W. Grimes #endif /* DEBUG */ 5435b81b6b3SRodney W. Grimes 5445b81b6b3SRodney W. Grimes if (minor(dev) != 0) 5455b81b6b3SRodney W. Grimes return(ENXIO); 5465b81b6b3SRodney W. Grimes else if (cmd == SPKRTONE) 5475b81b6b3SRodney W. Grimes { 5485b81b6b3SRodney W. Grimes tone_t *tp = (tone_t *)cmdarg; 5495b81b6b3SRodney W. Grimes 5505b81b6b3SRodney W. Grimes if (tp->frequency == 0) 551424e2d04SAndrey A. Chernov rest(tp->duration); 5525b81b6b3SRodney W. Grimes else 553424e2d04SAndrey A. Chernov tone(tp->frequency, tp->duration); 554424e2d04SAndrey A. Chernov return 0; 5555b81b6b3SRodney W. Grimes } 5565b81b6b3SRodney W. Grimes else if (cmd == SPKRTUNE) 5575b81b6b3SRodney W. Grimes { 5585b81b6b3SRodney W. Grimes tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 5595b81b6b3SRodney W. Grimes tone_t ttp; 5605b81b6b3SRodney W. Grimes int error; 5615b81b6b3SRodney W. Grimes 5625b81b6b3SRodney W. Grimes for (; ; tp++) { 5635b81b6b3SRodney W. Grimes error = copyin(tp, &ttp, sizeof(tone_t)); 5645b81b6b3SRodney W. Grimes if (error) 5655b81b6b3SRodney W. Grimes return(error); 5665b81b6b3SRodney W. Grimes if (ttp.duration == 0) 5675b81b6b3SRodney W. Grimes break; 5685b81b6b3SRodney W. Grimes if (ttp.frequency == 0) 569424e2d04SAndrey A. Chernov rest(ttp.duration); 5705b81b6b3SRodney W. Grimes else 571424e2d04SAndrey A. Chernov tone(ttp.frequency, ttp.duration); 5725b81b6b3SRodney W. Grimes } 5735b81b6b3SRodney W. Grimes return(0); 5745b81b6b3SRodney W. Grimes } 575e614aa8aSAndrey A. Chernov return(EINVAL); 576e614aa8aSAndrey A. Chernov } 5775b81b6b3SRodney W. Grimes 5782a50a6d7SMike Smith /* 5792a50a6d7SMike Smith * Install placeholder to claim the resources owned by the 5802a50a6d7SMike Smith * AT tone generator. 5812a50a6d7SMike Smith */ 58230201b6cSMatthew N. Dodd static struct isa_pnp_id speaker_ids[] = { 58330201b6cSMatthew N. Dodd #ifndef PC98 584257427efSMatthew N. Dodd { 0x0008d041 /* PNP0800 */, SPKR_DESC }, 58530201b6cSMatthew N. Dodd #endif 5862a50a6d7SMike Smith { 0 } 5872a50a6d7SMike Smith }; 5882a50a6d7SMike Smith 58989c9c53dSPoul-Henning Kamp static struct cdev *speaker_dev; 590ce6d929bSWes Peters 5912a50a6d7SMike Smith static int 59230201b6cSMatthew N. Dodd speaker_probe(device_t dev) 5932a50a6d7SMike Smith { 594c7f718ecSMatthew N. Dodd int error; 595c7f718ecSMatthew N. Dodd 59630201b6cSMatthew N. Dodd error = ISA_PNP_PROBE(device_get_parent(dev), dev, speaker_ids); 597c7f718ecSMatthew N. Dodd 598c7f718ecSMatthew N. Dodd /* PnP match */ 599c7f718ecSMatthew N. Dodd if (error == 0) 600c7f718ecSMatthew N. Dodd return (0); 601c7f718ecSMatthew N. Dodd 602c7f718ecSMatthew N. Dodd /* No match */ 603c7f718ecSMatthew N. Dodd if (error == ENXIO) 604c7f718ecSMatthew N. Dodd return (ENXIO); 605c7f718ecSMatthew N. Dodd 606c7f718ecSMatthew N. Dodd /* Not configured by hints. */ 60730201b6cSMatthew N. Dodd if (strncmp(device_get_name(dev), "speaker", 9)) 608c7f718ecSMatthew N. Dodd return (ENXIO); 609c7f718ecSMatthew N. Dodd 610257427efSMatthew N. Dodd device_set_desc(dev, SPKR_DESC); 611c7f718ecSMatthew N. Dodd 612c7f718ecSMatthew N. Dodd return (0); 6132a50a6d7SMike Smith } 6142a50a6d7SMike Smith 6152a50a6d7SMike Smith static int 61630201b6cSMatthew N. Dodd speaker_attach(device_t dev) 6172a50a6d7SMike Smith { 618c7f718ecSMatthew N. Dodd 61930201b6cSMatthew N. Dodd if (speaker_dev) { 620c7f718ecSMatthew N. Dodd device_printf(dev, "Already attached!\n"); 621c7f718ecSMatthew N. Dodd return (ENXIO); 622c7f718ecSMatthew N. Dodd } 623c7f718ecSMatthew N. Dodd 62430201b6cSMatthew N. Dodd speaker_dev = make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 625ce6d929bSWes Peters "speaker"); 626ce6d929bSWes Peters return (0); 627ce6d929bSWes Peters } 628ce6d929bSWes Peters 629ce6d929bSWes Peters static int 63030201b6cSMatthew N. Dodd speaker_detach(device_t dev) 631ce6d929bSWes Peters { 63230201b6cSMatthew N. Dodd destroy_dev(speaker_dev); 6332a50a6d7SMike Smith return (0); 6342a50a6d7SMike Smith } 6352a50a6d7SMike Smith 63630201b6cSMatthew N. Dodd static device_method_t speaker_methods[] = { 6372a50a6d7SMike Smith /* Device interface */ 63830201b6cSMatthew N. Dodd DEVMETHOD(device_probe, speaker_probe), 63930201b6cSMatthew N. Dodd DEVMETHOD(device_attach, speaker_attach), 64030201b6cSMatthew N. Dodd DEVMETHOD(device_detach, speaker_detach), 6412a50a6d7SMike Smith DEVMETHOD(device_shutdown, bus_generic_shutdown), 6422a50a6d7SMike Smith DEVMETHOD(device_suspend, bus_generic_suspend), 6432a50a6d7SMike Smith DEVMETHOD(device_resume, bus_generic_resume), 6442a50a6d7SMike Smith { 0, 0 } 6452a50a6d7SMike Smith }; 6462a50a6d7SMike Smith 64730201b6cSMatthew N. Dodd static driver_t speaker_driver = { 64830201b6cSMatthew N. Dodd "speaker", 64930201b6cSMatthew N. Dodd speaker_methods, 6502a50a6d7SMike Smith 1, /* no softc */ 6512a50a6d7SMike Smith }; 6522a50a6d7SMike Smith 65330201b6cSMatthew N. Dodd static devclass_t speaker_devclass; 6542a50a6d7SMike Smith 65530201b6cSMatthew N. Dodd DRIVER_MODULE(speaker, isa, speaker_driver, speaker_devclass, 0, 0); 65630201b6cSMatthew N. Dodd #ifndef PC98 65730201b6cSMatthew N. Dodd DRIVER_MODULE(speaker, acpi, speaker_driver, speaker_devclass, 0, 0); 65830201b6cSMatthew N. Dodd #endif 65953ac6efbSJulian Elischer 6605b81b6b3SRodney W. Grimes /* spkr.c ends here */ 661