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