123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- /*
- * ALSA sequencer Priority Queue
- * Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * 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 <linux/time.h>
- #include <linux/slab.h>
- #include <sound/core.h>
- #include "seq_timer.h"
- #include "seq_prioq.h"
- /* Implementation is a simple linked list for now...
- This priority queue orders the events on timestamp. For events with an
- equeal timestamp the queue behaves as a FIFO.
- *
- * +-------+
- * Head --> | first |
- * +-------+
- * |next
- * +-----v-+
- * | |
- * +-------+
- * |
- * +-----v-+
- * | |
- * +-------+
- * |
- * +-----v-+
- * Tail --> | last |
- * +-------+
- *
- */
- /* create new prioq (constructor) */
- struct snd_seq_prioq *snd_seq_prioq_new(void)
- {
- struct snd_seq_prioq *f;
- f = kzalloc(sizeof(*f), GFP_KERNEL);
- if (!f)
- return NULL;
-
- spin_lock_init(&f->lock);
- f->head = NULL;
- f->tail = NULL;
- f->cells = 0;
-
- return f;
- }
- /* delete prioq (destructor) */
- void snd_seq_prioq_delete(struct snd_seq_prioq **fifo)
- {
- struct snd_seq_prioq *f = *fifo;
- *fifo = NULL;
- if (f == NULL) {
- pr_debug("ALSA: seq: snd_seq_prioq_delete() called with NULL prioq\n");
- return;
- }
- /* release resources...*/
- /*....................*/
-
- if (f->cells > 0) {
- /* drain prioQ */
- while (f->cells > 0)
- snd_seq_cell_free(snd_seq_prioq_cell_out(f, NULL));
- }
-
- kfree(f);
- }
- /* compare timestamp between events */
- /* return 1 if a >= b; 0 */
- static inline int compare_timestamp(struct snd_seq_event *a,
- struct snd_seq_event *b)
- {
- if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
- /* compare ticks */
- return (snd_seq_compare_tick_time(&a->time.tick, &b->time.tick));
- } else {
- /* compare real time */
- return (snd_seq_compare_real_time(&a->time.time, &b->time.time));
- }
- }
- /* compare timestamp between events */
- /* return negative if a < b;
- * zero if a = b;
- * positive if a > b;
- */
- static inline int compare_timestamp_rel(struct snd_seq_event *a,
- struct snd_seq_event *b)
- {
- if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
- /* compare ticks */
- if (a->time.tick > b->time.tick)
- return 1;
- else if (a->time.tick == b->time.tick)
- return 0;
- else
- return -1;
- } else {
- /* compare real time */
- if (a->time.time.tv_sec > b->time.time.tv_sec)
- return 1;
- else if (a->time.time.tv_sec == b->time.time.tv_sec) {
- if (a->time.time.tv_nsec > b->time.time.tv_nsec)
- return 1;
- else if (a->time.time.tv_nsec == b->time.time.tv_nsec)
- return 0;
- else
- return -1;
- } else
- return -1;
- }
- }
- /* enqueue cell to prioq */
- int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
- struct snd_seq_event_cell * cell)
- {
- struct snd_seq_event_cell *cur, *prev;
- unsigned long flags;
- int count;
- int prior;
- if (snd_BUG_ON(!f || !cell))
- return -EINVAL;
-
- /* check flags */
- prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
- spin_lock_irqsave(&f->lock, flags);
- /* check if this element needs to inserted at the end (ie. ordered
- data is inserted) This will be very likeley if a sequencer
- application or midi file player is feeding us (sequential) data */
- if (f->tail && !prior) {
- if (compare_timestamp(&cell->event, &f->tail->event)) {
- /* add new cell to tail of the fifo */
- f->tail->next = cell;
- f->tail = cell;
- cell->next = NULL;
- f->cells++;
- spin_unlock_irqrestore(&f->lock, flags);
- return 0;
- }
- }
- /* traverse list of elements to find the place where the new cell is
- to be inserted... Note that this is a order n process ! */
- prev = NULL; /* previous cell */
- cur = f->head; /* cursor */
- count = 10000; /* FIXME: enough big, isn't it? */
- while (cur != NULL) {
- /* compare timestamps */
- int rel = compare_timestamp_rel(&cell->event, &cur->event);
- if (rel < 0)
- /* new cell has earlier schedule time, */
- break;
- else if (rel == 0 && prior)
- /* equal schedule time and prior to others */
- break;
- /* new cell has equal or larger schedule time, */
- /* move cursor to next cell */
- prev = cur;
- cur = cur->next;
- if (! --count) {
- spin_unlock_irqrestore(&f->lock, flags);
- pr_err("ALSA: seq: cannot find a pointer.. infinite loop?\n");
- return -EINVAL;
- }
- }
- /* insert it before cursor */
- if (prev != NULL)
- prev->next = cell;
- cell->next = cur;
- if (f->head == cur) /* this is the first cell, set head to it */
- f->head = cell;
- if (cur == NULL) /* reached end of the list */
- f->tail = cell;
- f->cells++;
- spin_unlock_irqrestore(&f->lock, flags);
- return 0;
- }
- /* return 1 if the current time >= event timestamp */
- static int event_is_ready(struct snd_seq_event *ev, void *current_time)
- {
- if ((ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK)
- return snd_seq_compare_tick_time(current_time, &ev->time.tick);
- else
- return snd_seq_compare_real_time(current_time, &ev->time.time);
- }
- /* dequeue cell from prioq */
- struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
- void *current_time)
- {
- struct snd_seq_event_cell *cell;
- unsigned long flags;
- if (f == NULL) {
- pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
- return NULL;
- }
- spin_lock_irqsave(&f->lock, flags);
- cell = f->head;
- if (cell && current_time && !event_is_ready(&cell->event, current_time))
- cell = NULL;
- if (cell) {
- f->head = cell->next;
- /* reset tail if this was the last element */
- if (f->tail == cell)
- f->tail = NULL;
- cell->next = NULL;
- f->cells--;
- }
- spin_unlock_irqrestore(&f->lock, flags);
- return cell;
- }
- /* return number of events available in prioq */
- int snd_seq_prioq_avail(struct snd_seq_prioq * f)
- {
- if (f == NULL) {
- pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
- return 0;
- }
- return f->cells;
- }
- static inline int prioq_match(struct snd_seq_event_cell *cell,
- int client, int timestamp)
- {
- if (cell->event.source.client == client ||
- cell->event.dest.client == client)
- return 1;
- if (!timestamp)
- return 0;
- switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
- case SNDRV_SEQ_TIME_STAMP_TICK:
- if (cell->event.time.tick)
- return 1;
- break;
- case SNDRV_SEQ_TIME_STAMP_REAL:
- if (cell->event.time.time.tv_sec ||
- cell->event.time.time.tv_nsec)
- return 1;
- break;
- }
- return 0;
- }
- /* remove cells for left client */
- void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
- {
- register struct snd_seq_event_cell *cell, *next;
- unsigned long flags;
- struct snd_seq_event_cell *prev = NULL;
- struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
- /* collect all removed cells */
- spin_lock_irqsave(&f->lock, flags);
- cell = f->head;
- while (cell) {
- next = cell->next;
- if (prioq_match(cell, client, timestamp)) {
- /* remove cell from prioq */
- if (cell == f->head) {
- f->head = cell->next;
- } else {
- prev->next = cell->next;
- }
- if (cell == f->tail)
- f->tail = cell->next;
- f->cells--;
- /* add cell to free list */
- cell->next = NULL;
- if (freefirst == NULL) {
- freefirst = cell;
- } else {
- freeprev->next = cell;
- }
- freeprev = cell;
- } else {
- #if 0
- pr_debug("ALSA: seq: type = %i, source = %i, dest = %i, "
- "client = %i\n",
- cell->event.type,
- cell->event.source.client,
- cell->event.dest.client,
- client);
- #endif
- prev = cell;
- }
- cell = next;
- }
- spin_unlock_irqrestore(&f->lock, flags);
- /* remove selected cells */
- while (freefirst) {
- freenext = freefirst->next;
- snd_seq_cell_free(freefirst);
- freefirst = freenext;
- }
- }
- static int prioq_remove_match(struct snd_seq_remove_events *info,
- struct snd_seq_event *ev)
- {
- int res;
- if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
- if (ev->dest.client != info->dest.client ||
- ev->dest.port != info->dest.port)
- return 0;
- }
- if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
- if (! snd_seq_ev_is_channel_type(ev))
- return 0;
- /* data.note.channel and data.control.channel are identical */
- if (ev->data.note.channel != info->channel)
- return 0;
- }
- if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
- if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
- res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
- else
- res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
- if (!res)
- return 0;
- }
- if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
- if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
- res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
- else
- res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
- if (res)
- return 0;
- }
- if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
- if (ev->type != info->type)
- return 0;
- }
- if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) {
- /* Do not remove off events */
- switch (ev->type) {
- case SNDRV_SEQ_EVENT_NOTEOFF:
- /* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */
- return 0;
- default:
- break;
- }
- }
- if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
- if (info->tag != ev->tag)
- return 0;
- }
- return 1;
- }
- /* remove cells matching remove criteria */
- void snd_seq_prioq_remove_events(struct snd_seq_prioq * f, int client,
- struct snd_seq_remove_events *info)
- {
- struct snd_seq_event_cell *cell, *next;
- unsigned long flags;
- struct snd_seq_event_cell *prev = NULL;
- struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
- /* collect all removed cells */
- spin_lock_irqsave(&f->lock, flags);
- cell = f->head;
- while (cell) {
- next = cell->next;
- if (cell->event.source.client == client &&
- prioq_remove_match(info, &cell->event)) {
- /* remove cell from prioq */
- if (cell == f->head) {
- f->head = cell->next;
- } else {
- prev->next = cell->next;
- }
- if (cell == f->tail)
- f->tail = cell->next;
- f->cells--;
- /* add cell to free list */
- cell->next = NULL;
- if (freefirst == NULL) {
- freefirst = cell;
- } else {
- freeprev->next = cell;
- }
- freeprev = cell;
- } else {
- prev = cell;
- }
- cell = next;
- }
- spin_unlock_irqrestore(&f->lock, flags);
- /* remove selected cells */
- while (freefirst) {
- freenext = freefirst->next;
- snd_seq_cell_free(freefirst);
- freefirst = freenext;
- }
- }
|