您的位置:首页 > 运维架构 > Linux

LINUX摄像驱动一:V4L2大致框架及vivi初步分析

2017-05-29 11:41 876 查看
回顾:

怎么写分层驱动:

1、分配某结构体

2、设置

3、注册

4、硬件相关操作

V4L2框架可参考文章:

http://blog.csdn.net/lizuobin2/article/details/53000720

http://blog.csdn.net/lanbing510/article/details/24204655?locationNum=15&fps=1



一. V4L2框架: video for linux version 2

大致猜测:

至少分为两层:

第一层:核心层: v4l2_dev.c

1、编写file_operations结构体

2、注册

然后:

cdev_alloc

cdev->ops = v4l2_fops

cdev_add

第二层:硬件相关层:

1、分配video_device

2、设置

3、注册vido_register_device

uvc_driver.c

v4l2_device_register 这个其实并不重要

video_device_alloc

video_register_device 这个重要

虚拟视频驱动vivi.c分析:

1.分配 video_device

2.设置

3.注册:video_register_device

ViVi分析:

vivi_init
vivi_create_instance
v4l2_device_register   // 不是主要, 只是用于初始化一些东西,比如自旋锁、引用计数 、并没有注册什么东西
video_device_alloc     //分配vido_device设备,分配好后进行设置
// 设置
1. vfd:
.fops           = &vivi_fops,     //Garmen:这个fops 指的是 :v4l2_file_oprations结构体
.ioctl_ops     = &vivi_ioctl_ops,
.release    = video_device_release,
2.
vfd->v4l2_dev = &dev->v4l2_dev;     //Garmen:dev->v4l2_dev这个结构体,实际上就是前面v4l2_device_register函数得到的结构体!
3. 设置"ctrl属性"(用于APP的ioctl):
v4l2_ctrl_handler_init(hdl, 11);
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);          //音量
dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);               //亮度     0:亮度Min,255:亮度Max,127:默认
dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 16);

Garmen:进行好上面的分配之后,然后进行注册!
video_register_device(video_device, type:VFL_TYPE_GRABBER, nr)     //Garmen: 根据这个类型type:VFL_TYPE_GRABBER,创建不同的设备节点;不同类型,次设备号不同
__video_register_device
vdev->cdev = cdev_alloc();          //Garmen: 从这里我们可以看到,不管驱动框架多么复杂,最后还是有cdev_alloc();和cdev_add;
vdev->cdev->ops = &v4l2_fops;          //猜测:v4l2_fops 可能就是上面第1点里面的.fops
cdev_add

video_device[vdev->minor] = vdev;     //Garmen:以次设备号为下标,将vdev结构体存入video_device,当我们调用open函数的时候就反方向操作

Garmen:对应了问1里面的问题;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;


附上vivi.c 中vivi_init里的vivi_create_instance函数(这是主要函数)

static int __init vivi_create_instance(int inst)
{
struct vivi_dev *dev;
struct video_device *vfd;
struct v4l2_ctrl_handler *hdl;
struct vb2_queue *q;
int ret;

dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;

snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
"%s-%03d", VIVI_MODULE_NAME, inst);
ret = v4l2_device_register(NULL, &dev->v4l2_dev);
if (ret)
goto free_dev;

dev->fmt = &formats[0];
dev->width = 640;
dev->height = 480;
hdl = &dev->ctrl_handler;
v4l2_ctrl_handler_init(hdl, 11);
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 16);
dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_SATURATION, 0, 255, 1, 127);
dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_HUE, -128, 127, 1, 0);
dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_GAIN, 0, 255, 1, 100);
dev->button = v4l2_c
4000
trl_new_custom(hdl, &vivi_ctrl_button, NULL);
dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
if (hdl->error) {
ret = hdl->error;
goto unreg_dev;
}
v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
dev->v4l2_dev.ctrl_handler = hdl;

/* initialize locks */
spin_lock_init(&dev->slock);

/* initialize queue */
q = &dev->vb_vidq;
memset(q, 0, sizeof(dev->vb_vidq));
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivi_buffer);
q->ops = &vivi_video_qops;
q->mem_ops = &vb2_vmalloc_memops;

vb2_queue_init(q);

mutex_init(&dev->mutex);

/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
init_waitqueue_head(&dev->vidq.wq);

ret = -ENOMEM;
vfd = video_device_alloc();
if (!vfd)
goto unreg_dev;

*vfd = vivi_template;
vfd->debug = debug;
vfd->v4l2_dev = &dev->v4l2_dev;
set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);

/*
* Provide a mutex to v4l2 core. It will be used to protect
* all fops and v4l2 ioctls.
*/
vfd->lock = &dev->mutex;

ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
if (ret < 0)
goto rel_vdev;

video_set_drvdata(vfd, dev);

/* Now that everything is fine, let's add it to device list */
list_add_tail(&dev->vivi_devlist, &vivi_devlist);

if (video_nr != -1)
video_nr++;

dev->vfd = vfd;
v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
video_device_node_name(vfd));
return 0;

rel_vdev:
video_device_release(vfd);
unreg_dev:
v4l2_ctrl_handler_free(hdl);
v4l2_device_unregister(&dev->v4l2_dev);
free_dev:
kfree(dev);
return ret;
}


对于一个驱动程序,你要想彻底的弄清楚它,就是要去分析它的open,read,write,ioctl过程

分析vivi.c的open,read,write,ioctl过程

1. open
app:     open("/dev/video0",....)
---------------------------------------------------
drv:     v4l2_fops.v4l2_open
vdev = video_devdata(filp);  // 根据次设备号从数组中得到video_device
ret = vdev->fops->open(filp);
vivi_ioctl_ops.open    //转到了我们硬件相关层所提供的open函数了
v4l2_fh_open

2. read
app:    read ....
---------------------------------------------------
drv:    v4l2_fops.v4l2_read          //调用file_operations v4l2_fops
struct video_device *vdev = video_devdata(filp);    //首先根据次设备号,从数组里面得到之前注册的video_device结构体
ret = vdev->fops->read(filp, buf, sz, off);            //然后调用它的fops里的read函数;

3. ioctl
app:   ioctl
----------------------------------------------------
drv:   v4l2_fops.unlocked_ioctl
v4l2_ioctl
struct video_device *vdev = video_devdata(filp);
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
video_ioctl2          //分析了vivi_fops中的ioctl函数;
video_usercopy(file, cmd, arg, __video_do_ioctl);
__video_do_ioctl
struct video_device *vfd = video_devdata(file);
根据APP传入的cmd来获得、设置"某些属性"
//Garmen:发现大概是做了这些事情:从用户空间,根据这些参数,把用户空间的参数复制进来,
然后调用 __video_do_ioctl这个参数
----------------------------------------------------
4、v4l2_ctrl_handler的使用过程:
__video_do_ioctl
struct video_device *vfd = video_devdata(file);

case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *p = arg;

if (vfh && vfh->ctrl_handler)
ret = v4l2_queryctrl(vfh->ctrl_handler, p);
else if (vfd->ctrl_handler)  // 问1:在哪设置?   答1:在video_register_device
ret = v4l2_queryctrl(vfd->ctrl_handler, p);
// 根据ID在ctrl_handler里找到v4l2_ctrl,返回它的值
Garmen:我们在vivi.c里面,所有创建的v4l2_ctrl,都有一个ID!


Garmen:小结

怎么写V4L2驱动:

实际上并没有脱离字符设备驱动程序的框架!

1、分配、设置、注册 V4L2_device (实际上并不重要,但是它里面提供了一些辅助信息,比如自旋锁、引用计数 )

它的关键函数是:v4l2_device_register,得到一个结构体v4l2_device

2、分配:vido_device,调用vido_device_alloc函数

3、设置:

A:vfd,它里面有一些结构体,比如v4l2_dev,我们将它指向V4L2_device,我们以后会引用到里面的一些结构体

B : vfd里面还有很多结构体,例如vfd.fops,fops里面有open、read、write函数,被上层的V4l2_fops调用

ioctl_ops 调用

C : App可以通过ioctl来设置亮度等信息,驱动程序里,谁来接收/存储/设置到硬件

App可以通过ioctl来获得亮度等信息,驱动程序里,谁来提供这些信息呢?

我们用V4l2_ctrl

问:谁来管理?

答:V4l2_ctrl_handle

该函数主要要完成的事情

①:初始化V4l2_ctrl_handler_init,进行V4l2_ctrl_handle初始化

②:设置;添加了类似V4l2_Ctrl_new_std、V4l2_Ctrl_new_custom的各项函数

问:这些函数干嘛用呢?

答:创建各个V4l2_Ctrl,并且放入V4l2_ctrl_handle这个链表

③:跟vdev进行关联

v4l2_dev.ctrl_handler = hdl;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux