v4l2相关代码位于linux-3.4/drivers/media/video目录中。 v4l2-dev.c文件提供设备节点实现与用户层数据交流,设备节点在/dev/目录下以video0、video1等名字出现。注册字符设备的语句如下: /* part 3: initialize the character device */ vdev-cdev = cdev_
v4l2相关代码位于linux-3.4/drivers/media/video目录中。
v4l2-dev.c文件提供设备节点实现与用户层数据交流,设备节点在/dev/目录下以video0、video1等名字出现。注册字符设备的语句如下:
/* part 3: initialize the character device */ vdev->cdev = cdev_alloc(); if (vdev->cdev == null) { ret = -enomem; goto cleanup; } vdev->cdev->ops = &v4l2_fops; vdev->cdev->owner = owner; ret = cdev_add(vdev->cdev, mkdev(video_major, vdev->minor), 1); if (ret cdev); vdev->cdev = null; goto cleanup; } /* part 4: register the device with sysfs */ vdev->dev.class = &video_class; vdev->dev.devt = mkdev(video_major, vdev->minor); if (vdev->parent) vdev->dev.parent = vdev->parent; dev_set_name(&vdev->dev, %s%d, name_base, vdev->num); 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:
/* create the i2c client */ if (info->addr == 0 && probe_addrs) client = i2c_new_probed_device(adapter, info, probe_addrs, null); else client = i2c_new_device(adapter, info);............................... /* register with the v4l2_device which increases the module's use count as well. */ if (v4l2_device_register_subdev(v4l2_dev, sd)) 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中看看:static struct i2c_driver sensor_driver = { .driver = { .owner = this_module, .name = sensor_name, }, .probe = sensor_probe, .remove = sensor_remove, .id_table = sensor_id,};
毫无疑问,sensor_probe函数会被调用了:static int sensor_probe(struct i2c_client *client, const struct i2c_device_id *id){ struct v4l2_subdev *sd; struct sensor_info *info;// int ret; info = kzalloc(sizeof(struct sensor_info), gfp_kernel); if (info == null) return -enomem; sd = &info->sd; glb_sd = sd; v4l2_i2c_subdev_init(sd, client, &sensor_ops); info->fmt = &sensor_formats[0]; info->af_first_flag = 1; info->init_first_flag = 1; info->auto_focus = 0; return 0;}
这里就看到了前面提到的v4l2_subdev结构体,v4l2_i2c_subdev_init函数会进入v4l2-subdev.c进行一系列初始化操作,并且用i2c_set_clientdata(client, sd);保存子系统指针,以便后续取出。这里sensor_ops结构体即为子系统支持的类型:static const struct v4l2_subdev_ops sensor_ops = { .core = &sensor_core_ops, .video = &sensor_video_ops,};
camera当然是是video的了,core是核心,应该是不可少的操作吧。其实v4l2_subdev所支持的类型很多,其全部类型定义如下:struct v4l2_subdev_ops { const struct v4l2_subdev_core_ops *core; const struct v4l2_subdev_tuner_ops *tuner; const struct v4l2_subdev_audio_ops *audio; const struct v4l2_subdev_video_ops *video; const struct v4l2_subdev_vbi_ops *vbi; const struct v4l2_subdev_ir_ops *ir; const struct v4l2_subdev_sensor_ops *sensor; const struct v4l2_subdev_pad_ops *pad;};
tuner应该属于电视一类的吧,还有ir,红外;audio,音频等。看看video要实现的操作:struct v4l2_subdev_video_ops { int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config); int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags); int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); int (*g_std_output)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*g_tvnorms_output)(struct v4l2_subdev *sd, v4l2_std_id *std); int (*g_input_status)(struct v4l2_subdev *sd, u32 *status); int (*s_stream)(struct v4l2_subdev *sd, int enable); int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc); int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop); int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop); int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); int (*g_frame_interval)(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval); int (*s_frame_interval)(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval); int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize); int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival); int (*enum_dv_presets) (struct v4l2_subdev *sd, struct v4l2_dv_enum_preset *preset); int (*s_dv_preset)(struct v4l2_subdev *sd, struct v4l2_dv_preset *preset); int (*g_dv_preset)(struct v4l2_subdev *sd, struct v4l2_dv_preset *preset); int (*query_dv_preset)(struct v4l2_subdev *sd, struct v4l2_dv_preset *preset); int (*s_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings); int (*g_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings); int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code); int (*enum_mbus_fsizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize); int (*g_mbus_fmt)(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt); int (*try_mbus_fmt)(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt); int (*s_mbus_fmt)(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt); int (*g_mbus_config)(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg); int (*s_mbus_config)(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg);};
太多了,这就是一个具体摄像头主要实现的操作,而ov5640只是支持其中一部分操作而已:static const struct v4l2_subdev_core_ops sensor_core_ops = { .g_chip_ident = sensor_g_chip_ident, .g_ctrl = sensor_g_ctrl, .s_ctrl = sensor_s_ctrl, .queryctrl = sensor_queryctrl, .reset = sensor_reset, .init = sensor_init, .s_power = sensor_power, .ioctl = sensor_ioctl,};
static const struct v4l2_subdev_video_ops sensor_video_ops = { .enum_mbus_fmt = sensor_enum_fmt, .enum_framesizes = sensor_enum_size, .try_mbus_fmt = sensor_try_fmt, .s_mbus_fmt = sensor_s_fmt, .s_parm = sensor_s_parm, .g_parm = sensor_g_parm, .g_mbus_config = sensor_g_mbus_config,};
这样回到v4l2_i2c_new_subdev_board函数中,调用v4l2_device_register_subdev把sd注册到了子系统中去。
回到vfe.c的probe函数中。常用到的操作是v4l2_subdev_call,其实这是一个宏:
#define v4l2_subdev_call(sd, o, f, args...) \ (!(sd) ? -enodev : (((sd)->ops->o && (sd)->ops->o->f) ? \ (sd)->ops->o->f((sd) , ##args) : -enoioctlcmd))
好处在于检查子系统是否支持某个操作,比如上述ov5640的,只支持sensor_core_ops和sensor_video_ops所定义的操作。其他的返回error。最后:
vfd = video_device_alloc();*vfd = vfe_template;ret = video_register_device(vfd, vfl_type_grabber, dev->id);
分配video_device空间并赋值,赋值内容如下:
static struct video_device vfe_template = { .name = vfe, .fops = &vfe_fops, .ioctl_ops = &vfe_ioctl_ops, .release = video_device_release,};
最后注册到了v4l2系统中。
如果从上层ioctl调用开始分析,vdev->fops->ioctl(filp, cmd, arg);调用的就是这里的vfe_fops:
static const struct v4l2_file_operations vfe_fops = { .owner = this_module, .open = vfe_open, .release = vfe_close, .read = vfe_read, .poll = vfe_poll, .ioctl = video_ioctl2, //.unlocked_ioctl = .mmap = vfe_mmap,};
video_ioctl2是要调用v4l2-ioctl.c通用的ioctl来处理,它会检查用户空间参数时候合法等操作。在该文件中调用__video_do_ioctl函数,包含了所有v4l2支持的ioctl操作命令,函数很长,代码就不贴出来了;在该ioctl中最终才调用vfe_ioctl_ops的操作:
static const struct v4l2_ioctl_ops vfe_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm,#ifdef config_video_v4l1_compat .vidiocgmbuf = vidiocgmbuf,#endif};
而在这些通用的函数操作用,最终用v4l2_subdev_call来调用具体的camera操作如ov5640