spkr.c revision 177648
1139790Simp/*- 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 74Srgrimes */ 84Srgrimes 9115703Sobrien#include <sys/cdefs.h> 10115703Sobrien__FBSDID("$FreeBSD: head/sys/dev/speaker/spkr.c 177648 2008-03-26 21:33:41Z phk $"); 11115703Sobrien 122056Swollman#include <sys/param.h> 132056Swollman#include <sys/systm.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> 207090Sbde#include <machine/clock.h> 21152306Sru#include <dev/speaker/speaker.h> 224Srgrimes 2312675Sjulianstatic d_open_t spkropen; 2412675Sjulianstatic d_close_t spkrclose; 2512675Sjulianstatic d_write_t spkrwrite; 2612675Sjulianstatic d_ioctl_t spkrioctl; 2712502Sjulian 2847625Sphkstatic struct cdevsw spkr_cdevsw = { 29126080Sphk .d_version = D_VERSION, 30126080Sphk .d_flags = D_NEEDGIANT, 31111815Sphk .d_open = spkropen, 32111815Sphk .d_close = spkrclose, 33111815Sphk .d_write = spkrwrite, 34111815Sphk .d_ioctl = spkrioctl, 35111815Sphk .d_name = "spkr", 3647625Sphk}; 3712675Sjulian 3869774Sphkstatic MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer"); 3960038Sphk 404Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE ************************* 414Srgrimes * 424Srgrimes * This section defines a function tone() which causes a tone of given 4319174Sbde * frequency and duration from the ISA console speaker. 444Srgrimes * Another function endtone() is defined to force sound off, and there is 454Srgrimes * also a rest() entry point to do pauses. 464Srgrimes * 474Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and 4819174Sbde * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The 494Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is 504Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired. 514Srgrimes */ 524Srgrimes 53766Sache#define SPKRPRI PSOCK 54766Sachestatic char endtone, endrest; 554Srgrimes 56170278Sbrianstatic void tone(unsigned int thz, unsigned int centisecs); 57170278Sbrianstatic void rest(int centisecs); 5892765Salfredstatic void playinit(void); 5992765Salfredstatic void playtone(int pitch, int value, int sustain); 6092765Salfredstatic void playstring(char *cp, size_t slen); 6112854Sbde 62170278Sbrian/* emit tone of frequency thz for given number of centisecs */ 6317232Sjoergstatic void 64170278Sbriantone(thz, centisecs) 65170278Sbrian unsigned int thz, centisecs; 664Srgrimes{ 67170278Sbrian int sps, timo; 684Srgrimes 698288Sdg if (thz <= 0) 708288Sdg return; 718288Sdg 724Srgrimes#ifdef DEBUG 73170278Sbrian (void) printf("tone: thz=%d centisecs=%d\n", thz, centisecs); 744Srgrimes#endif /* DEBUG */ 754Srgrimes 764Srgrimes /* set timer to generate clicks at given frequency in Hertz */ 7717232Sjoerg sps = splclock(); 781393Ssos 79146211Snyan if (timer_spkr_acquire()) { 801393Ssos /* enter list of waiting procs ??? */ 8117232Sjoerg splx(sps); 821393Ssos return; 831393Ssos } 8417232Sjoerg splx(sps); 8517232Sjoerg disable_intr(); 86177642Sphk timer_spkr_setfreq(thz); 8717232Sjoerg enable_intr(); 884Srgrimes 894Srgrimes /* 904Srgrimes * Set timeout to endtone function, then give up the timeslice. 914Srgrimes * This is so other processes can execute while the tone is being 924Srgrimes * emitted. 934Srgrimes */ 94170278Sbrian timo = centisecs * hz / 100; 95170278Sbrian if (timo > 0) 96170278Sbrian tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", timo); 9717232Sjoerg sps = splclock(); 98146211Snyan timer_spkr_release(); 9917232Sjoerg splx(sps); 1004Srgrimes} 1014Srgrimes 102170278Sbrian/* rest for given number of centisecs */ 10317232Sjoergstatic void 104170278Sbrianrest(centisecs) 105170278Sbrian int centisecs; 1064Srgrimes{ 107170278Sbrian int timo; 108170278Sbrian 1094Srgrimes /* 1104Srgrimes * Set timeout to endrest function, then give up the timeslice. 1114Srgrimes * This is so other processes can execute while the rest is being 1124Srgrimes * waited out. 1134Srgrimes */ 1144Srgrimes#ifdef DEBUG 115170278Sbrian (void) printf("rest: %d\n", centisecs); 1164Srgrimes#endif /* DEBUG */ 117170278Sbrian timo = centisecs * hz / 100; 118170278Sbrian if (timo > 0) 119170278Sbrian tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", timo); 1204Srgrimes} 1214Srgrimes 1224Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 1234Srgrimes * 1244Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 125738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 126738Sache * tracking facility are added. 1274Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible 1284Srgrimes * except possibly at physical block boundaries. 1294Srgrimes */ 1304Srgrimes 1314Srgrimestypedef int bool; 1324Srgrimes#define TRUE 1 1334Srgrimes#define FALSE 0 1344Srgrimes 1354Srgrimes#define dtoi(c) ((c) - '0') 1364Srgrimes 1374Srgrimesstatic int octave; /* currently selected octave */ 1384Srgrimesstatic int whole; /* whole-note time at current tempo, in ticks */ 1394Srgrimesstatic int value; /* whole divisor for note time, quarter note = 1 */ 1404Srgrimesstatic int fill; /* controls spacing of notes */ 1414Srgrimesstatic bool octtrack; /* octave-tracking on? */ 1424Srgrimesstatic bool octprefix; /* override current octave-tracking state? */ 1434Srgrimes 1444Srgrimes/* 1454Srgrimes * Magic number avoidance... 1464Srgrimes */ 1474Srgrimes#define SECS_PER_MIN 60 /* seconds per minute */ 1484Srgrimes#define WHOLE_NOTE 4 /* quarter notes per whole note */ 1494Srgrimes#define MIN_VALUE 64 /* the most we can divide a note by */ 1504Srgrimes#define DFLT_VALUE 4 /* default value (quarter-note) */ 1514Srgrimes#define FILLTIME 8 /* for articulation, break note in parts */ 1524Srgrimes#define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 1534Srgrimes#define NORMAL 7 /* 7/8ths of note interval is filled */ 1544Srgrimes#define LEGATO 8 /* all of note interval is filled */ 1554Srgrimes#define DFLT_OCTAVE 4 /* default octave */ 1564Srgrimes#define MIN_TEMPO 32 /* minimum tempo */ 1574Srgrimes#define DFLT_TEMPO 120 /* default tempo */ 1584Srgrimes#define MAX_TEMPO 255 /* max tempo */ 1594Srgrimes#define NUM_MULT 3 /* numerator of dot multiplier */ 1604Srgrimes#define DENOM_MULT 2 /* denominator of dot multiplier */ 1614Srgrimes 1624Srgrimes/* letter to half-tone: A B C D E F G */ 1634Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 1644Srgrimes 1654Srgrimes/* 1664Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies 1674Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 1684Srgrimes * our octave 0 is standard octave 2. 1694Srgrimes */ 1704Srgrimes#define OCTAVE_NOTES 12 /* semitones per octave */ 1714Srgrimesstatic int pitchtab[] = 1724Srgrimes{ 1734Srgrimes/* C C# D D# E F F# G G# A A# B*/ 1744Srgrimes/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 1754Srgrimes/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 1764Srgrimes/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 1774Srgrimes/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1784Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 1794Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 1804Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 1814Srgrimes}; 1824Srgrimes 18317232Sjoergstatic void 18417232Sjoergplayinit() 1854Srgrimes{ 1864Srgrimes octave = DFLT_OCTAVE; 187170280Sbrian whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 1884Srgrimes fill = NORMAL; 1894Srgrimes value = DFLT_VALUE; 1904Srgrimes octtrack = FALSE; 1914Srgrimes octprefix = TRUE; /* act as though there was an initial O(n) */ 1924Srgrimes} 1934Srgrimes 1944Srgrimes/* play tone of proper duration for current rhythm signature */ 19517232Sjoergstatic void 19617232Sjoergplaytone(pitch, value, sustain) 19717232Sjoerg int pitch, value, sustain; 1984Srgrimes{ 1994Srgrimes register int sound, silence, snum = 1, sdenom = 1; 2004Srgrimes 2014Srgrimes /* this weirdness avoids floating-point arithmetic */ 2024Srgrimes for (; sustain; sustain--) 2034Srgrimes { 204738Sache /* See the BUGS section in the man page for discussion */ 2054Srgrimes snum *= NUM_MULT; 2064Srgrimes sdenom *= DENOM_MULT; 2074Srgrimes } 2084Srgrimes 2098288Sdg if (value == 0 || sdenom == 0) 2108288Sdg return; 2118288Sdg 2124Srgrimes if (pitch == -1) 2131016Sache rest(whole * snum / (value * sdenom)); 2144Srgrimes else 2154Srgrimes { 2164Srgrimes sound = (whole * snum) / (value * sdenom) 2174Srgrimes - (whole * (FILLTIME - fill)) / (value * FILLTIME); 2184Srgrimes silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 2194Srgrimes 2204Srgrimes#ifdef DEBUG 221738Sache (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 2224Srgrimes pitch, sound, silence); 2234Srgrimes#endif /* DEBUG */ 2244Srgrimes 2251016Sache tone(pitchtab[pitch], sound); 2264Srgrimes if (fill != LEGATO) 2271016Sache rest(silence); 2284Srgrimes } 2294Srgrimes} 2304Srgrimes 2314Srgrimes/* interpret and play an item from a notation string */ 23217232Sjoergstatic void 23317232Sjoergplaystring(cp, slen) 23417232Sjoerg char *cp; 23517232Sjoerg size_t slen; 2364Srgrimes{ 237738Sache int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 2384Srgrimes 2394Srgrimes#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 2404Srgrimes {v = v * 10 + (*++cp - '0'); slen--;} 2414Srgrimes for (; slen--; cp++) 2424Srgrimes { 2434Srgrimes int sustain, timeval, tempo; 2444Srgrimes register char c = toupper(*cp); 2454Srgrimes 2464Srgrimes#ifdef DEBUG 247738Sache (void) printf("playstring: %c (%x)\n", c, c); 2484Srgrimes#endif /* DEBUG */ 2494Srgrimes 2504Srgrimes switch (c) 2514Srgrimes { 2524Srgrimes case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 2534Srgrimes 2544Srgrimes /* compute pitch */ 2554Srgrimes pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 2564Srgrimes 2574Srgrimes /* this may be followed by an accidental sign */ 2584Srgrimes if (cp[1] == '#' || cp[1] == '+') 2594Srgrimes { 2604Srgrimes ++pitch; 2614Srgrimes ++cp; 2624Srgrimes slen--; 2634Srgrimes } 2644Srgrimes else if (cp[1] == '-') 2654Srgrimes { 2664Srgrimes --pitch; 2674Srgrimes ++cp; 2684Srgrimes slen--; 2694Srgrimes } 2704Srgrimes 2714Srgrimes /* 2724Srgrimes * If octave-tracking mode is on, and there has been no octave- 2734Srgrimes * setting prefix, find the version of the current letter note 2744Srgrimes * closest to the last regardless of octave. 2754Srgrimes */ 2764Srgrimes if (octtrack && !octprefix) 2774Srgrimes { 2784Srgrimes if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) 2794Srgrimes { 2804Srgrimes ++octave; 2814Srgrimes pitch += OCTAVE_NOTES; 2824Srgrimes } 2834Srgrimes 2844Srgrimes if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) 2854Srgrimes { 2864Srgrimes --octave; 2874Srgrimes pitch -= OCTAVE_NOTES; 2884Srgrimes } 2894Srgrimes } 2904Srgrimes octprefix = FALSE; 2914Srgrimes lastpitch = pitch; 2924Srgrimes 2934Srgrimes /* ...which may in turn be followed by an override time value */ 2944Srgrimes GETNUM(cp, timeval); 2954Srgrimes if (timeval <= 0 || timeval > MIN_VALUE) 2964Srgrimes timeval = value; 2974Srgrimes 2984Srgrimes /* ...and/or sustain dots */ 2994Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 3004Srgrimes { 3014Srgrimes slen--; 3024Srgrimes sustain++; 3034Srgrimes } 3044Srgrimes 305738Sache /* ...and/or a slur mark */ 306738Sache oldfill = fill; 307738Sache if (cp[1] == '_') 308738Sache { 309738Sache fill = LEGATO; 310738Sache ++cp; 311738Sache slen--; 312738Sache } 313738Sache 3144Srgrimes /* time to emit the actual tone */ 3151016Sache playtone(pitch, timeval, sustain); 316738Sache 317738Sache fill = oldfill; 3184Srgrimes break; 3194Srgrimes 3204Srgrimes case 'O': 3214Srgrimes if (cp[1] == 'N' || cp[1] == 'n') 3224Srgrimes { 3234Srgrimes octprefix = octtrack = FALSE; 3244Srgrimes ++cp; 3254Srgrimes slen--; 3264Srgrimes } 3274Srgrimes else if (cp[1] == 'L' || cp[1] == 'l') 3284Srgrimes { 3294Srgrimes octtrack = TRUE; 3304Srgrimes ++cp; 3314Srgrimes slen--; 3324Srgrimes } 3334Srgrimes else 3344Srgrimes { 3354Srgrimes GETNUM(cp, octave); 3363593Sache if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES) 3374Srgrimes octave = DFLT_OCTAVE; 3384Srgrimes octprefix = TRUE; 3394Srgrimes } 3404Srgrimes break; 3414Srgrimes 3424Srgrimes case '>': 3433593Sache if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1) 3444Srgrimes octave++; 3454Srgrimes octprefix = TRUE; 3464Srgrimes break; 3474Srgrimes 3484Srgrimes case '<': 3494Srgrimes if (octave > 0) 3504Srgrimes octave--; 3514Srgrimes octprefix = TRUE; 3524Srgrimes break; 3534Srgrimes 3544Srgrimes case 'N': 3554Srgrimes GETNUM(cp, pitch); 3564Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 3574Srgrimes { 3584Srgrimes slen--; 3594Srgrimes sustain++; 3604Srgrimes } 361738Sache oldfill = fill; 362738Sache if (cp[1] == '_') 363738Sache { 364738Sache fill = LEGATO; 365738Sache ++cp; 366738Sache slen--; 367738Sache } 3681016Sache playtone(pitch - 1, value, sustain); 369738Sache fill = oldfill; 3704Srgrimes break; 3714Srgrimes 3724Srgrimes case 'L': 3734Srgrimes GETNUM(cp, value); 3744Srgrimes if (value <= 0 || value > MIN_VALUE) 3754Srgrimes value = DFLT_VALUE; 3764Srgrimes break; 3774Srgrimes 3784Srgrimes case 'P': 3794Srgrimes case '~': 3804Srgrimes /* this may be followed by an override time value */ 3814Srgrimes GETNUM(cp, timeval); 3824Srgrimes if (timeval <= 0 || timeval > MIN_VALUE) 3834Srgrimes timeval = value; 3844Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 3854Srgrimes { 3864Srgrimes slen--; 3874Srgrimes sustain++; 3884Srgrimes } 3891016Sache playtone(-1, timeval, sustain); 3904Srgrimes break; 3914Srgrimes 3924Srgrimes case 'T': 3934Srgrimes GETNUM(cp, tempo); 3944Srgrimes if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 3954Srgrimes tempo = DFLT_TEMPO; 396170280Sbrian whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / tempo; 3974Srgrimes break; 3984Srgrimes 3994Srgrimes case 'M': 4004Srgrimes if (cp[1] == 'N' || cp[1] == 'n') 4014Srgrimes { 4024Srgrimes fill = NORMAL; 4034Srgrimes ++cp; 4044Srgrimes slen--; 4054Srgrimes } 4064Srgrimes else if (cp[1] == 'L' || cp[1] == 'l') 4074Srgrimes { 4084Srgrimes fill = LEGATO; 4094Srgrimes ++cp; 4104Srgrimes slen--; 4114Srgrimes } 4124Srgrimes else if (cp[1] == 'S' || cp[1] == 's') 4134Srgrimes { 4144Srgrimes fill = STACCATO; 4154Srgrimes ++cp; 4164Srgrimes slen--; 4174Srgrimes } 4184Srgrimes break; 4194Srgrimes } 4204Srgrimes } 4214Srgrimes} 4224Srgrimes 4234Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE ************************** 4244Srgrimes * 4254Srgrimes * This section implements driver hooks to run playstring() and the tone(), 4264Srgrimes * endtone(), and rest() functions defined above. 4274Srgrimes */ 4284Srgrimes 429738Sachestatic int spkr_active = FALSE; /* exclusion flag */ 43060038Sphkstatic char *spkr_inbuf; /* incoming buf */ 4314Srgrimes 432105224Sphkstatic int 43383366Sjulianspkropen(dev, flags, fmt, td) 434130585Sphk struct cdev *dev; 43517232Sjoerg int flags; 43617232Sjoerg int fmt; 43783366Sjulian struct thread *td; 4384Srgrimes{ 4394Srgrimes#ifdef DEBUG 44049982Sbillf (void) printf("spkropen: entering with dev = %s\n", devtoname(dev)); 4414Srgrimes#endif /* DEBUG */ 4424Srgrimes 4434Srgrimes if (minor(dev) != 0) 4444Srgrimes return(ENXIO); 4454Srgrimes else if (spkr_active) 4464Srgrimes return(EBUSY); 4474Srgrimes else 4484Srgrimes { 449738Sache#ifdef DEBUG 450738Sache (void) printf("spkropen: about to perform play initialization\n"); 451738Sache#endif /* DEBUG */ 4524Srgrimes playinit(); 453111119Simp spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK); 454738Sache spkr_active = TRUE; 455738Sache return(0); 4564Srgrimes } 4574Srgrimes} 4584Srgrimes 459105224Sphkstatic int 46017232Sjoergspkrwrite(dev, uio, ioflag) 461130585Sphk struct cdev *dev; 46217232Sjoerg struct uio *uio; 46317232Sjoerg int ioflag; 4644Srgrimes{ 4654Srgrimes#ifdef DEBUG 46649982Sbillf printf("spkrwrite: entering with dev = %s, count = %d\n", 46749982Sbillf devtoname(dev), uio->uio_resid); 4684Srgrimes#endif /* DEBUG */ 4694Srgrimes 4704Srgrimes if (minor(dev) != 0) 4714Srgrimes return(ENXIO); 47217803Speter else if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */ 473738Sache return(E2BIG); 4744Srgrimes else 4754Srgrimes { 476738Sache unsigned n; 477738Sache char *cp; 478738Sache int error; 479738Sache 480738Sache n = uio->uio_resid; 48160038Sphk cp = spkr_inbuf; 48217803Speter error = uiomove(cp, n, uio); 48317803Speter if (!error) { 48417803Speter cp[n] = '\0'; 4851016Sache playstring(cp, n); 48617803Speter } 4874Srgrimes return(error); 4884Srgrimes } 4894Srgrimes} 4904Srgrimes 491105224Sphkstatic int 49283366Sjulianspkrclose(dev, flags, fmt, td) 493130585Sphk struct cdev *dev; 49417232Sjoerg int flags; 49517232Sjoerg int fmt; 49683366Sjulian struct thread *td; 4974Srgrimes{ 4984Srgrimes#ifdef DEBUG 49949982Sbillf (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev)); 5004Srgrimes#endif /* DEBUG */ 5014Srgrimes 5024Srgrimes if (minor(dev) != 0) 5034Srgrimes return(ENXIO); 5044Srgrimes else 5054Srgrimes { 506111748Sdes wakeup(&endtone); 507111748Sdes wakeup(&endrest); 50860038Sphk free(spkr_inbuf, M_SPKR); 509738Sache spkr_active = FALSE; 510738Sache return(0); 5114Srgrimes } 5124Srgrimes} 5134Srgrimes 514105224Sphkstatic int 51583366Sjulianspkrioctl(dev, cmd, cmdarg, flags, td) 516130585Sphk struct cdev *dev; 51738505Sbde unsigned long cmd; 51817232Sjoerg caddr_t cmdarg; 51917232Sjoerg int flags; 52083366Sjulian struct thread *td; 5214Srgrimes{ 5224Srgrimes#ifdef DEBUG 52350253Sbde (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n", 52450253Sbde devtoname(dev), cmd); 5254Srgrimes#endif /* DEBUG */ 5264Srgrimes 5274Srgrimes if (minor(dev) != 0) 5284Srgrimes return(ENXIO); 5294Srgrimes else if (cmd == SPKRTONE) 5304Srgrimes { 5314Srgrimes tone_t *tp = (tone_t *)cmdarg; 5324Srgrimes 5334Srgrimes if (tp->frequency == 0) 5341016Sache rest(tp->duration); 5354Srgrimes else 5361016Sache tone(tp->frequency, tp->duration); 5371016Sache return 0; 5384Srgrimes } 5394Srgrimes else if (cmd == SPKRTUNE) 5404Srgrimes { 5414Srgrimes tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 5424Srgrimes tone_t ttp; 5434Srgrimes int error; 5444Srgrimes 5454Srgrimes for (; ; tp++) { 5464Srgrimes error = copyin(tp, &ttp, sizeof(tone_t)); 5474Srgrimes if (error) 5484Srgrimes return(error); 5494Srgrimes if (ttp.duration == 0) 5504Srgrimes break; 5514Srgrimes if (ttp.frequency == 0) 5521016Sache rest(ttp.duration); 5534Srgrimes else 5541016Sache tone(ttp.frequency, ttp.duration); 5554Srgrimes } 556738Sache return(0); 5574Srgrimes } 558738Sache return(EINVAL); 5594Srgrimes} 5604Srgrimes 561177648Sphkstatic struct cdev *speaker_dev; 562177648Sphk 56361994Smsmith/* 564177648Sphk * Module handling 56561994Smsmith */ 56661994Smsmithstatic int 567177648Sphkspeaker_modevent(module_t mod, int type, void *data) 56861994Smsmith{ 569177648Sphk int error = 0; 570106070Smdodd 571177648Sphk switch(type) { 572177648Sphk case MOD_LOAD: 573177648Sphk speaker_dev = make_dev(&spkr_cdevsw, 0, 574177648Sphk UID_ROOT, GID_WHEEL, 0600, "speaker"); 575177648Sphk break; 576177648Sphk case MOD_SHUTDOWN: 577177648Sphk case MOD_UNLOAD: 578177648Sphk destroy_dev(speaker_dev); 579177648Sphk break; 580177648Sphk default: 581177648Sphk error = EOPNOTSUPP; 582106070Smdodd } 583177648Sphk return (error); 58461994Smsmith} 58561994Smsmith 586177648SphkDEV_MODULE(speaker, speaker_modevent, NULL); 587