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