1/*
2 * sound/oss/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}
79EXPORT_SYMBOL(sound_timer_syncinterval);
80
81static void tmr_reset(void)
82{
83	unsigned long   flags;
84
85	spin_lock_irqsave(&lock,flags);
86	tmr_offs = 0;
87	ticks_offs = 0;
88	tmr_ctr = 0;
89	next_event_time = (unsigned long) -1;
90	prev_event_time = 0;
91	curr_ticks = 0;
92	spin_unlock_irqrestore(&lock,flags);
93}
94
95static int timer_open(int dev, int mode)
96{
97	if (opened)
98		return -EBUSY;
99	tmr_reset();
100	curr_tempo = 60;
101	curr_timebase = 100;
102	opened = 1;
103	reprogram_timer();
104	return 0;
105}
106
107static void timer_close(int dev)
108{
109	opened = tmr_running = 0;
110	tmr->tmr_disable(tmr->dev);
111}
112
113static int timer_event(int dev, unsigned char *event)
114{
115	unsigned char cmd = event[1];
116	unsigned long parm = *(int *) &event[4];
117
118	switch (cmd)
119	{
120		case TMR_WAIT_REL:
121			parm += prev_event_time;
122		case TMR_WAIT_ABS:
123			if (parm > 0)
124			{
125				long time;
126
127				if (parm <= curr_ticks)	/* It's the time */
128					return TIMER_NOT_ARMED;
129				time = parm;
130				next_event_time = prev_event_time = time;
131				return TIMER_ARMED;
132			}
133			break;
134
135		case TMR_START:
136			tmr_reset();
137			tmr_running = 1;
138			reprogram_timer();
139			break;
140
141		case TMR_STOP:
142			tmr_running = 0;
143			break;
144
145		case TMR_CONTINUE:
146			tmr_running = 1;
147			reprogram_timer();
148			break;
149
150		case TMR_TEMPO:
151			if (parm)
152			{
153				if (parm < 8)
154					parm = 8;
155				if (parm > 250)
156					parm = 250;
157				tmr_offs = tmr_ctr;
158				ticks_offs += tmr2ticks(tmr_ctr);
159				tmr_ctr = 0;
160				curr_tempo = parm;
161				reprogram_timer();
162			}
163			break;
164
165		case TMR_ECHO:
166			seq_copy_to_input(event, 8);
167			break;
168
169		default:;
170	}
171	return TIMER_NOT_ARMED;
172}
173
174static unsigned long timer_get_time(int dev)
175{
176	if (!opened)
177		return 0;
178	return curr_ticks;
179}
180
181static int timer_ioctl(int dev, unsigned int cmd, void __user *arg)
182{
183	int __user *p = arg;
184	int val;
185
186	switch (cmd)
187	{
188		case SNDCTL_TMR_SOURCE:
189			val = TMR_INTERNAL;
190			break;
191
192		case SNDCTL_TMR_START:
193			tmr_reset();
194			tmr_running = 1;
195			return 0;
196
197		case SNDCTL_TMR_STOP:
198			tmr_running = 0;
199			return 0;
200
201		case SNDCTL_TMR_CONTINUE:
202			tmr_running = 1;
203			return 0;
204
205		case SNDCTL_TMR_TIMEBASE:
206			if (get_user(val, p))
207				return -EFAULT;
208			if (val)
209			{
210				if (val < 1)
211					val = 1;
212				if (val > 1000)
213					val = 1000;
214				curr_timebase = val;
215			}
216			val = curr_timebase;
217			break;
218
219		case SNDCTL_TMR_TEMPO:
220			if (get_user(val, p))
221				return -EFAULT;
222			if (val)
223			{
224				if (val < 8)
225					val = 8;
226				if (val > 250)
227					val = 250;
228				tmr_offs = tmr_ctr;
229				ticks_offs += tmr2ticks(tmr_ctr);
230				tmr_ctr = 0;
231				curr_tempo = val;
232				reprogram_timer();
233			}
234			val = curr_tempo;
235			break;
236
237		case SNDCTL_SEQ_CTRLRATE:
238			if (get_user(val, p))
239				return -EFAULT;
240			if (val != 0)	/* Can't change */
241				return -EINVAL;
242			val = ((curr_tempo * curr_timebase) + 30) / 60;
243			break;
244
245		case SNDCTL_SEQ_GETTIME:
246			val = curr_ticks;
247			break;
248
249		case SNDCTL_TMR_METRONOME:
250		default:
251			return -EINVAL;
252	}
253	return put_user(val, p);
254}
255
256static void timer_arm(int dev, long time)
257{
258	if (time < 0)
259		time = curr_ticks + 1;
260	else if (time <= curr_ticks)	/* It's the time */
261		return;
262
263	next_event_time = prev_event_time = time;
264	return;
265}
266
267static struct sound_timer_operations sound_timer =
268{
269	.owner		= THIS_MODULE,
270	.info		= {"Sound Timer", 0},
271	.priority	= 1,	/* Priority */
272	.devlink	= 0,	/* Local device link */
273	.open		= timer_open,
274	.close		= timer_close,
275	.event		= timer_event,
276	.get_time	= timer_get_time,
277	.ioctl		= timer_ioctl,
278	.arm_timer	= timer_arm
279};
280
281void sound_timer_interrupt(void)
282{
283	unsigned long flags;
284
285	if (!opened)
286		return;
287
288	tmr->tmr_restart(tmr->dev);
289
290	if (!tmr_running)
291		return;
292
293	spin_lock_irqsave(&lock,flags);
294	tmr_ctr++;
295	curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
296
297	if (curr_ticks >= next_event_time)
298	{
299		next_event_time = (unsigned long) -1;
300		sequencer_timer(0);
301	}
302	spin_unlock_irqrestore(&lock,flags);
303}
304EXPORT_SYMBOL(sound_timer_interrupt);
305
306void  sound_timer_init(struct sound_lowlev_timer *t, char *name)
307{
308	int n;
309
310	if (initialized)
311	{
312		if (t->priority <= tmr->priority)
313			return;	/* There is already a similar or better timer */
314		tmr = t;
315		return;
316	}
317	initialized = 1;
318	tmr = t;
319
320	n = sound_alloc_timerdev();
321	if (n == -1)
322		n = 0;		/* Overwrite the system timer */
323	strcpy(sound_timer.info.name, name);
324	sound_timer_devs[n] = &sound_timer;
325}
326EXPORT_SYMBOL(sound_timer_init);
327