📄 mmvideo.c
字号:
mmvideo_hw_stop( mvdev ); mvdev->user = 0; MOD_DEC_USE_COUNT;}static long mmvideo_v4l_write( struct video_device * vdev, const char * buf, unsigned long cnt, int noblock ){ struct mmvideo_device * mvdev = (struct mmvideo_device *)(vdev->priv); if ( cnt != MMVIDEO_FRAMESIZE ) return -EINVAL; down( & mvdev->lock ); if ( copy_from_user( mvdev->hw_frame[mvdev->hw_frame_nr].data, buf, cnt ) != 0 ) { up( & mvdev->lock ); return -EFAULT; } // mvdev->frame_index = 0; up( & mvdev->lock ); return MMVIDEO_FRAMESIZE;}static long mmvideo_v4l_read( struct video_device * vdev, char * buf, unsigned long cnt, int noblock ){ struct mmvideo_device * mvdev = (struct mmvideo_device *)(vdev->priv); DECLARE_WAITQUEUE( wait, current ); // struct file * file = vdev->file; // clipping not supported if ( cnt < MMVIDEO_FRAMESIZE ) return -EINVAL; /* pre-sleep */ add_wait_queue( & mvdev->wq, & wait ); current->state = TASK_INTERRUPTIBLE; down( & mvdev->lock ); while ( mvdev->frame_to_read < 0 ) { // printk("mmvideo_v4l_read frame_to_read %d wait\n", // mvdev->frame_to_read ); up( & mvdev->lock ); // if ( file->flags & O_NDELAY ) { // remove_wait_queue( & mmvideo_v4l_wait, & wait ); // current->state = TASK_RUNNING; // return -EWOULDBLOCK; // } if ( signal_pending( current ) ) { remove_wait_queue( & mvdev->wq, & wait ); current->state = TASK_RUNNING; return -ERESTARTSYS; } /* sleep */ schedule(); current->state = TASK_INTERRUPTIBLE; down( & mvdev->lock ); } /* post-sleep */ remove_wait_queue( & mvdev->wq, & wait ); current->state = TASK_RUNNING; // printk("mmvideo_v4l_read frame_to_read %d\n", mvdev->frame_to_read ); if ( copy_to_user(buf, mvdev->hw_frame[mvdev->frame_to_read].data, MMVIDEO_FRAMESIZE ) != 0 ) { mvdev->frame_to_read = -1; up( & mvdev->lock ); mvdev->error_cnt ++; return -EFAULT; } mvdev->frame_to_read = -1; up( & mvdev->lock ); mvdev->frame_cnt ++; return MMVIDEO_FRAMESIZE;}static int mmvideo_v4l_mmap( struct video_device * vdev, const char * addr, unsigned long size ){ struct mmvideo_device * mvdev = (struct mmvideo_device *)(vdev->priv); unsigned long start = (unsigned long)addr; unsigned long page, pos; // printk("mmvideo mmap() size %ld %ld \n", // size, MMVIDEO_NUMFRAMES * MMVIDEO_FRAMESIZE ); if ( size > MMVIDEO_NUMFRAMES * MMVIDEO_FRAMESIZE ) return -EINVAL; down( &mvdev->lock ); pos = (unsigned long) mvdev->fbuf; while ( size > 0 ) { page = kvirt_to_pa( pos ); if ( remap_page_range( start, page, PAGE_SIZE, PAGE_SHARED ) ) { up( &mvdev->lock ); return -EAGAIN; } start += PAGE_SIZE; pos += PAGE_SIZE; if ( size > PAGE_SIZE) { size -= PAGE_SIZE; } else { size = 0; } } up( &mvdev->lock ); return 0;}// this is called by V4L at the end of the initialization//static int mmvideo_v4l_init_done( struct video_device * vdev ){ struct mmvideo_device * mvdev = (struct mmvideo_device *)(vdev->priv); #if defined( CONFIG_PROC_FS ) && defined( CONFIG_VIDEO_PROC_FS ) if (mmvideo_proc_create_entry( mvdev ) < 0 ) return -1; #endif mvdev->hw_frame_nr = 0; // not really necessary return 0;}static int mmvideo_v4l_ioctl( struct video_device * vdev, unsigned int cmd, void * arg ){ struct mmvideo_device * mvdev = (struct mmvideo_device *)(vdev->priv); switch ( cmd ) { case VIDIOCGCAP: { struct video_capability b; strcpy( b.name, "mmvideo" ); b.type = VID_TYPE_CAPTURE; b.channels = 1; b.audios = 0; b.maxwidth = mvdev->cwidth; b.maxheight = mvdev->cheight; b.minwidth = mvdev->cwidth; b.minheight = mvdev->cheight; if ( copy_to_user( arg, &b, sizeof(b) ) ) return -EFAULT; return 0; } case VIDIOCGCHAN: { // only channel 0 allowed struct video_channel c; if ( copy_from_user( &c, arg, sizeof(c) ) ) return -EFAULT; if ( c.channel != 0 ) return -EINVAL; c.flags = 0; c.tuners = 0; c.type = VIDEO_TYPE_CAMERA; strcpy( c.name, "mmvideo" ); if ( copy_to_user( arg, &c, sizeof(c) ) ) return -EFAULT; return 0; } case VIDIOCSCHAN: return -EINVAL; case VIDIOCGPICT: if ( copy_to_user( arg, &(mvdev->vpict), sizeof(struct video_picture) ) ) return -EFAULT; return 0; case VIDIOCSPICT: return -EINVAL; case VIDIOCGWIN: { // instantiate and fill a video_window struct // // struct video_window w; // w.x = 0; // position on the framebuffer // w.y = 0; // w.chromakey = 0; // w.flags = 0; // w.clipcount = 0; // number of clip rectangles // w.width = mvdev->vwidth; // w.height = mvdev->vheight; // // and copy to userspace // if ( copy_to_user( arg, &w, sizeof(w) ) ) return -EFAULT; // return 0; return -EINVAL; } case VIDIOCSWIN: return -EINVAL; case VIDIOCGMBUF: { struct video_mbuf m; int i; memset(&m, 0, sizeof(m)); m.size = MMVIDEO_FRAMESIZE * MMVIDEO_NUMFRAMES; m.frames = MMVIDEO_NUMFRAMES; for (i=0; i<MMVIDEO_NUMFRAMES; i++) m.offsets[i] = i * MMVIDEO_FRAMESIZE; if ( copy_to_user( arg, &m, sizeof(m) ) ) return -EFAULT; return 0; } // VIDIOCMCAPTURE is used to tell the driver the size of the image // and to start acquiring a frame // VIDIOCSYNC is used to free the frame that has been acquired case VIDIOCMCAPTURE: { struct video_mmap m; if ( copy_from_user( &m, arg, sizeof(m) ) ) return -EFAULT; if ( m.format != MMVIDEO_PALETTE ) return -EINVAL; if ( m.width != mvdev->vwidth ) return -EINVAL; if ( m.height != mvdev->vheight ) return -EINVAL; if ( m.frame < 0 || m.frame >= MMVIDEO_NUMFRAMES ) return -EINVAL; // start mmap streaming return mmvideo_hw_mcapture( mvdev, m.frame ); return 0; } case VIDIOCSYNC: { int frame; if ( copy_from_user( (void *)&frame, arg, sizeof(int) ) ) return -EFAULT; if ( frame < 0 || frame >= MMVIDEO_NUMFRAMES ) return -EINVAL; return mmvideo_hw_mrelease( mvdev, frame ); } case VIDIOCGFBUF: { struct video_buffer b; memset( &b, 0, sizeof(b) ); b.base = mvdev->fbuf; b.width = mvdev->cwidth; b.height = mvdev->cheight; b.depth = MMVIDEO_DEPTH; b.bytesperline = mvdev->cwidth * MMVIDEO_DEPTH; if ( copy_to_user( arg, &b, sizeof(b) ) ) return -EFAULT; return 0; } case VIDIOCKEY: return 0; case VIDIOCCAPTURE: // overlay capture not supported return -EINVAL; case VIDIOCGTUNER: case VIDIOCSTUNER: case VIDIOCGFREQ: case VIDIOCSFREQ: case VIDIOCGAUDIO: case VIDIOCSAUDIO: return -EINVAL; default: return -ENOIOCTLCMD; } return 0;}static struct video_device vdev_template = { #ifdef VIDEO_DEVICE_HAVE_OWNER owner: THIS_MODULE, #endif name: "mmvideo", type: VID_TYPE_CAPTURE, // VID_TYPE_OVERLAY not supported // thus no VID_TYPE_CLIPPING / CHROMAKEY // VID_TYPE_FRAMERAM not supported hardware: VID_HARDWARE_PSEUDO, // see videodev.h open: mmvideo_v4l_open, close: mmvideo_v4l_close, read: mmvideo_v4l_read, write: mmvideo_v4l_write, ioctl: mmvideo_v4l_ioctl, mmap: mmvideo_v4l_mmap, initialize: mmvideo_v4l_init_done, // minor: 1,};/* ------------------------------------------------------------ */static struct mmvideo_device * mmvideo = NULL;static int __init mmvideo_init( void ){ int i,j; unsigned char * chp; int nr; if ((mmvideo = kmalloc( sizeof(struct mmvideo_device), GFP_KERNEL)) == NULL) { return -1; } memset( mmvideo, 0, sizeof(struct mmvideo_device) ); // video buffer memory allocation mmvideo->fbuf = rvmalloc( MMVIDEO_FRAMESIZE * MMVIDEO_NUMFRAMES ); if ( ! mmvideo->fbuf ) { kfree( mmvideo ); return -1; } mmvideo->hw_fbuf = rvmalloc( MMVIDEO_FRAMESIZE * 2); if ( ! mmvideo->hw_fbuf ) { rvfree( mmvideo->fbuf, MMVIDEO_FRAMESIZE * MMVIDEO_NUMFRAMES ); kfree( mmvideo ); return -1; } // mmvideo->frame_index = 0; // first frame for (i=0; i<2; i++) mmvideo->hw_frame[i].data = mmvideo->hw_fbuf + i * MMVIDEO_FRAMESIZE; for (i=0; i<MMVIDEO_NUMFRAMES; i++) mmvideo->frame[i].data = mmvideo->fbuf + i * MMVIDEO_FRAMESIZE; // now fake an initial picture chp = mmvideo->hw_frame[0].data; for (j=0; j<MMVIDEO_HEIGHT; j++) { for (i=0; i<MMVIDEO_WIDTH; i++) { if ( (i+j)>100 && (i+j)<150 && (i-j)>0 && (i-j)<50) *(chp) = 128; else if (i>150 && i<200 && j>100 && j<150) *(chp+1) = 128; else if (i>50 && i<100 && j>150 && j<200 && (i-j)<-100) *(chp+2) = 128; chp += 3; } } mmvideo->hw_frame_nr = 0; memcpy( &mmvideo->vdev, &vdev_template, sizeof(struct video_device) ); mmvideo->vdev.priv = mmvideo; // use private data init_waitqueue_head( &mmvideo->wq ); init_MUTEX( &mmvideo->lock ); wmb(); // the proc dir must be created before registering with V4L #if defined( CONFIG_PROC_FS ) && defined( CONFIG_VIDEO_PROC_FS ) if ( mmvideo_proc_create_dir() < 0 ) { video_unregister_device( &mmvideo->vdev ); rvfree( mmvideo->hw_fbuf, MMVIDEO_NUMFRAMES*2 ); rvfree( mmvideo->fbuf, MMVIDEO_NUMFRAMES*MMVIDEO_FRAMESIZE ); kfree( mmvideo ); return -1; } #endif PDEBUG( "mmvideo_init() registering with video\n"); // nr >= 0 specifies the device // nr = -1 means the first free device if ( // (nr = video_register_device( &mmvideo->vdev, VFL_TYPE_GRABBER, -1)) (nr = video_register_device( &mmvideo->vdev, VFL_TYPE_GRABBER)) == -1) { rvfree( mmvideo->hw_fbuf, MMVIDEO_NUMFRAMES*2 ); rvfree( mmvideo->fbuf, MMVIDEO_NUMFRAMES*MMVIDEO_FRAMESIZE ); kfree( mmvideo ); return -1; } // printk( "mmvideo_init() registered with video, nr. %d\n", nr); return 0;}static void __exit mmvideo_exit( void ){ if ( ! mmvideo ) return; wake_up_interruptible( & mmvideo->wq ); #if defined( CONFIG_PROC_FS ) && defined( CONFIG_VIDEO_PROC_FS ) mmvideo_proc_destroy_dir(); #endif lock_kernel(); if ( mmvideo->user == 0 ) { PDEBUG( "mmvideo_init() unregistering with video\n"); } else { PDEBUG( "mmvideo_init() unregistering when user 1\n"); } video_unregister_device( &mmvideo->vdev ); rvfree( mmvideo->hw_fbuf, MMVIDEO_NUMFRAMES*2 ); rvfree( mmvideo->fbuf, MMVIDEO_NUMFRAMES * MMVIDEO_FRAMESIZE ); kfree( mmvideo ); unlock_kernel();}module_init( mmvideo_init );module_exit( mmvideo_exit );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -