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