Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / sys / dev / sound / isa / i386 / spkr / spkr.c
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  *
7  * $FreeBSD: src/sys/i386/isa/spkr.c,v 1.45 2000/01/29 16:00:32 peter Exp $
8  * $DragonFly: src/sys/dev/sound/isa/i386/spkr/Attic/spkr.c,v 1.2 2003/06/17 04:28:37 dillon Exp $
9  */
10
11 #include <sys/param.h>
12 #include <sys/systm.h>
13 #include <sys/kernel.h>
14 #include <sys/buf.h>
15 #include <sys/uio.h>
16 #include <sys/conf.h>
17 #include <sys/ctype.h>
18 #include <i386/isa/isa.h>
19 #include <i386/isa/timerreg.h>
20 #include <machine/clock.h>
21 #include <machine/speaker.h>
22
23 static  d_open_t        spkropen;
24 static  d_close_t       spkrclose;
25 static  d_write_t       spkrwrite;
26 static  d_ioctl_t       spkrioctl;
27
28 #define CDEV_MAJOR 26
29 static struct cdevsw spkr_cdevsw = {
30         /* open */      spkropen,
31         /* close */     spkrclose,
32         /* read */      noread,
33         /* write */     spkrwrite,
34         /* ioctl */     spkrioctl,
35         /* poll */      nopoll,
36         /* mmap */      nommap,
37         /* strategy */  nostrategy,
38         /* name */      "spkr",
39         /* maj */       CDEV_MAJOR,
40         /* dump */      nodump,
41         /* psize */     nopsize,
42         /* flags */     0,
43         /* bmaj */      -1
44 };
45
46 /**************** MACHINE DEPENDENT PART STARTS HERE *************************
47  *
48  * This section defines a function tone() which causes a tone of given
49  * frequency and duration from the ISA console speaker.
50  * Another function endtone() is defined to force sound off, and there is
51  * also a rest() entry point to do pauses.
52  *
53  * Audible sound is generated using the Programmable Interval Timer (PIT) and
54  * Programmable Peripheral Interface (PPI) attached to the ISA speaker. The
55  * PPI controls whether sound is passed through at all; the PIT's channel 2 is
56  * used to generate clicks (a square wave) of whatever frequency is desired.
57  */
58
59 /*
60  * PPI control values.
61  * XXX should be in a header and used in clock.c.
62  */
63 #define PPI_SPKR        0x03    /* turn these PPI bits on to pass sound */
64
65 #define SPKRPRI PSOCK
66 static char endtone, endrest;
67
68 static void tone __P((unsigned int thz, unsigned int ticks));
69 static void rest __P((int ticks));
70 static void playinit __P((void));
71 static void playtone __P((int pitch, int value, int sustain));
72 static int abs __P((int n));
73 static void playstring __P((char *cp, size_t slen));
74
75 /* emit tone of frequency thz for given number of ticks */
76 static void
77 tone(thz, ticks)
78         unsigned int thz, ticks;
79 {
80     unsigned int divisor;
81     int sps;
82
83     if (thz <= 0)
84         return;
85
86     divisor = timer_freq / thz;
87
88 #ifdef DEBUG
89     (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
90 #endif /* DEBUG */
91
92     /* set timer to generate clicks at given frequency in Hertz */
93     sps = splclock();
94
95     if (acquire_timer2(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT)) {
96         /* enter list of waiting procs ??? */
97         splx(sps);
98         return;
99     }
100     splx(sps);
101     disable_intr();
102     outb(TIMER_CNTR2, (divisor & 0xff));        /* send lo byte */
103     outb(TIMER_CNTR2, (divisor >> 8));  /* send hi byte */
104     enable_intr();
105
106     /* turn the speaker on */
107     outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
108
109     /*
110      * Set timeout to endtone function, then give up the timeslice.
111      * This is so other processes can execute while the tone is being
112      * emitted.
113      */
114     if (ticks > 0)
115         tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
116     outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
117     sps = splclock();
118     release_timer2();
119     splx(sps);
120 }
121
122 /* rest for given number of ticks */
123 static void
124 rest(ticks)
125         int     ticks;
126 {
127     /*
128      * Set timeout to endrest function, then give up the timeslice.
129      * This is so other processes can execute while the rest is being
130      * waited out.
131      */
132 #ifdef DEBUG
133     (void) printf("rest: %d\n", ticks);
134 #endif /* DEBUG */
135     if (ticks > 0)
136         tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
137 }
138
139 /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
140  *
141  * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
142  * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
143  * tracking facility are added.
144  * Requires tone(), rest(), and endtone(). String play is not interruptible
145  * except possibly at physical block boundaries.
146  */
147
148 typedef int     bool;
149 #define TRUE    1
150 #define FALSE   0
151
152 #define dtoi(c)         ((c) - '0')
153
154 static int octave;      /* currently selected octave */
155 static int whole;       /* whole-note time at current tempo, in ticks */
156 static int value;       /* whole divisor for note time, quarter note = 1 */
157 static int fill;        /* controls spacing of notes */
158 static bool octtrack;   /* octave-tracking on? */
159 static bool octprefix;  /* override current octave-tracking state? */
160
161 /*
162  * Magic number avoidance...
163  */
164 #define SECS_PER_MIN    60      /* seconds per minute */
165 #define WHOLE_NOTE      4       /* quarter notes per whole note */
166 #define MIN_VALUE       64      /* the most we can divide a note by */
167 #define DFLT_VALUE      4       /* default value (quarter-note) */
168 #define FILLTIME        8       /* for articulation, break note in parts */
169 #define STACCATO        6       /* 6/8 = 3/4 of note is filled */
170 #define NORMAL          7       /* 7/8ths of note interval is filled */
171 #define LEGATO          8       /* all of note interval is filled */
172 #define DFLT_OCTAVE     4       /* default octave */
173 #define MIN_TEMPO       32      /* minimum tempo */
174 #define DFLT_TEMPO      120     /* default tempo */
175 #define MAX_TEMPO       255     /* max tempo */
176 #define NUM_MULT        3       /* numerator of dot multiplier */
177 #define DENOM_MULT      2       /* denominator of dot multiplier */
178
179 /* letter to half-tone:  A   B  C  D  E  F  G */
180 static int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
181
182 /*
183  * This is the American Standard A440 Equal-Tempered scale with frequencies
184  * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
185  * our octave 0 is standard octave 2.
186  */
187 #define OCTAVE_NOTES    12      /* semitones per octave */
188 static int pitchtab[] =
189 {
190 /*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
191 /* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
192 /* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
193 /* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
194 /* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
195 /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
196 /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
197 /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
198 };
199
200 static void
201 playinit()
202 {
203     octave = DFLT_OCTAVE;
204     whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
205     fill = NORMAL;
206     value = DFLT_VALUE;
207     octtrack = FALSE;
208     octprefix = TRUE;   /* act as though there was an initial O(n) */
209 }
210
211 /* play tone of proper duration for current rhythm signature */
212 static void
213 playtone(pitch, value, sustain)
214         int     pitch, value, sustain;
215 {
216     register int        sound, silence, snum = 1, sdenom = 1;
217
218     /* this weirdness avoids floating-point arithmetic */
219     for (; sustain; sustain--)
220     {
221         /* See the BUGS section in the man page for discussion */
222         snum *= NUM_MULT;
223         sdenom *= DENOM_MULT;
224     }
225
226     if (value == 0 || sdenom == 0)
227         return;
228
229     if (pitch == -1)
230         rest(whole * snum / (value * sdenom));
231     else
232     {
233         sound = (whole * snum) / (value * sdenom)
234                 - (whole * (FILLTIME - fill)) / (value * FILLTIME);
235         silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
236
237 #ifdef DEBUG
238         (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
239                         pitch, sound, silence);
240 #endif /* DEBUG */
241
242         tone(pitchtab[pitch], sound);
243         if (fill != LEGATO)
244             rest(silence);
245     }
246 }
247
248 static int
249 abs(n)
250         int n;
251 {
252     if (n < 0)
253         return(-n);
254     else
255         return(n);
256 }
257
258 /* interpret and play an item from a notation string */
259 static void
260 playstring(cp, slen)
261         char    *cp;
262         size_t  slen;
263 {
264     int         pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
265
266 #define GETNUM(cp, v)   for(v=0; isdigit(cp[1]) && slen > 0; ) \
267                                 {v = v * 10 + (*++cp - '0'); slen--;}
268     for (; slen--; cp++)
269     {
270         int             sustain, timeval, tempo;
271         register char   c = toupper(*cp);
272
273 #ifdef DEBUG
274         (void) printf("playstring: %c (%x)\n", c, c);
275 #endif /* DEBUG */
276
277         switch (c)
278         {
279         case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
280
281             /* compute pitch */
282             pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
283
284             /* this may be followed by an accidental sign */
285             if (cp[1] == '#' || cp[1] == '+')
286             {
287                 ++pitch;
288                 ++cp;
289                 slen--;
290             }
291             else if (cp[1] == '-')
292             {
293                 --pitch;
294                 ++cp;
295                 slen--;
296             }
297
298             /*
299              * If octave-tracking mode is on, and there has been no octave-
300              * setting prefix, find the version of the current letter note
301              * closest to the last regardless of octave.
302              */
303             if (octtrack && !octprefix)
304             {
305                 if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
306                 {
307                     ++octave;
308                     pitch += OCTAVE_NOTES;
309                 }
310
311                 if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
312                 {
313                     --octave;
314                     pitch -= OCTAVE_NOTES;
315                 }
316             }
317             octprefix = FALSE;
318             lastpitch = pitch;
319
320             /* ...which may in turn be followed by an override time value */
321             GETNUM(cp, timeval);
322             if (timeval <= 0 || timeval > MIN_VALUE)
323                 timeval = value;
324
325             /* ...and/or sustain dots */
326             for (sustain = 0; cp[1] == '.'; cp++)
327             {
328                 slen--;
329                 sustain++;
330             }
331
332             /* ...and/or a slur mark */
333             oldfill = fill;
334             if (cp[1] == '_')
335             {
336                 fill = LEGATO;
337                 ++cp;
338                 slen--;
339             }
340
341             /* time to emit the actual tone */
342             playtone(pitch, timeval, sustain);
343
344             fill = oldfill;
345             break;
346
347         case 'O':
348             if (cp[1] == 'N' || cp[1] == 'n')
349             {
350                 octprefix = octtrack = FALSE;
351                 ++cp;
352                 slen--;
353             }
354             else if (cp[1] == 'L' || cp[1] == 'l')
355             {
356                 octtrack = TRUE;
357                 ++cp;
358                 slen--;
359             }
360             else
361             {
362                 GETNUM(cp, octave);
363                 if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
364                     octave = DFLT_OCTAVE;
365                 octprefix = TRUE;
366             }
367             break;
368
369         case '>':
370             if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
371                 octave++;
372             octprefix = TRUE;
373             break;
374
375         case '<':
376             if (octave > 0)
377                 octave--;
378             octprefix = TRUE;
379             break;
380
381         case 'N':
382             GETNUM(cp, pitch);
383             for (sustain = 0; cp[1] == '.'; cp++)
384             {
385                 slen--;
386                 sustain++;
387             }
388             oldfill = fill;
389             if (cp[1] == '_')
390             {
391                 fill = LEGATO;
392                 ++cp;
393                 slen--;
394             }
395             playtone(pitch - 1, value, sustain);
396             fill = oldfill;
397             break;
398
399         case 'L':
400             GETNUM(cp, value);
401             if (value <= 0 || value > MIN_VALUE)
402                 value = DFLT_VALUE;
403             break;
404
405         case 'P':
406         case '~':
407             /* this may be followed by an override time value */
408             GETNUM(cp, timeval);
409             if (timeval <= 0 || timeval > MIN_VALUE)
410                 timeval = value;
411             for (sustain = 0; cp[1] == '.'; cp++)
412             {
413                 slen--;
414                 sustain++;
415             }
416             playtone(-1, timeval, sustain);
417             break;
418
419         case 'T':
420             GETNUM(cp, tempo);
421             if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
422                 tempo = DFLT_TEMPO;
423             whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
424             break;
425
426         case 'M':
427             if (cp[1] == 'N' || cp[1] == 'n')
428             {
429                 fill = NORMAL;
430                 ++cp;
431                 slen--;
432             }
433             else if (cp[1] == 'L' || cp[1] == 'l')
434             {
435                 fill = LEGATO;
436                 ++cp;
437                 slen--;
438             }
439             else if (cp[1] == 'S' || cp[1] == 's')
440             {
441                 fill = STACCATO;
442                 ++cp;
443                 slen--;
444             }
445             break;
446         }
447     }
448 }
449
450 /******************* UNIX DRIVER HOOKS BEGIN HERE **************************
451  *
452  * This section implements driver hooks to run playstring() and the tone(),
453  * endtone(), and rest() functions defined above.
454  */
455
456 static int spkr_active = FALSE; /* exclusion flag */
457 static struct buf *spkr_inbuf;  /* incoming buf */
458
459 int
460 spkropen(dev, flags, fmt, p)
461         dev_t           dev;
462         int             flags;
463         int             fmt;
464         struct proc     *p;
465 {
466 #ifdef DEBUG
467     (void) printf("spkropen: entering with dev = %s\n", devtoname(dev));
468 #endif /* DEBUG */
469
470     if (minor(dev) != 0)
471         return(ENXIO);
472     else if (spkr_active)
473         return(EBUSY);
474     else
475     {
476 #ifdef DEBUG
477         (void) printf("spkropen: about to perform play initialization\n");
478 #endif /* DEBUG */
479         playinit();
480         spkr_inbuf = geteblk(DEV_BSIZE);
481         spkr_active = TRUE;
482         return(0);
483     }
484 }
485
486 int
487 spkrwrite(dev, uio, ioflag)
488         dev_t           dev;
489         struct uio      *uio;
490         int             ioflag;
491 {
492 #ifdef DEBUG
493     printf("spkrwrite: entering with dev = %s, count = %d\n",
494                 devtoname(dev), uio->uio_resid);
495 #endif /* DEBUG */
496
497     if (minor(dev) != 0)
498         return(ENXIO);
499     else if (uio->uio_resid > (DEV_BSIZE - 1))     /* prevent system crashes */
500         return(E2BIG);
501     else
502     {
503         unsigned n;
504         char *cp;
505         int error;
506
507         n = uio->uio_resid;
508         cp = spkr_inbuf->b_data;
509         error = uiomove(cp, n, uio);
510         if (!error) {
511                 cp[n] = '\0';
512                 playstring(cp, n);
513         }
514         return(error);
515     }
516 }
517
518 int
519 spkrclose(dev, flags, fmt, p)
520         dev_t           dev;
521         int             flags;
522         int             fmt;
523         struct proc     *p;
524 {
525 #ifdef DEBUG
526     (void) printf("spkrclose: entering with dev = %s\n", devtoname(dev));
527 #endif /* DEBUG */
528
529     if (minor(dev) != 0)
530         return(ENXIO);
531     else
532     {
533         wakeup((caddr_t)&endtone);
534         wakeup((caddr_t)&endrest);
535         brelse(spkr_inbuf);
536         spkr_active = FALSE;
537         return(0);
538     }
539 }
540
541 int
542 spkrioctl(dev, cmd, cmdarg, flags, p)
543         dev_t           dev;
544         unsigned long   cmd;
545         caddr_t         cmdarg;
546         int             flags;
547         struct proc     *p;
548 {
549 #ifdef DEBUG
550     (void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n",
551         devtoname(dev), cmd);
552 #endif /* DEBUG */
553
554     if (minor(dev) != 0)
555         return(ENXIO);
556     else if (cmd == SPKRTONE)
557     {
558         tone_t  *tp = (tone_t *)cmdarg;
559
560         if (tp->frequency == 0)
561             rest(tp->duration);
562         else
563             tone(tp->frequency, tp->duration);
564         return 0;
565     }
566     else if (cmd == SPKRTUNE)
567     {
568         tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
569         tone_t ttp;
570         int error;
571
572         for (; ; tp++) {
573             error = copyin(tp, &ttp, sizeof(tone_t));
574             if (error)
575                     return(error);
576             if (ttp.duration == 0)
577                     break;
578             if (ttp.frequency == 0)
579                  rest(ttp.duration);
580             else
581                  tone(ttp.frequency, ttp.duration);
582         }
583         return(0);
584     }
585     return(EINVAL);
586 }
587
588 static void
589 spkr_drvinit(void *unused)
590 {
591         make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "speaker");
592 }
593
594 SYSINIT(spkrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,spkr_drvinit,NULL)
595
596
597 /* spkr.c ends here */