include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / drivers / media / video / videobuf-dma-contig.c
index 6109fb5..dce4f3a 100644 (file)
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
+#include <linux/pagemap.h>
 #include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
 #include <media/videobuf-dma-contig.h>
 
 struct videobuf_dma_contig_memory {
@@ -25,6 +28,7 @@ struct videobuf_dma_contig_memory {
        void *vaddr;
        dma_addr_t dma_handle;
        unsigned long size;
+       int is_userptr;
 };
 
 #define MAGIC_DC_MEM 0x0733ac61
@@ -103,11 +107,89 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
        }
 }
 
-static struct vm_operations_struct videobuf_vm_ops = {
+static const struct vm_operations_struct videobuf_vm_ops = {
        .open     = videobuf_vm_open,
        .close    = videobuf_vm_close,
 };
 
+/**
+ * videobuf_dma_contig_user_put() - reset pointer to user space buffer
+ * @mem: per-buffer private videobuf-dma-contig data
+ *
+ * This function resets the user space pointer
+ */
+static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
+{
+       mem->is_userptr = 0;
+       mem->dma_handle = 0;
+       mem->size = 0;
+}
+
+/**
+ * videobuf_dma_contig_user_get() - setup user space memory pointer
+ * @mem: per-buffer private videobuf-dma-contig data
+ * @vb: video buffer to map
+ *
+ * This function validates and sets up a pointer to user space memory.
+ * Only physically contiguous pfn-mapped memory is accepted.
+ *
+ * Returns 0 if successful.
+ */
+static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
+                                       struct videobuf_buffer *vb)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       unsigned long prev_pfn, this_pfn;
+       unsigned long pages_done, user_address;
+       unsigned int offset;
+       int ret;
+
+       offset = vb->baddr & ~PAGE_MASK;
+       mem->size = PAGE_ALIGN(vb->size + offset);
+       mem->is_userptr = 0;
+       ret = -EINVAL;
+
+       down_read(&mm->mmap_sem);
+
+       vma = find_vma(mm, vb->baddr);
+       if (!vma)
+               goto out_up;
+
+       if ((vb->baddr + mem->size) > vma->vm_end)
+               goto out_up;
+
+       pages_done = 0;
+       prev_pfn = 0; /* kill warning */
+       user_address = vb->baddr;
+
+       while (pages_done < (mem->size >> PAGE_SHIFT)) {
+               ret = follow_pfn(vma, user_address, &this_pfn);
+               if (ret)
+                       break;
+
+               if (pages_done == 0)
+                       mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
+               else if (this_pfn != (prev_pfn + 1))
+                       ret = -EFAULT;
+
+               if (ret)
+                       break;
+
+               prev_pfn = this_pfn;
+               user_address += PAGE_SIZE;
+               pages_done++;
+       }
+
+       if (!ret)
+               mem->is_userptr = 1;
+
+ out_up:
+       up_read(&current->mm->mmap_sem);
+
+       return ret;
+}
+
 static void *__videobuf_alloc(size_t size)
 {
        struct videobuf_dma_contig_memory *mem;
@@ -154,12 +236,11 @@ static int __videobuf_iolock(struct videobuf_queue *q,
        case V4L2_MEMORY_USERPTR:
                dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
 
-               /* The only USERPTR currently supported is the one needed for
-                  read() method.
-                */
+               /* handle pointer from user space */
                if (vb->baddr)
-                       return -EINVAL;
+                       return videobuf_dma_contig_user_get(mem, vb);
 
+               /* allocate memory for the read() method */
                mem->size = PAGE_ALIGN(vb->size);
                mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
                                                &mem->dma_handle, GFP_KERNEL);
@@ -182,19 +263,6 @@ static int __videobuf_iolock(struct videobuf_queue *q,
        return 0;
 }
 
-static int __videobuf_sync(struct videobuf_queue *q,
-                          struct videobuf_buffer *buf)
-{
-       struct videobuf_dma_contig_memory *mem = buf->priv;
-
-       BUG_ON(!mem);
-       MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
-
-       dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size,
-                               DMA_FROM_DEVICE);
-       return 0;
-}
-
 static int __videobuf_mmap_free(struct videobuf_queue *q)
 {
        unsigned int i;
@@ -356,7 +424,6 @@ static struct videobuf_qtype_ops qops = {
 
        .alloc        = __videobuf_alloc,
        .iolock       = __videobuf_iolock,
-       .sync         = __videobuf_sync,
        .mmap_free    = __videobuf_mmap_free,
        .mmap_mapper  = __videobuf_mmap_mapper,
        .video_copy_to_user = __videobuf_copy_to_user,
@@ -365,7 +432,7 @@ static struct videobuf_qtype_ops qops = {
 };
 
 void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
-                                   struct videobuf_queue_ops *ops,
+                                   const struct videobuf_queue_ops *ops,
                                    struct device *dev,
                                    spinlock_t *irqlock,
                                    enum v4l2_buf_type type,
@@ -400,7 +467,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q,
           So, it should free memory only if the memory were allocated for
           read() operation.
         */
-       if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
+       if (buf->memory != V4L2_MEMORY_USERPTR)
                return;
 
        if (!mem)
@@ -408,6 +475,13 @@ void videobuf_dma_contig_free(struct videobuf_queue *q,
 
        MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
 
+       /* handle user space pointer case */
+       if (buf->baddr) {
+               videobuf_dma_contig_user_put(mem);
+               return;
+       }
+
+       /* read() method */
        dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle);
        mem->vaddr = NULL;
 }