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