Import 2.3.18pre1
[davej-history.git] / drivers / sound / sound_timer.c
blob60f031ac885b001a015c551e528fc649a7b1ed94
1 /*
2 * sound/sound_timer.c
3 */
4 /*
5 * Copyright (C) by Hannu Savolainen 1993-1997
7 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
8 * Version 2 (June 1991). See the "COPYING" file distributed with this software
9 * for more info.
12 * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
14 #include <linux/config.h>
15 #include <linux/string.h>
18 #include "sound_config.h"
20 #if defined(CONFIG_SEQUENCER)
22 static volatile int initialized = 0, opened = 0, tmr_running = 0;
23 static volatile time_t tmr_offs, tmr_ctr;
24 static volatile unsigned long ticks_offs;
25 static volatile int curr_tempo, curr_timebase;
26 static volatile unsigned long curr_ticks;
27 static volatile unsigned long next_event_time;
28 static unsigned long prev_event_time;
29 static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
31 static struct sound_lowlev_timer *tmr = NULL;
33 static unsigned long tmr2ticks(int tmr_value)
36 * Convert timer ticks to MIDI ticks
39 unsigned long tmp;
40 unsigned long scale;
42 tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
43 scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
44 return (tmp + (scale / 2)) / scale;
47 void reprogram_timer(void)
49 unsigned long usecs_per_tick;
52 * The user is changing the timer rate before setting a timer
53 * slap, bad bad not allowed.
56 if(!tmr)
57 return;
59 usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
62 * Don't kill the system by setting too high timer rate
64 if (usecs_per_tick < 2000)
65 usecs_per_tick = 2000;
67 usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
70 void sound_timer_syncinterval(unsigned int new_usecs)
73 * This routine is called by the hardware level if
74 * the clock frequency has changed for some reason.
76 tmr_offs = tmr_ctr;
77 ticks_offs += tmr2ticks(tmr_ctr);
78 tmr_ctr = 0;
79 usecs_per_tmr = new_usecs;
82 static void tmr_reset(void)
84 unsigned long flags;
86 save_flags(flags);
87 cli();
88 tmr_offs = 0;
89 ticks_offs = 0;
90 tmr_ctr = 0;
91 next_event_time = (unsigned long) -1;
92 prev_event_time = 0;
93 curr_ticks = 0;
94 restore_flags(flags);
97 static int timer_open(int dev, int mode)
99 if (opened)
100 return -EBUSY;
101 tmr_reset();
102 curr_tempo = 60;
103 curr_timebase = 100;
104 opened = 1;
105 reprogram_timer();
106 return 0;
109 static void timer_close(int dev)
111 opened = tmr_running = 0;
112 tmr->tmr_disable(tmr->dev);
115 static int timer_event(int dev, unsigned char *event)
117 unsigned char cmd = event[1];
118 unsigned long parm = *(int *) &event[4];
120 switch (cmd)
122 case TMR_WAIT_REL:
123 parm += prev_event_time;
124 case TMR_WAIT_ABS:
125 if (parm > 0)
127 long time;
129 if (parm <= curr_ticks) /* It's the time */
130 return TIMER_NOT_ARMED;
131 time = parm;
132 next_event_time = prev_event_time = time;
133 return TIMER_ARMED;
135 break;
137 case TMR_START:
138 tmr_reset();
139 tmr_running = 1;
140 reprogram_timer();
141 break;
143 case TMR_STOP:
144 tmr_running = 0;
145 break;
147 case TMR_CONTINUE:
148 tmr_running = 1;
149 reprogram_timer();
150 break;
152 case TMR_TEMPO:
153 if (parm)
155 if (parm < 8)
156 parm = 8;
157 if (parm > 250)
158 parm = 250;
159 tmr_offs = tmr_ctr;
160 ticks_offs += tmr2ticks(tmr_ctr);
161 tmr_ctr = 0;
162 curr_tempo = parm;
163 reprogram_timer();
165 break;
167 case TMR_ECHO:
168 seq_copy_to_input(event, 8);
169 break;
171 default:
173 return TIMER_NOT_ARMED;
176 static unsigned long timer_get_time(int dev)
178 if (!opened)
179 return 0;
180 return curr_ticks;
183 static int timer_ioctl(int dev, unsigned int cmd, caddr_t arg)
185 int val;
187 switch (cmd)
189 case SNDCTL_TMR_SOURCE:
190 val = TMR_INTERNAL;
191 break;
193 case SNDCTL_TMR_START:
194 tmr_reset();
195 tmr_running = 1;
196 return 0;
198 case SNDCTL_TMR_STOP:
199 tmr_running = 0;
200 return 0;
202 case SNDCTL_TMR_CONTINUE:
203 tmr_running = 1;
204 return 0;
206 case SNDCTL_TMR_TIMEBASE:
207 if (get_user(val, (int *)arg))
208 return -EFAULT;
209 if (val)
211 if (val < 1)
212 val = 1;
213 if (val > 1000)
214 val = 1000;
215 curr_timebase = val;
217 val = curr_timebase;
218 break;
220 case SNDCTL_TMR_TEMPO:
221 if (get_user(val, (int *)arg))
222 return -EFAULT;
223 if (val)
225 if (val < 8)
226 val = 8;
227 if (val > 250)
228 val = 250;
229 tmr_offs = tmr_ctr;
230 ticks_offs += tmr2ticks(tmr_ctr);
231 tmr_ctr = 0;
232 curr_tempo = val;
233 reprogram_timer();
235 val = curr_tempo;
236 break;
238 case SNDCTL_SEQ_CTRLRATE:
239 if (get_user(val, (int *)arg))
240 return -EFAULT;
241 if (val != 0) /* Can't change */
242 return -EINVAL;
243 val = ((curr_tempo * curr_timebase) + 30) / 60;
244 break;
246 case SNDCTL_SEQ_GETTIME:
247 val = curr_ticks;
248 break;
250 case SNDCTL_TMR_METRONOME:
251 default:
252 return -EINVAL;
254 return put_user(val, (int *)arg);
257 static void timer_arm(int dev, long time)
259 if (time < 0)
260 time = curr_ticks + 1;
261 else if (time <= curr_ticks) /* It's the time */
262 return;
264 next_event_time = prev_event_time = time;
265 return;
268 static struct sound_timer_operations sound_timer =
270 {"Sound Timer", 0},
271 1, /* Priority */
272 0, /* Local device link */
273 timer_open,
274 timer_close,
275 timer_event,
276 timer_get_time,
277 timer_ioctl,
278 timer_arm
281 void sound_timer_interrupt(void)
283 if (!opened)
284 return;
286 tmr->tmr_restart(tmr->dev);
288 if (!tmr_running)
289 return;
291 tmr_ctr++;
292 curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
294 if (curr_ticks >= next_event_time)
296 next_event_time = (unsigned long) -1;
297 sequencer_timer(0);
301 void sound_timer_init(struct sound_lowlev_timer *t, char *name)
303 int n;
305 if (initialized)
307 if (t->priority <= tmr->priority)
308 return; /* There is already a similar or better timer */
309 tmr = t;
310 return;
312 initialized = 1;
313 tmr = t;
315 n = sound_alloc_timerdev();
316 if (n == -1)
317 n = 0; /* Overwrite the system timer */
318 strcpy(sound_timer.info.name, name);
319 sound_timer_devs[n] = &sound_timer;
322 #endif