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