1 /*
2  * sound/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 
16 
17 #include "sound_config.h"
18 
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 */
27 
28 static struct sound_lowlev_timer *tmr;
29 
tmr2ticks(int tmr_value)30 static unsigned long tmr2ticks(int tmr_value)
31 {
32 	/*
33 	 *    Convert timer ticks to MIDI ticks
34 	 */
35 
36 	unsigned long tmp;
37 	unsigned long scale;
38 
39 	tmp = tmr_value * usecs_per_tmr;	/* Convert to usecs */
40 	scale = (60 * 1000000) / (curr_tempo * curr_timebase);	/* usecs per MIDI tick */
41 	return (tmp + (scale / 2)) / scale;
42 }
43 
reprogram_timer(void)44 void reprogram_timer(void)
45 {
46 	unsigned long   usecs_per_tick;
47 
48 	/*
49 	 *	The user is changing the timer rate before setting a timer
50 	 *	slap, bad bad not allowed.
51 	 */
52 
53 	if(!tmr)
54 		return;
55 
56 	usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
57 
58 	/*
59 	 * Don't kill the system by setting too high timer rate
60 	 */
61 	if (usecs_per_tick < 2000)
62 		usecs_per_tick = 2000;
63 
64 	usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
65 }
66 
sound_timer_syncinterval(unsigned int new_usecs)67 void sound_timer_syncinterval(unsigned int new_usecs)
68 {
69 	/*
70 	 *    This routine is called by the hardware level if
71 	 *      the clock frequency has changed for some reason.
72 	 */
73 	tmr_offs = tmr_ctr;
74 	ticks_offs += tmr2ticks(tmr_ctr);
75 	tmr_ctr = 0;
76 	usecs_per_tmr = new_usecs;
77 }
78 
tmr_reset(void)79 static void tmr_reset(void)
80 {
81 	unsigned long   flags;
82 
83 	save_flags(flags);
84 	cli();
85 	tmr_offs = 0;
86 	ticks_offs = 0;
87 	tmr_ctr = 0;
88 	next_event_time = (unsigned long) -1;
89 	prev_event_time = 0;
90 	curr_ticks = 0;
91 	restore_flags(flags);
92 }
93 
timer_open(int dev,int mode)94 static int timer_open(int dev, int mode)
95 {
96 	if (opened)
97 		return -EBUSY;
98 	tmr_reset();
99 	curr_tempo = 60;
100 	curr_timebase = 100;
101 	opened = 1;
102 	reprogram_timer();
103 	return 0;
104 }
105 
timer_close(int dev)106 static void timer_close(int dev)
107 {
108 	opened = tmr_running = 0;
109 	tmr->tmr_disable(tmr->dev);
110 }
111 
timer_event(int dev,unsigned char * event)112 static int timer_event(int dev, unsigned char *event)
113 {
114 	unsigned char cmd = event[1];
115 	unsigned long parm = *(int *) &event[4];
116 
117 	switch (cmd)
118 	{
119 		case TMR_WAIT_REL:
120 			parm += prev_event_time;
121 		case TMR_WAIT_ABS:
122 			if (parm > 0)
123 			{
124 				long time;
125 
126 				if (parm <= curr_ticks)	/* It's the time */
127 					return TIMER_NOT_ARMED;
128 				time = parm;
129 				next_event_time = prev_event_time = time;
130 				return TIMER_ARMED;
131 			}
132 			break;
133 
134 		case TMR_START:
135 			tmr_reset();
136 			tmr_running = 1;
137 			reprogram_timer();
138 			break;
139 
140 		case TMR_STOP:
141 			tmr_running = 0;
142 			break;
143 
144 		case TMR_CONTINUE:
145 			tmr_running = 1;
146 			reprogram_timer();
147 			break;
148 
149 		case TMR_TEMPO:
150 			if (parm)
151 			{
152 				if (parm < 8)
153 					parm = 8;
154 				if (parm > 250)
155 					parm = 250;
156 				tmr_offs = tmr_ctr;
157 				ticks_offs += tmr2ticks(tmr_ctr);
158 				tmr_ctr = 0;
159 				curr_tempo = parm;
160 				reprogram_timer();
161 			}
162 			break;
163 
164 		case TMR_ECHO:
165 			seq_copy_to_input(event, 8);
166 			break;
167 
168 		default:;
169 	}
170 	return TIMER_NOT_ARMED;
171 }
172 
timer_get_time(int dev)173 static unsigned long timer_get_time(int dev)
174 {
175 	if (!opened)
176 		return 0;
177 	return curr_ticks;
178 }
179 
timer_ioctl(int dev,unsigned int cmd,caddr_t arg)180 static int timer_ioctl(int dev, unsigned int cmd, caddr_t arg)
181 {
182 	int val;
183 
184 	switch (cmd)
185 	{
186 		case SNDCTL_TMR_SOURCE:
187 			val = TMR_INTERNAL;
188 			break;
189 
190 		case SNDCTL_TMR_START:
191 			tmr_reset();
192 			tmr_running = 1;
193 			return 0;
194 
195 		case SNDCTL_TMR_STOP:
196 			tmr_running = 0;
197 			return 0;
198 
199 		case SNDCTL_TMR_CONTINUE:
200 			tmr_running = 1;
201 			return 0;
202 
203 		case SNDCTL_TMR_TIMEBASE:
204 			if (get_user(val, (int *)arg))
205 				return -EFAULT;
206 			if (val)
207 			{
208 				if (val < 1)
209 					val = 1;
210 				if (val > 1000)
211 					val = 1000;
212 				curr_timebase = val;
213 			}
214 			val = curr_timebase;
215 			break;
216 
217 		case SNDCTL_TMR_TEMPO:
218 			if (get_user(val, (int *)arg))
219 				return -EFAULT;
220 			if (val)
221 			{
222 				if (val < 8)
223 					val = 8;
224 				if (val > 250)
225 					val = 250;
226 				tmr_offs = tmr_ctr;
227 				ticks_offs += tmr2ticks(tmr_ctr);
228 				tmr_ctr = 0;
229 				curr_tempo = val;
230 				reprogram_timer();
231 			}
232 			val = curr_tempo;
233 			break;
234 
235 		case SNDCTL_SEQ_CTRLRATE:
236 			if (get_user(val, (int *)arg))
237 				return -EFAULT;
238 			if (val != 0)	/* Can't change */
239 				return -EINVAL;
240 			val = ((curr_tempo * curr_timebase) + 30) / 60;
241 			break;
242 
243 		case SNDCTL_SEQ_GETTIME:
244 			val = curr_ticks;
245 			break;
246 
247 		case SNDCTL_TMR_METRONOME:
248 		default:
249 			return -EINVAL;
250 	}
251 	return put_user(val, (int *)arg);
252 }
253 
timer_arm(int dev,long time)254 static void timer_arm(int dev, long time)
255 {
256 	if (time < 0)
257 		time = curr_ticks + 1;
258 	else if (time <= curr_ticks)	/* It's the time */
259 		return;
260 
261 	next_event_time = prev_event_time = time;
262 	return;
263 }
264 
265 static struct sound_timer_operations sound_timer =
266 {
267 	owner:		THIS_MODULE,
268 	info:		{"Sound Timer", 0},
269 	priority:	1,	/* Priority */
270 	devlink:	0,	/* Local device link */
271 	open:		timer_open,
272 	close:		timer_close,
273 	event:		timer_event,
274 	get_time:	timer_get_time,
275 	ioctl:		timer_ioctl,
276 	arm_timer:	timer_arm
277 };
278 
sound_timer_interrupt(void)279 void sound_timer_interrupt(void)
280 {
281 	if (!opened)
282 		return;
283 
284 	tmr->tmr_restart(tmr->dev);
285 
286 	if (!tmr_running)
287 		return;
288 
289 	tmr_ctr++;
290 	curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
291 
292 	if (curr_ticks >= next_event_time)
293 	{
294 		next_event_time = (unsigned long) -1;
295 		sequencer_timer(0);
296 	}
297 }
298 
sound_timer_init(struct sound_lowlev_timer * t,char * name)299 void  sound_timer_init(struct sound_lowlev_timer *t, char *name)
300 {
301 	int n;
302 
303 	if (initialized)
304 	{
305 		if (t->priority <= tmr->priority)
306 			return;	/* There is already a similar or better timer */
307 		tmr = t;
308 		return;
309 	}
310 	initialized = 1;
311 	tmr = t;
312 
313 	n = sound_alloc_timerdev();
314 	if (n == -1)
315 		n = 0;		/* Overwrite the system timer */
316 	strcpy(sound_timer.info.name, name);
317 	sound_timer_devs[n] = &sound_timer;
318 }
319