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