1*e8b15cb7Sriastradh /* $NetBSD: spkr.c,v 1.22 2022/02/12 03:24:36 riastradh Exp $ */ 262b1b0deSchristos 362b1b0deSchristos /* 462b1b0deSchristos * Copyright (c) 1990 Eric S. Raymond (esr@snark.thyrsus.com) 562b1b0deSchristos * Copyright (c) 1990 Andrew A. Chernov (ache@astral.msk.su) 662b1b0deSchristos * Copyright (c) 1990 Lennart Augustsson (lennart@augustsson.net) 762b1b0deSchristos * All rights reserved. 862b1b0deSchristos * 962b1b0deSchristos * Redistribution and use in source and binary forms, with or without 1062b1b0deSchristos * modification, are permitted provided that the following conditions 1162b1b0deSchristos * are met: 1262b1b0deSchristos * 1. Redistributions of source code must retain the above copyright 1362b1b0deSchristos * notice, this list of conditions and the following disclaimer. 1462b1b0deSchristos * 2. Redistributions in binary form must reproduce the above copyright 1562b1b0deSchristos * notice, this list of conditions and the following disclaimer in the 1662b1b0deSchristos * documentation and/or other materials provided with the distribution. 1762b1b0deSchristos * 3. All advertising materials mentioning features or use of this software 1862b1b0deSchristos * must display the following acknowledgement: 1962b1b0deSchristos * This product includes software developed by Eric S. Raymond 2062b1b0deSchristos * 4. The name of the author may not be used to endorse or promote products 2162b1b0deSchristos * derived from this software without specific prior written permission. 2262b1b0deSchristos * 2362b1b0deSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2462b1b0deSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 2562b1b0deSchristos * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 2662b1b0deSchristos * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 2762b1b0deSchristos * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 2862b1b0deSchristos * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 2962b1b0deSchristos * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3062b1b0deSchristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 3162b1b0deSchristos * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 3262b1b0deSchristos * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3362b1b0deSchristos * POSSIBILITY OF SUCH DAMAGE. 3462b1b0deSchristos */ 3562b1b0deSchristos 3662b1b0deSchristos /* 3762b1b0deSchristos * spkr.c -- device driver for console speaker on 80386 3862b1b0deSchristos * 3962b1b0deSchristos * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990 4062b1b0deSchristos * modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su> 4162b1b0deSchristos * 386bsd only clean version, all SYSV stuff removed 4262b1b0deSchristos * use hz value from param.c 4362b1b0deSchristos */ 4462b1b0deSchristos 4562b1b0deSchristos #include <sys/cdefs.h> 46*e8b15cb7Sriastradh __KERNEL_RCSID(0, "$NetBSD: spkr.c,v 1.22 2022/02/12 03:24:36 riastradh Exp $"); 47e2f2d55bSnat 48ff054bdaSpgoyette #if defined(_KERNEL_OPT) 49e2f2d55bSnat #include "wsmux.h" 50ff054bdaSpgoyette #endif 5162b1b0deSchristos 5262b1b0deSchristos #include <sys/param.h> 5362b1b0deSchristos #include <sys/systm.h> 5462b1b0deSchristos #include <sys/kernel.h> 5562b1b0deSchristos #include <sys/errno.h> 5662b1b0deSchristos #include <sys/device.h> 5762b1b0deSchristos #include <sys/malloc.h> 5862b1b0deSchristos #include <sys/module.h> 5962b1b0deSchristos #include <sys/uio.h> 6062b1b0deSchristos #include <sys/proc.h> 6162b1b0deSchristos #include <sys/ioctl.h> 6262b1b0deSchristos #include <sys/conf.h> 6362b1b0deSchristos 6462b1b0deSchristos #include <sys/bus.h> 6562b1b0deSchristos 6662b1b0deSchristos #include <dev/spkrio.h> 67a4cc4a33Schristos #include <dev/spkrvar.h> 68e2f2d55bSnat #include <dev/wscons/wsconsio.h> 69e2f2d55bSnat #include <dev/wscons/wsbellvar.h> 70e2f2d55bSnat #include <dev/wscons/wsbellmuxvar.h> 7162b1b0deSchristos 7214274eaeSriastradh #include "ioconf.h" 7314274eaeSriastradh 7462b1b0deSchristos dev_type_open(spkropen); 7562b1b0deSchristos dev_type_close(spkrclose); 7662b1b0deSchristos dev_type_write(spkrwrite); 7762b1b0deSchristos dev_type_ioctl(spkrioctl); 7862b1b0deSchristos 7962b1b0deSchristos const struct cdevsw spkr_cdevsw = { 8062b1b0deSchristos .d_open = spkropen, 8162b1b0deSchristos .d_close = spkrclose, 8262b1b0deSchristos .d_read = noread, 8362b1b0deSchristos .d_write = spkrwrite, 8462b1b0deSchristos .d_ioctl = spkrioctl, 8562b1b0deSchristos .d_stop = nostop, 8662b1b0deSchristos .d_tty = notty, 8762b1b0deSchristos .d_poll = nopoll, 8862b1b0deSchristos .d_mmap = nommap, 8962b1b0deSchristos .d_kqfilter = nokqfilter, 9062b1b0deSchristos .d_discard = nodiscard, 9162b1b0deSchristos .d_flag = D_OTHER 9262b1b0deSchristos }; 9362b1b0deSchristos 94dd2f4acbSchristos static void playinit(struct spkr_softc *); 95dd2f4acbSchristos static void playtone(struct spkr_softc *, int, int, int); 96dd2f4acbSchristos static void playstring(struct spkr_softc *, const char *, size_t); 9762b1b0deSchristos 9862b1b0deSchristos /**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 9962b1b0deSchristos * 10062b1b0deSchristos * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 10162b1b0deSchristos * M[LNS] are missing and the ~ synonym and octave-tracking facility is added. 1027a449eafSisaki * String play is not interruptible except possibly at physical block 1037a449eafSisaki * boundaries. 10462b1b0deSchristos */ 10562b1b0deSchristos 10662b1b0deSchristos /* 10762b1b0deSchristos * Magic number avoidance... 10862b1b0deSchristos */ 10962b1b0deSchristos #define SECS_PER_MIN 60 /* seconds per minute */ 11062b1b0deSchristos #define WHOLE_NOTE 4 /* quarter notes per whole note */ 11162b1b0deSchristos #define MIN_VALUE 64 /* the most we can divide a note by */ 11262b1b0deSchristos #define DFLT_VALUE 4 /* default value (quarter-note) */ 11362b1b0deSchristos #define FILLTIME 8 /* for articulation, break note in parts */ 11462b1b0deSchristos #define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 11562b1b0deSchristos #define NORMAL 7 /* 7/8ths of note interval is filled */ 11662b1b0deSchristos #define LEGATO 8 /* all of note interval is filled */ 11762b1b0deSchristos #define DFLT_OCTAVE 4 /* default octave */ 11862b1b0deSchristos #define MIN_TEMPO 32 /* minimum tempo */ 11962b1b0deSchristos #define DFLT_TEMPO 120 /* default tempo */ 12062b1b0deSchristos #define MAX_TEMPO 255 /* max tempo */ 12162b1b0deSchristos #define NUM_MULT 3 /* numerator of dot multiplier */ 12262b1b0deSchristos #define DENOM_MULT 2 /* denominator of dot multiplier */ 12362b1b0deSchristos 12462b1b0deSchristos /* letter to half-tone: A B C D E F G */ 12562b1b0deSchristos static const int notetab[8] = { 9, 11, 0, 2, 4, 5, 7 }; 12662b1b0deSchristos 12762b1b0deSchristos /* 12862b1b0deSchristos * This is the American Standard A440 Equal-Tempered scale with frequencies 12962b1b0deSchristos * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 13062b1b0deSchristos * our octave 0 is standard octave 2. 13162b1b0deSchristos */ 13262b1b0deSchristos #define OCTAVE_NOTES 12 /* semitones per octave */ 13362b1b0deSchristos static const int pitchtab[] = 13462b1b0deSchristos { 13562b1b0deSchristos /* C C# D D# E F F# G G# A A# B*/ 13662b1b0deSchristos /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 13762b1b0deSchristos /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 13862b1b0deSchristos /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 13962b1b0deSchristos /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 14062b1b0deSchristos /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 14162b1b0deSchristos /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 14262b1b0deSchristos /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 14362b1b0deSchristos }; 14462b1b0deSchristos #define NOCTAVES (int)(__arraycount(pitchtab) / OCTAVE_NOTES) 14562b1b0deSchristos 14662b1b0deSchristos static void 147dd2f4acbSchristos playinit(struct spkr_softc *sc) 14862b1b0deSchristos { 149dd2f4acbSchristos sc->sc_octave = DFLT_OCTAVE; 150dd2f4acbSchristos sc->sc_whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 151dd2f4acbSchristos sc->sc_fill = NORMAL; 152dd2f4acbSchristos sc->sc_value = DFLT_VALUE; 153dd2f4acbSchristos sc->sc_octtrack = false; 154dd2f4acbSchristos sc->sc_octprefix = true;/* act as though there was an initial O(n) */ 15562b1b0deSchristos } 15662b1b0deSchristos 1577a449eafSisaki #define SPKRPRI (PZERO - 1) 15862b1b0deSchristos 1597a449eafSisaki /* Rest for given number of ticks */ 1607a449eafSisaki static void 1617a449eafSisaki rest(struct spkr_softc *sc, int ticks) 1627a449eafSisaki { 1637a449eafSisaki 1647a449eafSisaki #ifdef SPKRDEBUG 1657a449eafSisaki device_printf(sc->sc_dev, "%s: rest for %d ticks\n", __func__, ticks); 1667a449eafSisaki #endif /* SPKRDEBUG */ 1677a449eafSisaki KASSERT(ticks > 0); 1687a449eafSisaki 1697a449eafSisaki tsleep(sc->sc_dev, SPKRPRI | PCATCH, device_xname(sc->sc_dev), ticks); 17062b1b0deSchristos } 17162b1b0deSchristos 1727a449eafSisaki /* 1737a449eafSisaki * Play tone of proper duration for current rhythm signature. 1747a449eafSisaki * note indicates "O0C" = 0, "O0C#" = 1, "O0D" = 2, ... , and 1757a449eafSisaki * -1 indiacates a rest. 1767a449eafSisaki * val indicates the length, "L4" = 4, "L8" = 8. 1777a449eafSisaki * sustain indicates the number of subsequent dots that extend the sound 1787a449eafSisaki * by one a half. 1797a449eafSisaki */ 1807a449eafSisaki static void 1817a449eafSisaki playtone(struct spkr_softc *sc, int note, int val, int sustain) 1827a449eafSisaki { 1837a449eafSisaki int whole; 1847a449eafSisaki int total; 1857a449eafSisaki int sound; 1867a449eafSisaki int silence; 1877a449eafSisaki 1887a449eafSisaki /* this weirdness avoids floating-point arithmetic */ 1897a449eafSisaki whole = sc->sc_whole; 1907a449eafSisaki for (; sustain; sustain--) { 1917a449eafSisaki whole *= NUM_MULT; 1927a449eafSisaki val *= DENOM_MULT; 1937a449eafSisaki } 1947a449eafSisaki 1957a449eafSisaki /* Total length in tick */ 1967a449eafSisaki total = whole / val; 1977a449eafSisaki 1987a449eafSisaki if (note == -1) { 1997a449eafSisaki #ifdef SPKRDEBUG 2007a449eafSisaki device_printf(sc->sc_dev, "%s: rest for %d ticks\n", 2017a449eafSisaki __func__, total); 2027a449eafSisaki #endif /* SPKRDEBUG */ 2037a449eafSisaki if (total != 0) 2047a449eafSisaki rest(sc, total); 205dd2f4acbSchristos return; 206dd2f4acbSchristos } 207dd2f4acbSchristos 2087a449eafSisaki /* 2097a449eafSisaki * Rest 1/8 (if NORMAL) or 3/8 (if STACCATO) in tick. 2107a449eafSisaki * silence should be rounded down. 2117a449eafSisaki */ 2127a449eafSisaki silence = total * (FILLTIME - sc->sc_fill) / FILLTIME; 2137a449eafSisaki sound = total - silence; 21462b1b0deSchristos 21562b1b0deSchristos #ifdef SPKRDEBUG 216c195ab7cSisaki device_printf(sc->sc_dev, 2177a449eafSisaki "%s: note %d for %d ticks, rest for %d ticks\n", __func__, 2187a449eafSisaki note, sound, silence); 21962b1b0deSchristos #endif /* SPKRDEBUG */ 22062b1b0deSchristos 2217a449eafSisaki if (sound != 0) 2227a449eafSisaki (*sc->sc_tone)(sc->sc_dev, pitchtab[note], sound); 2237a449eafSisaki if (silence != 0) 2247a449eafSisaki rest(sc, silence); 22562b1b0deSchristos } 22662b1b0deSchristos 22762b1b0deSchristos /* interpret and play an item from a notation string */ 228dd2f4acbSchristos static void 229dd2f4acbSchristos playstring(struct spkr_softc *sc, const char *cp, size_t slen) 23062b1b0deSchristos { 2317a449eafSisaki int pitch; 2327a449eafSisaki int lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 23362b1b0deSchristos 234dd2f4acbSchristos #define GETNUM(cp, v) \ 235dd2f4acbSchristos for (v = 0; slen > 0 && isdigit((unsigned char)cp[1]); ) { \ 236dd2f4acbSchristos v = v * 10 + (*++cp - '0'); \ 237dd2f4acbSchristos slen--; \ 238dd2f4acbSchristos } 239dd2f4acbSchristos 240dd2f4acbSchristos for (; slen--; cp++) { 24162b1b0deSchristos int sustain, timeval, tempo; 242dd2f4acbSchristos char c = toupper((unsigned char)*cp); 24362b1b0deSchristos 24462b1b0deSchristos #ifdef SPKRDEBUG 245c195ab7cSisaki if (0x20 <= c && c < 0x7f) { 246c195ab7cSisaki device_printf(sc->sc_dev, "%s: '%c'\n", __func__, c); 247c195ab7cSisaki } else { 248c195ab7cSisaki device_printf(sc->sc_dev, "%s: (0x%x)\n", __func__, c); 249c195ab7cSisaki } 25062b1b0deSchristos #endif /* SPKRDEBUG */ 25162b1b0deSchristos 252dd2f4acbSchristos switch (c) { 253dd2f4acbSchristos case 'A': case 'B': case 'C': case 'D': 254dd2f4acbSchristos case 'E': case 'F': case 'G': 25562b1b0deSchristos /* compute pitch */ 256dd2f4acbSchristos pitch = notetab[c - 'A'] + sc->sc_octave * OCTAVE_NOTES; 25762b1b0deSchristos 25862b1b0deSchristos /* this may be followed by an accidental sign */ 259dd2f4acbSchristos if (slen > 0 && (cp[1] == '#' || cp[1] == '+')) { 26062b1b0deSchristos ++pitch; 26162b1b0deSchristos ++cp; 26262b1b0deSchristos slen--; 263dd2f4acbSchristos } else if (slen > 0 && cp[1] == '-') { 26462b1b0deSchristos --pitch; 26562b1b0deSchristos ++cp; 26662b1b0deSchristos slen--; 26762b1b0deSchristos } 26862b1b0deSchristos 26962b1b0deSchristos /* 270dd2f4acbSchristos * If octave-tracking mode is on, and there has been no 271dd2f4acbSchristos * octave- setting prefix, find the version of the 272dd2f4acbSchristos * current letter note * closest to the last 273dd2f4acbSchristos * regardless of octave. 27462b1b0deSchristos */ 275dd2f4acbSchristos if (sc->sc_octtrack && !sc->sc_octprefix) { 276dd2f4acbSchristos int d = abs(pitch - lastpitch); 277dd2f4acbSchristos if (d > abs(pitch + OCTAVE_NOTES - lastpitch)) { 278dd2f4acbSchristos if (sc->sc_octave < NOCTAVES - 1) { 279dd2f4acbSchristos ++sc->sc_octave; 28062b1b0deSchristos pitch += OCTAVE_NOTES; 28162b1b0deSchristos } 28262b1b0deSchristos } 28362b1b0deSchristos 284dd2f4acbSchristos if (d > abs(pitch - OCTAVE_NOTES - lastpitch)) { 285dd2f4acbSchristos if (sc->sc_octave > 0) { 286dd2f4acbSchristos --sc->sc_octave; 28762b1b0deSchristos pitch -= OCTAVE_NOTES; 28862b1b0deSchristos } 28962b1b0deSchristos } 29062b1b0deSchristos } 291dd2f4acbSchristos sc->sc_octprefix = false; 29262b1b0deSchristos lastpitch = pitch; 29362b1b0deSchristos 294dd2f4acbSchristos /* 295dd2f4acbSchristos * ...which may in turn be followed by an override 296dd2f4acbSchristos * time value 297dd2f4acbSchristos */ 29862b1b0deSchristos GETNUM(cp, timeval); 29962b1b0deSchristos if (timeval <= 0 || timeval > MIN_VALUE) 300dd2f4acbSchristos timeval = sc->sc_value; 30162b1b0deSchristos 30262b1b0deSchristos /* ...and/or sustain dots */ 303dd2f4acbSchristos for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) { 30462b1b0deSchristos slen--; 30562b1b0deSchristos sustain++; 30662b1b0deSchristos } 30762b1b0deSchristos 30862b1b0deSchristos /* time to emit the actual tone */ 309dd2f4acbSchristos playtone(sc, pitch, timeval, sustain); 31062b1b0deSchristos break; 31162b1b0deSchristos 31262b1b0deSchristos case 'O': 313dd2f4acbSchristos if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) { 314dd2f4acbSchristos sc->sc_octprefix = sc->sc_octtrack = false; 31562b1b0deSchristos ++cp; 31662b1b0deSchristos slen--; 317dd2f4acbSchristos } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) { 318dd2f4acbSchristos sc->sc_octtrack = true; 31962b1b0deSchristos ++cp; 32062b1b0deSchristos slen--; 321dd2f4acbSchristos } else { 322dd2f4acbSchristos GETNUM(cp, sc->sc_octave); 323dd2f4acbSchristos if (sc->sc_octave >= NOCTAVES) 324dd2f4acbSchristos sc->sc_octave = DFLT_OCTAVE; 325dd2f4acbSchristos sc->sc_octprefix = true; 32662b1b0deSchristos } 32762b1b0deSchristos break; 32862b1b0deSchristos 32962b1b0deSchristos case '>': 330dd2f4acbSchristos if (sc->sc_octave < NOCTAVES - 1) 331dd2f4acbSchristos sc->sc_octave++; 332dd2f4acbSchristos sc->sc_octprefix = true; 33362b1b0deSchristos break; 33462b1b0deSchristos 33562b1b0deSchristos case '<': 336dd2f4acbSchristos if (sc->sc_octave > 0) 337dd2f4acbSchristos sc->sc_octave--; 338dd2f4acbSchristos sc->sc_octprefix = true; 33962b1b0deSchristos break; 34062b1b0deSchristos 34162b1b0deSchristos case 'N': 34262b1b0deSchristos GETNUM(cp, pitch); 343dd2f4acbSchristos for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) { 34462b1b0deSchristos slen--; 34562b1b0deSchristos sustain++; 34662b1b0deSchristos } 347dd2f4acbSchristos playtone(sc, pitch - 1, sc->sc_value, sustain); 34862b1b0deSchristos break; 34962b1b0deSchristos 35062b1b0deSchristos case 'L': 351dd2f4acbSchristos GETNUM(cp, sc->sc_value); 352dd2f4acbSchristos if (sc->sc_value <= 0 || sc->sc_value > MIN_VALUE) 353dd2f4acbSchristos sc->sc_value = DFLT_VALUE; 35462b1b0deSchristos break; 35562b1b0deSchristos 35662b1b0deSchristos case 'P': 35762b1b0deSchristos case '~': 35862b1b0deSchristos /* this may be followed by an override time value */ 35962b1b0deSchristos GETNUM(cp, timeval); 36062b1b0deSchristos if (timeval <= 0 || timeval > MIN_VALUE) 361dd2f4acbSchristos timeval = sc->sc_value; 362dd2f4acbSchristos for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) { 36362b1b0deSchristos slen--; 36462b1b0deSchristos sustain++; 36562b1b0deSchristos } 366dd2f4acbSchristos playtone(sc, -1, timeval, sustain); 36762b1b0deSchristos break; 36862b1b0deSchristos 36962b1b0deSchristos case 'T': 37062b1b0deSchristos GETNUM(cp, tempo); 37162b1b0deSchristos if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 37262b1b0deSchristos tempo = DFLT_TEMPO; 373dd2f4acbSchristos sc->sc_whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo; 37462b1b0deSchristos break; 37562b1b0deSchristos 37662b1b0deSchristos case 'M': 377dd2f4acbSchristos if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) { 378dd2f4acbSchristos sc->sc_fill = NORMAL; 37962b1b0deSchristos ++cp; 38062b1b0deSchristos slen--; 381dd2f4acbSchristos } else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) { 382dd2f4acbSchristos sc->sc_fill = LEGATO; 38362b1b0deSchristos ++cp; 38462b1b0deSchristos slen--; 385dd2f4acbSchristos } else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's')) { 386dd2f4acbSchristos sc->sc_fill = STACCATO; 38762b1b0deSchristos ++cp; 38862b1b0deSchristos slen--; 38962b1b0deSchristos } 39062b1b0deSchristos break; 39162b1b0deSchristos } 39262b1b0deSchristos } 39362b1b0deSchristos } 39462b1b0deSchristos 3957a449eafSisaki /******************* UNIX DRIVER HOOKS BEGIN HERE **************************/ 396dd2f4acbSchristos #define spkrenter(d) device_lookup_private(&spkr_cd, d) 39762b1b0deSchristos 3987a449eafSisaki /* 3997a449eafSisaki * Attaches spkr. Specify tone function with the following specification: 4007a449eafSisaki * 4017a449eafSisaki * void 4027a449eafSisaki * tone(device_t self, u_int pitch, u_int tick) 4037a449eafSisaki * plays a beep with specified parameters. 4047a449eafSisaki * The argument 'pitch' specifies the pitch of a beep in Hz. The argument 4057a449eafSisaki * 'tick' specifies the period of a beep in tick(9). This function waits 4067a449eafSisaki * to finish playing the beep and then halts it. 4077a449eafSisaki * If the pitch is zero, it halts all sound if any (for compatibility 4087a449eafSisaki * with the past confused specifications, but there should be no sound at 4097a449eafSisaki * this point). And it returns immediately, without waiting ticks. So 4107a449eafSisaki * you cannot use this as a rest. 4117a449eafSisaki * If the tick is zero, it returns immediately. 4127a449eafSisaki */ 413dd2f4acbSchristos void 4147a449eafSisaki spkr_attach(device_t self, void (*tone)(device_t, u_int, u_int)) 41562b1b0deSchristos { 416dd2f4acbSchristos struct spkr_softc *sc = device_private(self); 417dd2f4acbSchristos 418bccce848Spgoyette #ifdef SPKRDEBUG 419*e8b15cb7Sriastradh aprint_debug("%s: entering for unit %d\n", __func__, 420*e8b15cb7Sriastradh device_unit(self)); 421bccce848Spgoyette #endif /* SPKRDEBUG */ 422dd2f4acbSchristos sc->sc_dev = self; 423dd2f4acbSchristos sc->sc_tone = tone; 424dd2f4acbSchristos sc->sc_inbuf = NULL; 42525c4f25dSpgoyette sc->sc_wsbelldev = NULL; 426e2f2d55bSnat 4273bee0c11Sthorpej spkr_rescan(self, NULL, NULL); 42862b1b0deSchristos } 42962b1b0deSchristos 43062b1b0deSchristos int 431bccce848Spgoyette spkr_detach(device_t self, int flags) 432bccce848Spgoyette { 433bccce848Spgoyette struct spkr_softc *sc = device_private(self); 4343e5646a1Snat int rc; 435bccce848Spgoyette 436bccce848Spgoyette #ifdef SPKRDEBUG 437*e8b15cb7Sriastradh aprint_debug("%s: entering for unit %d\n", __func__, 438*e8b15cb7Sriastradh device_unit(self)); 439bccce848Spgoyette #endif /* SPKRDEBUG */ 440bccce848Spgoyette if (sc == NULL) 441bccce848Spgoyette return ENXIO; 442a642e421Snat 443a642e421Snat /* XXXNS If speaker never closes, we cannot complete the detach. */ 444a642e421Snat while ((flags & DETACH_FORCE) != 0 && sc->sc_inbuf != NULL) 445a642e421Snat kpause("spkrwait", TRUE, 1, NULL); 446bccce848Spgoyette if (sc->sc_inbuf != NULL) 447bccce848Spgoyette return EBUSY; 448bccce848Spgoyette 4493e5646a1Snat rc = config_detach_children(self, flags); 4503e5646a1Snat 4513e5646a1Snat return rc; 452bccce848Spgoyette } 453bccce848Spgoyette 45425c4f25dSpgoyette /* ARGSUSED */ 45525c4f25dSpgoyette int 45625c4f25dSpgoyette spkr_rescan(device_t self, const char *iattr, const int *locators) 45725c4f25dSpgoyette { 45825c4f25dSpgoyette #if NWSMUX > 0 45925c4f25dSpgoyette struct spkr_softc *sc = device_private(self); 46025c4f25dSpgoyette struct wsbelldev_attach_args a; 46125c4f25dSpgoyette 46225c4f25dSpgoyette if (sc->sc_wsbelldev == NULL) { 46325c4f25dSpgoyette a.accesscookie = sc; 4643bee0c11Sthorpej sc->sc_wsbelldev = config_found(self, &a, wsbelldevprint, 465beecddb6Sthorpej CFARGS_NONE); 46625c4f25dSpgoyette } 46725c4f25dSpgoyette #endif 46825c4f25dSpgoyette return 0; 46925c4f25dSpgoyette } 47025c4f25dSpgoyette 47125c4f25dSpgoyette int 47225c4f25dSpgoyette spkr_childdet(device_t self, device_t child) 47325c4f25dSpgoyette { 47425c4f25dSpgoyette struct spkr_softc *sc = device_private(self); 47525c4f25dSpgoyette 47625c4f25dSpgoyette if (sc->sc_wsbelldev == child) 47725c4f25dSpgoyette sc->sc_wsbelldev = NULL; 47825c4f25dSpgoyette 47925c4f25dSpgoyette return 0; 48025c4f25dSpgoyette } 48125c4f25dSpgoyette 482bccce848Spgoyette int 48362b1b0deSchristos spkropen(dev_t dev, int flags, int mode, struct lwp *l) 48462b1b0deSchristos { 485dd2f4acbSchristos struct spkr_softc *sc = spkrenter(minor(dev)); 48662b1b0deSchristos 487c195ab7cSisaki #ifdef SPKRDEBUG 488c195ab7cSisaki device_printf(sc->sc_dev, "%s: entering\n", __func__); 489c195ab7cSisaki #endif /* SPKRDEBUG */ 490dd2f4acbSchristos if (sc == NULL) 491dd2f4acbSchristos return ENXIO; 492bccce848Spgoyette if (sc->sc_inbuf != NULL) 493dd2f4acbSchristos return EBUSY; 494dd2f4acbSchristos 495dd2f4acbSchristos sc->sc_inbuf = malloc(DEV_BSIZE, M_DEVBUF, M_WAITOK); 496dd2f4acbSchristos playinit(sc); 497dd2f4acbSchristos return 0; 49862b1b0deSchristos } 49962b1b0deSchristos 50062b1b0deSchristos int 50162b1b0deSchristos spkrwrite(dev_t dev, struct uio *uio, int flags) 50262b1b0deSchristos { 503dd2f4acbSchristos struct spkr_softc *sc = spkrenter(minor(dev)); 50462b1b0deSchristos 505c195ab7cSisaki #ifdef SPKRDEBUG 506c195ab7cSisaki device_printf(sc->sc_dev, "%s: entering with length = %zu\n", 507c195ab7cSisaki __func__, uio->uio_resid); 508c195ab7cSisaki #endif /* SPKRDEBUG */ 509dd2f4acbSchristos if (sc == NULL) 510dd2f4acbSchristos return ENXIO; 511bccce848Spgoyette if (sc->sc_inbuf == NULL) 512dd2f4acbSchristos return EINVAL; 513dd2f4acbSchristos 514a8a5c538Sriastradh size_t n = uimin(DEV_BSIZE, uio->uio_resid); 515dd2f4acbSchristos int error = uiomove(sc->sc_inbuf, n, uio); 516dd2f4acbSchristos if (error) 517dd2f4acbSchristos return error; 518dd2f4acbSchristos playstring(sc, sc->sc_inbuf, n); 519dd2f4acbSchristos return 0; 52062b1b0deSchristos } 52162b1b0deSchristos 52262b1b0deSchristos int 52362b1b0deSchristos spkrclose(dev_t dev, int flags, int mode, struct lwp *l) 52462b1b0deSchristos { 525dd2f4acbSchristos struct spkr_softc *sc = spkrenter(minor(dev)); 52662b1b0deSchristos 527c195ab7cSisaki #ifdef SPKRDEBUG 528c195ab7cSisaki device_printf(sc->sc_dev, "%s: entering\n", __func__); 529c195ab7cSisaki #endif /* SPKRDEBUG */ 530dd2f4acbSchristos if (sc == NULL) 531dd2f4acbSchristos return ENXIO; 532bccce848Spgoyette if (sc->sc_inbuf == NULL) 533dd2f4acbSchristos return EINVAL; 534dd2f4acbSchristos 535dd2f4acbSchristos sc->sc_tone(sc->sc_dev, 0, 0); 536dd2f4acbSchristos free(sc->sc_inbuf, M_DEVBUF); 537dd2f4acbSchristos sc->sc_inbuf = NULL; 538dd2f4acbSchristos 539dd2f4acbSchristos return 0; 54062b1b0deSchristos } 541dd2f4acbSchristos 5427a449eafSisaki /* 5437a449eafSisaki * Play tone specified by tp. 5447a449eafSisaki * tp->frequency is the frequency (0 means a rest). 5457a449eafSisaki * tp->duration is the length in tick (returns immediately if 0). 5467a449eafSisaki */ 547dd2f4acbSchristos static void 548dd2f4acbSchristos playonetone(struct spkr_softc *sc, tone_t *tp) 549dd2f4acbSchristos { 5507a449eafSisaki if (tp->duration <= 0) 5517a449eafSisaki return; 5527a449eafSisaki 553dd2f4acbSchristos if (tp->frequency == 0) 5547a449eafSisaki rest(sc, tp->duration); 555dd2f4acbSchristos else 556dd2f4acbSchristos (*sc->sc_tone)(sc->sc_dev, tp->frequency, tp->duration); 55762b1b0deSchristos } 55862b1b0deSchristos 55962b1b0deSchristos int 56062b1b0deSchristos spkrioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 56162b1b0deSchristos { 562c195ab7cSisaki struct spkr_softc *sc = spkrenter(minor(dev)); 563dd2f4acbSchristos tone_t *tp; 56462b1b0deSchristos tone_t ttp; 56562b1b0deSchristos int error; 566c195ab7cSisaki 567dd2f4acbSchristos #ifdef SPKRDEBUG 568c195ab7cSisaki device_printf(sc->sc_dev, "%s: entering with cmd = %lx\n", 569c195ab7cSisaki __func__, cmd); 570dd2f4acbSchristos #endif /* SPKRDEBUG */ 571dd2f4acbSchristos if (sc == NULL) 572dd2f4acbSchristos return ENXIO; 573bccce848Spgoyette if (sc->sc_inbuf == NULL) 574dd2f4acbSchristos return EINVAL; 575dd2f4acbSchristos 576dd2f4acbSchristos switch (cmd) { 577dd2f4acbSchristos case SPKRTONE: 578dd2f4acbSchristos playonetone(sc, data); 579dd2f4acbSchristos return 0; 580dd2f4acbSchristos case SPKRTUNE: 581dd2f4acbSchristos for (tp = *(void **)data;; tp++) { 58262b1b0deSchristos error = copyin(tp, &ttp, sizeof(tone_t)); 58362b1b0deSchristos if (error) 58462b1b0deSchristos return(error); 58562b1b0deSchristos if (ttp.duration == 0) 58662b1b0deSchristos break; 587dd2f4acbSchristos playonetone(sc, &ttp); 58862b1b0deSchristos } 589dd2f4acbSchristos return 0; 590bfcf5f24Snat case SPKRGETVOL: 591bfcf5f24Snat if (data != NULL) 592bfcf5f24Snat *(u_int *)data = sc->sc_vol; 593bfcf5f24Snat return 0; 594bfcf5f24Snat case SPKRSETVOL: 595bfcf5f24Snat if (data != NULL && *(u_int *)data <= 100) 596bfcf5f24Snat sc->sc_vol = *(u_int *)data; 597bfcf5f24Snat return 0; 598dd2f4acbSchristos default: 599dd2f4acbSchristos return ENOTTY; 60062b1b0deSchristos } 60162b1b0deSchristos } 60262b1b0deSchristos 60346bee1b9Schristos #ifdef _MODULE 60446bee1b9Schristos #include "ioconf.c" 60546bee1b9Schristos #endif 60646bee1b9Schristos 607f309403eSpgoyette MODULE(MODULE_CLASS_DRIVER, spkr, "audio" /* and/or pcppi */ ); 608261a65d5Spgoyette 60962b1b0deSchristos int 610261a65d5Spgoyette spkr_modcmd(modcmd_t cmd, void *arg) 61162b1b0deSchristos { 6126391aa4bSpgoyette int error = 0; 61362b1b0deSchristos #ifdef _MODULE 61462b1b0deSchristos devmajor_t bmajor, cmajor; 6156391aa4bSpgoyette #endif 61662b1b0deSchristos 61762b1b0deSchristos switch(cmd) { 61862b1b0deSchristos case MODULE_CMD_INIT: 6196391aa4bSpgoyette #ifdef _MODULE 62062b1b0deSchristos bmajor = cmajor = -1; 62162b1b0deSchristos error = devsw_attach(spkr_cd.cd_name, NULL, &bmajor, 62262b1b0deSchristos &spkr_cdevsw, &cmajor); 62362b1b0deSchristos if (error) 62462b1b0deSchristos break; 62562b1b0deSchristos 62662b1b0deSchristos error = config_init_component(cfdriver_ioconf_spkr, 62762b1b0deSchristos cfattach_ioconf_spkr, cfdata_ioconf_spkr); 62862b1b0deSchristos if (error) { 62962b1b0deSchristos devsw_detach(NULL, &spkr_cdevsw); 63062b1b0deSchristos } 6316391aa4bSpgoyette #endif 63262b1b0deSchristos break; 63362b1b0deSchristos 63462b1b0deSchristos case MODULE_CMD_FINI: 6356391aa4bSpgoyette #ifdef _MODULE 636bccce848Spgoyette devsw_detach(NULL, &spkr_cdevsw); 63762b1b0deSchristos error = config_fini_component(cfdriver_ioconf_spkr, 63862b1b0deSchristos cfattach_ioconf_spkr, cfdata_ioconf_spkr); 639bccce848Spgoyette if (error) 640bccce848Spgoyette devsw_attach(spkr_cd.cd_name, NULL, &bmajor, 641bccce848Spgoyette &spkr_cdevsw, &cmajor); 6426391aa4bSpgoyette #endif 64362b1b0deSchristos break; 644bccce848Spgoyette 64562b1b0deSchristos default: 64662b1b0deSchristos error = ENOTTY; 64762b1b0deSchristos break; 64862b1b0deSchristos } 64962b1b0deSchristos 65062b1b0deSchristos return error; 65162b1b0deSchristos } 652