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