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