projects/linux·blob master · refs

292 lines · 5.8 KiB

1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * ALSA sequencer FIFO
4 * Copyright (c) 1998 by Frank van de Pol <[email protected]>
5 */
6
7#include <sound/core.h>
8#include <linux/slab.h>
9#include <linux/sched/signal.h>
10
11#include "seq_fifo.h"
12#include "seq_lock.h"
13
14
15/* FIFO */
16
17/* create new fifo */
18struct snd_seq_fifo *snd_seq_fifo_new(int poolsize)
19{
20 struct snd_seq_fifo *f;
21
22 f = kzalloc_obj(*f);
23 if (!f)
24 return NULL;
25
26 f->pool = snd_seq_pool_new(poolsize);
27 if (f->pool == NULL) {
28 kfree(f);
29 return NULL;
30 }
31 if (snd_seq_pool_init(f->pool) < 0) {
32 snd_seq_pool_delete(&f->pool);
33 kfree(f);
34 return NULL;
35 }
36
37 spin_lock_init(&f->lock);
38 snd_use_lock_init(&f->use_lock);
39 init_waitqueue_head(&f->input_sleep);
40 atomic_set(&f->overflow, 0);
41
42 f->head = NULL;
43 f->tail = NULL;
44 f->cells = 0;
45
46 return f;
47}
48
49void snd_seq_fifo_delete(struct snd_seq_fifo **fifo)
50{
51 struct snd_seq_fifo *f;
52
53 if (snd_BUG_ON(!fifo))
54 return;
55 f = *fifo;
56 if (snd_BUG_ON(!f))
57 return;
58 *fifo = NULL;
59
60 if (f->pool)
61 snd_seq_pool_mark_closing(f->pool);
62
63 snd_seq_fifo_clear(f);
64
65 /* wake up clients if any */
66 if (waitqueue_active(&f->input_sleep))
67 wake_up(&f->input_sleep);
68
69 /* release resources...*/
70 /*....................*/
71
72 if (f->pool) {
73 snd_seq_pool_done(f->pool);
74 snd_seq_pool_delete(&f->pool);
75 }
76
77 kfree(f);
78}
79
80static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f);
81
82/* clear queue */
83void snd_seq_fifo_clear(struct snd_seq_fifo *f)
84{
85 struct snd_seq_event_cell *cell;
86
87 /* clear overflow flag */
88 atomic_set(&f->overflow, 0);
89
90 snd_use_lock_sync(&f->use_lock);
91 guard(spinlock_irq)(&f->lock);
92 /* drain the fifo */
93 while ((cell = fifo_cell_out(f)) != NULL) {
94 snd_seq_cell_free(cell);
95 }
96}
97
98
99/* enqueue event to fifo */
100int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
101 struct snd_seq_event *event)
102{
103 struct snd_seq_event_cell *cell;
104 struct snd_seq_pool *pool;
105 bool linked;
106 int err;
107
108 if (snd_BUG_ON(!f))
109 return -EINVAL;
110
111 guard(snd_seq_fifo)(f);
112retry:
113 pool = READ_ONCE(f->pool);
114 err = snd_seq_event_dup(pool, event, &cell, 1, NULL, NULL); /* always non-blocking */
115 if (err < 0) {
116 if ((err == -ENOMEM) || (err == -EAGAIN))
117 atomic_inc(&f->overflow);
118 return err;
119 }
120
121 /* append new cells to fifo */
122 linked = false;
123 scoped_guard(spinlock_irqsave, &f->lock) {
124 if (cell->pool == f->pool) {
125 if (f->tail)
126 f->tail->next = cell;
127 f->tail = cell;
128 if (!f->head)
129 f->head = cell;
130 cell->next = NULL;
131 f->cells++;
132 linked = true;
133 }
134 }
135
136 if (!linked) {
137 /* Retry against the replacement pool after resize publishes it. */
138 snd_seq_cell_free(cell);
139 goto retry;
140 }
141
142 /* wakeup client */
143 if (waitqueue_active(&f->input_sleep))
144 wake_up(&f->input_sleep);
145
146 return 0; /* success */
147
148}
149
150/* dequeue cell from fifo */
151static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f)
152{
153 struct snd_seq_event_cell *cell;
154
155 cell = f->head;
156 if (cell) {
157 f->head = cell->next;
158
159 /* reset tail if this was the last element */
160 if (f->tail == cell)
161 f->tail = NULL;
162
163 cell->next = NULL;
164 f->cells--;
165 }
166
167 return cell;
168}
169
170/* dequeue cell from fifo and copy on user space */
171int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
172 struct snd_seq_event_cell **cellp, int nonblock)
173{
174 struct snd_seq_event_cell *cell;
175 unsigned long flags;
176 wait_queue_entry_t wait;
177
178 if (snd_BUG_ON(!f))
179 return -EINVAL;
180
181 *cellp = NULL;
182 init_waitqueue_entry(&wait, current);
183 spin_lock_irqsave(&f->lock, flags);
184 while ((cell = fifo_cell_out(f)) == NULL) {
185 if (nonblock) {
186 /* non-blocking - return immediately */
187 spin_unlock_irqrestore(&f->lock, flags);
188 return -EAGAIN;
189 }
190 set_current_state(TASK_INTERRUPTIBLE);
191 add_wait_queue(&f->input_sleep, &wait);
192 spin_unlock_irqrestore(&f->lock, flags);
193 schedule();
194 spin_lock_irqsave(&f->lock, flags);
195 remove_wait_queue(&f->input_sleep, &wait);
196 if (signal_pending(current)) {
197 spin_unlock_irqrestore(&f->lock, flags);
198 return -ERESTARTSYS;
199 }
200 }
201 spin_unlock_irqrestore(&f->lock, flags);
202 *cellp = cell;
203
204 return 0;
205}
206
207
208void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f,
209 struct snd_seq_event_cell *cell)
210{
211 bool linked = false;
212
213 if (cell) {
214 scoped_guard(spinlock_irqsave, &f->lock) {
215 if (cell->pool == f->pool) {
216 cell->next = f->head;
217 f->head = cell;
218 if (!f->tail)
219 f->tail = cell;
220 f->cells++;
221 linked = true;
222 }
223 }
224 if (!linked)
225 snd_seq_cell_free(cell);
226 }
227}
228
229
230/* polling; return non-zero if queue is available */
231int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file,
232 poll_table *wait)
233{
234 poll_wait(file, &f->input_sleep, wait);
235 guard(spinlock_irq)(&f->lock);
236 return (f->cells > 0);
237}
238
239/* change the size of pool; all old events are removed */
240int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
241{
242 struct snd_seq_pool *newpool, *oldpool;
243 struct snd_seq_event_cell *cell, *next, *oldhead;
244
245 if (snd_BUG_ON(!f || !f->pool))
246 return -EINVAL;
247
248 /* allocate new pool */
249 newpool = snd_seq_pool_new(poolsize);
250 if (newpool == NULL)
251 return -ENOMEM;
252 if (snd_seq_pool_init(newpool) < 0) {
253 snd_seq_pool_delete(&newpool);
254 return -ENOMEM;
255 }
256
257 scoped_guard(spinlock_irq, &f->lock) {
258 /* remember old pool */
259 oldpool = f->pool;
260 oldhead = f->head;
261 /* exchange pools */
262 WRITE_ONCE(f->pool, newpool);
263 f->head = NULL;
264 f->tail = NULL;
265 f->cells = 0;
266 /* NOTE: overflow flag is not cleared */
267 }
268
269 /* close the old pool and wait until all users are gone */
270 snd_seq_pool_mark_closing(oldpool);
271 snd_use_lock_sync(&f->use_lock);
272
273 /* release cells in old pool */
274 for (cell = oldhead; cell; cell = next) {
275 next = cell->next;
276 snd_seq_cell_free(cell);
277 }
278 snd_seq_pool_delete(&oldpool);
279
280 return 0;
281}
282
283/* get the number of unused cells safely */
284int snd_seq_fifo_unused_cells(struct snd_seq_fifo *f)
285{
286 if (!f)
287 return 0;
288
289 guard(snd_seq_fifo)(f);
290 guard(spinlock_irqsave)(&f->lock);
291 return snd_seq_unused_cells(f->pool);
292}