2007-08-03 06:31:33 +04:00
/*
* helper functions for vmalloc video4linux capture buffers
*
2008-07-17 04:27:49 +04:00
* The functions expect the hardware being able to scatter gather
2007-08-03 06:31:33 +04:00
* ( 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 .
*
* ( c ) 2007 Mauro Carvalho Chehab , < mchehab @ infradead . org >
*
* 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 ; either version 2
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/pci.h>
# include <linux/vmalloc.h>
# include <linux/pagemap.h>
# include <asm/page.h>
# include <asm/pgtable.h>
# include <media/videobuf-vmalloc.h>
# define MAGIC_DMABUF 0x17760309
# define MAGIC_VMAL_MEM 0x18221223
2010-03-17 10:01:04 +03:00
# define MAGIC_CHECK(is, should) \
if ( unlikely ( ( is ) ! = ( should ) ) ) { \
printk ( KERN_ERR " magic mismatch: %x (expected %x) \n " , \
is , should ) ; \
BUG ( ) ; \
}
2007-08-03 06:31:33 +04:00
2008-04-22 21:41:48 +04:00
static int debug ;
2007-08-03 06:31:33 +04:00
module_param ( debug , int , 0644 ) ;
MODULE_DESCRIPTION ( " helper module to manage video4linux vmalloc buffers " ) ;
MODULE_AUTHOR ( " Mauro Carvalho Chehab <mchehab@infradead.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2010-03-17 10:01:04 +03:00
# define dprintk(level, fmt, arg...) \
if ( debug > = level ) \
printk ( KERN_DEBUG " vbuf-vmalloc: " fmt , # # arg )
2007-08-03 06:31:33 +04:00
/***************************************************************************/
2010-03-17 10:01:04 +03:00
static void videobuf_vm_open ( struct vm_area_struct * vma )
2007-08-03 06:31:33 +04:00
{
struct videobuf_mapping * map = vma - > vm_private_data ;
2010-03-17 10:01:04 +03:00
dprintk ( 2 , " vm_open %p [count=%u,vma=%08lx-%08lx] \n " , map ,
map - > count , vma - > vm_start , vma - > vm_end ) ;
2007-08-03 06:31:33 +04:00
map - > count + + ;
}
2008-04-13 21:58:43 +04:00
static void videobuf_vm_close ( struct vm_area_struct * vma )
2007-08-03 06:31:33 +04:00
{
struct videobuf_mapping * map = vma - > vm_private_data ;
struct videobuf_queue * q = map - > q ;
int i ;
2010-03-17 10:01:04 +03:00
dprintk ( 2 , " vm_close %p [count=%u,vma=%08lx-%08lx] \n " , map ,
2008-04-13 21:58:43 +04:00
map - > count , vma - > vm_start , vma - > vm_end ) ;
2007-08-03 06:31:33 +04:00
map - > count - - ;
if ( 0 = = map - > count ) {
2008-04-13 21:58:43 +04:00
struct videobuf_vmalloc_memory * mem ;
dprintk ( 1 , " munmap %p q=%p \n " , map , q ) ;
2008-01-31 19:57:53 +03:00
mutex_lock ( & q - > vb_lock ) ;
2008-04-13 22:07:56 +04:00
/* We need first to cancel streams, before unmapping */
if ( q - > streaming )
videobuf_queue_cancel ( q ) ;
2007-08-03 06:31:33 +04:00
for ( i = 0 ; i < VIDEO_MAX_FRAME ; i + + ) {
if ( NULL = = q - > bufs [ i ] )
continue ;
2007-09-28 01:25:44 +04:00
if ( q - > bufs [ i ] - > map ! = map )
2007-08-03 06:31:33 +04:00
continue ;
2007-09-07 03:11:35 +04:00
2008-04-13 21:58:43 +04:00
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 .
*/
2008-04-13 22:07:56 +04:00
2008-04-13 21:58:43 +04:00
MAGIC_CHECK ( mem - > magic , MAGIC_VMAL_MEM ) ;
2008-04-13 22:07:56 +04:00
/* vfree is not atomic - can't be
called with IRQ ' s disabled
*/
dprintk ( 1 , " %s: buf[%d] freeing (%p) \n " ,
__func__ , i , mem - > vmalloc ) ;
2008-04-13 21:58:43 +04:00
vfree ( mem - > vmalloc ) ;
mem - > vmalloc = NULL ;
}
2007-09-28 01:25:44 +04:00
q - > bufs [ i ] - > map = NULL ;
2007-08-03 06:31:33 +04:00
q - > bufs [ i ] - > baddr = 0 ;
}
2008-04-13 22:07:56 +04:00
2007-08-03 06:31:33 +04:00
kfree ( map ) ;
2008-04-13 22:07:56 +04:00
mutex_unlock ( & q - > vb_lock ) ;
2007-08-03 06:31:33 +04:00
}
2008-04-13 22:07:56 +04:00
2007-08-03 06:31:33 +04:00
return ;
}
2010-03-17 10:01:04 +03:00
static const struct vm_operations_struct videobuf_vm_ops = {
2007-08-03 06:31:33 +04:00
. open = videobuf_vm_open ,
. close = videobuf_vm_close ,
} ;
/* ---------------------------------------------------------------------
* vmalloc handlers for the generic methods
*/
/* Allocated area consists on 3 parts:
struct video_buffer
struct < driver > _buffer ( cx88_buffer , saa7134_buf , . . . )
2008-04-22 21:42:13 +04:00
struct videobuf_dma_sg_memory
2007-08-03 06:31:33 +04:00
*/
2010-03-28 15:09:44 +04:00
static struct videobuf_buffer * __videobuf_alloc ( size_t size )
2007-08-03 06:31:33 +04:00
{
2008-02-05 02:52:21 +03:00
struct videobuf_vmalloc_memory * mem ;
2007-08-03 06:31:33 +04:00
struct videobuf_buffer * vb ;
2010-03-17 10:01:04 +03:00
vb = kzalloc ( size + sizeof ( * mem ) , GFP_KERNEL ) ;
2010-02-22 19:10:06 +03:00
if ( ! vb )
return vb ;
2007-08-03 06:31:33 +04:00
2010-03-17 10:01:04 +03:00
mem = vb - > priv = ( ( char * ) vb ) + size ;
mem - > magic = MAGIC_VMAL_MEM ;
2007-08-03 06:31:33 +04:00
2010-03-17 10:01:04 +03:00
dprintk ( 1 , " %s: allocated at %p(%ld+%ld) & %p(%ld) \n " ,
__func__ , vb , ( long ) sizeof ( * vb ) , ( long ) size - sizeof ( * vb ) ,
mem , ( long ) sizeof ( * mem ) ) ;
2007-08-03 06:31:33 +04:00
return vb ;
}
2010-03-17 10:01:04 +03:00
static int __videobuf_iolock ( struct videobuf_queue * q ,
struct videobuf_buffer * vb ,
struct v4l2_framebuffer * fbuf )
2007-08-03 06:31:33 +04:00
{
2008-04-13 21:58:21 +04:00
struct videobuf_vmalloc_memory * mem = vb - > priv ;
2008-04-13 22:07:56 +04:00
int pages ;
2007-08-03 06:31:33 +04:00
BUG_ON ( ! mem ) ;
2008-04-13 21:58:21 +04:00
MAGIC_CHECK ( mem - > magic , MAGIC_VMAL_MEM ) ;
2007-08-03 06:31:33 +04:00
2008-04-13 21:58:21 +04:00
switch ( vb - > memory ) {
case V4L2_MEMORY_MMAP :
dprintk ( 1 , " %s memory method MMAP \n " , __func__ ) ;
2007-08-03 06:31:33 +04:00
2008-04-13 21:58:21 +04:00
/* All handling should be done by __videobuf_mmap_mapper() */
if ( ! mem - > vmalloc ) {
printk ( KERN_ERR " memory is not alloced/mmapped. \n " ) ;
return - EINVAL ;
}
break ;
case V4L2_MEMORY_USERPTR :
2008-04-13 22:07:56 +04:00
pages = PAGE_ALIGN ( vb - > size ) ;
2007-08-03 06:31:33 +04:00
2008-04-13 21:58:21 +04:00
dprintk ( 1 , " %s memory method USERPTR \n " , __func__ ) ;
if ( vb - > baddr ) {
printk ( KERN_ERR " USERPTR is currently not supported \n " ) ;
return - EINVAL ;
}
2007-08-03 06:31:33 +04:00
2008-04-13 21:58:21 +04:00
/* The only USERPTR currently supported is the one needed for
2010-03-17 10:01:04 +03:00
* read ( ) method .
2008-04-13 21:58:21 +04:00
*/
mem - > vmalloc = vmalloc_user ( pages ) ;
if ( ! mem - > vmalloc ) {
printk ( KERN_ERR " vmalloc (%d pages) failed \n " , pages ) ;
return - ENOMEM ;
2007-08-03 06:31:33 +04:00
}
2008-04-13 21:58:21 +04:00
dprintk ( 1 , " vmalloc is at addr %p (%d pages) \n " ,
mem - > vmalloc , pages ) ;
#if 0
int rc ;
/* Kernel userptr is used also by read() method. In this case,
there ' s no need to remap , since data will be copied to user
*/
if ( ! vb - > baddr )
return 0 ;
/* FIXME: to properly support USERPTR, remap should occur.
2008-07-26 15:37:58 +04:00
The code below won ' t work , since mem - > vma = NULL
2008-04-13 21:58:21 +04:00
*/
/* Try to remap memory */
rc = remap_vmalloc_range ( mem - > vma , ( void * ) vb - > baddr , 0 ) ;
if ( rc < 0 ) {
2010-03-17 10:01:04 +03:00
printk ( KERN_ERR " mmap: remap failed with error %d " , rc ) ;
2008-04-13 21:58:21 +04:00
return - ENOMEM ;
}
# endif
break ;
case V4L2_MEMORY_OVERLAY :
default :
dprintk ( 1 , " %s memory method OVERLAY/unknown \n " , __func__ ) ;
/* Currently, doesn't support V4L2_MEMORY_OVERLAY */
printk ( KERN_ERR " Memory method currently unsupported. \n " ) ;
return - EINVAL ;
2007-08-03 06:31:33 +04:00
}
return 0 ;
}
static int __videobuf_sync ( struct videobuf_queue * q ,
struct videobuf_buffer * buf )
{
return 0 ;
}
static int __videobuf_mmap_mapper ( struct videobuf_queue * q ,
struct vm_area_struct * vma )
{
2008-02-05 02:52:21 +03:00
struct videobuf_vmalloc_memory * mem ;
2007-08-03 06:31:33 +04:00
struct videobuf_mapping * map ;
unsigned int first ;
2008-04-13 21:58:21 +04:00
int retval , pages ;
2007-08-03 06:31:33 +04:00
unsigned long offset = vma - > vm_pgoff < < PAGE_SHIFT ;
2008-04-13 21:58:21 +04:00
dprintk ( 1 , " %s \n " , __func__ ) ;
if ( ! ( vma - > vm_flags & VM_WRITE ) | | ! ( vma - > vm_flags & VM_SHARED ) )
2007-08-03 06:31:33 +04:00
return - EINVAL ;
/* look for first buffer to map */
for ( first = 0 ; first < VIDEO_MAX_FRAME ; first + + ) {
if ( NULL = = q - > bufs [ first ] )
continue ;
if ( V4L2_MEMORY_MMAP ! = q - > bufs [ first ] - > memory )
continue ;
if ( q - > bufs [ first ] - > boff = = offset )
break ;
}
if ( VIDEO_MAX_FRAME = = first ) {
2010-03-17 10:01:04 +03:00
dprintk ( 1 , " mmap app bug: offset invalid [offset=0x%lx] \n " ,
2007-08-03 06:31:33 +04:00
( vma - > vm_pgoff < < PAGE_SHIFT ) ) ;
return - EINVAL ;
}
/* create mapping + update buffer list */
2008-04-13 21:58:21 +04:00
map = kzalloc ( sizeof ( struct videobuf_mapping ) , GFP_KERNEL ) ;
2007-08-03 06:31:33 +04:00
if ( NULL = = map )
return - ENOMEM ;
2008-04-13 21:58:21 +04:00
q - > bufs [ first ] - > map = map ;
2007-08-03 06:31:33 +04:00
map - > start = vma - > vm_start ;
map - > end = vma - > vm_end ;
map - > q = q ;
q - > bufs [ first ] - > baddr = vma - > vm_start ;
2008-04-13 21:58:21 +04:00
mem = q - > bufs [ first ] - > priv ;
BUG_ON ( ! mem ) ;
MAGIC_CHECK ( mem - > magic , MAGIC_VMAL_MEM ) ;
2007-08-03 06:31:33 +04:00
2008-04-13 21:58:21 +04:00
pages = PAGE_ALIGN ( vma - > vm_end - vma - > vm_start ) ;
mem - > vmalloc = vmalloc_user ( pages ) ;
if ( ! mem - > vmalloc ) {
printk ( KERN_ERR " vmalloc (%d pages) failed \n " , pages ) ;
goto error ;
}
2010-03-17 10:01:04 +03:00
dprintk ( 1 , " vmalloc is at addr %p (%d pages) \n " , mem - > vmalloc , pages ) ;
2007-09-28 01:25:44 +04:00
2007-08-03 06:31:33 +04:00
/* Try to remap memory */
2008-04-13 21:58:21 +04:00
retval = remap_vmalloc_range ( vma , mem - > vmalloc , 0 ) ;
if ( retval < 0 ) {
printk ( KERN_ERR " mmap: remap failed with error %d. " , retval ) ;
vfree ( mem - > vmalloc ) ;
goto error ;
2007-08-03 06:31:33 +04:00
}
2008-04-13 21:58:21 +04:00
vma - > vm_ops = & videobuf_vm_ops ;
vma - > vm_flags | = VM_DONTEXPAND | VM_RESERVED ;
vma - > vm_private_data = map ;
2010-03-17 10:01:04 +03:00
dprintk ( 1 , " mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d \n " ,
2008-04-13 21:58:21 +04:00
map , q , vma - > vm_start , vma - > vm_end ,
2007-08-03 06:31:33 +04:00
( long int ) q - > bufs [ first ] - > bsize ,
2008-04-13 21:58:21 +04:00
vma - > vm_pgoff , first ) ;
2007-08-03 06:31:33 +04:00
videobuf_vm_open ( vma ) ;
2008-04-13 21:58:21 +04:00
return 0 ;
error :
mem = NULL ;
kfree ( map ) ;
return - ENOMEM ;
2007-08-03 06:31:33 +04:00
}
static struct videobuf_qtype_ops qops = {
. magic = MAGIC_QTYPE_OPS ,
. alloc = __videobuf_alloc ,
. iolock = __videobuf_iolock ,
. sync = __videobuf_sync ,
. mmap_mapper = __videobuf_mmap_mapper ,
2010-03-28 15:18:37 +04:00
. vaddr = videobuf_to_vmalloc ,
2007-08-03 06:31:33 +04:00
} ;
2010-03-17 10:01:04 +03:00
void videobuf_queue_vmalloc_init ( struct videobuf_queue * q ,
2009-11-18 01:43:41 +03:00
const struct videobuf_queue_ops * ops ,
2009-11-23 20:29:35 +03:00
struct device * dev ,
2007-08-03 06:31:33 +04:00
spinlock_t * irqlock ,
enum v4l2_buf_type type ,
enum v4l2_field field ,
unsigned int msize ,
void * priv )
{
2007-10-08 19:20:02 +04:00
videobuf_queue_core_init ( q , ops , dev , irqlock , type , field , msize ,
priv , & qops ) ;
2007-08-03 06:31:33 +04:00
}
EXPORT_SYMBOL_GPL ( videobuf_queue_vmalloc_init ) ;
2010-03-17 10:01:04 +03:00
void * videobuf_to_vmalloc ( struct videobuf_buffer * buf )
2007-08-03 06:31:33 +04:00
{
2010-03-17 10:01:04 +03:00
struct videobuf_vmalloc_memory * mem = buf - > priv ;
BUG_ON ( ! mem ) ;
MAGIC_CHECK ( mem - > magic , MAGIC_VMAL_MEM ) ;
2007-08-03 06:31:33 +04:00
return mem - > vmalloc ;
}
EXPORT_SYMBOL_GPL ( videobuf_to_vmalloc ) ;
2010-03-17 10:01:04 +03:00
void videobuf_vmalloc_free ( struct videobuf_buffer * buf )
2007-08-03 06:31:33 +04:00
{
2008-04-13 21:58:21 +04:00
struct videobuf_vmalloc_memory * mem = buf - > priv ;
2007-08-03 06:31:33 +04:00
2008-04-13 21:58:43 +04:00
/* 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 .
*/
2009-01-24 03:35:12 +03:00
if ( ( buf - > memory ! = V4L2_MEMORY_USERPTR ) | | buf - > baddr )
2008-04-13 21:58:43 +04:00
return ;
2008-04-13 21:58:21 +04:00
if ( ! mem )
return ;
MAGIC_CHECK ( mem - > magic , MAGIC_VMAL_MEM ) ;
2007-08-03 06:31:33 +04:00
vfree ( mem - > vmalloc ) ;
2008-04-13 21:58:21 +04:00
mem - > vmalloc = NULL ;
2007-08-03 06:31:33 +04:00
return ;
}
EXPORT_SYMBOL_GPL ( videobuf_vmalloc_free ) ;