vfb: make virtual framebuffer mmapable
Changed things:
1. vmalloc()/vfree() replaced with rvmalloc()/rvfree() (taken from
drivers/media/video/se401.c)
2. mmap method implemented (mostly taken from drivers/media/video/se401.c)
3. smem_start and smem_len fields of struct fb_fix_screeninfo initialized.
(smem_start initialized with virtual address, don't know if it is really
bad...)
[adaplas: sparse warning fix]
Signed-off-by: Antonino Daplas <adaplas@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c
index 02cfc6a..072638a 100644
--- a/drivers/video/vfb.c
+++ b/drivers/video/vfb.c
@@ -37,6 +37,48 @@
static u_long videomemorysize = VIDEOMEMSIZE;
module_param(videomemorysize, ulong, 0);
+/**********************************************************************
+ *
+ * Memory management
+ *
+ **********************************************************************/
+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
static struct fb_var_screeninfo vfb_default __initdata = {
.xres = 640,
.yres = 480,
@@ -371,7 +413,33 @@
static int vfb_mmap(struct fb_info *info,
struct vm_area_struct *vma)
{
- return -EINVAL;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long page, pos;
+
+ if (offset + size > info->fix.smem_len) {
+ return -EINVAL;
+ }
+
+ pos = (unsigned long)info->fix.smem_start + offset;
+
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+ return 0;
+
}
#ifndef MODULE
@@ -406,7 +474,7 @@
/*
* For real video cards we use ioremap.
*/
- if (!(videomemory = vmalloc(videomemorysize)))
+ if (!(videomemory = rvmalloc(videomemorysize)))
return retval;
/*
@@ -429,6 +497,8 @@
if (!retval || (retval == 4))
info->var = vfb_default;
+ vfb_fix.smem_start = (unsigned long) videomemory;
+ vfb_fix.smem_len = videomemorysize;
info->fix = vfb_fix;
info->pseudo_palette = info->par;
info->par = NULL;
@@ -452,7 +522,7 @@
err1:
framebuffer_release(info);
err:
- vfree(videomemory);
+ rvfree(videomemory, videomemorysize);
return retval;
}
@@ -462,7 +532,7 @@
if (info) {
unregister_framebuffer(info);
- vfree(videomemory);
+ rvfree(videomemory, videomemorysize);
framebuffer_release(info);
}
return 0;