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