1 /*-
2 * spkr.c -- device driver for console speaker
3 *
4 * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
5 * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
6 * modified for PC98 by Kakefuda
7 */
8
9 #include <sys/param.h>
10 #include <sys/systm.h>
11 #include <sys/kernel.h>
12 #include <sys/module.h>
13 #include <sys/sx.h>
14 #include <sys/uio.h>
15 #include <sys/conf.h>
16 #include <sys/ctype.h>
17 #include <sys/malloc.h>
18 #include <machine/clock.h>
19 #include <dev/speaker/speaker.h>
20
21 static d_open_t spkropen;
22 static d_close_t spkrclose;
23 static d_write_t spkrwrite;
24 static d_ioctl_t spkrioctl;
25
26 static struct cdevsw spkr_cdevsw = {
27 .d_version = D_VERSION,
28 .d_open = spkropen,
29 .d_close = spkrclose,
30 .d_write = spkrwrite,
31 .d_ioctl = spkrioctl,
32 .d_name = "spkr",
33 };
34
35 static MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer");
36
37 /*
38 **************** MACHINE DEPENDENT PART STARTS HERE *************************
39 * This section defines a function tone() which causes a tone of given
40 * frequency and duration from the ISA console speaker.
41 * Another function endtone() is defined to force sound off, and there is
42 * also a rest() entry point to do pauses.
43 *
44 * Audible sound is generated using the Programmable Interval Timer (PIT) and
45 * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The
46 * PPI controls whether sound is passed through at all; the PIT's channel 2 is
47 * used to generate clicks (a square wave) of whatever frequency is desired.
48 */
49
50 #define SPKRPRI PSOCK
51 static char endtone, endrest;
52
53 struct spkr_state;
54 static void tone(unsigned int thz, unsigned int centisecs);
55 static void rest(int centisecs);
56 static void playinit(struct spkr_state *state);
57 static void playtone(struct spkr_state *state, int pitch, int value, int sustain);
58 static void playstring(struct spkr_state *state, char *cp, size_t slen);
59
60 /*
61 * Emit tone of frequency thz for given number of centisecs
62 */
63 static void
tone(unsigned int thz,unsigned int centisecs)64 tone(unsigned int thz, unsigned int centisecs)
65 {
66 int timo;
67
68 if (thz <= 0)
69 return;
70
71 #ifdef DEBUG
72 (void) printf("tone: thz=%d centisecs=%d\n", thz, centisecs);
73 #endif /* DEBUG */
74
75 /*
76 * Acquire the i8254 clock, configure it to drive the speaker
77 * signal, and turn on the speaker.
78 */
79 if (timer_spkr_acquire()) {
80 return;
81 }
82 /* Configure the speaker with the tone frequency. */
83 timer_spkr_setfreq(thz);
84
85 /*
86 * Make the current thread sleep while the tone is being
87 * emitted.
88 */
89 timo = centisecs * hz / 100;
90 if (timo > 0)
91 tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", timo);
92
93 /*
94 * Turn off the speaker and release the i8254 clock.
95 */
96 timer_spkr_release();
97 }
98
99 /*
100 * Rest for given number of centisecs
101 */
102 static void
rest(int centisecs)103 rest(int centisecs)
104 {
105 int timo;
106
107 /*
108 * Set timeout to endrest function, then give up the timeslice.
109 * This is so other processes can execute while the rest is being
110 * waited out.
111 */
112 #ifdef DEBUG
113 (void) printf("rest: %d\n", centisecs);
114 #endif /* DEBUG */
115 timo = centisecs * hz / 100;
116 if (timo > 0)
117 tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", timo);
118 }
119
120 /*
121 **************** PLAY STRING INTERPRETER BEGINS HERE **********************
122 * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
123 * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
124 * tracking facility are added.
125 * Requires tone(), rest(), and endtone(). String play is not interruptible
126 * except possibly at physical block boundaries.
127 */
128
129 #define dtoi(c) ((c) - '0')
130
131 struct spkr_state {
132 char *inbuf; /* incoming melody buffer */
133
134 int octave; /* currently selected octave */
135 int whole; /* whole-note time at current tempo, in ticks */
136 int value; /* whole divisor for note time, quarter note = 1 */
137 int fill; /* controls spacing of notes */
138 bool octtrack; /* octave-tracking on? */
139 bool octprefix; /* override current octave-tracking state? */
140 };
141
142 /*
143 * Magic number avoidance...
144 */
145 #define SECS_PER_MIN 60 /* seconds per minute */
146 #define WHOLE_NOTE 4 /* quarter notes per whole note */
147 #define MIN_VALUE 64 /* the most we can divide a note by */
148 #define DFLT_VALUE 4 /* default value (quarter-note) */
149 #define FILLTIME 8 /* for articulation, break note in parts */
150 #define STACCATO 6 /* 6/8 = 3/4 of note is filled */
151 #define NORMAL 7 /* 7/8ths of note interval is filled */
152 #define LEGATO 8 /* all of note interval is filled */
153 #define DFLT_OCTAVE 4 /* default octave */
154 #define MIN_TEMPO 32 /* minimum tempo */
155 #define DFLT_TEMPO 120 /* default tempo */
156 #define MAX_TEMPO 255 /* max tempo */
157 #define NUM_MULT 3 /* numerator of dot multiplier */
158 #define DENOM_MULT 2 /* denominator of dot multiplier */
159
160 /* letter to half-tone: A B C D E F G */
161 static const int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
162
163 /*
164 * This is the American Standard A440 Equal-Tempered scale with frequencies
165 * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
166 * our octave 0 is standard octave 2.
167 */
168 #define OCTAVE_NOTES 12 /* semitones per octave */
169 static const int pitchtab[] =
170 {
171 /* C C# D D# E F F# G G# A A# B*/
172 /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
173 /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
174 /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
175 /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
176 /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
177 /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
178 /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
179 };
180
181 static void
playinit(struct spkr_state * state)182 playinit(struct spkr_state *state)
183 {
184 state->octave = DFLT_OCTAVE;
185 state->whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
186 state->fill = NORMAL;
187 state->value = DFLT_VALUE;
188 state->octtrack = false;
189 state->octprefix = true; /* act as though there was an initial O(n) */
190 }
191
192 /*
193 * Play tone of proper duration for current rhythm signature
194 */
195 static void
playtone(struct spkr_state * state,int pitch,int value,int sustain)196 playtone(struct spkr_state *state, int pitch, int value, int sustain)
197 {
198 int sound, silence, snum = 1, sdenom = 1;
199 int whole = state->whole;
200 int fill = state->fill;
201
202 /* this weirdness avoids floating-point arithmetic */
203 for (; sustain; sustain--) {
204 /* See the BUGS section in the man page for discussion */
205 snum *= NUM_MULT;
206 sdenom *= DENOM_MULT;
207 }
208
209 if (value == 0 || sdenom == 0)
210 return;
211
212 if (pitch == -1)
213 rest(whole * snum / (value * sdenom));
214 else {
215 sound = (whole * snum) / (value * sdenom)
216 - (whole * (FILLTIME - fill)) / (value * FILLTIME);
217 silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
218
219 #ifdef DEBUG
220 (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
221 pitch, sound, silence);
222 #endif /* DEBUG */
223
224 tone(pitchtab[pitch], sound);
225 if (fill != LEGATO)
226 rest(silence);
227 }
228 }
229
230 /*
231 * Interpret and play an item from a notation string
232 */
233 static void
playstring(struct spkr_state * state,char * cp,size_t slen)234 playstring(struct spkr_state *state, char *cp, size_t slen)
235 {
236 int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
237
238 #define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \
239 {v = v * 10 + (*++cp - '0'); slen--;}
240 for (; slen--; cp++) {
241 int sustain, timeval, tempo;
242 char c = toupper(*cp);
243
244 #ifdef DEBUG
245 (void) printf("playstring: %c (%x)\n", c, c);
246 #endif /* DEBUG */
247
248 switch (c) {
249 case 'A':
250 case 'B':
251 case 'C':
252 case 'D':
253 case 'E':
254 case 'F':
255 case 'G':
256 /* compute pitch */
257 pitch = notetab[c - 'A'] + state->octave * OCTAVE_NOTES;
258
259 /* this may be followed by an accidental sign */
260 if (cp[1] == '#' || cp[1] == '+') {
261 ++pitch;
262 ++cp;
263 slen--;
264 } else if (cp[1] == '-') {
265 --pitch;
266 ++cp;
267 slen--;
268 }
269
270 /*
271 * If octave-tracking mode is on, and there has been no octave-
272 * setting prefix, find the version of the current letter note
273 * closest to the last regardless of octave.
274 */
275 if (state->octtrack && !state->octprefix) {
276 if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES -
277 lastpitch)) {
278 ++state->octave;
279 pitch += OCTAVE_NOTES;
280 }
281
282 if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES) -
283 lastpitch)) {
284 --state->octave;
285 pitch -= OCTAVE_NOTES;
286 }
287 }
288 state->octprefix = false;
289 lastpitch = pitch;
290
291 /* ...which may in turn be followed by an override time value */
292 GETNUM(cp, timeval);
293 if (timeval <= 0 || timeval > MIN_VALUE)
294 timeval = state->value;
295
296 /* ...and/or sustain dots */
297 for (sustain = 0; cp[1] == '.'; cp++) {
298 slen--;
299 sustain++;
300 }
301
302 /* ...and/or a slur mark */
303 oldfill = state->fill;
304 if (cp[1] == '_') {
305 state->fill = LEGATO;
306 ++cp;
307 slen--;
308 }
309
310 /* time to emit the actual tone */
311 playtone(state, pitch, timeval, sustain);
312
313 state->fill = oldfill;
314 break;
315 case 'O':
316 if (cp[1] == 'N' || cp[1] == 'n') {
317 state->octprefix = state->octtrack = false;
318 ++cp;
319 slen--;
320 } else if (cp[1] == 'L' || cp[1] == 'l') {
321 state->octtrack = true;
322 ++cp;
323 slen--;
324 } else {
325 GETNUM(cp, state->octave);
326 if (state->octave >= nitems(pitchtab) / OCTAVE_NOTES)
327 state->octave = DFLT_OCTAVE;
328 state->octprefix = true;
329 }
330 break;
331 case '>':
332 if (state->octave < nitems(pitchtab) / OCTAVE_NOTES - 1)
333 state->octave++;
334 state->octprefix = true;
335 break;
336 case '<':
337 if (state->octave > 0)
338 state->octave--;
339 state->octprefix = true;
340 break;
341 case 'N':
342 GETNUM(cp, pitch);
343 for (sustain = 0; cp[1] == '.'; cp++) {
344 slen--;
345 sustain++;
346 }
347 oldfill = state->fill;
348 if (cp[1] == '_') {
349 state->fill = LEGATO;
350 ++cp;
351 slen--;
352 }
353 playtone(state, pitch - 1, state->value, sustain);
354 state->fill = oldfill;
355 break;
356 case 'L':
357 GETNUM(cp, state->value);
358 if (state->value <= 0 || state->value > MIN_VALUE)
359 state->value = DFLT_VALUE;
360 break;
361 case 'P':
362 case '~':
363 /* this may be followed by an override time value */
364 GETNUM(cp, timeval);
365 if (timeval <= 0 || timeval > MIN_VALUE)
366 timeval = state->value;
367 for (sustain = 0; cp[1] == '.'; cp++) {
368 slen--;
369 sustain++;
370 }
371 playtone(state, -1, timeval, sustain);
372 break;
373 case 'T':
374 GETNUM(cp, tempo);
375 if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
376 tempo = DFLT_TEMPO;
377 state->whole = (100 * SECS_PER_MIN * WHOLE_NOTE) / tempo;
378 break;
379 case 'M':
380 if (cp[1] == 'N' || cp[1] == 'n') {
381 state->fill = NORMAL;
382 ++cp;
383 slen--;
384 } else if (cp[1] == 'L' || cp[1] == 'l') {
385 state->fill = LEGATO;
386 ++cp;
387 slen--;
388 } else if (cp[1] == 'S' || cp[1] == 's') {
389 state->fill = STACCATO;
390 ++cp;
391 slen--;
392 }
393 break;
394 }
395 }
396 }
397
398 /*
399 * ****************** UNIX DRIVER HOOKS BEGIN HERE **************************
400 * This section implements driver hooks to run playstring() and the tone(),
401 * endtone(), and rest() functions defined above.
402 */
403
404 /*
405 * we use a lock to prevent interleaving of melodies written
406 * concurrently from two different threads.
407 */
408 static struct sx spkr_op_locked;
409
410 static void
spkr_dtor(void * data)411 spkr_dtor(void *data)
412 {
413 struct spkr_state *state = data;
414
415 free(state->inbuf, M_SPKR);
416 free(state, M_SPKR);
417 }
418
419 static int
spkropen(struct cdev * dev,int flags,int fmt,struct thread * td)420 spkropen(struct cdev *dev, int flags, int fmt, struct thread *td)
421 {
422 struct spkr_state *state;
423 int error;
424 #ifdef DEBUG
425 (void) printf("spkropen: entering with dev = %s\n", devtoname(dev));
426 #endif /* DEBUG */
427
428 /* allocate per-fd state */
429 state = malloc(sizeof(*state), M_SPKR, M_WAITOK | M_ZERO);
430 state->inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK);
431
432 /* initialize playstring state */
433 playinit(state);
434
435 /* store in the current fd private data */
436 error = devfs_set_cdevpriv(state, spkr_dtor);
437 if (error) {
438 free(state->inbuf, M_SPKR);
439 free(state, M_SPKR);
440 return (error);
441 }
442
443 return (0);
444 }
445
446 static int
spkrwrite(struct cdev * dev,struct uio * uio,int ioflag)447 spkrwrite(struct cdev *dev, struct uio *uio, int ioflag)
448 {
449 struct spkr_state *state;
450 int error;
451 unsigned n;
452
453 #ifdef DEBUG
454 printf("spkrwrite: entering with dev = %s, count = %zd\n",
455 devtoname(dev), uio->uio_resid);
456 #endif /* DEBUG */
457
458 /* is the melody too long? */
459 if (uio->uio_resid > (DEV_BSIZE - 1))
460 return (E2BIG);
461
462 /* get this fd's state */
463 error = devfs_get_cdevpriv((void **)&state);
464 if (error)
465 return (error);
466
467 /* copy melody from userspace */
468 n = uio->uio_resid;
469 error = uiomove(state->inbuf, n, uio);
470 if (error)
471 return (error);
472 state->inbuf[n] = '\0';
473
474 /* play the melody. */
475 sx_xlock(&spkr_op_locked);
476 playstring(state, state->inbuf, n);
477 sx_xunlock(&spkr_op_locked);
478
479 return (0);
480 }
481
482 static int
spkrclose(struct cdev * dev,int flags,int fmt,struct thread * td)483 spkrclose(struct cdev *dev, int flags, int fmt, struct thread *td)
484 {
485 #ifdef DEBUG
486 (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev));
487 #endif /* DEBUG */
488
489 wakeup(&endtone);
490 wakeup(&endrest);
491 /* devfs_clear_cdevpriv() calls spkr_dtor automatically */
492 return (0);
493 }
494
495 static int
spkrioctl(struct cdev * dev,unsigned long cmd,caddr_t cmdarg,int flags,struct thread * td)496 spkrioctl(struct cdev *dev, unsigned long cmd, caddr_t cmdarg, int flags,
497 struct thread *td)
498 {
499 #ifdef DEBUG
500 (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n",
501 devtoname(dev), cmd);
502 #endif /* DEBUG */
503
504 if (cmd == SPKRTONE) {
505 tone_t *tp = (tone_t *)cmdarg;
506
507 sx_xlock(&spkr_op_locked);
508 if (tp->frequency == 0)
509 rest(tp->duration);
510 else
511 tone(tp->frequency, tp->duration);
512 sx_xunlock(&spkr_op_locked);
513 return (0);
514 } else if (cmd == SPKRTUNE) {
515 tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg);
516 tone_t ttp;
517 int error;
518
519 sx_xlock(&spkr_op_locked);
520 for (; ; tp++) {
521 error = copyin(tp, &ttp, sizeof(tone_t));
522 if (error) {
523 sx_xunlock(&spkr_op_locked);
524 return (error);
525 }
526
527 if (ttp.duration == 0)
528 break;
529
530 if (ttp.frequency == 0)
531 rest(ttp.duration);
532 else
533 tone(ttp.frequency, ttp.duration);
534 }
535 sx_xunlock(&spkr_op_locked);
536 return (0);
537 }
538 return (EINVAL);
539 }
540
541 static struct cdev *speaker_dev;
542
543 /*
544 * Module handling
545 */
546 static int
speaker_modevent(module_t mod,int type,void * data)547 speaker_modevent(module_t mod, int type, void *data)
548 {
549 int error = 0;
550
551 switch(type) {
552 case MOD_LOAD:
553 sx_init(&spkr_op_locked, "spkr");
554 speaker_dev = make_dev(&spkr_cdevsw, 0,
555 UID_ROOT, GID_WHEEL, 0600, "speaker");
556 break;
557 case MOD_SHUTDOWN:
558 case MOD_UNLOAD:
559 destroy_dev(speaker_dev);
560 sx_destroy(&spkr_op_locked);
561 break;
562 default:
563 error = EOPNOTSUPP;
564 }
565 return (error);
566 }
567
568 DEV_MODULE(speaker, speaker_modevent, NULL);
569