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