xref: /openbmc/linux/sound/synth/util_mem.c (revision 9b93eb47)
1 /*
2  *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
3  *
4  *  Generic memory management routines for soundcard memory allocation
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 #include <linux/mutex.h>
22 #include <linux/init.h>
23 #include <linux/slab.h>
24 #include <linux/module.h>
25 #include <sound/core.h>
26 #include <sound/util_mem.h>
27 
28 MODULE_AUTHOR("Takashi Iwai");
29 MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation");
30 MODULE_LICENSE("GPL");
31 
32 #define get_memblk(p)	list_entry(p, struct snd_util_memblk, list)
33 
34 /*
35  * create a new memory manager
36  */
37 struct snd_util_memhdr *
38 snd_util_memhdr_new(int memsize)
39 {
40 	struct snd_util_memhdr *hdr;
41 
42 	hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
43 	if (hdr == NULL)
44 		return NULL;
45 	hdr->size = memsize;
46 	mutex_init(&hdr->block_mutex);
47 	INIT_LIST_HEAD(&hdr->block);
48 
49 	return hdr;
50 }
51 
52 /*
53  * free a memory manager
54  */
55 void snd_util_memhdr_free(struct snd_util_memhdr *hdr)
56 {
57 	struct list_head *p;
58 
59 	if (!hdr)
60 		return;
61 	/* release all blocks */
62 	while ((p = hdr->block.next) != &hdr->block) {
63 		list_del(p);
64 		kfree(get_memblk(p));
65 	}
66 	kfree(hdr);
67 }
68 
69 /*
70  * allocate a memory block (without mutex)
71  */
72 struct snd_util_memblk *
73 __snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size)
74 {
75 	struct snd_util_memblk *blk;
76 	unsigned int units, prev_offset;
77 	struct list_head *p;
78 
79 	if (snd_BUG_ON(!hdr || size <= 0))
80 		return NULL;
81 
82 	/* word alignment */
83 	units = size;
84 	if (units & 1)
85 		units++;
86 	if (units > hdr->size)
87 		return NULL;
88 
89 	/* look for empty block */
90 	prev_offset = 0;
91 	list_for_each(p, &hdr->block) {
92 		blk = get_memblk(p);
93 		if (blk->offset - prev_offset >= units)
94 			goto __found;
95 		prev_offset = blk->offset + blk->size;
96 	}
97 	if (hdr->size - prev_offset < units)
98 		return NULL;
99 
100 __found:
101 	return __snd_util_memblk_new(hdr, units, p->prev);
102 }
103 
104 
105 /*
106  * create a new memory block with the given size
107  * the block is linked next to prev
108  */
109 struct snd_util_memblk *
110 __snd_util_memblk_new(struct snd_util_memhdr *hdr, unsigned int units,
111 		      struct list_head *prev)
112 {
113 	struct snd_util_memblk *blk;
114 
115 	blk = kmalloc(sizeof(struct snd_util_memblk) + hdr->block_extra_size,
116 		      GFP_KERNEL);
117 	if (blk == NULL)
118 		return NULL;
119 
120 	if (prev == &hdr->block)
121 		blk->offset = 0;
122 	else {
123 		struct snd_util_memblk *p = get_memblk(prev);
124 		blk->offset = p->offset + p->size;
125 	}
126 	blk->size = units;
127 	list_add(&blk->list, prev);
128 	hdr->nblocks++;
129 	hdr->used += units;
130 	return blk;
131 }
132 
133 
134 /*
135  * allocate a memory block (with mutex)
136  */
137 struct snd_util_memblk *
138 snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size)
139 {
140 	struct snd_util_memblk *blk;
141 	mutex_lock(&hdr->block_mutex);
142 	blk = __snd_util_mem_alloc(hdr, size);
143 	mutex_unlock(&hdr->block_mutex);
144 	return blk;
145 }
146 
147 
148 /*
149  * remove the block from linked-list and free resource
150  * (without mutex)
151  */
152 void
153 __snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
154 {
155 	list_del(&blk->list);
156 	hdr->nblocks--;
157 	hdr->used -= blk->size;
158 	kfree(blk);
159 }
160 
161 /*
162  * free a memory block (with mutex)
163  */
164 int snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
165 {
166 	if (snd_BUG_ON(!hdr || !blk))
167 		return -EINVAL;
168 
169 	mutex_lock(&hdr->block_mutex);
170 	__snd_util_mem_free(hdr, blk);
171 	mutex_unlock(&hdr->block_mutex);
172 	return 0;
173 }
174 
175 /*
176  * return available memory size
177  */
178 int snd_util_mem_avail(struct snd_util_memhdr *hdr)
179 {
180 	unsigned int size;
181 	mutex_lock(&hdr->block_mutex);
182 	size = hdr->size - hdr->used;
183 	mutex_unlock(&hdr->block_mutex);
184 	return size;
185 }
186 
187 
188 EXPORT_SYMBOL(snd_util_memhdr_new);
189 EXPORT_SYMBOL(snd_util_memhdr_free);
190 EXPORT_SYMBOL(snd_util_mem_alloc);
191 EXPORT_SYMBOL(snd_util_mem_free);
192 EXPORT_SYMBOL(snd_util_mem_avail);
193 EXPORT_SYMBOL(__snd_util_mem_alloc);
194 EXPORT_SYMBOL(__snd_util_mem_free);
195 EXPORT_SYMBOL(__snd_util_memblk_new);
196