xref: /linux/sound/core/seq/seq_queue.c (revision 54a8a2220c936a47840c9a3d74910c5a56fae2ed)
1 /*
2  *   ALSA sequencer Timing queue handling
3  *   Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18  *
19  * MAJOR CHANGES
20  *   Nov. 13, 1999	Takashi Iwai <iwai@ww.uni-erlangen.de>
21  *     - Queues are allocated dynamically via ioctl.
22  *     - When owner client is deleted, all owned queues are deleted, too.
23  *     - Owner of unlocked queue is kept unmodified even if it is
24  *	 manipulated by other clients.
25  *     - Owner field in SET_QUEUE_OWNER ioctl must be identical with the
26  *       caller client.  i.e. Changing owner to a third client is not
27  *       allowed.
28  *
29  *  Aug. 30, 2000	Takashi Iwai
30  *     - Queues are managed in static array again, but with better way.
31  *       The API itself is identical.
32  *     - The queue is locked when queue_t pinter is returned via
33  *       queueptr().  This pointer *MUST* be released afterward by
34  *       queuefree(ptr).
35  *     - Addition of experimental sync support.
36  */
37 
38 #include <sound/driver.h>
39 #include <linux/init.h>
40 #include <linux/slab.h>
41 #include <sound/core.h>
42 
43 #include "seq_memory.h"
44 #include "seq_queue.h"
45 #include "seq_clientmgr.h"
46 #include "seq_fifo.h"
47 #include "seq_timer.h"
48 #include "seq_info.h"
49 
50 /* list of allocated queues */
51 static queue_t *queue_list[SNDRV_SEQ_MAX_QUEUES];
52 static DEFINE_SPINLOCK(queue_list_lock);
53 /* number of queues allocated */
54 static int num_queues;
55 
56 int snd_seq_queue_get_cur_queues(void)
57 {
58 	return num_queues;
59 }
60 
61 /*----------------------------------------------------------------*/
62 
63 /* assign queue id and insert to list */
64 static int queue_list_add(queue_t *q)
65 {
66 	int i;
67 	unsigned long flags;
68 
69 	spin_lock_irqsave(&queue_list_lock, flags);
70 	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
71 		if (! queue_list[i]) {
72 			queue_list[i] = q;
73 			q->queue = i;
74 			num_queues++;
75 			spin_unlock_irqrestore(&queue_list_lock, flags);
76 			return i;
77 		}
78 	}
79 	spin_unlock_irqrestore(&queue_list_lock, flags);
80 	return -1;
81 }
82 
83 static queue_t *queue_list_remove(int id, int client)
84 {
85 	queue_t *q;
86 	unsigned long flags;
87 
88 	spin_lock_irqsave(&queue_list_lock, flags);
89 	q = queue_list[id];
90 	if (q) {
91 		spin_lock(&q->owner_lock);
92 		if (q->owner == client) {
93 			/* found */
94 			q->klocked = 1;
95 			spin_unlock(&q->owner_lock);
96 			queue_list[id] = NULL;
97 			num_queues--;
98 			spin_unlock_irqrestore(&queue_list_lock, flags);
99 			return q;
100 		}
101 		spin_unlock(&q->owner_lock);
102 	}
103 	spin_unlock_irqrestore(&queue_list_lock, flags);
104 	return NULL;
105 }
106 
107 /*----------------------------------------------------------------*/
108 
109 /* create new queue (constructor) */
110 static queue_t *queue_new(int owner, int locked)
111 {
112 	queue_t *q;
113 
114 	q = kzalloc(sizeof(*q), GFP_KERNEL);
115 	if (q == NULL) {
116 		snd_printd("malloc failed for snd_seq_queue_new()\n");
117 		return NULL;
118 	}
119 
120 	spin_lock_init(&q->owner_lock);
121 	spin_lock_init(&q->check_lock);
122 	init_MUTEX(&q->timer_mutex);
123 	snd_use_lock_init(&q->use_lock);
124 	q->queue = -1;
125 
126 	q->tickq = snd_seq_prioq_new();
127 	q->timeq = snd_seq_prioq_new();
128 	q->timer = snd_seq_timer_new();
129 	if (q->tickq == NULL || q->timeq == NULL || q->timer == NULL) {
130 		snd_seq_prioq_delete(&q->tickq);
131 		snd_seq_prioq_delete(&q->timeq);
132 		snd_seq_timer_delete(&q->timer);
133 		kfree(q);
134 		return NULL;
135 	}
136 
137 	q->owner = owner;
138 	q->locked = locked;
139 	q->klocked = 0;
140 
141 	return q;
142 }
143 
144 /* delete queue (destructor) */
145 static void queue_delete(queue_t *q)
146 {
147 	/* stop and release the timer */
148 	snd_seq_timer_stop(q->timer);
149 	snd_seq_timer_close(q);
150 	/* wait until access free */
151 	snd_use_lock_sync(&q->use_lock);
152 	/* release resources... */
153 	snd_seq_prioq_delete(&q->tickq);
154 	snd_seq_prioq_delete(&q->timeq);
155 	snd_seq_timer_delete(&q->timer);
156 
157 	kfree(q);
158 }
159 
160 
161 /*----------------------------------------------------------------*/
162 
163 /* setup queues */
164 int __init snd_seq_queues_init(void)
165 {
166 	/*
167 	memset(queue_list, 0, sizeof(queue_list));
168 	num_queues = 0;
169 	*/
170 	return 0;
171 }
172 
173 /* delete all existing queues */
174 void __exit snd_seq_queues_delete(void)
175 {
176 	int i;
177 
178 	/* clear list */
179 	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
180 		if (queue_list[i])
181 			queue_delete(queue_list[i]);
182 	}
183 }
184 
185 /* allocate a new queue -
186  * return queue index value or negative value for error
187  */
188 int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
189 {
190 	queue_t *q;
191 
192 	q = queue_new(client, locked);
193 	if (q == NULL)
194 		return -ENOMEM;
195 	q->info_flags = info_flags;
196 	if (queue_list_add(q) < 0) {
197 		queue_delete(q);
198 		return -ENOMEM;
199 	}
200 	snd_seq_queue_use(q->queue, client, 1); /* use this queue */
201 	return q->queue;
202 }
203 
204 /* delete a queue - queue must be owned by the client */
205 int snd_seq_queue_delete(int client, int queueid)
206 {
207 	queue_t *q;
208 
209 	if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
210 		return -EINVAL;
211 	q = queue_list_remove(queueid, client);
212 	if (q == NULL)
213 		return -EINVAL;
214 	queue_delete(q);
215 
216 	return 0;
217 }
218 
219 
220 /* return pointer to queue structure for specified id */
221 queue_t *queueptr(int queueid)
222 {
223 	queue_t *q;
224 	unsigned long flags;
225 
226 	if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
227 		return NULL;
228 	spin_lock_irqsave(&queue_list_lock, flags);
229 	q = queue_list[queueid];
230 	if (q)
231 		snd_use_lock_use(&q->use_lock);
232 	spin_unlock_irqrestore(&queue_list_lock, flags);
233 	return q;
234 }
235 
236 /* return the (first) queue matching with the specified name */
237 queue_t *snd_seq_queue_find_name(char *name)
238 {
239 	int i;
240 	queue_t *q;
241 
242 	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
243 		if ((q = queueptr(i)) != NULL) {
244 			if (strncmp(q->name, name, sizeof(q->name)) == 0)
245 				return q;
246 			queuefree(q);
247 		}
248 	}
249 	return NULL;
250 }
251 
252 
253 /* -------------------------------------------------------- */
254 
255 void snd_seq_check_queue(queue_t *q, int atomic, int hop)
256 {
257 	unsigned long flags;
258 	snd_seq_event_cell_t *cell;
259 
260 	if (q == NULL)
261 		return;
262 
263 	/* make this function non-reentrant */
264 	spin_lock_irqsave(&q->check_lock, flags);
265 	if (q->check_blocked) {
266 		q->check_again = 1;
267 		spin_unlock_irqrestore(&q->check_lock, flags);
268 		return;		/* other thread is already checking queues */
269 	}
270 	q->check_blocked = 1;
271 	spin_unlock_irqrestore(&q->check_lock, flags);
272 
273       __again:
274 	/* Process tick queue... */
275 	while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) {
276 		if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick, &cell->event.time.tick)) {
277 			cell = snd_seq_prioq_cell_out(q->tickq);
278 			if (cell)
279 				snd_seq_dispatch_event(cell, atomic, hop);
280 		} else {
281 			/* event remains in the queue */
282 			break;
283 		}
284 	}
285 
286 
287 	/* Process time queue... */
288 	while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) {
289 		if (snd_seq_compare_real_time(&q->timer->cur_time, &cell->event.time.time)) {
290 			cell = snd_seq_prioq_cell_out(q->timeq);
291 			if (cell)
292 				snd_seq_dispatch_event(cell, atomic, hop);
293 		} else {
294 			/* event remains in the queue */
295 			break;
296 		}
297 	}
298 
299 	/* free lock */
300 	spin_lock_irqsave(&q->check_lock, flags);
301 	if (q->check_again) {
302 		q->check_again = 0;
303 		spin_unlock_irqrestore(&q->check_lock, flags);
304 		goto __again;
305 	}
306 	q->check_blocked = 0;
307 	spin_unlock_irqrestore(&q->check_lock, flags);
308 }
309 
310 
311 /* enqueue a event to singe queue */
312 int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop)
313 {
314 	int dest, err;
315 	queue_t *q;
316 
317 	snd_assert(cell != NULL, return -EINVAL);
318 	dest = cell->event.queue;	/* destination queue */
319 	q = queueptr(dest);
320 	if (q == NULL)
321 		return -EINVAL;
322 	/* handle relative time stamps, convert them into absolute */
323 	if ((cell->event.flags & SNDRV_SEQ_TIME_MODE_MASK) == SNDRV_SEQ_TIME_MODE_REL) {
324 		switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
325 		case SNDRV_SEQ_TIME_STAMP_TICK:
326 			cell->event.time.tick += q->timer->tick.cur_tick;
327 			break;
328 
329 		case SNDRV_SEQ_TIME_STAMP_REAL:
330 			snd_seq_inc_real_time(&cell->event.time.time, &q->timer->cur_time);
331 			break;
332 		}
333 		cell->event.flags &= ~SNDRV_SEQ_TIME_MODE_MASK;
334 		cell->event.flags |= SNDRV_SEQ_TIME_MODE_ABS;
335 	}
336 	/* enqueue event in the real-time or midi queue */
337 	switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
338 	case SNDRV_SEQ_TIME_STAMP_TICK:
339 		err = snd_seq_prioq_cell_in(q->tickq, cell);
340 		break;
341 
342 	case SNDRV_SEQ_TIME_STAMP_REAL:
343 	default:
344 		err = snd_seq_prioq_cell_in(q->timeq, cell);
345 		break;
346 	}
347 
348 	if (err < 0) {
349 		queuefree(q); /* unlock */
350 		return err;
351 	}
352 
353 	/* trigger dispatching */
354 	snd_seq_check_queue(q, atomic, hop);
355 
356 	queuefree(q); /* unlock */
357 
358 	return 0;
359 }
360 
361 
362 /*----------------------------------------------------------------*/
363 
364 static inline int check_access(queue_t *q, int client)
365 {
366 	return (q->owner == client) || (!q->locked && !q->klocked);
367 }
368 
369 /* check if the client has permission to modify queue parameters.
370  * if it does, lock the queue
371  */
372 static int queue_access_lock(queue_t *q, int client)
373 {
374 	unsigned long flags;
375 	int access_ok;
376 
377 	spin_lock_irqsave(&q->owner_lock, flags);
378 	access_ok = check_access(q, client);
379 	if (access_ok)
380 		q->klocked = 1;
381 	spin_unlock_irqrestore(&q->owner_lock, flags);
382 	return access_ok;
383 }
384 
385 /* unlock the queue */
386 static inline void queue_access_unlock(queue_t *q)
387 {
388 	unsigned long flags;
389 
390 	spin_lock_irqsave(&q->owner_lock, flags);
391 	q->klocked = 0;
392 	spin_unlock_irqrestore(&q->owner_lock, flags);
393 }
394 
395 /* exported - only checking permission */
396 int snd_seq_queue_check_access(int queueid, int client)
397 {
398 	queue_t *q = queueptr(queueid);
399 	int access_ok;
400 	unsigned long flags;
401 
402 	if (! q)
403 		return 0;
404 	spin_lock_irqsave(&q->owner_lock, flags);
405 	access_ok = check_access(q, client);
406 	spin_unlock_irqrestore(&q->owner_lock, flags);
407 	queuefree(q);
408 	return access_ok;
409 }
410 
411 /*----------------------------------------------------------------*/
412 
413 /*
414  * change queue's owner and permission
415  */
416 int snd_seq_queue_set_owner(int queueid, int client, int locked)
417 {
418 	queue_t *q = queueptr(queueid);
419 
420 	if (q == NULL)
421 		return -EINVAL;
422 
423 	if (! queue_access_lock(q, client)) {
424 		queuefree(q);
425 		return -EPERM;
426 	}
427 
428 	q->locked = locked ? 1 : 0;
429 	q->owner = client;
430 	queue_access_unlock(q);
431 	queuefree(q);
432 
433 	return 0;
434 }
435 
436 
437 /*----------------------------------------------------------------*/
438 
439 /* open timer -
440  * q->use mutex should be down before calling this function to avoid
441  * confliction with snd_seq_queue_use()
442  */
443 int snd_seq_queue_timer_open(int queueid)
444 {
445 	int result = 0;
446 	queue_t *queue;
447 	seq_timer_t *tmr;
448 
449 	queue = queueptr(queueid);
450 	if (queue == NULL)
451 		return -EINVAL;
452 	tmr = queue->timer;
453 	if ((result = snd_seq_timer_open(queue)) < 0) {
454 		snd_seq_timer_defaults(tmr);
455 		result = snd_seq_timer_open(queue);
456 	}
457 	queuefree(queue);
458 	return result;
459 }
460 
461 /* close timer -
462  * q->use mutex should be down before calling this function
463  */
464 int snd_seq_queue_timer_close(int queueid)
465 {
466 	queue_t *queue;
467 	seq_timer_t *tmr;
468 	int result = 0;
469 
470 	queue = queueptr(queueid);
471 	if (queue == NULL)
472 		return -EINVAL;
473 	tmr = queue->timer;
474 	snd_seq_timer_close(queue);
475 	queuefree(queue);
476 	return result;
477 }
478 
479 /* change queue tempo and ppq */
480 int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info)
481 {
482 	queue_t *q = queueptr(queueid);
483 	int result;
484 
485 	if (q == NULL)
486 		return -EINVAL;
487 	if (! queue_access_lock(q, client)) {
488 		queuefree(q);
489 		return -EPERM;
490 	}
491 
492 	result = snd_seq_timer_set_tempo(q->timer, info->tempo);
493 	if (result >= 0)
494 		result = snd_seq_timer_set_ppq(q->timer, info->ppq);
495 	if (result >= 0 && info->skew_base > 0)
496 		result = snd_seq_timer_set_skew(q->timer, info->skew_value, info->skew_base);
497 	queue_access_unlock(q);
498 	queuefree(q);
499 	return result;
500 }
501 
502 
503 /* use or unuse this queue -
504  * if it is the first client, starts the timer.
505  * if it is not longer used by any clients, stop the timer.
506  */
507 int snd_seq_queue_use(int queueid, int client, int use)
508 {
509 	queue_t *queue;
510 
511 	queue = queueptr(queueid);
512 	if (queue == NULL)
513 		return -EINVAL;
514 	down(&queue->timer_mutex);
515 	if (use) {
516 		if (!test_and_set_bit(client, queue->clients_bitmap))
517 			queue->clients++;
518 	} else {
519 		if (test_and_clear_bit(client, queue->clients_bitmap))
520 			queue->clients--;
521 	}
522 	if (queue->clients) {
523 		if (use && queue->clients == 1)
524 			snd_seq_timer_defaults(queue->timer);
525 		snd_seq_timer_open(queue);
526 	} else {
527 		snd_seq_timer_close(queue);
528 	}
529 	up(&queue->timer_mutex);
530 	queuefree(queue);
531 	return 0;
532 }
533 
534 /*
535  * check if queue is used by the client
536  * return negative value if the queue is invalid.
537  * return 0 if not used, 1 if used.
538  */
539 int snd_seq_queue_is_used(int queueid, int client)
540 {
541 	queue_t *q;
542 	int result;
543 
544 	q = queueptr(queueid);
545 	if (q == NULL)
546 		return -EINVAL; /* invalid queue */
547 	result = test_bit(client, q->clients_bitmap) ? 1 : 0;
548 	queuefree(q);
549 	return result;
550 }
551 
552 
553 /*----------------------------------------------------------------*/
554 
555 /* notification that client has left the system -
556  * stop the timer on all queues owned by this client
557  */
558 void snd_seq_queue_client_termination(int client)
559 {
560 	unsigned long flags;
561 	int i;
562 	queue_t *q;
563 
564 	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
565 		if ((q = queueptr(i)) == NULL)
566 			continue;
567 		spin_lock_irqsave(&q->owner_lock, flags);
568 		if (q->owner == client)
569 			q->klocked = 1;
570 		spin_unlock_irqrestore(&q->owner_lock, flags);
571 		if (q->owner == client) {
572 			if (q->timer->running)
573 				snd_seq_timer_stop(q->timer);
574 			snd_seq_timer_reset(q->timer);
575 		}
576 		queuefree(q);
577 	}
578 }
579 
580 /* final stage notification -
581  * remove cells for no longer exist client (for non-owned queue)
582  * or delete this queue (for owned queue)
583  */
584 void snd_seq_queue_client_leave(int client)
585 {
586 	int i;
587 	queue_t *q;
588 
589 	/* delete own queues from queue list */
590 	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
591 		if ((q = queue_list_remove(i, client)) != NULL)
592 			queue_delete(q);
593 	}
594 
595 	/* remove cells from existing queues -
596 	 * they are not owned by this client
597 	 */
598 	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
599 		if ((q = queueptr(i)) == NULL)
600 			continue;
601 		if (test_bit(client, q->clients_bitmap)) {
602 			snd_seq_prioq_leave(q->tickq, client, 0);
603 			snd_seq_prioq_leave(q->timeq, client, 0);
604 			snd_seq_queue_use(q->queue, client, 0);
605 		}
606 		queuefree(q);
607 	}
608 }
609 
610 
611 
612 /*----------------------------------------------------------------*/
613 
614 /* remove cells from all queues */
615 void snd_seq_queue_client_leave_cells(int client)
616 {
617 	int i;
618 	queue_t *q;
619 
620 	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
621 		if ((q = queueptr(i)) == NULL)
622 			continue;
623 		snd_seq_prioq_leave(q->tickq, client, 0);
624 		snd_seq_prioq_leave(q->timeq, client, 0);
625 		queuefree(q);
626 	}
627 }
628 
629 /* remove cells based on flush criteria */
630 void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info)
631 {
632 	int i;
633 	queue_t *q;
634 
635 	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
636 		if ((q = queueptr(i)) == NULL)
637 			continue;
638 		if (test_bit(client, q->clients_bitmap) &&
639 		    (! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) ||
640 		     q->queue == info->queue)) {
641 			snd_seq_prioq_remove_events(q->tickq, client, info);
642 			snd_seq_prioq_remove_events(q->timeq, client, info);
643 		}
644 		queuefree(q);
645 	}
646 }
647 
648 /*----------------------------------------------------------------*/
649 
650 /*
651  * send events to all subscribed ports
652  */
653 static void queue_broadcast_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop)
654 {
655 	snd_seq_event_t sev;
656 
657 	sev = *ev;
658 
659 	sev.flags = SNDRV_SEQ_TIME_STAMP_TICK|SNDRV_SEQ_TIME_MODE_ABS;
660 	sev.time.tick = q->timer->tick.cur_tick;
661 	sev.queue = q->queue;
662 	sev.data.queue.queue = q->queue;
663 
664 	/* broadcast events from Timer port */
665 	sev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
666 	sev.source.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
667 	sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
668 	snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop);
669 }
670 
671 /*
672  * process a received queue-control event.
673  * this function is exported for seq_sync.c.
674  */
675 static void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev,
676 					int atomic, int hop)
677 {
678 	switch (ev->type) {
679 	case SNDRV_SEQ_EVENT_START:
680 		snd_seq_prioq_leave(q->tickq, ev->source.client, 1);
681 		snd_seq_prioq_leave(q->timeq, ev->source.client, 1);
682 		if (! snd_seq_timer_start(q->timer))
683 			queue_broadcast_event(q, ev, atomic, hop);
684 		break;
685 
686 	case SNDRV_SEQ_EVENT_CONTINUE:
687 		if (! snd_seq_timer_continue(q->timer))
688 			queue_broadcast_event(q, ev, atomic, hop);
689 		break;
690 
691 	case SNDRV_SEQ_EVENT_STOP:
692 		snd_seq_timer_stop(q->timer);
693 		queue_broadcast_event(q, ev, atomic, hop);
694 		break;
695 
696 	case SNDRV_SEQ_EVENT_TEMPO:
697 		snd_seq_timer_set_tempo(q->timer, ev->data.queue.param.value);
698 		queue_broadcast_event(q, ev, atomic, hop);
699 		break;
700 
701 	case SNDRV_SEQ_EVENT_SETPOS_TICK:
702 		if (snd_seq_timer_set_position_tick(q->timer, ev->data.queue.param.time.tick) == 0) {
703 			queue_broadcast_event(q, ev, atomic, hop);
704 		}
705 		break;
706 
707 	case SNDRV_SEQ_EVENT_SETPOS_TIME:
708 		if (snd_seq_timer_set_position_time(q->timer, ev->data.queue.param.time.time) == 0) {
709 			queue_broadcast_event(q, ev, atomic, hop);
710 		}
711 		break;
712 	case SNDRV_SEQ_EVENT_QUEUE_SKEW:
713 		if (snd_seq_timer_set_skew(q->timer,
714 					   ev->data.queue.param.skew.value,
715 					   ev->data.queue.param.skew.base) == 0) {
716 			queue_broadcast_event(q, ev, atomic, hop);
717 		}
718 		break;
719 	}
720 }
721 
722 
723 /*
724  * Queue control via timer control port:
725  * this function is exported as a callback of timer port.
726  */
727 int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop)
728 {
729 	queue_t *q;
730 
731 	snd_assert(ev != NULL, return -EINVAL);
732 	q = queueptr(ev->data.queue.queue);
733 
734 	if (q == NULL)
735 		return -EINVAL;
736 
737 	if (! queue_access_lock(q, ev->source.client)) {
738 		queuefree(q);
739 		return -EPERM;
740 	}
741 
742 	snd_seq_queue_process_event(q, ev, atomic, hop);
743 
744 	queue_access_unlock(q);
745 	queuefree(q);
746 	return 0;
747 }
748 
749 
750 /*----------------------------------------------------------------*/
751 
752 /* exported to seq_info.c */
753 void snd_seq_info_queues_read(snd_info_entry_t *entry,
754 			      snd_info_buffer_t * buffer)
755 {
756 	int i, bpm;
757 	queue_t *q;
758 	seq_timer_t *tmr;
759 
760 	for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
761 		if ((q = queueptr(i)) == NULL)
762 			continue;
763 
764 		tmr = q->timer;
765 		if (tmr->tempo)
766 			bpm = 60000000 / tmr->tempo;
767 		else
768 			bpm = 0;
769 
770 		snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
771 		snd_iprintf(buffer, "owned by client    : %d\n", q->owner);
772 		snd_iprintf(buffer, "lock status        : %s\n", q->locked ? "Locked" : "Free");
773 		snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
774 		snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
775 		snd_iprintf(buffer, "timer state        : %s\n", tmr->running ? "Running" : "Stopped");
776 		snd_iprintf(buffer, "timer PPQ          : %d\n", tmr->ppq);
777 		snd_iprintf(buffer, "current tempo      : %d\n", tmr->tempo);
778 		snd_iprintf(buffer, "current BPM        : %d\n", bpm);
779 		snd_iprintf(buffer, "current time       : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);
780 		snd_iprintf(buffer, "current tick       : %d\n", tmr->tick.cur_tick);
781 		snd_iprintf(buffer, "\n");
782 		queuefree(q);
783 	}
784 }
785