spkr.c revision 139790
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 139790 2005-01-06 22:18:23Z imp $"); 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> 22106323Smdodd#ifdef PC98 23106323Smdodd#include <pc98/pc98/pc98.h> 24106323Smdodd#else 252056Swollman#include <i386/isa/isa.h> 26106323Smdodd#endif 272056Swollman#include <i386/isa/timerreg.h> 287090Sbde#include <machine/clock.h> 292056Swollman#include <machine/speaker.h> 304Srgrimes 3112675Sjulianstatic d_open_t spkropen; 3212675Sjulianstatic d_close_t spkrclose; 3312675Sjulianstatic d_write_t spkrwrite; 3412675Sjulianstatic d_ioctl_t spkrioctl; 3512502Sjulian 3647625Sphkstatic struct cdevsw spkr_cdevsw = { 37126080Sphk .d_version = D_VERSION, 38126080Sphk .d_flags = D_NEEDGIANT, 39111815Sphk .d_open = spkropen, 40111815Sphk .d_close = spkrclose, 41111815Sphk .d_write = spkrwrite, 42111815Sphk .d_ioctl = spkrioctl, 43111815Sphk .d_name = "spkr", 4447625Sphk}; 4512675Sjulian 4669774Sphkstatic MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer"); 4760038Sphk 484Srgrimes/**************** MACHINE DEPENDENT PART STARTS HERE ************************* 494Srgrimes * 504Srgrimes * This section defines a function tone() which causes a tone of given 5119174Sbde * frequency and duration from the ISA console speaker. 524Srgrimes * Another function endtone() is defined to force sound off, and there is 534Srgrimes * also a rest() entry point to do pauses. 544Srgrimes * 554Srgrimes * Audible sound is generated using the Programmable Interval Timer (PIT) and 5619174Sbde * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The 574Srgrimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is 584Srgrimes * used to generate clicks (a square wave) of whatever frequency is desired. 594Srgrimes */ 604Srgrimes 614Srgrimes/* 62106323Smdodd * XXX PPI control values should be in a header and used in clock.c. 634Srgrimes */ 64106323Smdodd#ifdef PC98 65112561Smdodd#define SPKR_DESC "PC98 speaker" 66106323Smdodd#define PPI_SPKR 0x08 /* turn these PPI bits on to pass sound */ 67106323Smdodd#define PIT_COUNT 0x3fdb /* PIT count address */ 68106323Smdodd 69106323Smdodd#define SPEAKER_ON outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR) 70106323Smdodd#define SPEAKER_OFF outb(IO_PPI, inb(IO_PPI) | PPI_SPKR) 71106323Smdodd#define TIMER_ACQUIRE acquire_timer1(TIMER_SEL1 | TIMER_SQWAVE | TIMER_16BIT) 72106323Smdodd#define TIMER_RELEASE release_timer1() 73106323Smdodd#define SPEAKER_WRITE(val) { \ 74106323Smdodd outb(PIT_COUNT, (val & 0xff)); \ 75106323Smdodd outb(PIT_COUNT, (val >> 8)); \ 76106323Smdodd } 77106323Smdodd#else 78112561Smdodd#define SPKR_DESC "PC speaker" 794Srgrimes#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */ 804Srgrimes 81106323Smdodd#define SPEAKER_ON outb(IO_PPI, inb(IO_PPI) | PPI_SPKR) 82106323Smdodd#define SPEAKER_OFF outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR) 83106323Smdodd#define TIMER_ACQUIRE acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT) 84106323Smdodd#define TIMER_RELEASE release_timer2() 85106323Smdodd#define SPEAKER_WRITE(val) { \ 86106323Smdodd outb(TIMER_CNTR2, (val & 0xff)); \ 87106323Smdodd outb(TIMER_CNTR2, (val >> 8)); \ 88106323Smdodd } 89106323Smdodd#endif 90106323Smdodd 91766Sache#define SPKRPRI PSOCK 92766Sachestatic char endtone, endrest; 934Srgrimes 9492765Salfredstatic void tone(unsigned int thz, unsigned int ticks); 9592765Salfredstatic void rest(int ticks); 9692765Salfredstatic void playinit(void); 9792765Salfredstatic void playtone(int pitch, int value, int sustain); 9892765Salfredstatic void playstring(char *cp, size_t slen); 9912854Sbde 100766Sache/* emit tone of frequency thz for given number of ticks */ 10117232Sjoergstatic void 10217232Sjoergtone(thz, ticks) 10317232Sjoerg unsigned int thz, ticks; 1044Srgrimes{ 1058288Sdg unsigned int divisor; 1061016Sache int sps; 1074Srgrimes 1088288Sdg if (thz <= 0) 1098288Sdg return; 1108288Sdg 11119174Sbde divisor = timer_freq / thz; 1128288Sdg 1134Srgrimes#ifdef DEBUG 114766Sache (void) printf("tone: thz=%d ticks=%d\n", thz, ticks); 1154Srgrimes#endif /* DEBUG */ 1164Srgrimes 1174Srgrimes /* set timer to generate clicks at given frequency in Hertz */ 11817232Sjoerg sps = splclock(); 1191393Ssos 120106323Smdodd if (TIMER_ACQUIRE) { 1211393Ssos /* enter list of waiting procs ??? */ 12217232Sjoerg splx(sps); 1231393Ssos return; 1241393Ssos } 12517232Sjoerg splx(sps); 12617232Sjoerg disable_intr(); 127106323Smdodd SPEAKER_WRITE(divisor); 12817232Sjoerg enable_intr(); 1294Srgrimes 1304Srgrimes /* turn the speaker on */ 131106323Smdodd SPEAKER_ON; 1324Srgrimes 1334Srgrimes /* 1344Srgrimes * Set timeout to endtone function, then give up the timeslice. 1354Srgrimes * This is so other processes can execute while the tone is being 1364Srgrimes * emitted. 1374Srgrimes */ 1386152Sache if (ticks > 0) 139111748Sdes tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", ticks); 140106323Smdodd SPEAKER_OFF; 14117232Sjoerg sps = splclock(); 142106323Smdodd TIMER_RELEASE; 14317232Sjoerg splx(sps); 1444Srgrimes} 1454Srgrimes 1464Srgrimes/* rest for given number of ticks */ 14717232Sjoergstatic void 14817232Sjoergrest(ticks) 14917232Sjoerg int ticks; 1504Srgrimes{ 1514Srgrimes /* 1524Srgrimes * Set timeout to endrest function, then give up the timeslice. 1534Srgrimes * This is so other processes can execute while the rest is being 1544Srgrimes * waited out. 1554Srgrimes */ 1564Srgrimes#ifdef DEBUG 157738Sache (void) printf("rest: %d\n", ticks); 1584Srgrimes#endif /* DEBUG */ 1596152Sache if (ticks > 0) 160111748Sdes tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", ticks); 1614Srgrimes} 1624Srgrimes 1634Srgrimes/**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 1644Srgrimes * 1654Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 166738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 167738Sache * tracking facility are added. 1684Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible 1694Srgrimes * except possibly at physical block boundaries. 1704Srgrimes */ 1714Srgrimes 1724Srgrimestypedef int bool; 1734Srgrimes#define TRUE 1 1744Srgrimes#define FALSE 0 1754Srgrimes 1764Srgrimes#define dtoi(c) ((c) - '0') 1774Srgrimes 1784Srgrimesstatic int octave; /* currently selected octave */ 1794Srgrimesstatic int whole; /* whole-note time at current tempo, in ticks */ 1804Srgrimesstatic int value; /* whole divisor for note time, quarter note = 1 */ 1814Srgrimesstatic int fill; /* controls spacing of notes */ 1824Srgrimesstatic bool octtrack; /* octave-tracking on? */ 1834Srgrimesstatic bool octprefix; /* override current octave-tracking state? */ 1844Srgrimes 1854Srgrimes/* 1864Srgrimes * Magic number avoidance... 1874Srgrimes */ 1884Srgrimes#define SECS_PER_MIN 60 /* seconds per minute */ 1894Srgrimes#define WHOLE_NOTE 4 /* quarter notes per whole note */ 1904Srgrimes#define MIN_VALUE 64 /* the most we can divide a note by */ 1914Srgrimes#define DFLT_VALUE 4 /* default value (quarter-note) */ 1924Srgrimes#define FILLTIME 8 /* for articulation, break note in parts */ 1934Srgrimes#define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 1944Srgrimes#define NORMAL 7 /* 7/8ths of note interval is filled */ 1954Srgrimes#define LEGATO 8 /* all of note interval is filled */ 1964Srgrimes#define DFLT_OCTAVE 4 /* default octave */ 1974Srgrimes#define MIN_TEMPO 32 /* minimum tempo */ 1984Srgrimes#define DFLT_TEMPO 120 /* default tempo */ 1994Srgrimes#define MAX_TEMPO 255 /* max tempo */ 2004Srgrimes#define NUM_MULT 3 /* numerator of dot multiplier */ 2014Srgrimes#define DENOM_MULT 2 /* denominator of dot multiplier */ 2024Srgrimes 2034Srgrimes/* letter to half-tone: A B C D E F G */ 2044Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 2054Srgrimes 2064Srgrimes/* 2074Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies 2084Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 2094Srgrimes * our octave 0 is standard octave 2. 2104Srgrimes */ 2114Srgrimes#define OCTAVE_NOTES 12 /* semitones per octave */ 2124Srgrimesstatic int pitchtab[] = 2134Srgrimes{ 2144Srgrimes/* C C# D D# E F F# G G# A A# B*/ 2154Srgrimes/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 2164Srgrimes/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 2174Srgrimes/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 2184Srgrimes/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 2194Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 2204Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 2214Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 2224Srgrimes}; 2234Srgrimes 22417232Sjoergstatic void 22517232Sjoergplayinit() 2264Srgrimes{ 2274Srgrimes octave = DFLT_OCTAVE; 228766Sache whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 2294Srgrimes fill = NORMAL; 2304Srgrimes value = DFLT_VALUE; 2314Srgrimes octtrack = FALSE; 2324Srgrimes octprefix = TRUE; /* act as though there was an initial O(n) */ 2334Srgrimes} 2344Srgrimes 2354Srgrimes/* play tone of proper duration for current rhythm signature */ 23617232Sjoergstatic void 23717232Sjoergplaytone(pitch, value, sustain) 23817232Sjoerg int pitch, value, sustain; 2394Srgrimes{ 2404Srgrimes register int sound, silence, snum = 1, sdenom = 1; 2414Srgrimes 2424Srgrimes /* this weirdness avoids floating-point arithmetic */ 2434Srgrimes for (; sustain; sustain--) 2444Srgrimes { 245738Sache /* See the BUGS section in the man page for discussion */ 2464Srgrimes snum *= NUM_MULT; 2474Srgrimes sdenom *= DENOM_MULT; 2484Srgrimes } 2494Srgrimes 2508288Sdg if (value == 0 || sdenom == 0) 2518288Sdg return; 2528288Sdg 2534Srgrimes if (pitch == -1) 2541016Sache rest(whole * snum / (value * sdenom)); 2554Srgrimes else 2564Srgrimes { 2574Srgrimes sound = (whole * snum) / (value * sdenom) 2584Srgrimes - (whole * (FILLTIME - fill)) / (value * FILLTIME); 2594Srgrimes silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 2604Srgrimes 2614Srgrimes#ifdef DEBUG 262738Sache (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 2634Srgrimes pitch, sound, silence); 2644Srgrimes#endif /* DEBUG */ 2654Srgrimes 2661016Sache tone(pitchtab[pitch], sound); 2674Srgrimes if (fill != LEGATO) 2681016Sache rest(silence); 2694Srgrimes } 2704Srgrimes} 2714Srgrimes 2724Srgrimes/* interpret and play an item from a notation string */ 27317232Sjoergstatic void 27417232Sjoergplaystring(cp, slen) 27517232Sjoerg char *cp; 27617232Sjoerg size_t slen; 2774Srgrimes{ 278738Sache int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 2794Srgrimes 2804Srgrimes#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 2814Srgrimes {v = v * 10 + (*++cp - '0'); slen--;} 2824Srgrimes for (; slen--; cp++) 2834Srgrimes { 2844Srgrimes int sustain, timeval, tempo; 2854Srgrimes register char c = toupper(*cp); 2864Srgrimes 2874Srgrimes#ifdef DEBUG 288738Sache (void) printf("playstring: %c (%x)\n", c, c); 2894Srgrimes#endif /* DEBUG */ 2904Srgrimes 2914Srgrimes switch (c) 2924Srgrimes { 2934Srgrimes case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 2944Srgrimes 2954Srgrimes /* compute pitch */ 2964Srgrimes pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 2974Srgrimes 2984Srgrimes /* this may be followed by an accidental sign */ 2994Srgrimes if (cp[1] == '#' || cp[1] == '+') 3004Srgrimes { 3014Srgrimes ++pitch; 3024Srgrimes ++cp; 3034Srgrimes slen--; 3044Srgrimes } 3054Srgrimes else if (cp[1] == '-') 3064Srgrimes { 3074Srgrimes --pitch; 3084Srgrimes ++cp; 3094Srgrimes slen--; 3104Srgrimes } 3114Srgrimes 3124Srgrimes /* 3134Srgrimes * If octave-tracking mode is on, and there has been no octave- 3144Srgrimes * setting prefix, find the version of the current letter note 3154Srgrimes * closest to the last regardless of octave. 3164Srgrimes */ 3174Srgrimes if (octtrack && !octprefix) 3184Srgrimes { 3194Srgrimes if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) 3204Srgrimes { 3214Srgrimes ++octave; 3224Srgrimes pitch += OCTAVE_NOTES; 3234Srgrimes } 3244Srgrimes 3254Srgrimes if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) 3264Srgrimes { 3274Srgrimes --octave; 3284Srgrimes pitch -= OCTAVE_NOTES; 3294Srgrimes } 3304Srgrimes } 3314Srgrimes octprefix = FALSE; 3324Srgrimes lastpitch = pitch; 3334Srgrimes 3344Srgrimes /* ...which may in turn be followed by an override time value */ 3354Srgrimes GETNUM(cp, timeval); 3364Srgrimes if (timeval <= 0 || timeval > MIN_VALUE) 3374Srgrimes timeval = value; 3384Srgrimes 3394Srgrimes /* ...and/or sustain dots */ 3404Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 3414Srgrimes { 3424Srgrimes slen--; 3434Srgrimes sustain++; 3444Srgrimes } 3454Srgrimes 346738Sache /* ...and/or a slur mark */ 347738Sache oldfill = fill; 348738Sache if (cp[1] == '_') 349738Sache { 350738Sache fill = LEGATO; 351738Sache ++cp; 352738Sache slen--; 353738Sache } 354738Sache 3554Srgrimes /* time to emit the actual tone */ 3561016Sache playtone(pitch, timeval, sustain); 357738Sache 358738Sache fill = oldfill; 3594Srgrimes break; 3604Srgrimes 3614Srgrimes case 'O': 3624Srgrimes if (cp[1] == 'N' || cp[1] == 'n') 3634Srgrimes { 3644Srgrimes octprefix = octtrack = FALSE; 3654Srgrimes ++cp; 3664Srgrimes slen--; 3674Srgrimes } 3684Srgrimes else if (cp[1] == 'L' || cp[1] == 'l') 3694Srgrimes { 3704Srgrimes octtrack = TRUE; 3714Srgrimes ++cp; 3724Srgrimes slen--; 3734Srgrimes } 3744Srgrimes else 3754Srgrimes { 3764Srgrimes GETNUM(cp, octave); 3773593Sache if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES) 3784Srgrimes octave = DFLT_OCTAVE; 3794Srgrimes octprefix = TRUE; 3804Srgrimes } 3814Srgrimes break; 3824Srgrimes 3834Srgrimes case '>': 3843593Sache if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1) 3854Srgrimes octave++; 3864Srgrimes octprefix = TRUE; 3874Srgrimes break; 3884Srgrimes 3894Srgrimes case '<': 3904Srgrimes if (octave > 0) 3914Srgrimes octave--; 3924Srgrimes octprefix = TRUE; 3934Srgrimes break; 3944Srgrimes 3954Srgrimes case 'N': 3964Srgrimes GETNUM(cp, pitch); 3974Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 3984Srgrimes { 3994Srgrimes slen--; 4004Srgrimes sustain++; 4014Srgrimes } 402738Sache oldfill = fill; 403738Sache if (cp[1] == '_') 404738Sache { 405738Sache fill = LEGATO; 406738Sache ++cp; 407738Sache slen--; 408738Sache } 4091016Sache playtone(pitch - 1, value, sustain); 410738Sache fill = oldfill; 4114Srgrimes break; 4124Srgrimes 4134Srgrimes case 'L': 4144Srgrimes GETNUM(cp, value); 4154Srgrimes if (value <= 0 || value > MIN_VALUE) 4164Srgrimes value = DFLT_VALUE; 4174Srgrimes break; 4184Srgrimes 4194Srgrimes case 'P': 4204Srgrimes case '~': 4214Srgrimes /* this may be followed by an override time value */ 4224Srgrimes GETNUM(cp, timeval); 4234Srgrimes if (timeval <= 0 || timeval > MIN_VALUE) 4244Srgrimes timeval = value; 4254Srgrimes for (sustain = 0; cp[1] == '.'; cp++) 4264Srgrimes { 4274Srgrimes slen--; 4284Srgrimes sustain++; 4294Srgrimes } 4301016Sache playtone(-1, timeval, sustain); 4314Srgrimes break; 4324Srgrimes 4334Srgrimes case 'T': 4344Srgrimes GETNUM(cp, tempo); 4354Srgrimes if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 4364Srgrimes tempo = DFLT_TEMPO; 437766Sache whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo; 4384Srgrimes break; 4394Srgrimes 4404Srgrimes case 'M': 4414Srgrimes if (cp[1] == 'N' || cp[1] == 'n') 4424Srgrimes { 4434Srgrimes fill = NORMAL; 4444Srgrimes ++cp; 4454Srgrimes slen--; 4464Srgrimes } 4474Srgrimes else if (cp[1] == 'L' || cp[1] == 'l') 4484Srgrimes { 4494Srgrimes fill = LEGATO; 4504Srgrimes ++cp; 4514Srgrimes slen--; 4524Srgrimes } 4534Srgrimes else if (cp[1] == 'S' || cp[1] == 's') 4544Srgrimes { 4554Srgrimes fill = STACCATO; 4564Srgrimes ++cp; 4574Srgrimes slen--; 4584Srgrimes } 4594Srgrimes break; 4604Srgrimes } 4614Srgrimes } 4624Srgrimes} 4634Srgrimes 4644Srgrimes/******************* UNIX DRIVER HOOKS BEGIN HERE ************************** 4654Srgrimes * 4664Srgrimes * This section implements driver hooks to run playstring() and the tone(), 4674Srgrimes * endtone(), and rest() functions defined above. 4684Srgrimes */ 4694Srgrimes 470738Sachestatic int spkr_active = FALSE; /* exclusion flag */ 47160038Sphkstatic char *spkr_inbuf; /* incoming buf */ 4724Srgrimes 473105224Sphkstatic int 47483366Sjulianspkropen(dev, flags, fmt, td) 475130585Sphk struct cdev *dev; 47617232Sjoerg int flags; 47717232Sjoerg int fmt; 47883366Sjulian struct thread *td; 4794Srgrimes{ 4804Srgrimes#ifdef DEBUG 48149982Sbillf (void) printf("spkropen: entering with dev = %s\n", devtoname(dev)); 4824Srgrimes#endif /* DEBUG */ 4834Srgrimes 4844Srgrimes if (minor(dev) != 0) 4854Srgrimes return(ENXIO); 4864Srgrimes else if (spkr_active) 4874Srgrimes return(EBUSY); 4884Srgrimes else 4894Srgrimes { 490738Sache#ifdef DEBUG 491738Sache (void) printf("spkropen: about to perform play initialization\n"); 492738Sache#endif /* DEBUG */ 4934Srgrimes playinit(); 494111119Simp spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK); 495738Sache spkr_active = TRUE; 496738Sache return(0); 4974Srgrimes } 4984Srgrimes} 4994Srgrimes 500105224Sphkstatic int 50117232Sjoergspkrwrite(dev, uio, ioflag) 502130585Sphk struct cdev *dev; 50317232Sjoerg struct uio *uio; 50417232Sjoerg int ioflag; 5054Srgrimes{ 5064Srgrimes#ifdef DEBUG 50749982Sbillf printf("spkrwrite: entering with dev = %s, count = %d\n", 50849982Sbillf devtoname(dev), uio->uio_resid); 5094Srgrimes#endif /* DEBUG */ 5104Srgrimes 5114Srgrimes if (minor(dev) != 0) 5124Srgrimes return(ENXIO); 51317803Speter else if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */ 514738Sache return(E2BIG); 5154Srgrimes else 5164Srgrimes { 517738Sache unsigned n; 518738Sache char *cp; 519738Sache int error; 520738Sache 521738Sache n = uio->uio_resid; 52260038Sphk cp = spkr_inbuf; 52317803Speter error = uiomove(cp, n, uio); 52417803Speter if (!error) { 52517803Speter cp[n] = '\0'; 5261016Sache playstring(cp, n); 52717803Speter } 5284Srgrimes return(error); 5294Srgrimes } 5304Srgrimes} 5314Srgrimes 532105224Sphkstatic int 53383366Sjulianspkrclose(dev, flags, fmt, td) 534130585Sphk struct cdev *dev; 53517232Sjoerg int flags; 53617232Sjoerg int fmt; 53783366Sjulian struct thread *td; 5384Srgrimes{ 5394Srgrimes#ifdef DEBUG 54049982Sbillf (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev)); 5414Srgrimes#endif /* DEBUG */ 5424Srgrimes 5434Srgrimes if (minor(dev) != 0) 5444Srgrimes return(ENXIO); 5454Srgrimes else 5464Srgrimes { 547111748Sdes wakeup(&endtone); 548111748Sdes wakeup(&endrest); 54960038Sphk free(spkr_inbuf, M_SPKR); 550738Sache spkr_active = FALSE; 551738Sache return(0); 5524Srgrimes } 5534Srgrimes} 5544Srgrimes 555105224Sphkstatic int 55683366Sjulianspkrioctl(dev, cmd, cmdarg, flags, td) 557130585Sphk struct cdev *dev; 55838505Sbde unsigned long cmd; 55917232Sjoerg caddr_t cmdarg; 56017232Sjoerg int flags; 56183366Sjulian struct thread *td; 5624Srgrimes{ 5634Srgrimes#ifdef DEBUG 56450253Sbde (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n", 56550253Sbde devtoname(dev), cmd); 5664Srgrimes#endif /* DEBUG */ 5674Srgrimes 5684Srgrimes if (minor(dev) != 0) 5694Srgrimes return(ENXIO); 5704Srgrimes else if (cmd == SPKRTONE) 5714Srgrimes { 5724Srgrimes tone_t *tp = (tone_t *)cmdarg; 5734Srgrimes 5744Srgrimes if (tp->frequency == 0) 5751016Sache rest(tp->duration); 5764Srgrimes else 5771016Sache tone(tp->frequency, tp->duration); 5781016Sache return 0; 5794Srgrimes } 5804Srgrimes else if (cmd == SPKRTUNE) 5814Srgrimes { 5824Srgrimes tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 5834Srgrimes tone_t ttp; 5844Srgrimes int error; 5854Srgrimes 5864Srgrimes for (; ; tp++) { 5874Srgrimes error = copyin(tp, &ttp, sizeof(tone_t)); 5884Srgrimes if (error) 5894Srgrimes return(error); 5904Srgrimes if (ttp.duration == 0) 5914Srgrimes break; 5924Srgrimes if (ttp.frequency == 0) 5931016Sache rest(ttp.duration); 5944Srgrimes else 5951016Sache tone(ttp.frequency, ttp.duration); 5964Srgrimes } 597738Sache return(0); 5984Srgrimes } 599738Sache return(EINVAL); 6004Srgrimes} 6014Srgrimes 60261994Smsmith/* 60361994Smsmith * Install placeholder to claim the resources owned by the 60461994Smsmith * AT tone generator. 60561994Smsmith */ 606106323Smdoddstatic struct isa_pnp_id speaker_ids[] = { 607106323Smdodd#ifndef PC98 608112561Smdodd { 0x0008d041 /* PNP0800 */, SPKR_DESC }, 609106323Smdodd#endif 61061994Smsmith { 0 } 61161994Smsmith}; 61212517Sjulian 613130585Sphkstatic struct cdev *speaker_dev; 61489679Swes 61561994Smsmithstatic int 616106323Smdoddspeaker_probe(device_t dev) 61761994Smsmith{ 618106070Smdodd int error; 619106070Smdodd 620106323Smdodd error = ISA_PNP_PROBE(device_get_parent(dev), dev, speaker_ids); 621106070Smdodd 622106070Smdodd /* PnP match */ 623106070Smdodd if (error == 0) 624106070Smdodd return (0); 625106070Smdodd 626106070Smdodd /* No match */ 627106070Smdodd if (error == ENXIO) 628106070Smdodd return (ENXIO); 629106070Smdodd 630106070Smdodd /* Not configured by hints. */ 631106323Smdodd if (strncmp(device_get_name(dev), "speaker", 9)) 632106070Smdodd return (ENXIO); 633106070Smdodd 634112561Smdodd device_set_desc(dev, SPKR_DESC); 635106070Smdodd 636106070Smdodd return (0); 63761994Smsmith} 63861994Smsmith 63961994Smsmithstatic int 640106323Smdoddspeaker_attach(device_t dev) 64161994Smsmith{ 642106070Smdodd 643106323Smdodd if (speaker_dev) { 644106070Smdodd device_printf(dev, "Already attached!\n"); 645106070Smdodd return (ENXIO); 646106070Smdodd } 647106070Smdodd 648106323Smdodd speaker_dev = make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 64989679Swes "speaker"); 65089679Swes return (0); 65161994Smsmith} 65261994Smsmith 65389679Swesstatic int 654106323Smdoddspeaker_detach(device_t dev) 65589679Swes{ 656106323Smdodd destroy_dev(speaker_dev); 65789679Swes return (0); 65889679Swes} 65989679Swes 660106323Smdoddstatic device_method_t speaker_methods[] = { 66161994Smsmith /* Device interface */ 662106323Smdodd DEVMETHOD(device_probe, speaker_probe), 663106323Smdodd DEVMETHOD(device_attach, speaker_attach), 664106323Smdodd DEVMETHOD(device_detach, speaker_detach), 66561994Smsmith DEVMETHOD(device_shutdown, bus_generic_shutdown), 66661994Smsmith DEVMETHOD(device_suspend, bus_generic_suspend), 66761994Smsmith DEVMETHOD(device_resume, bus_generic_resume), 66861994Smsmith { 0, 0 } 66961994Smsmith}; 67061994Smsmith 671106323Smdoddstatic driver_t speaker_driver = { 672106323Smdodd "speaker", 673106323Smdodd speaker_methods, 67461994Smsmith 1, /* no softc */ 67561994Smsmith}; 67661994Smsmith 677106323Smdoddstatic devclass_t speaker_devclass; 67861994Smsmith 679106323SmdoddDRIVER_MODULE(speaker, isa, speaker_driver, speaker_devclass, 0, 0); 680106323Smdodd#ifndef PC98 681106323SmdoddDRIVER_MODULE(speaker, acpi, speaker_driver, speaker_devclass, 0, 0); 682106323Smdodd#endif 68361994Smsmith 6844Srgrimes/* spkr.c ends here */ 685