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 28MODULE_AUTHOR("Takashi Iwai"); 29MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation"); 30MODULE_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 */ 37struct snd_util_memhdr * 38snd_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 */ 55void 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 */ 72struct 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 */ 109struct 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 */ 137struct snd_util_memblk * 138snd_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 */ 152void 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 */ 164int 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 */ 178int 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 188EXPORT_SYMBOL(snd_util_memhdr_new); 189EXPORT_SYMBOL(snd_util_memhdr_free); 190EXPORT_SYMBOL(snd_util_mem_alloc); 191EXPORT_SYMBOL(snd_util_mem_free); 192EXPORT_SYMBOL(snd_util_mem_avail); 193EXPORT_SYMBOL(__snd_util_mem_alloc); 194EXPORT_SYMBOL(__snd_util_mem_free); 195EXPORT_SYMBOL(__snd_util_memblk_new); 196 197/* 198 * INIT part 199 */ 200 201static int __init alsa_util_mem_init(void) 202{ 203 return 0; 204} 205 206static void __exit alsa_util_mem_exit(void) 207{ 208} 209 210module_init(alsa_util_mem_init) 211module_exit(alsa_util_mem_exit) 212