FreeBSD/Linux Kernel Cross Reference
sys/dev/isa/spkr.c
1 /* $NetBSD: spkr.c,v 1.15 2003/06/29 22:30:22 fvdl Exp $ */
2
3 /*
4 * Copyright (c) 1990 Eric S. Raymond (esr@snark.thyrsus.com)
5 * Copyright (c) 1990 Andrew A. Chernov (ache@astral.msk.su)
6 * Copyright (c) 1990 Lennart Augustsson (lennart@augustsson.net)
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Eric S. Raymond
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /*
37 * spkr.c -- device driver for console speaker on 80386
38 *
39 * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
40 * modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
41 * 386bsd only clean version, all SYSV stuff removed
42 * use hz value from param.c
43 */
44
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: spkr.c,v 1.15 2003/06/29 22:30:22 fvdl Exp $");
47
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/kernel.h>
51 #include <sys/errno.h>
52 #include <sys/device.h>
53 #include <sys/malloc.h>
54 #include <sys/uio.h>
55 #include <sys/proc.h>
56 #include <sys/ioctl.h>
57 #include <sys/conf.h>
58
59 #include <dev/isa/pcppivar.h>
60
61 #include <dev/isa/spkrio.h>
62
63 int spkrprobe __P((struct device *, struct cfdata *, void *));
64 void spkrattach __P((struct device *, struct device *, void *));
65
66 struct spkr_softc {
67 struct device sc_dev;
68 };
69
70 CFATTACH_DECL(spkr, sizeof(struct spkr_softc),
71 spkrprobe, spkrattach, NULL, NULL);
72
73 dev_type_open(spkropen);
74 dev_type_close(spkrclose);
75 dev_type_write(spkrwrite);
76 dev_type_ioctl(spkrioctl);
77
78 const struct cdevsw spkr_cdevsw = {
79 spkropen, spkrclose, noread, spkrwrite, spkrioctl,
80 nostop, notty, nopoll, nommap, nokqfilter,
81 };
82
83 static pcppi_tag_t ppicookie;
84
85 #define SPKRPRI (PZERO - 1)
86
87 static void tone __P((u_int, u_int));
88 static void rest __P((int));
89 static void playinit __P((void));
90 static void playtone __P((int, int, int));
91 static void playstring __P((char *, int));
92
93 static
94 void tone(hz, ticks)
95 /* emit tone of frequency hz for given number of ticks */
96 u_int hz, ticks;
97 {
98 pcppi_bell(ppicookie, hz, ticks, PCPPI_BELL_SLEEP);
99 }
100
101 static void
102 rest(ticks)
103 /* rest for given number of ticks */
104 int ticks;
105 {
106 /*
107 * Set timeout to endrest function, then give up the timeslice.
108 * This is so other processes can execute while the rest is being
109 * waited out.
110 */
111 #ifdef SPKRDEBUG
112 printf("rest: %d\n", ticks);
113 #endif /* SPKRDEBUG */
114 if (ticks > 0)
115 tsleep(rest, SPKRPRI | PCATCH, "rest", ticks);
116 }
117
118 /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
119 *
120 * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
121 * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
122 * Requires tone(), rest(), and endtone(). String play is not interruptible
123 * except possibly at physical block boundaries.
124 */
125
126 typedef int bool;
127 #define TRUE 1
128 #define FALSE 0
129
130 #define dtoi(c) ((c) - '')
131
132 static int octave; /* currently selected octave */
133 static int whole; /* whole-note time at current tempo, in ticks */
134 static int value; /* whole divisor for note time, quarter note = 1 */
135 static int fill; /* controls spacing of notes */
136 static bool octtrack; /* octave-tracking on? */
137 static bool octprefix; /* override current octave-tracking state? */
138
139 /*
140 * Magic number avoidance...
141 */
142 #define SECS_PER_MIN 60 /* seconds per minute */
143 #define WHOLE_NOTE 4 /* quarter notes per whole note */
144 #define MIN_VALUE 64 /* the most we can divide a note by */
145 #define DFLT_VALUE 4 /* default value (quarter-note) */
146 #define FILLTIME 8 /* for articulation, break note in parts */
147 #define STACCATO 6 /* 6/8 = 3/4 of note is filled */
148 #define NORMAL 7 /* 7/8ths of note interval is filled */
149 #define LEGATO 8 /* all of note interval is filled */
150 #define DFLT_OCTAVE 4 /* default octave */
151 #define MIN_TEMPO 32 /* minimum tempo */
152 #define DFLT_TEMPO 120 /* default tempo */
153 #define MAX_TEMPO 255 /* max tempo */
154 #define NUM_MULT 3 /* numerator of dot multiplier */
155 #define DENOM_MULT 2 /* denominator of dot multiplier */
156
157 /* letter to half-tone: A B C D E F G */
158 static const int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
159
160 /*
161 * This is the American Standard A440 Equal-Tempered scale with frequencies
162 * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
163 * our octave 0 is standard octave 2.
164 */
165 #define OCTAVE_NOTES 12 /* semitones per octave */
166 static const int pitchtab[] =
167 {
168 /* C C# D D# E F F# G G# A A# B*/
169 /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
170 /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
171 /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
172 /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
173 /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
174 /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
175 /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
176 };
177 #define NOCTAVES (sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
178
179 static void
180 playinit()
181 {
182 octave = DFLT_OCTAVE;
183 whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
184 fill = NORMAL;
185 value = DFLT_VALUE;
186 octtrack = FALSE;
187 octprefix = TRUE; /* act as though there was an initial O(n) */
188 }
189
190 static void
191 playtone(pitch, value, sustain)
192 /* play tone of proper duration for current rhythm signature */
193 int pitch, value, sustain;
194 {
195 int sound, silence, snum = 1, sdenom = 1;
196
197 /* this weirdness avoids floating-point arithmetic */
198 for (; sustain; sustain--)
199 {
200 snum *= NUM_MULT;
201 sdenom *= DENOM_MULT;
202 }
203
204 if (pitch == -1)
205 rest(whole * snum / (value * sdenom));
206 else
207 {
208 sound = (whole * snum) / (value * sdenom)
209 - (whole * (FILLTIME - fill)) / (value * FILLTIME);
210 silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
211
212 #ifdef SPKRDEBUG
213 printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
214 pitch, sound, silence);
215 #endif /* SPKRDEBUG */
216
217 tone(pitchtab[pitch], sound);
218 if (fill != LEGATO)
219 rest(silence);
220 }
221 }
222
223 static void
224 playstring(cp, slen)
225 /* interpret and play an item from a notation string */
226 char *cp;
227 int slen;
228 {
229 int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
230
231 #define GETNUM(cp, v) for(v=0; slen > 0 && isdigit(cp[1]); ) \
232 {v = v * 10 + (*++cp - ''); slen--;}
233 for (; slen--; cp++)
234 {
235 int sustain, timeval, tempo;
236 char c = toupper(*cp);
237
238 #ifdef SPKRDEBUG
239 printf("playstring: %c (%x)\n", c, c);
240 #endif /* SPKRDEBUG */
241
242 switch (c)
243 {
244 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
245
246 /* compute pitch */
247 pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
248
249 /* this may be followed by an accidental sign */
250 if (slen > 0 && (cp[1] == '#' || cp[1] == '+'))
251 {
252 ++pitch;
253 ++cp;
254 slen--;
255 }
256 else if (slen > 0 && cp[1] == '-')
257 {
258 --pitch;
259 ++cp;
260 slen--;
261 }
262
263 /*
264 * If octave-tracking mode is on, and there has been no octave-
265 * setting prefix, find the version of the current letter note
266 * closest to the last regardless of octave.
267 */
268 if (octtrack && !octprefix)
269 {
270 if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
271 {
272 if (octave < NOCTAVES - 1) {
273 ++octave;
274 pitch += OCTAVE_NOTES;
275 }
276 }
277
278 if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
279 {
280 if (octave > 0) {
281 --octave;
282 pitch -= OCTAVE_NOTES;
283 }
284 }
285 }
286 octprefix = FALSE;
287 lastpitch = pitch;
288
289 /* ...which may in turn be followed by an override time value */
290 GETNUM(cp, timeval);
291 if (timeval <= 0 || timeval > MIN_VALUE)
292 timeval = value;
293
294 /* ...and/or sustain dots */
295 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++)
296 {
297 slen--;
298 sustain++;
299 }
300
301 /* time to emit the actual tone */
302 playtone(pitch, timeval, sustain);
303 break;
304
305 case 'O':
306 if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n'))
307 {
308 octprefix = octtrack = FALSE;
309 ++cp;
310 slen--;
311 }
312 else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l'))
313 {
314 octtrack = TRUE;
315 ++cp;
316 slen--;
317 }
318 else
319 {
320 GETNUM(cp, octave);
321 if (octave >= NOCTAVES)
322 octave = DFLT_OCTAVE;
323 octprefix = TRUE;
324 }
325 break;
326
327 case '>':
328 if (octave < NOCTAVES - 1)
329 octave++;
330 octprefix = TRUE;
331 break;
332
333 case '<':
334 if (octave > 0)
335 octave--;
336 octprefix = TRUE;
337 break;
338
339 case 'N':
340 GETNUM(cp, pitch);
341 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++)
342 {
343 slen--;
344 sustain++;
345 }
346 playtone(pitch - 1, value, sustain);
347 break;
348
349 case 'L':
350 GETNUM(cp, value);
351 if (value <= 0 || value > MIN_VALUE)
352 value = DFLT_VALUE;
353 break;
354
355 case 'P':
356 case '~':
357 /* this may be followed by an override time value */
358 GETNUM(cp, timeval);
359 if (timeval <= 0 || timeval > MIN_VALUE)
360 timeval = value;
361 for (sustain = 0; slen > 0 && cp[1] == '.'; cp++)
362 {
363 slen--;
364 sustain++;
365 }
366 playtone(-1, timeval, sustain);
367 break;
368
369 case 'T':
370 GETNUM(cp, tempo);
371 if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
372 tempo = DFLT_TEMPO;
373 whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
374 break;
375
376 case 'M':
377 if (slen > 0 && (cp[1] == 'N' || cp[1] == 'n'))
378 {
379 fill = NORMAL;
380 ++cp;
381 slen--;
382 }
383 else if (slen > 0 && (cp[1] == 'L' || cp[1] == 'l'))
384 {
385 fill = LEGATO;
386 ++cp;
387 slen--;
388 }
389 else if (slen > 0 && (cp[1] == 'S' || cp[1] == 's'))
390 {
391 fill = STACCATO;
392 ++cp;
393 slen--;
394 }
395 break;
396 }
397 }
398 }
399
400 /******************* UNIX DRIVER HOOKS BEGIN HERE **************************
401 *
402 * This section implements driver hooks to run playstring() and the tone(),
403 * endtone(), and rest() functions defined above.
404 */
405
406 static int spkr_active; /* exclusion flag */
407 static void *spkr_inbuf;
408
409 static int spkr_attached = 0;
410
411 int
412 spkrprobe (parent, match, aux)
413 struct device *parent;
414 struct cfdata *match;
415 void *aux;
416 {
417 return (!spkr_attached);
418 }
419
420 void
421 spkrattach(parent, self, aux)
422 struct device *parent;
423 struct device *self;
424 void *aux;
425 {
426 printf("\n");
427 ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
428 spkr_attached = 1;
429 }
430
431 int
432 spkropen(dev, flags, mode, p)
433 dev_t dev;
434 int flags;
435 int mode;
436 struct proc *p;
437 {
438 #ifdef SPKRDEBUG
439 printf("spkropen: entering with dev = %x\n", dev);
440 #endif /* SPKRDEBUG */
441
442 if (minor(dev) != 0 || !spkr_attached)
443 return(ENXIO);
444 else if (spkr_active)
445 return(EBUSY);
446 else
447 {
448 playinit();
449 spkr_inbuf = malloc(DEV_BSIZE, M_DEVBUF, M_WAITOK);
450 spkr_active = 1;
451 }
452 return(0);
453 }
454
455 int
456 spkrwrite(dev, uio, flags)
457 dev_t dev;
458 struct uio *uio;
459 int flags;
460 {
461 int n;
462 int error;
463 #ifdef SPKRDEBUG
464 printf("spkrwrite: entering with dev = %x, count = %d\n",
465 dev, uio->uio_resid);
466 #endif /* SPKRDEBUG */
467
468 if (minor(dev) != 0)
469 return(ENXIO);
470 else
471 {
472 n = min(DEV_BSIZE, uio->uio_resid);
473 error = uiomove(spkr_inbuf, n, uio);
474 if (!error)
475 playstring((char *)spkr_inbuf, n);
476 return(error);
477 }
478 }
479
480 int spkrclose(dev, flags, mode, p)
481 dev_t dev;
482 int flags;
483 int mode;
484 struct proc *p;
485 {
486 #ifdef SPKRDEBUG
487 printf("spkrclose: entering with dev = %x\n", dev);
488 #endif /* SPKRDEBUG */
489
490 if (minor(dev) != 0)
491 return(ENXIO);
492 else
493 {
494 tone(0, 0);
495 free(spkr_inbuf, M_DEVBUF);
496 spkr_active = 0;
497 }
498 return(0);
499 }
500
501 int spkrioctl(dev, cmd, data, flag, p)
502 dev_t dev;
503 u_long cmd;
504 caddr_t data;
505 int flag;
506 struct proc *p;
507 {
508 #ifdef SPKRDEBUG
509 printf("spkrioctl: entering with dev = %x, cmd = %lx\n", dev, cmd);
510 #endif /* SPKRDEBUG */
511
512 if (minor(dev) != 0)
513 return(ENXIO);
514 else if (cmd == SPKRTONE)
515 {
516 tone_t *tp = (tone_t *)data;
517
518 if (tp->frequency == 0)
519 rest(tp->duration);
520 else
521 tone(tp->frequency, tp->duration);
522 }
523 else if (cmd == SPKRTUNE)
524 {
525 tone_t *tp = (tone_t *)(*(caddr_t *)data);
526 tone_t ttp;
527 int error;
528
529 for (; ; tp++) {
530 error = copyin(tp, &ttp, sizeof(tone_t));
531 if (error)
532 return(error);
533 if (ttp.duration == 0)
534 break;
535 if (ttp.frequency == 0)
536 rest(ttp.duration);
537 else
538 tone(ttp.frequency, ttp.duration);
539 }
540 }
541 else
542 return(EINVAL);
543 return(0);
544 }
545
546 /* spkr.c ends here */
Cache object: 7ece97c1a45341180a1e62e39a714bb4
|