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