1/* 2 * ALSA sequencer FIFO 3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl> 4 * 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 * 20 */ 21 22#include <sound/core.h> 23#include <linux/slab.h> 24#include "seq_fifo.h" 25#include "seq_lock.h" 26 27 28/* FIFO */ 29 30/* create new fifo */ 31struct snd_seq_fifo *snd_seq_fifo_new(int poolsize) 32{ 33 struct snd_seq_fifo *f; 34 35 f = kzalloc(sizeof(*f), GFP_KERNEL); 36 if (!f) 37 return NULL; 38 39 f->pool = snd_seq_pool_new(poolsize); 40 if (f->pool == NULL) { 41 kfree(f); 42 return NULL; 43 } 44 if (snd_seq_pool_init(f->pool) < 0) { 45 snd_seq_pool_delete(&f->pool); 46 kfree(f); 47 return NULL; 48 } 49 50 spin_lock_init(&f->lock); 51 snd_use_lock_init(&f->use_lock); 52 init_waitqueue_head(&f->input_sleep); 53 atomic_set(&f->overflow, 0); 54 55 f->head = NULL; 56 f->tail = NULL; 57 f->cells = 0; 58 59 return f; 60} 61 62void snd_seq_fifo_delete(struct snd_seq_fifo **fifo) 63{ 64 struct snd_seq_fifo *f; 65 66 if (snd_BUG_ON(!fifo)) 67 return; 68 f = *fifo; 69 if (snd_BUG_ON(!f)) 70 return; 71 *fifo = NULL; 72 73 snd_seq_fifo_clear(f); 74 75 /* wake up clients if any */ 76 if (waitqueue_active(&f->input_sleep)) 77 wake_up(&f->input_sleep); 78 79 /* release resources...*/ 80 /*....................*/ 81 82 if (f->pool) { 83 snd_seq_pool_done(f->pool); 84 snd_seq_pool_delete(&f->pool); 85 } 86 87 kfree(f); 88} 89 90static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f); 91 92/* clear queue */ 93void snd_seq_fifo_clear(struct snd_seq_fifo *f) 94{ 95 struct snd_seq_event_cell *cell; 96 unsigned long flags; 97 98 /* clear overflow flag */ 99 atomic_set(&f->overflow, 0); 100 101 snd_use_lock_sync(&f->use_lock); 102 spin_lock_irqsave(&f->lock, flags); 103 /* drain the fifo */ 104 while ((cell = fifo_cell_out(f)) != NULL) { 105 snd_seq_cell_free(cell); 106 } 107 spin_unlock_irqrestore(&f->lock, flags); 108} 109 110 111/* enqueue event to fifo */ 112int snd_seq_fifo_event_in(struct snd_seq_fifo *f, 113 struct snd_seq_event *event) 114{ 115 struct snd_seq_event_cell *cell; 116 unsigned long flags; 117 int err; 118 119 if (snd_BUG_ON(!f)) 120 return -EINVAL; 121 122 snd_use_lock_use(&f->use_lock); 123 err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */ 124 if (err < 0) { 125 if ((err == -ENOMEM) || (err == -EAGAIN)) 126 atomic_inc(&f->overflow); 127 snd_use_lock_free(&f->use_lock); 128 return err; 129 } 130 131 /* append new cells to fifo */ 132 spin_lock_irqsave(&f->lock, flags); 133 if (f->tail != NULL) 134 f->tail->next = cell; 135 f->tail = cell; 136 if (f->head == NULL) 137 f->head = cell; 138 f->cells++; 139 spin_unlock_irqrestore(&f->lock, flags); 140 141 /* wakeup client */ 142 if (waitqueue_active(&f->input_sleep)) 143 wake_up(&f->input_sleep); 144 145 snd_use_lock_free(&f->use_lock); 146 147 return 0; /* success */ 148 149} 150 151/* dequeue cell from fifo */ 152static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f) 153{ 154 struct snd_seq_event_cell *cell; 155 156 if ((cell = f->head) != NULL) { 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_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_irq(&f->lock); 193 schedule(); 194 spin_lock_irq(&f->lock); 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 unsigned long flags; 212 213 if (cell) { 214 spin_lock_irqsave(&f->lock, flags); 215 cell->next = f->head; 216 f->head = cell; 217 f->cells++; 218 spin_unlock_irqrestore(&f->lock, flags); 219 } 220} 221 222 223/* polling; return non-zero if queue is available */ 224int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file, 225 poll_table *wait) 226{ 227 poll_wait(file, &f->input_sleep, wait); 228 return (f->cells > 0); 229} 230 231/* change the size of pool; all old events are removed */ 232int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize) 233{ 234 unsigned long flags; 235 struct snd_seq_pool *newpool, *oldpool; 236 struct snd_seq_event_cell *cell, *next, *oldhead; 237 238 if (snd_BUG_ON(!f || !f->pool)) 239 return -EINVAL; 240 241 /* allocate new pool */ 242 newpool = snd_seq_pool_new(poolsize); 243 if (newpool == NULL) 244 return -ENOMEM; 245 if (snd_seq_pool_init(newpool) < 0) { 246 snd_seq_pool_delete(&newpool); 247 return -ENOMEM; 248 } 249 250 spin_lock_irqsave(&f->lock, flags); 251 /* remember old pool */ 252 oldpool = f->pool; 253 oldhead = f->head; 254 /* exchange pools */ 255 f->pool = newpool; 256 f->head = NULL; 257 f->tail = NULL; 258 f->cells = 0; 259 /* NOTE: overflow flag is not cleared */ 260 spin_unlock_irqrestore(&f->lock, flags); 261 262 /* release cells in old pool */ 263 for (cell = oldhead; cell; cell = next) { 264 next = cell->next; 265 snd_seq_cell_free(cell); 266 } 267 snd_seq_pool_delete(&oldpool); 268 269 return 0; 270} 271