LINUX摄像驱动一:V4L2大致框架及vivi初步分析
来源:互联网 发布:淘宝优惠券哪里领取 编辑:程序博客网 时间:2024/05/22 14:48
回顾:
怎么写分层驱动:
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_ctrl_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. openapp: 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_open2. readapp: 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. ioctlapp: 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摄像驱动一:V4L2大致框架及vivi初步分析
- LINUX摄像驱动二:虚拟驱动VIVI测试及彻底分析
- linux v4L2 初步框架
- 【Linux开发】V4L2驱动框架分析学习
- V4L2(二)虚拟摄像头驱动vivi深入分析
- Linux摄像驱动四:USB摄像驱动分析
- V4L2驱动简单分析一
- linux V4L2框架 视频驱动
- 摄像头驱动(二)————V4L2 虚拟摄像头驱动vivi深入分析
- linux camera V4L2框架(一)
- v4l2视频驱动中关于vivi.c的个人分析(菜鸟入门,请轻拍!)
- 摄像头驱动笔记1---V4L2框架分析
- Linux设备驱动之LCD显示摄像图像之二编写V4l2程序
- 虚拟视频驱动vivi.c分析(linux-3.4.2版本)
- Linux USB驱动框架分析(一)
- Linux USB驱动框架分析(一)
- Linux USB驱动框架分析(一)
- 虚拟摄像头vivi驱动分析
- 刚性、惯量、响应时间及伺服增益调整之间的关系
- C语言对齐、补齐
- IntelliJ IDEA15 简要使用手册
- Node面试题
- Joint Deep Learning for Pedestrian Detection(2014)
- LINUX摄像驱动一:V4L2大致框架及vivi初步分析
- Codeforces-788B Weird journey 欧拉回路
- 如何去解决fatal: refusing to merge unrelated histories
- java——io处理大文件
- hduProblem-1016简单dfs
- JAVA使用Tess4j识别图片内容
- Junit4测试套件的使用
- 多线程系列1:入门
- 用Maven创建一个Web项目