Home | History | Annotate | Line # | Download | only in dev
opmbell.c revision 1.12
      1 /*	$NetBSD: opmbell.c,v 1.12 2004/05/08 08:38:36 minoura Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995 MINOURA Makoto, Takuya Harakawa.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by MINOURA Makoto,
     18  *	Takuya Harakawa.
     19  * 4. Neither the name of the authors may be used to endorse or promote
     20  *    products derived from this software without specific prior written
     21  *    permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  *
     35  */
     36 
     37 /*
     38  * bell device driver
     39  */
     40 
     41 #include <sys/cdefs.h>
     42 __KERNEL_RCSID(0, "$NetBSD: opmbell.c,v 1.12 2004/05/08 08:38:36 minoura Exp $");
     43 
     44 #include "bell.h"
     45 #if NBELL > 0
     46 
     47 #if NBELL > 1
     48 #undef NBELL
     49 #define NBELL 1
     50 #endif
     51 
     52 #include <sys/param.h>
     53 #include <sys/errno.h>
     54 #include <sys/uio.h>
     55 #include <sys/device.h>
     56 #include <sys/malloc.h>
     57 #include <sys/file.h>
     58 #include <sys/systm.h>
     59 #include <sys/callout.h>
     60 #include <sys/conf.h>
     61 #include <sys/event.h>
     62 
     63 #include <machine/opmbellio.h>
     64 
     65 #include <x68k/x68k/iodevice.h>
     66 #include <x68k/dev/opmvar.h>
     67 
     68 /* In opm.c. */
     69 void opm_set_volume __P((int, int));
     70 void opm_set_key __P((int, int));
     71 void opm_set_voice __P((int, struct opm_voice *));
     72 void opm_key_on __P((u_char));
     73 void opm_key_off __P((u_char));
     74 
     75 static u_int bell_pitchtokey __P((u_int));
     76 static void bell_timeout __P((void *));
     77 
     78 struct bell_softc {
     79 	int sc_flags;
     80 	u_char ch;
     81 	u_char volume;
     82 	u_int pitch;
     83 	u_int msec;
     84 	u_int key;
     85 };
     86 
     87 struct bell_softc *bell_softc;
     88 
     89 struct callout bell_ch = CALLOUT_INITIALIZER;
     90 
     91 static struct opm_voice vtab[NBELL];
     92 
     93 static struct opm_voice bell_voice = DEFAULT_BELL_VOICE;
     94 
     95 /* sc_flags values */
     96 #define	BELLF_READ	0x01
     97 #define	BELLF_WRITE	0x02
     98 #define	BELLF_ALIVE	0x04
     99 #define	BELLF_OPEN	0x08
    100 #define BELLF_OUT	0x10
    101 #define BELLF_ON	0x20
    102 
    103 #define UNIT(x)		minor(x)
    104 
    105 void bell_on __P((struct bell_softc *sc));
    106 void bell_off __P((struct bell_softc *sc));
    107 void opm_bell __P((void));
    108 void opm_bell_on __P((void));
    109 void opm_bell_off __P((void));
    110 int opm_bell_setup __P((struct bell_info *));
    111 int bellmstohz __P((int));
    112 
    113 void bellattach __P((int));
    114 
    115 dev_type_open(bellopen);
    116 dev_type_close(bellclose);
    117 dev_type_ioctl(bellioctl);
    118 
    119 const struct cdevsw bell_cdevsw = {
    120 	bellopen, bellclose, noread, nowrite, bellioctl,
    121 	nostop, notty, nopoll, nommap, nokqfilter,
    122 };
    123 
    124 void
    125 bellattach(num)
    126 	int num;
    127 {
    128 	char *mem;
    129 	register u_long size;
    130 	register struct bell_softc *sc;
    131 	int unit;
    132 
    133 	if (num <= 0)
    134 		return;
    135 	size = num * sizeof(struct bell_softc);
    136 	mem = malloc(size, M_DEVBUF, M_NOWAIT);
    137 	if (mem == NULL) {
    138 		printf("WARNING: no memory for opm bell\n");
    139 		return;
    140 	}
    141 	memset(mem, 0, size);
    142 	bell_softc = (struct bell_softc *)mem;
    143 
    144 	for (unit = 0; unit < num; unit++) {
    145 		sc = &bell_softc[unit];
    146 		sc->sc_flags = BELLF_ALIVE;
    147 		sc->ch = BELL_CHANNEL;
    148 		sc->volume = BELL_VOLUME;
    149 		sc->pitch = BELL_PITCH;
    150 		sc->msec = BELL_DURATION;
    151 		sc->key = bell_pitchtokey(sc->pitch);
    152 
    153 		/* setup initial voice parameter */
    154 		memcpy(&vtab[unit], &bell_voice, sizeof(bell_voice));
    155 		opm_set_voice(sc->ch, &vtab[unit]);
    156 
    157 		printf("bell%d: YM2151 OPM bell emulation.\n", unit);
    158 	}
    159 }
    160 
    161 int
    162 bellopen(dev, flags, mode, p)
    163 	dev_t dev;
    164 	int flags, mode;
    165 	struct proc *p;
    166 {
    167 	register int unit = UNIT(dev);
    168 	register struct bell_softc *sc = &bell_softc[unit];
    169 
    170 	if (unit >= NBELL || !(sc->sc_flags & BELLF_ALIVE))
    171 		return ENXIO;
    172 
    173 	if (sc->sc_flags & BELLF_OPEN)
    174 		return EBUSY;
    175 
    176 	sc->sc_flags |= BELLF_OPEN;
    177 	sc->sc_flags |= (flags & (FREAD | FWRITE));
    178 
    179 	return 0;
    180 }
    181 
    182 int
    183 bellclose(dev, flags, mode, p)
    184 	dev_t dev;
    185 	int flags, mode;
    186 	struct proc *p;
    187 {
    188 	int unit = UNIT(dev);
    189 	struct bell_softc *sc = &bell_softc[unit];
    190 
    191 	sc->sc_flags &= ~BELLF_OPEN;
    192 	return 0;
    193 }
    194 
    195 int
    196 bellioctl(dev, cmd, addr, flag, p)
    197 	dev_t dev;
    198 	u_long cmd;
    199 	caddr_t addr;
    200 	int flag;
    201 	struct proc *p;
    202 {
    203 	int unit = UNIT(dev);
    204 	struct bell_softc *sc = &bell_softc[unit];
    205 
    206 	switch (cmd) {
    207 	case BELLIOCGPARAM:
    208 	  {
    209 	      struct bell_info *bp = (struct bell_info *)addr;
    210 	      if (!(sc->sc_flags & FREAD))
    211 		  return EBADF;
    212 
    213 	      bp->volume = sc->volume;
    214 	      bp->pitch = sc->pitch;
    215 	      bp->msec = sc->msec;
    216 	      break;
    217 	  }
    218 
    219 	case BELLIOCSPARAM:
    220 	  {
    221 	      struct bell_info *bp = (struct bell_info *)addr;
    222 
    223 	      if (!(sc->sc_flags & FWRITE))
    224 		  return EBADF;
    225 
    226 	      return opm_bell_setup(bp);
    227 	  }
    228 
    229 	case BELLIOCGVOICE:
    230 	    if (!(sc->sc_flags & FREAD))
    231 		return EBADF;
    232 
    233 	    if (addr == NULL)
    234 		return EFAULT;
    235 
    236 	    memcpy(addr, &vtab[unit], sizeof(struct opm_voice));
    237 	    break;
    238 
    239 	case BELLIOCSVOICE:
    240 	    if (!(sc->sc_flags & FWRITE))
    241 		return EBADF;
    242 
    243 	    if (addr == NULL)
    244 		return EFAULT;
    245 
    246 	    memcpy(&vtab[unit], addr, sizeof(struct opm_voice));
    247 	    opm_set_voice(sc->ch, &vtab[unit]);
    248 	    break;
    249 
    250 	default:
    251 	    return EINVAL;
    252 	}
    253 	return 0;
    254 }
    255 
    256 /*
    257  * The next table is used for calculating KeyCode/KeyFraction pair
    258  * from frequency.
    259  */
    260 
    261 static u_int note[] = {
    262     0x0800, 0x0808, 0x0810, 0x081c,
    263     0x0824, 0x0830, 0x0838, 0x0844,
    264     0x084c, 0x0858, 0x0860, 0x086c,
    265     0x0874, 0x0880, 0x0888, 0x0890,
    266     0x089c, 0x08a4, 0x08b0, 0x08b8,
    267     0x08c4, 0x08cc, 0x08d8, 0x08e0,
    268     0x08ec, 0x08f4, 0x0900, 0x0908,
    269     0x0910, 0x091c, 0x0924, 0x092c,
    270     0x0938, 0x0940, 0x0948, 0x0954,
    271     0x095c, 0x0968, 0x0970, 0x0978,
    272     0x0984, 0x098c, 0x0994, 0x09a0,
    273     0x09a8, 0x09b4, 0x09bc, 0x09c4,
    274     0x09d0, 0x09d8, 0x09e0, 0x09ec,
    275     0x09f4, 0x0a00, 0x0a08, 0x0a10,
    276     0x0a18, 0x0a20, 0x0a28, 0x0a30,
    277     0x0a38, 0x0a44, 0x0a4c, 0x0a54,
    278     0x0a5c, 0x0a64, 0x0a6c, 0x0a74,
    279     0x0a80, 0x0a88, 0x0a90, 0x0a98,
    280     0x0aa0, 0x0aa8, 0x0ab0, 0x0ab8,
    281     0x0ac4, 0x0acc, 0x0ad4, 0x0adc,
    282     0x0ae4, 0x0aec, 0x0af4, 0x0c00,
    283     0x0c08, 0x0c10, 0x0c18, 0x0c20,
    284     0x0c28, 0x0c30, 0x0c38, 0x0c40,
    285     0x0c48, 0x0c50, 0x0c58, 0x0c60,
    286     0x0c68, 0x0c70, 0x0c78, 0x0c84,
    287     0x0c8c, 0x0c94, 0x0c9c, 0x0ca4,
    288     0x0cac, 0x0cb4, 0x0cbc, 0x0cc4,
    289     0x0ccc, 0x0cd4, 0x0cdc, 0x0ce4,
    290     0x0cec, 0x0cf4, 0x0d00, 0x0d04,
    291     0x0d0c, 0x0d14, 0x0d1c, 0x0d24,
    292     0x0d2c, 0x0d34, 0x0d3c, 0x0d44,
    293     0x0d4c, 0x0d54, 0x0d5c, 0x0d64,
    294     0x0d6c, 0x0d74, 0x0d7c, 0x0d80,
    295     0x0d88, 0x0d90, 0x0d98, 0x0da0,
    296     0x0da8, 0x0db0, 0x0db8, 0x0dc0,
    297     0x0dc8, 0x0dd0, 0x0dd8, 0x0de0,
    298     0x0de8, 0x0df0, 0x0df8, 0x0e00,
    299     0x0e04, 0x0e0c, 0x0e14, 0x0e1c,
    300     0x0e24, 0x0e28, 0x0e30, 0x0e38,
    301     0x0e40, 0x0e48, 0x0e50, 0x0e54,
    302     0x0e5c, 0x0e64, 0x0e6c, 0x0e74,
    303     0x0e7c, 0x0e80, 0x0e88, 0x0e90,
    304     0x0e98, 0x0ea0, 0x0ea8, 0x0eac,
    305     0x0eb4, 0x0ebc, 0x0ec4, 0x0ecc,
    306     0x0ed4, 0x0ed8, 0x0ee0, 0x0ee8,
    307     0x0ef0, 0x0ef8, 0x1000, 0x1004,
    308     0x100c, 0x1014, 0x1018, 0x1020,
    309     0x1028, 0x1030, 0x1034, 0x103c,
    310     0x1044, 0x104c, 0x1050, 0x1058,
    311     0x1060, 0x1064, 0x106c, 0x1074,
    312     0x107c, 0x1080, 0x1088, 0x1090,
    313     0x1098, 0x109c, 0x10a4, 0x10ac,
    314     0x10b0, 0x10b8, 0x10c0, 0x10c8,
    315     0x10cc, 0x10d4, 0x10dc, 0x10e4,
    316     0x10e8, 0x10f0, 0x10f8, 0x1100,
    317     0x1104, 0x110c, 0x1110, 0x1118,
    318     0x1120, 0x1124, 0x112c, 0x1134,
    319     0x1138, 0x1140, 0x1148, 0x114c,
    320     0x1154, 0x1158, 0x1160, 0x1168,
    321     0x116c, 0x1174, 0x117c, 0x1180,
    322     0x1188, 0x1190, 0x1194, 0x119c,
    323     0x11a4, 0x11a8, 0x11b0, 0x11b4,
    324     0x11bc, 0x11c4, 0x11c8, 0x11d0,
    325     0x11d8, 0x11dc, 0x11e4, 0x11ec,
    326     0x11f0, 0x11f8, 0x1200, 0x1204,
    327     0x120c, 0x1210, 0x1218, 0x121c,
    328     0x1224, 0x1228, 0x1230, 0x1238,
    329     0x123c, 0x1244, 0x1248, 0x1250,
    330     0x1254, 0x125c, 0x1260, 0x1268,
    331     0x1270, 0x1274, 0x127c, 0x1280,
    332     0x1288, 0x128c, 0x1294, 0x129c,
    333     0x12a0, 0x12a8, 0x12ac, 0x12b4,
    334     0x12b8, 0x12c0, 0x12c4, 0x12cc,
    335     0x12d4, 0x12d8, 0x12e0, 0x12e4,
    336     0x12ec, 0x12f0, 0x12f8, 0x1400,
    337     0x1404, 0x1408, 0x1410, 0x1414,
    338     0x141c, 0x1420, 0x1428, 0x142c,
    339     0x1434, 0x1438, 0x1440, 0x1444,
    340     0x1448, 0x1450, 0x1454, 0x145c,
    341     0x1460, 0x1468, 0x146c, 0x1474,
    342     0x1478, 0x1480, 0x1484, 0x1488,
    343     0x1490, 0x1494, 0x149c, 0x14a0,
    344     0x14a8, 0x14ac, 0x14b4, 0x14b8,
    345     0x14c0, 0x14c4, 0x14c8, 0x14d0,
    346     0x14d4, 0x14dc, 0x14e0, 0x14e8,
    347     0x14ec, 0x14f4, 0x14f8, 0x1500,
    348     0x1504, 0x1508, 0x1510, 0x1514,
    349     0x1518, 0x1520, 0x1524, 0x1528,
    350     0x1530, 0x1534, 0x1538, 0x1540,
    351     0x1544, 0x154c, 0x1550, 0x1554,
    352     0x155c, 0x1560, 0x1564, 0x156c,
    353     0x1570, 0x1574, 0x157c, 0x1580,
    354     0x1588, 0x158c, 0x1590, 0x1598,
    355     0x159c, 0x15a0, 0x15a8, 0x15ac,
    356     0x15b0, 0x15b8, 0x15bc, 0x15c4,
    357     0x15c8, 0x15cc, 0x15d4, 0x15d8,
    358     0x15dc, 0x15e4, 0x15e8, 0x15ec,
    359     0x15f4, 0x15f8, 0x1600, 0x1604,
    360     0x1608, 0x160c, 0x1614, 0x1618,
    361     0x161c, 0x1620, 0x1628, 0x162c,
    362     0x1630, 0x1638, 0x163c, 0x1640,
    363     0x1644, 0x164c, 0x1650, 0x1654,
    364     0x165c, 0x1660, 0x1664, 0x1668,
    365     0x1670, 0x1674, 0x1678, 0x1680,
    366     0x1684, 0x1688, 0x168c, 0x1694,
    367     0x1698, 0x169c, 0x16a0, 0x16a8,
    368     0x16ac, 0x16b0, 0x16b8, 0x16bc,
    369     0x16c0, 0x16c4, 0x16cc, 0x16d0,
    370     0x16d4, 0x16dc, 0x16e0, 0x16e4,
    371     0x16e8, 0x16f0, 0x16f4, 0x16f8,
    372 };
    373 
    374 static u_int
    375 bell_pitchtokey(pitch)
    376 	u_int pitch;
    377 {
    378     int i, oct;
    379     u_int key;
    380 
    381     i = 16 * pitch / 440;
    382     for (oct = -1; i > 0; i >>= 1, oct++)
    383 	;
    384 
    385     i  = (pitch * 16 - (440 * (1 << oct))) / (1 << oct);
    386     key = (oct << 12) + note[i];
    387 
    388     return key;
    389 }
    390 
    391 /*
    392  * The next table is a little trikcy table of volume factors.
    393  * Its values have been calculated as table[i] = -15 * log10(i/100)
    394  * with an obvious exception for i = 0; This log-table converts a linear
    395  * volume-scaling (0...100) to a logarithmic scaling as present in the
    396  * OPM chips. so: Volume 50% = 6 db.
    397  */
    398 
    399 static u_char vol_table[] = {
    400     0x7f, 0x35, 0x2d, 0x28, 0x25, 0x22, 0x20, 0x1e,
    401     0x1d, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15,
    402     0x15, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11,
    403     0x10, 0x10, 0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0d,
    404     0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0a,
    405     0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x08, 0x08,
    406     0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06,
    407     0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05,
    408     0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03,
    409     0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
    410     0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
    411     0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
    412     0x00, 0x00, 0x00, 0x00, 0x00,
    413 };
    414 
    415 void
    416 bell_on(sc)
    417 	register struct bell_softc *sc;
    418 {
    419     int sps;
    420 
    421     sps = spltty();
    422     opm_set_volume(sc->ch, vol_table[sc->volume]);
    423     opm_set_key(sc->ch, sc->key);
    424     splx(sps);
    425 
    426     opm_key_on(sc->ch);
    427     sc->sc_flags |= BELLF_ON;
    428 }
    429 
    430 void
    431 bell_off(sc)
    432 	register struct bell_softc *sc;
    433 {
    434     if (sc->sc_flags & BELLF_ON) {
    435 	opm_key_off(sc->ch);
    436 	sc->sc_flags &= ~BELLF_ON;
    437     }
    438 }
    439 
    440 void
    441 opm_bell()
    442 {
    443     register struct bell_softc *sc = &bell_softc[0];
    444     register int ticks;
    445 
    446     if (sc->msec != 0) {
    447 	if (sc->sc_flags & BELLF_OUT) {
    448 	    bell_timeout(0);
    449 	} else if (sc->sc_flags & BELLF_ON)
    450 	    return;
    451 
    452 	ticks = bellmstohz(sc->msec);
    453 
    454 	bell_on(sc);
    455 	sc->sc_flags |= BELLF_OUT;
    456 
    457 	callout_reset(&bell_ch, ticks, bell_timeout, NULL);
    458     }
    459 }
    460 
    461 static void
    462 bell_timeout(arg)
    463 	void *arg;
    464 {
    465     struct bell_softc *sc = &bell_softc[0];
    466 
    467     sc->sc_flags &= ~BELLF_OUT;
    468     bell_off(sc);
    469     callout_stop(&bell_ch);
    470 }
    471 
    472 void
    473 opm_bell_on()
    474 {
    475     register struct bell_softc *sc = &bell_softc[0];
    476 
    477     if (sc->sc_flags & BELLF_OUT)
    478 	bell_timeout(0);
    479     if (sc->sc_flags & BELLF_ON)
    480 	return;
    481 
    482     bell_on(sc);
    483 }
    484 
    485 void
    486 opm_bell_off()
    487 {
    488     register struct bell_softc *sc = &bell_softc[0];
    489 
    490     if (sc->sc_flags & BELLF_ON)
    491 	bell_off(sc);
    492 }
    493 
    494 int
    495 opm_bell_setup(data)
    496 	struct bell_info *data;
    497 {
    498     register struct bell_softc *sc = &bell_softc[0];
    499 
    500     /* bounds check */
    501     if (data->pitch > MAXBPITCH || data->pitch < MINBPITCH ||
    502 	data->volume > MAXBVOLUME || data->msec > MAXBTIME) {
    503 	return EINVAL;
    504     } else {
    505 	sc->volume = data->volume;
    506 	sc->pitch = data->pitch;
    507 	sc->msec = data->msec;
    508 
    509 	sc->key = bell_pitchtokey(data->pitch);
    510     }
    511     return 0;
    512 }
    513 
    514 int
    515 bellmstohz(m)
    516 	int m;
    517 {
    518     extern int hz;
    519     register int h = m;
    520 
    521     if (h > 0) {
    522 	h = h * hz / 1000;
    523 	if (h == 0)
    524 	    h = 1000 / hz;
    525     }
    526     return h;
    527 }
    528 
    529 #endif
    530