Home | History | Annotate | Line # | Download | only in dev
spkr.c revision 1.9
      1  1.8       nat /*	$NetBSD: spkr.c,v 1.9 2017/06/11 03:55:56 nat Exp $	*/
      2  1.1  christos 
      3  1.1  christos /*
      4  1.1  christos  * Copyright (c) 1990 Eric S. Raymond (esr (at) snark.thyrsus.com)
      5  1.1  christos  * Copyright (c) 1990 Andrew A. Chernov (ache (at) astral.msk.su)
      6  1.1  christos  * Copyright (c) 1990 Lennart Augustsson (lennart (at) augustsson.net)
      7  1.1  christos  * All rights reserved.
      8  1.1  christos  *
      9  1.1  christos  * Redistribution and use in source and binary forms, with or without
     10  1.1  christos  * modification, are permitted provided that the following conditions
     11  1.1  christos  * are met:
     12  1.1  christos  * 1. Redistributions of source code must retain the above copyright
     13  1.1  christos  *    notice, this list of conditions and the following disclaimer.
     14  1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     15  1.1  christos  *    notice, this list of conditions and the following disclaimer in the
     16  1.1  christos  *    documentation and/or other materials provided with the distribution.
     17  1.1  christos  * 3. All advertising materials mentioning features or use of this software
     18  1.1  christos  *    must display the following acknowledgement:
     19  1.1  christos  *	This product includes software developed by Eric S. Raymond
     20  1.1  christos  * 4. The name of the author may not be used to endorse or promote products
     21  1.1  christos  *    derived from this software without specific prior written permission.
     22  1.1  christos  *
     23  1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     24  1.1  christos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     25  1.1  christos  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     26  1.1  christos  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     27  1.1  christos  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     28  1.1  christos  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     29  1.1  christos  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  1.1  christos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     31  1.1  christos  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     32  1.1  christos  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     33  1.1  christos  * POSSIBILITY OF SUCH DAMAGE.
     34  1.1  christos  */
     35  1.1  christos 
     36  1.1  christos /*
     37  1.1  christos  * spkr.c -- device driver for console speaker on 80386
     38  1.1  christos  *
     39  1.1  christos  * v1.1 by Eric S. Raymond (esr (at) snark.thyrsus.com) Feb 1990
     40  1.1  christos  *      modified for 386bsd by Andrew A. Chernov <ache (at) astral.msk.su>
     41  1.1  christos  *      386bsd only clean version, all SYSV stuff removed
     42  1.1  christos  *      use hz value from param.c
     43  1.1  christos  */
     44  1.1  christos 
     45  1.1  christos #include <sys/cdefs.h>
     46  1.8       nat __KERNEL_RCSID(0, "$NetBSD: spkr.c,v 1.9 2017/06/11 03:55:56 nat Exp $");
     47  1.1  christos 
     48  1.9       nat #include "wsmux.h"
     49  1.9       nat 
     50  1.1  christos #include <sys/param.h>
     51  1.1  christos #include <sys/systm.h>
     52  1.1  christos #include <sys/kernel.h>
     53  1.1  christos #include <sys/errno.h>
     54  1.1  christos #include <sys/device.h>
     55  1.1  christos #include <sys/malloc.h>
     56  1.1  christos #include <sys/module.h>
     57  1.1  christos #include <sys/uio.h>
     58  1.1  christos #include <sys/proc.h>
     59  1.1  christos #include <sys/ioctl.h>
     60  1.1  christos #include <sys/conf.h>
     61  1.1  christos 
     62  1.1  christos #include <sys/bus.h>
     63  1.1  christos 
     64  1.1  christos #include <dev/spkrio.h>
     65  1.2  christos #include <dev/spkrvar.h>
     66  1.9       nat #include <dev/wscons/wsconsio.h>
     67  1.9       nat #include <dev/wscons/wsbellvar.h>
     68  1.9       nat #include <dev/wscons/wsbellmuxvar.h>
     69  1.1  christos 
     70  1.1  christos dev_type_open(spkropen);
     71  1.1  christos dev_type_close(spkrclose);
     72  1.1  christos dev_type_write(spkrwrite);
     73  1.1  christos dev_type_ioctl(spkrioctl);
     74  1.1  christos 
     75  1.1  christos const struct cdevsw spkr_cdevsw = {
     76  1.1  christos 	.d_open = spkropen,
     77  1.1  christos 	.d_close = spkrclose,
     78  1.1  christos 	.d_read = noread,
     79  1.1  christos 	.d_write = spkrwrite,
     80  1.1  christos 	.d_ioctl = spkrioctl,
     81  1.1  christos 	.d_stop = nostop,
     82  1.1  christos 	.d_tty = notty,
     83  1.1  christos 	.d_poll = nopoll,
     84  1.1  christos 	.d_mmap = nommap,
     85  1.1  christos 	.d_kqfilter = nokqfilter,
     86  1.1  christos 	.d_discard = nodiscard,
     87  1.1  christos 	.d_flag = D_OTHER
     88  1.1  christos };
     89  1.1  christos 
     90  1.4  christos static void playinit(struct spkr_softc *);
     91  1.4  christos static void playtone(struct spkr_softc *, int, int, int);
     92  1.4  christos static void playstring(struct spkr_softc *, const char *, size_t);
     93  1.1  christos 
     94  1.1  christos /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
     95  1.1  christos  *
     96  1.1  christos  * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
     97  1.1  christos  * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
     98  1.1  christos  * Requires spkr_tone(), spkr_rest(). String play is not interruptible
     99  1.1  christos  * except possibly at physical block boundaries.
    100  1.1  christos  */
    101  1.1  christos 
    102  1.1  christos #define dtoi(c)		((c) - '0')
    103  1.1  christos 
    104  1.1  christos /*
    105  1.1  christos  * Magic number avoidance...
    106  1.1  christos  */
    107  1.1  christos #define SECS_PER_MIN	60	/* seconds per minute */
    108  1.1  christos #define WHOLE_NOTE	4	/* quarter notes per whole note */
    109  1.1  christos #define MIN_VALUE	64	/* the most we can divide a note by */
    110  1.1  christos #define DFLT_VALUE	4	/* default value (quarter-note) */
    111  1.1  christos #define FILLTIME	8	/* for articulation, break note in parts */
    112  1.1  christos #define STACCATO	6	/* 6/8 = 3/4 of note is filled */
    113  1.1  christos #define NORMAL		7	/* 7/8ths of note interval is filled */
    114  1.1  christos #define LEGATO		8	/* all of note interval is filled */
    115  1.1  christos #define DFLT_OCTAVE	4	/* default octave */
    116  1.1  christos #define MIN_TEMPO	32	/* minimum tempo */
    117  1.1  christos #define DFLT_TEMPO	120	/* default tempo */
    118  1.1  christos #define MAX_TEMPO	255	/* max tempo */
    119  1.1  christos #define NUM_MULT	3	/* numerator of dot multiplier */
    120  1.1  christos #define DENOM_MULT	2	/* denominator of dot multiplier */
    121  1.1  christos 
    122  1.1  christos /* letter to half-tone:  A   B  C  D  E  F  G */
    123  1.1  christos static const int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
    124  1.1  christos 
    125  1.1  christos /*
    126  1.1  christos  * This is the American Standard A440 Equal-Tempered scale with frequencies
    127  1.1  christos  * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
    128  1.1  christos  * our octave 0 is standard octave 2.
    129  1.1  christos  */
    130  1.1  christos #define OCTAVE_NOTES	12	/* semitones per octave */
    131  1.1  christos static const int pitchtab[] =
    132  1.1  christos {
    133  1.1  christos /*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
    134  1.1  christos /* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
    135  1.1  christos /* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
    136  1.1  christos /* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
    137  1.1  christos /* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
    138  1.1  christos /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
    139  1.1  christos /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
    140  1.1  christos /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
    141  1.1  christos };
    142  1.1  christos #define NOCTAVES (int)(__arraycount(pitchtab) / OCTAVE_NOTES)
    143  1.1  christos 
    144  1.1  christos static void
    145  1.4  christos playinit(struct spkr_softc *sc)
    146  1.1  christos {
    147  1.4  christos 	sc->sc_octave = DFLT_OCTAVE;
    148  1.4  christos 	sc->sc_whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
    149  1.4  christos 	sc->sc_fill = NORMAL;
    150  1.4  christos 	sc->sc_value = DFLT_VALUE;
    151  1.4  christos 	sc->sc_octtrack = false;
    152  1.4  christos 	sc->sc_octprefix = true;/* act as though there was an initial O(n) */
    153  1.1  christos }
    154  1.1  christos 
    155  1.4  christos /* play tone of proper duration for current rhythm signature */
    156  1.1  christos static void
    157  1.4  christos playtone(struct spkr_softc *sc, int pitch, int val, int sustain)
    158  1.1  christos {
    159  1.4  christos 	int sound, silence, snum = 1, sdenom = 1;
    160  1.1  christos 
    161  1.4  christos 	/* this weirdness avoids floating-point arithmetic */
    162  1.4  christos 	for (; sustain; sustain--) {
    163  1.4  christos 		snum *= NUM_MULT;
    164  1.4  christos 		sdenom *= DENOM_MULT;
    165  1.4  christos 	}
    166  1.1  christos 
    167  1.4  christos 	if (pitch == -1) {
    168  1.4  christos 		(*sc->sc_rest)(sc->sc_dev, sc->sc_whole
    169  1.4  christos 		    * snum / (val * sdenom));
    170  1.4  christos 		return;
    171  1.4  christos 	}
    172  1.4  christos 
    173  1.4  christos 	int fac = sc->sc_whole * (FILLTIME - sc->sc_fill);
    174  1.4  christos 	int fval = FILLTIME * val;
    175  1.4  christos 	sound = (sc->sc_whole * snum) / (val * sdenom) -  fac / fval;
    176  1.4  christos 	silence = fac * snum / (fval * sdenom);
    177  1.1  christos 
    178  1.1  christos #ifdef SPKRDEBUG
    179  1.4  christos 	aprint_debug_dev(sc->sc_dev,
    180  1.4  christos 	    "%s: pitch %d for %d ticks, rest for %d ticks\n", __func__,
    181  1.1  christos 	    pitch, sound, silence);
    182  1.1  christos #endif /* SPKRDEBUG */
    183  1.1  christos 
    184  1.4  christos 	(*sc->sc_tone)(sc->sc_dev, pitchtab[pitch], sound);
    185  1.4  christos 	if (sc->sc_fill != LEGATO)
    186  1.4  christos 		(*sc->sc_rest)(sc->sc_dev, silence);
    187  1.1  christos }
    188  1.1  christos 
    189  1.4  christos /* interpret and play an item from a notation string */
    190  1.1  christos static void
    191  1.4  christos playstring(struct spkr_softc *sc, const char *cp, size_t slen)
    192  1.1  christos {
    193  1.4  christos 	int		pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
    194  1.4  christos 
    195  1.4  christos #define GETNUM(cp, v)	\
    196  1.4  christos 	for (v = 0; slen > 0 && isdigit((unsigned char)cp[1]); ) { \
    197  1.4  christos 		v = v * 10 + (*++cp - '0'); \
    198  1.4  christos 		slen--; \
    199  1.4  christos 	}
    200  1.1  christos 
    201  1.4  christos 	for (; slen--; cp++) {
    202  1.4  christos 		int sustain, timeval, tempo;
    203  1.4  christos 		char c = toupper((unsigned char)*cp);
    204  1.1  christos 
    205  1.1  christos #ifdef SPKRDEBUG
    206  1.4  christos 		aprint_debug_dev(sc->sc_dev, "%s: %c (%x)\n", __func__, c, c);
    207  1.1  christos #endif /* SPKRDEBUG */
    208  1.1  christos 
    209  1.4  christos 		switch (c) {
    210  1.4  christos 		case 'A':  case 'B': case 'C': case 'D':
    211  1.4  christos 		case 'E': case 'F': case 'G':
    212  1.4  christos 			/* compute pitch */
    213  1.4  christos 			pitch = notetab[c - 'A'] + sc->sc_octave * OCTAVE_NOTES;
    214  1.4  christos 
    215  1.4  christos 			/* this may be followed by an accidental sign */
    216  1.4  christos 			if (slen > 0 && (cp[1] == '#' || cp[1] == '+')) {
    217  1.4  christos 				++pitch;
    218  1.4  christos 				++cp;
    219  1.4  christos 				slen--;
    220  1.4  christos 			} else if (slen > 0 && cp[1] == '-') {
    221  1.4  christos 				--pitch;
    222  1.4  christos 				++cp;
    223  1.4  christos 				slen--;
    224  1.4  christos 			}
    225  1.4  christos 
    226  1.4  christos 			/*
    227  1.4  christos 			 * If octave-tracking mode is on, and there has been no
    228  1.4  christos 			 * octave- setting prefix, find the version of the
    229  1.4  christos 			 * current letter note * closest to the last
    230  1.4  christos 			 * regardless of octave.
    231  1.4  christos 			 */
    232  1.4  christos 			if (sc->sc_octtrack && !sc->sc_octprefix) {
    233  1.4  christos 				int d = abs(pitch - lastpitch);
    234  1.4  christos 				if (d > abs(pitch + OCTAVE_NOTES - lastpitch)) {
    235  1.4  christos 					if (sc->sc_octave < NOCTAVES - 1) {
    236  1.4  christos 						++sc->sc_octave;
    237  1.4  christos 						pitch += OCTAVE_NOTES;
    238  1.4  christos 					}
    239  1.4  christos 				}
    240  1.4  christos 
    241  1.4  christos 				if (d > abs(pitch - OCTAVE_NOTES - lastpitch)) {
    242  1.4  christos 					if (sc->sc_octave > 0) {
    243  1.4  christos 						--sc->sc_octave;
    244  1.4  christos 						pitch -= OCTAVE_NOTES;
    245  1.4  christos 					}
    246  1.4  christos 				}
    247  1.4  christos 			}
    248  1.4  christos 			sc->sc_octprefix = false;
    249  1.4  christos 			lastpitch = pitch;
    250  1.4  christos 
    251  1.4  christos 			/*
    252  1.4  christos 			 * ...which may in turn be followed by an override
    253  1.4  christos 			 * time value
    254  1.4  christos 			 */
    255  1.4  christos 			GETNUM(cp, timeval);
    256  1.4  christos 			if (timeval <= 0 || timeval > MIN_VALUE)
    257  1.4  christos 				timeval = sc->sc_value;
    258  1.4  christos 
    259  1.4  christos 			/* ...and/or sustain dots */
    260  1.4  christos 			for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
    261  1.4  christos 				slen--;
    262  1.4  christos 				sustain++;
    263  1.4  christos 			}
    264  1.4  christos 
    265  1.4  christos 			/* time to emit the actual tone */
    266  1.4  christos 			playtone(sc, pitch, timeval, sustain);
    267  1.4  christos 			break;
    268  1.4  christos 
    269  1.4  christos 		case 'O':
    270  1.4  christos 			if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
    271  1.4  christos 				sc->sc_octprefix = sc->sc_octtrack = false;
    272  1.4  christos 				++cp;
    273  1.4  christos 				slen--;
    274  1.4  christos 			} else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
    275  1.4  christos 				sc->sc_octtrack = true;
    276  1.4  christos 				++cp;
    277  1.4  christos 				slen--;
    278  1.4  christos 			} else {
    279  1.4  christos 				GETNUM(cp, sc->sc_octave);
    280  1.4  christos 				if (sc->sc_octave >= NOCTAVES)
    281  1.4  christos 					sc->sc_octave = DFLT_OCTAVE;
    282  1.4  christos 				sc->sc_octprefix = true;
    283  1.4  christos 			}
    284  1.4  christos 			break;
    285  1.4  christos 
    286  1.4  christos 		case '>':
    287  1.4  christos 			if (sc->sc_octave < NOCTAVES - 1)
    288  1.4  christos 				sc->sc_octave++;
    289  1.4  christos 			sc->sc_octprefix = true;
    290  1.4  christos 			break;
    291  1.4  christos 
    292  1.4  christos 		case '<':
    293  1.4  christos 			if (sc->sc_octave > 0)
    294  1.4  christos 				sc->sc_octave--;
    295  1.4  christos 			sc->sc_octprefix = true;
    296  1.4  christos 			break;
    297  1.4  christos 
    298  1.4  christos 		case 'N':
    299  1.4  christos 			GETNUM(cp, pitch);
    300  1.4  christos 			for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
    301  1.4  christos 				slen--;
    302  1.4  christos 				sustain++;
    303  1.4  christos 			}
    304  1.4  christos 			playtone(sc, pitch - 1, sc->sc_value, sustain);
    305  1.4  christos 			break;
    306  1.4  christos 
    307  1.4  christos 		case 'L':
    308  1.4  christos 			GETNUM(cp, sc->sc_value);
    309  1.4  christos 			if (sc->sc_value <= 0 || sc->sc_value > MIN_VALUE)
    310  1.4  christos 				sc->sc_value = DFLT_VALUE;
    311  1.4  christos 			break;
    312  1.4  christos 
    313  1.4  christos 		case 'P':
    314  1.4  christos 		case '~':
    315  1.4  christos 			/* this may be followed by an override time value */
    316  1.4  christos 			GETNUM(cp, timeval);
    317  1.4  christos 			if (timeval <= 0 || timeval > MIN_VALUE)
    318  1.4  christos 				timeval = sc->sc_value;
    319  1.4  christos 			for (sustain = 0; slen > 0 && cp[1] == '.'; cp++) {
    320  1.4  christos 				slen--;
    321  1.4  christos 				sustain++;
    322  1.4  christos 			}
    323  1.4  christos 			playtone(sc, -1, timeval, sustain);
    324  1.4  christos 			break;
    325  1.4  christos 
    326  1.4  christos 		case 'T':
    327  1.4  christos 			GETNUM(cp, tempo);
    328  1.4  christos 			if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
    329  1.4  christos 				tempo = DFLT_TEMPO;
    330  1.4  christos 			sc->sc_whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
    331  1.4  christos 			break;
    332  1.1  christos 
    333  1.4  christos 		case 'M':
    334  1.4  christos 			if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n')) {
    335  1.4  christos 				sc->sc_fill = NORMAL;
    336  1.4  christos 				++cp;
    337  1.4  christos 				slen--;
    338  1.4  christos 			} else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l')) {
    339  1.4  christos 				sc->sc_fill = LEGATO;
    340  1.4  christos 				++cp;
    341  1.4  christos 				slen--;
    342  1.4  christos 			} else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's')) {
    343  1.4  christos 				sc->sc_fill = STACCATO;
    344  1.4  christos 				++cp;
    345  1.4  christos 				slen--;
    346  1.4  christos 			}
    347  1.4  christos 			break;
    348  1.1  christos 		}
    349  1.1  christos 	}
    350  1.1  christos }
    351  1.1  christos 
    352  1.1  christos /******************* UNIX DRIVER HOOKS BEGIN HERE **************************
    353  1.1  christos  *
    354  1.1  christos  * This section implements driver hooks to run playstring() and the spkr_tone()
    355  1.1  christos  * and spkr_rest() functions defined above.
    356  1.1  christos  */
    357  1.4  christos extern struct cfdriver spkr_cd;
    358  1.4  christos #define spkrenter(d)	device_lookup_private(&spkr_cd, d)
    359  1.1  christos 
    360  1.4  christos void
    361  1.4  christos spkr_attach(device_t self, void (*tone)(device_t, u_int, u_int),
    362  1.4  christos     void (*rest)(device_t, int))
    363  1.4  christos {
    364  1.4  christos 	struct spkr_softc *sc = device_private(self);
    365  1.1  christos 
    366  1.6  pgoyette #ifdef SPKRDEBUG
    367  1.6  pgoyette 	aprint_debug("%s: entering for unit %d\n", __func__, self->dv_unit);
    368  1.6  pgoyette #endif /* SPKRDEBUG */
    369  1.4  christos 	sc->sc_dev = self;
    370  1.4  christos 	sc->sc_tone = tone;
    371  1.4  christos 	sc->sc_rest = rest;
    372  1.4  christos 	sc->sc_inbuf = NULL;
    373  1.9       nat 
    374  1.9       nat #if (NWSMUX > 0)
    375  1.9       nat 	struct wsbelldev_attach_args a;
    376  1.9       nat 
    377  1.9       nat 	a.accesscookie = sc;
    378  1.9       nat 	sc->sc_wsbelldev = config_found(self, &a, wsbelldevprint);
    379  1.9       nat #endif
    380  1.1  christos }
    381  1.1  christos 
    382  1.1  christos int
    383  1.6  pgoyette spkr_detach(device_t self, int flags)
    384  1.6  pgoyette {
    385  1.6  pgoyette 	struct spkr_softc *sc = device_private(self);
    386  1.6  pgoyette 
    387  1.6  pgoyette #ifdef SPKRDEBUG
    388  1.6  pgoyette 	aprint_debug("%s: entering for unit %d\n", __func__, self->dv_unit);
    389  1.6  pgoyette #endif /* SPKRDEBUG */
    390  1.6  pgoyette 	if (sc == NULL)
    391  1.6  pgoyette 		return ENXIO;
    392  1.6  pgoyette 	if (sc->sc_inbuf != NULL)
    393  1.6  pgoyette 		return EBUSY;
    394  1.6  pgoyette 
    395  1.6  pgoyette 	return 0;
    396  1.6  pgoyette }
    397  1.6  pgoyette 
    398  1.6  pgoyette int
    399  1.1  christos spkropen(dev_t dev, int	flags, int mode, struct lwp *l)
    400  1.1  christos {
    401  1.1  christos #ifdef SPKRDEBUG
    402  1.4  christos 	aprint_debug("%s: entering with dev = %"PRIx64"\n", __func__, dev);
    403  1.1  christos #endif /* SPKRDEBUG */
    404  1.4  christos 	struct spkr_softc *sc = spkrenter(minor(dev));
    405  1.1  christos 
    406  1.4  christos 	if (sc == NULL)
    407  1.4  christos 		return ENXIO;
    408  1.6  pgoyette 	if (sc->sc_inbuf != NULL)
    409  1.4  christos 		return EBUSY;
    410  1.4  christos 
    411  1.4  christos 	sc->sc_inbuf = malloc(DEV_BSIZE, M_DEVBUF, M_WAITOK);
    412  1.4  christos 	playinit(sc);
    413  1.4  christos 	return 0;
    414  1.1  christos }
    415  1.1  christos 
    416  1.1  christos int
    417  1.1  christos spkrwrite(dev_t dev, struct uio *uio, int flags)
    418  1.1  christos {
    419  1.1  christos #ifdef SPKRDEBUG
    420  1.4  christos 	aprint_debug("%s: entering with dev = %"PRIx64", count = %zu\n",
    421  1.4  christos 	    __func__, dev, uio->uio_resid);
    422  1.1  christos #endif /* SPKRDEBUG */
    423  1.4  christos 	struct spkr_softc *sc = spkrenter(minor(dev));
    424  1.1  christos 
    425  1.4  christos 	if (sc == NULL)
    426  1.4  christos 		return ENXIO;
    427  1.6  pgoyette 	if (sc->sc_inbuf == NULL)
    428  1.4  christos 		return EINVAL;
    429  1.4  christos 
    430  1.4  christos 	size_t n = min(DEV_BSIZE, uio->uio_resid);
    431  1.4  christos 	int error = uiomove(sc->sc_inbuf, n, uio);
    432  1.4  christos 	if (error)
    433  1.4  christos 		return error;
    434  1.4  christos 	playstring(sc, sc->sc_inbuf, n);
    435  1.4  christos 	return 0;
    436  1.1  christos }
    437  1.1  christos 
    438  1.1  christos int
    439  1.1  christos spkrclose(dev_t dev, int flags, int mode, struct lwp *l)
    440  1.1  christos {
    441  1.1  christos #ifdef SPKRDEBUG
    442  1.4  christos 	aprint_debug("%s: entering with dev = %"PRIx64"\n", __func__, dev);
    443  1.1  christos #endif /* SPKRDEBUG */
    444  1.4  christos 	struct spkr_softc *sc = spkrenter(minor(dev));
    445  1.4  christos 
    446  1.4  christos 	if (sc == NULL)
    447  1.4  christos 		return ENXIO;
    448  1.6  pgoyette 	if (sc->sc_inbuf == NULL)
    449  1.4  christos 		return EINVAL;
    450  1.4  christos 
    451  1.4  christos 	sc->sc_tone(sc->sc_dev, 0, 0);
    452  1.4  christos 	free(sc->sc_inbuf, M_DEVBUF);
    453  1.4  christos 	sc->sc_inbuf = NULL;
    454  1.1  christos 
    455  1.4  christos 	return 0;
    456  1.4  christos }
    457  1.4  christos 
    458  1.4  christos static void
    459  1.4  christos playonetone(struct spkr_softc *sc, tone_t *tp)
    460  1.4  christos {
    461  1.4  christos     if (tp->frequency == 0)
    462  1.4  christos 	    (*sc->sc_rest)(sc->sc_dev, tp->duration);
    463  1.1  christos     else
    464  1.4  christos 	    (*sc->sc_tone)(sc->sc_dev, tp->frequency, tp->duration);
    465  1.1  christos }
    466  1.1  christos 
    467  1.1  christos int
    468  1.1  christos spkrioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    469  1.1  christos {
    470  1.4  christos 	tone_t *tp;
    471  1.4  christos 	tone_t ttp;
    472  1.4  christos 	int error;
    473  1.1  christos #ifdef SPKRDEBUG
    474  1.4  christos 	aprint_debug("%s: entering with dev = %"PRIx64", cmd = %lx\n",
    475  1.4  christos 	    __func__, dev, cmd);
    476  1.1  christos #endif /* SPKRDEBUG */
    477  1.1  christos 
    478  1.4  christos 	struct spkr_softc *sc = spkrenter(minor(dev));
    479  1.1  christos 
    480  1.4  christos 	if (sc == NULL)
    481  1.4  christos 		return ENXIO;
    482  1.6  pgoyette 	if (sc->sc_inbuf == NULL)
    483  1.4  christos 		return EINVAL;
    484  1.4  christos 
    485  1.4  christos 	switch (cmd) {
    486  1.4  christos     	case SPKRTONE:
    487  1.4  christos 		playonetone(sc, data);
    488  1.4  christos 		return 0;
    489  1.4  christos 	case SPKRTUNE:
    490  1.4  christos 		for (tp = *(void **)data;; tp++) {
    491  1.4  christos 			error = copyin(tp, &ttp, sizeof(tone_t));
    492  1.4  christos 			if (error)
    493  1.4  christos 				return(error);
    494  1.4  christos 			if (ttp.duration == 0)
    495  1.4  christos 				break;
    496  1.4  christos 			playonetone(sc, &ttp);
    497  1.4  christos 		}
    498  1.4  christos 		return 0;
    499  1.8       nat 	case SPKRGETVOL:
    500  1.8       nat 		if (data != NULL)
    501  1.8       nat 			*(u_int *)data = sc->sc_vol;
    502  1.8       nat 		return 0;
    503  1.8       nat 	case SPKRSETVOL:
    504  1.8       nat 		if (data != NULL && *(u_int *)data <= 100)
    505  1.8       nat 			sc->sc_vol = *(u_int *)data;
    506  1.8       nat 		return 0;
    507  1.4  christos 	default:
    508  1.4  christos 		return ENOTTY;
    509  1.1  christos 	}
    510  1.1  christos }
    511  1.1  christos 
    512  1.3  christos #ifdef _MODULE
    513  1.3  christos extern struct cfdriver spkr_cd;
    514  1.3  christos #include "ioconf.c"
    515  1.3  christos #endif
    516  1.3  christos 
    517  1.7  pgoyette MODULE(MODULE_CLASS_DRIVER, spkr, "audio" /* and/or pcppi */ );
    518  1.5  pgoyette 
    519  1.1  christos int
    520  1.5  pgoyette spkr_modcmd(modcmd_t cmd, void *arg)
    521  1.1  christos {
    522  1.1  christos #ifdef _MODULE
    523  1.1  christos 	devmajor_t bmajor, cmajor;
    524  1.1  christos 	int error = 0;
    525  1.1  christos 
    526  1.1  christos 	switch(cmd) {
    527  1.1  christos 	case MODULE_CMD_INIT:
    528  1.1  christos 		bmajor = cmajor = -1;
    529  1.1  christos 		error = devsw_attach(spkr_cd.cd_name, NULL, &bmajor,
    530  1.1  christos 		    &spkr_cdevsw, &cmajor);
    531  1.1  christos 		if (error)
    532  1.1  christos 			break;
    533  1.1  christos 
    534  1.1  christos 		error = config_init_component(cfdriver_ioconf_spkr,
    535  1.6  pgoyette 		    cfattach_ioconf_spkr, cfdata_ioconf_spkr);
    536  1.1  christos 		if (error) {
    537  1.1  christos 			devsw_detach(NULL, &spkr_cdevsw);
    538  1.1  christos 		}
    539  1.1  christos 		break;
    540  1.1  christos 
    541  1.1  christos 	case MODULE_CMD_FINI:
    542  1.6  pgoyette 		devsw_detach(NULL, &spkr_cdevsw);
    543  1.1  christos 		error = config_fini_component(cfdriver_ioconf_spkr,
    544  1.6  pgoyette 		    cfattach_ioconf_spkr, cfdata_ioconf_spkr);
    545  1.6  pgoyette 		if (error)
    546  1.6  pgoyette 			devsw_attach(spkr_cd.cd_name, NULL, &bmajor,
    547  1.6  pgoyette 			    &spkr_cdevsw, &cmajor);
    548  1.1  christos 		break;
    549  1.6  pgoyette 
    550  1.1  christos 	default:
    551  1.1  christos 		error = ENOTTY;
    552  1.1  christos 		break;
    553  1.1  christos 	}
    554  1.1  christos 
    555  1.1  christos 	return error;
    556  1.3  christos #else
    557  1.3  christos 	return 0;
    558  1.3  christos #endif
    559  1.1  christos }
    560