kernel tree reorganization stage 1: Major cvs repository work (not logged as
[dragonfly.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  * $DragonFly: src/sys/dev/sound/isa/i386/Attic/sound_timer.c,v 1.2 2003/08/07 21:17:12 dillon Exp $
27  */
28
29 #define SEQUENCER_C
30 #include "sound_config.h"
31
32 #if NSND > 0
33
34 #if defined(CONFIG_SEQUENCER)
35
36 static volatile int initialized = 0, opened = 0, tmr_running = 0;
37 static volatile time_t tmr_offs, tmr_ctr;
38 static volatile u_long ticks_offs;
39 static volatile int curr_tempo, curr_timebase;
40 static volatile u_long curr_ticks;
41 static volatile u_long next_event_time;
42 static u_long prev_event_time;
43 static volatile u_long usecs_per_tmr;   /* Length of the current interval */
44
45 static struct sound_lowlev_timer *tmr = NULL;
46
47 static u_long
48 tmr2ticks(int tmr_value)
49 {
50     /*
51      * Convert timer ticks to MIDI ticks
52      */
53
54     u_long   tmp;
55     u_long   scale;
56
57     tmp = tmr_value * usecs_per_tmr;    /* Convert to usecs */
58
59     scale = (60 * 1000000) / (curr_tempo * curr_timebase);      /* usecs per MIDI tick */
60
61     return (tmp + (scale / 2)) / scale;
62 }
63
64 static void
65 reprogram_timer(void)
66 {
67     u_long   usecs_per_tick;
68
69     usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
70
71     /*
72      * Don't kill the system by setting too high timer rate
73      */
74     if (usecs_per_tick < 2000)
75         usecs_per_tick = 2000;
76
77     usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
78 }
79
80 void
81 sound_timer_syncinterval(u_int new_usecs)
82 {
83     /*
84      * This routine is called by the hardware level if the clock
85      * frequency has changed for some reason.
86      */
87     tmr_offs = tmr_ctr;
88     ticks_offs += tmr2ticks(tmr_ctr);
89     tmr_ctr = 0;
90
91     usecs_per_tmr = new_usecs;
92 }
93
94 static void
95 tmr_reset(void)
96 {
97     u_long   flags;
98
99     flags = splhigh();
100     tmr_offs = 0;
101     ticks_offs = 0;
102     tmr_ctr = 0;
103     next_event_time = 0xffffffff;
104     prev_event_time = 0;
105     curr_ticks = 0;
106     splx(flags);
107 }
108
109 static int
110 timer_open(int dev, int mode)
111 {
112     if (opened)
113         return -(EBUSY);
114
115     tmr_reset();
116     curr_tempo = 60;
117     curr_timebase = hz;
118     opened = 1;
119     reprogram_timer();
120
121     return 0;
122 }
123
124 static void
125 timer_close(int dev)
126 {
127     opened = tmr_running = 0;
128     tmr->tmr_disable(tmr->dev);
129 }
130
131 static int
132 timer_event(int dev, u_char *event)
133 {
134     u_char   cmd = event[1];
135     u_long   parm = *(int *) &event[4];
136
137     switch (cmd) {
138     case TMR_WAIT_REL:
139         parm += prev_event_time;
140     case TMR_WAIT_ABS:
141         if (parm > 0) {
142             long            time;
143
144             if (parm <= curr_ticks)     /* It's the time */
145                 return TIMER_NOT_ARMED;
146
147             time = parm;
148             next_event_time = prev_event_time = time;
149
150             return TIMER_ARMED;
151         }
152         break;
153
154     case TMR_START:
155         tmr_reset();
156         tmr_running = 1;
157         reprogram_timer();
158         break;
159
160     case TMR_STOP:
161         tmr_running = 0;
162         break;
163
164     case TMR_CONTINUE:
165         tmr_running = 1;
166         reprogram_timer();
167         break;
168
169     case TMR_TEMPO:
170         if (parm) {
171             if (parm < 8)
172                 parm = 8;
173             if (parm > 250)
174                 parm = 250;
175             tmr_offs = tmr_ctr;
176             ticks_offs += tmr2ticks(tmr_ctr);
177             tmr_ctr = 0;
178             curr_tempo = parm;
179             reprogram_timer();
180         }
181         break;
182
183     case TMR_ECHO:
184         seq_copy_to_input(event, 8);
185         break;
186
187     default:;
188     }
189
190     return TIMER_NOT_ARMED;
191 }
192
193 static u_long
194 timer_get_time(int dev)
195 {
196     if (!opened)
197         return 0;
198
199     return curr_ticks;
200 }
201
202 static int
203 timer_ioctl(int dev, u_int cmd, ioctl_arg arg)
204 {
205     switch (cmd) {
206     case SNDCTL_TMR_SOURCE:
207         return *(int *) arg = TMR_INTERNAL;
208         break;
209
210     case SNDCTL_TMR_START:
211         tmr_reset();
212         tmr_running = 1;
213         return 0;
214         break;
215
216     case SNDCTL_TMR_STOP:
217         tmr_running = 0;
218         return 0;
219         break;
220
221     case SNDCTL_TMR_CONTINUE:
222         tmr_running = 1;
223         return 0;
224         break;
225
226     case SNDCTL_TMR_TIMEBASE:
227         {
228             int             val = (*(int *) arg);
229
230             if (val) {
231                 if (val < 1)
232                     val = 1;
233                 if (val > 1000)
234                     val = 1000;
235                 curr_timebase = val;
236             }
237             return *(int *) arg = curr_timebase;
238         }
239         break;
240
241     case SNDCTL_TMR_TEMPO:
242         {
243             int             val = (*(int *) arg);
244
245             if (val) {
246                 if (val < 8)
247                     val = 8;
248                 if (val > 250)
249                     val = 250;
250                 tmr_offs = tmr_ctr;
251                 ticks_offs += tmr2ticks(tmr_ctr);
252                 tmr_ctr = 0;
253                 curr_tempo = val;
254                 reprogram_timer();
255             }
256             return *(int *) arg = curr_tempo;
257         }
258         break;
259
260     case SNDCTL_SEQ_CTRLRATE:
261         if ((*(int *) arg) != 0)        /* Can't change */
262             return -(EINVAL);
263
264         return *(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60;
265         break;
266
267     case SNDCTL_TMR_METRONOME:
268         /* NOP */
269         break;
270
271     default:;
272     }
273
274     return -(EINVAL);
275 }
276
277 static void
278 timer_arm(int dev, long time)
279 {
280     if (time < 0)
281         time = curr_ticks + 1;
282     else if (time <= curr_ticks)        /* It's the time */
283         return;
284
285     next_event_time = prev_event_time = time;
286
287     return;
288 }
289
290 static struct sound_timer_operations sound_timer =
291 {
292         {"GUS Timer", 0},
293         1,                      /* Priority */
294         0,                      /* Local device link */
295         timer_open,
296         timer_close,
297         timer_event,
298         timer_get_time,
299         timer_ioctl,
300         timer_arm
301 };
302
303 void
304 sound_timer_interrupt(void)
305 {
306     if (!opened)
307         return;
308
309     tmr->tmr_restart(tmr->dev);
310
311     if (!tmr_running)
312         return;
313
314     tmr_ctr++;
315     curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
316
317     if (curr_ticks >= next_event_time) {
318         next_event_time = 0xffffffff;
319         sequencer_timer(0);
320     }
321 }
322
323 void
324 sound_timer_init(struct sound_lowlev_timer * t, char *name)
325 {
326     int             n;
327
328     if (initialized || t == NULL)
329         return;         /* There is already a similar timer */
330
331     initialized = 1;
332     tmr = t;
333
334     if (num_sound_timers >= MAX_TIMER_DEV)
335         n = 0;          /* Overwrite the system timer */
336     else
337         n = num_sound_timers++;
338
339     snprintf(sound_timer.info.name, sizeof(sound_timer.info.name), "%s", name);
340
341     sound_timer_devs[n] = &sound_timer;
342 }
343
344 #endif
345 #endif