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