649 lines
16 KiB
C
649 lines
16 KiB
C
/* $NetBSD: spkr.c,v 1.17.12.1 2021/04/03 22:28:43 thorpej Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1990 Eric S. Raymond (esr@snark.thyrsus.com)
|
|
* Copyright (c) 1990 Andrew A. Chernov (ache@astral.msk.su)
|
|
* Copyright (c) 1990 Lennart Augustsson (lennart@augustsson.net)
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Eric S. Raymond
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* spkr.c -- device driver for console speaker on 80386
|
|
*
|
|
* v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
|
|
* modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
|
|
* 386bsd only clean version, all SYSV stuff removed
|
|
* use hz value from param.c
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: spkr.c,v 1.17.12.1 2021/04/03 22:28:43 thorpej Exp $");
|
|
|
|
#if defined(_KERNEL_OPT)
|
|
#include "wsmux.h"
|
|
#endif
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/device.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/module.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <dev/spkrio.h>
|
|
#include <dev/spkrvar.h>
|
|
#include <dev/wscons/wsconsio.h>
|
|
#include <dev/wscons/wsbellvar.h>
|
|
#include <dev/wscons/wsbellmuxvar.h>
|
|
|
|
#include "ioconf.h"
|
|
|
|
dev_type_open(spkropen);
|
|
dev_type_close(spkrclose);
|
|
dev_type_write(spkrwrite);
|
|
dev_type_ioctl(spkrioctl);
|
|
|
|
const struct cdevsw spkr_cdevsw = {
|
|
.d_open = spkropen,
|
|
.d_close = spkrclose,
|
|
.d_read = noread,
|
|
.d_write = spkrwrite,
|
|
.d_ioctl = spkrioctl,
|
|
.d_stop = nostop,
|
|
.d_tty = notty,
|
|
.d_poll = nopoll,
|
|
.d_mmap = nommap,
|
|
.d_kqfilter = nokqfilter,
|
|
.d_discard = nodiscard,
|
|
.d_flag = D_OTHER
|
|
};
|
|
|
|
static void playinit(struct spkr_softc *);
|
|
static void playtone(struct spkr_softc *, int, int, int);
|
|
static void playstring(struct spkr_softc *, const char *, size_t);
|
|
|
|
/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
|
|
*
|
|
* Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
|
|
* M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
|
|
* String play is not interruptible except possibly at physical block
|
|
* boundaries.
|
|
*/
|
|
|
|
/*
|
|
* Magic number avoidance...
|
|
*/
|
|
#define SECS_PER_MIN 60 /* seconds per minute */
|
|
#define WHOLE_NOTE 4 /* quarter notes per whole note */
|
|
#define MIN_VALUE 64 /* the most we can divide a note by */
|
|
#define DFLT_VALUE 4 /* default value (quarter-note) */
|
|
#define FILLTIME 8 /* for articulation, break note in parts */
|
|
#define STACCATO 6 /* 6/8 = 3/4 of note is filled */
|
|
#define NORMAL 7 /* 7/8ths of note interval is filled */
|
|
#define LEGATO 8 /* all of note interval is filled */
|
|
#define DFLT_OCTAVE 4 /* default octave */
|
|
#define MIN_TEMPO 32 /* minimum tempo */
|
|
#define DFLT_TEMPO 120 /* default tempo */
|
|
#define MAX_TEMPO 255 /* max tempo */
|
|
#define NUM_MULT 3 /* numerator of dot multiplier */
|
|
#define DENOM_MULT 2 /* denominator of dot multiplier */
|
|
|
|
/* letter to half-tone: A B C D E F G */
|
|
static const int notetab[8] = { 9, 11, 0, 2, 4, 5, 7 };
|
|
|
|
/*
|
|
* This is the American Standard A440 Equal-Tempered scale with frequencies
|
|
* rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
|
|
* our octave 0 is standard octave 2.
|
|
*/
|
|
#define OCTAVE_NOTES 12 /* semitones per octave */
|
|
static const int pitchtab[] =
|
|
{
|
|
/* C C# D D# E F F# G G# A A# B*/
|
|
/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
|
|
/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
|
|
/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
|
|
/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
|
|
/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
|
|
/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
|
|
/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
|
|
};
|
|
#define NOCTAVES (int)(__arraycount(pitchtab) / OCTAVE_NOTES)
|
|
|
|
static void
|
|
playinit(struct spkr_softc *sc)
|
|
{
|
|
sc->sc_octave = DFLT_OCTAVE;
|
|
sc->sc_whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
|
|
sc->sc_fill = NORMAL;
|
|
sc->sc_value = DFLT_VALUE;
|
|
sc->sc_octtrack = false;
|
|
sc->sc_octprefix = true;/* act as though there was an initial O(n) */
|
|
}
|
|
|
|
#define SPKRPRI (PZERO - 1)
|
|
|
|
/* Rest for given number of ticks */
|
|
static void
|
|
rest(struct spkr_softc *sc, int ticks)
|
|
{
|
|
|
|
#ifdef SPKRDEBUG
|
|
device_printf(sc->sc_dev, "%s: rest for %d ticks\n", __func__, ticks);
|
|
#endif /* SPKRDEBUG */
|
|
KASSERT(ticks > 0);
|
|
|
|
tsleep(sc->sc_dev, SPKRPRI | PCATCH, device_xname(sc->sc_dev), ticks);
|
|
}
|
|
|
|
/*
|
|
* Play tone of proper duration for current rhythm signature.
|
|
* note indicates "O0C" = 0, "O0C#" = 1, "O0D" = 2, ... , and
|
|
* -1 indiacates a rest.
|
|
* val indicates the length, "L4" = 4, "L8" = 8.
|
|
* sustain indicates the number of subsequent dots that extend the sound
|
|
* by one a half.
|
|
*/
|
|
static void
|
|
playtone(struct spkr_softc *sc, int note, int val, int sustain)
|
|
{
|
|
int whole;
|
|
int total;
|
|
int sound;
|
|
int silence;
|
|
|
|
/* this weirdness avoids floating-point arithmetic */
|
|
whole = sc->sc_whole;
|
|
for (; sustain; sustain--) {
|
|
whole *= NUM_MULT;
|
|
val *= DENOM_MULT;
|
|
}
|
|
|
|
/* Total length in tick */
|
|
total = whole / val;
|
|
|
|
if (note == -1) {
|
|
#ifdef SPKRDEBUG
|
|
device_printf(sc->sc_dev, "%s: rest for %d ticks\n",
|
|
__func__, total);
|
|
#endif /* SPKRDEBUG */
|
|
if (total != 0)
|
|
rest(sc, total);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Rest 1/8 (if NORMAL) or 3/8 (if STACCATO) in tick.
|
|
* silence should be rounded down.
|
|
*/
|
|
silence = total * (FILLTIME - sc->sc_fill) / FILLTIME;
|
|
sound = total - silence;
|
|
|
|
#ifdef SPKRDEBUG
|
|
device_printf(sc->sc_dev,
|
|
"%s: note %d for %d ticks, rest for %d ticks\n", __func__,
|
|
note, sound, silence);
|
|
#endif /* SPKRDEBUG */
|
|
|
|
if (sound != 0)
|
|
(*sc->sc_tone)(sc->sc_dev, pitchtab[note], sound);
|
|
if (silence != 0)
|
|
rest(sc, silence);
|
|
}
|
|
|
|
/* interpret and play an item from a notation string */
|
|
static void
|
|
playstring(struct spkr_softc *sc, const char *cp, size_t slen)
|
|
{
|
|
int pitch;
|
|
int lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
|
|
|
|
#define GETNUM(cp, v) \
|
|
for (v = 0; slen > 0 && isdigit((unsigned char)cp[1]); ) { \
|
|
v = v * 10 + (*++cp - '0'); \
|
|
slen--; \
|
|
}
|
|
|
|
for (; slen--; cp++) {
|
|
int sustain, timeval, tempo;
|
|
char c = toupper((unsigned char)*cp);
|
|
|
|
#ifdef SPKRDEBUG
|
|
if (0x20 <= c && c < 0x7f) {
|
|
device_printf(sc->sc_dev, "%s: '%c'\n", __func__, c);
|
|
} else {
|
|
device_printf(sc->sc_dev, "%s: (0x%x)\n", __func__, c);
|
|
}
|
|
#endif /* SPKRDEBUG */
|
|
|
|
switch (c) {
|
|
case 'A': case 'B': case 'C': case 'D':
|
|
case 'E': case 'F': case 'G':
|
|
/* compute pitch */
|
|
pitch = notetab[c - 'A'] + sc->sc_octave * OCTAVE_NOTES;
|
|
|
|
/* this may be followed by an accidental sign */
|
|
if (slen > 0 && (cp[1] == '#' || cp[1] == '+')) {
|
|
++pitch;
|
|
++cp;
|
|
slen--;
|
|
} else if (slen > 0 && cp[1] == '-') {
|
|
--pitch;
|
|
++cp;
|
|
slen--;
|
|
}
|
|
|
|
/*
|
|
* If octave-tracking mode is on, and there has been no
|
|
* octave- setting prefix, find the version of the
|
|
* current letter note * closest to the last
|
|
* regardless of octave.
|
|
*/
|
|
if (sc->sc_octtrack && !sc->sc_octprefix) {
|
|
int d = abs(pitch - lastpitch);
|
|
if (d > abs(pitch + OCTAVE_NOTES - lastpitch)) {
|
|
if (sc->sc_octave < NOCTAVES - 1) {
|
|
++sc->sc_octave;
|
|
pitch += OCTAVE_NOTES;
|
|
}
|
|
}
|
|
|
|
if (d > abs(pitch - OCTAVE_NOTES - lastpitch)) {
|
|
if (sc->sc_octave > 0) {
|
|
--sc->sc_octave;
|
|
pitch -= OCTAVE_NOTES;
|
|
}
|
|
}
|
|
}
|
|
sc->sc_octprefix = false;
|
|
lastpitch = pitch;
|
|
|
|
/*
|
|
* ...which may in turn be followed by an override
|
|
* time value
|
|
*/
|
|
GETNUM(cp, timeval);
|
|
if (timeval <= 0 || timeval > MIN_VALUE)
|
|
timeval = sc->sc_value;
|
|
|
|
/* ...and/or sustain dots */
|
|
for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
|
|
slen--;
|
|
sustain++;
|
|
}
|
|
|
|
/* time to emit the actual tone */
|
|
playtone(sc, pitch, timeval, sustain);
|
|
break;
|
|
|
|
case 'O':
|
|
if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
|
|
sc->sc_octprefix = sc->sc_octtrack = false;
|
|
++cp;
|
|
slen--;
|
|
} else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
|
|
sc->sc_octtrack = true;
|
|
++cp;
|
|
slen--;
|
|
} else {
|
|
GETNUM(cp, sc->sc_octave);
|
|
if (sc->sc_octave >= NOCTAVES)
|
|
sc->sc_octave = DFLT_OCTAVE;
|
|
sc->sc_octprefix = true;
|
|
}
|
|
break;
|
|
|
|
case '>':
|
|
if (sc->sc_octave < NOCTAVES - 1)
|
|
sc->sc_octave++;
|
|
sc->sc_octprefix = true;
|
|
break;
|
|
|
|
case '<':
|
|
if (sc->sc_octave > 0)
|
|
sc->sc_octave--;
|
|
sc->sc_octprefix = true;
|
|
break;
|
|
|
|
case 'N':
|
|
GETNUM(cp, pitch);
|
|
for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
|
|
slen--;
|
|
sustain++;
|
|
}
|
|
playtone(sc, pitch - 1, sc->sc_value, sustain);
|
|
break;
|
|
|
|
case 'L':
|
|
GETNUM(cp, sc->sc_value);
|
|
if (sc->sc_value <= 0 || sc->sc_value > MIN_VALUE)
|
|
sc->sc_value = DFLT_VALUE;
|
|
break;
|
|
|
|
case 'P':
|
|
case '~':
|
|
/* this may be followed by an override time value */
|
|
GETNUM(cp, timeval);
|
|
if (timeval <= 0 || timeval > MIN_VALUE)
|
|
timeval = sc->sc_value;
|
|
for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
|
|
slen--;
|
|
sustain++;
|
|
}
|
|
playtone(sc, -1, timeval, sustain);
|
|
break;
|
|
|
|
case 'T':
|
|
GETNUM(cp, tempo);
|
|
if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
|
|
tempo = DFLT_TEMPO;
|
|
sc->sc_whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
|
|
break;
|
|
|
|
case 'M':
|
|
if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
|
|
sc->sc_fill = NORMAL;
|
|
++cp;
|
|
slen--;
|
|
} else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
|
|
sc->sc_fill = LEGATO;
|
|
++cp;
|
|
slen--;
|
|
} else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's')) {
|
|
sc->sc_fill = STACCATO;
|
|
++cp;
|
|
slen--;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************* UNIX DRIVER HOOKS BEGIN HERE **************************/
|
|
#define spkrenter(d) device_lookup_private(&spkr_cd, d)
|
|
|
|
/*
|
|
* Attaches spkr. Specify tone function with the following specification:
|
|
*
|
|
* void
|
|
* tone(device_t self, u_int pitch, u_int tick)
|
|
* plays a beep with specified parameters.
|
|
* The argument 'pitch' specifies the pitch of a beep in Hz. The argument
|
|
* 'tick' specifies the period of a beep in tick(9). This function waits
|
|
* to finish playing the beep and then halts it.
|
|
* If the pitch is zero, it halts all sound if any (for compatibility
|
|
* with the past confused specifications, but there should be no sound at
|
|
* this point). And it returns immediately, without waiting ticks. So
|
|
* you cannot use this as a rest.
|
|
* If the tick is zero, it returns immediately.
|
|
*/
|
|
void
|
|
spkr_attach(device_t self, void (*tone)(device_t, u_int, u_int))
|
|
{
|
|
struct spkr_softc *sc = device_private(self);
|
|
|
|
#ifdef SPKRDEBUG
|
|
aprint_debug("%s: entering for unit %d\n", __func__, self->dv_unit);
|
|
#endif /* SPKRDEBUG */
|
|
sc->sc_dev = self;
|
|
sc->sc_tone = tone;
|
|
sc->sc_inbuf = NULL;
|
|
sc->sc_wsbelldev = NULL;
|
|
|
|
spkr_rescan(self, "", NULL);
|
|
}
|
|
|
|
int
|
|
spkr_detach(device_t self, int flags)
|
|
{
|
|
struct spkr_softc *sc = device_private(self);
|
|
int rc;
|
|
|
|
#ifdef SPKRDEBUG
|
|
aprint_debug("%s: entering for unit %d\n", __func__, self->dv_unit);
|
|
#endif /* SPKRDEBUG */
|
|
if (sc == NULL)
|
|
return ENXIO;
|
|
|
|
/* XXXNS If speaker never closes, we cannot complete the detach. */
|
|
while ((flags & DETACH_FORCE) != 0 && sc->sc_inbuf != NULL)
|
|
kpause("spkrwait", TRUE, 1, NULL);
|
|
if (sc->sc_inbuf != NULL)
|
|
return EBUSY;
|
|
|
|
rc = config_detach_children(self, flags);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
spkr_rescan(device_t self, const char *iattr, const int *locators)
|
|
{
|
|
#if NWSMUX > 0
|
|
struct spkr_softc *sc = device_private(self);
|
|
struct wsbelldev_attach_args a;
|
|
|
|
if (sc->sc_wsbelldev == NULL) {
|
|
a.accesscookie = sc;
|
|
sc->sc_wsbelldev = config_found(self, &a, wsbelldevprint);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
spkr_childdet(device_t self, device_t child)
|
|
{
|
|
struct spkr_softc *sc = device_private(self);
|
|
|
|
if (sc->sc_wsbelldev == child)
|
|
sc->sc_wsbelldev = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
spkropen(dev_t dev, int flags, int mode, struct lwp *l)
|
|
{
|
|
struct spkr_softc *sc = spkrenter(minor(dev));
|
|
|
|
#ifdef SPKRDEBUG
|
|
device_printf(sc->sc_dev, "%s: entering\n", __func__);
|
|
#endif /* SPKRDEBUG */
|
|
if (sc == NULL)
|
|
return ENXIO;
|
|
if (sc->sc_inbuf != NULL)
|
|
return EBUSY;
|
|
|
|
sc->sc_inbuf = malloc(DEV_BSIZE, M_DEVBUF, M_WAITOK);
|
|
playinit(sc);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
spkrwrite(dev_t dev, struct uio *uio, int flags)
|
|
{
|
|
struct spkr_softc *sc = spkrenter(minor(dev));
|
|
|
|
#ifdef SPKRDEBUG
|
|
device_printf(sc->sc_dev, "%s: entering with length = %zu\n",
|
|
__func__, uio->uio_resid);
|
|
#endif /* SPKRDEBUG */
|
|
if (sc == NULL)
|
|
return ENXIO;
|
|
if (sc->sc_inbuf == NULL)
|
|
return EINVAL;
|
|
|
|
size_t n = uimin(DEV_BSIZE, uio->uio_resid);
|
|
int error = uiomove(sc->sc_inbuf, n, uio);
|
|
if (error)
|
|
return error;
|
|
playstring(sc, sc->sc_inbuf, n);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
spkrclose(dev_t dev, int flags, int mode, struct lwp *l)
|
|
{
|
|
struct spkr_softc *sc = spkrenter(minor(dev));
|
|
|
|
#ifdef SPKRDEBUG
|
|
device_printf(sc->sc_dev, "%s: entering\n", __func__);
|
|
#endif /* SPKRDEBUG */
|
|
if (sc == NULL)
|
|
return ENXIO;
|
|
if (sc->sc_inbuf == NULL)
|
|
return EINVAL;
|
|
|
|
sc->sc_tone(sc->sc_dev, 0, 0);
|
|
free(sc->sc_inbuf, M_DEVBUF);
|
|
sc->sc_inbuf = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Play tone specified by tp.
|
|
* tp->frequency is the frequency (0 means a rest).
|
|
* tp->duration is the length in tick (returns immediately if 0).
|
|
*/
|
|
static void
|
|
playonetone(struct spkr_softc *sc, tone_t *tp)
|
|
{
|
|
if (tp->duration <= 0)
|
|
return;
|
|
|
|
if (tp->frequency == 0)
|
|
rest(sc, tp->duration);
|
|
else
|
|
(*sc->sc_tone)(sc->sc_dev, tp->frequency, tp->duration);
|
|
}
|
|
|
|
int
|
|
spkrioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
|
|
{
|
|
struct spkr_softc *sc = spkrenter(minor(dev));
|
|
tone_t *tp;
|
|
tone_t ttp;
|
|
int error;
|
|
|
|
#ifdef SPKRDEBUG
|
|
device_printf(sc->sc_dev, "%s: entering with cmd = %lx\n",
|
|
__func__, cmd);
|
|
#endif /* SPKRDEBUG */
|
|
if (sc == NULL)
|
|
return ENXIO;
|
|
if (sc->sc_inbuf == NULL)
|
|
return EINVAL;
|
|
|
|
switch (cmd) {
|
|
case SPKRTONE:
|
|
playonetone(sc, data);
|
|
return 0;
|
|
case SPKRTUNE:
|
|
for (tp = *(void **)data;; tp++) {
|
|
error = copyin(tp, &ttp, sizeof(tone_t));
|
|
if (error)
|
|
return(error);
|
|
if (ttp.duration == 0)
|
|
break;
|
|
playonetone(sc, &ttp);
|
|
}
|
|
return 0;
|
|
case SPKRGETVOL:
|
|
if (data != NULL)
|
|
*(u_int *)data = sc->sc_vol;
|
|
return 0;
|
|
case SPKRSETVOL:
|
|
if (data != NULL && *(u_int *)data <= 100)
|
|
sc->sc_vol = *(u_int *)data;
|
|
return 0;
|
|
default:
|
|
return ENOTTY;
|
|
}
|
|
}
|
|
|
|
#ifdef _MODULE
|
|
#include "ioconf.c"
|
|
#endif
|
|
|
|
MODULE(MODULE_CLASS_DRIVER, spkr, "audio" /* and/or pcppi */ );
|
|
|
|
int
|
|
spkr_modcmd(modcmd_t cmd, void *arg)
|
|
{
|
|
int error = 0;
|
|
#ifdef _MODULE
|
|
devmajor_t bmajor, cmajor;
|
|
#endif
|
|
|
|
switch(cmd) {
|
|
case MODULE_CMD_INIT:
|
|
#ifdef _MODULE
|
|
bmajor = cmajor = -1;
|
|
error = devsw_attach(spkr_cd.cd_name, NULL, &bmajor,
|
|
&spkr_cdevsw, &cmajor);
|
|
if (error)
|
|
break;
|
|
|
|
error = config_init_component(cfdriver_ioconf_spkr,
|
|
cfattach_ioconf_spkr, cfdata_ioconf_spkr);
|
|
if (error) {
|
|
devsw_detach(NULL, &spkr_cdevsw);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case MODULE_CMD_FINI:
|
|
#ifdef _MODULE
|
|
devsw_detach(NULL, &spkr_cdevsw);
|
|
error = config_fini_component(cfdriver_ioconf_spkr,
|
|
cfattach_ioconf_spkr, cfdata_ioconf_spkr);
|
|
if (error)
|
|
devsw_attach(spkr_cd.cd_name, NULL, &bmajor,
|
|
&spkr_cdevsw, &cmajor);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
error = ENOTTY;
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|