spkr.c revision 228443
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 228443 2011-12-12 18:43:24Z mdf $"); 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 40179004Sphk/* 41179004Sphk **************** MACHINE DEPENDENT PART STARTS HERE ************************* 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 62179004Sphk/* 63179004Sphk * Emit tone of frequency thz for given number of centisecs 64179004Sphk */ 6517232Sjoergstatic void 66179004Sphktone(unsigned int thz, unsigned int centisecs) 674Srgrimes{ 68179004Sphk int sps, timo; 694Srgrimes 70179004Sphk if (thz <= 0) 71179004Sphk return; 728288Sdg 734Srgrimes#ifdef DEBUG 74179004Sphk (void) printf("tone: thz=%d centisecs=%d\n", thz, centisecs); 754Srgrimes#endif /* DEBUG */ 764Srgrimes 77179004Sphk /* set timer to generate clicks at given frequency in Hertz */ 78179004Sphk sps = splclock(); 791393Ssos 80179004Sphk if (timer_spkr_acquire()) { 81179004Sphk /* enter list of waiting procs ??? */ 82179004Sphk splx(sps); 83179004Sphk return; 84179004Sphk } 8517232Sjoerg splx(sps); 86179004Sphk disable_intr(); 87179004Sphk timer_spkr_setfreq(thz); 88179004Sphk enable_intr(); 894Srgrimes 90179004Sphk /* 91179004Sphk * Set timeout to endtone function, then give up the timeslice. 92179004Sphk * This is so other processes can execute while the tone is being 93179004Sphk * emitted. 94179004Sphk */ 95179004Sphk timo = centisecs * hz / 100; 96179004Sphk if (timo > 0) 97179004Sphk tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", timo); 98179004Sphk sps = splclock(); 99179004Sphk timer_spkr_release(); 100179004Sphk splx(sps); 1014Srgrimes} 1024Srgrimes 103179004Sphk/* 104179004Sphk * Rest for given number of centisecs 105179004Sphk */ 10617232Sjoergstatic void 107179004Sphkrest(int centisecs) 1084Srgrimes{ 109179004Sphk int timo; 110170278Sbrian 111179004Sphk /* 112179004Sphk * Set timeout to endrest function, then give up the timeslice. 113179004Sphk * This is so other processes can execute while the rest is being 114179004Sphk * waited out. 115179004Sphk */ 1164Srgrimes#ifdef DEBUG 117179004Sphk (void) printf("rest: %d\n", centisecs); 1184Srgrimes#endif /* DEBUG */ 119179004Sphk timo = centisecs * hz / 100; 120179004Sphk if (timo > 0) 121179004Sphk tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", timo); 1224Srgrimes} 1234Srgrimes 124179004Sphk/* 125179004Sphk **************** PLAY STRING INTERPRETER BEGINS HERE ********************** 1264Srgrimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 127738Sache * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 128738Sache * tracking facility are added. 1294Srgrimes * Requires tone(), rest(), and endtone(). String play is not interruptible 1304Srgrimes * except possibly at physical block boundaries. 1314Srgrimes */ 1324Srgrimes 133228443Smdf#ifndef __bool_true_false_are_defined 1344Srgrimestypedef int bool; 135228443Smdf#endif 1364Srgrimes#define TRUE 1 1374Srgrimes#define FALSE 0 1384Srgrimes 1394Srgrimes#define dtoi(c) ((c) - '0') 1404Srgrimes 1414Srgrimesstatic int octave; /* currently selected octave */ 1424Srgrimesstatic int whole; /* whole-note time at current tempo, in ticks */ 1434Srgrimesstatic int value; /* whole divisor for note time, quarter note = 1 */ 1444Srgrimesstatic int fill; /* controls spacing of notes */ 1454Srgrimesstatic bool octtrack; /* octave-tracking on? */ 1464Srgrimesstatic bool octprefix; /* override current octave-tracking state? */ 1474Srgrimes 1484Srgrimes/* 1494Srgrimes * Magic number avoidance... 1504Srgrimes */ 1514Srgrimes#define SECS_PER_MIN 60 /* seconds per minute */ 1524Srgrimes#define WHOLE_NOTE 4 /* quarter notes per whole note */ 1534Srgrimes#define MIN_VALUE 64 /* the most we can divide a note by */ 1544Srgrimes#define DFLT_VALUE 4 /* default value (quarter-note) */ 1554Srgrimes#define FILLTIME 8 /* for articulation, break note in parts */ 1564Srgrimes#define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 1574Srgrimes#define NORMAL 7 /* 7/8ths of note interval is filled */ 1584Srgrimes#define LEGATO 8 /* all of note interval is filled */ 1594Srgrimes#define DFLT_OCTAVE 4 /* default octave */ 1604Srgrimes#define MIN_TEMPO 32 /* minimum tempo */ 1614Srgrimes#define DFLT_TEMPO 120 /* default tempo */ 1624Srgrimes#define MAX_TEMPO 255 /* max tempo */ 1634Srgrimes#define NUM_MULT 3 /* numerator of dot multiplier */ 1644Srgrimes#define DENOM_MULT 2 /* denominator of dot multiplier */ 1654Srgrimes 1664Srgrimes/* letter to half-tone: A B C D E F G */ 1674Srgrimesstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 1684Srgrimes 1694Srgrimes/* 1704Srgrimes * This is the American Standard A440 Equal-Tempered scale with frequencies 1714Srgrimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 1724Srgrimes * our octave 0 is standard octave 2. 1734Srgrimes */ 1744Srgrimes#define OCTAVE_NOTES 12 /* semitones per octave */ 1754Srgrimesstatic int pitchtab[] = 1764Srgrimes{ 1774Srgrimes/* C C# D D# E F F# G G# A A# B*/ 1784Srgrimes/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 1794Srgrimes/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 1804Srgrimes/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 1814Srgrimes/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1824Srgrimes/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 1834Srgrimes/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 1844Srgrimes/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 1854Srgrimes}; 1864Srgrimes 18717232Sjoergstatic void 18817232Sjoergplayinit() 1894Srgrimes{ 1904Srgrimes octave = DFLT_OCTAVE; 191170280Sbrian whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 1924Srgrimes fill = NORMAL; 1934Srgrimes value = DFLT_VALUE; 1944Srgrimes octtrack = FALSE; 1954Srgrimes octprefix = TRUE; /* act as though there was an initial O(n) */ 1964Srgrimes} 1974Srgrimes 198179004Sphk/* 199179004Sphk * Play tone of proper duration for current rhythm signature 200179004Sphk */ 20117232Sjoergstatic void 202179004Sphkplaytone(int pitch, int value, int sustain) 2034Srgrimes{ 204179004Sphk register int sound, silence, snum = 1, sdenom = 1; 2054Srgrimes 206179004Sphk /* this weirdness avoids floating-point arithmetic */ 207179004Sphk for (; sustain; sustain--) { 208179004Sphk /* See the BUGS section in the man page for discussion */ 209179004Sphk snum *= NUM_MULT; 210179004Sphk sdenom *= DENOM_MULT; 211179004Sphk } 2124Srgrimes 213179004Sphk if (value == 0 || sdenom == 0) 214179004Sphk return; 2158288Sdg 216179004Sphk if (pitch == -1) 217179004Sphk rest(whole * snum / (value * sdenom)); 218179004Sphk else { 219179004Sphk sound = (whole * snum) / (value * sdenom) 220179004Sphk - (whole * (FILLTIME - fill)) / (value * FILLTIME); 221179004Sphk silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 2224Srgrimes 2234Srgrimes#ifdef DEBUG 224179004Sphk (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 2254Srgrimes pitch, sound, silence); 2264Srgrimes#endif /* DEBUG */ 2274Srgrimes 228179004Sphk tone(pitchtab[pitch], sound); 229179004Sphk if (fill != LEGATO) 230179004Sphk rest(silence); 231179004Sphk } 2324Srgrimes} 2334Srgrimes 234179004Sphk/* 235179004Sphk * Interpret and play an item from a notation string 236179004Sphk */ 23717232Sjoergstatic void 238179004Sphkplaystring(char *cp, size_t slen) 2394Srgrimes{ 240179004Sphk int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 2414Srgrimes 2424Srgrimes#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 2434Srgrimes {v = v * 10 + (*++cp - '0'); slen--;} 244179004Sphk for (; slen--; cp++) { 245179004Sphk int sustain, timeval, tempo; 246179004Sphk register char c = toupper(*cp); 2474Srgrimes 2484Srgrimes#ifdef DEBUG 249179004Sphk (void) printf("playstring: %c (%x)\n", c, c); 2504Srgrimes#endif /* DEBUG */ 2514Srgrimes 252179004Sphk switch (c) { 253179004Sphk case 'A': 254179004Sphk case 'B': 255179004Sphk case 'C': 256179004Sphk case 'D': 257179004Sphk case 'E': 258179004Sphk case 'F': 259179004Sphk case 'G': 260179004Sphk /* compute pitch */ 261179004Sphk pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 2624Srgrimes 263179004Sphk /* this may be followed by an accidental sign */ 264179004Sphk if (cp[1] == '#' || cp[1] == '+') { 265179004Sphk ++pitch; 266179004Sphk ++cp; 267179004Sphk slen--; 268179004Sphk } else if (cp[1] == '-') { 269179004Sphk --pitch; 270179004Sphk ++cp; 271179004Sphk slen--; 272179004Sphk } 2734Srgrimes 274179004Sphk /* 275179004Sphk * If octave-tracking mode is on, and there has been no octave- 276179004Sphk * setting prefix, find the version of the current letter note 277179004Sphk * closest to the last regardless of octave. 278179004Sphk */ 279179004Sphk if (octtrack && !octprefix) { 280179004Sphk if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES - 281179004Sphk lastpitch)) { 282179004Sphk ++octave; 283179004Sphk pitch += OCTAVE_NOTES; 284179004Sphk } 2854Srgrimes 286179004Sphk if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES) - 287179004Sphk lastpitch)) { 288179004Sphk --octave; 289179004Sphk pitch -= OCTAVE_NOTES; 290179004Sphk } 291179004Sphk } 292179004Sphk octprefix = FALSE; 293179004Sphk lastpitch = pitch; 2944Srgrimes 295179004Sphk /* ...which may in turn be followed by an override time value */ 296179004Sphk GETNUM(cp, timeval); 297179004Sphk if (timeval <= 0 || timeval > MIN_VALUE) 298179004Sphk timeval = value; 2994Srgrimes 300179004Sphk /* ...and/or sustain dots */ 301179004Sphk for (sustain = 0; cp[1] == '.'; cp++) { 302179004Sphk slen--; 303179004Sphk sustain++; 304179004Sphk } 3054Srgrimes 306179004Sphk /* ...and/or a slur mark */ 307179004Sphk oldfill = fill; 308179004Sphk if (cp[1] == '_') { 309179004Sphk fill = LEGATO; 310179004Sphk ++cp; 311179004Sphk slen--; 312179004Sphk } 3134Srgrimes 314179004Sphk /* time to emit the actual tone */ 315179004Sphk playtone(pitch, timeval, sustain); 316738Sache 317179004Sphk fill = oldfill; 318179004Sphk break; 319179004Sphk case 'O': 320179004Sphk if (cp[1] == 'N' || cp[1] == 'n') { 321179004Sphk octprefix = octtrack = FALSE; 322179004Sphk ++cp; 323179004Sphk slen--; 324179004Sphk } else if (cp[1] == 'L' || cp[1] == 'l') { 325179004Sphk octtrack = TRUE; 326179004Sphk ++cp; 327179004Sphk slen--; 328179004Sphk } else { 329179004Sphk GETNUM(cp, octave); 330179004Sphk if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / 331179004Sphk OCTAVE_NOTES) 332179004Sphk octave = DFLT_OCTAVE; 333179004Sphk octprefix = TRUE; 334179004Sphk } 335179004Sphk break; 336179004Sphk case '>': 337179004Sphk if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / 338179004Sphk OCTAVE_NOTES - 1) 339179004Sphk octave++; 340179004Sphk octprefix = TRUE; 341179004Sphk break; 342179004Sphk case '<': 343179004Sphk if (octave > 0) 344179004Sphk octave--; 345179004Sphk octprefix = TRUE; 346179004Sphk break; 347179004Sphk case 'N': 348179004Sphk GETNUM(cp, pitch); 349179004Sphk for (sustain = 0; cp[1] == '.'; cp++) { 350179004Sphk slen--; 351179004Sphk sustain++; 352179004Sphk } 353179004Sphk oldfill = fill; 354179004Sphk if (cp[1] == '_') { 355179004Sphk fill = LEGATO; 356179004Sphk ++cp; 357179004Sphk slen--; 358179004Sphk } 359179004Sphk playtone(pitch - 1, value, sustain); 360179004Sphk fill = oldfill; 361179004Sphk break; 362179004Sphk case 'L': 363179004Sphk GETNUM(cp, value); 364179004Sphk if (value <= 0 || value > MIN_VALUE) 365179004Sphk value = DFLT_VALUE; 366179004Sphk break; 367179004Sphk case 'P': 368179004Sphk case '~': 369179004Sphk /* this may be followed by an override time value */ 370179004Sphk GETNUM(cp, timeval); 371179004Sphk if (timeval <= 0 || timeval > MIN_VALUE) 372179004Sphk timeval = value; 373179004Sphk for (sustain = 0; cp[1] == '.'; cp++) { 374179004Sphk slen--; 375179004Sphk sustain++; 376179004Sphk } 377179004Sphk playtone(-1, timeval, sustain); 378179004Sphk break; 379179004Sphk case 'T': 380179004Sphk GETNUM(cp, tempo); 381179004Sphk if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 382179004Sphk tempo = DFLT_TEMPO; 383179004Sphk whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / tempo; 384179004Sphk break; 385179004Sphk case 'M': 386179004Sphk if (cp[1] == 'N' || cp[1] == 'n') { 387179004Sphk fill = NORMAL; 388179004Sphk ++cp; 389179004Sphk slen--; 390179004Sphk } else if (cp[1] == 'L' || cp[1] == 'l') { 391179004Sphk fill = LEGATO; 392179004Sphk ++cp; 393179004Sphk slen--; 394179004Sphk } else if (cp[1] == 'S' || cp[1] == 's') { 395179004Sphk fill = STACCATO; 396179004Sphk ++cp; 397179004Sphk slen--; 398179004Sphk } 399179004Sphk break; 400179004Sphk } 4014Srgrimes } 4024Srgrimes} 4034Srgrimes 404179004Sphk/* 405179004Sphk * ****************** UNIX DRIVER HOOKS BEGIN HERE ************************** 4064Srgrimes * This section implements driver hooks to run playstring() and the tone(), 4074Srgrimes * endtone(), and rest() functions defined above. 4084Srgrimes */ 4094Srgrimes 410738Sachestatic int spkr_active = FALSE; /* exclusion flag */ 41160038Sphkstatic char *spkr_inbuf; /* incoming buf */ 4124Srgrimes 413105224Sphkstatic int 41483366Sjulianspkropen(dev, flags, fmt, td) 415130585Sphk struct cdev *dev; 416179004Sphk int flags; 417179004Sphk int fmt; 41883366Sjulian struct thread *td; 4194Srgrimes{ 4204Srgrimes#ifdef DEBUG 421179004Sphk (void) printf("spkropen: entering with dev = %s\n", devtoname(dev)); 4224Srgrimes#endif /* DEBUG */ 4234Srgrimes 424187683Sed if (spkr_active) 425179004Sphk return(EBUSY); 426179004Sphk else { 427738Sache#ifdef DEBUG 428179004Sphk (void) printf("spkropen: about to perform play initialization\n"); 429738Sache#endif /* DEBUG */ 430179004Sphk playinit(); 431179004Sphk spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK); 432179004Sphk spkr_active = TRUE; 433179004Sphk return(0); 434179004Sphk } 4354Srgrimes} 4364Srgrimes 437105224Sphkstatic int 43817232Sjoergspkrwrite(dev, uio, ioflag) 439130585Sphk struct cdev *dev; 440179004Sphk struct uio *uio; 441179004Sphk int ioflag; 4424Srgrimes{ 4434Srgrimes#ifdef DEBUG 444194990Skib printf("spkrwrite: entering with dev = %s, count = %zd\n", 44549982Sbillf devtoname(dev), uio->uio_resid); 4464Srgrimes#endif /* DEBUG */ 447187683Sed 448187683Sed if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */ 449179004Sphk return(E2BIG); 450179004Sphk else { 451179004Sphk unsigned n; 452179004Sphk char *cp; 453179004Sphk int error; 4544Srgrimes 455179004Sphk n = uio->uio_resid; 456179004Sphk cp = spkr_inbuf; 457179004Sphk error = uiomove(cp, n, uio); 458179004Sphk if (!error) { 459179004Sphk cp[n] = '\0'; 460179004Sphk playstring(cp, n); 461179004Sphk } 462179004Sphk return(error); 46317803Speter } 4644Srgrimes} 4654Srgrimes 466105224Sphkstatic int 46783366Sjulianspkrclose(dev, flags, fmt, td) 468130585Sphk struct cdev *dev; 469179004Sphk int flags; 470179004Sphk int fmt; 471179004Sphk struct thread *td; 4724Srgrimes{ 4734Srgrimes#ifdef DEBUG 474179004Sphk (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev)); 4754Srgrimes#endif /* DEBUG */ 4764Srgrimes 477187683Sed wakeup(&endtone); 478187683Sed wakeup(&endrest); 479187683Sed free(spkr_inbuf, M_SPKR); 480187683Sed spkr_active = FALSE; 481187683Sed return(0); 4824Srgrimes} 4834Srgrimes 484105224Sphkstatic int 48583366Sjulianspkrioctl(dev, cmd, cmdarg, flags, td) 486130585Sphk struct cdev *dev; 487179004Sphk unsigned long cmd; 488179004Sphk caddr_t cmdarg; 489179004Sphk int flags; 490179004Sphk struct thread *td; 4914Srgrimes{ 4924Srgrimes#ifdef DEBUG 493179004Sphk (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n", 494179004Sphk devtoname(dev), cmd); 4954Srgrimes#endif /* DEBUG */ 4964Srgrimes 497187683Sed if (cmd == SPKRTONE) { 498179004Sphk tone_t *tp = (tone_t *)cmdarg; 4994Srgrimes 500179004Sphk if (tp->frequency == 0) 501179004Sphk rest(tp->duration); 502179004Sphk else 503179004Sphk tone(tp->frequency, tp->duration); 504179004Sphk return 0; 505179004Sphk } else if (cmd == SPKRTUNE) { 506179004Sphk tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 507179004Sphk tone_t ttp; 508179004Sphk int error; 5094Srgrimes 510179004Sphk for (; ; tp++) { 511179004Sphk error = copyin(tp, &ttp, sizeof(tone_t)); 512179004Sphk if (error) 513179004Sphk return(error); 514179004Sphk 515179004Sphk if (ttp.duration == 0) 516179004Sphk break; 517179004Sphk 518179004Sphk if (ttp.frequency == 0) 519179004Sphk rest(ttp.duration); 520179004Sphk else 521179004Sphk tone(ttp.frequency, ttp.duration); 522179004Sphk } 523179004Sphk return(0); 5244Srgrimes } 525179004Sphk return(EINVAL); 5264Srgrimes} 5274Srgrimes 528177648Sphkstatic struct cdev *speaker_dev; 529177648Sphk 53061994Smsmith/* 531177648Sphk * Module handling 53261994Smsmith */ 53361994Smsmithstatic int 534177648Sphkspeaker_modevent(module_t mod, int type, void *data) 53561994Smsmith{ 536177648Sphk int error = 0; 537106070Smdodd 538177648Sphk switch(type) { 539177648Sphk case MOD_LOAD: 540177648Sphk speaker_dev = make_dev(&spkr_cdevsw, 0, 541177648Sphk UID_ROOT, GID_WHEEL, 0600, "speaker"); 542177648Sphk break; 543177648Sphk case MOD_SHUTDOWN: 544177648Sphk case MOD_UNLOAD: 545177648Sphk destroy_dev(speaker_dev); 546177648Sphk break; 547177648Sphk default: 548177648Sphk error = EOPNOTSUPP; 549106070Smdodd } 550177648Sphk return (error); 55161994Smsmith} 55261994Smsmith 553177648SphkDEV_MODULE(speaker, speaker_modevent, NULL); 554