1 /* 2 * ALSA sequencer Memory Manager 3 * Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl> 4 * Jaroslav Kysela <perex@suse.cz> 5 * 2000 by Takashi Iwai <tiwai@suse.de> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * 21 */ 22 23 #include <sound/driver.h> 24 #include <linux/init.h> 25 #include <linux/slab.h> 26 #include <linux/vmalloc.h> 27 #include <sound/core.h> 28 29 #include <sound/seq_kernel.h> 30 #include "seq_memory.h" 31 #include "seq_queue.h" 32 #include "seq_info.h" 33 #include "seq_lock.h" 34 35 /* semaphore in struct file record */ 36 #define semaphore_of(fp) ((fp)->f_dentry->d_inode->i_sem) 37 38 39 inline static int snd_seq_pool_available(pool_t *pool) 40 { 41 return pool->total_elements - atomic_read(&pool->counter); 42 } 43 44 inline static int snd_seq_output_ok(pool_t *pool) 45 { 46 return snd_seq_pool_available(pool) >= pool->room; 47 } 48 49 /* 50 * Variable length event: 51 * The event like sysex uses variable length type. 52 * The external data may be stored in three different formats. 53 * 1) kernel space 54 * This is the normal case. 55 * ext.data.len = length 56 * ext.data.ptr = buffer pointer 57 * 2) user space 58 * When an event is generated via read(), the external data is 59 * kept in user space until expanded. 60 * ext.data.len = length | SNDRV_SEQ_EXT_USRPTR 61 * ext.data.ptr = userspace pointer 62 * 3) chained cells 63 * When the variable length event is enqueued (in prioq or fifo), 64 * the external data is decomposed to several cells. 65 * ext.data.len = length | SNDRV_SEQ_EXT_CHAINED 66 * ext.data.ptr = the additiona cell head 67 * -> cell.next -> cell.next -> .. 68 */ 69 70 /* 71 * exported: 72 * call dump function to expand external data. 73 */ 74 75 static int get_var_len(const snd_seq_event_t *event) 76 { 77 if ((event->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) 78 return -EINVAL; 79 80 return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK; 81 } 82 83 int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data) 84 { 85 int len, err; 86 snd_seq_event_cell_t *cell; 87 88 if ((len = get_var_len(event)) <= 0) 89 return len; 90 91 if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { 92 char buf[32]; 93 char __user *curptr = (char __user *)event->data.ext.ptr; 94 while (len > 0) { 95 int size = sizeof(buf); 96 if (len < size) 97 size = len; 98 if (copy_from_user(buf, curptr, size)) 99 return -EFAULT; 100 err = func(private_data, buf, size); 101 if (err < 0) 102 return err; 103 curptr += size; 104 len -= size; 105 } 106 return 0; 107 } if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) { 108 return func(private_data, event->data.ext.ptr, len); 109 } 110 111 cell = (snd_seq_event_cell_t*)event->data.ext.ptr; 112 for (; len > 0 && cell; cell = cell->next) { 113 int size = sizeof(snd_seq_event_t); 114 if (len < size) 115 size = len; 116 err = func(private_data, &cell->event, size); 117 if (err < 0) 118 return err; 119 len -= size; 120 } 121 return 0; 122 } 123 124 125 /* 126 * exported: 127 * expand the variable length event to linear buffer space. 128 */ 129 130 static int seq_copy_in_kernel(char **bufptr, const void *src, int size) 131 { 132 memcpy(*bufptr, src, size); 133 *bufptr += size; 134 return 0; 135 } 136 137 static int seq_copy_in_user(char __user **bufptr, const void *src, int size) 138 { 139 if (copy_to_user(*bufptr, src, size)) 140 return -EFAULT; 141 *bufptr += size; 142 return 0; 143 } 144 145 int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned) 146 { 147 int len, newlen; 148 int err; 149 150 if ((len = get_var_len(event)) < 0) 151 return len; 152 newlen = len; 153 if (size_aligned > 0) 154 newlen = ((len + size_aligned - 1) / size_aligned) * size_aligned; 155 if (count < newlen) 156 return -EAGAIN; 157 158 if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { 159 if (! in_kernel) 160 return -EINVAL; 161 if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len)) 162 return -EFAULT; 163 return newlen; 164 } 165 err = snd_seq_dump_var_event(event, 166 in_kernel ? (snd_seq_dump_func_t)seq_copy_in_kernel : 167 (snd_seq_dump_func_t)seq_copy_in_user, 168 &buf); 169 return err < 0 ? err : newlen; 170 } 171 172 173 /* 174 * release this cell, free extended data if available 175 */ 176 177 static inline void free_cell(pool_t *pool, snd_seq_event_cell_t *cell) 178 { 179 cell->next = pool->free; 180 pool->free = cell; 181 atomic_dec(&pool->counter); 182 } 183 184 void snd_seq_cell_free(snd_seq_event_cell_t * cell) 185 { 186 unsigned long flags; 187 pool_t *pool; 188 189 snd_assert(cell != NULL, return); 190 pool = cell->pool; 191 snd_assert(pool != NULL, return); 192 193 spin_lock_irqsave(&pool->lock, flags); 194 free_cell(pool, cell); 195 if (snd_seq_ev_is_variable(&cell->event)) { 196 if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) { 197 snd_seq_event_cell_t *curp, *nextptr; 198 curp = cell->event.data.ext.ptr; 199 for (; curp; curp = nextptr) { 200 nextptr = curp->next; 201 curp->next = pool->free; 202 free_cell(pool, curp); 203 } 204 } 205 } 206 if (waitqueue_active(&pool->output_sleep)) { 207 /* has enough space now? */ 208 if (snd_seq_output_ok(pool)) 209 wake_up(&pool->output_sleep); 210 } 211 spin_unlock_irqrestore(&pool->lock, flags); 212 } 213 214 215 /* 216 * allocate an event cell. 217 */ 218 static int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file) 219 { 220 snd_seq_event_cell_t *cell; 221 unsigned long flags; 222 int err = -EAGAIN; 223 wait_queue_t wait; 224 225 if (pool == NULL) 226 return -EINVAL; 227 228 *cellp = NULL; 229 230 init_waitqueue_entry(&wait, current); 231 spin_lock_irqsave(&pool->lock, flags); 232 if (pool->ptr == NULL) { /* not initialized */ 233 snd_printd("seq: pool is not initialized\n"); 234 err = -EINVAL; 235 goto __error; 236 } 237 while (pool->free == NULL && ! nonblock && ! pool->closing) { 238 239 set_current_state(TASK_INTERRUPTIBLE); 240 add_wait_queue(&pool->output_sleep, &wait); 241 spin_unlock_irq(&pool->lock); 242 schedule(); 243 spin_lock_irq(&pool->lock); 244 remove_wait_queue(&pool->output_sleep, &wait); 245 /* interrupted? */ 246 if (signal_pending(current)) { 247 err = -ERESTARTSYS; 248 goto __error; 249 } 250 } 251 if (pool->closing) { /* closing.. */ 252 err = -ENOMEM; 253 goto __error; 254 } 255 256 cell = pool->free; 257 if (cell) { 258 int used; 259 pool->free = cell->next; 260 atomic_inc(&pool->counter); 261 used = atomic_read(&pool->counter); 262 if (pool->max_used < used) 263 pool->max_used = used; 264 pool->event_alloc_success++; 265 /* clear cell pointers */ 266 cell->next = NULL; 267 err = 0; 268 } else 269 pool->event_alloc_failures++; 270 *cellp = cell; 271 272 __error: 273 spin_unlock_irqrestore(&pool->lock, flags); 274 return err; 275 } 276 277 278 /* 279 * duplicate the event to a cell. 280 * if the event has external data, the data is decomposed to additional 281 * cells. 282 */ 283 int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file) 284 { 285 int ncells, err; 286 unsigned int extlen; 287 snd_seq_event_cell_t *cell; 288 289 *cellp = NULL; 290 291 ncells = 0; 292 extlen = 0; 293 if (snd_seq_ev_is_variable(event)) { 294 extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK; 295 ncells = (extlen + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t); 296 } 297 if (ncells >= pool->total_elements) 298 return -ENOMEM; 299 300 err = snd_seq_cell_alloc(pool, &cell, nonblock, file); 301 if (err < 0) 302 return err; 303 304 /* copy the event */ 305 cell->event = *event; 306 307 /* decompose */ 308 if (snd_seq_ev_is_variable(event)) { 309 int len = extlen; 310 int is_chained = event->data.ext.len & SNDRV_SEQ_EXT_CHAINED; 311 int is_usrptr = event->data.ext.len & SNDRV_SEQ_EXT_USRPTR; 312 snd_seq_event_cell_t *src, *tmp, *tail; 313 char *buf; 314 315 cell->event.data.ext.len = extlen | SNDRV_SEQ_EXT_CHAINED; 316 cell->event.data.ext.ptr = NULL; 317 318 src = (snd_seq_event_cell_t*)event->data.ext.ptr; 319 buf = (char *)event->data.ext.ptr; 320 tail = NULL; 321 322 while (ncells-- > 0) { 323 int size = sizeof(snd_seq_event_t); 324 if (len < size) 325 size = len; 326 err = snd_seq_cell_alloc(pool, &tmp, nonblock, file); 327 if (err < 0) 328 goto __error; 329 if (cell->event.data.ext.ptr == NULL) 330 cell->event.data.ext.ptr = tmp; 331 if (tail) 332 tail->next = tmp; 333 tail = tmp; 334 /* copy chunk */ 335 if (is_chained && src) { 336 tmp->event = src->event; 337 src = src->next; 338 } else if (is_usrptr) { 339 if (copy_from_user(&tmp->event, (char __user *)buf, size)) { 340 err = -EFAULT; 341 goto __error; 342 } 343 } else { 344 memcpy(&tmp->event, buf, size); 345 } 346 buf += size; 347 len -= size; 348 } 349 } 350 351 *cellp = cell; 352 return 0; 353 354 __error: 355 snd_seq_cell_free(cell); 356 return err; 357 } 358 359 360 /* poll wait */ 361 int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait) 362 { 363 poll_wait(file, &pool->output_sleep, wait); 364 return snd_seq_output_ok(pool); 365 } 366 367 368 /* allocate room specified number of events */ 369 int snd_seq_pool_init(pool_t *pool) 370 { 371 int cell; 372 snd_seq_event_cell_t *cellptr; 373 unsigned long flags; 374 375 snd_assert(pool != NULL, return -EINVAL); 376 if (pool->ptr) /* should be atomic? */ 377 return 0; 378 379 pool->ptr = vmalloc(sizeof(snd_seq_event_cell_t) * pool->size); 380 if (pool->ptr == NULL) { 381 snd_printd("seq: malloc for sequencer events failed\n"); 382 return -ENOMEM; 383 } 384 385 /* add new cells to the free cell list */ 386 spin_lock_irqsave(&pool->lock, flags); 387 pool->free = NULL; 388 389 for (cell = 0; cell < pool->size; cell++) { 390 cellptr = pool->ptr + cell; 391 cellptr->pool = pool; 392 cellptr->next = pool->free; 393 pool->free = cellptr; 394 } 395 pool->room = (pool->size + 1) / 2; 396 397 /* init statistics */ 398 pool->max_used = 0; 399 pool->total_elements = pool->size; 400 spin_unlock_irqrestore(&pool->lock, flags); 401 return 0; 402 } 403 404 /* remove events */ 405 int snd_seq_pool_done(pool_t *pool) 406 { 407 unsigned long flags; 408 snd_seq_event_cell_t *ptr; 409 int max_count = 5 * HZ; 410 411 snd_assert(pool != NULL, return -EINVAL); 412 413 /* wait for closing all threads */ 414 spin_lock_irqsave(&pool->lock, flags); 415 pool->closing = 1; 416 spin_unlock_irqrestore(&pool->lock, flags); 417 418 if (waitqueue_active(&pool->output_sleep)) 419 wake_up(&pool->output_sleep); 420 421 while (atomic_read(&pool->counter) > 0) { 422 if (max_count == 0) { 423 snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter)); 424 break; 425 } 426 set_current_state(TASK_UNINTERRUPTIBLE); 427 schedule_timeout(1); 428 max_count--; 429 } 430 431 /* release all resources */ 432 spin_lock_irqsave(&pool->lock, flags); 433 ptr = pool->ptr; 434 pool->ptr = NULL; 435 pool->free = NULL; 436 pool->total_elements = 0; 437 spin_unlock_irqrestore(&pool->lock, flags); 438 439 vfree(ptr); 440 441 spin_lock_irqsave(&pool->lock, flags); 442 pool->closing = 0; 443 spin_unlock_irqrestore(&pool->lock, flags); 444 445 return 0; 446 } 447 448 449 /* init new memory pool */ 450 pool_t *snd_seq_pool_new(int poolsize) 451 { 452 pool_t *pool; 453 454 /* create pool block */ 455 pool = kcalloc(1, sizeof(*pool), GFP_KERNEL); 456 if (pool == NULL) { 457 snd_printd("seq: malloc failed for pool\n"); 458 return NULL; 459 } 460 spin_lock_init(&pool->lock); 461 pool->ptr = NULL; 462 pool->free = NULL; 463 pool->total_elements = 0; 464 atomic_set(&pool->counter, 0); 465 pool->closing = 0; 466 init_waitqueue_head(&pool->output_sleep); 467 468 pool->size = poolsize; 469 470 /* init statistics */ 471 pool->max_used = 0; 472 return pool; 473 } 474 475 /* remove memory pool */ 476 int snd_seq_pool_delete(pool_t **ppool) 477 { 478 pool_t *pool = *ppool; 479 480 *ppool = NULL; 481 if (pool == NULL) 482 return 0; 483 snd_seq_pool_done(pool); 484 kfree(pool); 485 return 0; 486 } 487 488 /* initialize sequencer memory */ 489 int __init snd_sequencer_memory_init(void) 490 { 491 return 0; 492 } 493 494 /* release sequencer memory */ 495 void __exit snd_sequencer_memory_done(void) 496 { 497 } 498 499 500 /* exported to seq_clientmgr.c */ 501 void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t *pool, char *space) 502 { 503 if (pool == NULL) 504 return; 505 snd_iprintf(buffer, "%sPool size : %d\n", space, pool->total_elements); 506 snd_iprintf(buffer, "%sCells in use : %d\n", space, atomic_read(&pool->counter)); 507 snd_iprintf(buffer, "%sPeak cells in use : %d\n", space, pool->max_used); 508 snd_iprintf(buffer, "%sAlloc success : %d\n", space, pool->event_alloc_success); 509 snd_iprintf(buffer, "%sAlloc failures : %d\n", space, pool->event_alloc_failures); 510 } 511