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