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