Commit ec89fc1b authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: seq: prioq: Unify cell removal functions

Both snd_seq_prioq_remove_events() and snd_seq_prioq_leave() have a
very similar loop for removing events.  Unify them with a callback for
code simplification.

Only the code refactoring, and no functional changes.

Link: https://lore.kernel.org/r/20240222132152.29063-1-tiwai@suse.de


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent bc80e83e
Loading
Loading
Loading
Loading
+84 −113
Original line number Diff line number Diff line
@@ -249,30 +249,11 @@ int snd_seq_prioq_avail(struct snd_seq_prioq * f)
	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)
/* remove cells matching with the condition */
static void prioq_remove_cells(struct snd_seq_prioq *f,
			       bool (*match)(struct snd_seq_event_cell *cell,
					     void *arg),
			       void *arg)
{
	register struct snd_seq_event_cell *cell, *next;
	unsigned long flags;
@@ -281,39 +262,29 @@ void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)

	/* collect all removed cells */
	spin_lock_irqsave(&f->lock, flags);
	cell = f->head;
	while (cell) {
	for (cell = f->head; cell; cell = next) {
		next = cell->next;
		if (prioq_match(cell, client, timestamp)) {
		if (!match(cell, arg)) {
			prev = cell;
			continue;
		}

		/* remove cell from prioq */
			if (cell == f->head) {
		if (cell == f->head)
			f->head = cell->next;
			} else {
		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) {
		if (freefirst == NULL)
			freefirst = cell;
			} else {
		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);	

@@ -325,22 +296,68 @@ void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
	}
}

static int prioq_remove_match(struct snd_seq_remove_events *info,
			      struct snd_seq_event *ev)
struct prioq_match_arg {
	int client;
	int timestamp;
};

static inline bool prioq_match(struct snd_seq_event_cell *cell, void *arg)
{
	struct prioq_match_arg *v = arg;

	if (cell->event.source.client == v->client ||
	    cell->event.dest.client == v->client)
		return true;
	if (!v->timestamp)
		return false;
	switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
	case SNDRV_SEQ_TIME_STAMP_TICK:
		if (cell->event.time.tick)
			return true;
		break;
	case SNDRV_SEQ_TIME_STAMP_REAL:
		if (cell->event.time.time.tv_sec ||
		    cell->event.time.time.tv_nsec)
			return true;
		break;
	}
	return false;
}

/* remove cells for left client */
void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp)
{
	struct prioq_match_arg arg = { client, timestamp };

	return prioq_remove_cells(f, prioq_match, &arg);
}

struct prioq_remove_match_arg {
	int client;
	struct snd_seq_remove_events *info;
};

static bool prioq_remove_match(struct snd_seq_event_cell *cell, void *arg)
{
	struct prioq_remove_match_arg *v = arg;
	struct snd_seq_event *ev = &cell->event;
	struct snd_seq_remove_events *info = v->info;
	int res;

	if (ev->source.client != v->client)
		return false;

	if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
		if (ev->dest.client != info->dest.client ||
				ev->dest.port != info->dest.port)
			return 0;
			return false;
	}
	if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
		if (! snd_seq_ev_is_channel_type(ev))
			return 0;
			return false;
		/* data.note.channel and data.control.channel are identical */
		if (ev->data.note.channel != info->channel)
			return 0;
			return false;
	}
	if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
		if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
@@ -348,7 +365,7 @@ static int prioq_remove_match(struct snd_seq_remove_events *info,
		else
			res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
		if (!res)
			return 0;
			return false;
	}
	if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
		if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
@@ -356,81 +373,35 @@ static int prioq_remove_match(struct snd_seq_remove_events *info,
		else
			res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
		if (res)
			return 0;
			return false;
	}
	if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
		if (ev->type != info->type)
			return 0;
			return false;
	}
	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;
			return false;
		default:
			break;
		}
	}
	if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
		if (info->tag != ev->tag)
			return 0;
			return false;
	}

	return 1;
	return true;
}

/* 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;
			}
	struct prioq_remove_match_arg arg = { client, info };

			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;		
	return prioq_remove_cells(f, prioq_remove_match, &arg);
}
	spin_unlock_irqrestore(&f->lock, flags);	

	/* remove selected cells */
	while (freefirst) {
		freenext = freefirst->next;
		snd_seq_cell_free(freefirst);
		freefirst = freenext;
	}
}