spkr.c revision 106323
14Srgrimes/* 2738Sache * spkr.c -- device driver for console speaker 34Srgrimes * 4738Sache * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993 5738Sache * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su> 6106323Smdodd * modified for PC98 by Kakefuda 7619Srgrimes * 850477Speter * $FreeBSD: head/sys/dev/speaker/spkr.c 106323 2002-11-02 04:18:10Z mdodd $ 94Srgrimes */ 104Srgrimes 112056Swollman#include <sys/param.h> 122056Swollman#include <sys/systm.h> 1361994Smsmith#include <sys/bus.h> 142056Swollman#include <sys/kernel.h> 1561994Smsmith#include <sys/module.h> 162056Swollman#include <sys/uio.h> 1712675Sjulian#include <sys/conf.h> 1852843Sphk#include <sys/ctype.h> 1960038Sphk#include <sys/malloc.h> 2061994Smsmith#include <isa/isavar.h> 21106323Smdodd#ifdef PC98 22106323Smdodd#include <pc98/pc98/pc98.h> 23106323Smdodd#else 242056Swollman#include <i386/isa/isa.h> 25106323Smdodd#endif 262056Swollman#include <i386/isa/timerreg.h> 277090Sbde#include <machine/clock.h> 282056Swollman#include <machine/speaker.h> 294Srgrimes 3012675Sjulianstatic d_open_t spkropen; 3112675Sjulianstatic d_close_t spkrclose; 3212675Sjulianstatic d_write_t spkrwrite; 3312675Sjulianstatic d_ioctl_t spkrioctl; 3412502Sjulian 3512675Sjulian#define CDEV_MAJOR 26 3647625Sphkstatic struct cdevsw spkr_cdevsw = { 3747625Sphk /* open */ spkropen, 3847625Sphk /* close */ spkrclose, 3947625Sphk /* read */ noread, 4047625Sphk /* write */ spkrwrite, 4147625Sphk /* ioctl */ spkrioctl, 4247625Sphk /* poll */ nopoll, 4347625Sphk /* mmap */ nommap, 4447625Sphk /* strategy */ nostrategy, 4547625Sphk /* name */ "spkr", 4647625Sphk /* maj */ CDEV_MAJOR, 4747625Sphk /* dump */ nodump, 4847625Sphk /* psize */ nopsize, 4947625Sphk /* flags */ 0, 5047625Sphk}; 5112675Sjulian 5269774Sphkstatic MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer"); 5360038Sphk 544Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE ************************* 554Srgrimes * 564Srgrimes * This section defines a function tone() which causes a tone of given 5719174Sbde * frequency and duration from the ISA console speaker. 584Srgrimes * Another function endtone() is defined to force sound off, and there is 594Srgrimes * also a rest() entry point to do pauses. 604Srgrimes * 614Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and 6219174Sbde * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The 634Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is 644Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired. 654Srgrimes */ 664Srgrimes 674Srgrimes/* 68106323Smdodd * XXX PPI control values should be in a header and used in clock.c. 694Srgrimes */ 70106323Smdodd#ifdef PC98 71106323Smdodd#define PPI_SPKR 0x08 /* turn these PPI bits on to pass sound */ 72106323Smdodd#define PIT_COUNT 0x3fdb /* PIT count address */ 73106323Smdodd 74106323Smdodd#define SPEAKER_ON outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR) 75106323Smdodd#define SPEAKER_OFF outb(IO_PPI, inb(IO_PPI) | PPI_SPKR) 76106323Smdodd#define TIMER_ACQUIRE acquire_timer1(TIMER_SEL1 | TIMER_SQWAVE | TIMER_16BIT) 77106323Smdodd#define TIMER_RELEASE release_timer1() 78106323Smdodd#define SPEAKER_WRITE(val) { \ 79106323Smdodd outb(PIT_COUNT, (val & 0xff)); \ 80106323Smdodd outb(PIT_COUNT, (val >> 8)); \ 81106323Smdodd } 82106323Smdodd#else 834Srgrimes#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */ 844Srgrimes 85106323Smdodd#define SPEAKER_ON outb(IO_PPI, inb(IO_PPI) | PPI_SPKR) 86106323Smdodd#define SPEAKER_OFF outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR) 87106323Smdodd#define TIMER_ACQUIRE acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT) 88106323Smdodd#define TIMER_RELEASE release_timer2() 89106323Smdodd#define SPEAKER_WRITE(val) { \ 90106323Smdodd outb(TIMER_CNTR2, (val & 0xff)); \ 91106323Smdodd outb(TIMER_CNTR2, (val >> 8)); \ 92106323Smdodd } 93106323Smdodd#endif 94106323Smdodd 95766Sache#define SPKRPRI PSOCK 96766Sachestatic char endtone, endrest; 974Srgrimes 9892765Salfredstatic void tone(unsigned int thz, unsigned int ticks); 9992765Salfredstatic void rest(int ticks); 10092765Salfredstatic void playinit(void); 10192765Salfredstatic void playtone(int pitch, int value, int sustain); 10292765Salfredstatic int abs(int n); 10392765Salfredstatic void playstring(char *cp, size_t slen); 10412854Sbde 105766Sache/* emit tone of frequency thz for given number of ticks */ 10617232Sjoergstatic void 10717232Sjoergtone(thz, ticks) 10817232Sjoerg unsigned int thz, ticks; 1094Srgrimes{ 1108288Sdg unsigned int divisor; 1111016Sache int sps; 1124Srgrimes 1138288Sdg if (thz <= 0) 1148288Sdg return; 1158288Sdg 11619174Sbde divisor = timer_freq / thz; 1178288Sdg 1184Srgrimes#ifdef DEBUG 119766Sache (void) printf("tone: thz=%d ticks=%d\n", thz, ticks); 1204Srgrimes#endif /* DEBUG */ 1214Srgrimes 1224Srgrimes /* set timer to generate clicks at given frequency in Hertz */ 12317232Sjoerg sps = splclock(); 1241393Ssos 125106323Smdodd if (TIMER_ACQUIRE) { 1261393Ssos /* enter list of waiting procs ??? */ 12717232Sjoerg splx(sps); 1281393Ssos return; 1291393Ssos } 13017232Sjoerg splx(sps); 13117232Sjoerg disable_intr(); 132106323Smdodd SPEAKER_WRITE(divisor); 13317232Sjoerg enable_intr(); 1344Srgrimes 1354Srgrimes /* turn the speaker on */ 136106323Smdodd SPEAKER_ON; 1374Srgrimes 1384Srgrimes /* 1394Srgrimes * Set timeout to endtone function, then give up the timeslice. 1404Srgrimes * This is so other processes can execute while the tone is being 1414Srgrimes * emitted. 1424Srgrimes */ 1436152Sache if (ticks > 0) 1446152Sache tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks); 145106323Smdodd SPEAKER_OFF; 14617232Sjoerg sps = splclock(); 147106323Smdodd TIMER_RELEASE; 14817232Sjoerg splx(sps); 1494Srgrimes} 1504Srgrimes 1514Srgrimes/* rest for given number of ticks */ 15217232Sjoergstatic void 15317232Sjoergrest(ticks) 15417232Sjoerg int ticks; 1554Srgrimes{ 1564Srgrimes /* 1574Srgrimes * Set timeout to endrest function, then give up the timeslice. 1584Srgrimes * This is so other processes can execute while the rest is being 1594Srgrimes * waited out. 1604Srgrimes */ 1614Srgrimes#ifdef DEBUG 162738Sache (void) printf("rest: %d\n", ticks); 1634Srgrimes#endif /* DEBUG */ 1646152Sache if (ticks > 0) 1656152Sache tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks); 1664Srgrimes} 1674Srgrimes 1684Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 1694Srgrimes * 1704Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 171738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 172738Sache * tracking facility are added. 1734Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible 1744Srgrimes * except possibly at physical block boundaries. 1754Srgrimes */ 1764Srgrimes 1774Srgrimestypedef int bool; 1784Srgrimes#define TRUE 1 1794Srgrimes#define FALSE 0 1804Srgrimes 1814Srgrimes#define dtoi(c) ((c) - '0') 1824Srgrimes 1834Srgrimesstatic int octave; /* currently selected octave */ 1844Srgrimesstatic int whole; /* whole-note time at current tempo, in ticks */ 1854Srgrimesstatic int value; /* whole divisor for note time, quarter note = 1 */ 1864Srgrimesstatic int fill; /* controls spacing of notes */ 1874Srgrimesstatic bool octtrack; /* octave-tracking on? */ 1884Srgrimesstatic bool octprefix; /* override current octave-tracking state? */ 1894Srgrimes 1904Srgrimes/* 1914Srgrimes * Magic number avoidance... 1924Srgrimes */ 1934Srgrimes#define SECS_PER_MIN 60 /* seconds per minute */ 1944Srgrimes#define WHOLE_NOTE 4 /* quarter notes per whole note */ 1954Srgrimes#define MIN_VALUE 64 /* the most we can divide a note by */ 1964Srgrimes#define DFLT_VALUE 4 /* default value (quarter-note) */ 1974Srgrimes#define FILLTIME 8 /* for articulation, break note in parts */ 1984Srgrimes#define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 1994Srgrimes#define NORMAL 7 /* 7/8ths of note interval is filled */ 2004Srgrimes#define LEGATO 8 /* all of note interval is filled */ 2014Srgrimes#define DFLT_OCTAVE 4 /* default octave */ 2024Srgrimes#define MIN_TEMPO 32 /* minimum tempo */ 2034Srgrimes#define DFLT_TEMPO 120 /* default tempo */ 2044Srgrimes#define MAX_TEMPO 255 /* max tempo */ 2054Srgrimes#define NUM_MULT 3 /* numerator of dot multiplier */ 2064Srgrimes#define DENOM_MULT 2 /* denominator of dot multiplier */ 2074Srgrimes 2084Srgrimes/* letter to half-tone: A B C D E F G */ 2094Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 2104Srgrimes 2114Srgrimes/* 2124Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies 2134Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 2144Srgrimes * our octave 0 is standard octave 2. 2154Srgrimes */ 2164Srgrimes#define OCTAVE_NOTES 12 /* semitones per octave */ 2174Srgrimesstatic int pitchtab[] = 2184Srgrimes{ 2194Srgrimes/* C C# D D# E F F# G G# A A# B*/ 2204Srgrimes/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 2214Srgrimes/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 2224Srgrimes/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 2234Srgrimes/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 2244Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 2254Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 2264Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 2274Srgrimes}; 2284Srgrimes 22917232Sjoergstatic void 23017232Sjoergplayinit() 2314Srgrimes{ 2324Srgrimes octave = DFLT_OCTAVE; 233766Sache whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 2344Srgrimes fill = NORMAL; 2354Srgrimes value = DFLT_VALUE; 2364Srgrimes octtrack = FALSE; 2374Srgrimes octprefix = TRUE; /* act as though there was an initial O(n) */ 2384Srgrimes} 2394Srgrimes 2404Srgrimes/* play tone of proper duration for current rhythm signature */ 24117232Sjoergstatic void 24217232Sjoergplaytone(pitch, value, sustain) 24317232Sjoerg int pitch, value, sustain; 2444Srgrimes{ 2454Srgrimes register int sound, silence, snum = 1, sdenom = 1; 2464Srgrimes 2474Srgrimes /* this weirdness avoids floating-point arithmetic */ 2484Srgrimes for (; sustain; sustain--) 2494Srgrimes { 250738Sache /* See the BUGS section in the man page for discussion */ 2514Srgrimes snum *= NUM_MULT; 2524Srgrimes sdenom *= DENOM_MULT; 2534Srgrimes } 2544Srgrimes 2558288Sdg if (value == 0 || sdenom == 0) 2568288Sdg return; 2578288Sdg 2584Srgrimes if (pitch == -1) 2591016Sache rest(whole * snum / (value * sdenom)); 2604Srgrimes else 2614Srgrimes { 2624Srgrimes sound = (whole * snum) / (value * sdenom) 2634Srgrimes - (whole * (FILLTIME - fill)) / (value * FILLTIME); 2644Srgrimes silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 2654Srgrimes 2664Srgrimes#ifdef DEBUG 267738Sache (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 2684Srgrimes pitch, sound, silence); 2694Srgrimes#endif /* DEBUG */ 2704Srgrimes 2711016Sache tone(pitchtab[pitch], sound); 2724Srgrimes if (fill != LEGATO) 2731016Sache rest(silence); 2744Srgrimes } 2754Srgrimes} 2764Srgrimes 27717232Sjoergstatic int 27817232Sjoergabs(n) 27917232Sjoerg int n; 2804Srgrimes{ 2814Srgrimes if (n < 0) 2824Srgrimes return(-n); 2834Srgrimes else 2844Srgrimes return(n); 2854Srgrimes} 2864Srgrimes 2874Srgrimes/* interpret and play an item from a notation string */ 28817232Sjoergstatic void 28917232Sjoergplaystring(cp, slen) 29017232Sjoerg char *cp; 29117232Sjoerg size_t slen; 2924Srgrimes{ 293738Sache int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 2944Srgrimes 2954Srgrimes#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 2964Srgrimes {v = v * 10 + (*++cp - '0'); slen--;} 2974Srgrimes for (; slen--; cp++) 2984Srgrimes { 2994Srgrimes int sustain, timeval, tempo; 3004Srgrimes register char c = toupper(*cp); 3014Srgrimes 3024Srgrimes#ifdef DEBUG 303738Sache (void) printf("playstring: %c (%x)\n", c, c); 3044Srgrimes#endif /* DEBUG */ 3054Srgrimes 3064Srgrimes switch (c) 3074Srgrimes { 3084Srgrimes case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 3094Srgrimes 3104Srgrimes /* compute pitch */ 3114Srgrimes pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 3124Srgrimes 3134Srgrimes /* this may be followed by an accidental sign */ 3144Srgrimes if (cp[1] == '#' || cp[1] == '+') 3154Srgrimes { 3164Srgrimes ++pitch; 3174Srgrimes ++cp; 3184Srgrimes slen--; 3194Srgrimes } 3204Srgrimes else if (cp[1] == '-') 3214Srgrimes { 3224Srgrimes --pitch; 3234Srgrimes ++cp; 3244Srgrimes slen--; 3254Srgrimes } 3264Srgrimes 3274Srgrimes /* 3284Srgrimes * If octave-tracking mode is on, and there has been no octave- 3294Srgrimes * setting prefix, find the version of the current letter note 3304Srgrimes * closest to the last regardless of octave. 3314Srgrimes */ 3324Srgrimes if (octtrack && !octprefix) 3334Srgrimes { 3344Srgrimes if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) 3354Srgrimes { 3364Srgrimes ++octave; 3374Srgrimes pitch += OCTAVE_NOTES; 3384Srgrimes } 3394Srgrimes 3404Srgrimes if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) 3414Srgrimes { 3424Srgrimes --octave; 3434Srgrimes pitch -= OCTAVE_NOTES; 3444Srgrimes } 3454Srgrimes } 3464Srgrimes octprefix = FALSE; 3474Srgrimes lastpitch = pitch; 3484Srgrimes 3494Srgrimes /* ...which may in turn be followed by an override time value */ 3504Srgrimes GETNUM(cp, timeval); 3514Srgrimes if (timeval <= 0 || timeval > MIN_VALUE) 3524Srgrimes timeval = value; 3534Srgrimes 3544Srgrimes /* ...and/or sustain dots */ 3554Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 3564Srgrimes { 3574Srgrimes slen--; 3584Srgrimes sustain++; 3594Srgrimes } 3604Srgrimes 361738Sache /* ...and/or a slur mark */ 362738Sache oldfill = fill; 363738Sache if (cp[1] == '_') 364738Sache { 365738Sache fill = LEGATO; 366738Sache ++cp; 367738Sache slen--; 368738Sache } 369738Sache 3704Srgrimes /* time to emit the actual tone */ 3711016Sache playtone(pitch, timeval, sustain); 372738Sache 373738Sache fill = oldfill; 3744Srgrimes break; 3754Srgrimes 3764Srgrimes case 'O': 3774Srgrimes if (cp[1] == 'N' || cp[1] == 'n') 3784Srgrimes { 3794Srgrimes octprefix = octtrack = FALSE; 3804Srgrimes ++cp; 3814Srgrimes slen--; 3824Srgrimes } 3834Srgrimes else if (cp[1] == 'L' || cp[1] == 'l') 3844Srgrimes { 3854Srgrimes octtrack = TRUE; 3864Srgrimes ++cp; 3874Srgrimes slen--; 3884Srgrimes } 3894Srgrimes else 3904Srgrimes { 3914Srgrimes GETNUM(cp, octave); 3923593Sache if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES) 3934Srgrimes octave = DFLT_OCTAVE; 3944Srgrimes octprefix = TRUE; 3954Srgrimes } 3964Srgrimes break; 3974Srgrimes 3984Srgrimes case '>': 3993593Sache if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1) 4004Srgrimes octave++; 4014Srgrimes octprefix = TRUE; 4024Srgrimes break; 4034Srgrimes 4044Srgrimes case '<': 4054Srgrimes if (octave > 0) 4064Srgrimes octave--; 4074Srgrimes octprefix = TRUE; 4084Srgrimes break; 4094Srgrimes 4104Srgrimes case 'N': 4114Srgrimes GETNUM(cp, pitch); 4124Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 4134Srgrimes { 4144Srgrimes slen--; 4154Srgrimes sustain++; 4164Srgrimes } 417738Sache oldfill = fill; 418738Sache if (cp[1] == '_') 419738Sache { 420738Sache fill = LEGATO; 421738Sache ++cp; 422738Sache slen--; 423738Sache } 4241016Sache playtone(pitch - 1, value, sustain); 425738Sache fill = oldfill; 4264Srgrimes break; 4274Srgrimes 4284Srgrimes case 'L': 4294Srgrimes GETNUM(cp, value); 4304Srgrimes if (value <= 0 || value > MIN_VALUE) 4314Srgrimes value = DFLT_VALUE; 4324Srgrimes break; 4334Srgrimes 4344Srgrimes case 'P': 4354Srgrimes case '~': 4364Srgrimes /* this may be followed by an override time value */ 4374Srgrimes GETNUM(cp, timeval); 4384Srgrimes if (timeval <= 0 || timeval > MIN_VALUE) 4394Srgrimes timeval = value; 4404Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 4414Srgrimes { 4424Srgrimes slen--; 4434Srgrimes sustain++; 4444Srgrimes } 4451016Sache playtone(-1, timeval, sustain); 4464Srgrimes break; 4474Srgrimes 4484Srgrimes case 'T': 4494Srgrimes GETNUM(cp, tempo); 4504Srgrimes if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 4514Srgrimes tempo = DFLT_TEMPO; 452766Sache whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo; 4534Srgrimes break; 4544Srgrimes 4554Srgrimes case 'M': 4564Srgrimes if (cp[1] == 'N' || cp[1] == 'n') 4574Srgrimes { 4584Srgrimes fill = NORMAL; 4594Srgrimes ++cp; 4604Srgrimes slen--; 4614Srgrimes } 4624Srgrimes else if (cp[1] == 'L' || cp[1] == 'l') 4634Srgrimes { 4644Srgrimes fill = LEGATO; 4654Srgrimes ++cp; 4664Srgrimes slen--; 4674Srgrimes } 4684Srgrimes else if (cp[1] == 'S' || cp[1] == 's') 4694Srgrimes { 4704Srgrimes fill = STACCATO; 4714Srgrimes ++cp; 4724Srgrimes slen--; 4734Srgrimes } 4744Srgrimes break; 4754Srgrimes } 4764Srgrimes } 4774Srgrimes} 4784Srgrimes 4794Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE ************************** 4804Srgrimes * 4814Srgrimes * This section implements driver hooks to run playstring() and the tone(), 4824Srgrimes * endtone(), and rest() functions defined above. 4834Srgrimes */ 4844Srgrimes 485738Sachestatic int spkr_active = FALSE; /* exclusion flag */ 48660038Sphkstatic char *spkr_inbuf; /* incoming buf */ 4874Srgrimes 488105224Sphkstatic int 48983366Sjulianspkropen(dev, flags, fmt, td) 49017232Sjoerg dev_t dev; 49117232Sjoerg int flags; 49217232Sjoerg int fmt; 49383366Sjulian struct thread *td; 4944Srgrimes{ 4954Srgrimes#ifdef DEBUG 49649982Sbillf (void) printf("spkropen: entering with dev = %s\n", devtoname(dev)); 4974Srgrimes#endif /* DEBUG */ 4984Srgrimes 4994Srgrimes if (minor(dev) != 0) 5004Srgrimes return(ENXIO); 5014Srgrimes else if (spkr_active) 5024Srgrimes return(EBUSY); 5034Srgrimes else 5044Srgrimes { 505738Sache#ifdef DEBUG 506738Sache (void) printf("spkropen: about to perform play initialization\n"); 507738Sache#endif /* DEBUG */ 5084Srgrimes playinit(); 50960038Sphk spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK); 510738Sache spkr_active = TRUE; 511738Sache return(0); 5124Srgrimes } 5134Srgrimes} 5144Srgrimes 515105224Sphkstatic int 51617232Sjoergspkrwrite(dev, uio, ioflag) 51717232Sjoerg dev_t dev; 51817232Sjoerg struct uio *uio; 51917232Sjoerg int ioflag; 5204Srgrimes{ 5214Srgrimes#ifdef DEBUG 52249982Sbillf printf("spkrwrite: entering with dev = %s, count = %d\n", 52349982Sbillf devtoname(dev), uio->uio_resid); 5244Srgrimes#endif /* DEBUG */ 5254Srgrimes 5264Srgrimes if (minor(dev) != 0) 5274Srgrimes return(ENXIO); 52817803Speter else if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */ 529738Sache return(E2BIG); 5304Srgrimes else 5314Srgrimes { 532738Sache unsigned n; 533738Sache char *cp; 534738Sache int error; 535738Sache 536738Sache n = uio->uio_resid; 53760038Sphk cp = spkr_inbuf; 53817803Speter error = uiomove(cp, n, uio); 53917803Speter if (!error) { 54017803Speter cp[n] = '\0'; 5411016Sache playstring(cp, n); 54217803Speter } 5434Srgrimes return(error); 5444Srgrimes } 5454Srgrimes} 5464Srgrimes 547105224Sphkstatic int 54883366Sjulianspkrclose(dev, flags, fmt, td) 54917232Sjoerg dev_t dev; 55017232Sjoerg int flags; 55117232Sjoerg int fmt; 55283366Sjulian struct thread *td; 5534Srgrimes{ 5544Srgrimes#ifdef DEBUG 55549982Sbillf (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev)); 5564Srgrimes#endif /* DEBUG */ 5574Srgrimes 5584Srgrimes if (minor(dev) != 0) 5594Srgrimes return(ENXIO); 5604Srgrimes else 5614Srgrimes { 562766Sache wakeup((caddr_t)&endtone); 563766Sache wakeup((caddr_t)&endrest); 56460038Sphk free(spkr_inbuf, M_SPKR); 565738Sache spkr_active = FALSE; 566738Sache return(0); 5674Srgrimes } 5684Srgrimes} 5694Srgrimes 570105224Sphkstatic int 57183366Sjulianspkrioctl(dev, cmd, cmdarg, flags, td) 57217232Sjoerg dev_t dev; 57338505Sbde unsigned long cmd; 57417232Sjoerg caddr_t cmdarg; 57517232Sjoerg int flags; 57683366Sjulian struct thread *td; 5774Srgrimes{ 5784Srgrimes#ifdef DEBUG 57950253Sbde (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n", 58050253Sbde devtoname(dev), cmd); 5814Srgrimes#endif /* DEBUG */ 5824Srgrimes 5834Srgrimes if (minor(dev) != 0) 5844Srgrimes return(ENXIO); 5854Srgrimes else if (cmd == SPKRTONE) 5864Srgrimes { 5874Srgrimes tone_t *tp = (tone_t *)cmdarg; 5884Srgrimes 5894Srgrimes if (tp->frequency == 0) 5901016Sache rest(tp->duration); 5914Srgrimes else 5921016Sache tone(tp->frequency, tp->duration); 5931016Sache return 0; 5944Srgrimes } 5954Srgrimes else if (cmd == SPKRTUNE) 5964Srgrimes { 5974Srgrimes tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 5984Srgrimes tone_t ttp; 5994Srgrimes int error; 6004Srgrimes 6014Srgrimes for (; ; tp++) { 6024Srgrimes error = copyin(tp, &ttp, sizeof(tone_t)); 6034Srgrimes if (error) 6044Srgrimes return(error); 6054Srgrimes if (ttp.duration == 0) 6064Srgrimes break; 6074Srgrimes if (ttp.frequency == 0) 6081016Sache rest(ttp.duration); 6094Srgrimes else 6101016Sache tone(ttp.frequency, ttp.duration); 6114Srgrimes } 612738Sache return(0); 6134Srgrimes } 614738Sache return(EINVAL); 6154Srgrimes} 6164Srgrimes 61761994Smsmith/* 61861994Smsmith * Install placeholder to claim the resources owned by the 61961994Smsmith * AT tone generator. 62061994Smsmith */ 621106323Smdoddstatic struct isa_pnp_id speaker_ids[] = { 622106323Smdodd#ifndef PC98 623106323Smdodd { 0x0008d041 /* PNP0800 */, "PC speaker" }, 624106323Smdodd#endif 62561994Smsmith { 0 } 62661994Smsmith}; 62712517Sjulian 628106323Smdoddstatic dev_t speaker_dev; 62989679Swes 63061994Smsmithstatic int 631106323Smdoddspeaker_probe(device_t dev) 63261994Smsmith{ 633106070Smdodd int error; 634106070Smdodd 635106323Smdodd error = ISA_PNP_PROBE(device_get_parent(dev), dev, speaker_ids); 636106070Smdodd 637106070Smdodd /* PnP match */ 638106070Smdodd if (error == 0) 639106070Smdodd return (0); 640106070Smdodd 641106070Smdodd /* No match */ 642106070Smdodd if (error == ENXIO) 643106070Smdodd return (ENXIO); 644106070Smdodd 645106070Smdodd /* Not configured by hints. */ 646106323Smdodd if (strncmp(device_get_name(dev), "speaker", 9)) 647106070Smdodd return (ENXIO); 648106070Smdodd 649106323Smdodd device_set_desc(dev, "PC speaker"); 650106070Smdodd 651106070Smdodd return (0); 65261994Smsmith} 65361994Smsmith 65461994Smsmithstatic int 655106323Smdoddspeaker_attach(device_t dev) 65661994Smsmith{ 657106070Smdodd 658106323Smdodd if (speaker_dev) { 659106070Smdodd device_printf(dev, "Already attached!\n"); 660106070Smdodd return (ENXIO); 661106070Smdodd } 662106070Smdodd 663106323Smdodd speaker_dev = make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 66489679Swes "speaker"); 66589679Swes return (0); 66661994Smsmith} 66761994Smsmith 66889679Swesstatic int 669106323Smdoddspeaker_detach(device_t dev) 67089679Swes{ 671106323Smdodd destroy_dev(speaker_dev); 67289679Swes return (0); 67389679Swes} 67489679Swes 675106323Smdoddstatic device_method_t speaker_methods[] = { 67661994Smsmith /* Device interface */ 677106323Smdodd DEVMETHOD(device_probe, speaker_probe), 678106323Smdodd DEVMETHOD(device_attach, speaker_attach), 679106323Smdodd DEVMETHOD(device_detach, speaker_detach), 68061994Smsmith DEVMETHOD(device_shutdown, bus_generic_shutdown), 68161994Smsmith DEVMETHOD(device_suspend, bus_generic_suspend), 68261994Smsmith DEVMETHOD(device_resume, bus_generic_resume), 68361994Smsmith { 0, 0 } 68461994Smsmith}; 68561994Smsmith 686106323Smdoddstatic driver_t speaker_driver = { 687106323Smdodd "speaker", 688106323Smdodd speaker_methods, 68961994Smsmith 1, /* no softc */ 69061994Smsmith}; 69161994Smsmith 692106323Smdoddstatic devclass_t speaker_devclass; 69361994Smsmith 694106323SmdoddDRIVER_MODULE(speaker, isa, speaker_driver, speaker_devclass, 0, 0); 695106323Smdodd#ifndef PC98 696106323SmdoddDRIVER_MODULE(speaker, acpi, speaker_driver, speaker_devclass, 0, 0); 697106323Smdodd#endif 69861994Smsmith 6994Srgrimes/* spkr.c ends here */ 700