spkr.c revision 170280
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 170280 2007-06-04 09:27:13Z brian $"); 11115703Sobrien 122056Swollman#include <sys/param.h> 132056Swollman#include <sys/systm.h> 1461994Smsmith#include <sys/bus.h> 152056Swollman#include <sys/kernel.h> 1661994Smsmith#include <sys/module.h> 172056Swollman#include <sys/uio.h> 1812675Sjulian#include <sys/conf.h> 1952843Sphk#include <sys/ctype.h> 2060038Sphk#include <sys/malloc.h> 2161994Smsmith#include <isa/isavar.h> 227090Sbde#include <machine/clock.h> 23146211Snyan#include <machine/ppireg.h> 24146211Snyan#include <machine/timerreg.h> 25152306Sru#include <dev/speaker/speaker.h> 264Srgrimes 2712675Sjulianstatic d_open_t spkropen; 2812675Sjulianstatic d_close_t spkrclose; 2912675Sjulianstatic d_write_t spkrwrite; 3012675Sjulianstatic d_ioctl_t spkrioctl; 3112502Sjulian 3247625Sphkstatic struct cdevsw spkr_cdevsw = { 33126080Sphk .d_version = D_VERSION, 34126080Sphk .d_flags = D_NEEDGIANT, 35111815Sphk .d_open = spkropen, 36111815Sphk .d_close = spkrclose, 37111815Sphk .d_write = spkrwrite, 38111815Sphk .d_ioctl = spkrioctl, 39111815Sphk .d_name = "spkr", 4047625Sphk}; 4112675Sjulian 4269774Sphkstatic MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer"); 4360038Sphk 444Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE ************************* 454Srgrimes * 464Srgrimes * This section defines a function tone() which causes a tone of given 4719174Sbde * frequency and duration from the ISA console speaker. 484Srgrimes * Another function endtone() is defined to force sound off, and there is 494Srgrimes * also a rest() entry point to do pauses. 504Srgrimes * 514Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and 5219174Sbde * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The 534Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is 544Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired. 554Srgrimes */ 564Srgrimes 57106323Smdodd#ifdef PC98 58112561Smdodd#define SPKR_DESC "PC98 speaker" 59106323Smdodd#else 60112561Smdodd#define SPKR_DESC "PC speaker" 61106323Smdodd#endif 62106323Smdodd 63766Sache#define SPKRPRI PSOCK 64766Sachestatic char endtone, endrest; 654Srgrimes 66170278Sbrianstatic void tone(unsigned int thz, unsigned int centisecs); 67170278Sbrianstatic void rest(int centisecs); 6892765Salfredstatic void playinit(void); 6992765Salfredstatic void playtone(int pitch, int value, int sustain); 7092765Salfredstatic void playstring(char *cp, size_t slen); 7112854Sbde 72170278Sbrian/* emit tone of frequency thz for given number of centisecs */ 7317232Sjoergstatic void 74170278Sbriantone(thz, centisecs) 75170278Sbrian unsigned int thz, centisecs; 764Srgrimes{ 778288Sdg unsigned int divisor; 78170278Sbrian int sps, timo; 794Srgrimes 808288Sdg if (thz <= 0) 818288Sdg return; 828288Sdg 8319174Sbde divisor = timer_freq / thz; 848288Sdg 854Srgrimes#ifdef DEBUG 86170278Sbrian (void) printf("tone: thz=%d centisecs=%d\n", thz, centisecs); 874Srgrimes#endif /* DEBUG */ 884Srgrimes 894Srgrimes /* set timer to generate clicks at given frequency in Hertz */ 9017232Sjoerg sps = splclock(); 911393Ssos 92146211Snyan if (timer_spkr_acquire()) { 931393Ssos /* enter list of waiting procs ??? */ 9417232Sjoerg splx(sps); 951393Ssos return; 961393Ssos } 9717232Sjoerg splx(sps); 9817232Sjoerg disable_intr(); 99146211Snyan spkr_set_pitch(divisor); 10017232Sjoerg enable_intr(); 1014Srgrimes 1024Srgrimes /* turn the speaker on */ 103146211Snyan ppi_spkr_on(); 1044Srgrimes 1054Srgrimes /* 1064Srgrimes * Set timeout to endtone function, then give up the timeslice. 1074Srgrimes * This is so other processes can execute while the tone is being 1084Srgrimes * emitted. 1094Srgrimes */ 110170278Sbrian timo = centisecs * hz / 100; 111170278Sbrian if (timo > 0) 112170278Sbrian tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", timo); 113146211Snyan ppi_spkr_off(); 11417232Sjoerg sps = splclock(); 115146211Snyan timer_spkr_release(); 11617232Sjoerg splx(sps); 1174Srgrimes} 1184Srgrimes 119170278Sbrian/* rest for given number of centisecs */ 12017232Sjoergstatic void 121170278Sbrianrest(centisecs) 122170278Sbrian int centisecs; 1234Srgrimes{ 124170278Sbrian int timo; 125170278Sbrian 1264Srgrimes /* 1274Srgrimes * Set timeout to endrest function, then give up the timeslice. 1284Srgrimes * This is so other processes can execute while the rest is being 1294Srgrimes * waited out. 1304Srgrimes */ 1314Srgrimes#ifdef DEBUG 132170278Sbrian (void) printf("rest: %d\n", centisecs); 1334Srgrimes#endif /* DEBUG */ 134170278Sbrian timo = centisecs * hz / 100; 135170278Sbrian if (timo > 0) 136170278Sbrian tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", timo); 1374Srgrimes} 1384Srgrimes 1394Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 1404Srgrimes * 1414Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 142738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 143738Sache * tracking facility are added. 1444Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible 1454Srgrimes * except possibly at physical block boundaries. 1464Srgrimes */ 1474Srgrimes 1484Srgrimestypedef int bool; 1494Srgrimes#define TRUE 1 1504Srgrimes#define FALSE 0 1514Srgrimes 1524Srgrimes#define dtoi(c) ((c) - '0') 1534Srgrimes 1544Srgrimesstatic int octave; /* currently selected octave */ 1554Srgrimesstatic int whole; /* whole-note time at current tempo, in ticks */ 1564Srgrimesstatic int value; /* whole divisor for note time, quarter note = 1 */ 1574Srgrimesstatic int fill; /* controls spacing of notes */ 1584Srgrimesstatic bool octtrack; /* octave-tracking on? */ 1594Srgrimesstatic bool octprefix; /* override current octave-tracking state? */ 1604Srgrimes 1614Srgrimes/* 1624Srgrimes * Magic number avoidance... 1634Srgrimes */ 1644Srgrimes#define SECS_PER_MIN 60 /* seconds per minute */ 1654Srgrimes#define WHOLE_NOTE 4 /* quarter notes per whole note */ 1664Srgrimes#define MIN_VALUE 64 /* the most we can divide a note by */ 1674Srgrimes#define DFLT_VALUE 4 /* default value (quarter-note) */ 1684Srgrimes#define FILLTIME 8 /* for articulation, break note in parts */ 1694Srgrimes#define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 1704Srgrimes#define NORMAL 7 /* 7/8ths of note interval is filled */ 1714Srgrimes#define LEGATO 8 /* all of note interval is filled */ 1724Srgrimes#define DFLT_OCTAVE 4 /* default octave */ 1734Srgrimes#define MIN_TEMPO 32 /* minimum tempo */ 1744Srgrimes#define DFLT_TEMPO 120 /* default tempo */ 1754Srgrimes#define MAX_TEMPO 255 /* max tempo */ 1764Srgrimes#define NUM_MULT 3 /* numerator of dot multiplier */ 1774Srgrimes#define DENOM_MULT 2 /* denominator of dot multiplier */ 1784Srgrimes 1794Srgrimes/* letter to half-tone: A B C D E F G */ 1804Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 1814Srgrimes 1824Srgrimes/* 1834Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies 1844Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 1854Srgrimes * our octave 0 is standard octave 2. 1864Srgrimes */ 1874Srgrimes#define OCTAVE_NOTES 12 /* semitones per octave */ 1884Srgrimesstatic int pitchtab[] = 1894Srgrimes{ 1904Srgrimes/* C C# D D# E F F# G G# A A# B*/ 1914Srgrimes/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 1924Srgrimes/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 1934Srgrimes/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 1944Srgrimes/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1954Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 1964Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 1974Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 1984Srgrimes}; 1994Srgrimes 20017232Sjoergstatic void 20117232Sjoergplayinit() 2024Srgrimes{ 2034Srgrimes octave = DFLT_OCTAVE; 204170280Sbrian whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 2054Srgrimes fill = NORMAL; 2064Srgrimes value = DFLT_VALUE; 2074Srgrimes octtrack = FALSE; 2084Srgrimes octprefix = TRUE; /* act as though there was an initial O(n) */ 2094Srgrimes} 2104Srgrimes 2114Srgrimes/* play tone of proper duration for current rhythm signature */ 21217232Sjoergstatic void 21317232Sjoergplaytone(pitch, value, sustain) 21417232Sjoerg int pitch, value, sustain; 2154Srgrimes{ 2164Srgrimes register int sound, silence, snum = 1, sdenom = 1; 2174Srgrimes 2184Srgrimes /* this weirdness avoids floating-point arithmetic */ 2194Srgrimes for (; sustain; sustain--) 2204Srgrimes { 221738Sache /* See the BUGS section in the man page for discussion */ 2224Srgrimes snum *= NUM_MULT; 2234Srgrimes sdenom *= DENOM_MULT; 2244Srgrimes } 2254Srgrimes 2268288Sdg if (value == 0 || sdenom == 0) 2278288Sdg return; 2288288Sdg 2294Srgrimes if (pitch == -1) 2301016Sache rest(whole * snum / (value * sdenom)); 2314Srgrimes else 2324Srgrimes { 2334Srgrimes sound = (whole * snum) / (value * sdenom) 2344Srgrimes - (whole * (FILLTIME - fill)) / (value * FILLTIME); 2354Srgrimes silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 2364Srgrimes 2374Srgrimes#ifdef DEBUG 238738Sache (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 2394Srgrimes pitch, sound, silence); 2404Srgrimes#endif /* DEBUG */ 2414Srgrimes 2421016Sache tone(pitchtab[pitch], sound); 2434Srgrimes if (fill != LEGATO) 2441016Sache rest(silence); 2454Srgrimes } 2464Srgrimes} 2474Srgrimes 2484Srgrimes/* interpret and play an item from a notation string */ 24917232Sjoergstatic void 25017232Sjoergplaystring(cp, slen) 25117232Sjoerg char *cp; 25217232Sjoerg size_t slen; 2534Srgrimes{ 254738Sache int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 2554Srgrimes 2564Srgrimes#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 2574Srgrimes {v = v * 10 + (*++cp - '0'); slen--;} 2584Srgrimes for (; slen--; cp++) 2594Srgrimes { 2604Srgrimes int sustain, timeval, tempo; 2614Srgrimes register char c = toupper(*cp); 2624Srgrimes 2634Srgrimes#ifdef DEBUG 264738Sache (void) printf("playstring: %c (%x)\n", c, c); 2654Srgrimes#endif /* DEBUG */ 2664Srgrimes 2674Srgrimes switch (c) 2684Srgrimes { 2694Srgrimes case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 2704Srgrimes 2714Srgrimes /* compute pitch */ 2724Srgrimes pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 2734Srgrimes 2744Srgrimes /* this may be followed by an accidental sign */ 2754Srgrimes if (cp[1] == '#' || cp[1] == '+') 2764Srgrimes { 2774Srgrimes ++pitch; 2784Srgrimes ++cp; 2794Srgrimes slen--; 2804Srgrimes } 2814Srgrimes else if (cp[1] == '-') 2824Srgrimes { 2834Srgrimes --pitch; 2844Srgrimes ++cp; 2854Srgrimes slen--; 2864Srgrimes } 2874Srgrimes 2884Srgrimes /* 2894Srgrimes * If octave-tracking mode is on, and there has been no octave- 2904Srgrimes * setting prefix, find the version of the current letter note 2914Srgrimes * closest to the last regardless of octave. 2924Srgrimes */ 2934Srgrimes if (octtrack && !octprefix) 2944Srgrimes { 2954Srgrimes if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) 2964Srgrimes { 2974Srgrimes ++octave; 2984Srgrimes pitch += OCTAVE_NOTES; 2994Srgrimes } 3004Srgrimes 3014Srgrimes if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) 3024Srgrimes { 3034Srgrimes --octave; 3044Srgrimes pitch -= OCTAVE_NOTES; 3054Srgrimes } 3064Srgrimes } 3074Srgrimes octprefix = FALSE; 3084Srgrimes lastpitch = pitch; 3094Srgrimes 3104Srgrimes /* ...which may in turn be followed by an override time value */ 3114Srgrimes GETNUM(cp, timeval); 3124Srgrimes if (timeval <= 0 || timeval > MIN_VALUE) 3134Srgrimes timeval = value; 3144Srgrimes 3154Srgrimes /* ...and/or sustain dots */ 3164Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 3174Srgrimes { 3184Srgrimes slen--; 3194Srgrimes sustain++; 3204Srgrimes } 3214Srgrimes 322738Sache /* ...and/or a slur mark */ 323738Sache oldfill = fill; 324738Sache if (cp[1] == '_') 325738Sache { 326738Sache fill = LEGATO; 327738Sache ++cp; 328738Sache slen--; 329738Sache } 330738Sache 3314Srgrimes /* time to emit the actual tone */ 3321016Sache playtone(pitch, timeval, sustain); 333738Sache 334738Sache fill = oldfill; 3354Srgrimes break; 3364Srgrimes 3374Srgrimes case 'O': 3384Srgrimes if (cp[1] == 'N' || cp[1] == 'n') 3394Srgrimes { 3404Srgrimes octprefix = octtrack = FALSE; 3414Srgrimes ++cp; 3424Srgrimes slen--; 3434Srgrimes } 3444Srgrimes else if (cp[1] == 'L' || cp[1] == 'l') 3454Srgrimes { 3464Srgrimes octtrack = TRUE; 3474Srgrimes ++cp; 3484Srgrimes slen--; 3494Srgrimes } 3504Srgrimes else 3514Srgrimes { 3524Srgrimes GETNUM(cp, octave); 3533593Sache if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES) 3544Srgrimes octave = DFLT_OCTAVE; 3554Srgrimes octprefix = TRUE; 3564Srgrimes } 3574Srgrimes break; 3584Srgrimes 3594Srgrimes case '>': 3603593Sache if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1) 3614Srgrimes octave++; 3624Srgrimes octprefix = TRUE; 3634Srgrimes break; 3644Srgrimes 3654Srgrimes case '<': 3664Srgrimes if (octave > 0) 3674Srgrimes octave--; 3684Srgrimes octprefix = TRUE; 3694Srgrimes break; 3704Srgrimes 3714Srgrimes case 'N': 3724Srgrimes GETNUM(cp, pitch); 3734Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 3744Srgrimes { 3754Srgrimes slen--; 3764Srgrimes sustain++; 3774Srgrimes } 378738Sache oldfill = fill; 379738Sache if (cp[1] == '_') 380738Sache { 381738Sache fill = LEGATO; 382738Sache ++cp; 383738Sache slen--; 384738Sache } 3851016Sache playtone(pitch - 1, value, sustain); 386738Sache fill = oldfill; 3874Srgrimes break; 3884Srgrimes 3894Srgrimes case 'L': 3904Srgrimes GETNUM(cp, value); 3914Srgrimes if (value <= 0 || value > MIN_VALUE) 3924Srgrimes value = DFLT_VALUE; 3934Srgrimes break; 3944Srgrimes 3954Srgrimes case 'P': 3964Srgrimes case '~': 3974Srgrimes /* this may be followed by an override time value */ 3984Srgrimes GETNUM(cp, timeval); 3994Srgrimes if (timeval <= 0 || timeval > MIN_VALUE) 4004Srgrimes timeval = value; 4014Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 4024Srgrimes { 4034Srgrimes slen--; 4044Srgrimes sustain++; 4054Srgrimes } 4061016Sache playtone(-1, timeval, sustain); 4074Srgrimes break; 4084Srgrimes 4094Srgrimes case 'T': 4104Srgrimes GETNUM(cp, tempo); 4114Srgrimes if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 4124Srgrimes tempo = DFLT_TEMPO; 413170280Sbrian whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / tempo; 4144Srgrimes break; 4154Srgrimes 4164Srgrimes case 'M': 4174Srgrimes if (cp[1] == 'N' || cp[1] == 'n') 4184Srgrimes { 4194Srgrimes fill = NORMAL; 4204Srgrimes ++cp; 4214Srgrimes slen--; 4224Srgrimes } 4234Srgrimes else if (cp[1] == 'L' || cp[1] == 'l') 4244Srgrimes { 4254Srgrimes fill = LEGATO; 4264Srgrimes ++cp; 4274Srgrimes slen--; 4284Srgrimes } 4294Srgrimes else if (cp[1] == 'S' || cp[1] == 's') 4304Srgrimes { 4314Srgrimes fill = STACCATO; 4324Srgrimes ++cp; 4334Srgrimes slen--; 4344Srgrimes } 4354Srgrimes break; 4364Srgrimes } 4374Srgrimes } 4384Srgrimes} 4394Srgrimes 4404Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE ************************** 4414Srgrimes * 4424Srgrimes * This section implements driver hooks to run playstring() and the tone(), 4434Srgrimes * endtone(), and rest() functions defined above. 4444Srgrimes */ 4454Srgrimes 446738Sachestatic int spkr_active = FALSE; /* exclusion flag */ 44760038Sphkstatic char *spkr_inbuf; /* incoming buf */ 4484Srgrimes 449105224Sphkstatic int 45083366Sjulianspkropen(dev, flags, fmt, td) 451130585Sphk struct cdev *dev; 45217232Sjoerg int flags; 45317232Sjoerg int fmt; 45483366Sjulian struct thread *td; 4554Srgrimes{ 4564Srgrimes#ifdef DEBUG 45749982Sbillf (void) printf("spkropen: entering with dev = %s\n", devtoname(dev)); 4584Srgrimes#endif /* DEBUG */ 4594Srgrimes 4604Srgrimes if (minor(dev) != 0) 4614Srgrimes return(ENXIO); 4624Srgrimes else if (spkr_active) 4634Srgrimes return(EBUSY); 4644Srgrimes else 4654Srgrimes { 466738Sache#ifdef DEBUG 467738Sache (void) printf("spkropen: about to perform play initialization\n"); 468738Sache#endif /* DEBUG */ 4694Srgrimes playinit(); 470111119Simp spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK); 471738Sache spkr_active = TRUE; 472738Sache return(0); 4734Srgrimes } 4744Srgrimes} 4754Srgrimes 476105224Sphkstatic int 47717232Sjoergspkrwrite(dev, uio, ioflag) 478130585Sphk struct cdev *dev; 47917232Sjoerg struct uio *uio; 48017232Sjoerg int ioflag; 4814Srgrimes{ 4824Srgrimes#ifdef DEBUG 48349982Sbillf printf("spkrwrite: entering with dev = %s, count = %d\n", 48449982Sbillf devtoname(dev), uio->uio_resid); 4854Srgrimes#endif /* DEBUG */ 4864Srgrimes 4874Srgrimes if (minor(dev) != 0) 4884Srgrimes return(ENXIO); 48917803Speter else if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */ 490738Sache return(E2BIG); 4914Srgrimes else 4924Srgrimes { 493738Sache unsigned n; 494738Sache char *cp; 495738Sache int error; 496738Sache 497738Sache n = uio->uio_resid; 49860038Sphk cp = spkr_inbuf; 49917803Speter error = uiomove(cp, n, uio); 50017803Speter if (!error) { 50117803Speter cp[n] = '\0'; 5021016Sache playstring(cp, n); 50317803Speter } 5044Srgrimes return(error); 5054Srgrimes } 5064Srgrimes} 5074Srgrimes 508105224Sphkstatic int 50983366Sjulianspkrclose(dev, flags, fmt, td) 510130585Sphk struct cdev *dev; 51117232Sjoerg int flags; 51217232Sjoerg int fmt; 51383366Sjulian struct thread *td; 5144Srgrimes{ 5154Srgrimes#ifdef DEBUG 51649982Sbillf (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev)); 5174Srgrimes#endif /* DEBUG */ 5184Srgrimes 5194Srgrimes if (minor(dev) != 0) 5204Srgrimes return(ENXIO); 5214Srgrimes else 5224Srgrimes { 523111748Sdes wakeup(&endtone); 524111748Sdes wakeup(&endrest); 52560038Sphk free(spkr_inbuf, M_SPKR); 526738Sache spkr_active = FALSE; 527738Sache return(0); 5284Srgrimes } 5294Srgrimes} 5304Srgrimes 531105224Sphkstatic int 53283366Sjulianspkrioctl(dev, cmd, cmdarg, flags, td) 533130585Sphk struct cdev *dev; 53438505Sbde unsigned long cmd; 53517232Sjoerg caddr_t cmdarg; 53617232Sjoerg int flags; 53783366Sjulian struct thread *td; 5384Srgrimes{ 5394Srgrimes#ifdef DEBUG 54050253Sbde (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n", 54150253Sbde devtoname(dev), cmd); 5424Srgrimes#endif /* DEBUG */ 5434Srgrimes 5444Srgrimes if (minor(dev) != 0) 5454Srgrimes return(ENXIO); 5464Srgrimes else if (cmd == SPKRTONE) 5474Srgrimes { 5484Srgrimes tone_t *tp = (tone_t *)cmdarg; 5494Srgrimes 5504Srgrimes if (tp->frequency == 0) 5511016Sache rest(tp->duration); 5524Srgrimes else 5531016Sache tone(tp->frequency, tp->duration); 5541016Sache return 0; 5554Srgrimes } 5564Srgrimes else if (cmd == SPKRTUNE) 5574Srgrimes { 5584Srgrimes tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 5594Srgrimes tone_t ttp; 5604Srgrimes int error; 5614Srgrimes 5624Srgrimes for (; ; tp++) { 5634Srgrimes error = copyin(tp, &ttp, sizeof(tone_t)); 5644Srgrimes if (error) 5654Srgrimes return(error); 5664Srgrimes if (ttp.duration == 0) 5674Srgrimes break; 5684Srgrimes if (ttp.frequency == 0) 5691016Sache rest(ttp.duration); 5704Srgrimes else 5711016Sache tone(ttp.frequency, ttp.duration); 5724Srgrimes } 573738Sache return(0); 5744Srgrimes } 575738Sache return(EINVAL); 5764Srgrimes} 5774Srgrimes 57861994Smsmith/* 57961994Smsmith * Install placeholder to claim the resources owned by the 58061994Smsmith * AT tone generator. 58161994Smsmith */ 582106323Smdoddstatic struct isa_pnp_id speaker_ids[] = { 583106323Smdodd#ifndef PC98 584112561Smdodd { 0x0008d041 /* PNP0800 */, SPKR_DESC }, 585106323Smdodd#endif 58661994Smsmith { 0 } 58761994Smsmith}; 58812517Sjulian 589130585Sphkstatic struct cdev *speaker_dev; 59089679Swes 59161994Smsmithstatic int 592106323Smdoddspeaker_probe(device_t dev) 59361994Smsmith{ 594106070Smdodd int error; 595106070Smdodd 596106323Smdodd error = ISA_PNP_PROBE(device_get_parent(dev), dev, speaker_ids); 597106070Smdodd 598106070Smdodd /* PnP match */ 599106070Smdodd if (error == 0) 600106070Smdodd return (0); 601106070Smdodd 602106070Smdodd /* No match */ 603106070Smdodd if (error == ENXIO) 604106070Smdodd return (ENXIO); 605106070Smdodd 606106070Smdodd /* Not configured by hints. */ 607106323Smdodd if (strncmp(device_get_name(dev), "speaker", 9)) 608106070Smdodd return (ENXIO); 609106070Smdodd 610112561Smdodd device_set_desc(dev, SPKR_DESC); 611106070Smdodd 612106070Smdodd return (0); 61361994Smsmith} 61461994Smsmith 61561994Smsmithstatic int 616106323Smdoddspeaker_attach(device_t dev) 61761994Smsmith{ 618106070Smdodd 619106323Smdodd if (speaker_dev) { 620106070Smdodd device_printf(dev, "Already attached!\n"); 621106070Smdodd return (ENXIO); 622106070Smdodd } 623106070Smdodd 624106323Smdodd speaker_dev = make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 62589679Swes "speaker"); 62689679Swes return (0); 62761994Smsmith} 62861994Smsmith 62989679Swesstatic int 630106323Smdoddspeaker_detach(device_t dev) 63189679Swes{ 632106323Smdodd destroy_dev(speaker_dev); 63389679Swes return (0); 63489679Swes} 63589679Swes 636106323Smdoddstatic device_method_t speaker_methods[] = { 63761994Smsmith /* Device interface */ 638106323Smdodd DEVMETHOD(device_probe, speaker_probe), 639106323Smdodd DEVMETHOD(device_attach, speaker_attach), 640106323Smdodd DEVMETHOD(device_detach, speaker_detach), 64161994Smsmith DEVMETHOD(device_shutdown, bus_generic_shutdown), 64261994Smsmith DEVMETHOD(device_suspend, bus_generic_suspend), 64361994Smsmith DEVMETHOD(device_resume, bus_generic_resume), 64461994Smsmith { 0, 0 } 64561994Smsmith}; 64661994Smsmith 647106323Smdoddstatic driver_t speaker_driver = { 648106323Smdodd "speaker", 649106323Smdodd speaker_methods, 65061994Smsmith 1, /* no softc */ 65161994Smsmith}; 65261994Smsmith 653106323Smdoddstatic devclass_t speaker_devclass; 65461994Smsmith 655106323SmdoddDRIVER_MODULE(speaker, isa, speaker_driver, speaker_devclass, 0, 0); 656106323Smdodd#ifndef PC98 657106323SmdoddDRIVER_MODULE(speaker, acpi, speaker_driver, speaker_devclass, 0, 0); 658106323Smdodd#endif 65961994Smsmith 6604Srgrimes/* spkr.c ends here */ 661