opmbell.c revision 1.12 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