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