aboutsummaryrefslogtreecommitdiffstats
path: root/sound/oss/sound_timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/oss/sound_timer.c')
-rw-r--r--sound/oss/sound_timer.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c
new file mode 100644
index 000000000000..bc2777dd2ef9
--- /dev/null
+++ b/sound/oss/sound_timer.c
@@ -0,0 +1,323 @@
1/*
2 * sound/sound_timer.c
3 */
4/*
5 * Copyright (C) by Hannu Savolainen 1993-1997
6 *
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.
10 */
11/*
12 * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
13 */
14#include <linux/string.h>
15#include <linux/spinlock.h>
16
17#include "sound_config.h"
18
19static volatile int initialized, opened, tmr_running;
20static volatile time_t tmr_offs, tmr_ctr;
21static volatile unsigned long ticks_offs;
22static volatile int curr_tempo, curr_timebase;
23static volatile unsigned long curr_ticks;
24static volatile unsigned long next_event_time;
25static unsigned long prev_event_time;
26static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
27
28static struct sound_lowlev_timer *tmr;
29static spinlock_t lock;
30
31static unsigned long tmr2ticks(int tmr_value)
32{
33 /*
34 * Convert timer ticks to MIDI ticks
35 */
36
37 unsigned long tmp;
38 unsigned long scale;
39
40 tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
41 scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
42 return (tmp + (scale / 2)) / scale;
43}
44
45void reprogram_timer(void)
46{
47 unsigned long usecs_per_tick;
48
49 /*
50 * The user is changing the timer rate before setting a timer
51 * slap, bad bad not allowed.
52 */
53
54 if(!tmr)
55 return;
56
57 usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
58
59 /*
60 * Don't kill the system by setting too high timer rate
61 */
62 if (usecs_per_tick < 2000)
63 usecs_per_tick = 2000;
64
65 usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
66}
67
68void sound_timer_syncinterval(unsigned int new_usecs)
69{
70 /*
71 * This routine is called by the hardware level if
72 * the clock frequency has changed for some reason.
73 */
74 tmr_offs = tmr_ctr;
75 ticks_offs += tmr2ticks(tmr_ctr);
76 tmr_ctr = 0;
77 usecs_per_tmr = new_usecs;
78}
79
80static void tmr_reset(void)
81{
82 unsigned long flags;
83
84 spin_lock_irqsave(&lock,flags);
85 tmr_offs = 0;
86 ticks_offs = 0;
87 tmr_ctr = 0;
88 next_event_time = (unsigned long) -1;
89 prev_event_time = 0;
90 curr_ticks = 0;
91 spin_unlock_irqrestore(&lock,flags);
92}
93
94static int timer_open(int dev, int mode)
95{
96 if (opened)
97 return -EBUSY;
98 tmr_reset();
99 curr_tempo = 60;
100 curr_timebase = 100;
101 opened = 1;
102 reprogram_timer();
103 return 0;
104}
105
106static void timer_close(int dev)
107{
108 opened = tmr_running = 0;
109 tmr->tmr_disable(tmr->dev);
110}
111
112static int timer_event(int dev, unsigned char *event)
113{
114 unsigned char cmd = event[1];
115 unsigned long parm = *(int *) &event[4];
116
117 switch (cmd)
118 {
119 case TMR_WAIT_REL:
120 parm += prev_event_time;
121 case TMR_WAIT_ABS:
122 if (parm > 0)
123 {
124 long time;
125
126 if (parm <= curr_ticks) /* It's the time */
127 return TIMER_NOT_ARMED;
128 time = parm;
129 next_event_time = prev_event_time = time;
130 return TIMER_ARMED;
131 }
132 break;
133
134 case TMR_START:
135 tmr_reset();
136 tmr_running = 1;
137 reprogram_timer();
138 break;
139
140 case TMR_STOP:
141 tmr_running = 0;
142 break;
143
144 case TMR_CONTINUE:
145 tmr_running = 1;
146 reprogram_timer();
147 break;
148
149 case TMR_TEMPO:
150 if (parm)
151 {
152 if (parm < 8)
153 parm = 8;
154 if (parm > 250)
155 parm = 250;
156 tmr_offs = tmr_ctr;
157 ticks_offs += tmr2ticks(tmr_ctr);
158 tmr_ctr = 0;
159 curr_tempo = parm;
160 reprogram_timer();
161 }
162 break;
163
164 case TMR_ECHO:
165 seq_copy_to_input(event, 8);
166 break;
167
168 default:;
169 }
170 return TIMER_NOT_ARMED;
171}
172
173static unsigned long timer_get_time(int dev)
174{
175 if (!opened)
176 return 0;
177 return curr_ticks;
178}
179
180static int timer_ioctl(int dev, unsigned int cmd, void __user *arg)
181{
182 int __user *p = arg;
183 int val;
184
185 switch (cmd)
186 {
187 case SNDCTL_TMR_SOURCE:
188 val = TMR_INTERNAL;
189 break;
190
191 case SNDCTL_TMR_START:
192 tmr_reset();
193 tmr_running = 1;
194 return 0;
195
196 case SNDCTL_TMR_STOP:
197 tmr_running = 0;
198 return 0;
199
200 case SNDCTL_TMR_CONTINUE:
201 tmr_running = 1;
202 return 0;
203
204 case SNDCTL_TMR_TIMEBASE:
205 if (get_user(val, p))
206 return -EFAULT;
207 if (val)
208 {
209 if (val < 1)
210 val = 1;
211 if (val > 1000)
212 val = 1000;
213 curr_timebase = val;
214 }
215 val = curr_timebase;
216 break;
217
218 case SNDCTL_TMR_TEMPO:
219 if (get_user(val, p))
220 return -EFAULT;
221 if (val)
222 {
223 if (val < 8)
224 val = 8;
225 if (val > 250)
226 val = 250;
227 tmr_offs = tmr_ctr;
228 ticks_offs += tmr2ticks(tmr_ctr);
229 tmr_ctr = 0;
230 curr_tempo = val;
231 reprogram_timer();
232 }
233 val = curr_tempo;
234 break;
235
236 case SNDCTL_SEQ_CTRLRATE:
237 if (get_user(val, p))
238 return -EFAULT;
239 if (val != 0) /* Can't change */
240 return -EINVAL;
241 val = ((curr_tempo * curr_timebase) + 30) / 60;
242 break;
243
244 case SNDCTL_SEQ_GETTIME:
245 val = curr_ticks;
246 break;
247
248 case SNDCTL_TMR_METRONOME:
249 default:
250 return -EINVAL;
251 }
252 return put_user(val, p);
253}
254
255static void timer_arm(int dev, long time)
256{
257 if (time < 0)
258 time = curr_ticks + 1;
259 else if (time <= curr_ticks) /* It's the time */
260 return;
261
262 next_event_time = prev_event_time = time;
263 return;
264}
265
266static struct sound_timer_operations sound_timer =
267{
268 .owner = THIS_MODULE,
269 .info = {"Sound Timer", 0},
270 .priority = 1, /* Priority */
271 .devlink = 0, /* Local device link */
272 .open = timer_open,
273 .close = timer_close,
274 .event = timer_event,
275 .get_time = timer_get_time,
276 .ioctl = timer_ioctl,
277 .arm_timer = timer_arm
278};
279
280void sound_timer_interrupt(void)
281{
282 unsigned long flags;
283
284 if (!opened)
285 return;
286
287 tmr->tmr_restart(tmr->dev);
288
289 if (!tmr_running)
290 return;
291
292 spin_lock_irqsave(&lock,flags);
293 tmr_ctr++;
294 curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
295
296 if (curr_ticks >= next_event_time)
297 {
298 next_event_time = (unsigned long) -1;
299 sequencer_timer(0);
300 }
301 spin_unlock_irqrestore(&lock,flags);
302}
303
304void sound_timer_init(struct sound_lowlev_timer *t, char *name)
305{
306 int n;
307
308 if (initialized)
309 {
310 if (t->priority <= tmr->priority)
311 return; /* There is already a similar or better timer */
312 tmr = t;
313 return;
314 }
315 initialized = 1;
316 tmr = t;
317
318 n = sound_alloc_timerdev();
319 if (n == -1)
320 n = 0; /* Overwrite the system timer */
321 strcpy(sound_timer.info.name, name);
322 sound_timer_devs[n] = &sound_timer;
323}