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