2010-12-09 16:20:47 +03:00
/*
* videobuf2 - memops . c - generic memory handling routines for videobuf2
*
* Copyright ( C ) 2010 Samsung Electronics
*
2011-03-13 21:23:32 +03:00
* Author : Pawel Osciak < pawel @ osciak . com >
2010-12-09 16:20:47 +03:00
* Marek Szyprowski < m . szyprowski @ samsung . com >
*
* 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 .
*/
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/dma-mapping.h>
# include <linux/vmalloc.h>
# include <linux/mm.h>
# include <linux/sched.h>
# include <linux/file.h>
# include <media/videobuf2-core.h>
# include <media/videobuf2-memops.h>
/**
* vb2_get_vma ( ) - acquire and lock the virtual memory area
* @ vma : given virtual memory area
*
* This function attempts to acquire an area mapped in the userspace for
* the duration of a hardware operation . The area is " locked " by performing
* the same set of operation that are done when process calls fork ( ) and
* memory areas are duplicated .
*
* Returns a copy of a virtual memory region on success or NULL .
*/
struct vm_area_struct * vb2_get_vma ( struct vm_area_struct * vma )
{
struct vm_area_struct * vma_copy ;
vma_copy = kmalloc ( sizeof ( * vma_copy ) , GFP_KERNEL ) ;
if ( vma_copy = = NULL )
return NULL ;
if ( vma - > vm_ops & & vma - > vm_ops - > open )
vma - > vm_ops - > open ( vma ) ;
if ( vma - > vm_file )
get_file ( vma - > vm_file ) ;
memcpy ( vma_copy , vma , sizeof ( * vma ) ) ;
vma_copy - > vm_mm = NULL ;
vma_copy - > vm_next = NULL ;
vma_copy - > vm_prev = NULL ;
return vma_copy ;
}
2012-01-23 17:35:38 +04:00
EXPORT_SYMBOL_GPL ( vb2_get_vma ) ;
2010-12-09 16:20:47 +03:00
/**
* vb2_put_userptr ( ) - release a userspace virtual memory area
* @ vma : virtual memory region associated with the area to be released
*
* This function releases the previously acquired memory area after a hardware
* operation .
*/
void vb2_put_vma ( struct vm_area_struct * vma )
{
if ( ! vma )
return ;
if ( vma - > vm_ops & & vma - > vm_ops - > close )
vma - > vm_ops - > close ( vma ) ;
2011-08-30 09:31:54 +04:00
if ( vma - > vm_file )
fput ( vma - > vm_file ) ;
2010-12-09 16:20:47 +03:00
kfree ( vma ) ;
}
2011-01-29 03:06:19 +03:00
EXPORT_SYMBOL_GPL ( vb2_put_vma ) ;
2010-12-09 16:20:47 +03:00
/**
* vb2_get_contig_userptr ( ) - lock physically contiguous userspace mapped memory
* @ vaddr : starting virtual address of the area to be verified
* @ size : size of the area
* @ res_paddr : will return physical address for the given vaddr
* @ res_vma : will return locked copy of struct vm_area for the given area
*
* This function will go through memory area of size @ size mapped at @ vaddr and
* verify that the underlying physical pages are contiguous . If they are
* contiguous the virtual memory area is locked and a @ res_vma is filled with
* the copy and @ res_pa set to the physical address of the buffer .
*
* Returns 0 on success .
*/
int vb2_get_contig_userptr ( unsigned long vaddr , unsigned long size ,
struct vm_area_struct * * res_vma , dma_addr_t * res_pa )
{
struct mm_struct * mm = current - > mm ;
struct vm_area_struct * vma ;
unsigned long offset , start , end ;
unsigned long this_pfn , prev_pfn ;
dma_addr_t pa = 0 ;
start = vaddr ;
offset = start & ~ PAGE_MASK ;
end = start + size ;
vma = find_vma ( mm , start ) ;
if ( vma = = NULL | | vma - > vm_end < end )
2011-11-17 12:32:17 +04:00
return - EFAULT ;
2010-12-09 16:20:47 +03:00
for ( prev_pfn = 0 ; start < end ; start + = PAGE_SIZE ) {
2011-11-17 12:32:17 +04:00
int ret = follow_pfn ( vma , start , & this_pfn ) ;
2010-12-09 16:20:47 +03:00
if ( ret )
2011-11-17 12:32:17 +04:00
return ret ;
2010-12-09 16:20:47 +03:00
if ( prev_pfn = = 0 )
pa = this_pfn < < PAGE_SHIFT ;
2011-11-17 12:32:17 +04:00
else if ( this_pfn ! = prev_pfn + 1 )
return - EFAULT ;
2010-12-09 16:20:47 +03:00
prev_pfn = this_pfn ;
}
/*
* Memory is contigous , lock vma and return to the caller
*/
* res_vma = vb2_get_vma ( vma ) ;
2011-11-17 12:32:17 +04:00
if ( * res_vma = = NULL )
return - ENOMEM ;
2010-12-09 16:20:47 +03:00
2011-11-17 12:32:17 +04:00
* res_pa = pa + offset ;
return 0 ;
2010-12-09 16:20:47 +03:00
}
2011-01-29 03:06:19 +03:00
EXPORT_SYMBOL_GPL ( vb2_get_contig_userptr ) ;
2010-12-09 16:20:47 +03:00
/**
* vb2_common_vm_open ( ) - increase refcount of the vma
* @ vma : virtual memory region for the mapping
*
* This function adds another user to the provided vma . It expects
* struct vb2_vmarea_handler pointer in vma - > vm_private_data .
*/
static void vb2_common_vm_open ( struct vm_area_struct * vma )
{
struct vb2_vmarea_handler * h = vma - > vm_private_data ;
2011-06-02 00:19:22 +04:00
pr_debug ( " %s: %p, refcount: %d, vma: %08lx-%08lx \n " ,
2010-12-09 16:20:47 +03:00
__func__ , h , atomic_read ( h - > refcount ) , vma - > vm_start ,
vma - > vm_end ) ;
atomic_inc ( h - > refcount ) ;
}
/**
* vb2_common_vm_close ( ) - decrease refcount of the vma
* @ vma : virtual memory region for the mapping
*
* This function releases the user from the provided vma . It expects
* struct vb2_vmarea_handler pointer in vma - > vm_private_data .
*/
static void vb2_common_vm_close ( struct vm_area_struct * vma )
{
struct vb2_vmarea_handler * h = vma - > vm_private_data ;
2011-06-02 00:19:22 +04:00
pr_debug ( " %s: %p, refcount: %d, vma: %08lx-%08lx \n " ,
2010-12-09 16:20:47 +03:00
__func__ , h , atomic_read ( h - > refcount ) , vma - > vm_start ,
vma - > vm_end ) ;
h - > put ( h - > arg ) ;
}
/**
* vb2_common_vm_ops - common vm_ops used for tracking refcount of mmaped
* video buffers
*/
const struct vm_operations_struct vb2_common_vm_ops = {
. open = vb2_common_vm_open ,
. close = vb2_common_vm_close ,
} ;
EXPORT_SYMBOL_GPL ( vb2_common_vm_ops ) ;
MODULE_DESCRIPTION ( " common memory handling routines for videobuf2 " ) ;
2011-03-13 21:23:32 +03:00
MODULE_AUTHOR ( " Pawel Osciak <pawel@osciak.com> " ) ;
2010-12-09 16:20:47 +03:00
MODULE_LICENSE ( " GPL " ) ;