xref: /netbsd/sys/arch/x68k/dev/opmbell.c (revision 70747dc1)
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