Initial import from FreeBSD RELENG_4:
[games.git] / sys / dev / sound / isa / i386 / sound_timer.c
1 /*
2  * sound/sound_timer.c
3  * 
4  * Copyright by Hannu Savolainen 1993
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met: 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer. 2.
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  * 
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  * 
26  */
27
28 #define SEQUENCER_C
29 #include <i386/isa/sound/sound_config.h>
30
31 #if NSND > 0
32
33 #if defined(CONFIG_SEQUENCER)
34
35 static volatile int initialized = 0, opened = 0, tmr_running = 0;
36 static volatile time_t tmr_offs, tmr_ctr;
37 static volatile u_long ticks_offs;
38 static volatile int curr_tempo, curr_timebase;
39 static volatile u_long curr_ticks;
40 static volatile u_long next_event_time;
41 static u_long prev_event_time;
42 static volatile u_long usecs_per_tmr;   /* Length of the current interval */
43
44 static struct sound_lowlev_timer *tmr = NULL;
45
46 static u_long
47 tmr2ticks(int tmr_value)
48 {
49     /*
50      * Convert timer ticks to MIDI ticks
51      */
52
53     u_long   tmp;
54     u_long   scale;
55
56     tmp = tmr_value * usecs_per_tmr;    /* Convert to usecs */
57
58     scale = (60 * 1000000) / (curr_tempo * curr_timebase);      /* usecs per MIDI tick */
59
60     return (tmp + (scale / 2)) / scale;
61 }
62
63 static void
64 reprogram_timer(void)
65 {
66     u_long   usecs_per_tick;
67
68     usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
69
70     /*
71      * Don't kill the system by setting too high timer rate
72      */
73     if (usecs_per_tick < 2000)
74         usecs_per_tick = 2000;
75
76     usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
77 }
78
79 void
80 sound_timer_syncinterval(u_int new_usecs)
81 {
82     /*
83      * This routine is called by the hardware level if the clock
84      * frequency has changed for some reason.
85      */
86     tmr_offs = tmr_ctr;
87     ticks_offs += tmr2ticks(tmr_ctr);
88     tmr_ctr = 0;
89
90     usecs_per_tmr = new_usecs;
91 }
92
93 static void
94 tmr_reset(void)
95 {
96     u_long   flags;
97
98     flags = splhigh();
99     tmr_offs = 0;
100     ticks_offs = 0;
101     tmr_ctr = 0;
102     next_event_time = 0xffffffff;
103     prev_event_time = 0;
104     curr_ticks = 0;
105     splx(flags);
106 }
107
108 static int
109 timer_open(int dev, int mode)
110 {
111     if (opened)
112         return -(EBUSY);
113
114     tmr_reset();
115     curr_tempo = 60;
116     curr_timebase = hz;
117     opened = 1;
118     reprogram_timer();
119
120     return 0;
121 }
122
123 static void
124 timer_close(int dev)
125 {
126     opened = tmr_running = 0;
127     tmr->tmr_disable(tmr->dev);
128 }
129
130 static int
131 timer_event(int dev, u_char *event)
132 {
133     u_char   cmd = event[1];
134     u_long   parm = *(int *) &event[4];
135
136     switch (cmd) {
137     case TMR_WAIT_REL:
138         parm += prev_event_time;
139     case TMR_WAIT_ABS:
140         if (parm > 0) {
141             long            time;
142
143             if (parm <= curr_ticks)     /* It's the time */
144                 return TIMER_NOT_ARMED;
145
146             time = parm;
147             next_event_time = prev_event_time = time;
148
149             return TIMER_ARMED;
150         }
151         break;
152
153     case TMR_START:
154         tmr_reset();
155         tmr_running = 1;
156         reprogram_timer();
157         break;
158
159     case TMR_STOP:
160         tmr_running = 0;
161         break;
162
163     case TMR_CONTINUE:
164         tmr_running = 1;
165         reprogram_timer();
166         break;
167
168     case TMR_TEMPO:
169         if (parm) {
170             if (parm < 8)
171                 parm = 8;
172             if (parm > 250)
173                 parm = 250;
174             tmr_offs = tmr_ctr;
175             ticks_offs += tmr2ticks(tmr_ctr);
176             tmr_ctr = 0;
177             curr_tempo = parm;
178             reprogram_timer();
179         }
180         break;
181
182     case TMR_ECHO:
183         seq_copy_to_input(event, 8);
184         break;
185
186     default:;
187     }
188
189     return TIMER_NOT_ARMED;
190 }
191
192 static u_long
193 timer_get_time(int dev)
194 {
195     if (!opened)
196         return 0;
197
198     return curr_ticks;
199 }
200
201 static int
202 timer_ioctl(int dev, u_int cmd, ioctl_arg arg)
203 {
204     switch (cmd) {
205     case SNDCTL_TMR_SOURCE:
206         return *(int *) arg = TMR_INTERNAL;
207         break;
208
209     case SNDCTL_TMR_START:
210         tmr_reset();
211         tmr_running = 1;
212         return 0;
213         break;
214
215     case SNDCTL_TMR_STOP:
216         tmr_running = 0;
217         return 0;
218         break;
219
220     case SNDCTL_TMR_CONTINUE:
221         tmr_running = 1;
222         return 0;
223         break;
224
225     case SNDCTL_TMR_TIMEBASE:
226         {
227             int             val = (*(int *) arg);
228
229             if (val) {
230                 if (val < 1)
231                     val = 1;
232                 if (val > 1000)
233                     val = 1000;
234                 curr_timebase = val;
235             }
236             return *(int *) arg = curr_timebase;
237         }
238         break;
239
240     case SNDCTL_TMR_TEMPO:
241         {
242             int             val = (*(int *) arg);
243
244             if (val) {
245                 if (val < 8)
246                     val = 8;
247                 if (val > 250)
248                     val = 250;
249                 tmr_offs = tmr_ctr;
250                 ticks_offs += tmr2ticks(tmr_ctr);
251                 tmr_ctr = 0;
252                 curr_tempo = val;
253                 reprogram_timer();
254             }
255             return *(int *) arg = curr_tempo;
256         }
257         break;
258
259     case SNDCTL_SEQ_CTRLRATE:
260         if ((*(int *) arg) != 0)        /* Can't change */
261             return -(EINVAL);
262
263         return *(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60;
264         break;
265
266     case SNDCTL_TMR_METRONOME:
267         /* NOP */
268         break;
269
270     default:;
271     }
272
273     return -(EINVAL);
274 }
275
276 static void
277 timer_arm(int dev, long time)
278 {
279     if (time < 0)
280         time = curr_ticks + 1;
281     else if (time <= curr_ticks)        /* It's the time */
282         return;
283
284     next_event_time = prev_event_time = time;
285
286     return;
287 }
288
289 static struct sound_timer_operations sound_timer =
290 {
291         {"GUS Timer", 0},
292         1,                      /* Priority */
293         0,                      /* Local device link */
294         timer_open,
295         timer_close,
296         timer_event,
297         timer_get_time,
298         timer_ioctl,
299         timer_arm
300 };
301
302 void
303 sound_timer_interrupt(void)
304 {
305     if (!opened)
306         return;
307
308     tmr->tmr_restart(tmr->dev);
309
310     if (!tmr_running)
311         return;
312
313     tmr_ctr++;
314     curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
315
316     if (curr_ticks >= next_event_time) {
317         next_event_time = 0xffffffff;
318         sequencer_timer(0);
319     }
320 }
321
322 void
323 sound_timer_init(struct sound_lowlev_timer * t, char *name)
324 {
325     int             n;
326
327     if (initialized || t == NULL)
328         return;         /* There is already a similar timer */
329
330     initialized = 1;
331     tmr = t;
332
333     if (num_sound_timers >= MAX_TIMER_DEV)
334         n = 0;          /* Overwrite the system timer */
335     else
336         n = num_sound_timers++;
337
338     snprintf(sound_timer.info.name, sizeof(sound_timer.info.name), "%s", name);
339
340     sound_timer_devs[n] = &sound_timer;
341 }
342
343 #endif
344 #endif