Blame view

sound/core/memalloc.c 13.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
2
   *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   *                   Takashi Iwai <tiwai@suse.de>
   * 
   *  Generic memory allocators
   *
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
  #include <linux/module.h>
  #include <linux/proc_fs.h>
  #include <linux/init.h>
  #include <linux/pci.h>
  #include <linux/slab.h>
  #include <linux/mm.h>
ccec6e2c4   Takashi Iwai   Convert snd-page-...
29
  #include <linux/seq_file.h>
b6a969155   Takashi Iwai   [ALSA] Add write ...
30
  #include <asm/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
  #include <linux/dma-mapping.h>
  #include <linux/moduleparam.h>
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
33
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  #include <sound/memalloc.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35

c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
36
  MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
  MODULE_DESCRIPTION("Memory allocator for ALSA system.");
  MODULE_LICENSE("GPL");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
  /*
   */
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
41
  static DEFINE_MUTEX(list_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
46
47
48
49
50
51
52
  static LIST_HEAD(mem_list_head);
  
  /* buffer preservation list */
  struct snd_mem_list {
  	struct snd_dma_buffer buffer;
  	unsigned int id;
  	struct list_head list;
  };
  
  /* id for pre-allocated buffers */
  #define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
   *
   *  Generic memory allocators
   *
   */
  
  static long snd_allocated_pages; /* holding the number of allocated pages */
  
  static inline void inc_snd_pages(int order)
  {
  	snd_allocated_pages += 1 << order;
  }
  
  static inline void dec_snd_pages(int order)
  {
  	snd_allocated_pages -= 1 << order;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
76
77
78
  /**
   * snd_malloc_pages - allocate pages with the given size
   * @size: the size to allocate in bytes
   * @gfp_flags: the allocation conditions, GFP_XXX
   *
   * Allocates the physically contiguous pages with the given size.
   *
   * Returns the pointer of the buffer, or NULL if no enoguh memory.
   */
1ef64e670   Al Viro   [PATCH] gfp_t: sound
79
  void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
  {
  	int pg;
  	void *res;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
83
84
85
86
  	if (WARN_ON(!size))
  		return NULL;
  	if (WARN_ON(!gfp_flags))
  		return NULL;
f3d48f037   Hugh Dickins   [PATCH] unpaged: ...
87
  	gfp_flags |= __GFP_COMP;	/* compound page lets parts be mapped */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  	pg = get_order(size);
2ba8c15c7   Takashi Iwai   [ALSA] Removed un...
89
  	if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  		inc_snd_pages(pg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  	return res;
  }
  
  /**
   * snd_free_pages - release the pages
   * @ptr: the buffer pointer to release
   * @size: the allocated buffer size
   *
   * Releases the buffer allocated via snd_malloc_pages().
   */
  void snd_free_pages(void *ptr, size_t size)
  {
  	int pg;
  
  	if (ptr == NULL)
  		return;
  	pg = get_order(size);
  	dec_snd_pages(pg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
115
116
  	free_pages((unsigned long) ptr, pg);
  }
  
  /*
   *
   *  Bus-specific memory allocators
   *
   */
8f11551b1   Takashi Iwai   [ALSA] Fix build ...
117
  #ifdef CONFIG_HAS_DMA
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
  /* allocate the coherent DMA pages */
  static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
  {
  	int pg;
  	void *res;
1ef64e670   Al Viro   [PATCH] gfp_t: sound
123
  	gfp_t gfp_flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
125
126
  	if (WARN_ON(!dma))
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
  	pg = get_order(size);
  	gfp_flags = GFP_KERNEL
f3d48f037   Hugh Dickins   [PATCH] unpaged: ...
129
  		| __GFP_COMP	/* compound page lets parts be mapped */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
  		| __GFP_NORETRY /* don't trigger OOM-killer */
  		| __GFP_NOWARN; /* no stack trace print - this call is non-critical */
  	res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
2ba8c15c7   Takashi Iwai   [ALSA] Removed un...
133
  	if (res != NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
  		inc_snd_pages(pg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  
  	return res;
  }
  
  /* free the coherent DMA pages */
  static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
  			       dma_addr_t dma)
  {
  	int pg;
  
  	if (ptr == NULL)
  		return;
  	pg = get_order(size);
  	dec_snd_pages(pg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
  	dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
  }
8f11551b1   Takashi Iwai   [ALSA] Fix build ...
151
  #endif /* CONFIG_HAS_DMA */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  /*
   *
   *  ALSA generic memory management
   *
   */
  
  
  /**
   * snd_dma_alloc_pages - allocate the buffer area according to the given type
   * @type: the DMA buffer type
   * @device: the device pointer
   * @size: the buffer size to allocate
   * @dmab: buffer allocation record to store the allocated data
   *
   * Calls the memory-allocator function for the corresponding
   * buffer type.
   * 
   * Returns zero if the buffer with the given size is allocated successfuly,
   * other a negative value at error.
   */
  int snd_dma_alloc_pages(int type, struct device *device, size_t size,
  			struct snd_dma_buffer *dmab)
  {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
176
177
178
179
  	if (WARN_ON(!size))
  		return -ENXIO;
  	if (WARN_ON(!dmab))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
183
184
185
  
  	dmab->dev.type = type;
  	dmab->dev.dev = device;
  	dmab->bytes = 0;
  	switch (type) {
  	case SNDRV_DMA_TYPE_CONTINUOUS:
fea952e5c   Clemens Ladisch   ALSA: core: spars...
186
187
  		dmab->area = snd_malloc_pages(size,
  					(__force gfp_t)(unsigned long)device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
  		dmab->addr = 0;
  		break;
8f11551b1   Takashi Iwai   [ALSA] Fix build ...
190
  #ifdef CONFIG_HAS_DMA
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
  	case SNDRV_DMA_TYPE_DEV:
  		dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
  		break;
cc6a8acde   Takashi Iwai   ALSA: Fix SG-buff...
194
195
  #endif
  #ifdef CONFIG_SND_DMA_SGBUF
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
  	case SNDRV_DMA_TYPE_DEV_SG:
  		snd_malloc_sgbuf_pages(device, size, dmab, NULL);
  		break;
8f11551b1   Takashi Iwai   [ALSA] Fix build ...
199
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
  	default:
  		printk(KERN_ERR "snd-malloc: invalid device type %d
  ", type);
  		dmab->area = NULL;
  		dmab->addr = 0;
  		return -ENXIO;
  	}
  	if (! dmab->area)
  		return -ENOMEM;
  	dmab->bytes = size;
  	return 0;
  }
  
  /**
   * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
   * @type: the DMA buffer type
   * @device: the device pointer
   * @size: the buffer size to allocate
   * @dmab: buffer allocation record to store the allocated data
   *
   * Calls the memory-allocator function for the corresponding
   * buffer type.  When no space is left, this function reduces the size and
   * tries to allocate again.  The size actually allocated is stored in
   * res_size argument.
   * 
   * Returns zero if the buffer with the given size is allocated successfuly,
   * other a negative value at error.
   */
  int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
  				 struct snd_dma_buffer *dmab)
  {
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  	while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
4e184f8fc   Takashi Iwai   ALSA: Fix allocat...
233
  		size_t aligned_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
  		if (err != -ENOMEM)
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
  		if (size <= PAGE_SIZE)
  			return -ENOMEM;
4e184f8fc   Takashi Iwai   ALSA: Fix allocat...
238
239
240
241
242
  		aligned_size = PAGE_SIZE << get_order(size);
  		if (size != aligned_size)
  			size = aligned_size;
  		else
  			size >>= 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  	}
  	if (! dmab->area)
  		return -ENOMEM;
  	return 0;
  }
  
  
  /**
   * snd_dma_free_pages - release the allocated buffer
   * @dmab: the buffer allocation record to release
   *
   * Releases the allocated buffer via snd_dma_alloc_pages().
   */
  void snd_dma_free_pages(struct snd_dma_buffer *dmab)
  {
  	switch (dmab->dev.type) {
  	case SNDRV_DMA_TYPE_CONTINUOUS:
  		snd_free_pages(dmab->area, dmab->bytes);
  		break;
8f11551b1   Takashi Iwai   [ALSA] Fix build ...
262
  #ifdef CONFIG_HAS_DMA
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
  	case SNDRV_DMA_TYPE_DEV:
  		snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
  		break;
cc6a8acde   Takashi Iwai   ALSA: Fix SG-buff...
266
267
  #endif
  #ifdef CONFIG_SND_DMA_SGBUF
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
269
270
  	case SNDRV_DMA_TYPE_DEV_SG:
  		snd_free_sgbuf_pages(dmab);
  		break;
8f11551b1   Takashi Iwai   [ALSA] Fix build ...
271
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
  	default:
  		printk(KERN_ERR "snd-malloc: invalid device type %d
  ", dmab->dev.type);
  	}
  }
  
  
  /**
   * snd_dma_get_reserved - get the reserved buffer for the given device
   * @dmab: the buffer allocation record to store
   * @id: the buffer id
   *
   * Looks for the reserved-buffer list and re-uses if the same buffer
   * is found in the list.  When the buffer is found, it's removed from the free list.
   *
   * Returns the size of buffer if the buffer is found, or zero if not found.
   */
  size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
  	struct snd_mem_list *mem;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
292
293
  	if (WARN_ON(!dmab))
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294

1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
295
  	mutex_lock(&list_mutex);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
296
  	list_for_each_entry(mem, &mem_list_head, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
  		if (mem->id == id &&
b6a969155   Takashi Iwai   [ALSA] Add write ...
298
299
300
  		    (mem->buffer.dev.dev == NULL || dmab->dev.dev == NULL ||
  		     ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev)))) {
  			struct device *dev = dmab->dev.dev;
9244b2c30   Johannes Berg   [ALSA] alsa core:...
301
  			list_del(&mem->list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
  			*dmab = mem->buffer;
b6a969155   Takashi Iwai   [ALSA] Add write ...
303
304
  			if (dmab->dev.dev == NULL)
  				dmab->dev.dev = dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
  			kfree(mem);
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
306
  			mutex_unlock(&list_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
  			return dmab->bytes;
  		}
  	}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
310
  	mutex_unlock(&list_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
  	return 0;
  }
  
  /**
   * snd_dma_reserve_buf - reserve the buffer
   * @dmab: the buffer to reserve
   * @id: the buffer id
   *
   * Reserves the given buffer as a reserved buffer.
   * 
   * Returns zero if successful, or a negative code at error.
   */
  int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)
  {
  	struct snd_mem_list *mem;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
326
327
  	if (WARN_ON(!dmab))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
  	mem = kmalloc(sizeof(*mem), GFP_KERNEL);
  	if (! mem)
  		return -ENOMEM;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
331
  	mutex_lock(&list_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
334
  	mem->buffer = *dmab;
  	mem->id = id;
  	list_add_tail(&mem->list, &mem_list_head);
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
335
  	mutex_unlock(&list_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
340
341
342
343
344
345
  	return 0;
  }
  
  /*
   * purge all reserved buffers
   */
  static void free_all_reserved_pages(void)
  {
  	struct list_head *p;
  	struct snd_mem_list *mem;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
346
  	mutex_lock(&list_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
350
351
352
353
  	while (! list_empty(&mem_list_head)) {
  		p = mem_list_head.next;
  		mem = list_entry(p, struct snd_mem_list, list);
  		list_del(p);
  		snd_dma_free_pages(&mem->buffer);
  		kfree(mem);
  	}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
354
  	mutex_unlock(&list_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
359
  #ifdef CONFIG_PROC_FS
  /*
   * proc file interface
   */
b6a969155   Takashi Iwai   [ALSA] Add write ...
360
  #define SND_MEM_PROC_FILE	"driver/snd-page-alloc"
a53fc188e   Clemens Ladisch   [ALSA] make local...
361
  static struct proc_dir_entry *snd_mem_proc;
b6a969155   Takashi Iwai   [ALSA] Add write ...
362

ccec6e2c4   Takashi Iwai   Convert snd-page-...
363
  static int snd_mem_proc_read(struct seq_file *seq, void *offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
  	long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
  	struct snd_mem_list *mem;
  	int devno;
759ee81be   David S. Miller   alsa: Remove spec...
368
  	static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG" };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369

1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
370
  	mutex_lock(&list_mutex);
ccec6e2c4   Takashi Iwai   Convert snd-page-...
371
372
373
  	seq_printf(seq, "pages  : %li bytes (%li pages per %likB)
  ",
  		   pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
  	devno = 0;
9244b2c30   Johannes Berg   [ALSA] alsa core:...
375
  	list_for_each_entry(mem, &mem_list_head, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
  		devno++;
ccec6e2c4   Takashi Iwai   Convert snd-page-...
377
378
379
380
381
382
383
  		seq_printf(seq, "buffer %d : ID %08x : type %s
  ",
  			   devno, mem->id, types[mem->buffer.dev.type]);
  		seq_printf(seq, "  addr = 0x%lx, size = %d bytes
  ",
  			   (unsigned long)mem->buffer.addr,
  			   (int)mem->buffer.bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  	}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
385
  	mutex_unlock(&list_mutex);
ccec6e2c4   Takashi Iwai   Convert snd-page-...
386
387
388
389
390
391
  	return 0;
  }
  
  static int snd_mem_proc_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, snd_mem_proc_read, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
  }
b6a969155   Takashi Iwai   [ALSA] Add write ...
393
394
395
396
397
  
  /* FIXME: for pci only - other bus? */
  #ifdef CONFIG_PCI
  #define gettoken(bufp) strsep(bufp, " \t
  ")
ccec6e2c4   Takashi Iwai   Convert snd-page-...
398
399
  static ssize_t snd_mem_proc_write(struct file *file, const char __user * buffer,
  				  size_t count, loff_t * ppos)
b6a969155   Takashi Iwai   [ALSA] Add write ...
400
401
402
  {
  	char buf[128];
  	char *token, *p;
ccec6e2c4   Takashi Iwai   Convert snd-page-...
403
404
  	if (count > sizeof(buf) - 1)
  		return -EINVAL;
b6a969155   Takashi Iwai   [ALSA] Add write ...
405
406
  	if (copy_from_user(buf, buffer, count))
  		return -EFAULT;
ccec6e2c4   Takashi Iwai   Convert snd-page-...
407
  	buf[count] = '\0';
b6a969155   Takashi Iwai   [ALSA] Add write ...
408
409
410
411
  
  	p = buf;
  	token = gettoken(&p);
  	if (! token || *token == '#')
ccec6e2c4   Takashi Iwai   Convert snd-page-...
412
  		return count;
b6a969155   Takashi Iwai   [ALSA] Add write ...
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
  	if (strcmp(token, "add") == 0) {
  		char *endp;
  		int vendor, device, size, buffers;
  		long mask;
  		int i, alloced;
  		struct pci_dev *pci;
  
  		if ((token = gettoken(&p)) == NULL ||
  		    (vendor = simple_strtol(token, NULL, 0)) <= 0 ||
  		    (token = gettoken(&p)) == NULL ||
  		    (device = simple_strtol(token, NULL, 0)) <= 0 ||
  		    (token = gettoken(&p)) == NULL ||
  		    (mask = simple_strtol(token, NULL, 0)) < 0 ||
  		    (token = gettoken(&p)) == NULL ||
  		    (size = memparse(token, &endp)) < 64*1024 ||
  		    size > 16*1024*1024 /* too big */ ||
  		    (token = gettoken(&p)) == NULL ||
  		    (buffers = simple_strtol(token, NULL, 0)) <= 0 ||
  		    buffers > 4) {
  			printk(KERN_ERR "snd-page-alloc: invalid proc write format
  ");
ccec6e2c4   Takashi Iwai   Convert snd-page-...
434
  			return count;
b6a969155   Takashi Iwai   [ALSA] Add write ...
435
436
437
438
439
440
  		}
  		vendor &= 0xffff;
  		device &= 0xffff;
  
  		alloced = 0;
  		pci = NULL;
0dd119f70   Jiri Slaby   [ALSA] pci_find_d...
441
  		while ((pci = pci_get_device(vendor, device, pci)) != NULL) {
b6a969155   Takashi Iwai   [ALSA] Add write ...
442
443
444
445
446
  			if (mask > 0 && mask < 0xffffffff) {
  				if (pci_set_dma_mask(pci, mask) < 0 ||
  				    pci_set_consistent_dma_mask(pci, mask) < 0) {
  					printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x
  ", mask, vendor, device);
df1deb675   Julia Lawall   [ALSA] sound/core...
447
  					pci_dev_put(pci);
ccec6e2c4   Takashi Iwai   Convert snd-page-...
448
  					return count;
b6a969155   Takashi Iwai   [ALSA] Add write ...
449
450
451
452
453
454
455
456
457
  				}
  			}
  			for (i = 0; i < buffers; i++) {
  				struct snd_dma_buffer dmab;
  				memset(&dmab, 0, sizeof(dmab));
  				if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
  							size, &dmab) < 0) {
  					printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)
  ", size);
0dd119f70   Jiri Slaby   [ALSA] pci_find_d...
458
  					pci_dev_put(pci);
ccec6e2c4   Takashi Iwai   Convert snd-page-...
459
  					return count;
b6a969155   Takashi Iwai   [ALSA] Add write ...
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
  				}
  				snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
  			}
  			alloced++;
  		}
  		if (! alloced) {
  			for (i = 0; i < buffers; i++) {
  				struct snd_dma_buffer dmab;
  				memset(&dmab, 0, sizeof(dmab));
  				/* FIXME: We can allocate only in ZONE_DMA
  				 * without a device pointer!
  				 */
  				if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, NULL,
  							size, &dmab) < 0) {
  					printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)
  ", size);
  					break;
  				}
  				snd_dma_reserve_buf(&dmab, (unsigned int)((vendor << 16) | device));
  			}
  		}
  	} else if (strcmp(token, "erase") == 0)
  		/* FIXME: need for releasing each buffer chunk? */
  		free_all_reserved_pages();
  	else
  		printk(KERN_ERR "snd-page-alloc: invalid proc cmd
  ");
ccec6e2c4   Takashi Iwai   Convert snd-page-...
487
  	return count;
b6a969155   Takashi Iwai   [ALSA] Add write ...
488
489
  }
  #endif /* CONFIG_PCI */
ccec6e2c4   Takashi Iwai   Convert snd-page-...
490
491
492
493
494
495
496
497
498
499
500
  
  static const struct file_operations snd_mem_proc_fops = {
  	.owner		= THIS_MODULE,
  	.open		= snd_mem_proc_open,
  	.read		= seq_read,
  #ifdef CONFIG_PCI
  	.write		= snd_mem_proc_write,
  #endif
  	.llseek		= seq_lseek,
  	.release	= single_release,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
505
506
507
508
509
  #endif /* CONFIG_PROC_FS */
  
  /*
   * module entry
   */
  
  static int __init snd_mem_init(void)
  {
  #ifdef CONFIG_PROC_FS
7bf4e6d3e   Denis V. Lunev   sound: use non-ra...
510
511
  	snd_mem_proc = proc_create(SND_MEM_PROC_FILE, 0644, NULL,
  				   &snd_mem_proc_fops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
516
517
  	return 0;
  }
  
  static void __exit snd_mem_exit(void)
  {
e0be4d32b   Takashi Iwai   [ALSA] Fix compil...
518
  	remove_proc_entry(SND_MEM_PROC_FILE, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
  	free_all_reserved_pages();
  	if (snd_allocated_pages > 0)
  		printk(KERN_ERR "snd-malloc: Memory leak?  pages not freed = %li
  ", snd_allocated_pages);
  }
  
  
  module_init(snd_mem_init)
  module_exit(snd_mem_exit)
  
  
  /*
   * exports
   */
  EXPORT_SYMBOL(snd_dma_alloc_pages);
  EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
  EXPORT_SYMBOL(snd_dma_free_pages);
  
  EXPORT_SYMBOL(snd_dma_get_reserved_buf);
  EXPORT_SYMBOL(snd_dma_reserve_buf);
  
  EXPORT_SYMBOL(snd_malloc_pages);
  EXPORT_SYMBOL(snd_free_pages);