V4L/DVB (8703): gspca: Do controls work for spca561 revision 12a.
[safe/jmp/linux-2.6] / drivers / media / video / videobuf-vmalloc.c
index 075acdf..be65a2f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * helper functions for vmalloc video4linux capture buffers
  *
- * The functions expect the hardware being able to scatter gatter
+ * The functions expect the hardware being able to scatter gather
  * (i.e. the buffers are not linear in physical memory, but fragmented
  * into PAGE_SIZE chunks).  They also assume the driver does not need
  * to touch the video data.
@@ -57,20 +57,26 @@ videobuf_vm_open(struct vm_area_struct *vma)
        map->count++;
 }
 
-static void
-videobuf_vm_close(struct vm_area_struct *vma)
+static void videobuf_vm_close(struct vm_area_struct *vma)
 {
        struct videobuf_mapping *map = vma->vm_private_data;
        struct videobuf_queue *q = map->q;
        int i;
 
-       dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n",map,
-               map->count,vma->vm_start,vma->vm_end);
+       dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n", map,
+               map->count, vma->vm_start, vma->vm_end);
 
        map->count--;
        if (0 == map->count) {
-               dprintk(1,"munmap %p q=%p\n",map,q);
+               struct videobuf_vmalloc_memory *mem;
+
+               dprintk(1, "munmap %p q=%p\n", map, q);
                mutex_lock(&q->vb_lock);
+
+               /* We need first to cancel streams, before unmapping */
+               if (q->streaming)
+                       videobuf_queue_cancel(q);
+
                for (i = 0; i < VIDEO_MAX_FRAME; i++) {
                        if (NULL == q->bufs[i])
                                continue;
@@ -78,12 +84,35 @@ videobuf_vm_close(struct vm_area_struct *vma)
                        if (q->bufs[i]->map != map)
                                continue;
 
+                       mem = q->bufs[i]->priv;
+                       if (mem) {
+                               /* This callback is called only if kernel has
+                                  allocated memory and this memory is mmapped.
+                                  In this case, memory should be freed,
+                                  in order to do memory unmap.
+                                */
+
+                               MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
+
+                               /* vfree is not atomic - can't be
+                                  called with IRQ's disabled
+                                */
+                               dprintk(1, "%s: buf[%d] freeing (%p)\n",
+                                       __func__, i, mem->vmalloc);
+
+                               vfree(mem->vmalloc);
+                               mem->vmalloc = NULL;
+                       }
+
                        q->bufs[i]->map   = NULL;
                        q->bufs[i]->baddr = 0;
                }
-               mutex_unlock(&q->vb_lock);
+
                kfree(map);
+
+               mutex_unlock(&q->vb_lock);
        }
+
        return;
 }
 
@@ -125,6 +154,7 @@ static int __videobuf_iolock (struct videobuf_queue* q,
                              struct v4l2_framebuffer *fbuf)
 {
        struct videobuf_vmalloc_memory *mem = vb->priv;
+       int pages;
 
        BUG_ON(!mem);
 
@@ -141,8 +171,7 @@ static int __videobuf_iolock (struct videobuf_queue* q,
                }
                break;
        case V4L2_MEMORY_USERPTR:
-       {
-               int pages = PAGE_ALIGN(vb->size);
+               pages = PAGE_ALIGN(vb->size);
 
                dprintk(1, "%s memory method USERPTR\n", __func__);
 
@@ -174,7 +203,7 @@ static int __videobuf_iolock (struct videobuf_queue* q,
                        return 0;
 
                /* FIXME: to properly support USERPTR, remap should occur.
-                  The code bellow won't work, since mem->vma = NULL
+                  The code below won't work, since mem->vma = NULL
                 */
                /* Try to remap memory */
                rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0);
@@ -185,7 +214,6 @@ static int __videobuf_iolock (struct videobuf_queue* q,
 #endif
 
                break;
-       }
        case V4L2_MEMORY_OVERLAY:
        default:
                dprintk(1, "%s memory method OVERLAY/unknown\n", __func__);
@@ -359,6 +387,7 @@ static struct videobuf_qtype_ops qops = {
        .mmap_mapper  = __videobuf_mmap_mapper,
        .video_copy_to_user = __videobuf_copy_to_user,
        .copy_stream  = __videobuf_copy_stream,
+       .vmalloc      = videobuf_to_vmalloc,
 };
 
 void videobuf_queue_vmalloc_init(struct videobuf_queue* q,
@@ -390,6 +419,15 @@ void videobuf_vmalloc_free (struct videobuf_buffer *buf)
 {
        struct videobuf_vmalloc_memory *mem = buf->priv;
 
+       /* mmapped memory can't be freed here, otherwise mmapped region
+          would be released, while still needed. In this case, the memory
+          release should happen inside videobuf_vm_close().
+          So, it should free memory only if the memory were allocated for
+          read() operation.
+        */
+       if ((buf->memory != V4L2_MEMORY_USERPTR) || (buf->baddr == 0))
+               return;
+
        if (!mem)
                return;
 
@@ -398,10 +436,6 @@ void videobuf_vmalloc_free (struct videobuf_buffer *buf)
        vfree(mem->vmalloc);
        mem->vmalloc = NULL;
 
-
-
-       /* FIXME: need to do buf->priv = NULL? */
-
        return;
 }
 EXPORT_SYMBOL_GPL(videobuf_vmalloc_free);