sound_timer.c 5.67 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2
/*
 * sound/sound_timer.c
Linus Torvalds's avatar
Linus Torvalds committed
3 4
 */
/*
Linus Torvalds's avatar
Linus Torvalds committed
5
 * Copyright (C) by Hannu Savolainen 1993-1997
Linus Torvalds's avatar
Linus Torvalds committed
6
 *
Linus Torvalds's avatar
Linus Torvalds committed
7
 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
Linus Torvalds's avatar
Linus Torvalds committed
8 9
 * Version 2 (June 1991). See the "COPYING" file distributed with this software
 * for more info.
Linus Torvalds's avatar
Linus Torvalds committed
10
 */
Linus Torvalds's avatar
Linus Torvalds committed
11 12 13
/*
 * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
 */
Linus Torvalds's avatar
Linus Torvalds committed
14 15
#include <linux/config.h>

Linus Torvalds's avatar
Linus Torvalds committed
16 17 18

#include "sound_config.h"

Linus Torvalds's avatar
Linus Torvalds committed
19
#if defined(CONFIG_SEQUENCER) || defined(CONFIG_SEQUENCER_MODULE)
Linus Torvalds's avatar
Linus Torvalds committed
20 21 22 23 24 25 26 27 28 29

static volatile int initialized = 0, opened = 0, tmr_running = 0;
static volatile time_t tmr_offs, tmr_ctr;
static volatile unsigned long ticks_offs;
static volatile int curr_tempo, curr_timebase;
static volatile unsigned long curr_ticks;
static volatile unsigned long next_event_time;
static unsigned long prev_event_time;
static volatile unsigned long usecs_per_tmr;	/* Length of the current interval */

Linus Torvalds's avatar
Linus Torvalds committed
30
static struct sound_lowlev_timer *tmr = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
31

Linus Torvalds's avatar
Linus Torvalds committed
32
static unsigned long tmr2ticks(int tmr_value)
Linus Torvalds's avatar
Linus Torvalds committed
33
{
Linus Torvalds's avatar
Linus Torvalds committed
34 35 36
	/*
	 *    Convert timer ticks to MIDI ticks
	 */
Linus Torvalds's avatar
Linus Torvalds committed
37

Linus Torvalds's avatar
Linus Torvalds committed
38 39
	unsigned long tmp;
	unsigned long scale;
Linus Torvalds's avatar
Linus Torvalds committed
40

Linus Torvalds's avatar
Linus Torvalds committed
41 42 43
	tmp = tmr_value * usecs_per_tmr;	/* Convert to usecs */
	scale = (60 * 1000000) / (curr_tempo * curr_timebase);	/* usecs per MIDI tick */
	return (tmp + (scale / 2)) / scale;
Linus Torvalds's avatar
Linus Torvalds committed
44 45
}

Linus Torvalds's avatar
Linus Torvalds committed
46
void reprogram_timer(void)
Linus Torvalds's avatar
Linus Torvalds committed
47
{
Linus Torvalds's avatar
Linus Torvalds committed
48
	unsigned long   usecs_per_tick;
Linus Torvalds's avatar
Linus Torvalds committed
49

Linus Torvalds's avatar
Linus Torvalds committed
50
	usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
Linus Torvalds's avatar
Linus Torvalds committed
51

Linus Torvalds's avatar
Linus Torvalds committed
52
	/*
Linus Torvalds's avatar
Linus Torvalds committed
53
	 * Don't kill the system by setting too high timer rate
Linus Torvalds's avatar
Linus Torvalds committed
54 55 56
	 */
	if (usecs_per_tick < 2000)
		usecs_per_tick = 2000;
Linus Torvalds's avatar
Linus Torvalds committed
57

Linus Torvalds's avatar
Linus Torvalds committed
58
	usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
Linus Torvalds's avatar
Linus Torvalds committed
59
}
Linus Torvalds's avatar
Linus Torvalds committed
60

Linus Torvalds's avatar
Linus Torvalds committed
61
void sound_timer_syncinterval(unsigned int new_usecs)
Linus Torvalds's avatar
Linus Torvalds committed
62
{
Linus Torvalds's avatar
Linus Torvalds committed
63 64 65 66
	/*
	 *    This routine is called by the hardware level if
	 *      the clock frequency has changed for some reason.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
67 68 69 70
	tmr_offs = tmr_ctr;
	ticks_offs += tmr2ticks(tmr_ctr);
	tmr_ctr = 0;
	usecs_per_tmr = new_usecs;
Linus Torvalds's avatar
Linus Torvalds committed
71 72
}

Linus Torvalds's avatar
Linus Torvalds committed
73
static void tmr_reset(void)
Linus Torvalds's avatar
Linus Torvalds committed
74
{
Linus Torvalds's avatar
Linus Torvalds committed
75 76 77 78 79 80 81 82 83 84 85
	unsigned long   flags;

	save_flags(flags);
	cli();
	tmr_offs = 0;
	ticks_offs = 0;
	tmr_ctr = 0;
	next_event_time = (unsigned long) -1;
	prev_event_time = 0;
	curr_ticks = 0;
	restore_flags(flags);
Linus Torvalds's avatar
Linus Torvalds committed
86 87
}

Linus Torvalds's avatar
Linus Torvalds committed
88
static int timer_open(int dev, int mode)
Linus Torvalds's avatar
Linus Torvalds committed
89
{
Linus Torvalds's avatar
Linus Torvalds committed
90 91 92 93 94 95 96 97
	if (opened)
		return -EBUSY;
	tmr_reset();
	curr_tempo = 60;
	curr_timebase = 100;
	opened = 1;
	reprogram_timer();
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
98 99
}

Linus Torvalds's avatar
Linus Torvalds committed
100
static void timer_close(int dev)
Linus Torvalds's avatar
Linus Torvalds committed
101
{
Linus Torvalds's avatar
Linus Torvalds committed
102 103
	opened = tmr_running = 0;
	tmr->tmr_disable(tmr->dev);
Linus Torvalds's avatar
Linus Torvalds committed
104 105
}

Linus Torvalds's avatar
Linus Torvalds committed
106
static int timer_event(int dev, unsigned char *event)
Linus Torvalds's avatar
Linus Torvalds committed
107
{
Linus Torvalds's avatar
Linus Torvalds committed
108 109
	unsigned char cmd = event[1];
	unsigned long parm = *(int *) &event[4];
Linus Torvalds's avatar
Linus Torvalds committed
110 111

	switch (cmd)
Linus Torvalds's avatar
Linus Torvalds committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
	{
		case TMR_WAIT_REL:
			parm += prev_event_time;
		case TMR_WAIT_ABS:
			if (parm > 0)
			{
				long time;

				if (parm <= curr_ticks)	/* It's the time */
					return TIMER_NOT_ARMED;
				time = parm;
				next_event_time = prev_event_time = time;
				return TIMER_ARMED;
			}
			break;

		case TMR_START:
			tmr_reset();
			tmr_running = 1;
			reprogram_timer();
			break;

		case TMR_STOP:
			tmr_running = 0;
			break;
Linus Torvalds's avatar
Linus Torvalds committed
137

Linus Torvalds's avatar
Linus Torvalds committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
		case TMR_CONTINUE:
			tmr_running = 1;
			reprogram_timer();
			break;

		case TMR_TEMPO:
			if (parm)
			{
				if (parm < 8)
					parm = 8;
				if (parm > 250)
					parm = 250;
				tmr_offs = tmr_ctr;
				ticks_offs += tmr2ticks(tmr_ctr);
				tmr_ctr = 0;
				curr_tempo = parm;
				reprogram_timer();
			}
			break;

		case TMR_ECHO:
			seq_copy_to_input(event, 8);
			break;

		default:
	}
Linus Torvalds's avatar
Linus Torvalds committed
164
	return TIMER_NOT_ARMED;
Linus Torvalds's avatar
Linus Torvalds committed
165 166
}

Linus Torvalds's avatar
Linus Torvalds committed
167
static unsigned long timer_get_time(int dev)
Linus Torvalds's avatar
Linus Torvalds committed
168
{
Linus Torvalds's avatar
Linus Torvalds committed
169 170 171
	if (!opened)
		return 0;
	return curr_ticks;
Linus Torvalds's avatar
Linus Torvalds committed
172 173
}

Linus Torvalds's avatar
Linus Torvalds committed
174
static int timer_ioctl(int dev, unsigned int cmd, caddr_t arg)
Linus Torvalds's avatar
Linus Torvalds committed
175
{
Linus Torvalds's avatar
Linus Torvalds committed
176
	int val;
Linus Torvalds's avatar
Linus Torvalds committed
177

Linus Torvalds's avatar
Linus Torvalds committed
178 179 180 181 182
	switch (cmd) 
	{
		case SNDCTL_TMR_SOURCE:
			val = TMR_INTERNAL;
			break;
Linus Torvalds's avatar
Linus Torvalds committed
183

Linus Torvalds's avatar
Linus Torvalds committed
184 185 186 187
		case SNDCTL_TMR_START:
			tmr_reset();
			tmr_running = 1;
			return 0;
Linus Torvalds's avatar
Linus Torvalds committed
188
		
Linus Torvalds's avatar
Linus Torvalds committed
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
		case SNDCTL_TMR_STOP:
			tmr_running = 0;
			return 0;

		case SNDCTL_TMR_CONTINUE:
			tmr_running = 1;
			return 0;

		case SNDCTL_TMR_TIMEBASE:
			if (get_user(val, (int *)arg))
				return -EFAULT;
			if (val) 
			{
				if (val < 1)
					val = 1;
				if (val > 1000)
					val = 1000;
				curr_timebase = val;
			}
			val = curr_timebase;
			break;

		case SNDCTL_TMR_TEMPO:
			if (get_user(val, (int *)arg))
				return -EFAULT;
			if (val) 
			{
				if (val < 8)
					val = 8;
				if (val > 250)
					val = 250;
				tmr_offs = tmr_ctr;
				ticks_offs += tmr2ticks(tmr_ctr);
				tmr_ctr = 0;
				curr_tempo = val;
				reprogram_timer();
			}
			val = curr_tempo;
			break;

		case SNDCTL_SEQ_CTRLRATE:
			if (get_user(val, (int *)arg))
				return -EFAULT;
			if (val != 0)	/* Can't change */
				return -EINVAL;
			val = ((curr_tempo * curr_timebase) + 30) / 60;
			break;
Linus Torvalds's avatar
Linus Torvalds committed
236
		
Linus Torvalds's avatar
Linus Torvalds committed
237 238 239
		case SNDCTL_SEQ_GETTIME:
			val = curr_ticks;
			break;
Linus Torvalds's avatar
Linus Torvalds committed
240
		
Linus Torvalds's avatar
Linus Torvalds committed
241 242 243
		case SNDCTL_TMR_METRONOME:
		default:
			return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
244
	}
Linus Torvalds's avatar
Linus Torvalds committed
245
	return put_user(val, (int *)arg);
Linus Torvalds's avatar
Linus Torvalds committed
246 247
}

Linus Torvalds's avatar
Linus Torvalds committed
248
static void timer_arm(int dev, long time)
Linus Torvalds's avatar
Linus Torvalds committed
249
{
Linus Torvalds's avatar
Linus Torvalds committed
250 251 252 253
	if (time < 0)
		time = curr_ticks + 1;
	else if (time <= curr_ticks)	/* It's the time */
		return;
Linus Torvalds's avatar
Linus Torvalds committed
254

Linus Torvalds's avatar
Linus Torvalds committed
255 256
	next_event_time = prev_event_time = time;
	return;
Linus Torvalds's avatar
Linus Torvalds committed
257 258 259 260
}

static struct sound_timer_operations sound_timer =
{
Linus Torvalds's avatar
Linus Torvalds committed
261 262 263 264 265 266 267 268 269
	{"Sound Timer", 0},
	1,			/* Priority */
	0,			/* Local device link */
	timer_open,
	timer_close,
	timer_event,
	timer_get_time,
	timer_ioctl,
	timer_arm
Linus Torvalds's avatar
Linus Torvalds committed
270 271
};

Linus Torvalds's avatar
Linus Torvalds committed
272
void sound_timer_interrupt(void)
Linus Torvalds's avatar
Linus Torvalds committed
273
{
Linus Torvalds's avatar
Linus Torvalds committed
274 275
	if (!opened)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
276

Linus Torvalds's avatar
Linus Torvalds committed
277
	tmr->tmr_restart(tmr->dev);
Linus Torvalds's avatar
Linus Torvalds committed
278

Linus Torvalds's avatar
Linus Torvalds committed
279 280
	if (!tmr_running)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
281

Linus Torvalds's avatar
Linus Torvalds committed
282 283
	tmr_ctr++;
	curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
Linus Torvalds's avatar
Linus Torvalds committed
284

Linus Torvalds's avatar
Linus Torvalds committed
285
	if (curr_ticks >= next_event_time)
Linus Torvalds's avatar
Linus Torvalds committed
286 287 288 289
	{
		next_event_time = (unsigned long) -1;
		sequencer_timer(0);
	}
Linus Torvalds's avatar
Linus Torvalds committed
290 291
}

Linus Torvalds's avatar
Linus Torvalds committed
292
void  sound_timer_init(struct sound_lowlev_timer *t, char *name)
Linus Torvalds's avatar
Linus Torvalds committed
293
{
Linus Torvalds's avatar
Linus Torvalds committed
294
	int n;
Linus Torvalds's avatar
Linus Torvalds committed
295 296

	if (initialized)
Linus Torvalds's avatar
Linus Torvalds committed
297 298 299 300 301 302
	{
		if (t->priority <= tmr->priority)
			return;	/* There is already a similar or better timer */
		tmr = t;
		return;
	}
Linus Torvalds's avatar
Linus Torvalds committed
303 304 305 306 307 308 309 310
	initialized = 1;
	tmr = t;

	n = sound_alloc_timerdev();
	if (n == -1)
		n = 0;		/* Overwrite the system timer */
	strcpy(sound_timer.info.name, name);
	sound_timer_devs[n] = &sound_timer;
Linus Torvalds's avatar
Linus Torvalds committed
311 312 313
}

#endif