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