码迷,mamicode.com
首页 > 其他好文 > 详细

重头写一个v4l2的虚拟驱动_1

时间:2015-02-24 18:42:44      阅读:220      评论:0      收藏:0      [点我收藏+]

标签:摄像头   内核   v4l2   

简介

  因为在qcom平台上和linux原生都是用的v4l2框架作为camera的驱动框架,所以本着学习记录的笔记,做了如下文档记录。
该文档是学习《卫东山老师视频教程第三期》的个人学习笔记,非常感谢老师的资料。该记录仅供学习交流,如有侵犯到大家利益,还望海涵,请联系博主删除。


注册video_device


代码演示

  首先是驱动程序的入口、出口以及license,然后第一步就要分配,设置和注册一个video_device结构(基于内核版本:linux-3.13.1)。
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/random.h>
#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/highmem.h>
#include <linux/freezer.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
 
#define DRIVER_NAME "myvivi_video"
 
static struct video_device *myvivi_device;
struct v4l2_device	myvivi_v4l2_dev;
 
static void myvivi_release(struct video_device *vdev)
{
}
 
static const struct v4l2_file_operations myvivi_fops = {                                                                             
	.owner      = THIS_MODULE,
};
 
static int myvivi_init(void)
{
    int error;
 
    /* 1. 分配一个video_device结构体 */
    myvivi_device = video_device_alloc();
 
    /* 2. 设置 */
    strlcpy(myvivi_v4l2_dev.name, DRIVER_NAME, sizeof(myvivi_v4l2_dev.name));
    error = v4l2_device_register(NULL, &myvivi_v4l2_dev);
    myvivi_device->v4l2_dev = &myvivi_v4l2_dev;
    myvivi_device->release = myvivi_release;
	myvivi_device->fops    = &myvivi_fops;
    /* 2.1 */
 
    error = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -1);
 
    return error;
}
 
static void myvivi_exit(void)
{
    video_unregister_device(myvivi_device);
    video_device_release(myvivi_device);
}
 
module_init(myvivi_init);
module_exit(myvivi_exit);
 
MODULE_LICENSE("GPL");


代码讲解

  首先是分配一个video_device_alloc来分配一个v4l2_device结构,接着填充设置这个结构的v4l2_dev参数、操作函数fops以及释放函数release,
在代码中为了简洁,所有fops和release函数都设置为了空函数。
  接着注册了该v4l2设备,注意这里的video_register_device函数,第一个参数表示我们需要注册的video_device 结构,VFL_TYPE_GRABBER表示是图像采集设备
,最后的-1参数表示,注册时候使用第一个空闲的设备名,如果/dev/video0已经被使用了,那么这次注册就是作为/dev/video1.如果参数不是-1,比如是5,那么注
册的设备文件为:/dev/video5.
  最后是驱动的出口,对应的操作也就是注销掉之前注册的v4l2设备,然后销毁该设备结构。
  如此,一个最简单的v4l2设备就完成了。


注意

  我使用的内核版本是linux-3.13.1,在该版本中,必须填充了v4l2_device结构的v4l2_dev参数、fops操作函数以及释放函数release之后,
调用video_register_device函数进行注册才能注册成功,否则驱动程序,会报出空指针或者参数之类的错误。
  同时在加载该驱动之前,需要保证内核中依赖的v4l2驱动模块已经是加载好了的,v4l2一般对应有如下几个模块:
videodev.ko videobuf-vmalloc.ko videobuf-core.ko  v4l2-common.ko
  驱动依赖哪些模块,可以使用sudo modinfo myvivi.ko 来查看到。


效果演示

  最后insmod加载该驱动模块之后,会生成video*的设备节点,效果如下:

技术分享

  接着安装工具:sudo apt-get install xawtv
  然后使用该工具后会提示说:
Failed to query video capabilities: Inappropriate ioctl for device
libv4l2: error getting capabilities: Inappropriate ioctl for device
Failed to query video capabilities: Inappropriate ioctl for device
libv4l2: error getting capabilities: Inappropriate ioctl for device
vid-open-auto: failed to open a capture device at /dev/video0
vid-open: could not find a suitable videodev
no video grabber device available


表示为video设备

  在前面使用xawtv的时候,提示说没有找到适合的video设备,所以接着这一步就是将我们之前写的v4l2注册驱动表示为video设备。


代码讲解

添加代码如下:
...............
static const struct v4l2_file_operations myvivi_fops = {                                                                             
    .owner      = THIS_MODULE,
    .ioctl      = video_ioctl2, /* V4L2 ioctl handler */
};
 
static int myvivi_vidioc_querycap(struct file *file, void  *priv,                                                                    
        struct v4l2_capability *cap){
 
    strcpy(cap->driver, "myvivi");
    strcpy(cap->card, "myvivi");
    cap->version = 0x0001;
    cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;   /*表明是个视频捕捉设备和通过ioctl来访问*/
    return 0;
}
 
static const struct v4l2_ioctl_ops myvivi_ioctl_ops = {
    // 表示它是一个摄像头设备
    .vidioc_querycap      = myvivi_vidioc_querycap,
};                                                                                                                                   
 
static int myvivi_init(void)
{
    int error;
 
    /* 1. 分配一个video_device结构体 */
    myvivi_device = video_device_alloc();
 
    /* 2. 设置 */
    strlcpy(myvivi_v4l2_dev.name, DRIVER_NAME, sizeof(myvivi_v4l2_dev.name));
    error = v4l2_device_register(NULL, &myvivi_v4l2_dev);
    myvivi_device->v4l2_dev = &myvivi_v4l2_dev;
    myvivi_device->release = myvivi_release;
    myvivi_device->fops    = &myvivi_fops;
    myvivi_device->ioctl_ops = &myvivi_ioctl_ops;
    ..............
}
..............
  新加入初始化了一个myvivi_ioctl_ops操作函数结构,在其中填充了函数myvivi_vidioc_querycap,在应用程序中,会调用该函数来确实该驱动设备类型。
在该函数中V4L2_CAP_VIDEO_CAPTURE表示该驱动设备是一个视频获取设备,V4L2_CAP_STREAMING表示是通过ioctl来进行获取数据。
  同时在调用myvivi_vidioc_querycap函数的时候还需要调用到myvivi_fops中的ioctl函数,所以还需要填充该ioctl。这里使用v4l2提供的ioctl标准操作
函数video_ioctl2。


效果演示

  接着继续用xawtv工具来测试该驱动。效果如下:
技术分享
  这里标明xawtv找到了/dev/video0是一个video设备。


设置支持的video格式

  前面一步中,xawtv已经识别到了该设备驱动是video设备,接着这一步在驱动中设置该驱动支持的video格式。


代码讲解

static struct v4l2_format myvivi_format;
/* 列举支持哪种格式 */
static int myvivi_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
		struct v4l2_fmtdesc *f){
 
	if (f->index >= 1) /*我们这里设置为只支持一种格式,所以设置f->index如果大于等于1,就返回错误*/
		return -EINVAL;
 
	strcpy(f->description, "4:2:2, packed, YUYV");
	f->pixelformat = V4L2_PIX_FMT_YUYV;
	return 0;
}
 
/* 返回当前所使用的格式 */
static int myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
		struct v4l2_format *f){
	memcpy(f, &myvivi_format, sizeof(myvivi_format));
	return (0);
}
 
/* 测试驱动程序是否支持某种格式 */
static int myvivi_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
		struct v4l2_format *f){
	unsigned int maxw, maxh;
 
	if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
		return -EINVAL;
 
	/*该设备支持的最大宽度和高度*/
	maxw  = 1024;
	maxh  = 768;
 
	/* 调整format的最大最小width, height,和对齐方式
	 * 计算bytesperline, sizeimage
	 */
	v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
			&f->fmt.pix.height, 32, maxh, 0, 0);
 
	f->fmt.pix.bytesperline =
		(f->fmt.pix.width * 16) >> 3;  /*设置颜色深度为16*/
	f->fmt.pix.sizeimage =
		f->fmt.pix.height * f->fmt.pix.bytesperline; /*设置图像size*/
 
	return 0;
}
 
static int myvivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
		struct v4l2_format *f){
	int ret = myvivi_vidioc_try_fmt_vid_cap(file, NULL, f);
	if (ret < 0)
		return ret;
 
	memcpy(&myvivi_format, f, sizeof(myvivi_format));
 
	return ret;
}
 
static const struct v4l2_ioctl_ops myvivi_ioctl_ops = {
	// 表示它是一个摄像头设备
	.vidioc_querycap      = myvivi_vidioc_querycap,
 
	/* 用于列举、获得、测试、设置摄像头的数据的格式 */
	.vidioc_enum_fmt_vid_cap  = myvivi_vidioc_enum_fmt_vid_cap,
	.vidioc_g_fmt_vid_cap     = myvivi_vidioc_g_fmt_vid_cap,
	.vidioc_try_fmt_vid_cap   = myvivi_vidioc_try_fmt_vid_cap,
	.vidioc_s_fmt_vid_cap     = myvivi_vidioc_s_fmt_vid_cap,
 
};

  如上所示,一共增加了4个函数。
  myvivi_vidioc_enum_fmt_vid_cap:用来设置支持的格式,这里设置为只支持一种格式 V4L2_PIX_FMT_YUYV。
  myvivi_vidioc_g_fmt_vid_cap:用来获得支持的video格式信息,格式信息保存在结构myvivi_format中。
  myvivi_vidioc_try_fmt_vid_cap:用来测试传入的某个video格式是否被该设备驱动支持,同时设置了该格式下的video的width、height和image size等信息。
  myvivi_vidioc_s_fmt_vid_cap:首先用myvivi_vidioc_try_fmt_vid_cap,判断传入的某种格式是否支持,如果支持就保存到myvivi_format结构中。


效果演示

  同样是用xawtv工具来演示下效果,不过这里看不出太明显。
技术分享
  这里可以看到,xawtv有读取到了一些格式相关信息。

重头写一个v4l2的虚拟驱动_1

标签:摄像头   内核   v4l2   

原文地址:http://blog.csdn.net/u011630458/article/details/43926223

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!