1/*
2 * sound/sys_timer.c
3 *
4 * The default timer for the Level 2 sequencer interface
5 * Uses the (1/HZ sec) timer of kernel.
6 */
7/*
8 * Copyright (C) by Hannu Savolainen 1993-1997
9 *
10 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
11 * Version 2 (June 1991). See the "COPYING" file distributed with this software
12 * for more info.
13 */
14/*
15 * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
16 * Andrew Veliath  : adapted tmr2ticks from level 1 sequencer (avoid overflow)
17 */
18#include "sound_config.h"
19
20static volatile int opened = 0, tmr_running = 0;
21static volatile time_t tmr_offs, tmr_ctr;
22static volatile unsigned long ticks_offs;
23static volatile int curr_tempo, curr_timebase;
24static volatile unsigned long curr_ticks;
25static volatile unsigned long next_event_time;
26static unsigned long prev_event_time;
27
28static void     poll_def_tmr(unsigned long dummy);
29
30
31static struct timer_list def_tmr =
32{function: poll_def_tmr};
33
34static unsigned long
35tmr2ticks(int tmr_value)
36{
37	/*
38	 *    Convert timer ticks to MIDI ticks
39	 */
40
41	unsigned long tmp;
42	unsigned long scale;
43
44	/* tmr_value (ticks per sec) *
45	   1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */
46	tmp = tmr_value * (1000000 / HZ);
47	scale = (60 * 1000000) / (curr_tempo * curr_timebase);	/* usecs per MIDI tick */
48	return (tmp + scale / 2) / scale;
49}
50
51static void
52poll_def_tmr(unsigned long dummy)
53{
54
55	if (opened)
56	  {
57
58		  {
59			  def_tmr.expires = (1) + jiffies;
60			  add_timer(&def_tmr);
61		  };
62
63		  if (tmr_running)
64		    {
65			    tmr_ctr++;
66			    curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
67
68			    if (curr_ticks >= next_event_time)
69			      {
70				      next_event_time = (unsigned long) -1;
71				      sequencer_timer(0);
72			      }
73		    }
74	  }
75}
76
77static void
78tmr_reset(void)
79{
80	unsigned long   flags;
81
82	save_flags(flags);
83	cli();
84	tmr_offs = 0;
85	ticks_offs = 0;
86	tmr_ctr = 0;
87	next_event_time = (unsigned long) -1;
88	prev_event_time = 0;
89	curr_ticks = 0;
90	restore_flags(flags);
91}
92
93static int
94def_tmr_open(int dev, int mode)
95{
96	if (opened)
97		return -EBUSY;
98
99	tmr_reset();
100	curr_tempo = 60;
101	curr_timebase = 100;
102	opened = 1;
103
104	;
105
106	{
107		def_tmr.expires = (1) + jiffies;
108		add_timer(&def_tmr);
109	};
110
111	return 0;
112}
113
114static void
115def_tmr_close(int dev)
116{
117	opened = tmr_running = 0;
118	del_timer(&def_tmr);;
119}
120
121static int
122def_tmr_event(int dev, unsigned char *event)
123{
124	unsigned char   cmd = event[1];
125	unsigned long   parm = *(int *) &event[4];
126
127	switch (cmd)
128	  {
129	  case TMR_WAIT_REL:
130		  parm += prev_event_time;
131	  case TMR_WAIT_ABS:
132		  if (parm > 0)
133		    {
134			    long            time;
135
136			    if (parm <= curr_ticks)	/* It's the time */
137				    return TIMER_NOT_ARMED;
138
139			    time = parm;
140			    next_event_time = prev_event_time = time;
141
142			    return TIMER_ARMED;
143		    }
144		  break;
145
146	  case TMR_START:
147		  tmr_reset();
148		  tmr_running = 1;
149		  break;
150
151	  case TMR_STOP:
152		  tmr_running = 0;
153		  break;
154
155	  case TMR_CONTINUE:
156		  tmr_running = 1;
157		  break;
158
159	  case TMR_TEMPO:
160		  if (parm)
161		    {
162			    if (parm < 8)
163				    parm = 8;
164			    if (parm > 360)
165				    parm = 360;
166			    tmr_offs = tmr_ctr;
167			    ticks_offs += tmr2ticks(tmr_ctr);
168			    tmr_ctr = 0;
169			    curr_tempo = parm;
170		    }
171		  break;
172
173	  case TMR_ECHO:
174		  seq_copy_to_input(event, 8);
175		  break;
176
177	  default:;
178	  }
179
180	return TIMER_NOT_ARMED;
181}
182
183static unsigned long
184def_tmr_get_time(int dev)
185{
186	if (!opened)
187		return 0;
188
189	return curr_ticks;
190}
191
192/* same as sound_timer.c:timer_ioctl!? */
193static int def_tmr_ioctl(int dev, unsigned int cmd, caddr_t arg)
194{
195	int val;
196
197	switch (cmd) {
198	case SNDCTL_TMR_SOURCE:
199		return __put_user(TMR_INTERNAL, (int *)arg);
200
201	case SNDCTL_TMR_START:
202		tmr_reset();
203		tmr_running = 1;
204		return 0;
205
206	case SNDCTL_TMR_STOP:
207		tmr_running = 0;
208		return 0;
209
210	case SNDCTL_TMR_CONTINUE:
211		tmr_running = 1;
212		return 0;
213
214	case SNDCTL_TMR_TIMEBASE:
215		if (__get_user(val, (int *)arg))
216			return -EFAULT;
217		if (val) {
218			if (val < 1)
219				val = 1;
220			if (val > 1000)
221				val = 1000;
222			curr_timebase = val;
223		}
224		return __put_user(curr_timebase, (int *)arg);
225
226	case SNDCTL_TMR_TEMPO:
227		if (__get_user(val, (int *)arg))
228			return -EFAULT;
229		if (val) {
230			if (val < 8)
231				val = 8;
232			if (val > 250)
233				val = 250;
234			tmr_offs = tmr_ctr;
235			ticks_offs += tmr2ticks(tmr_ctr);
236			tmr_ctr = 0;
237			curr_tempo = val;
238			reprogram_timer();
239		}
240		return __put_user(curr_tempo, (int *)arg);
241
242	case SNDCTL_SEQ_CTRLRATE:
243		if (__get_user(val, (int *)arg))
244			return -EFAULT;
245		if (val != 0)	/* Can't change */
246			return -EINVAL;
247		val = ((curr_tempo * curr_timebase) + 30) / 60;
248		return __put_user(val, (int *)arg);
249
250	case SNDCTL_SEQ_GETTIME:
251		return __put_user(curr_ticks, (int *)arg);
252
253	case SNDCTL_TMR_METRONOME:
254		/* NOP */
255		break;
256
257	default:;
258	}
259	return -EINVAL;
260}
261
262static void
263def_tmr_arm(int dev, long time)
264{
265	if (time < 0)
266		time = curr_ticks + 1;
267	else if (time <= curr_ticks)	/* It's the time */
268		return;
269
270	next_event_time = prev_event_time = time;
271
272	return;
273}
274
275struct sound_timer_operations default_sound_timer =
276{
277	owner:		THIS_MODULE,
278	info:		{"System clock", 0},
279	priority:	0,	/* Priority */
280	devlink:	0,	/* Local device link */
281	open:		def_tmr_open,
282	close:		def_tmr_close,
283	event:		def_tmr_event,
284	get_time:	def_tmr_get_time,
285	ioctl:		def_tmr_ioctl,
286	arm_timer:	def_tmr_arm
287};
288