📄 mmvideo.c
字号:
/** mmvideo.c * * author marco corvi <marco_corvi@geocities.com> * date may 2003 * * credits: after the stv680 driver (thanks) * */#include <linux/config.h>#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/vmalloc.h>#include <linux/slab.h>#include <linux/proc_fs.h>#include <linux/pagemap.h>#include <linux/wrapper.h>#include <linux/smp_lock.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/videodev.h>#include "mmvideo.h"#define DRIVER_VERSION "v0.10"#define DRIVER_AUTHOR "marco corvi <marco_corvi@geocities.com>"#define DRIVER_DESC "memory based video driver"#define MMVIDEO_FPS 20 /* 10 frames per sec */MODULE_AUTHOR( DRIVER_AUTHOR );MODULE_DESCRIPTION( DRIVER_DESC );#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 10) MODULE_LICENSE( "GPL" );#endifEXPORT_NO_SYMBOLS;// #define VIDEO_DEVICE_HAVE_OWNER#define PDEBUG( fmt, args... ) printk(KERN_ALERT fmt, ## args );/* ================================================================ *//* virtual memory *//* ---------------------------------------------------------------- */static inline unsigned long uvirt_to_kva( pgd_t * pgd, unsigned long addr ){ unsigned long ret = 0UL; pmd_t * pmd; pte_t * pte; if ( !pgd_none(*pgd)) { pmd = pmd_offset( pgd, addr ); if ( !pmd_none( *pmd )) { pte = pte_offset( pmd, addr ); if ( pte_present( *pte ) ) { ret = (unsigned long) page_address( pte_page( *pte ) ); ret |= (addr & (PAGE_SIZE - 1)); } } } return ret;}static inline unsigned long kvirt_to_pa( unsigned long addr ){ unsigned long va, kva, ret; va = VMALLOC_VMADDR( addr ); // recast unsigned long kva = uvirt_to_kva( pgd_offset_k(va), va ); // (init->mm)->pgd + pgd_index( va ) ret = __pa( kva ); // kva - PAGE_OFFSET return ret;}static void * rvmalloc( unsigned long size ){ void * mem; unsigned long addr, page; size = (size + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1); mem = vmalloc_32( size ); if ( ! mem ) return NULL; memset( mem, 0, size ); addr = (unsigned long) mem; while ( size > 0 ) { page = kvirt_to_pa( addr ); mem_map_reserve( virt_to_page( __va( page ) ) ); addr += PAGE_SIZE; if ( size > PAGE_SIZE ) size -= PAGE_SIZE; else size = 0; } return mem;}static void rvfree( void * mem, unsigned long size ){ unsigned long addr, page; if ( ! mem ) return; size = (size + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1); addr = (unsigned long)mem; while ( size > 0 ) { page = kvirt_to_pa( addr ); mem_map_unreserve( virt_to_page( __va( page ) ) ); addr += PAGE_SIZE; if ( size > PAGE_SIZE ) size -= PAGE_SIZE; else size = 0; } vfree( mem );}/* ---------------------------------------------------------------- *//* hardware *//* ---------------------------------------------------------------- */static int mmvideo_hw_reset( struct mmvideo_device * mvdev ){ // real hardware initialization // return -1; if something goes wrong mvdev->total_cnt = 0; // init statistics mvdev->error_cnt = 0; mvdev->frame_cnt = 0; mvdev->mmap_cnt = 0; mvdev->drop_cnt = 0; mvdev->vpict.brightness = 32767; mvdev->vpict.whiteness = 0; mvdev->vpict.colour = 32767; mvdev->vpict.contrast = 32767; mvdev->vpict.hue = 32767; mvdev->vpict.palette = MMVIDEO_PALETTE; mvdev->vpict.depth = MMVIDEO_DEPTH; mvdev->cwidth = MMVIDEO_WIDTH; mvdev->cheight = MMVIDEO_HEIGHT; mvdev->vwidth = MMVIDEO_WIDTH; mvdev->vheight = MMVIDEO_HEIGHT; return 0;}static void mmvideo_hw_interrupt( struct mmvideo_device * mvdev ){ int i; // printk("mmvideo_hw_interrupt() frame %d\n", mvdev->hw_frame_nr); mvdev->frame_to_read = -1; for (i=0; i<MMVIDEO_NUMFRAMES; i++) { mvdev->frame_nr = (mvdev->frame_nr + 1) % MMVIDEO_NUMFRAMES; if ( ( mvdev->frame_flag[mvdev->frame_nr] & MMVIDEO_FRAME_MMAP ) && ( mvdev->frame_flag[mvdev->frame_nr] & MMVIDEO_FRAME_MMAPUSED ) ) { down( & mvdev->lock ); memcpy( mvdev->frame[mvdev->frame_nr].data, mvdev->hw_frame[ mvdev->hw_frame_nr ].data, MMVIDEO_FRAMESIZE ); mvdev->frame_flag[mvdev->frame_nr] &= ~MMVIDEO_FRAME_MMAPUSED; up( & mvdev->lock ); // increase counter of mmaped frames mvdev->mmap_cnt ++; break; } } if ( i == MMVIDEO_NUMFRAMES ) { mvdev->frame_to_read = mvdev->hw_frame_nr; wake_up_interruptible( &mvdev->wq ); // wake up any waiting process } // increase total number of frames mvdev->total_cnt ++;} // compute the new frame (hardware real acquisition)//void mmvideo_hw_newframe( unsigned long arg ){ struct mmvideo_device * mvdev = (struct mmvideo_device * )arg; int i,j; int f0 = mvdev->hw_frame_nr; int f1 = (f0+1)%2; // printk("mmvideo_hw_newframe %d (old %d)\n", f0, f1); down( & mvdev->lock ); { unsigned char * r0 = mvdev->hw_frame[f0].data; unsigned char * r1 = mvdev->hw_frame[f1].data; unsigned char * g0 = r0+1; unsigned char * g1 = r1+1; unsigned char * b0 = r0+2; unsigned char * b1 = r1+2; unsigned char rtmp, gtmp, btmp; for (j=0; j<MMVIDEO_HEIGHT; j++) { rtmp = *r0; gtmp = *g0; btmp = *b0; for (i=0; i<MMVIDEO_WIDTH-1; i++) { r0 += 3; *r1 = *r0; r1 += 3; g0 += 3; *g1 = *g0; g1 += 3; b0 += 3; *b1 = *b0; b1 += 3; } r0 += 3; *r1 = rtmp; r1 += 3; g0 += 3; *g1 = gtmp; g1 += 3; b0 += 3; *b1 = btmp; b1 += 3; } } mvdev->hw_frame_nr = f1; up( & mvdev->lock ); mod_timer( &mvdev->timer, jiffies+HZ/MMVIDEO_FPS ); // schedule next capture mmvideo_hw_interrupt( mvdev );} static int mmvideo_hw_mrelease( struct mmvideo_device * mvdev, int frame ){ down( & mvdev->lock ); if ( ! (mvdev->frame_flag[frame] & MMVIDEO_FRAME_MMAP) ) { up( & mvdev->lock ); return -EINVAL; } mvdev->frame_flag[frame] |= MMVIDEO_FRAME_MMAPUSED; up( & mvdev->lock ); return 0;}// there should be a way to end the mmap capture // and let the driver reset overwrite to 1// this should be done at the munmap() syscall, but the driver is// not invoked at that time ...static int mmvideo_hw_mcapture( struct mmvideo_device * mvdev, int frame ){ int i; down( & mvdev->lock ); for (i=0; i<frame; i++) mvdev->frame_flag[i] |= ( MMVIDEO_FRAME_MMAP | MMVIDEO_FRAME_MMAPUSED ); up( & mvdev->lock ); return 0;}static void mmvideo_hw_start( struct mmvideo_device * mvdev ){ int i; for (i=0; i<MMVIDEO_NUMFRAMES; i++) mvdev->frame_flag[i] = MMVIDEO_FRAME_READY; mvdev->frame_nr = 0; // start at frame 0 init_timer( &mvdev->timer ); mvdev->timer.function = mmvideo_hw_newframe; mvdev->timer.expires = jiffies + HZ/MMVIDEO_FPS; mvdev->timer.data = (unsigned long)mvdev; add_timer( &mvdev->timer );}static void mmvideo_hw_stop( struct mmvideo_device * mvdev ){ int i; del_timer_sync( &mvdev->timer ); for (i=0; i<MMVIDEO_NUMFRAMES; i++) mvdev->frame_flag[i] &= MMVIDEO_FRAME_READY; mvdev->frame_nr = -1;}/* ---------------------------------------------------------------- *//* proc fs *//* ---------------------------------------------------------------- */#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)static struct proc_dir_entry * mmvideo_proc_dir = NULL;extern struct proc_dir_entry * video_proc_entry;static int mmvideo_proc_read( char * page, char** start, off_t off, int cnt, int * eof, void * data ){ char * out = page; long len; struct mmvideo_device * mvdev = (struct mmvideo_device *)data; out += sprintf(out, "Version : %s\n", DRIVER_VERSION); out += sprintf(out, "In use : %d\n", mvdev->user); out += sprintf(out, "Nr frames : %d\n", MMVIDEO_NUMFRAMES); out += sprintf(out, "Size : %dx%d\n", mvdev->vwidth, mvdev->vheight); out += sprintf(out, "Palette : %d\n", mvdev->vpict.palette ); out += sprintf(out, "Frame total : %d\n", mvdev->total_cnt); out += sprintf(out, "Frame read : %d\n", mvdev->frame_cnt); out += sprintf(out, "Frame mmap : %d\n", mvdev->mmap_cnt); out += sprintf(out, "Error : %d\n", mvdev->error_cnt); out += sprintf(out, "Dropped : %d\n", mvdev->drop_cnt); len = out - page; len -= off; if (len < cnt) { *eof = 1; if (len < 0) return 0; } else { len = cnt; } *start = page + off; return len;}static int mmvideo_proc_create_entry( struct mmvideo_device * mvdev ){ char name[12]; struct proc_dir_entry * ent; if ( ! mmvideo_proc_dir || ! mvdev) return -1; sprintf(name, "mmvideo%d", mvdev->vdev.minor); ent = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, mmvideo_proc_dir); if ( ! ent ) return -1; ent->data = mvdev; // copy address of mmvideo_device into 'data' ent->read_proc = mmvideo_proc_read; mvdev->proc_entry = ent; return 0;}static void mmvideo_proc_destroy_entry( struct mmvideo_device * mvdev ){ char name[12]; if ( ! mmvideo_proc_dir || ! mvdev) return; sprintf(name, "mmvideo%d", mvdev->vdev.minor); remove_proc_entry( name, mmvideo_proc_dir); mvdev->proc_entry = NULL;} static int mmvideo_proc_create_dir( void ){ if ( ! video_proc_entry ) return -1; mmvideo_proc_dir = create_proc_entry("mmvideo", S_IFDIR, video_proc_entry); if ( mmvideo_proc_dir ) { mmvideo_proc_dir->owner = THIS_MODULE; } else { return -1; } return 0;}static void mmvideo_proc_destroy_dir( void ){ if ( ! video_proc_entry ) return; remove_proc_entry( "mmvideo", video_proc_entry);}#endif // defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)/* ---------------------------------------------------------------- *//* video 4 linux *//* ---------------------------------------------------------------- */static int mmvideo_v4l_open( struct video_device * vdev, int flags ){ struct mmvideo_device * mvdev = (struct mmvideo_device *)(vdev->priv); int err = 0; // only one user at a time if ( mvdev->user > 0 ) { return -EINVAL; } MOD_INC_USE_COUNT; mvdev->user = 1; // initialize hw and device struct err = mmvideo_hw_reset( mvdev ); if ( err ) { MOD_DEC_USE_COUNT; mvdev->user = 0; } // start hardware video streaming mmvideo_hw_start( mvdev ); return err;} static void mmvideo_v4l_close( struct video_device * vdev ) { // called with BKL held struct mmvideo_device * mvdev = (struct mmvideo_device *)(vdev->priv); // stop hardware video-streaming
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -