opmbell.c revision 1.10 1 /* $NetBSD: opmbell.c,v 1.10 2002/10/23 09:12:46 jdolecek 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 #include <sys/event.h>
59
60 #include <x68k/x68k/iodevice.h>
61 #include <machine/opmbellio.h>
62 #include <x68k/dev/opmreg.h>
63 #include <x68k/dev/opmbellvar.h>
64
65 /* In opm.c. */
66 void opm_set_volume __P((int, int));
67 void opm_set_key __P((int, int));
68 void opm_set_voice __P((int, struct opm_voice *));
69 void opm_key_on __P((u_char));
70 void opm_key_off __P((u_char));
71
72 static u_int bell_pitchtokey __P((u_int));
73 static void bell_timeout __P((void *));
74
75 struct bell_softc {
76 int sc_flags;
77 u_char ch;
78 u_char volume;
79 u_int pitch;
80 u_int msec;
81 u_int key;
82 };
83
84 struct bell_softc *bell_softc;
85
86 struct callout bell_ch = CALLOUT_INITIALIZER;
87
88 static struct opm_voice vtab[NBELL];
89
90 /* sc_flags values */
91 #define BELLF_READ 0x01
92 #define BELLF_WRITE 0x02
93 #define BELLF_ALIVE 0x04
94 #define BELLF_OPEN 0x08
95 #define BELLF_OUT 0x10
96 #define BELLF_ON 0x20
97
98 #define UNIT(x) minor(x)
99
100 void bell_on __P((struct bell_softc *sc));
101 void bell_off __P((struct bell_softc *sc));
102 void opm_bell __P((void));
103 void opm_bell_on __P((void));
104 void opm_bell_off __P((void));
105 int opm_bell_setup __P((struct bell_info *));
106 int bellmstohz __P((int));
107
108 void bellattach __P((int));
109
110 dev_type_open(bellopen);
111 dev_type_close(bellclose);
112 dev_type_ioctl(bellioctl);
113
114 const struct cdevsw bell_cdevsw = {
115 bellopen, bellclose, noread, nowrite, bellioctl,
116 nostop, notty, nopoll, nommap, nokqfilter,
117 };
118
119 void
120 bellattach(num)
121 int num;
122 {
123 char *mem;
124 register u_long size;
125 register struct bell_softc *sc;
126 int unit;
127
128 if (num <= 0)
129 return;
130 size = num * sizeof(struct bell_softc);
131 mem = malloc(size, M_DEVBUF, M_NOWAIT);
132 if (mem == NULL) {
133 printf("WARNING: no memory for opm bell\n");
134 return;
135 }
136 memset(mem, 0, size);
137 bell_softc = (struct bell_softc *)mem;
138
139 for (unit = 0; unit < num; unit++) {
140 sc = &bell_softc[unit];
141 sc->sc_flags = BELLF_ALIVE;
142 sc->ch = BELL_CHANNEL;
143 sc->volume = BELL_VOLUME;
144 sc->pitch = BELL_PITCH;
145 sc->msec = BELL_DURATION;
146 sc->key = bell_pitchtokey(sc->pitch);
147
148 /* setup initial voice parameter */
149 memcpy(&vtab[unit], &bell_voice, sizeof(bell_voice));
150 opm_set_voice(sc->ch, &vtab[unit]);
151
152 printf("bell%d: YM2151 OPM bell emulation.\n", unit);
153 }
154 }
155
156 int
157 bellopen(dev, flags, mode, p)
158 dev_t dev;
159 int flags, mode;
160 struct proc *p;
161 {
162 register int unit = UNIT(dev);
163 register struct bell_softc *sc = &bell_softc[unit];
164
165 if (unit >= NBELL || !(sc->sc_flags & BELLF_ALIVE))
166 return ENXIO;
167
168 if (sc->sc_flags & BELLF_OPEN)
169 return EBUSY;
170
171 sc->sc_flags |= BELLF_OPEN;
172 sc->sc_flags |= (flags & (FREAD | FWRITE));
173
174 return 0;
175 }
176
177 int
178 bellclose(dev, flags, mode, p)
179 dev_t dev;
180 int flags, mode;
181 struct proc *p;
182 {
183 int unit = UNIT(dev);
184 struct bell_softc *sc = &bell_softc[unit];
185
186 sc->sc_flags &= ~BELLF_OPEN;
187 return 0;
188 }
189
190 int
191 bellioctl(dev, cmd, addr, flag, p)
192 dev_t dev;
193 u_long cmd;
194 caddr_t addr;
195 int flag;
196 struct proc *p;
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(pitch)
371 u_int pitch;
372 {
373 int i, oct;
374 u_int key;
375
376 i = 16 * pitch / 440;
377 for (oct = -1; i > 0; i >>= 1, oct++)
378 ;
379
380 i = (pitch * 16 - (440 * (1 << oct))) / (1 << oct);
381 key = (oct << 12) + note[i];
382
383 return key;
384 }
385
386 /*
387 * The next table is a little trikcy table of volume factors.
388 * Its values have been calculated as table[i] = -15 * log10(i/100)
389 * with an obvious exception for i = 0; This log-table converts a linear
390 * volume-scaling (0...100) to a logarithmic scaling as present in the
391 * OPM chips. so: Volume 50% = 6 db.
392 */
393
394 static u_char vol_table[] = {
395 0x7f, 0x35, 0x2d, 0x28, 0x25, 0x22, 0x20, 0x1e,
396 0x1d, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15,
397 0x15, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11,
398 0x10, 0x10, 0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0d,
399 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0a,
400 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x08, 0x08,
401 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06,
402 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05,
403 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03,
404 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
405 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
406 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
407 0x00, 0x00, 0x00, 0x00, 0x00,
408 };
409
410 void
411 bell_on(sc)
412 register struct bell_softc *sc;
413 {
414 int sps;
415
416 sps = spltty();
417 opm_set_volume(sc->ch, vol_table[sc->volume]);
418 opm_set_key(sc->ch, sc->key);
419 splx(sps);
420
421 opm_key_on(sc->ch);
422 sc->sc_flags |= BELLF_ON;
423 }
424
425 void
426 bell_off(sc)
427 register struct bell_softc *sc;
428 {
429 if (sc->sc_flags & BELLF_ON) {
430 opm_key_off(sc->ch);
431 sc->sc_flags &= ~BELLF_ON;
432 }
433 }
434
435 void
436 opm_bell()
437 {
438 register struct bell_softc *sc = &bell_softc[0];
439 register int ticks;
440
441 if (sc->msec != 0) {
442 if (sc->sc_flags & BELLF_OUT) {
443 bell_timeout(0);
444 } else if (sc->sc_flags & BELLF_ON)
445 return;
446
447 ticks = bellmstohz(sc->msec);
448
449 bell_on(sc);
450 sc->sc_flags |= BELLF_OUT;
451
452 callout_reset(&bell_ch, ticks, bell_timeout, NULL);
453 }
454 }
455
456 static void
457 bell_timeout(arg)
458 void *arg;
459 {
460 struct bell_softc *sc = &bell_softc[0];
461
462 sc->sc_flags &= ~BELLF_OUT;
463 bell_off(sc);
464 callout_stop(&bell_ch);
465 }
466
467 void
468 opm_bell_on()
469 {
470 register struct bell_softc *sc = &bell_softc[0];
471
472 if (sc->sc_flags & BELLF_OUT)
473 bell_timeout(0);
474 if (sc->sc_flags & BELLF_ON)
475 return;
476
477 bell_on(sc);
478 }
479
480 void
481 opm_bell_off()
482 {
483 register struct bell_softc *sc = &bell_softc[0];
484
485 if (sc->sc_flags & BELLF_ON)
486 bell_off(sc);
487 }
488
489 int
490 opm_bell_setup(data)
491 struct bell_info *data;
492 {
493 register struct bell_softc *sc = &bell_softc[0];
494
495 /* bounds check */
496 if (data->pitch > MAXBPITCH || data->pitch < MINBPITCH ||
497 data->volume > MAXBVOLUME || data->msec > MAXBTIME) {
498 return EINVAL;
499 } else {
500 sc->volume = data->volume;
501 sc->pitch = data->pitch;
502 sc->msec = data->msec;
503
504 sc->key = bell_pitchtokey(data->pitch);
505 }
506 return 0;
507 }
508
509 int
510 bellmstohz(m)
511 int m;
512 {
513 extern int hz;
514 register int h = m;
515
516 if (h > 0) {
517 h = h * hz / 1000;
518 if (h == 0)
519 h = 1000 / hz;
520 }
521 return h;
522 }
523
524 #endif
525