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