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