1/*
2 * sound/oss/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 <linux/spinlock.h>
19#include "sound_config.h"
20
21static volatile int opened, tmr_running;
22static volatile time_t tmr_offs, tmr_ctr;
23static volatile unsigned long ticks_offs;
24static volatile int curr_tempo, curr_timebase;
25static volatile unsigned long curr_ticks;
26static volatile unsigned long next_event_time;
27static unsigned long prev_event_time;
28
29static void     poll_def_tmr(unsigned long dummy);
30static DEFINE_SPINLOCK(lock);
31static DEFINE_TIMER(def_tmr, poll_def_tmr, 0, 0);
32
33static unsigned long
34tmr2ticks(int tmr_value)
35{
36	/*
37	 *    Convert timer ticks to MIDI ticks
38	 */
39
40	unsigned long tmp;
41	unsigned long scale;
42
43	/* tmr_value (ticks per sec) *
44	   1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */
45	tmp = tmr_value * (1000000 / HZ);
46	scale = (60 * 1000000) / (curr_tempo * curr_timebase);	/* usecs per MIDI tick */
47	return (tmp + scale / 2) / scale;
48}
49
50static void
51poll_def_tmr(unsigned long dummy)
52{
53
54	if (opened)
55	  {
56
57		  {
58			  def_tmr.expires = (1) + jiffies;
59			  add_timer(&def_tmr);
60		  };
61
62		  if (tmr_running)
63		    {
64				spin_lock(&lock);
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				spin_unlock(&lock);
74		    }
75	  }
76}
77
78static void
79tmr_reset(void)
80{
81	unsigned long   flags;
82
83	spin_lock_irqsave(&lock,flags);
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	spin_unlock_irqrestore(&lock,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, void __user *arg)
194{
195	int __user *p = arg;
196	int val;
197
198	switch (cmd) {
199	case SNDCTL_TMR_SOURCE:
200		return __put_user(TMR_INTERNAL, p);
201
202	case SNDCTL_TMR_START:
203		tmr_reset();
204		tmr_running = 1;
205		return 0;
206
207	case SNDCTL_TMR_STOP:
208		tmr_running = 0;
209		return 0;
210
211	case SNDCTL_TMR_CONTINUE:
212		tmr_running = 1;
213		return 0;
214
215	case SNDCTL_TMR_TIMEBASE:
216		if (__get_user(val, p))
217			return -EFAULT;
218		if (val) {
219			if (val < 1)
220				val = 1;
221			if (val > 1000)
222				val = 1000;
223			curr_timebase = val;
224		}
225		return __put_user(curr_timebase, p);
226
227	case SNDCTL_TMR_TEMPO:
228		if (__get_user(val, p))
229			return -EFAULT;
230		if (val) {
231			if (val < 8)
232				val = 8;
233			if (val > 250)
234				val = 250;
235			tmr_offs = tmr_ctr;
236			ticks_offs += tmr2ticks(tmr_ctr);
237			tmr_ctr = 0;
238			curr_tempo = val;
239			reprogram_timer();
240		}
241		return __put_user(curr_tempo, p);
242
243	case SNDCTL_SEQ_CTRLRATE:
244		if (__get_user(val, p))
245			return -EFAULT;
246		if (val != 0)	/* Can't change */
247			return -EINVAL;
248		val = ((curr_tempo * curr_timebase) + 30) / 60;
249		return __put_user(val, p);
250
251	case SNDCTL_SEQ_GETTIME:
252		return __put_user(curr_ticks, p);
253
254	case SNDCTL_TMR_METRONOME:
255		/* NOP */
256		break;
257
258	default:;
259	}
260	return -EINVAL;
261}
262
263static void
264def_tmr_arm(int dev, long time)
265{
266	if (time < 0)
267		time = curr_ticks + 1;
268	else if (time <= curr_ticks)	/* It's the time */
269		return;
270
271	next_event_time = prev_event_time = time;
272
273	return;
274}
275
276struct sound_timer_operations default_sound_timer =
277{
278	.owner		= THIS_MODULE,
279	.info		= {"System clock", 0},
280	.priority	= 0,	/* Priority */
281	.devlink	= 0,	/* Local device link */
282	.open		= def_tmr_open,
283	.close		= def_tmr_close,
284	.event		= def_tmr_event,
285	.get_time	= def_tmr_get_time,
286	.ioctl		= def_tmr_ioctl,
287	.arm_timer	= def_tmr_arm
288};
289