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