Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sound_timer.c
Go to the documentation of this file.
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 
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 static DEFINE_SPINLOCK(lock);
30 
31 static 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 
45 void 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 
68 void 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 }
80 
81 static 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 
95 static 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 
107 static void timer_close(int dev)
108 {
109  opened = tmr_running = 0;
110  tmr->tmr_disable(tmr->dev);
111 }
112 
113 static 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 
174 static unsigned long timer_get_time(int dev)
175 {
176  if (!opened)
177  return 0;
178  return curr_ticks;
179 }
180 
181 static 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 
250  default:
251  return -EINVAL;
252  }
253  return put_user(val, p);
254 }
255 
256 static 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 
267 static 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 
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 }
305 
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 }
327