~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/core/seq/seq_timer.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php