全志A40i,linux v4l2驱动 ioctl的调用过程

news/2025/3/15 11:35:39/

最近在移植 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);
}


http://www.ppmy.cn/news/490050.html

相关文章

摄像头V4L2获取的YUY2格式转YUV420格式

摄像头取出格式YUY2(YUYV) Y U00Y V00Y U01Y V01Y U02Y V02Y U03Y V03YU10YV10YU11YV11YU12YV12YU13YV13YU20YV20YU21YV21YU22YV22YU23YV23YU30YV30YU31YV31YU32YV32YU33YV33 转为 NV12(YUV420) 这里的转化主要是我的电脑软件pyuv 只支持yuv420格式的预览…

正点原子imx6ull开发板视频监控项目实战系列5: 摄像头(V4L2)和声卡(ALSA)接口简介

1、摄像头: 1)、对于不同的摄像头,有不同的接口方式,eg:USB,CMOS等,但是他们都遵循同样的标准-V4L2. 所以,不同的APP可以使用同样的API访问不同硬件接口的摄像头。 2)、摄像头参数:…

Linux V4L2 源码分析

Linux V4L2 源码分析 前言层次必要的数据结构源码分析ov2640.c 前言 Video For Linux 2真的是一个很复杂的框架,抽象倒不是它复杂的原因,是因为耦合了其他框架的内容,导致要掌握V4L2必须得需要一个非常广的内核层知识面,以及Linu…

使用Linux的V4L2读取摄像头数据+Opencv图像处理

一、前言:对于Linux系统的V4L2的使用和JPEG软件解码库解码过程,大家可以另行查找和阅读相关的博文。 二、正题:使用Linux系统的V4L2接口读取USB摄像头数据,图像格式一般为MJPEG,使用JPEG软件解码库解码成RGB格式的数据…

一些新技术学习心得

实战为主: 看再多的文章不如敲几行代码。 推进度: 学新东西,遇到不懂的不要死磕,直接跳过去。 对应阶段:从0到入门,从啥都不懂到会的过程不要以原理为借口,长时间进度停滞。 关于原理&#…

我们如何实现业务操作日志功能?

1. 需求 我们经常会有这样的需求,需要对关键的业务功能做操作日志记录,也就是用户在指定的时间操作了哪个功能,操作前后的数据记录,必要的时候可以一键回退,今天我就为大家实现这个的功能,让大家可以直接拿…

Spark 4/5

4. 启动Spark Shell编程 4.1 什么是Spark Shell spark shell是spark中的交互式命令行客户端,可以在spark shell中使用scala编写spark程序,启动后默认已经创建了SparkContext,别名为sc 4.2 启动Spark Shell Shell /opt/apps/spark-3.2.3-bi…

OpenCV项目开发实战--一步一步介绍使用 OpenPose进行多人姿势估计C++/python实现

文末附基于Python和C++两种方式实现的测试代码下载链接 在我们上一篇文章中中,我们使用OpenPose模型对单个人执行人体姿态估计。在这篇文章中,我们将讨论如何进行多人姿态估计。 当一张照片中有多个人时,姿势估计会产生多个独立的关键点。我们需要弄清楚哪组关键点属于同一…