cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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