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 */ 31 struct 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 62 void 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 90 static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f); 91 92 /* clear queue */ 93 void 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 */ 112 int 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 cell->next = NULL; 139 f->cells++; 140 spin_unlock_irqrestore(&f->lock, flags); 141 142 /* wakeup client */ 143 if (waitqueue_active(&f->input_sleep)) 144 wake_up(&f->input_sleep); 145 146 snd_use_lock_free(&f->use_lock); 147 148 return 0; /* success */ 149 150 } 151 152 /* dequeue cell from fifo */ 153 static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f) 154 { 155 struct snd_seq_event_cell *cell; 156 157 if ((cell = f->head) != NULL) { 158 f->head = cell->next; 159 160 /* reset tail if this was the last element */ 161 if (f->tail == cell) 162 f->tail = NULL; 163 164 cell->next = NULL; 165 f->cells--; 166 } 167 168 return cell; 169 } 170 171 /* dequeue cell from fifo and copy on user space */ 172 int snd_seq_fifo_cell_out(struct snd_seq_fifo *f, 173 struct snd_seq_event_cell **cellp, int nonblock) 174 { 175 struct snd_seq_event_cell *cell; 176 unsigned long flags; 177 wait_queue_t wait; 178 179 if (snd_BUG_ON(!f)) 180 return -EINVAL; 181 182 *cellp = NULL; 183 init_waitqueue_entry(&wait, current); 184 spin_lock_irqsave(&f->lock, flags); 185 while ((cell = fifo_cell_out(f)) == NULL) { 186 if (nonblock) { 187 /* non-blocking - return immediately */ 188 spin_unlock_irqrestore(&f->lock, flags); 189 return -EAGAIN; 190 } 191 set_current_state(TASK_INTERRUPTIBLE); 192 add_wait_queue(&f->input_sleep, &wait); 193 spin_unlock_irq(&f->lock); 194 schedule(); 195 spin_lock_irq(&f->lock); 196 remove_wait_queue(&f->input_sleep, &wait); 197 if (signal_pending(current)) { 198 spin_unlock_irqrestore(&f->lock, flags); 199 return -ERESTARTSYS; 200 } 201 } 202 spin_unlock_irqrestore(&f->lock, flags); 203 *cellp = cell; 204 205 return 0; 206 } 207 208 209 void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f, 210 struct snd_seq_event_cell *cell) 211 { 212 unsigned long flags; 213 214 if (cell) { 215 spin_lock_irqsave(&f->lock, flags); 216 cell->next = f->head; 217 f->head = cell; 218 if (!f->tail) 219 f->tail = cell; 220 f->cells++; 221 spin_unlock_irqrestore(&f->lock, flags); 222 } 223 } 224 225 226 /* polling; return non-zero if queue is available */ 227 int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file, 228 poll_table *wait) 229 { 230 poll_wait(file, &f->input_sleep, wait); 231 return (f->cells > 0); 232 } 233 234 /* change the size of pool; all old events are removed */ 235 int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize) 236 { 237 unsigned long flags; 238 struct snd_seq_pool *newpool, *oldpool; 239 struct snd_seq_event_cell *cell, *next, *oldhead; 240 241 if (snd_BUG_ON(!f || !f->pool)) 242 return -EINVAL; 243 244 /* allocate new pool */ 245 newpool = snd_seq_pool_new(poolsize); 246 if (newpool == NULL) 247 return -ENOMEM; 248 if (snd_seq_pool_init(newpool) < 0) { 249 snd_seq_pool_delete(&newpool); 250 return -ENOMEM; 251 } 252 253 spin_lock_irqsave(&f->lock, flags); 254 /* remember old pool */ 255 oldpool = f->pool; 256 oldhead = f->head; 257 /* exchange pools */ 258 f->pool = newpool; 259 f->head = NULL; 260 f->tail = NULL; 261 f->cells = 0; 262 /* NOTE: overflow flag is not cleared */ 263 spin_unlock_irqrestore(&f->lock, flags); 264 265 /* release cells in old pool */ 266 for (cell = oldhead; cell; cell = next) { 267 next = cell->next; 268 snd_seq_cell_free(cell); 269 } 270 snd_seq_pool_delete(&oldpool); 271 272 return 0; 273 } 274