xref: /netbsd-src/sys/arch/x68k/dev/opmbell.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: opmbell.c,v 1.27 2015/08/22 14:11:19 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.27 2015/08/22 14:11:19 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 "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
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