Linux Audio

Check our new training course

Loading...
  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 DEFINE_SPINLOCK(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	strlcpy(sound_timer.info.name, name, sizeof(sound_timer.info.name));
324	sound_timer_devs[n] = &sound_timer;
325}
326EXPORT_SYMBOL(sound_timer_init);
327