15b81b6b3SRodney W. Grimes /* 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> 66f78ca60SRodney W. Grimes * 7b8e49f68SBill Fumerola * $Id: spkr.c,v 1.37 1999/05/31 11:26:32 phk Exp $ 85b81b6b3SRodney W. Grimes */ 95b81b6b3SRodney W. Grimes 105b81b6b3SRodney W. Grimes #include "speaker.h" 115b81b6b3SRodney W. Grimes 125b81b6b3SRodney W. Grimes #if NSPEAKER > 0 135b81b6b3SRodney W. Grimes 147b778b5eSEivind Eklund #include "opt_devfs.h" 157b778b5eSEivind Eklund 16f540b106SGarrett Wollman #include <sys/param.h> 17f540b106SGarrett Wollman #include <sys/systm.h> 18f540b106SGarrett Wollman #include <sys/kernel.h> 19f540b106SGarrett Wollman #include <sys/buf.h> 20f540b106SGarrett Wollman #include <sys/uio.h> 2187f6c662SJulian Elischer #include <sys/conf.h> 22f540b106SGarrett Wollman #include <i386/isa/isa.h> 23f540b106SGarrett Wollman #include <i386/isa/timerreg.h> 24b5e8ce9fSBruce Evans #include <machine/clock.h> 25f540b106SGarrett Wollman #include <machine/speaker.h> 265b81b6b3SRodney W. Grimes 278af5d536SJulian Elischer #ifdef DEVFS 288af5d536SJulian Elischer #include <sys/devfsext.h> 29303b270bSEivind Eklund static void *devfs_token; 307146c13eSJulian Elischer #endif 318af5d536SJulian Elischer 3287f6c662SJulian Elischer static d_open_t spkropen; 3387f6c662SJulian Elischer static d_close_t spkrclose; 3487f6c662SJulian Elischer static d_write_t spkrwrite; 3587f6c662SJulian Elischer static d_ioctl_t spkrioctl; 3687f6c662SJulian Elischer 3787f6c662SJulian Elischer #define CDEV_MAJOR 26 384e2f199eSPoul-Henning Kamp static struct cdevsw spkr_cdevsw = { 394e2f199eSPoul-Henning Kamp /* open */ spkropen, 404e2f199eSPoul-Henning Kamp /* close */ spkrclose, 414e2f199eSPoul-Henning Kamp /* read */ noread, 424e2f199eSPoul-Henning Kamp /* write */ spkrwrite, 434e2f199eSPoul-Henning Kamp /* ioctl */ spkrioctl, 444e2f199eSPoul-Henning Kamp /* stop */ nostop, 454e2f199eSPoul-Henning Kamp /* reset */ noreset, 464e2f199eSPoul-Henning Kamp /* devtotty */ nodevtotty, 474e2f199eSPoul-Henning Kamp /* poll */ nopoll, 484e2f199eSPoul-Henning Kamp /* mmap */ nommap, 494e2f199eSPoul-Henning Kamp /* strategy */ nostrategy, 504e2f199eSPoul-Henning Kamp /* name */ "spkr", 514e2f199eSPoul-Henning Kamp /* parms */ noparms, 524e2f199eSPoul-Henning Kamp /* maj */ CDEV_MAJOR, 534e2f199eSPoul-Henning Kamp /* dump */ nodump, 544e2f199eSPoul-Henning Kamp /* psize */ nopsize, 554e2f199eSPoul-Henning Kamp /* flags */ 0, 564e2f199eSPoul-Henning Kamp /* maxio */ 0, 574e2f199eSPoul-Henning Kamp /* bmaj */ -1 584e2f199eSPoul-Henning Kamp }; 598af5d536SJulian Elischer 605b81b6b3SRodney W. Grimes /**************** MACHINE DEPENDENT PART STARTS HERE ************************* 615b81b6b3SRodney W. Grimes * 625b81b6b3SRodney W. Grimes * This section defines a function tone() which causes a tone of given 639275c7ccSBruce Evans * frequency and duration from the ISA console speaker. 645b81b6b3SRodney W. Grimes * Another function endtone() is defined to force sound off, and there is 655b81b6b3SRodney W. Grimes * also a rest() entry point to do pauses. 665b81b6b3SRodney W. Grimes * 675b81b6b3SRodney W. Grimes * Audible sound is generated using the Programmable Interval Timer (PIT) and 689275c7ccSBruce Evans * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The 695b81b6b3SRodney W. Grimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is 705b81b6b3SRodney W. Grimes * used to generate clicks (a square wave) of whatever frequency is desired. 715b81b6b3SRodney W. Grimes */ 725b81b6b3SRodney W. Grimes 735b81b6b3SRodney W. Grimes /* 749275c7ccSBruce Evans * PPI control values. 759275c7ccSBruce Evans * XXX should be in a header and used in clock.c. 765b81b6b3SRodney W. Grimes */ 775b81b6b3SRodney W. Grimes #define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */ 785b81b6b3SRodney W. Grimes 791e064bd4SAndrey A. Chernov #define SPKRPRI PSOCK 801e064bd4SAndrey A. Chernov static char endtone, endrest; 815b81b6b3SRodney W. Grimes 820dfe10a6SBruce Evans static void tone __P((unsigned int thz, unsigned int ticks)); 830dfe10a6SBruce Evans static void rest __P((int ticks)); 840dfe10a6SBruce Evans static void playinit __P((void)); 850dfe10a6SBruce Evans static void playtone __P((int pitch, int value, int sustain)); 860dfe10a6SBruce Evans static int abs __P((int n)); 870dfe10a6SBruce Evans static void playstring __P((char *cp, size_t slen)); 880dfe10a6SBruce Evans 891e064bd4SAndrey A. Chernov /* emit tone of frequency thz for given number of ticks */ 90f4de22acSJoerg Wunsch static void 91f4de22acSJoerg Wunsch tone(thz, ticks) 921e064bd4SAndrey A. Chernov unsigned int thz, ticks; 935b81b6b3SRodney W. Grimes { 949714de6aSDavid Greenman unsigned int divisor; 95424e2d04SAndrey A. Chernov int sps; 965b81b6b3SRodney W. Grimes 979714de6aSDavid Greenman if (thz <= 0) 989714de6aSDavid Greenman return; 999714de6aSDavid Greenman 1009275c7ccSBruce Evans divisor = timer_freq / thz; 1019714de6aSDavid Greenman 1025b81b6b3SRodney W. Grimes #ifdef DEBUG 1031e064bd4SAndrey A. Chernov (void) printf("tone: thz=%d ticks=%d\n", thz, ticks); 1045b81b6b3SRodney W. Grimes #endif /* DEBUG */ 1055b81b6b3SRodney W. Grimes 1065b81b6b3SRodney W. Grimes /* set timer to generate clicks at given frequency in Hertz */ 107f4de22acSJoerg Wunsch sps = splclock(); 108bc4ff6e3SSøren Schmidt 1099275c7ccSBruce Evans if (acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT)) { 110bc4ff6e3SSøren Schmidt /* enter list of waiting procs ??? */ 111f4de22acSJoerg Wunsch splx(sps); 112bc4ff6e3SSøren Schmidt return; 113bc4ff6e3SSøren Schmidt } 114f4de22acSJoerg Wunsch splx(sps); 115f4de22acSJoerg Wunsch disable_intr(); 116bc4ff6e3SSøren Schmidt outb(TIMER_CNTR2, (divisor & 0xff)); /* send lo byte */ 117bc4ff6e3SSøren Schmidt outb(TIMER_CNTR2, (divisor >> 8)); /* send hi byte */ 118f4de22acSJoerg Wunsch enable_intr(); 1195b81b6b3SRodney W. Grimes 1205b81b6b3SRodney W. Grimes /* turn the speaker on */ 121bc4ff6e3SSøren Schmidt outb(IO_PPI, inb(IO_PPI) | PPI_SPKR); 1225b81b6b3SRodney W. Grimes 1235b81b6b3SRodney W. Grimes /* 1245b81b6b3SRodney W. Grimes * Set timeout to endtone function, then give up the timeslice. 1255b81b6b3SRodney W. Grimes * This is so other processes can execute while the tone is being 1265b81b6b3SRodney W. Grimes * emitted. 1275b81b6b3SRodney W. Grimes */ 1283bdc031eSAndrey A. Chernov if (ticks > 0) 1293bdc031eSAndrey A. Chernov tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks); 130bc4ff6e3SSøren Schmidt outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR); 131f4de22acSJoerg Wunsch sps = splclock(); 132bc4ff6e3SSøren Schmidt release_timer2(); 133f4de22acSJoerg Wunsch splx(sps); 1345b81b6b3SRodney W. Grimes } 1355b81b6b3SRodney W. Grimes 1365b81b6b3SRodney W. Grimes /* rest for given number of ticks */ 137f4de22acSJoerg Wunsch static void 138f4de22acSJoerg Wunsch rest(ticks) 1395b81b6b3SRodney W. Grimes int ticks; 1405b81b6b3SRodney W. Grimes { 1415b81b6b3SRodney W. Grimes /* 1425b81b6b3SRodney W. Grimes * Set timeout to endrest function, then give up the timeslice. 1435b81b6b3SRodney W. Grimes * This is so other processes can execute while the rest is being 1445b81b6b3SRodney W. Grimes * waited out. 1455b81b6b3SRodney W. Grimes */ 1465b81b6b3SRodney W. Grimes #ifdef DEBUG 147e614aa8aSAndrey A. Chernov (void) printf("rest: %d\n", ticks); 1485b81b6b3SRodney W. Grimes #endif /* DEBUG */ 1493bdc031eSAndrey A. Chernov if (ticks > 0) 1503bdc031eSAndrey A. Chernov tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks); 1515b81b6b3SRodney W. Grimes } 1525b81b6b3SRodney W. Grimes 1535b81b6b3SRodney W. Grimes /**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 1545b81b6b3SRodney W. Grimes * 1555b81b6b3SRodney W. Grimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 156e614aa8aSAndrey A. Chernov * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 157e614aa8aSAndrey A. Chernov * tracking facility are added. 1585b81b6b3SRodney W. Grimes * Requires tone(), rest(), and endtone(). String play is not interruptible 1595b81b6b3SRodney W. Grimes * except possibly at physical block boundaries. 1605b81b6b3SRodney W. Grimes */ 1615b81b6b3SRodney W. Grimes 1625b81b6b3SRodney W. Grimes typedef int bool; 1635b81b6b3SRodney W. Grimes #define TRUE 1 1645b81b6b3SRodney W. Grimes #define FALSE 0 1655b81b6b3SRodney W. Grimes 1665b81b6b3SRodney W. Grimes #define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z'))) 1675b81b6b3SRodney W. Grimes #define isdigit(c) (((c) >= '0') && ((c) <= '9')) 1685b81b6b3SRodney W. Grimes #define dtoi(c) ((c) - '0') 1695b81b6b3SRodney W. Grimes 1705b81b6b3SRodney W. Grimes static int octave; /* currently selected octave */ 1715b81b6b3SRodney W. Grimes static int whole; /* whole-note time at current tempo, in ticks */ 1725b81b6b3SRodney W. Grimes static int value; /* whole divisor for note time, quarter note = 1 */ 1735b81b6b3SRodney W. Grimes static int fill; /* controls spacing of notes */ 1745b81b6b3SRodney W. Grimes static bool octtrack; /* octave-tracking on? */ 1755b81b6b3SRodney W. Grimes static bool octprefix; /* override current octave-tracking state? */ 1765b81b6b3SRodney W. Grimes 1775b81b6b3SRodney W. Grimes /* 1785b81b6b3SRodney W. Grimes * Magic number avoidance... 1795b81b6b3SRodney W. Grimes */ 1805b81b6b3SRodney W. Grimes #define SECS_PER_MIN 60 /* seconds per minute */ 1815b81b6b3SRodney W. Grimes #define WHOLE_NOTE 4 /* quarter notes per whole note */ 1825b81b6b3SRodney W. Grimes #define MIN_VALUE 64 /* the most we can divide a note by */ 1835b81b6b3SRodney W. Grimes #define DFLT_VALUE 4 /* default value (quarter-note) */ 1845b81b6b3SRodney W. Grimes #define FILLTIME 8 /* for articulation, break note in parts */ 1855b81b6b3SRodney W. Grimes #define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 1865b81b6b3SRodney W. Grimes #define NORMAL 7 /* 7/8ths of note interval is filled */ 1875b81b6b3SRodney W. Grimes #define LEGATO 8 /* all of note interval is filled */ 1885b81b6b3SRodney W. Grimes #define DFLT_OCTAVE 4 /* default octave */ 1895b81b6b3SRodney W. Grimes #define MIN_TEMPO 32 /* minimum tempo */ 1905b81b6b3SRodney W. Grimes #define DFLT_TEMPO 120 /* default tempo */ 1915b81b6b3SRodney W. Grimes #define MAX_TEMPO 255 /* max tempo */ 1925b81b6b3SRodney W. Grimes #define NUM_MULT 3 /* numerator of dot multiplier */ 1935b81b6b3SRodney W. Grimes #define DENOM_MULT 2 /* denominator of dot multiplier */ 1945b81b6b3SRodney W. Grimes 1955b81b6b3SRodney W. Grimes /* letter to half-tone: A B C D E F G */ 1965b81b6b3SRodney W. Grimes static int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 1975b81b6b3SRodney W. Grimes 1985b81b6b3SRodney W. Grimes /* 1995b81b6b3SRodney W. Grimes * This is the American Standard A440 Equal-Tempered scale with frequencies 2005b81b6b3SRodney W. Grimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 2015b81b6b3SRodney W. Grimes * our octave 0 is standard octave 2. 2025b81b6b3SRodney W. Grimes */ 2035b81b6b3SRodney W. Grimes #define OCTAVE_NOTES 12 /* semitones per octave */ 2045b81b6b3SRodney W. Grimes static int pitchtab[] = 2055b81b6b3SRodney W. Grimes { 2065b81b6b3SRodney W. Grimes /* C C# D D# E F F# G G# A A# B*/ 2075b81b6b3SRodney W. Grimes /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 2085b81b6b3SRodney W. Grimes /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 2095b81b6b3SRodney W. Grimes /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 2105b81b6b3SRodney W. Grimes /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 2115b81b6b3SRodney W. Grimes /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 2125b81b6b3SRodney W. Grimes /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 2135b81b6b3SRodney W. Grimes /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 2145b81b6b3SRodney W. Grimes }; 2155b81b6b3SRodney W. Grimes 216f4de22acSJoerg Wunsch static void 217f4de22acSJoerg Wunsch playinit() 2185b81b6b3SRodney W. Grimes { 2195b81b6b3SRodney W. Grimes octave = DFLT_OCTAVE; 2201e064bd4SAndrey A. Chernov whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 2215b81b6b3SRodney W. Grimes fill = NORMAL; 2225b81b6b3SRodney W. Grimes value = DFLT_VALUE; 2235b81b6b3SRodney W. Grimes octtrack = FALSE; 2245b81b6b3SRodney W. Grimes octprefix = TRUE; /* act as though there was an initial O(n) */ 2255b81b6b3SRodney W. Grimes } 2265b81b6b3SRodney W. Grimes 2275b81b6b3SRodney W. Grimes /* play tone of proper duration for current rhythm signature */ 228f4de22acSJoerg Wunsch static void 229f4de22acSJoerg Wunsch playtone(pitch, value, sustain) 2305b81b6b3SRodney W. Grimes int pitch, value, sustain; 2315b81b6b3SRodney W. Grimes { 2325b81b6b3SRodney W. Grimes register int sound, silence, snum = 1, sdenom = 1; 2335b81b6b3SRodney W. Grimes 2345b81b6b3SRodney W. Grimes /* this weirdness avoids floating-point arithmetic */ 2355b81b6b3SRodney W. Grimes for (; sustain; sustain--) 2365b81b6b3SRodney W. Grimes { 237e614aa8aSAndrey A. Chernov /* See the BUGS section in the man page for discussion */ 2385b81b6b3SRodney W. Grimes snum *= NUM_MULT; 2395b81b6b3SRodney W. Grimes sdenom *= DENOM_MULT; 2405b81b6b3SRodney W. Grimes } 2415b81b6b3SRodney W. Grimes 2429714de6aSDavid Greenman if (value == 0 || sdenom == 0) 2439714de6aSDavid Greenman return; 2449714de6aSDavid Greenman 2455b81b6b3SRodney W. Grimes if (pitch == -1) 246424e2d04SAndrey A. Chernov rest(whole * snum / (value * sdenom)); 2475b81b6b3SRodney W. Grimes else 2485b81b6b3SRodney W. Grimes { 2495b81b6b3SRodney W. Grimes sound = (whole * snum) / (value * sdenom) 2505b81b6b3SRodney W. Grimes - (whole * (FILLTIME - fill)) / (value * FILLTIME); 2515b81b6b3SRodney W. Grimes silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 2525b81b6b3SRodney W. Grimes 2535b81b6b3SRodney W. Grimes #ifdef DEBUG 254e614aa8aSAndrey A. Chernov (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 2555b81b6b3SRodney W. Grimes pitch, sound, silence); 2565b81b6b3SRodney W. Grimes #endif /* DEBUG */ 2575b81b6b3SRodney W. Grimes 258424e2d04SAndrey A. Chernov tone(pitchtab[pitch], sound); 2595b81b6b3SRodney W. Grimes if (fill != LEGATO) 260424e2d04SAndrey A. Chernov rest(silence); 2615b81b6b3SRodney W. Grimes } 2625b81b6b3SRodney W. Grimes } 2635b81b6b3SRodney W. Grimes 264f4de22acSJoerg Wunsch static int 265f4de22acSJoerg Wunsch abs(n) 2665b81b6b3SRodney W. Grimes int n; 2675b81b6b3SRodney W. Grimes { 2685b81b6b3SRodney W. Grimes if (n < 0) 2695b81b6b3SRodney W. Grimes return(-n); 2705b81b6b3SRodney W. Grimes else 2715b81b6b3SRodney W. Grimes return(n); 2725b81b6b3SRodney W. Grimes } 2735b81b6b3SRodney W. Grimes 2745b81b6b3SRodney W. Grimes /* interpret and play an item from a notation string */ 275f4de22acSJoerg Wunsch static void 276f4de22acSJoerg Wunsch playstring(cp, slen) 2775b81b6b3SRodney W. Grimes char *cp; 2785b81b6b3SRodney W. Grimes size_t slen; 2795b81b6b3SRodney W. Grimes { 280e614aa8aSAndrey A. Chernov int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 2815b81b6b3SRodney W. Grimes 2825b81b6b3SRodney W. Grimes #define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 2835b81b6b3SRodney W. Grimes {v = v * 10 + (*++cp - '0'); slen--;} 2845b81b6b3SRodney W. Grimes for (; slen--; cp++) 2855b81b6b3SRodney W. Grimes { 2865b81b6b3SRodney W. Grimes int sustain, timeval, tempo; 2875b81b6b3SRodney W. Grimes register char c = toupper(*cp); 2885b81b6b3SRodney W. Grimes 2895b81b6b3SRodney W. Grimes #ifdef DEBUG 290e614aa8aSAndrey A. Chernov (void) printf("playstring: %c (%x)\n", c, c); 2915b81b6b3SRodney W. Grimes #endif /* DEBUG */ 2925b81b6b3SRodney W. Grimes 2935b81b6b3SRodney W. Grimes switch (c) 2945b81b6b3SRodney W. Grimes { 2955b81b6b3SRodney W. Grimes case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 2965b81b6b3SRodney W. Grimes 2975b81b6b3SRodney W. Grimes /* compute pitch */ 2985b81b6b3SRodney W. Grimes pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 2995b81b6b3SRodney W. Grimes 3005b81b6b3SRodney W. Grimes /* this may be followed by an accidental sign */ 3015b81b6b3SRodney W. Grimes if (cp[1] == '#' || cp[1] == '+') 3025b81b6b3SRodney W. Grimes { 3035b81b6b3SRodney W. Grimes ++pitch; 3045b81b6b3SRodney W. Grimes ++cp; 3055b81b6b3SRodney W. Grimes slen--; 3065b81b6b3SRodney W. Grimes } 3075b81b6b3SRodney W. Grimes else if (cp[1] == '-') 3085b81b6b3SRodney W. Grimes { 3095b81b6b3SRodney W. Grimes --pitch; 3105b81b6b3SRodney W. Grimes ++cp; 3115b81b6b3SRodney W. Grimes slen--; 3125b81b6b3SRodney W. Grimes } 3135b81b6b3SRodney W. Grimes 3145b81b6b3SRodney W. Grimes /* 3155b81b6b3SRodney W. Grimes * If octave-tracking mode is on, and there has been no octave- 3165b81b6b3SRodney W. Grimes * setting prefix, find the version of the current letter note 3175b81b6b3SRodney W. Grimes * closest to the last regardless of octave. 3185b81b6b3SRodney W. Grimes */ 3195b81b6b3SRodney W. Grimes if (octtrack && !octprefix) 3205b81b6b3SRodney W. Grimes { 3215b81b6b3SRodney W. Grimes if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) 3225b81b6b3SRodney W. Grimes { 3235b81b6b3SRodney W. Grimes ++octave; 3245b81b6b3SRodney W. Grimes pitch += OCTAVE_NOTES; 3255b81b6b3SRodney W. Grimes } 3265b81b6b3SRodney W. Grimes 3275b81b6b3SRodney W. Grimes if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) 3285b81b6b3SRodney W. Grimes { 3295b81b6b3SRodney W. Grimes --octave; 3305b81b6b3SRodney W. Grimes pitch -= OCTAVE_NOTES; 3315b81b6b3SRodney W. Grimes } 3325b81b6b3SRodney W. Grimes } 3335b81b6b3SRodney W. Grimes octprefix = FALSE; 3345b81b6b3SRodney W. Grimes lastpitch = pitch; 3355b81b6b3SRodney W. Grimes 3365b81b6b3SRodney W. Grimes /* ...which may in turn be followed by an override time value */ 3375b81b6b3SRodney W. Grimes GETNUM(cp, timeval); 3385b81b6b3SRodney W. Grimes if (timeval <= 0 || timeval > MIN_VALUE) 3395b81b6b3SRodney W. Grimes timeval = value; 3405b81b6b3SRodney W. Grimes 3415b81b6b3SRodney W. Grimes /* ...and/or sustain dots */ 3425b81b6b3SRodney W. Grimes for (sustain = 0; cp[1] == '.'; cp++) 3435b81b6b3SRodney W. Grimes { 3445b81b6b3SRodney W. Grimes slen--; 3455b81b6b3SRodney W. Grimes sustain++; 3465b81b6b3SRodney W. Grimes } 3475b81b6b3SRodney W. Grimes 348e614aa8aSAndrey A. Chernov /* ...and/or a slur mark */ 349e614aa8aSAndrey A. Chernov oldfill = fill; 350e614aa8aSAndrey A. Chernov if (cp[1] == '_') 351e614aa8aSAndrey A. Chernov { 352e614aa8aSAndrey A. Chernov fill = LEGATO; 353e614aa8aSAndrey A. Chernov ++cp; 354e614aa8aSAndrey A. Chernov slen--; 355e614aa8aSAndrey A. Chernov } 356e614aa8aSAndrey A. Chernov 3575b81b6b3SRodney W. Grimes /* time to emit the actual tone */ 358424e2d04SAndrey A. Chernov playtone(pitch, timeval, sustain); 359e614aa8aSAndrey A. Chernov 360e614aa8aSAndrey A. Chernov fill = oldfill; 3615b81b6b3SRodney W. Grimes break; 3625b81b6b3SRodney W. Grimes 3635b81b6b3SRodney W. Grimes case 'O': 3645b81b6b3SRodney W. Grimes if (cp[1] == 'N' || cp[1] == 'n') 3655b81b6b3SRodney W. Grimes { 3665b81b6b3SRodney W. Grimes octprefix = octtrack = FALSE; 3675b81b6b3SRodney W. Grimes ++cp; 3685b81b6b3SRodney W. Grimes slen--; 3695b81b6b3SRodney W. Grimes } 3705b81b6b3SRodney W. Grimes else if (cp[1] == 'L' || cp[1] == 'l') 3715b81b6b3SRodney W. Grimes { 3725b81b6b3SRodney W. Grimes octtrack = TRUE; 3735b81b6b3SRodney W. Grimes ++cp; 3745b81b6b3SRodney W. Grimes slen--; 3755b81b6b3SRodney W. Grimes } 3765b81b6b3SRodney W. Grimes else 3775b81b6b3SRodney W. Grimes { 3785b81b6b3SRodney W. Grimes GETNUM(cp, octave); 3790a97fb59SAndrey A. Chernov if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES) 3805b81b6b3SRodney W. Grimes octave = DFLT_OCTAVE; 3815b81b6b3SRodney W. Grimes octprefix = TRUE; 3825b81b6b3SRodney W. Grimes } 3835b81b6b3SRodney W. Grimes break; 3845b81b6b3SRodney W. Grimes 3855b81b6b3SRodney W. Grimes case '>': 3860a97fb59SAndrey A. Chernov if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1) 3875b81b6b3SRodney W. Grimes octave++; 3885b81b6b3SRodney W. Grimes octprefix = TRUE; 3895b81b6b3SRodney W. Grimes break; 3905b81b6b3SRodney W. Grimes 3915b81b6b3SRodney W. Grimes case '<': 3925b81b6b3SRodney W. Grimes if (octave > 0) 3935b81b6b3SRodney W. Grimes octave--; 3945b81b6b3SRodney W. Grimes octprefix = TRUE; 3955b81b6b3SRodney W. Grimes break; 3965b81b6b3SRodney W. Grimes 3975b81b6b3SRodney W. Grimes case 'N': 3985b81b6b3SRodney W. Grimes GETNUM(cp, pitch); 3995b81b6b3SRodney W. Grimes for (sustain = 0; cp[1] == '.'; cp++) 4005b81b6b3SRodney W. Grimes { 4015b81b6b3SRodney W. Grimes slen--; 4025b81b6b3SRodney W. Grimes sustain++; 4035b81b6b3SRodney W. Grimes } 404e614aa8aSAndrey A. Chernov oldfill = fill; 405e614aa8aSAndrey A. Chernov if (cp[1] == '_') 406e614aa8aSAndrey A. Chernov { 407e614aa8aSAndrey A. Chernov fill = LEGATO; 408e614aa8aSAndrey A. Chernov ++cp; 409e614aa8aSAndrey A. Chernov slen--; 410e614aa8aSAndrey A. Chernov } 411424e2d04SAndrey A. Chernov playtone(pitch - 1, value, sustain); 412e614aa8aSAndrey A. Chernov fill = oldfill; 4135b81b6b3SRodney W. Grimes break; 4145b81b6b3SRodney W. Grimes 4155b81b6b3SRodney W. Grimes case 'L': 4165b81b6b3SRodney W. Grimes GETNUM(cp, value); 4175b81b6b3SRodney W. Grimes if (value <= 0 || value > MIN_VALUE) 4185b81b6b3SRodney W. Grimes value = DFLT_VALUE; 4195b81b6b3SRodney W. Grimes break; 4205b81b6b3SRodney W. Grimes 4215b81b6b3SRodney W. Grimes case 'P': 4225b81b6b3SRodney W. Grimes case '~': 4235b81b6b3SRodney W. Grimes /* this may be followed by an override time value */ 4245b81b6b3SRodney W. Grimes GETNUM(cp, timeval); 4255b81b6b3SRodney W. Grimes if (timeval <= 0 || timeval > MIN_VALUE) 4265b81b6b3SRodney W. Grimes timeval = value; 4275b81b6b3SRodney W. Grimes for (sustain = 0; cp[1] == '.'; cp++) 4285b81b6b3SRodney W. Grimes { 4295b81b6b3SRodney W. Grimes slen--; 4305b81b6b3SRodney W. Grimes sustain++; 4315b81b6b3SRodney W. Grimes } 432424e2d04SAndrey A. Chernov playtone(-1, timeval, sustain); 4335b81b6b3SRodney W. Grimes break; 4345b81b6b3SRodney W. Grimes 4355b81b6b3SRodney W. Grimes case 'T': 4365b81b6b3SRodney W. Grimes GETNUM(cp, tempo); 4375b81b6b3SRodney W. Grimes if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 4385b81b6b3SRodney W. Grimes tempo = DFLT_TEMPO; 4391e064bd4SAndrey A. Chernov whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo; 4405b81b6b3SRodney W. Grimes break; 4415b81b6b3SRodney W. Grimes 4425b81b6b3SRodney W. Grimes case 'M': 4435b81b6b3SRodney W. Grimes if (cp[1] == 'N' || cp[1] == 'n') 4445b81b6b3SRodney W. Grimes { 4455b81b6b3SRodney W. Grimes fill = NORMAL; 4465b81b6b3SRodney W. Grimes ++cp; 4475b81b6b3SRodney W. Grimes slen--; 4485b81b6b3SRodney W. Grimes } 4495b81b6b3SRodney W. Grimes else if (cp[1] == 'L' || cp[1] == 'l') 4505b81b6b3SRodney W. Grimes { 4515b81b6b3SRodney W. Grimes fill = LEGATO; 4525b81b6b3SRodney W. Grimes ++cp; 4535b81b6b3SRodney W. Grimes slen--; 4545b81b6b3SRodney W. Grimes } 4555b81b6b3SRodney W. Grimes else if (cp[1] == 'S' || cp[1] == 's') 4565b81b6b3SRodney W. Grimes { 4575b81b6b3SRodney W. Grimes fill = STACCATO; 4585b81b6b3SRodney W. Grimes ++cp; 4595b81b6b3SRodney W. Grimes slen--; 4605b81b6b3SRodney W. Grimes } 4615b81b6b3SRodney W. Grimes break; 4625b81b6b3SRodney W. Grimes } 4635b81b6b3SRodney W. Grimes } 4645b81b6b3SRodney W. Grimes } 4655b81b6b3SRodney W. Grimes 4665b81b6b3SRodney W. Grimes /******************* UNIX DRIVER HOOKS BEGIN HERE ************************** 4675b81b6b3SRodney W. Grimes * 4685b81b6b3SRodney W. Grimes * This section implements driver hooks to run playstring() and the tone(), 4695b81b6b3SRodney W. Grimes * endtone(), and rest() functions defined above. 4705b81b6b3SRodney W. Grimes */ 4715b81b6b3SRodney W. Grimes 472e614aa8aSAndrey A. Chernov static int spkr_active = FALSE; /* exclusion flag */ 4735b81b6b3SRodney W. Grimes static struct buf *spkr_inbuf; /* incoming buf */ 4745b81b6b3SRodney W. Grimes 475f4de22acSJoerg Wunsch int 476f4de22acSJoerg Wunsch spkropen(dev, flags, fmt, p) 4775b81b6b3SRodney W. Grimes dev_t dev; 47860039670SBruce Evans int flags; 47960039670SBruce Evans int fmt; 48060039670SBruce Evans struct proc *p; 4815b81b6b3SRodney W. Grimes { 4825b81b6b3SRodney W. Grimes #ifdef DEBUG 483b8e49f68SBill Fumerola (void) printf("spkropen: entering with dev = %s\n", devtoname(dev)); 4845b81b6b3SRodney W. Grimes #endif /* DEBUG */ 4855b81b6b3SRodney W. Grimes 4865b81b6b3SRodney W. Grimes if (minor(dev) != 0) 4875b81b6b3SRodney W. Grimes return(ENXIO); 4885b81b6b3SRodney W. Grimes else if (spkr_active) 4895b81b6b3SRodney W. Grimes return(EBUSY); 4905b81b6b3SRodney W. Grimes else 4915b81b6b3SRodney W. Grimes { 492e614aa8aSAndrey A. Chernov #ifdef DEBUG 493e614aa8aSAndrey A. Chernov (void) printf("spkropen: about to perform play initialization\n"); 494e614aa8aSAndrey A. Chernov #endif /* DEBUG */ 4955b81b6b3SRodney W. Grimes playinit(); 4965b81b6b3SRodney W. Grimes spkr_inbuf = geteblk(DEV_BSIZE); 497e614aa8aSAndrey A. Chernov spkr_active = TRUE; 4985b81b6b3SRodney W. Grimes return(0); 4995b81b6b3SRodney W. Grimes } 500e614aa8aSAndrey A. Chernov } 5015b81b6b3SRodney W. Grimes 502f4de22acSJoerg Wunsch int 503f4de22acSJoerg Wunsch spkrwrite(dev, uio, ioflag) 5045b81b6b3SRodney W. Grimes dev_t dev; 5055b81b6b3SRodney W. Grimes struct uio *uio; 50660039670SBruce Evans int ioflag; 5075b81b6b3SRodney W. Grimes { 5085b81b6b3SRodney W. Grimes #ifdef DEBUG 509b8e49f68SBill Fumerola printf("spkrwrite: entering with dev = %s, count = %d\n", 510b8e49f68SBill Fumerola devtoname(dev), uio->uio_resid); 5115b81b6b3SRodney W. Grimes #endif /* DEBUG */ 5125b81b6b3SRodney W. Grimes 5135b81b6b3SRodney W. Grimes if (minor(dev) != 0) 5145b81b6b3SRodney W. Grimes return(ENXIO); 5153bd9f6dbSPeter Wemm else if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */ 516e614aa8aSAndrey A. Chernov return(E2BIG); 5175b81b6b3SRodney W. Grimes else 5185b81b6b3SRodney W. Grimes { 519e614aa8aSAndrey A. Chernov unsigned n; 520e614aa8aSAndrey A. Chernov char *cp; 521e614aa8aSAndrey A. Chernov int error; 522e614aa8aSAndrey A. Chernov 523e614aa8aSAndrey A. Chernov n = uio->uio_resid; 524ab3f7469SPoul-Henning Kamp cp = spkr_inbuf->b_data; 5253bd9f6dbSPeter Wemm error = uiomove(cp, n, uio); 5263bd9f6dbSPeter Wemm if (!error) { 5273bd9f6dbSPeter Wemm cp[n] = '\0'; 528424e2d04SAndrey A. Chernov playstring(cp, n); 5293bd9f6dbSPeter Wemm } 5305b81b6b3SRodney W. Grimes return(error); 5315b81b6b3SRodney W. Grimes } 5325b81b6b3SRodney W. Grimes } 5335b81b6b3SRodney W. Grimes 534f4de22acSJoerg Wunsch int 535f4de22acSJoerg Wunsch spkrclose(dev, flags, fmt, p) 5365b81b6b3SRodney W. Grimes dev_t dev; 53760039670SBruce Evans int flags; 53860039670SBruce Evans int fmt; 53960039670SBruce Evans struct proc *p; 5405b81b6b3SRodney W. Grimes { 5415b81b6b3SRodney W. Grimes #ifdef DEBUG 542b8e49f68SBill Fumerola (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev)); 5435b81b6b3SRodney W. Grimes #endif /* DEBUG */ 5445b81b6b3SRodney W. Grimes 5455b81b6b3SRodney W. Grimes if (minor(dev) != 0) 5465b81b6b3SRodney W. Grimes return(ENXIO); 5475b81b6b3SRodney W. Grimes else 5485b81b6b3SRodney W. Grimes { 5491e064bd4SAndrey A. Chernov wakeup((caddr_t)&endtone); 5501e064bd4SAndrey A. Chernov wakeup((caddr_t)&endrest); 5515b81b6b3SRodney W. Grimes brelse(spkr_inbuf); 552e614aa8aSAndrey A. Chernov spkr_active = FALSE; 5535b81b6b3SRodney W. Grimes return(0); 5545b81b6b3SRodney W. Grimes } 555e614aa8aSAndrey A. Chernov } 5565b81b6b3SRodney W. Grimes 557f4de22acSJoerg Wunsch int 558f4de22acSJoerg Wunsch spkrioctl(dev, cmd, cmdarg, flags, p) 5595b81b6b3SRodney W. Grimes dev_t dev; 56000671271SBruce Evans unsigned long cmd; 5615b81b6b3SRodney W. Grimes caddr_t cmdarg; 56260039670SBruce Evans int flags; 56360039670SBruce Evans struct proc *p; 5645b81b6b3SRodney W. Grimes { 5655b81b6b3SRodney W. Grimes #ifdef DEBUG 56600671271SBruce Evans (void) printf("spkrioctl: entering with dev = %lx, cmd = %lx\n", 56700671271SBruce Evans (unsigned long)dev, cmd); 5685b81b6b3SRodney W. Grimes #endif /* DEBUG */ 5695b81b6b3SRodney W. Grimes 5705b81b6b3SRodney W. Grimes if (minor(dev) != 0) 5715b81b6b3SRodney W. Grimes return(ENXIO); 5725b81b6b3SRodney W. Grimes else if (cmd == SPKRTONE) 5735b81b6b3SRodney W. Grimes { 5745b81b6b3SRodney W. Grimes tone_t *tp = (tone_t *)cmdarg; 5755b81b6b3SRodney W. Grimes 5765b81b6b3SRodney W. Grimes if (tp->frequency == 0) 577424e2d04SAndrey A. Chernov rest(tp->duration); 5785b81b6b3SRodney W. Grimes else 579424e2d04SAndrey A. Chernov tone(tp->frequency, tp->duration); 580424e2d04SAndrey A. Chernov return 0; 5815b81b6b3SRodney W. Grimes } 5825b81b6b3SRodney W. Grimes else if (cmd == SPKRTUNE) 5835b81b6b3SRodney W. Grimes { 5845b81b6b3SRodney W. Grimes tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 5855b81b6b3SRodney W. Grimes tone_t ttp; 5865b81b6b3SRodney W. Grimes int error; 5875b81b6b3SRodney W. Grimes 5885b81b6b3SRodney W. Grimes for (; ; tp++) { 5895b81b6b3SRodney W. Grimes error = copyin(tp, &ttp, sizeof(tone_t)); 5905b81b6b3SRodney W. Grimes if (error) 5915b81b6b3SRodney W. Grimes return(error); 5925b81b6b3SRodney W. Grimes if (ttp.duration == 0) 5935b81b6b3SRodney W. Grimes break; 5945b81b6b3SRodney W. Grimes if (ttp.frequency == 0) 595424e2d04SAndrey A. Chernov rest(ttp.duration); 5965b81b6b3SRodney W. Grimes else 597424e2d04SAndrey A. Chernov tone(ttp.frequency, ttp.duration); 5985b81b6b3SRodney W. Grimes } 5995b81b6b3SRodney W. Grimes return(0); 6005b81b6b3SRodney W. Grimes } 601e614aa8aSAndrey A. Chernov return(EINVAL); 602e614aa8aSAndrey A. Chernov } 6035b81b6b3SRodney W. Grimes 604f4de22acSJoerg Wunsch static void 605f4de22acSJoerg Wunsch spkr_drvinit(void *unused) 6067146c13eSJulian Elischer { 6072447bec8SPoul-Henning Kamp cdevsw_add(&spkr_cdevsw); 60853ac6efbSJulian Elischer #ifdef DEVFS 6092412bc33SBruce Evans devfs_token = devfs_add_devswf(&spkr_cdevsw, 0, DV_CHR, 6102447bec8SPoul-Henning Kamp UID_ROOT, GID_WHEEL, 0600, "speaker"); 61153ac6efbSJulian Elischer #endif 61253ac6efbSJulian Elischer } 61353ac6efbSJulian Elischer 61453ac6efbSJulian Elischer SYSINIT(spkrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,spkr_drvinit,NULL) 61553ac6efbSJulian Elischer 61653ac6efbSJulian Elischer 6175b81b6b3SRodney W. Grimes #endif /* NSPEAKER > 0 */ 6185b81b6b3SRodney W. Grimes /* spkr.c ends here */ 619