2 * sound/oss/sound_timer.c
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
12 * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
14 #include <linux/string.h>
15 #include <linux/spinlock.h>
17 #include "sound_config.h"
19 static volatile int initialized, opened, tmr_running;
20 static volatile time_t tmr_offs, tmr_ctr;
21 static volatile unsigned long ticks_offs;
22 static volatile int curr_tempo, curr_timebase;
23 static volatile unsigned long curr_ticks;
24 static volatile unsigned long next_event_time;
25 static unsigned long prev_event_time;
26 static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
28 static struct sound_lowlev_timer *tmr;
29 static spinlock_t lock;
31 static unsigned long tmr2ticks(int tmr_value)
34 * Convert timer ticks to MIDI ticks
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;
45 void reprogram_timer(void)
47 unsigned long usecs_per_tick;
50 * The user is changing the timer rate before setting a timer
51 * slap, bad bad not allowed.
57 usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
60 * Don't kill the system by setting too high timer rate
62 if (usecs_per_tick < 2000)
63 usecs_per_tick = 2000;
65 usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
68 void sound_timer_syncinterval(unsigned int new_usecs)
71 * This routine is called by the hardware level if
72 * the clock frequency has changed for some reason.
75 ticks_offs += tmr2ticks(tmr_ctr);
77 usecs_per_tmr = new_usecs;
79 EXPORT_SYMBOL(sound_timer_syncinterval);
81 static void tmr_reset(void)
85 spin_lock_irqsave(&lock,flags);
89 next_event_time = (unsigned long) -1;
92 spin_unlock_irqrestore(&lock,flags);
95 static int timer_open(int dev, int mode)
107 static void timer_close(int dev)
109 opened = tmr_running = 0;
110 tmr->tmr_disable(tmr->dev);
113 static int timer_event(int dev, unsigned char *event)
115 unsigned char cmd = event[1];
116 unsigned long parm = *(int *) &event[4];
121 parm += prev_event_time;
127 if (parm <= curr_ticks) /* It's the time */
128 return TIMER_NOT_ARMED;
130 next_event_time = prev_event_time = time;
158 ticks_offs += tmr2ticks(tmr_ctr);
166 seq_copy_to_input(event, 8);
171 return TIMER_NOT_ARMED;
174 static unsigned long timer_get_time(int dev)
181 static int timer_ioctl(int dev, unsigned int cmd, void __user *arg)
188 case SNDCTL_TMR_SOURCE:
192 case SNDCTL_TMR_START:
197 case SNDCTL_TMR_STOP:
201 case SNDCTL_TMR_CONTINUE:
205 case SNDCTL_TMR_TIMEBASE:
206 if (get_user(val, p))
219 case SNDCTL_TMR_TEMPO:
220 if (get_user(val, p))
229 ticks_offs += tmr2ticks(tmr_ctr);
237 case SNDCTL_SEQ_CTRLRATE:
238 if (get_user(val, p))
240 if (val != 0) /* Can't change */
242 val = ((curr_tempo * curr_timebase) + 30) / 60;
245 case SNDCTL_SEQ_GETTIME:
249 case SNDCTL_TMR_METRONOME:
253 return put_user(val, p);
256 static void timer_arm(int dev, long time)
259 time = curr_ticks + 1;
260 else if (time <= curr_ticks) /* It's the time */
263 next_event_time = prev_event_time = time;
267 static struct sound_timer_operations sound_timer =
269 .owner = THIS_MODULE,
270 .info = {"Sound Timer", 0},
271 .priority = 1, /* Priority */
272 .devlink = 0, /* Local device link */
274 .close = timer_close,
275 .event = timer_event,
276 .get_time = timer_get_time,
277 .ioctl = timer_ioctl,
278 .arm_timer = timer_arm
281 void sound_timer_interrupt(void)
288 tmr->tmr_restart(tmr->dev);
293 spin_lock_irqsave(&lock,flags);
295 curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
297 if (curr_ticks >= next_event_time)
299 next_event_time = (unsigned long) -1;
302 spin_unlock_irqrestore(&lock,flags);
304 EXPORT_SYMBOL(sound_timer_interrupt);
306 void sound_timer_init(struct sound_lowlev_timer *t, char *name)
312 if (t->priority <= tmr->priority)
313 return; /* There is already a similar or better timer */
320 n = sound_alloc_timerdev();
322 n = 0; /* Overwrite the system timer */
323 strcpy(sound_timer.info.name, name);
324 sound_timer_devs[n] = &sound_timer;
326 EXPORT_SYMBOL(sound_timer_init);