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
;
80 static void tmr_reset(void)
84 spin_lock_irqsave(&lock
,flags
);
88 next_event_time
= (unsigned long) -1;
91 spin_unlock_irqrestore(&lock
,flags
);
94 static int timer_open(int dev
, int mode
)
106 static void timer_close(int dev
)
108 opened
= tmr_running
= 0;
109 tmr
->tmr_disable(tmr
->dev
);
112 static int timer_event(int dev
, unsigned char *event
)
114 unsigned char cmd
= event
[1];
115 unsigned long parm
= *(int *) &event
[4];
120 parm
+= prev_event_time
;
126 if (parm
<= curr_ticks
) /* It's the time */
127 return TIMER_NOT_ARMED
;
129 next_event_time
= prev_event_time
= time
;
157 ticks_offs
+= tmr2ticks(tmr_ctr
);
165 seq_copy_to_input(event
, 8);
170 return TIMER_NOT_ARMED
;
173 static unsigned long timer_get_time(int dev
)
180 static int timer_ioctl(int dev
, unsigned int cmd
, void __user
*arg
)
187 case SNDCTL_TMR_SOURCE
:
191 case SNDCTL_TMR_START
:
196 case SNDCTL_TMR_STOP
:
200 case SNDCTL_TMR_CONTINUE
:
204 case SNDCTL_TMR_TIMEBASE
:
205 if (get_user(val
, p
))
218 case SNDCTL_TMR_TEMPO
:
219 if (get_user(val
, p
))
228 ticks_offs
+= tmr2ticks(tmr_ctr
);
236 case SNDCTL_SEQ_CTRLRATE
:
237 if (get_user(val
, p
))
239 if (val
!= 0) /* Can't change */
241 val
= ((curr_tempo
* curr_timebase
) + 30) / 60;
244 case SNDCTL_SEQ_GETTIME
:
248 case SNDCTL_TMR_METRONOME
:
252 return put_user(val
, p
);
255 static void timer_arm(int dev
, long time
)
258 time
= curr_ticks
+ 1;
259 else if (time
<= curr_ticks
) /* It's the time */
262 next_event_time
= prev_event_time
= time
;
266 static struct sound_timer_operations sound_timer
=
268 .owner
= THIS_MODULE
,
269 .info
= {"Sound Timer", 0},
270 .priority
= 1, /* Priority */
271 .devlink
= 0, /* Local device link */
273 .close
= timer_close
,
274 .event
= timer_event
,
275 .get_time
= timer_get_time
,
276 .ioctl
= timer_ioctl
,
277 .arm_timer
= timer_arm
280 void sound_timer_interrupt(void)
287 tmr
->tmr_restart(tmr
->dev
);
292 spin_lock_irqsave(&lock
,flags
);
294 curr_ticks
= ticks_offs
+ tmr2ticks(tmr_ctr
);
296 if (curr_ticks
>= next_event_time
)
298 next_event_time
= (unsigned long) -1;
301 spin_unlock_irqrestore(&lock
,flags
);
304 void sound_timer_init(struct sound_lowlev_timer
*t
, char *name
)
310 if (t
->priority
<= tmr
->priority
)
311 return; /* There is already a similar or better timer */
318 n
= sound_alloc_timerdev();
320 n
= 0; /* Overwrite the system timer */
321 strcpy(sound_timer
.info
.name
, name
);
322 sound_timer_devs
[n
] = &sound_timer
;