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