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