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
);