linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / sound / core / memalloc.c
index cc0b602..19b3dcb 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
+#include <asm/uaccess.h>
 #include <linux/dma-mapping.h>
 #include <linux/moduleparam.h>
 #include <asm/semaphore.h>
@@ -42,18 +43,6 @@ MODULE_DESCRIPTION("Memory allocator for ALSA system.");
 MODULE_LICENSE("GPL");
 
 
-#ifndef SNDRV_CARDS
-#define SNDRV_CARDS    8
-#endif
-
-/* FIXME: so far only some PCI devices have the preallocation table */
-#ifdef CONFIG_PCI
-static int enable[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
-static int boot_devs;
-module_param_array(enable, bool, boot_devs, 0444);
-MODULE_PARM_DESC(enable, "Enable cards to allocate buffers.");
-#endif
-
 /*
  */
 
@@ -112,7 +101,8 @@ struct snd_mem_list {
  */
 
 static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size,
-                                        dma_addr_t *dma_handle, int flags)
+                                        dma_addr_t *dma_handle,
+                                        gfp_t flags)
 {
        void *ret;
        u64 dma_mask, coherent_dma_mask;
@@ -196,13 +186,14 @@ static void unmark_pages(struct page *page, int order)
  *
  * Returns the pointer of the buffer, or NULL if no enoguh memory.
  */
-void *snd_malloc_pages(size_t size, unsigned int gfp_flags)
+void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
 {
        int pg;
        void *res;
 
        snd_assert(size > 0, return NULL);
        snd_assert(gfp_flags != 0, return NULL);
+       gfp_flags |= __GFP_COMP;        /* compound page lets parts be mapped */
        pg = get_order(size);
        if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) {
                mark_pages(virt_to_page(res), pg);
@@ -241,14 +232,15 @@ static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *d
 {
        int pg;
        void *res;
-       unsigned int gfp_flags;
+       gfp_t gfp_flags;
 
        snd_assert(size > 0, return NULL);
        snd_assert(dma != NULL, return NULL);
        pg = get_order(size);
-       gfp_flags = GFP_KERNEL;
-       if (pg > 0)
-               gfp_flags |= __GFP_NOWARN;
+       gfp_flags = GFP_KERNEL
+               | __GFP_COMP    /* compound page lets parts be mapped */
+               | __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);
        if (res != NULL) {
 #ifdef NEED_RESERVE_PAGES
@@ -452,9 +444,13 @@ size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
        list_for_each(p, &mem_list_head) {
                mem = list_entry(p, struct snd_mem_list, list);
                if (mem->id == id &&
-                   ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev))) {
+                   (mem->buffer.dev.dev == NULL || dmab->dev.dev == NULL ||
+                    ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev)))) {
+                       struct device *dev = dmab->dev.dev;
                        list_del(p);
                        *dmab = mem->buffer;
+                       if (dmab->dev.dev == NULL)
+                               dmab->dev.dev = dev;
                        kfree(mem);
                        up(&list_mutex);
                        return dmab->bytes;
@@ -509,91 +505,13 @@ static void free_all_reserved_pages(void)
 }
 
 
-
-/*
- * allocation of buffers for pre-defined devices
- */
-
-#ifdef CONFIG_PCI
-/* FIXME: for pci only - other bus? */
-struct prealloc_dev {
-       unsigned short vendor;
-       unsigned short device;
-       unsigned long dma_mask;
-       unsigned int size;
-       unsigned int buffers;
-};
-
-#define HAMMERFALL_BUFFER_SIZE    (16*1024*4*(26+1)+0x10000)
-
-static struct prealloc_dev prealloc_devices[] __initdata = {
-       {
-               /* hammerfall */
-               .vendor = 0x10ee,
-               .device = 0x3fc4,
-               .dma_mask = 0xffffffff,
-               .size = HAMMERFALL_BUFFER_SIZE,
-               .buffers = 2
-       },
-       {
-               /* HDSP */
-               .vendor = 0x10ee,
-               .device = 0x3fc5,
-               .dma_mask = 0xffffffff,
-               .size = HAMMERFALL_BUFFER_SIZE,
-               .buffers = 2
-       },
-       { }, /* terminator */
-};
-
-static void __init preallocate_cards(void)
-{
-       struct pci_dev *pci = NULL;
-       int card;
-
-       card = 0;
-
-       while ((pci = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci)) != NULL) {
-               struct prealloc_dev *dev;
-               unsigned int i;
-               if (card >= SNDRV_CARDS)
-                       break;
-               for (dev = prealloc_devices; dev->vendor; dev++) {
-                       if (dev->vendor == pci->vendor && dev->device == pci->device)
-                               break;
-               }
-               if (! dev->vendor)
-                       continue;
-               if (! enable[card++]) {
-                       printk(KERN_DEBUG "snd-page-alloc: skipping card %d, device %04x:%04x\n", card, pci->vendor, pci->device);
-                       continue;
-               }
-                       
-               if (pci_set_dma_mask(pci, dev->dma_mask) < 0 ||
-                   pci_set_consistent_dma_mask(pci, dev->dma_mask) < 0) {
-                       printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", dev->dma_mask, dev->vendor, dev->device);
-                       continue;
-               }
-               for (i = 0; i < dev->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),
-                                               dev->size, &dmab) < 0)
-                               printk(KERN_WARNING "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", dev->size);
-                       else
-                               snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
-               }
-       }
-}
-#else
-#define preallocate_cards()    /* NOP */
-#endif
-
-
 #ifdef CONFIG_PROC_FS
 /*
  * proc file interface
  */
+#define SND_MEM_PROC_FILE      "driver/snd-page-alloc"
+static struct proc_dir_entry *snd_mem_proc;
+
 static int snd_mem_proc_read(char *page, char **start, off_t off,
                             int count, int *eof, void *data)
 {
@@ -622,6 +540,98 @@ static int snd_mem_proc_read(char *page, char **start, off_t off,
        up(&list_mutex);
        return len;
 }
+
+/* FIXME: for pci only - other bus? */
+#ifdef CONFIG_PCI
+#define gettoken(bufp) strsep(bufp, " \t\n")
+
+static int snd_mem_proc_write(struct file *file, const char __user *buffer,
+                             unsigned long count, void *data)
+{
+       char buf[128];
+       char *token, *p;
+
+       if (count > ARRAY_SIZE(buf) - 1)
+               count = ARRAY_SIZE(buf) - 1;
+       if (copy_from_user(buf, buffer, count))
+               return -EFAULT;
+       buf[ARRAY_SIZE(buf) - 1] = '\0';
+
+       p = buf;
+       token = gettoken(&p);
+       if (! token || *token == '#')
+               return (int)count;
+       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\n");
+                       return (int)count;
+               }
+               vendor &= 0xffff;
+               device &= 0xffff;
+
+               alloced = 0;
+               pci = NULL;
+               while ((pci = pci_get_device(vendor, device, pci)) != NULL) {
+                       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\n", mask, vendor, device);
+                                       return (int)count;
+                               }
+                       }
+                       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)\n", size);
+                                       pci_dev_put(pci);
+                                       return (int)count;
+                               }
+                               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)\n", 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\n");
+       return (int)count;
+}
+#endif /* CONFIG_PCI */
 #endif /* CONFIG_PROC_FS */
 
 /*
@@ -631,15 +641,20 @@ static int snd_mem_proc_read(char *page, char **start, off_t off,
 static int __init snd_mem_init(void)
 {
 #ifdef CONFIG_PROC_FS
-       create_proc_read_entry("driver/snd-page-alloc", 0, NULL, snd_mem_proc_read, NULL);
+       snd_mem_proc = create_proc_entry(SND_MEM_PROC_FILE, 0644, NULL);
+       if (snd_mem_proc) {
+               snd_mem_proc->read_proc = snd_mem_proc_read;
+#ifdef CONFIG_PCI
+               snd_mem_proc->write_proc = snd_mem_proc_write;
+#endif
+       }
 #endif
-       preallocate_cards();
        return 0;
 }
 
 static void __exit snd_mem_exit(void)
 {
-       remove_proc_entry("driver/snd-page-alloc", NULL);
+       remove_proc_entry(SND_MEM_PROC_FILE, NULL);
        free_all_reserved_pages();
        if (snd_allocated_pages > 0)
                printk(KERN_ERR "snd-malloc: Memory leak?  pages not freed = %li\n", snd_allocated_pages);