spkr.c revision 111815
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 111815 2003-03-03 12:15:54Z phk $ 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 = { 37111815Sphk .d_open = spkropen, 38111815Sphk .d_close = spkrclose, 39111815Sphk .d_write = spkrwrite, 40111815Sphk .d_ioctl = spkrioctl, 41111815Sphk .d_name = "spkr", 42111815Sphk .d_maj = CDEV_MAJOR, 4347625Sphk}; 4412675Sjulian 4569774Sphkstatic MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer"); 4660038Sphk 474Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE ************************* 484Srgrimes * 494Srgrimes * This section defines a function tone() which causes a tone of given 5019174Sbde * frequency and duration from the ISA console speaker. 514Srgrimes * Another function endtone() is defined to force sound off, and there is 524Srgrimes * also a rest() entry point to do pauses. 534Srgrimes * 544Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and 5519174Sbde * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The 564Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is 574Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired. 584Srgrimes */ 594Srgrimes 604Srgrimes/* 61106323Smdodd * XXX PPI control values should be in a header and used in clock.c. 624Srgrimes */ 63106323Smdodd#ifdef PC98 64106323Smdodd#define PPI_SPKR 0x08 /* turn these PPI bits on to pass sound */ 65106323Smdodd#define PIT_COUNT 0x3fdb /* PIT count address */ 66106323Smdodd 67106323Smdodd#define SPEAKER_ON outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR) 68106323Smdodd#define SPEAKER_OFF outb(IO_PPI, inb(IO_PPI) | PPI_SPKR) 69106323Smdodd#define TIMER_ACQUIRE acquire_timer1(TIMER_SEL1 | TIMER_SQWAVE | TIMER_16BIT) 70106323Smdodd#define TIMER_RELEASE release_timer1() 71106323Smdodd#define SPEAKER_WRITE(val) { \ 72106323Smdodd outb(PIT_COUNT, (val & 0xff)); \ 73106323Smdodd outb(PIT_COUNT, (val >> 8)); \ 74106323Smdodd } 75106323Smdodd#else 764Srgrimes#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */ 774Srgrimes 78106323Smdodd#define SPEAKER_ON outb(IO_PPI, inb(IO_PPI) | PPI_SPKR) 79106323Smdodd#define SPEAKER_OFF outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR) 80106323Smdodd#define TIMER_ACQUIRE acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT) 81106323Smdodd#define TIMER_RELEASE release_timer2() 82106323Smdodd#define SPEAKER_WRITE(val) { \ 83106323Smdodd outb(TIMER_CNTR2, (val & 0xff)); \ 84106323Smdodd outb(TIMER_CNTR2, (val >> 8)); \ 85106323Smdodd } 86106323Smdodd#endif 87106323Smdodd 88766Sache#define SPKRPRI PSOCK 89766Sachestatic char endtone, endrest; 904Srgrimes 9192765Salfredstatic void tone(unsigned int thz, unsigned int ticks); 9292765Salfredstatic void rest(int ticks); 9392765Salfredstatic void playinit(void); 9492765Salfredstatic void playtone(int pitch, int value, int sustain); 9592765Salfredstatic void playstring(char *cp, size_t slen); 9612854Sbde 97766Sache/* emit tone of frequency thz for given number of ticks */ 9817232Sjoergstatic void 9917232Sjoergtone(thz, ticks) 10017232Sjoerg unsigned int thz, ticks; 1014Srgrimes{ 1028288Sdg unsigned int divisor; 1031016Sache int sps; 1044Srgrimes 1058288Sdg if (thz <= 0) 1068288Sdg return; 1078288Sdg 10819174Sbde divisor = timer_freq / thz; 1098288Sdg 1104Srgrimes#ifdef DEBUG 111766Sache (void) printf("tone: thz=%d ticks=%d\n", thz, ticks); 1124Srgrimes#endif /* DEBUG */ 1134Srgrimes 1144Srgrimes /* set timer to generate clicks at given frequency in Hertz */ 11517232Sjoerg sps = splclock(); 1161393Ssos 117106323Smdodd if (TIMER_ACQUIRE) { 1181393Ssos /* enter list of waiting procs ??? */ 11917232Sjoerg splx(sps); 1201393Ssos return; 1211393Ssos } 12217232Sjoerg splx(sps); 12317232Sjoerg disable_intr(); 124106323Smdodd SPEAKER_WRITE(divisor); 12517232Sjoerg enable_intr(); 1264Srgrimes 1274Srgrimes /* turn the speaker on */ 128106323Smdodd SPEAKER_ON; 1294Srgrimes 1304Srgrimes /* 1314Srgrimes * Set timeout to endtone function, then give up the timeslice. 1324Srgrimes * This is so other processes can execute while the tone is being 1334Srgrimes * emitted. 1344Srgrimes */ 1356152Sache if (ticks > 0) 136111748Sdes tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", ticks); 137106323Smdodd SPEAKER_OFF; 13817232Sjoerg sps = splclock(); 139106323Smdodd TIMER_RELEASE; 14017232Sjoerg splx(sps); 1414Srgrimes} 1424Srgrimes 1434Srgrimes/* rest for given number of ticks */ 14417232Sjoergstatic void 14517232Sjoergrest(ticks) 14617232Sjoerg int ticks; 1474Srgrimes{ 1484Srgrimes /* 1494Srgrimes * Set timeout to endrest function, then give up the timeslice. 1504Srgrimes * This is so other processes can execute while the rest is being 1514Srgrimes * waited out. 1524Srgrimes */ 1534Srgrimes#ifdef DEBUG 154738Sache (void) printf("rest: %d\n", ticks); 1554Srgrimes#endif /* DEBUG */ 1566152Sache if (ticks > 0) 157111748Sdes tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", ticks); 1584Srgrimes} 1594Srgrimes 1604Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 1614Srgrimes * 1624Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 163738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 164738Sache * tracking facility are added. 1654Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible 1664Srgrimes * except possibly at physical block boundaries. 1674Srgrimes */ 1684Srgrimes 1694Srgrimestypedef int bool; 1704Srgrimes#define TRUE 1 1714Srgrimes#define FALSE 0 1724Srgrimes 1734Srgrimes#define dtoi(c) ((c) - '0') 1744Srgrimes 1754Srgrimesstatic int octave; /* currently selected octave */ 1764Srgrimesstatic int whole; /* whole-note time at current tempo, in ticks */ 1774Srgrimesstatic int value; /* whole divisor for note time, quarter note = 1 */ 1784Srgrimesstatic int fill; /* controls spacing of notes */ 1794Srgrimesstatic bool octtrack; /* octave-tracking on? */ 1804Srgrimesstatic bool octprefix; /* override current octave-tracking state? */ 1814Srgrimes 1824Srgrimes/* 1834Srgrimes * Magic number avoidance... 1844Srgrimes */ 1854Srgrimes#define SECS_PER_MIN 60 /* seconds per minute */ 1864Srgrimes#define WHOLE_NOTE 4 /* quarter notes per whole note */ 1874Srgrimes#define MIN_VALUE 64 /* the most we can divide a note by */ 1884Srgrimes#define DFLT_VALUE 4 /* default value (quarter-note) */ 1894Srgrimes#define FILLTIME 8 /* for articulation, break note in parts */ 1904Srgrimes#define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 1914Srgrimes#define NORMAL 7 /* 7/8ths of note interval is filled */ 1924Srgrimes#define LEGATO 8 /* all of note interval is filled */ 1934Srgrimes#define DFLT_OCTAVE 4 /* default octave */ 1944Srgrimes#define MIN_TEMPO 32 /* minimum tempo */ 1954Srgrimes#define DFLT_TEMPO 120 /* default tempo */ 1964Srgrimes#define MAX_TEMPO 255 /* max tempo */ 1974Srgrimes#define NUM_MULT 3 /* numerator of dot multiplier */ 1984Srgrimes#define DENOM_MULT 2 /* denominator of dot multiplier */ 1994Srgrimes 2004Srgrimes/* letter to half-tone: A B C D E F G */ 2014Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 2024Srgrimes 2034Srgrimes/* 2044Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies 2054Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 2064Srgrimes * our octave 0 is standard octave 2. 2074Srgrimes */ 2084Srgrimes#define OCTAVE_NOTES 12 /* semitones per octave */ 2094Srgrimesstatic int pitchtab[] = 2104Srgrimes{ 2114Srgrimes/* C C# D D# E F F# G G# A A# B*/ 2124Srgrimes/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 2134Srgrimes/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 2144Srgrimes/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 2154Srgrimes/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 2164Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 2174Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 2184Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 2194Srgrimes}; 2204Srgrimes 22117232Sjoergstatic void 22217232Sjoergplayinit() 2234Srgrimes{ 2244Srgrimes octave = DFLT_OCTAVE; 225766Sache whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 2264Srgrimes fill = NORMAL; 2274Srgrimes value = DFLT_VALUE; 2284Srgrimes octtrack = FALSE; 2294Srgrimes octprefix = TRUE; /* act as though there was an initial O(n) */ 2304Srgrimes} 2314Srgrimes 2324Srgrimes/* play tone of proper duration for current rhythm signature */ 23317232Sjoergstatic void 23417232Sjoergplaytone(pitch, value, sustain) 23517232Sjoerg int pitch, value, sustain; 2364Srgrimes{ 2374Srgrimes register int sound, silence, snum = 1, sdenom = 1; 2384Srgrimes 2394Srgrimes /* this weirdness avoids floating-point arithmetic */ 2404Srgrimes for (; sustain; sustain--) 2414Srgrimes { 242738Sache /* See the BUGS section in the man page for discussion */ 2434Srgrimes snum *= NUM_MULT; 2444Srgrimes sdenom *= DENOM_MULT; 2454Srgrimes } 2464Srgrimes 2478288Sdg if (value == 0 || sdenom == 0) 2488288Sdg return; 2498288Sdg 2504Srgrimes if (pitch == -1) 2511016Sache rest(whole * snum / (value * sdenom)); 2524Srgrimes else 2534Srgrimes { 2544Srgrimes sound = (whole * snum) / (value * sdenom) 2554Srgrimes - (whole * (FILLTIME - fill)) / (value * FILLTIME); 2564Srgrimes silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 2574Srgrimes 2584Srgrimes#ifdef DEBUG 259738Sache (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 2604Srgrimes pitch, sound, silence); 2614Srgrimes#endif /* DEBUG */ 2624Srgrimes 2631016Sache tone(pitchtab[pitch], sound); 2644Srgrimes if (fill != LEGATO) 2651016Sache rest(silence); 2664Srgrimes } 2674Srgrimes} 2684Srgrimes 2694Srgrimes/* interpret and play an item from a notation string */ 27017232Sjoergstatic void 27117232Sjoergplaystring(cp, slen) 27217232Sjoerg char *cp; 27317232Sjoerg size_t slen; 2744Srgrimes{ 275738Sache int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 2764Srgrimes 2774Srgrimes#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 2784Srgrimes {v = v * 10 + (*++cp - '0'); slen--;} 2794Srgrimes for (; slen--; cp++) 2804Srgrimes { 2814Srgrimes int sustain, timeval, tempo; 2824Srgrimes register char c = toupper(*cp); 2834Srgrimes 2844Srgrimes#ifdef DEBUG 285738Sache (void) printf("playstring: %c (%x)\n", c, c); 2864Srgrimes#endif /* DEBUG */ 2874Srgrimes 2884Srgrimes switch (c) 2894Srgrimes { 2904Srgrimes case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 2914Srgrimes 2924Srgrimes /* compute pitch */ 2934Srgrimes pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 2944Srgrimes 2954Srgrimes /* this may be followed by an accidental sign */ 2964Srgrimes if (cp[1] == '#' || cp[1] == '+') 2974Srgrimes { 2984Srgrimes ++pitch; 2994Srgrimes ++cp; 3004Srgrimes slen--; 3014Srgrimes } 3024Srgrimes else if (cp[1] == '-') 3034Srgrimes { 3044Srgrimes --pitch; 3054Srgrimes ++cp; 3064Srgrimes slen--; 3074Srgrimes } 3084Srgrimes 3094Srgrimes /* 3104Srgrimes * If octave-tracking mode is on, and there has been no octave- 3114Srgrimes * setting prefix, find the version of the current letter note 3124Srgrimes * closest to the last regardless of octave. 3134Srgrimes */ 3144Srgrimes if (octtrack && !octprefix) 3154Srgrimes { 3164Srgrimes if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) 3174Srgrimes { 3184Srgrimes ++octave; 3194Srgrimes pitch += OCTAVE_NOTES; 3204Srgrimes } 3214Srgrimes 3224Srgrimes if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) 3234Srgrimes { 3244Srgrimes --octave; 3254Srgrimes pitch -= OCTAVE_NOTES; 3264Srgrimes } 3274Srgrimes } 3284Srgrimes octprefix = FALSE; 3294Srgrimes lastpitch = pitch; 3304Srgrimes 3314Srgrimes /* ...which may in turn be followed by an override time value */ 3324Srgrimes GETNUM(cp, timeval); 3334Srgrimes if (timeval <= 0 || timeval > MIN_VALUE) 3344Srgrimes timeval = value; 3354Srgrimes 3364Srgrimes /* ...and/or sustain dots */ 3374Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 3384Srgrimes { 3394Srgrimes slen--; 3404Srgrimes sustain++; 3414Srgrimes } 3424Srgrimes 343738Sache /* ...and/or a slur mark */ 344738Sache oldfill = fill; 345738Sache if (cp[1] == '_') 346738Sache { 347738Sache fill = LEGATO; 348738Sache ++cp; 349738Sache slen--; 350738Sache } 351738Sache 3524Srgrimes /* time to emit the actual tone */ 3531016Sache playtone(pitch, timeval, sustain); 354738Sache 355738Sache fill = oldfill; 3564Srgrimes break; 3574Srgrimes 3584Srgrimes case 'O': 3594Srgrimes if (cp[1] == 'N' || cp[1] == 'n') 3604Srgrimes { 3614Srgrimes octprefix = octtrack = FALSE; 3624Srgrimes ++cp; 3634Srgrimes slen--; 3644Srgrimes } 3654Srgrimes else if (cp[1] == 'L' || cp[1] == 'l') 3664Srgrimes { 3674Srgrimes octtrack = TRUE; 3684Srgrimes ++cp; 3694Srgrimes slen--; 3704Srgrimes } 3714Srgrimes else 3724Srgrimes { 3734Srgrimes GETNUM(cp, octave); 3743593Sache if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES) 3754Srgrimes octave = DFLT_OCTAVE; 3764Srgrimes octprefix = TRUE; 3774Srgrimes } 3784Srgrimes break; 3794Srgrimes 3804Srgrimes case '>': 3813593Sache if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1) 3824Srgrimes octave++; 3834Srgrimes octprefix = TRUE; 3844Srgrimes break; 3854Srgrimes 3864Srgrimes case '<': 3874Srgrimes if (octave > 0) 3884Srgrimes octave--; 3894Srgrimes octprefix = TRUE; 3904Srgrimes break; 3914Srgrimes 3924Srgrimes case 'N': 3934Srgrimes GETNUM(cp, pitch); 3944Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 3954Srgrimes { 3964Srgrimes slen--; 3974Srgrimes sustain++; 3984Srgrimes } 399738Sache oldfill = fill; 400738Sache if (cp[1] == '_') 401738Sache { 402738Sache fill = LEGATO; 403738Sache ++cp; 404738Sache slen--; 405738Sache } 4061016Sache playtone(pitch - 1, value, sustain); 407738Sache fill = oldfill; 4084Srgrimes break; 4094Srgrimes 4104Srgrimes case 'L': 4114Srgrimes GETNUM(cp, value); 4124Srgrimes if (value <= 0 || value > MIN_VALUE) 4134Srgrimes value = DFLT_VALUE; 4144Srgrimes break; 4154Srgrimes 4164Srgrimes case 'P': 4174Srgrimes case '~': 4184Srgrimes /* this may be followed by an override time value */ 4194Srgrimes GETNUM(cp, timeval); 4204Srgrimes if (timeval <= 0 || timeval > MIN_VALUE) 4214Srgrimes timeval = value; 4224Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 4234Srgrimes { 4244Srgrimes slen--; 4254Srgrimes sustain++; 4264Srgrimes } 4271016Sache playtone(-1, timeval, sustain); 4284Srgrimes break; 4294Srgrimes 4304Srgrimes case 'T': 4314Srgrimes GETNUM(cp, tempo); 4324Srgrimes if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 4334Srgrimes tempo = DFLT_TEMPO; 434766Sache whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo; 4354Srgrimes break; 4364Srgrimes 4374Srgrimes case 'M': 4384Srgrimes if (cp[1] == 'N' || cp[1] == 'n') 4394Srgrimes { 4404Srgrimes fill = NORMAL; 4414Srgrimes ++cp; 4424Srgrimes slen--; 4434Srgrimes } 4444Srgrimes else if (cp[1] == 'L' || cp[1] == 'l') 4454Srgrimes { 4464Srgrimes fill = LEGATO; 4474Srgrimes ++cp; 4484Srgrimes slen--; 4494Srgrimes } 4504Srgrimes else if (cp[1] == 'S' || cp[1] == 's') 4514Srgrimes { 4524Srgrimes fill = STACCATO; 4534Srgrimes ++cp; 4544Srgrimes slen--; 4554Srgrimes } 4564Srgrimes break; 4574Srgrimes } 4584Srgrimes } 4594Srgrimes} 4604Srgrimes 4614Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE ************************** 4624Srgrimes * 4634Srgrimes * This section implements driver hooks to run playstring() and the tone(), 4644Srgrimes * endtone(), and rest() functions defined above. 4654Srgrimes */ 4664Srgrimes 467738Sachestatic int spkr_active = FALSE; /* exclusion flag */ 46860038Sphkstatic char *spkr_inbuf; /* incoming buf */ 4694Srgrimes 470105224Sphkstatic int 47183366Sjulianspkropen(dev, flags, fmt, td) 47217232Sjoerg dev_t dev; 47317232Sjoerg int flags; 47417232Sjoerg int fmt; 47583366Sjulian struct thread *td; 4764Srgrimes{ 4774Srgrimes#ifdef DEBUG 47849982Sbillf (void) printf("spkropen: entering with dev = %s\n", devtoname(dev)); 4794Srgrimes#endif /* DEBUG */ 4804Srgrimes 4814Srgrimes if (minor(dev) != 0) 4824Srgrimes return(ENXIO); 4834Srgrimes else if (spkr_active) 4844Srgrimes return(EBUSY); 4854Srgrimes else 4864Srgrimes { 487738Sache#ifdef DEBUG 488738Sache (void) printf("spkropen: about to perform play initialization\n"); 489738Sache#endif /* DEBUG */ 4904Srgrimes playinit(); 491111119Simp spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK); 492738Sache spkr_active = TRUE; 493738Sache return(0); 4944Srgrimes } 4954Srgrimes} 4964Srgrimes 497105224Sphkstatic int 49817232Sjoergspkrwrite(dev, uio, ioflag) 49917232Sjoerg dev_t dev; 50017232Sjoerg struct uio *uio; 50117232Sjoerg int ioflag; 5024Srgrimes{ 5034Srgrimes#ifdef DEBUG 50449982Sbillf printf("spkrwrite: entering with dev = %s, count = %d\n", 50549982Sbillf devtoname(dev), uio->uio_resid); 5064Srgrimes#endif /* DEBUG */ 5074Srgrimes 5084Srgrimes if (minor(dev) != 0) 5094Srgrimes return(ENXIO); 51017803Speter else if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */ 511738Sache return(E2BIG); 5124Srgrimes else 5134Srgrimes { 514738Sache unsigned n; 515738Sache char *cp; 516738Sache int error; 517738Sache 518738Sache n = uio->uio_resid; 51960038Sphk cp = spkr_inbuf; 52017803Speter error = uiomove(cp, n, uio); 52117803Speter if (!error) { 52217803Speter cp[n] = '\0'; 5231016Sache playstring(cp, n); 52417803Speter } 5254Srgrimes return(error); 5264Srgrimes } 5274Srgrimes} 5284Srgrimes 529105224Sphkstatic int 53083366Sjulianspkrclose(dev, flags, fmt, td) 53117232Sjoerg dev_t dev; 53217232Sjoerg int flags; 53317232Sjoerg int fmt; 53483366Sjulian struct thread *td; 5354Srgrimes{ 5364Srgrimes#ifdef DEBUG 53749982Sbillf (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev)); 5384Srgrimes#endif /* DEBUG */ 5394Srgrimes 5404Srgrimes if (minor(dev) != 0) 5414Srgrimes return(ENXIO); 5424Srgrimes else 5434Srgrimes { 544111748Sdes wakeup(&endtone); 545111748Sdes wakeup(&endrest); 54660038Sphk free(spkr_inbuf, M_SPKR); 547738Sache spkr_active = FALSE; 548738Sache return(0); 5494Srgrimes } 5504Srgrimes} 5514Srgrimes 552105224Sphkstatic int 55383366Sjulianspkrioctl(dev, cmd, cmdarg, flags, td) 55417232Sjoerg dev_t dev; 55538505Sbde unsigned long cmd; 55617232Sjoerg caddr_t cmdarg; 55717232Sjoerg int flags; 55883366Sjulian struct thread *td; 5594Srgrimes{ 5604Srgrimes#ifdef DEBUG 56150253Sbde (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n", 56250253Sbde devtoname(dev), cmd); 5634Srgrimes#endif /* DEBUG */ 5644Srgrimes 5654Srgrimes if (minor(dev) != 0) 5664Srgrimes return(ENXIO); 5674Srgrimes else if (cmd == SPKRTONE) 5684Srgrimes { 5694Srgrimes tone_t *tp = (tone_t *)cmdarg; 5704Srgrimes 5714Srgrimes if (tp->frequency == 0) 5721016Sache rest(tp->duration); 5734Srgrimes else 5741016Sache tone(tp->frequency, tp->duration); 5751016Sache return 0; 5764Srgrimes } 5774Srgrimes else if (cmd == SPKRTUNE) 5784Srgrimes { 5794Srgrimes tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 5804Srgrimes tone_t ttp; 5814Srgrimes int error; 5824Srgrimes 5834Srgrimes for (; ; tp++) { 5844Srgrimes error = copyin(tp, &ttp, sizeof(tone_t)); 5854Srgrimes if (error) 5864Srgrimes return(error); 5874Srgrimes if (ttp.duration == 0) 5884Srgrimes break; 5894Srgrimes if (ttp.frequency == 0) 5901016Sache rest(ttp.duration); 5914Srgrimes else 5921016Sache tone(ttp.frequency, ttp.duration); 5934Srgrimes } 594738Sache return(0); 5954Srgrimes } 596738Sache return(EINVAL); 5974Srgrimes} 5984Srgrimes 59961994Smsmith/* 60061994Smsmith * Install placeholder to claim the resources owned by the 60161994Smsmith * AT tone generator. 60261994Smsmith */ 603106323Smdoddstatic struct isa_pnp_id speaker_ids[] = { 604106323Smdodd#ifndef PC98 605106323Smdodd { 0x0008d041 /* PNP0800 */, "PC speaker" }, 606106323Smdodd#endif 60761994Smsmith { 0 } 60861994Smsmith}; 60912517Sjulian 610106323Smdoddstatic dev_t speaker_dev; 61189679Swes 61261994Smsmithstatic int 613106323Smdoddspeaker_probe(device_t dev) 61461994Smsmith{ 615106070Smdodd int error; 616106070Smdodd 617106323Smdodd error = ISA_PNP_PROBE(device_get_parent(dev), dev, speaker_ids); 618106070Smdodd 619106070Smdodd /* PnP match */ 620106070Smdodd if (error == 0) 621106070Smdodd return (0); 622106070Smdodd 623106070Smdodd /* No match */ 624106070Smdodd if (error == ENXIO) 625106070Smdodd return (ENXIO); 626106070Smdodd 627106070Smdodd /* Not configured by hints. */ 628106323Smdodd if (strncmp(device_get_name(dev), "speaker", 9)) 629106070Smdodd return (ENXIO); 630106070Smdodd 631106323Smdodd device_set_desc(dev, "PC speaker"); 632106070Smdodd 633106070Smdodd return (0); 63461994Smsmith} 63561994Smsmith 63661994Smsmithstatic int 637106323Smdoddspeaker_attach(device_t dev) 63861994Smsmith{ 639106070Smdodd 640106323Smdodd if (speaker_dev) { 641106070Smdodd device_printf(dev, "Already attached!\n"); 642106070Smdodd return (ENXIO); 643106070Smdodd } 644106070Smdodd 645106323Smdodd speaker_dev = make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 64689679Swes "speaker"); 64789679Swes return (0); 64861994Smsmith} 64961994Smsmith 65089679Swesstatic int 651106323Smdoddspeaker_detach(device_t dev) 65289679Swes{ 653106323Smdodd destroy_dev(speaker_dev); 65489679Swes return (0); 65589679Swes} 65689679Swes 657106323Smdoddstatic device_method_t speaker_methods[] = { 65861994Smsmith /* Device interface */ 659106323Smdodd DEVMETHOD(device_probe, speaker_probe), 660106323Smdodd DEVMETHOD(device_attach, speaker_attach), 661106323Smdodd DEVMETHOD(device_detach, speaker_detach), 66261994Smsmith DEVMETHOD(device_shutdown, bus_generic_shutdown), 66361994Smsmith DEVMETHOD(device_suspend, bus_generic_suspend), 66461994Smsmith DEVMETHOD(device_resume, bus_generic_resume), 66561994Smsmith { 0, 0 } 66661994Smsmith}; 66761994Smsmith 668106323Smdoddstatic driver_t speaker_driver = { 669106323Smdodd "speaker", 670106323Smdodd speaker_methods, 67161994Smsmith 1, /* no softc */ 67261994Smsmith}; 67361994Smsmith 674106323Smdoddstatic devclass_t speaker_devclass; 67561994Smsmith 676106323SmdoddDRIVER_MODULE(speaker, isa, speaker_driver, speaker_devclass, 0, 0); 677106323Smdodd#ifndef PC98 678106323SmdoddDRIVER_MODULE(speaker, acpi, speaker_driver, speaker_devclass, 0, 0); 679106323Smdodd#endif 68061994Smsmith 6814Srgrimes/* spkr.c ends here */ 682