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