最近在移植 A40i的红外摄像头驱动,把所研究的记录下来。
step1: v4l2-dev.c 通过v4l2_ioctl 的 ret = vdev->fops->ioctl(filp, cmd, arg); 跳转到 step2
static const struct file_operations v4l2_fops = {....unlocked_ioctl = v4l2_ioctl,...
} static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct video_device *vdev = video_devdata(filp);...if (vdev->fops->unlocked_ioctl) {...} else if (vdev->fops->ioctl) {...if (video_is_registered(vdev))ret = vdev->fops->ioctl(filp, cmd, arg); // 此处跳转到 step2}
}
step2: vfe.c 摄像头接口驱动(Camera Interface (CAMIF) driver)
struct v4l2_file_operations vfe_fops = {
.....
.ioctl = video_ioctl2, //重要,跳转到 step3
}
probe_work_handle() 函数通过__video_register_device 注册 vfe_template[] 定义的 struct video_device 设备,即是 上面 v4l2_ioctl()函数的 vdev->fops->ioctl(filp, cmd, arg);
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, //重要,跳转到 step3//.unlocked_ioctl =
#ifdef CONFIG_COMPAT.compat_ioctl32 = vfe_compat_ioctl32,
#endif.mmap = vfe_mmap,
};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_g_parm = vidioc_g_parm,.vidioc_s_parm = vidioc_s_parm,.vidioc_default = vfe_param_handler,
};static struct video_device vfe_template[] =
{[0] = {.name = "vfe_0",.fops = &vfe_fops,.ioctl_ops = &vfe_ioctl_ops,.release = video_device_release,},[1] = {.name = "vfe_1",.fops = &vfe_fops,.ioctl_ops = &vfe_ioctl_ops,.release = video_device_release,},
};static void probe_work_handle(struct work_struct *work)
{...../* v4l2 device register */ret = v4l2_device_register(&dev->pdev->dev, &dev->v4l2_dev);...../*video device register */vfd = video_device_alloc(); //分配一个video device.....*vfd = vfe_template[dev->id]; //上述表vfd->v4l2_dev = &dev->v4l2_dev;if(0 != device_valid_count){ret = video_register_device(vfd, VFL_TYPE_GRABBER, dev->id); //注册.....}.....
}
step3: v4l2-ioctl.c 通过 struct v4l2_ioctl_info v4l2_ioctls[] 数组跳转到相应的项,比如 v4l_s_fmt 跳转到step4
long video_ioctl2(struct file *file,unsigned int cmd, unsigned long arg)
{return video_usercopy(file, cmd, arg, __video_do_ioctl);
}static long __video_do_ioctl(struct file *file,unsigned int cmd, void *arg)
{struct video_device *vfd = video_devdata(file);....if (v4l2_is_known_ioctl(cmd)) {info = &v4l2_ioctls[_IOC_NR(cmd)]; //重点 数组....} else {....}write_only = _IOC_DIR(cmd) == _IOC_WRITE;if (info->flags & INFO_FL_STD) {typedef int (*vidioc_op)(struct file *file, void *fh, void *p);const void *p = vfd->ioctl_ops; //INFO_FL_STD 调用此处const vidioc_op *vidioc = p + info->u.offset;ret = (*vidioc)(file, fh, arg);} else if (info->flags & INFO_FL_FUNC) {ret = info->u.func(ops, file, fh, arg); //A40i是调用这个} else if (!ops->vidioc_default) {ret = -ENOTTY;} else {....}
}static struct v4l2_ioctl_info v4l2_ioctls[] = {IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)),IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),//跳转到step4....
};
step4: v4l2-ioctl.c 通过 v4l2_s_fmt()函数里的 return ops->vidioc_s_fmt_vid_cap(file, fh, arg); 跳转到step5
static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,struct file *file, void *fh, void *arg)
{struct v4l2_format *p = arg;struct video_device *vfd = video_devdata(file);....switch (p->type) {case V4L2_BUF_TYPE_VIDEO_CAPTURE:if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap))break;CLEAR_AFTER_FIELD(p, fmt.pix);return ops->vidioc_s_fmt_vid_cap(file, fh, arg); //跳转到step5case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:........
}
step5:vfe.c 调用 struct v4l2_ioctl_ops vfe_ioctl_ops结构体里的成员vidioc_s_fmt_vid_cap(),里面调用回调 .s_mbus_fmt 跳转step6
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_g_parm = vidioc_g_parm,.vidioc_s_parm = vidioc_s_parm,.vidioc_default = vfe_param_handler,
};static struct video_device vfe_template[] =
{[0] = {.name = "vfe_0",.fops = &vfe_fops,.ioctl_ops = &vfe_ioctl_ops,.release = video_device_release,},[1] = {.name = "vfe_1",.fops = &vfe_fops,.ioctl_ops = &vfe_ioctl_ops,.release = video_device_release,},
};static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,struct v4l2_format *f)
{struct vfe_dev *dev = video_drvdata(file);vfe_dbg(0,"vidioc_s_fmt_vid_cap\n");if (vfe_is_generating(dev)) {vfe_err("%s device busy\n", __func__);return -EBUSY;}bus_pix_code = try_fmt_internal(dev,f);....ret = v4l2_subdev_call(dev->sd,core,ioctl,GET_CURRENT_WIN_CFG,&win_cfg);....ret = v4l2_subdev_call(dev->sd,video,g_mbus_config,&mbus_cfg);....ret = v4l2_subdev_call(dev->csi_sd, video, s_mbus_config, &mbus_cfg);if (mbus_cfg.type == V4L2_MBUS_CSI2) {ret = v4l2_subdev_call(dev->mipi_sd, video, s_mbus_config, &mbus_cfg);....ret = v4l2_subdev_call(dev->mipi_sd, pad, set_fmt, NULL, &mipi_fmt);....v4l2_subdev_call(dev->mipi_sd, core, s_power, 0);....v4l2_subdev_call(dev->mipi_sd, core, s_power, 1);....}....ret = v4l2_subdev_call(dev->sd,video,s_mbus_fmt,&ccm_fmt); //.s_mbus_fmt 调用此子函数, 请看step6 ....ret = v4l2_subdev_call(dev->csi_sd, pad, set_fmt, NULL, &csi_fmt);if(dev->is_isp_used) {ret = v4l2_subdev_call(dev->isp_sd, core, ioctl,VIDIOC_SUNXI_ISP_MAIN_CH_CFG , &main_cfg);} else {v4l2_subdev_call(dev->csi_sd, core, ioctl,VIDIOC_SUNXI_CSI_GET_FRM_SIZE , &dev->buf_byte_size);}....return ret;
}
step6: ov2640.c,
static const struct v4l2_subdev_video_ops sensor_video_ops = {
.....
.s_mbus_fmt = sensor_s_fmt, //回调在 此处,
}
sensor_s_fmt() 最终函数
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,.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,
};static const struct v4l2_subdev_ops sensor_ops = { // 注册subdev用到.core = &sensor_core_ops,.video = &sensor_video_ops,
};/** Set a format.*/
static int sensor_s_fmt(struct v4l2_subdev *sd,struct v4l2_mbus_framefmt *fmt)
{....ret = sensor_try_fmt_internal(sd, fmt, &sensor_fmt, &wsize);....sensor_write_array(sd, sensor_fmt->regs , sensor_fmt->regs_size); //通过I2C写摄像头....return 0;
}下面是注册回调函数过程, 关注 v4l2_i2c_subdev_init(sd, client, sensor_ops) 和 v4l2_subdev_init(sd, ops);
static int sensor_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct v4l2_subdev *sd;struct sensor_info *info;info = kzalloc(sizeof(struct sensor_info), GFP_KERNEL);if (info == NULL)return -ENOMEM;sd = &info->sd;glb_sd = sd;cci_dev_probe_helper(sd, client, &sensor_ops, &cci_drv);info->fmt = &sensor_formats[0];info->af_first_flag = 1;info->init_first_flag = 1;info->auto_focus = 0;return 0;
}int cci_dev_probe_helper(struct v4l2_subdev *sd, struct i2c_client *client, const struct v4l2_subdev_ops *sensor_ops, struct cci_driver *cci_drv)
{if(client){v4l2_i2c_subdev_init(sd, client, sensor_ops);cci_drv->sd = sd;v4l2_set_subdev_hostdata(sd, cci_drv);}else{cci_subdev_init(sd, cci_drv, sensor_ops);}cci_sys_register(cci_drv);return 0;
}void cci_subdev_init(struct v4l2_subdev *sd, struct cci_driver *drv_data,const struct v4l2_subdev_ops *ops)
{cci_dbg(0, "cci_subdev_init!\n"); v4l2_subdev_init(sd, ops);/* initialize name */snprintf(sd->name, sizeof(sd->name), "%s", drv_data->name);drv_data->sd = sd;drv_data->id = cci_dev_num_id;drv_data->is_drv_registerd = 1;v4l2_set_subdevdata(sd,drv_data);list_add(&drv_data->cci_list, &cci_drv_list);cci_dev_num_id++;cci_dbg(0,"cci_dev_num_id = %d, name = %s, cci_subdev_init,sd pt=%p!\n",cci_dev_num_id,drv_data->name, drv_data->sd);
}