seq_timer.c 10.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 *   ALSA sequencer Timer
 *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
4
 *                              Jaroslav Kysela <perex@perex.cz>
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include <sound/core.h>
#include <linux/slab.h>
#include "seq_timer.h"
#include "seq_queue.h"
#include "seq_info.h"

29 30 31 32 33
/* allowed sequencer timer frequencies, in Hz */
#define MIN_FREQUENCY		10
#define MAX_FREQUENCY		6250
#define DEFAULT_FREQUENCY	1000

L
Linus Torvalds 已提交
34 35
#define SKEW_BASE	0x10000	/* 16bit shift */

36
static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr)
L
Linus Torvalds 已提交
37
{
38 39
	if (tmr->tempo < 1000000)
		tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq;
L
Linus Torvalds 已提交
40 41 42
	else {
		/* might overflow.. */
		unsigned int s;
43 44 45 46
		s = tmr->tempo % tmr->ppq;
		s = (s * 1000) / tmr->ppq;
		tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000;
		tmr->tick.resolution += s;
L
Linus Torvalds 已提交
47
	}
48 49 50
	if (tmr->tick.resolution <= 0)
		tmr->tick.resolution = 1;
	snd_seq_timer_update_tick(&tmr->tick, 0);
L
Linus Torvalds 已提交
51 52 53
}

/* create new timer (constructor) */
54
struct snd_seq_timer *snd_seq_timer_new(void)
L
Linus Torvalds 已提交
55
{
56
	struct snd_seq_timer *tmr;
L
Linus Torvalds 已提交
57
	
58
	tmr = kzalloc(sizeof(*tmr), GFP_KERNEL);
59
	if (!tmr)
L
Linus Torvalds 已提交
60 61 62 63 64 65 66 67 68 69 70 71 72
		return NULL;
	spin_lock_init(&tmr->lock);

	/* reset setup to defaults */
	snd_seq_timer_defaults(tmr);
	
	/* reset time */
	snd_seq_timer_reset(tmr);
	
	return tmr;
}

/* delete timer (destructor) */
73
void snd_seq_timer_delete(struct snd_seq_timer **tmr)
L
Linus Torvalds 已提交
74
{
75
	struct snd_seq_timer *t = *tmr;
L
Linus Torvalds 已提交
76 77 78
	*tmr = NULL;

	if (t == NULL) {
79
		pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n");
L
Linus Torvalds 已提交
80 81 82 83 84 85 86 87 88 89 90
		return;
	}
	t->running = 0;

	/* reset time */
	snd_seq_timer_stop(t);
	snd_seq_timer_reset(t);

	kfree(t);
}

91
void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
L
Linus Torvalds 已提交
92 93 94 95
{
	/* setup defaults */
	tmr->ppq = 96;		/* 96 PPQ */
	tmr->tempo = 500000;	/* 120 BPM */
96
	snd_seq_timer_set_tick_resolution(tmr);
L
Linus Torvalds 已提交
97 98 99 100 101 102 103 104 105 106 107 108 109
	tmr->running = 0;

	tmr->type = SNDRV_SEQ_TIMER_ALSA;
	tmr->alsa_id.dev_class = seq_default_timer_class;
	tmr->alsa_id.dev_sclass = seq_default_timer_sclass;
	tmr->alsa_id.card = seq_default_timer_card;
	tmr->alsa_id.device = seq_default_timer_device;
	tmr->alsa_id.subdevice = seq_default_timer_subdevice;
	tmr->preferred_resolution = seq_default_timer_resolution;

	tmr->skew = tmr->skew_base = SKEW_BASE;
}

110
void snd_seq_timer_reset(struct snd_seq_timer * tmr)
L
Linus Torvalds 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
{
	unsigned long flags;

	spin_lock_irqsave(&tmr->lock, flags);

	/* reset time & songposition */
	tmr->cur_time.tv_sec = 0;
	tmr->cur_time.tv_nsec = 0;

	tmr->tick.cur_tick = 0;
	tmr->tick.fraction = 0;

	spin_unlock_irqrestore(&tmr->lock, flags);
}


/* called by timer interrupt routine. the period time since previous invocation is passed */
128
static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
L
Linus Torvalds 已提交
129 130 131 132
				    unsigned long resolution,
				    unsigned long ticks)
{
	unsigned long flags;
133 134
	struct snd_seq_queue *q = timeri->callback_data;
	struct snd_seq_timer *tmr;
L
Linus Torvalds 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

	if (q == NULL)
		return;
	tmr = q->timer;
	if (tmr == NULL)
		return;
	if (!tmr->running)
		return;

	resolution *= ticks;
	if (tmr->skew != tmr->skew_base) {
		/* FIXME: assuming skew_base = 0x10000 */
		resolution = (resolution >> 16) * tmr->skew +
			(((resolution & 0xffff) * tmr->skew) >> 16);
	}

	spin_lock_irqsave(&tmr->lock, flags);

	/* update timer */
	snd_seq_inc_time_nsec(&tmr->cur_time, resolution);

	/* calculate current tick */
	snd_seq_timer_update_tick(&tmr->tick, resolution);

	/* register actual time of this timer update */
	do_gettimeofday(&tmr->last_update);

	spin_unlock_irqrestore(&tmr->lock, flags);

	/* check queues and dispatch events */
	snd_seq_check_queue(q, 1, 0);
}

/* set current tempo */
169
int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
L
Linus Torvalds 已提交
170 171 172
{
	unsigned long flags;

173 174
	if (snd_BUG_ON(!tmr))
		return -EINVAL;
L
Linus Torvalds 已提交
175 176 177 178 179
	if (tempo <= 0)
		return -EINVAL;
	spin_lock_irqsave(&tmr->lock, flags);
	if ((unsigned int)tempo != tmr->tempo) {
		tmr->tempo = tempo;
180
		snd_seq_timer_set_tick_resolution(tmr);
L
Linus Torvalds 已提交
181 182 183 184 185 186
	}
	spin_unlock_irqrestore(&tmr->lock, flags);
	return 0;
}

/* set current ppq */
187
int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq)
L
Linus Torvalds 已提交
188 189 190
{
	unsigned long flags;

191 192
	if (snd_BUG_ON(!tmr))
		return -EINVAL;
L
Linus Torvalds 已提交
193 194 195 196 197 198 199
	if (ppq <= 0)
		return -EINVAL;
	spin_lock_irqsave(&tmr->lock, flags);
	if (tmr->running && (ppq != tmr->ppq)) {
		/* refuse to change ppq on running timers */
		/* because it will upset the song position (ticks) */
		spin_unlock_irqrestore(&tmr->lock, flags);
200
		pr_debug("ALSA: seq: cannot change ppq of a running timer\n");
L
Linus Torvalds 已提交
201 202 203 204
		return -EBUSY;
	}

	tmr->ppq = ppq;
205
	snd_seq_timer_set_tick_resolution(tmr);
L
Linus Torvalds 已提交
206 207 208 209 210
	spin_unlock_irqrestore(&tmr->lock, flags);
	return 0;
}

/* set current tick position */
211 212
int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
				    snd_seq_tick_time_t position)
L
Linus Torvalds 已提交
213 214 215
{
	unsigned long flags;

216 217
	if (snd_BUG_ON(!tmr))
		return -EINVAL;
L
Linus Torvalds 已提交
218 219 220 221 222 223 224 225 226

	spin_lock_irqsave(&tmr->lock, flags);
	tmr->tick.cur_tick = position;
	tmr->tick.fraction = 0;
	spin_unlock_irqrestore(&tmr->lock, flags);
	return 0;
}

/* set current real-time position */
227 228
int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
				    snd_seq_real_time_t position)
L
Linus Torvalds 已提交
229 230 231
{
	unsigned long flags;

232 233
	if (snd_BUG_ON(!tmr))
		return -EINVAL;
L
Linus Torvalds 已提交
234 235 236 237 238 239 240 241 242

	snd_seq_sanity_real_time(&position);
	spin_lock_irqsave(&tmr->lock, flags);
	tmr->cur_time = position;
	spin_unlock_irqrestore(&tmr->lock, flags);
	return 0;
}

/* set timer skew */
243 244
int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
			   unsigned int base)
L
Linus Torvalds 已提交
245 246 247
{
	unsigned long flags;

248 249
	if (snd_BUG_ON(!tmr))
		return -EINVAL;
L
Linus Torvalds 已提交
250 251 252

	/* FIXME */
	if (base != SKEW_BASE) {
253
		pr_debug("ALSA: seq: invalid skew base 0x%x\n", base);
L
Linus Torvalds 已提交
254 255 256 257 258 259 260 261
		return -EINVAL;
	}
	spin_lock_irqsave(&tmr->lock, flags);
	tmr->skew = skew;
	spin_unlock_irqrestore(&tmr->lock, flags);
	return 0;
}

262
int snd_seq_timer_open(struct snd_seq_queue *q)
L
Linus Torvalds 已提交
263
{
264 265
	struct snd_timer_instance *t;
	struct snd_seq_timer *tmr;
L
Linus Torvalds 已提交
266 267 268 269
	char str[32];
	int err;

	tmr = q->timer;
270 271
	if (snd_BUG_ON(!tmr))
		return -EINVAL;
L
Linus Torvalds 已提交
272 273 274 275 276 277 278 279 280 281 282
	if (tmr->timeri)
		return -EBUSY;
	sprintf(str, "sequencer queue %i", q->queue);
	if (tmr->type != SNDRV_SEQ_TIMER_ALSA)	/* standard ALSA timer */
		return -EINVAL;
	if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
		tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
	err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue);
	if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
		if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
		    tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
283
			struct snd_timer_id tid;
L
Linus Torvalds 已提交
284 285 286 287 288 289 290
			memset(&tid, 0, sizeof(tid));
			tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
			tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
			tid.card = -1;
			tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
			err = snd_timer_open(&t, str, &tid, q->queue);
		}
291 292
	}
	if (err < 0) {
293
		pr_err("ALSA: seq fatal error: cannot create timer (%i)\n", err);
294
		return err;
L
Linus Torvalds 已提交
295 296 297 298 299 300 301 302
	}
	t->callback = snd_seq_timer_interrupt;
	t->callback_data = q;
	t->flags |= SNDRV_TIMER_IFLG_AUTO;
	tmr->timeri = t;
	return 0;
}

303
int snd_seq_timer_close(struct snd_seq_queue *q)
L
Linus Torvalds 已提交
304
{
305
	struct snd_seq_timer *tmr;
L
Linus Torvalds 已提交
306 307
	
	tmr = q->timer;
308 309
	if (snd_BUG_ON(!tmr))
		return -EINVAL;
L
Linus Torvalds 已提交
310 311 312 313 314 315 316 317
	if (tmr->timeri) {
		snd_timer_stop(tmr->timeri);
		snd_timer_close(tmr->timeri);
		tmr->timeri = NULL;
	}
	return 0;
}

318
int snd_seq_timer_stop(struct snd_seq_timer * tmr)
L
Linus Torvalds 已提交
319 320 321 322 323 324 325 326 327 328
{
	if (! tmr->timeri)
		return -EINVAL;
	if (!tmr->running)
		return 0;
	tmr->running = 0;
	snd_timer_pause(tmr->timeri);
	return 0;
}

329
static int initialize_timer(struct snd_seq_timer *tmr)
L
Linus Torvalds 已提交
330
{
331
	struct snd_timer *t;
332 333
	unsigned long freq;

L
Linus Torvalds 已提交
334
	t = tmr->timeri->timer;
335 336
	if (snd_BUG_ON(!t))
		return -EINVAL;
L
Linus Torvalds 已提交
337

338 339 340 341 342 343 344 345
	freq = tmr->preferred_resolution;
	if (!freq)
		freq = DEFAULT_FREQUENCY;
	else if (freq < MIN_FREQUENCY)
		freq = MIN_FREQUENCY;
	else if (freq > MAX_FREQUENCY)
		freq = MAX_FREQUENCY;

L
Linus Torvalds 已提交
346
	tmr->ticks = 1;
347
	if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
L
Linus Torvalds 已提交
348 349 350 351
		unsigned long r = t->hw.resolution;
		if (! r && t->hw.c_resolution)
			r = t->hw.c_resolution(t);
		if (r) {
352
			tmr->ticks = (unsigned int)(1000000000uL / (r * freq));
L
Linus Torvalds 已提交
353 354 355 356 357 358 359 360
			if (! tmr->ticks)
				tmr->ticks = 1;
		}
	}
	tmr->initialized = 1;
	return 0;
}

361
int snd_seq_timer_start(struct snd_seq_timer * tmr)
L
Linus Torvalds 已提交
362 363 364 365 366 367 368 369 370 371 372 373 374 375
{
	if (! tmr->timeri)
		return -EINVAL;
	if (tmr->running)
		snd_seq_timer_stop(tmr);
	snd_seq_timer_reset(tmr);
	if (initialize_timer(tmr) < 0)
		return -EINVAL;
	snd_timer_start(tmr->timeri, tmr->ticks);
	tmr->running = 1;
	do_gettimeofday(&tmr->last_update);
	return 0;
}

376
int snd_seq_timer_continue(struct snd_seq_timer * tmr)
L
Linus Torvalds 已提交
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
{
	if (! tmr->timeri)
		return -EINVAL;
	if (tmr->running)
		return -EBUSY;
	if (! tmr->initialized) {
		snd_seq_timer_reset(tmr);
		if (initialize_timer(tmr) < 0)
			return -EINVAL;
	}
	snd_timer_start(tmr->timeri, tmr->ticks);
	tmr->running = 1;
	do_gettimeofday(&tmr->last_update);
	return 0;
}

/* return current 'real' time. use timeofday() to get better granularity. */
394
snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
L
Linus Torvalds 已提交
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
{
	snd_seq_real_time_t cur_time;

	cur_time = tmr->cur_time;
	if (tmr->running) { 
		struct timeval tm;
		int usec;
		do_gettimeofday(&tm);
		usec = (int)(tm.tv_usec - tmr->last_update.tv_usec);
		if (usec < 0) {
			cur_time.tv_nsec += (1000000 + usec) * 1000;
			cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1;
		} else {
			cur_time.tv_nsec += usec * 1000;
			cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec;
		}
		snd_seq_sanity_real_time(&cur_time);
	}
                
	return cur_time;	
}

/* TODO: use interpolation on tick queue (will only be useful for very
 high PPQ values) */
419
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
L
Linus Torvalds 已提交
420 421 422 423 424
{
	return tmr->tick.cur_tick;
}


425
#ifdef CONFIG_SND_PROC_FS
L
Linus Torvalds 已提交
426
/* exported to seq_info.c */
427 428
void snd_seq_info_timer_read(struct snd_info_entry *entry,
			     struct snd_info_buffer *buffer)
L
Linus Torvalds 已提交
429 430
{
	int idx;
431 432 433
	struct snd_seq_queue *q;
	struct snd_seq_timer *tmr;
	struct snd_timer_instance *ti;
L
Linus Torvalds 已提交
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
	unsigned long resolution;
	
	for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) {
		q = queueptr(idx);
		if (q == NULL)
			continue;
		if ((tmr = q->timer) == NULL ||
		    (ti = tmr->timeri) == NULL) {
			queuefree(q);
			continue;
		}
		snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
		resolution = snd_timer_resolution(ti) * tmr->ticks;
		snd_iprintf(buffer, "  Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
		snd_iprintf(buffer, "  Skew : %u / %u\n", tmr->skew, tmr->skew_base);
		queuefree(q);
 	}
}
452
#endif /* CONFIG_SND_PROC_FS */
453