Camera V4L2 架构分析

来源:互联网 发布:角度弧度转换器软件 编辑:程序博客网 时间:2024/05/21 07:57

转自:点击打开链接


V4L2相关代码位于linux-3.4/drivers/media/video目录中。

v4l2-dev.c文件提供设备节点实现与用户层数据交流,设备节点在/dev/目录下以video0、video1等名字出现。注册字符设备的语句如下:

[java] view plain copy
  1. /* Part 3: Initialize the character device */  
  2. vdev->cdev = cdev_alloc();  
  3. if (vdev->cdev == NULL) {  
  4.     ret = -ENOMEM;  
  5.     goto cleanup;  
  6. }     
  7. vdev->cdev->ops = &v4l2_fops;  
  8. vdev->cdev->owner = owner;  
  9. ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);   
  10. if (ret < 0) {  
  11.     printk(KERN_ERR "%s: cdev_add failed\n", __func__);  
  12.     kfree(vdev->cdev);  
  13.     vdev->cdev = NULL;  
  14.     goto cleanup;  
  15. }     
  16.   
  17. /* Part 4: register the device with sysfs */  
  18. vdev->dev.class = &video_class;  
  19. vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);  
  20. if (vdev->parent)  
  21.     vdev->dev.parent = vdev->parent;  
  22. dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);  
  23. ret = device_register(&vdev->dev);  

只要调用函数video_register_device即可把一个video_device注册到V4L2架构中;但这只是一个总纲领,Camera驱动主要是实现V4L2的一个子系统的功能,子系统用一个v4l2_subdev结构体来描述,只有实现好了系统要求的相关函数操作,最后一步才是注册到V4L2中。

子系统实现的方式每个平台都有差别,这里分析的是全志A23平台的代码。

在sunxi-vfe/vfe.c文件中,以platform形式注册了前后摄像头的平台资源。匹配过程忽略,最终vfe_probe函数会被调用。在probe中,看到有函数v4l2_i2c_new_subdev_board:

[java] view plain copy
  1.     /* Create the i2c client */  
  2.     if (info->addr == 0 && probe_addrs)  
  3.         client = i2c_new_probed_device(adapter, info, probe_addrs,  
  4.                            NULL);  
  5.     else  
  6.         client = i2c_new_device(adapter, info);  
  7. ...............................  
  8.     /* Register with the v4l2_device which increases the module's 
  9.        use count as well. */  
  10.     if (v4l2_device_register_subdev(v4l2_dev, sd))                                                                                                               
  11.         sd = NULL;  

这里的client获得在之前写的I2C驱动的文章已经分析过,可见驱动的知识都是环环相扣的;根据I2C驱动文章的分析可知获取client过程中,device_register(&client->dev)会被调用,而根据device和device_driver的模型关系可知,device所在的bus总线会进行匹配,device处于i2c总线下,根据I2C match函数的匹配,名字相同的device_driver将被匹配中;我们假设i2c_client的name为"ov5460"吧,这样ov5460.c中看看:

[java] view plain copy
  1. static struct i2c_driver sensor_driver = {  
  2.   .driver = {  
  3.     .owner = THIS_MODULE,  
  4.   .name = SENSOR_NAME,  
  5.   },  
  6.   .probe = sensor_probe,                                                                                                                                         
  7.   .remove = sensor_remove,  
  8.   .id_table = sensor_id,  
  9. };  

毫无疑问,sensor_probe函数会被调用了:

[java] view plain copy
  1. static int sensor_probe(struct i2c_client *client,                                                                                                               
  2.       const struct i2c_device_id *id)  
  3. {  
  4.   struct v4l2_subdev *sd;  
  5.   struct sensor_info *info;  
  6. //  int ret;  
  7.   
  8.   info = kzalloc(sizeof(struct sensor_info), GFP_KERNEL);  
  9.   if (info == NULL)  
  10.     return -ENOMEM;  
  11.   sd = &info->sd;  
  12.   glb_sd = sd;  
  13.   v4l2_i2c_subdev_init(sd, client, &sensor_ops);  
  14.   
  15.   info->fmt = &sensor_formats[0];  
  16.   info->af_first_flag = 1;  
  17.   info->init_first_flag = 1;  
  18.   info->auto_focus = 0;  
  19.   
  20.   return 0;  
  21. }  

这里就看到了前面提到的v4l2_subdev结构体,v4l2_i2c_subdev_init函数会进入v4l2-subdev.c进行一系列初始化操作,并且用i2c_set_clientdata(client, sd);保存子系统指针,以便后续取出。这里sensor_ops结构体即为子系统支持的类型:

[java] view plain copy
  1. static const struct v4l2_subdev_ops sensor_ops = {                                                                                                               
  2.   .core = &sensor_core_ops,  
  3.   .video = &sensor_video_ops,  
  4. };  

Camera当然是是video的了,core是核心,应该是不可少的操作吧。其实v4l2_subdev所支持的类型很多,其全部类型定义如下:

[java] view plain copy
  1. struct v4l2_subdev_ops {                                                                                                                                         
  2.     const struct v4l2_subdev_core_ops   *core;  
  3.     const struct v4l2_subdev_tuner_ops  *tuner;  
  4.     const struct v4l2_subdev_audio_ops  *audio;  
  5.     const struct v4l2_subdev_video_ops  *video;  
  6.     const struct v4l2_subdev_vbi_ops    *vbi;  
  7.     const struct v4l2_subdev_ir_ops     *ir;  
  8.     const struct v4l2_subdev_sensor_ops *sensor;  
  9.     const struct v4l2_subdev_pad_ops    *pad;  
  10. };  

tuner应该属于电视一类的吧,还有ir,红外;audio,音频等。看看video要实现的操作:

[java] view plain copy
  1. struct v4l2_subdev_video_ops {  
  2.     int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);  
  3.     int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags);  
  4.     int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);  
  5.     int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std);  
  6.     int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);  
  7.     int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std);  
  8.     int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);  
  9.     int (*s_stream)(struct v4l2_subdev *sd, int enable);  
  10.     int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc);  
  11.     int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);  
  12.     int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);                                                                                               
  13.     int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);  
  14.     int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);  
  15.     int (*g_frame_interval)(struct v4l2_subdev *sd,  
  16.                 struct v4l2_subdev_frame_interval *interval);  
  17.     int (*s_frame_interval)(struct v4l2_subdev *sd,  
  18.                 struct v4l2_subdev_frame_interval *interval);  
  19.     int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize);  
  20.     int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival);  
  21.     int (*enum_dv_presets) (struct v4l2_subdev *sd,  
  22.             struct v4l2_dv_enum_preset *preset);  
  23.     int (*s_dv_preset)(struct v4l2_subdev *sd,  
  24.             struct v4l2_dv_preset *preset);  
  25.     int (*g_dv_preset)(struct v4l2_subdev *sd,  
  26.             struct v4l2_dv_preset *preset);  
  27.     int (*query_dv_preset)(struct v4l2_subdev *sd,  
  28.             struct v4l2_dv_preset *preset);  
  29.     int (*s_dv_timings)(struct v4l2_subdev *sd,  
  30.             struct v4l2_dv_timings *timings);  
  31.     int (*g_dv_timings)(struct v4l2_subdev *sd,  
  32.             struct v4l2_dv_timings *timings);  
  33.     int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index,  
  34.                  enum v4l2_mbus_pixelcode *code);  
  35.     int (*enum_mbus_fsizes)(struct v4l2_subdev *sd,  
  36.                  struct v4l2_frmsizeenum *fsize);  
  37.     int (*g_mbus_fmt)(struct v4l2_subdev *sd,  
  38.               struct v4l2_mbus_framefmt *fmt);  
  39.     int (*try_mbus_fmt)(struct v4l2_subdev *sd,  
  40.                 struct v4l2_mbus_framefmt *fmt);  
  41.     int (*s_mbus_fmt)(struct v4l2_subdev *sd,  
  42.               struct v4l2_mbus_framefmt *fmt);  
  43.     int (*g_mbus_config)(struct v4l2_subdev *sd,  
  44.                  struct v4l2_mbus_config *cfg);  
  45.     int (*s_mbus_config)(struct v4l2_subdev *sd,  
  46.                  const struct v4l2_mbus_config *cfg);  
  47. };  

太多了,这就是一个具体摄像头主要实现的操作,而ov5640只是支持其中一部分操作而已:

[java] view plain copy
  1. static const struct v4l2_subdev_core_ops sensor_core_ops = {  
  2.   .g_chip_ident = sensor_g_chip_ident,  
  3.   .g_ctrl = sensor_g_ctrl,  
  4.   .s_ctrl = sensor_s_ctrl,  
  5.   .queryctrl = sensor_queryctrl,  
  6.   .reset = sensor_reset,  
  7.   .init = sensor_init,  
  8.   .s_power = sensor_power,  
  9.   .ioctl = sensor_ioctl,  
  10. };  

[java] view plain copy
  1. static const struct v4l2_subdev_video_ops sensor_video_ops = {  
  2.   .enum_mbus_fmt = sensor_enum_fmt,                                                                                                                              
  3.   .enum_framesizes = sensor_enum_size,  
  4.   .try_mbus_fmt = sensor_try_fmt,  
  5.   .s_mbus_fmt = sensor_s_fmt,  
  6.   .s_parm = sensor_s_parm,  
  7.   .g_parm = sensor_g_parm,  
  8.   .g_mbus_config = sensor_g_mbus_config,  
  9. };  

这样回到v4l2_i2c_new_subdev_board函数中,调用v4l2_device_register_subdev把sd注册到了子系统中去。


回到vfe.c的probe函数中。常用到的操作是v4l2_subdev_call,其实这是一个宏:

[java] view plain copy
  1. #define v4l2_subdev_call(sd, o, f, args...)             \                                                                                                        
  2.     (!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ? \  
  3.         (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD))  
好处在于检查子系统是否支持某个操作,比如上述ov5640的,只支持sensor_core_ops和sensor_video_ops所定义的操作。其他的返回error。

最后:

[java] view plain copy
  1. vfd = video_device_alloc();  
  2. *vfd = vfe_template;  
  3. ret = video_register_device(vfd, VFL_TYPE_GRABBER, dev->id);  

分配video_device空间并赋值,赋值内容如下:

[java] view plain copy
  1. static struct video_device vfe_template = {                                                                                                                      
  2.   .name       = "vfe",  
  3.   .fops       = &vfe_fops,  
  4.   .ioctl_ops  = &vfe_ioctl_ops,  
  5.   .release    = video_device_release,  
  6. };  

最后注册到了V4L2系统中。


如果从上层ioctl调用开始分析,vdev->fops->ioctl(filp, cmd, arg);调用的就是这里的vfe_fops:

[java] view plain copy
  1. static const struct v4l2_file_operations vfe_fops = {  
  2.   .owner          = THIS_MODULE,  
  3.   .open           = vfe_open,  
  4.   .release        = vfe_close,  
  5.   .read           = vfe_read,  
  6.   .poll           = vfe_poll,  
  7.   .ioctl          = video_ioctl2,                                                                                                                                
  8.   //.unlocked_ioctl =   
  9.   .mmap           = vfe_mmap,  
  10. };    

video_ioctl2是要调用v4l2-ioctl.c通用的ioctl来处理,它会检查用户空间参数时候合法等操作。在该文件中调用__video_do_ioctl函数,包含了所有V4L2支持的ioctl操作命令,函数很长,代码就不贴出来了;在该ioctl中最终才调用vfe_ioctl_ops的操作:

[java] view plain copy
  1. static const struct v4l2_ioctl_ops vfe_ioctl_ops = {                                                                                                             
  2.   .vidioc_querycap          = vidioc_querycap,  
  3.   .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,  
  4.   .vidioc_enum_framesizes   = vidioc_enum_framesizes,  
  5.   .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,  
  6.   .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,  
  7.   .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,  
  8.   .vidioc_reqbufs           = vidioc_reqbufs,  
  9.   .vidioc_querybuf          = vidioc_querybuf,  
  10.   .vidioc_qbuf              = vidioc_qbuf,  
  11.   .vidioc_dqbuf             = vidioc_dqbuf,  
  12.   .vidioc_enum_input        = vidioc_enum_input,  
  13.   .vidioc_g_input           = vidioc_g_input,  
  14.   .vidioc_s_input           = vidioc_s_input,  
  15.   .vidioc_streamon          = vidioc_streamon,  
  16.   .vidioc_streamoff         = vidioc_streamoff,  
  17.   .vidioc_queryctrl         = vidioc_queryctrl,  
  18.   .vidioc_g_ctrl            = vidioc_g_ctrl,  
  19.   .vidioc_s_ctrl            = vidioc_s_ctrl,  
  20.   .vidioc_g_parm            = vidioc_g_parm,  
  21.   .vidioc_s_parm            = vidioc_s_parm,  
  22. #ifdef CONFIG_VIDEO_V4L1_COMPAT  
  23.   .vidiocgmbuf              = vidiocgmbuf,  
  24. #endif  
  25. };  

而在这些通用的函数操作用,最终用v4l2_subdev_call来调用具体的Camera操作如ov5640