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