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 if (f->pool) 74 snd_seq_pool_mark_closing(f->pool); 75 76 snd_seq_fifo_clear(f); 77 78 /* wake up clients if any */ 79 if (waitqueue_active(&f->input_sleep)) 80 wake_up(&f->input_sleep); 81 82 /* release resources...*/ 83 /*....................*/ 84 85 if (f->pool) { 86 snd_seq_pool_done(f->pool); 87 snd_seq_pool_delete(&f->pool); 88 } 89 90 kfree(f); 91 } 92 93 static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f); 94 95 /* clear queue */ 96 void snd_seq_fifo_clear(struct snd_seq_fifo *f) 97 { 98 struct snd_seq_event_cell *cell; 99 unsigned long flags; 100 101 /* clear overflow flag */ 102 atomic_set(&f->overflow, 0); 103 104 snd_use_lock_sync(&f->use_lock); 105 spin_lock_irqsave(&f->lock, flags); 106 /* drain the fifo */ 107 while ((cell = fifo_cell_out(f)) != NULL) { 108 snd_seq_cell_free(cell); 109 } 110 spin_unlock_irqrestore(&f->lock, flags); 111 } 112 113 114 /* enqueue event to fifo */ 115 int snd_seq_fifo_event_in(struct snd_seq_fifo *f, 116 struct snd_seq_event *event) 117 { 118 struct snd_seq_event_cell *cell; 119 unsigned long flags; 120 int err; 121 122 if (snd_BUG_ON(!f)) 123 return -EINVAL; 124 125 snd_use_lock_use(&f->use_lock); 126 err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */ 127 if (err < 0) { 128 if ((err == -ENOMEM) || (err == -EAGAIN)) 129 atomic_inc(&f->overflow); 130 snd_use_lock_free(&f->use_lock); 131 return err; 132 } 133 134 /* append new cells to fifo */ 135 spin_lock_irqsave(&f->lock, flags); 136 if (f->tail != NULL) 137 f->tail->next = cell; 138 f->tail = cell; 139 if (f->head == NULL) 140 f->head = cell; 141 cell->next = NULL; 142 f->cells++; 143 spin_unlock_irqrestore(&f->lock, flags); 144 145 /* wakeup client */ 146 if (waitqueue_active(&f->input_sleep)) 147 wake_up(&f->input_sleep); 148 149 snd_use_lock_free(&f->use_lock); 150 151 return 0; /* success */ 152 153 } 154 155 /* dequeue cell from fifo */ 156 static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f) 157 { 158 struct snd_seq_event_cell *cell; 159 160 if ((cell = f->head) != NULL) { 161 f->head = cell->next; 162 163 /* reset tail if this was the last element */ 164 if (f->tail == cell) 165 f->tail = NULL; 166 167 cell->next = NULL; 168 f->cells--; 169 } 170 171 return cell; 172 } 173 174 /* dequeue cell from fifo and copy on user space */ 175 int snd_seq_fifo_cell_out(struct snd_seq_fifo *f, 176 struct snd_seq_event_cell **cellp, int nonblock) 177 { 178 struct snd_seq_event_cell *cell; 179 unsigned long flags; 180 wait_queue_t wait; 181 182 if (snd_BUG_ON(!f)) 183 return -EINVAL; 184 185 *cellp = NULL; 186 init_waitqueue_entry(&wait, current); 187 spin_lock_irqsave(&f->lock, flags); 188 while ((cell = fifo_cell_out(f)) == NULL) { 189 if (nonblock) { 190 /* non-blocking - return immediately */ 191 spin_unlock_irqrestore(&f->lock, flags); 192 return -EAGAIN; 193 } 194 set_current_state(TASK_INTERRUPTIBLE); 195 add_wait_queue(&f->input_sleep, &wait); 196 spin_unlock_irq(&f->lock); 197 schedule(); 198 spin_lock_irq(&f->lock); 199 remove_wait_queue(&f->input_sleep, &wait); 200 if (signal_pending(current)) { 201 spin_unlock_irqrestore(&f->lock, flags); 202 return -ERESTARTSYS; 203 } 204 } 205 spin_unlock_irqrestore(&f->lock, flags); 206 *cellp = cell; 207 208 return 0; 209 } 210 211 212 void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f, 213 struct snd_seq_event_cell *cell) 214 { 215 unsigned long flags; 216 217 if (cell) { 218 spin_lock_irqsave(&f->lock, flags); 219 cell->next = f->head; 220 f->head = cell; 221 if (!f->tail) 222 f->tail = cell; 223 f->cells++; 224 spin_unlock_irqrestore(&f->lock, flags); 225 } 226 } 227 228 229 /* polling; return non-zero if queue is available */ 230 int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file, 231 poll_table *wait) 232 { 233 poll_wait(file, &f->input_sleep, wait); 234 return (f->cells > 0); 235 } 236 237 /* change the size of pool; all old events are removed */ 238 int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize) 239 { 240 unsigned long flags; 241 struct snd_seq_pool *newpool, *oldpool; 242 struct snd_seq_event_cell *cell, *next, *oldhead; 243 244 if (snd_BUG_ON(!f || !f->pool)) 245 return -EINVAL; 246 247 /* allocate new pool */ 248 newpool = snd_seq_pool_new(poolsize); 249 if (newpool == NULL) 250 return -ENOMEM; 251 if (snd_seq_pool_init(newpool) < 0) { 252 snd_seq_pool_delete(&newpool); 253 return -ENOMEM; 254 } 255 256 spin_lock_irqsave(&f->lock, flags); 257 /* remember old pool */ 258 oldpool = f->pool; 259 oldhead = f->head; 260 /* exchange pools */ 261 f->pool = newpool; 262 f->head = NULL; 263 f->tail = NULL; 264 f->cells = 0; 265 /* NOTE: overflow flag is not cleared */ 266 spin_unlock_irqrestore(&f->lock, flags); 267 268 /* close the old pool and wait until all users are gone */ 269 snd_seq_pool_mark_closing(oldpool); 270 snd_use_lock_sync(&f->use_lock); 271 272 /* release cells in old pool */ 273 for (cell = oldhead; cell; cell = next) { 274 next = cell->next; 275 snd_seq_cell_free(cell); 276 } 277 snd_seq_pool_delete(&oldpool); 278 279 return 0; 280 } 281
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.