Root/
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 | } |
79 | EXPORT_SYMBOL(sound_timer_syncinterval); |
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 | |
249 | case SNDCTL_TMR_METRONOME: |
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 | |
281 | void sound_timer_interrupt(void) |
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 | } |
304 | EXPORT_SYMBOL(sound_timer_interrupt); |
305 | |
306 | void sound_timer_init(struct sound_lowlev_timer *t, char *name) |
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 | } |
326 | EXPORT_SYMBOL(sound_timer_init); |
327 | |
328 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9