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