b99343616f81a3d26f113df1c87c5fac3da778fa
[pandora-kernel.git] / sound / core / memory.c
1 /*
2  *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
3  * 
4  *  Memory allocation helpers.
5  *
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 <asm/io.h>
25 #include <asm/uaccess.h>
26 #include <linux/init.h>
27 #include <linux/slab.h>
28 #include <linux/time.h>
29 #include <linux/pci.h>
30 #include <sound/core.h>
31 #include <sound/info.h>
32
33 /*
34  *  memory allocation helpers and debug routines
35  */
36
37 #ifdef CONFIG_SND_DEBUG_MEMORY
38
39 struct snd_alloc_track {
40         unsigned long magic;
41         void *caller;
42         size_t size;
43         struct list_head list;
44         long data[0];
45 };
46
47 #define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data)
48
49 static long snd_alloc_kmalloc;
50 static LIST_HEAD(snd_alloc_kmalloc_list);
51 static DEFINE_SPINLOCK(snd_alloc_kmalloc_lock);
52 #define KMALLOC_MAGIC 0x87654321
53 static snd_info_entry_t *snd_memory_info_entry;
54
55 void __init snd_memory_init(void)
56 {
57         snd_alloc_kmalloc = 0;
58 }
59
60 void snd_memory_done(void)
61 {
62         struct list_head *head;
63         struct snd_alloc_track *t;
64
65         if (snd_alloc_kmalloc > 0)
66                 snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc);
67         list_for_each_prev(head, &snd_alloc_kmalloc_list) {
68                 t = list_entry(head, struct snd_alloc_track, list);
69                 if (t->magic != KMALLOC_MAGIC) {
70                         snd_printk(KERN_ERR "Corrupted kmalloc\n");
71                         break;
72                 }
73                 snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller);
74         }
75 }
76
77 static void *__snd_kmalloc(size_t size, gfp_t flags, void *caller)
78 {
79         unsigned long cpu_flags;
80         struct snd_alloc_track *t;
81         void *ptr;
82         
83         ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags);
84         if (ptr != NULL) {
85                 t = (struct snd_alloc_track *)ptr;
86                 t->magic = KMALLOC_MAGIC;
87                 t->caller = caller;
88                 spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags);
89                 list_add_tail(&t->list, &snd_alloc_kmalloc_list);
90                 spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags);
91                 t->size = size;
92                 snd_alloc_kmalloc += size;
93                 ptr = t->data;
94         }
95         return ptr;
96 }
97
98 #define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0));
99 void *snd_hidden_kmalloc(size_t size, gfp_t flags)
100 {
101         return _snd_kmalloc(size, flags);
102 }
103
104 void *snd_hidden_kzalloc(size_t size, gfp_t flags)
105 {
106         void *ret = _snd_kmalloc(size, flags);
107         if (ret)
108                 memset(ret, 0, size);
109         return ret;
110 }
111 EXPORT_SYMBOL(snd_hidden_kzalloc);
112
113 void *snd_hidden_kcalloc(size_t n, size_t size, gfp_t flags)
114 {
115         void *ret = NULL;
116         if (n != 0 && size > INT_MAX / n)
117                 return ret;
118         return snd_hidden_kzalloc(n * size, flags);
119 }
120
121 void snd_hidden_kfree(const void *obj)
122 {
123         unsigned long flags;
124         struct snd_alloc_track *t;
125         if (obj == NULL)
126                 return;
127         t = snd_alloc_track_entry(obj);
128         if (t->magic != KMALLOC_MAGIC) {
129                 snd_printk(KERN_WARNING "bad kfree (called from %p)\n", __builtin_return_address(0));
130                 return;
131         }
132         spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags);
133         list_del(&t->list);
134         spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags);
135         t->magic = 0;
136         snd_alloc_kmalloc -= t->size;
137         obj = t;
138         snd_wrapper_kfree(obj);
139 }
140
141 char *snd_hidden_kstrdup(const char *s, gfp_t flags)
142 {
143         int len;
144         char *buf;
145
146         if (!s) return NULL;
147
148         len = strlen(s) + 1;
149         buf = _snd_kmalloc(len, flags);
150         if (buf)
151                 memcpy(buf, s, len);
152         return buf;
153 }
154
155 static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
156 {
157         snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc);
158 }
159
160 int __init snd_memory_info_init(void)
161 {
162         snd_info_entry_t *entry;
163
164         entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL);
165         if (entry) {
166                 entry->c.text.read_size = 256;
167                 entry->c.text.read = snd_memory_info_read;
168                 if (snd_info_register(entry) < 0) {
169                         snd_info_free_entry(entry);
170                         entry = NULL;
171                 }
172         }
173         snd_memory_info_entry = entry;
174         return 0;
175 }
176
177 int __exit snd_memory_info_done(void)
178 {
179         if (snd_memory_info_entry)
180                 snd_info_unregister(snd_memory_info_entry);
181         return 0;
182 }
183
184 #endif /* CONFIG_SND_DEBUG_MEMORY */
185
186 /**
187  * copy_to_user_fromio - copy data from mmio-space to user-space
188  * @dst: the destination pointer on user-space
189  * @src: the source pointer on mmio
190  * @count: the data size to copy in bytes
191  *
192  * Copies the data from mmio-space to user-space.
193  *
194  * Returns zero if successful, or non-zero on failure.
195  */
196 int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count)
197 {
198 #if defined(__i386__) || defined(CONFIG_SPARC32)
199         return copy_to_user(dst, (const void __force*)src, count) ? -EFAULT : 0;
200 #else
201         char buf[256];
202         while (count) {
203                 size_t c = count;
204                 if (c > sizeof(buf))
205                         c = sizeof(buf);
206                 memcpy_fromio(buf, (void __iomem *)src, c);
207                 if (copy_to_user(dst, buf, c))
208                         return -EFAULT;
209                 count -= c;
210                 dst += c;
211                 src += c;
212         }
213         return 0;
214 #endif
215 }
216
217 /**
218  * copy_from_user_toio - copy data from user-space to mmio-space
219  * @dst: the destination pointer on mmio-space
220  * @src: the source pointer on user-space
221  * @count: the data size to copy in bytes
222  *
223  * Copies the data from user-space to mmio-space.
224  *
225  * Returns zero if successful, or non-zero on failure.
226  */
227 int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count)
228 {
229 #if defined(__i386__) || defined(CONFIG_SPARC32)
230         return copy_from_user((void __force *)dst, src, count) ? -EFAULT : 0;
231 #else
232         char buf[256];
233         while (count) {
234                 size_t c = count;
235                 if (c > sizeof(buf))
236                         c = sizeof(buf);
237                 if (copy_from_user(buf, src, c))
238                         return -EFAULT;
239                 memcpy_toio(dst, buf, c);
240                 count -= c;
241                 dst += c;
242                 src += c;
243         }
244         return 0;
245 #endif
246 }