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