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