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