]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * ALSA sequencer Timer | |
3 | * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl> | |
c1017a4c | 4 | * Jaroslav Kysela <perex@perex.cz> |
1da177e4 LT |
5 | * |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | * | |
21 | */ | |
22 | ||
1da177e4 LT |
23 | #include <sound/core.h> |
24 | #include <linux/slab.h> | |
25 | #include "seq_timer.h" | |
26 | #include "seq_queue.h" | |
27 | #include "seq_info.h" | |
28 | ||
87ef7779 CL |
29 | /* allowed sequencer timer frequencies, in Hz */ |
30 | #define MIN_FREQUENCY 10 | |
31 | #define MAX_FREQUENCY 6250 | |
32 | #define DEFAULT_FREQUENCY 1000 | |
33 | ||
1da177e4 LT |
34 | #define SKEW_BASE 0x10000 /* 16bit shift */ |
35 | ||
a32f6674 | 36 | static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr) |
1da177e4 | 37 | { |
a32f6674 CL |
38 | if (tmr->tempo < 1000000) |
39 | tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq; | |
1da177e4 LT |
40 | else { |
41 | /* might overflow.. */ | |
42 | unsigned int s; | |
a32f6674 CL |
43 | s = tmr->tempo % tmr->ppq; |
44 | s = (s * 1000) / tmr->ppq; | |
45 | tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000; | |
46 | tmr->tick.resolution += s; | |
1da177e4 | 47 | } |
a32f6674 CL |
48 | if (tmr->tick.resolution <= 0) |
49 | tmr->tick.resolution = 1; | |
50 | snd_seq_timer_update_tick(&tmr->tick, 0); | |
1da177e4 LT |
51 | } |
52 | ||
53 | /* create new timer (constructor) */ | |
c7e0b5bf | 54 | struct snd_seq_timer *snd_seq_timer_new(void) |
1da177e4 | 55 | { |
c7e0b5bf | 56 | struct snd_seq_timer *tmr; |
1da177e4 | 57 | |
ecca82b4 | 58 | tmr = kzalloc(sizeof(*tmr), GFP_KERNEL); |
1da177e4 LT |
59 | if (tmr == NULL) { |
60 | snd_printd("malloc failed for snd_seq_timer_new() \n"); | |
61 | return NULL; | |
62 | } | |
63 | spin_lock_init(&tmr->lock); | |
64 | ||
65 | /* reset setup to defaults */ | |
66 | snd_seq_timer_defaults(tmr); | |
67 | ||
68 | /* reset time */ | |
69 | snd_seq_timer_reset(tmr); | |
70 | ||
71 | return tmr; | |
72 | } | |
73 | ||
74 | /* delete timer (destructor) */ | |
c7e0b5bf | 75 | void snd_seq_timer_delete(struct snd_seq_timer **tmr) |
1da177e4 | 76 | { |
c7e0b5bf | 77 | struct snd_seq_timer *t = *tmr; |
1da177e4 LT |
78 | *tmr = NULL; |
79 | ||
80 | if (t == NULL) { | |
81 | snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n"); | |
82 | return; | |
83 | } | |
84 | t->running = 0; | |
85 | ||
86 | /* reset time */ | |
87 | snd_seq_timer_stop(t); | |
88 | snd_seq_timer_reset(t); | |
89 | ||
90 | kfree(t); | |
91 | } | |
92 | ||
c7e0b5bf | 93 | void snd_seq_timer_defaults(struct snd_seq_timer * tmr) |
1da177e4 LT |
94 | { |
95 | /* setup defaults */ | |
96 | tmr->ppq = 96; /* 96 PPQ */ | |
97 | tmr->tempo = 500000; /* 120 BPM */ | |
a32f6674 | 98 | snd_seq_timer_set_tick_resolution(tmr); |
1da177e4 LT |
99 | tmr->running = 0; |
100 | ||
101 | tmr->type = SNDRV_SEQ_TIMER_ALSA; | |
102 | tmr->alsa_id.dev_class = seq_default_timer_class; | |
103 | tmr->alsa_id.dev_sclass = seq_default_timer_sclass; | |
104 | tmr->alsa_id.card = seq_default_timer_card; | |
105 | tmr->alsa_id.device = seq_default_timer_device; | |
106 | tmr->alsa_id.subdevice = seq_default_timer_subdevice; | |
107 | tmr->preferred_resolution = seq_default_timer_resolution; | |
108 | ||
109 | tmr->skew = tmr->skew_base = SKEW_BASE; | |
110 | } | |
111 | ||
c7e0b5bf | 112 | void snd_seq_timer_reset(struct snd_seq_timer * tmr) |
1da177e4 LT |
113 | { |
114 | unsigned long flags; | |
115 | ||
116 | spin_lock_irqsave(&tmr->lock, flags); | |
117 | ||
118 | /* reset time & songposition */ | |
119 | tmr->cur_time.tv_sec = 0; | |
120 | tmr->cur_time.tv_nsec = 0; | |
121 | ||
122 | tmr->tick.cur_tick = 0; | |
123 | tmr->tick.fraction = 0; | |
124 | ||
125 | spin_unlock_irqrestore(&tmr->lock, flags); | |
126 | } | |
127 | ||
128 | ||
129 | /* called by timer interrupt routine. the period time since previous invocation is passed */ | |
c7e0b5bf | 130 | static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, |
1da177e4 LT |
131 | unsigned long resolution, |
132 | unsigned long ticks) | |
133 | { | |
134 | unsigned long flags; | |
c7e0b5bf TI |
135 | struct snd_seq_queue *q = timeri->callback_data; |
136 | struct snd_seq_timer *tmr; | |
1da177e4 LT |
137 | |
138 | if (q == NULL) | |
139 | return; | |
140 | tmr = q->timer; | |
141 | if (tmr == NULL) | |
142 | return; | |
143 | if (!tmr->running) | |
144 | return; | |
145 | ||
146 | resolution *= ticks; | |
147 | if (tmr->skew != tmr->skew_base) { | |
148 | /* FIXME: assuming skew_base = 0x10000 */ | |
149 | resolution = (resolution >> 16) * tmr->skew + | |
150 | (((resolution & 0xffff) * tmr->skew) >> 16); | |
151 | } | |
152 | ||
153 | spin_lock_irqsave(&tmr->lock, flags); | |
154 | ||
155 | /* update timer */ | |
156 | snd_seq_inc_time_nsec(&tmr->cur_time, resolution); | |
157 | ||
158 | /* calculate current tick */ | |
159 | snd_seq_timer_update_tick(&tmr->tick, resolution); | |
160 | ||
161 | /* register actual time of this timer update */ | |
162 | do_gettimeofday(&tmr->last_update); | |
163 | ||
164 | spin_unlock_irqrestore(&tmr->lock, flags); | |
165 | ||
166 | /* check queues and dispatch events */ | |
167 | snd_seq_check_queue(q, 1, 0); | |
168 | } | |
169 | ||
170 | /* set current tempo */ | |
c7e0b5bf | 171 | int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo) |
1da177e4 LT |
172 | { |
173 | unsigned long flags; | |
174 | ||
7eaa943c TI |
175 | if (snd_BUG_ON(!tmr)) |
176 | return -EINVAL; | |
1da177e4 LT |
177 | if (tempo <= 0) |
178 | return -EINVAL; | |
179 | spin_lock_irqsave(&tmr->lock, flags); | |
180 | if ((unsigned int)tempo != tmr->tempo) { | |
181 | tmr->tempo = tempo; | |
a32f6674 | 182 | snd_seq_timer_set_tick_resolution(tmr); |
1da177e4 LT |
183 | } |
184 | spin_unlock_irqrestore(&tmr->lock, flags); | |
185 | return 0; | |
186 | } | |
187 | ||
188 | /* set current ppq */ | |
c7e0b5bf | 189 | int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq) |
1da177e4 LT |
190 | { |
191 | unsigned long flags; | |
192 | ||
7eaa943c TI |
193 | if (snd_BUG_ON(!tmr)) |
194 | return -EINVAL; | |
1da177e4 LT |
195 | if (ppq <= 0) |
196 | return -EINVAL; | |
197 | spin_lock_irqsave(&tmr->lock, flags); | |
198 | if (tmr->running && (ppq != tmr->ppq)) { | |
199 | /* refuse to change ppq on running timers */ | |
200 | /* because it will upset the song position (ticks) */ | |
201 | spin_unlock_irqrestore(&tmr->lock, flags); | |
202 | snd_printd("seq: cannot change ppq of a running timer\n"); | |
203 | return -EBUSY; | |
204 | } | |
205 | ||
206 | tmr->ppq = ppq; | |
a32f6674 | 207 | snd_seq_timer_set_tick_resolution(tmr); |
1da177e4 LT |
208 | spin_unlock_irqrestore(&tmr->lock, flags); |
209 | return 0; | |
210 | } | |
211 | ||
212 | /* set current tick position */ | |
c7e0b5bf TI |
213 | int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, |
214 | snd_seq_tick_time_t position) | |
1da177e4 LT |
215 | { |
216 | unsigned long flags; | |
217 | ||
7eaa943c TI |
218 | if (snd_BUG_ON(!tmr)) |
219 | return -EINVAL; | |
1da177e4 LT |
220 | |
221 | spin_lock_irqsave(&tmr->lock, flags); | |
222 | tmr->tick.cur_tick = position; | |
223 | tmr->tick.fraction = 0; | |
224 | spin_unlock_irqrestore(&tmr->lock, flags); | |
225 | return 0; | |
226 | } | |
227 | ||
228 | /* set current real-time position */ | |
c7e0b5bf TI |
229 | int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, |
230 | snd_seq_real_time_t position) | |
1da177e4 LT |
231 | { |
232 | unsigned long flags; | |
233 | ||
7eaa943c TI |
234 | if (snd_BUG_ON(!tmr)) |
235 | return -EINVAL; | |
1da177e4 LT |
236 | |
237 | snd_seq_sanity_real_time(&position); | |
238 | spin_lock_irqsave(&tmr->lock, flags); | |
239 | tmr->cur_time = position; | |
240 | spin_unlock_irqrestore(&tmr->lock, flags); | |
241 | return 0; | |
242 | } | |
243 | ||
244 | /* set timer skew */ | |
c7e0b5bf TI |
245 | int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, |
246 | unsigned int base) | |
1da177e4 LT |
247 | { |
248 | unsigned long flags; | |
249 | ||
7eaa943c TI |
250 | if (snd_BUG_ON(!tmr)) |
251 | return -EINVAL; | |
1da177e4 LT |
252 | |
253 | /* FIXME */ | |
254 | if (base != SKEW_BASE) { | |
255 | snd_printd("invalid skew base 0x%x\n", base); | |
256 | return -EINVAL; | |
257 | } | |
258 | spin_lock_irqsave(&tmr->lock, flags); | |
259 | tmr->skew = skew; | |
260 | spin_unlock_irqrestore(&tmr->lock, flags); | |
261 | return 0; | |
262 | } | |
263 | ||
c7e0b5bf | 264 | int snd_seq_timer_open(struct snd_seq_queue *q) |
1da177e4 | 265 | { |
c7e0b5bf TI |
266 | struct snd_timer_instance *t; |
267 | struct snd_seq_timer *tmr; | |
1da177e4 LT |
268 | char str[32]; |
269 | int err; | |
270 | ||
271 | tmr = q->timer; | |
7eaa943c TI |
272 | if (snd_BUG_ON(!tmr)) |
273 | return -EINVAL; | |
1da177e4 LT |
274 | if (tmr->timeri) |
275 | return -EBUSY; | |
276 | sprintf(str, "sequencer queue %i", q->queue); | |
277 | if (tmr->type != SNDRV_SEQ_TIMER_ALSA) /* standard ALSA timer */ | |
278 | return -EINVAL; | |
279 | if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) | |
280 | tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER; | |
281 | err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue); | |
282 | if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) { | |
283 | if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL || | |
284 | tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) { | |
c7e0b5bf | 285 | struct snd_timer_id tid; |
1da177e4 LT |
286 | memset(&tid, 0, sizeof(tid)); |
287 | tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; | |
288 | tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER; | |
289 | tid.card = -1; | |
290 | tid.device = SNDRV_TIMER_GLOBAL_SYSTEM; | |
291 | err = snd_timer_open(&t, str, &tid, q->queue); | |
292 | } | |
293 | if (err < 0) { | |
294 | snd_printk(KERN_ERR "seq fatal error: cannot create timer (%i)\n", err); | |
295 | return err; | |
296 | } | |
297 | } | |
298 | t->callback = snd_seq_timer_interrupt; | |
299 | t->callback_data = q; | |
300 | t->flags |= SNDRV_TIMER_IFLG_AUTO; | |
301 | tmr->timeri = t; | |
302 | return 0; | |
303 | } | |
304 | ||
c7e0b5bf | 305 | int snd_seq_timer_close(struct snd_seq_queue *q) |
1da177e4 | 306 | { |
c7e0b5bf | 307 | struct snd_seq_timer *tmr; |
1da177e4 LT |
308 | |
309 | tmr = q->timer; | |
7eaa943c TI |
310 | if (snd_BUG_ON(!tmr)) |
311 | return -EINVAL; | |
1da177e4 LT |
312 | if (tmr->timeri) { |
313 | snd_timer_stop(tmr->timeri); | |
314 | snd_timer_close(tmr->timeri); | |
315 | tmr->timeri = NULL; | |
316 | } | |
317 | return 0; | |
318 | } | |
319 | ||
c7e0b5bf | 320 | int snd_seq_timer_stop(struct snd_seq_timer * tmr) |
1da177e4 LT |
321 | { |
322 | if (! tmr->timeri) | |
323 | return -EINVAL; | |
324 | if (!tmr->running) | |
325 | return 0; | |
326 | tmr->running = 0; | |
327 | snd_timer_pause(tmr->timeri); | |
328 | return 0; | |
329 | } | |
330 | ||
c7e0b5bf | 331 | static int initialize_timer(struct snd_seq_timer *tmr) |
1da177e4 | 332 | { |
c7e0b5bf | 333 | struct snd_timer *t; |
87ef7779 CL |
334 | unsigned long freq; |
335 | ||
1da177e4 | 336 | t = tmr->timeri->timer; |
7eaa943c TI |
337 | if (snd_BUG_ON(!t)) |
338 | return -EINVAL; | |
1da177e4 | 339 | |
87ef7779 CL |
340 | freq = tmr->preferred_resolution; |
341 | if (!freq) | |
342 | freq = DEFAULT_FREQUENCY; | |
343 | else if (freq < MIN_FREQUENCY) | |
344 | freq = MIN_FREQUENCY; | |
345 | else if (freq > MAX_FREQUENCY) | |
346 | freq = MAX_FREQUENCY; | |
347 | ||
1da177e4 | 348 | tmr->ticks = 1; |
87ef7779 | 349 | if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { |
1da177e4 LT |
350 | unsigned long r = t->hw.resolution; |
351 | if (! r && t->hw.c_resolution) | |
352 | r = t->hw.c_resolution(t); | |
353 | if (r) { | |
87ef7779 | 354 | tmr->ticks = (unsigned int)(1000000000uL / (r * freq)); |
1da177e4 LT |
355 | if (! tmr->ticks) |
356 | tmr->ticks = 1; | |
357 | } | |
358 | } | |
359 | tmr->initialized = 1; | |
360 | return 0; | |
361 | } | |
362 | ||
c7e0b5bf | 363 | int snd_seq_timer_start(struct snd_seq_timer * tmr) |
1da177e4 LT |
364 | { |
365 | if (! tmr->timeri) | |
366 | return -EINVAL; | |
367 | if (tmr->running) | |
368 | snd_seq_timer_stop(tmr); | |
369 | snd_seq_timer_reset(tmr); | |
370 | if (initialize_timer(tmr) < 0) | |
371 | return -EINVAL; | |
372 | snd_timer_start(tmr->timeri, tmr->ticks); | |
373 | tmr->running = 1; | |
374 | do_gettimeofday(&tmr->last_update); | |
375 | return 0; | |
376 | } | |
377 | ||
c7e0b5bf | 378 | int snd_seq_timer_continue(struct snd_seq_timer * tmr) |
1da177e4 LT |
379 | { |
380 | if (! tmr->timeri) | |
381 | return -EINVAL; | |
382 | if (tmr->running) | |
383 | return -EBUSY; | |
384 | if (! tmr->initialized) { | |
385 | snd_seq_timer_reset(tmr); | |
386 | if (initialize_timer(tmr) < 0) | |
387 | return -EINVAL; | |
388 | } | |
389 | snd_timer_start(tmr->timeri, tmr->ticks); | |
390 | tmr->running = 1; | |
391 | do_gettimeofday(&tmr->last_update); | |
392 | return 0; | |
393 | } | |
394 | ||
395 | /* return current 'real' time. use timeofday() to get better granularity. */ | |
c7e0b5bf | 396 | snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) |
1da177e4 LT |
397 | { |
398 | snd_seq_real_time_t cur_time; | |
399 | ||
400 | cur_time = tmr->cur_time; | |
401 | if (tmr->running) { | |
402 | struct timeval tm; | |
403 | int usec; | |
404 | do_gettimeofday(&tm); | |
405 | usec = (int)(tm.tv_usec - tmr->last_update.tv_usec); | |
406 | if (usec < 0) { | |
407 | cur_time.tv_nsec += (1000000 + usec) * 1000; | |
408 | cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1; | |
409 | } else { | |
410 | cur_time.tv_nsec += usec * 1000; | |
411 | cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec; | |
412 | } | |
413 | snd_seq_sanity_real_time(&cur_time); | |
414 | } | |
415 | ||
416 | return cur_time; | |
417 | } | |
418 | ||
419 | /* TODO: use interpolation on tick queue (will only be useful for very | |
420 | high PPQ values) */ | |
c7e0b5bf | 421 | snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr) |
1da177e4 LT |
422 | { |
423 | return tmr->tick.cur_tick; | |
424 | } | |
425 | ||
426 | ||
04f141a8 | 427 | #ifdef CONFIG_PROC_FS |
1da177e4 | 428 | /* exported to seq_info.c */ |
c7e0b5bf TI |
429 | void snd_seq_info_timer_read(struct snd_info_entry *entry, |
430 | struct snd_info_buffer *buffer) | |
1da177e4 LT |
431 | { |
432 | int idx; | |
c7e0b5bf TI |
433 | struct snd_seq_queue *q; |
434 | struct snd_seq_timer *tmr; | |
435 | struct snd_timer_instance *ti; | |
1da177e4 LT |
436 | unsigned long resolution; |
437 | ||
438 | for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) { | |
439 | q = queueptr(idx); | |
440 | if (q == NULL) | |
441 | continue; | |
442 | if ((tmr = q->timer) == NULL || | |
443 | (ti = tmr->timeri) == NULL) { | |
444 | queuefree(q); | |
445 | continue; | |
446 | } | |
447 | snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name); | |
448 | resolution = snd_timer_resolution(ti) * tmr->ticks; | |
449 | snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000); | |
450 | snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base); | |
451 | queuefree(q); | |
452 | } | |
453 | } | |
04f141a8 TI |
454 | #endif /* CONFIG_PROC_FS */ |
455 |