xref: /openbmc/linux/sound/core/seq/seq_fifo.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *   ALSA sequencer FIFO
41da177e4SLinus Torvalds  *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds 
71da177e4SLinus Torvalds #include <sound/core.h>
81da177e4SLinus Torvalds #include <linux/slab.h>
9174cd4b1SIngo Molnar #include <linux/sched/signal.h>
10174cd4b1SIngo Molnar 
111da177e4SLinus Torvalds #include "seq_fifo.h"
121da177e4SLinus Torvalds #include "seq_lock.h"
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds /* FIFO */
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds /* create new fifo */
snd_seq_fifo_new(int poolsize)18c7e0b5bfSTakashi Iwai struct snd_seq_fifo *snd_seq_fifo_new(int poolsize)
191da177e4SLinus Torvalds {
20c7e0b5bfSTakashi Iwai 	struct snd_seq_fifo *f;
211da177e4SLinus Torvalds 
22ecca82b4STakashi Iwai 	f = kzalloc(sizeof(*f), GFP_KERNEL);
2324db8bbaSTakashi Iwai 	if (!f)
241da177e4SLinus Torvalds 		return NULL;
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds 	f->pool = snd_seq_pool_new(poolsize);
271da177e4SLinus Torvalds 	if (f->pool == NULL) {
281da177e4SLinus Torvalds 		kfree(f);
291da177e4SLinus Torvalds 		return NULL;
301da177e4SLinus Torvalds 	}
311da177e4SLinus Torvalds 	if (snd_seq_pool_init(f->pool) < 0) {
321da177e4SLinus Torvalds 		snd_seq_pool_delete(&f->pool);
331da177e4SLinus Torvalds 		kfree(f);
341da177e4SLinus Torvalds 		return NULL;
351da177e4SLinus Torvalds 	}
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds 	spin_lock_init(&f->lock);
381da177e4SLinus Torvalds 	snd_use_lock_init(&f->use_lock);
391da177e4SLinus Torvalds 	init_waitqueue_head(&f->input_sleep);
401da177e4SLinus Torvalds 	atomic_set(&f->overflow, 0);
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds 	f->head = NULL;
431da177e4SLinus Torvalds 	f->tail = NULL;
441da177e4SLinus Torvalds 	f->cells = 0;
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 	return f;
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds 
snd_seq_fifo_delete(struct snd_seq_fifo ** fifo)49c7e0b5bfSTakashi Iwai void snd_seq_fifo_delete(struct snd_seq_fifo **fifo)
501da177e4SLinus Torvalds {
51c7e0b5bfSTakashi Iwai 	struct snd_seq_fifo *f;
521da177e4SLinus Torvalds 
537eaa943cSTakashi Iwai 	if (snd_BUG_ON(!fifo))
547eaa943cSTakashi Iwai 		return;
551da177e4SLinus Torvalds 	f = *fifo;
567eaa943cSTakashi Iwai 	if (snd_BUG_ON(!f))
577eaa943cSTakashi Iwai 		return;
581da177e4SLinus Torvalds 	*fifo = NULL;
591da177e4SLinus Torvalds 
60c520ff3dSTakashi Iwai 	if (f->pool)
61c520ff3dSTakashi Iwai 		snd_seq_pool_mark_closing(f->pool);
62c520ff3dSTakashi Iwai 
631da177e4SLinus Torvalds 	snd_seq_fifo_clear(f);
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 	/* wake up clients if any */
661da177e4SLinus Torvalds 	if (waitqueue_active(&f->input_sleep))
671da177e4SLinus Torvalds 		wake_up(&f->input_sleep);
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds 	/* release resources...*/
701da177e4SLinus Torvalds 	/*....................*/
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	if (f->pool) {
731da177e4SLinus Torvalds 		snd_seq_pool_done(f->pool);
741da177e4SLinus Torvalds 		snd_seq_pool_delete(&f->pool);
751da177e4SLinus Torvalds 	}
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds 	kfree(f);
781da177e4SLinus Torvalds }
791da177e4SLinus Torvalds 
80c7e0b5bfSTakashi Iwai static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f);
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds /* clear queue */
snd_seq_fifo_clear(struct snd_seq_fifo * f)83c7e0b5bfSTakashi Iwai void snd_seq_fifo_clear(struct snd_seq_fifo *f)
841da177e4SLinus Torvalds {
85c7e0b5bfSTakashi Iwai 	struct snd_seq_event_cell *cell;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	/* clear overflow flag */
881da177e4SLinus Torvalds 	atomic_set(&f->overflow, 0);
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 	snd_use_lock_sync(&f->use_lock);
91f823b8a7STakashi Iwai 	spin_lock_irq(&f->lock);
921da177e4SLinus Torvalds 	/* drain the fifo */
931da177e4SLinus Torvalds 	while ((cell = fifo_cell_out(f)) != NULL) {
941da177e4SLinus Torvalds 		snd_seq_cell_free(cell);
951da177e4SLinus Torvalds 	}
96f823b8a7STakashi Iwai 	spin_unlock_irq(&f->lock);
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds /* enqueue event to fifo */
snd_seq_fifo_event_in(struct snd_seq_fifo * f,struct snd_seq_event * event)101c7e0b5bfSTakashi Iwai int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
102c7e0b5bfSTakashi Iwai 			  struct snd_seq_event *event)
1031da177e4SLinus Torvalds {
104c7e0b5bfSTakashi Iwai 	struct snd_seq_event_cell *cell;
1051da177e4SLinus Torvalds 	unsigned long flags;
1061da177e4SLinus Torvalds 	int err;
1071da177e4SLinus Torvalds 
1087eaa943cSTakashi Iwai 	if (snd_BUG_ON(!f))
1097eaa943cSTakashi Iwai 		return -EINVAL;
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds 	snd_use_lock_use(&f->use_lock);
1127bd80091STakashi Iwai 	err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */
1131da177e4SLinus Torvalds 	if (err < 0) {
11421fd3e95SAdam Goode 		if ((err == -ENOMEM) || (err == -EAGAIN))
1151da177e4SLinus Torvalds 			atomic_inc(&f->overflow);
1161da177e4SLinus Torvalds 		snd_use_lock_free(&f->use_lock);
1171da177e4SLinus Torvalds 		return err;
1181da177e4SLinus Torvalds 	}
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 	/* append new cells to fifo */
1211da177e4SLinus Torvalds 	spin_lock_irqsave(&f->lock, flags);
1221da177e4SLinus Torvalds 	if (f->tail != NULL)
1231da177e4SLinus Torvalds 		f->tail->next = cell;
1241da177e4SLinus Torvalds 	f->tail = cell;
1251da177e4SLinus Torvalds 	if (f->head == NULL)
1261da177e4SLinus Torvalds 		f->head = cell;
127f3ac9f73STakashi Iwai 	cell->next = NULL;
1281da177e4SLinus Torvalds 	f->cells++;
1291da177e4SLinus Torvalds 	spin_unlock_irqrestore(&f->lock, flags);
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 	/* wakeup client */
1321da177e4SLinus Torvalds 	if (waitqueue_active(&f->input_sleep))
1331da177e4SLinus Torvalds 		wake_up(&f->input_sleep);
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	snd_use_lock_free(&f->use_lock);
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 	return 0; /* success */
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds }
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds /* dequeue cell from fifo */
fifo_cell_out(struct snd_seq_fifo * f)142c7e0b5bfSTakashi Iwai static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f)
1431da177e4SLinus Torvalds {
144c7e0b5bfSTakashi Iwai 	struct snd_seq_event_cell *cell;
1451da177e4SLinus Torvalds 
146*f9a6bb84STakashi Iwai 	cell = f->head;
147*f9a6bb84STakashi Iwai 	if (cell) {
1481da177e4SLinus Torvalds 		f->head = cell->next;
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 		/* reset tail if this was the last element */
1511da177e4SLinus Torvalds 		if (f->tail == cell)
1521da177e4SLinus Torvalds 			f->tail = NULL;
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 		cell->next = NULL;
1551da177e4SLinus Torvalds 		f->cells--;
1561da177e4SLinus Torvalds 	}
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds 	return cell;
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds /* dequeue cell from fifo and copy on user space */
snd_seq_fifo_cell_out(struct snd_seq_fifo * f,struct snd_seq_event_cell ** cellp,int nonblock)162c7e0b5bfSTakashi Iwai int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
163c7e0b5bfSTakashi Iwai 			  struct snd_seq_event_cell **cellp, int nonblock)
1641da177e4SLinus Torvalds {
165c7e0b5bfSTakashi Iwai 	struct snd_seq_event_cell *cell;
1661da177e4SLinus Torvalds 	unsigned long flags;
167ac6424b9SIngo Molnar 	wait_queue_entry_t wait;
1681da177e4SLinus Torvalds 
1697eaa943cSTakashi Iwai 	if (snd_BUG_ON(!f))
1707eaa943cSTakashi Iwai 		return -EINVAL;
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	*cellp = NULL;
1731da177e4SLinus Torvalds 	init_waitqueue_entry(&wait, current);
1741da177e4SLinus Torvalds 	spin_lock_irqsave(&f->lock, flags);
1751da177e4SLinus Torvalds 	while ((cell = fifo_cell_out(f)) == NULL) {
1761da177e4SLinus Torvalds 		if (nonblock) {
1771da177e4SLinus Torvalds 			/* non-blocking - return immediately */
1781da177e4SLinus Torvalds 			spin_unlock_irqrestore(&f->lock, flags);
1791da177e4SLinus Torvalds 			return -EAGAIN;
1801da177e4SLinus Torvalds 		}
1811da177e4SLinus Torvalds 		set_current_state(TASK_INTERRUPTIBLE);
1821da177e4SLinus Torvalds 		add_wait_queue(&f->input_sleep, &wait);
1834b24b960STakashi Iwai 		spin_unlock_irqrestore(&f->lock, flags);
1841da177e4SLinus Torvalds 		schedule();
1854b24b960STakashi Iwai 		spin_lock_irqsave(&f->lock, flags);
1861da177e4SLinus Torvalds 		remove_wait_queue(&f->input_sleep, &wait);
1871da177e4SLinus Torvalds 		if (signal_pending(current)) {
1881da177e4SLinus Torvalds 			spin_unlock_irqrestore(&f->lock, flags);
1891da177e4SLinus Torvalds 			return -ERESTARTSYS;
1901da177e4SLinus Torvalds 		}
1911da177e4SLinus Torvalds 	}
1921da177e4SLinus Torvalds 	spin_unlock_irqrestore(&f->lock, flags);
1931da177e4SLinus Torvalds 	*cellp = cell;
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 	return 0;
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 
snd_seq_fifo_cell_putback(struct snd_seq_fifo * f,struct snd_seq_event_cell * cell)199c7e0b5bfSTakashi Iwai void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f,
200c7e0b5bfSTakashi Iwai 			       struct snd_seq_event_cell *cell)
2011da177e4SLinus Torvalds {
2021da177e4SLinus Torvalds 	unsigned long flags;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	if (cell) {
2051da177e4SLinus Torvalds 		spin_lock_irqsave(&f->lock, flags);
2061da177e4SLinus Torvalds 		cell->next = f->head;
2071da177e4SLinus Torvalds 		f->head = cell;
208f3ac9f73STakashi Iwai 		if (!f->tail)
209f3ac9f73STakashi Iwai 			f->tail = cell;
2101da177e4SLinus Torvalds 		f->cells++;
2111da177e4SLinus Torvalds 		spin_unlock_irqrestore(&f->lock, flags);
2121da177e4SLinus Torvalds 	}
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds /* polling; return non-zero if queue is available */
snd_seq_fifo_poll_wait(struct snd_seq_fifo * f,struct file * file,poll_table * wait)217c7e0b5bfSTakashi Iwai int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file,
218c7e0b5bfSTakashi Iwai 			   poll_table *wait)
2191da177e4SLinus Torvalds {
2201da177e4SLinus Torvalds 	poll_wait(file, &f->input_sleep, wait);
2211da177e4SLinus Torvalds 	return (f->cells > 0);
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds /* change the size of pool; all old events are removed */
snd_seq_fifo_resize(struct snd_seq_fifo * f,int poolsize)225c7e0b5bfSTakashi Iwai int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
2261da177e4SLinus Torvalds {
227c7e0b5bfSTakashi Iwai 	struct snd_seq_pool *newpool, *oldpool;
228c7e0b5bfSTakashi Iwai 	struct snd_seq_event_cell *cell, *next, *oldhead;
2291da177e4SLinus Torvalds 
2307eaa943cSTakashi Iwai 	if (snd_BUG_ON(!f || !f->pool))
2317eaa943cSTakashi Iwai 		return -EINVAL;
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	/* allocate new pool */
2341da177e4SLinus Torvalds 	newpool = snd_seq_pool_new(poolsize);
2351da177e4SLinus Torvalds 	if (newpool == NULL)
2361da177e4SLinus Torvalds 		return -ENOMEM;
2371da177e4SLinus Torvalds 	if (snd_seq_pool_init(newpool) < 0) {
2381da177e4SLinus Torvalds 		snd_seq_pool_delete(&newpool);
2391da177e4SLinus Torvalds 		return -ENOMEM;
2401da177e4SLinus Torvalds 	}
2411da177e4SLinus Torvalds 
242f823b8a7STakashi Iwai 	spin_lock_irq(&f->lock);
2431da177e4SLinus Torvalds 	/* remember old pool */
2441da177e4SLinus Torvalds 	oldpool = f->pool;
2451da177e4SLinus Torvalds 	oldhead = f->head;
2461da177e4SLinus Torvalds 	/* exchange pools */
2471da177e4SLinus Torvalds 	f->pool = newpool;
2481da177e4SLinus Torvalds 	f->head = NULL;
2491da177e4SLinus Torvalds 	f->tail = NULL;
2501da177e4SLinus Torvalds 	f->cells = 0;
2511da177e4SLinus Torvalds 	/* NOTE: overflow flag is not cleared */
252f823b8a7STakashi Iwai 	spin_unlock_irq(&f->lock);
2531da177e4SLinus Torvalds 
2542d7d5400STakashi Iwai 	/* close the old pool and wait until all users are gone */
2552d7d5400STakashi Iwai 	snd_seq_pool_mark_closing(oldpool);
2562d7d5400STakashi Iwai 	snd_use_lock_sync(&f->use_lock);
2572d7d5400STakashi Iwai 
2581da177e4SLinus Torvalds 	/* release cells in old pool */
2591da177e4SLinus Torvalds 	for (cell = oldhead; cell; cell = next) {
2601da177e4SLinus Torvalds 		next = cell->next;
2611da177e4SLinus Torvalds 		snd_seq_cell_free(cell);
2621da177e4SLinus Torvalds 	}
2631da177e4SLinus Torvalds 	snd_seq_pool_delete(&oldpool);
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds 	return 0;
2661da177e4SLinus Torvalds }
26775545304STakashi Iwai 
26875545304STakashi Iwai /* get the number of unused cells safely */
snd_seq_fifo_unused_cells(struct snd_seq_fifo * f)26975545304STakashi Iwai int snd_seq_fifo_unused_cells(struct snd_seq_fifo *f)
27075545304STakashi Iwai {
27175545304STakashi Iwai 	unsigned long flags;
27275545304STakashi Iwai 	int cells;
27375545304STakashi Iwai 
27475545304STakashi Iwai 	if (!f)
27575545304STakashi Iwai 		return 0;
27675545304STakashi Iwai 
27775545304STakashi Iwai 	snd_use_lock_use(&f->use_lock);
27875545304STakashi Iwai 	spin_lock_irqsave(&f->lock, flags);
27975545304STakashi Iwai 	cells = snd_seq_unused_cells(f->pool);
28075545304STakashi Iwai 	spin_unlock_irqrestore(&f->lock, flags);
28175545304STakashi Iwai 	snd_use_lock_free(&f->use_lock);
28275545304STakashi Iwai 	return cells;
28375545304STakashi Iwai }
284