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