Linux驱动分析之V4L2驱动框架

一口Linux 2022-09-26 00:00


前言

    前面我们了解了一些Camera的基础知识《Camera基础知识》。接下来来看看Linux为Camera提供的驱动框架。



V4L2简介

    V4L2( Video for Linux Two),是一套Linux内核视频设备的驱动框架。该驱动框架为应用层提供一套统一的操作接口(一系列的ioctl)。V4L2在设计时,是要支持更广泛的设备,它们其中只有一部分在本质上是真正的视频设备,所以它不仅仅是为Camera设计。



V4L2框架

网络图片


    在应用层,我们可以在 /dev 目录发现 videoX的设备节点,应用程序打开设备节点进行数据捕获,显示视频画面。video0、video1、video2...这些设备节点是在核心层注册。

    核心层(v4l2-dev.c)起承上启下的作用,它会为每一个驱动注册进来的设备设置一个统一的接口 v4l2_fops ,这些统一的接口最终将调用到驱动中的 video_device 的 fops 。



重要结构体


  • video_device

//表示一个视频设备struct video_device {#if defined(CONFIG_MEDIA_CONTROLLER);  struct media_entity entity;  struct media_intf_devnode *intf_devnode;  struct media_pipeline pipe;#endif;  const struct v4l2_file_operations *fops; //文件操作接口(dev/videoX)  u32 device_caps; //设备功能,用于v4l2_capabilities(应用层定义的结构体)  struct device dev;  struct cdev *cdev; //字符设备  struct v4l2_device *v4l2_dev; //V4L2设备  struct device *dev_parent;  struct v4l2_ctrl_handler *ctrl_handler; //设备节点对应的控制句柄  struct vb2_queue *queue;  struct v4l2_prio_state *prio;  char name[32];  //Video设备名称  enum vfl_devnode_type vfl_type; //V4L设备类型  enum vfl_devnode_direction vfl_dir; //V4L 接收者/发送者/m2m  int minor; //子设备号,主设备为81  u16 num;   unsigned long flags;  int index;  spinlock_t fh_lock;  struct list_head        fh_list;  int dev_debug;  v4l2_std_id tvnorms;  void (*release)(struct video_device *vdev); //video_device release()回调  const struct v4l2_ioctl_ops *ioctl_ops; //IOCTL回调  unsigned long valid_ioctls[BITS_TO_LONGS(BASE_VIDIOC_PRIVATE)];  struct mutex *lock;};



  • v4l2_device

struct v4l2_device {  struct device *dev;  struct media_device *mdev;  struct list_head subdevs; //用于追踪已注册的subdev  spinlock_t lock;  char name[V4L2_DEVICE_NAME_SIZE]; //设备名  void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg);  struct v4l2_ctrl_handler *ctrl_handler; //控制句柄  struct v4l2_prio_state prio; //设备的优先状态  struct kref ref; //引用  void (*release)(struct v4l2_device *v4l2_dev);//引用计数为0后调用};

嵌入到video_device中,表示一个v4l2设备的实例。



  • v4l2_subdev

struct v4l2_subdev {#if defined(CONFIG_MEDIA_CONTROLLER);  struct media_entity entity;#endif;  struct list_head list;  //subdev列表  struct module *owner;  bool owner_v4l2_dev;  u32 flags;  struct v4l2_device *v4l2_dev;  //依附的v4l2_device  const struct v4l2_subdev_ops *ops; //subdev操作函数  const struct v4l2_subdev_internal_ops *internal_ops;  struct v4l2_ctrl_handler *ctrl_handler; //控制句柄  char name[V4L2_SUBDEV_NAME_SIZE]; //subdev名称  u32 grp_id;  void *dev_priv;  void *host_priv;  struct video_device *devnode;  struct device *dev;  struct fwnode_handle *fwnode;  struct list_head async_list;  struct v4l2_async_subdev *asd;  struct v4l2_async_notifier *notifier;  struct v4l2_async_notifier *subdev_notifier;  struct v4l2_subdev_platform_data *pdata;//subdev平台数据};

依附在v4l2_device之下,并表示一个v4l2设备的子设备,一个v4l2_device下可以有多个sub_device。



  • v4l2_fh

struct v4l2_fh {  struct list_head        list; //文件句柄列表  struct video_device     *vdev; //依附的video_device  struct v4l2_ctrl_handler *ctrl_handler;  enum v4l2_priority      prio; //文件句柄的优先级  wait_queue_head_t wait;  struct mutex            subscribe_lock;  struct list_head        subscribed; //订阅的事件列表  struct list_head        available; //可用的事件  unsigned int            navailable; //可用的事件数  u32 sequence;  struct v4l2_m2m_ctx     *m2m_ctx;};

用于追踪的文件句柄



v4l2_device和v4l2_subdev的关系:

subdev的设计目的是为了多路复用,就是用一个v4l2_device可以挂接多个v4l2_subdev。所谓的多路复用就是使用一个摄像头控制器来控制多个摄像头。比如手机上有前置和后置摄像头。


在V4L2驱动中,使用v4l2_device来表示摄像头控制器(ISP)。使用v4l2_subdev来表示具体的某一个摄像头(Sensor)。




  • v4l2_file_operations

//V4L2设备操作函数struct v4l2_file_operations {  struct module *owner;  ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);  ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);  __poll_t (*poll) (struct file *, struct poll_table_struct *);  long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);#ifdef CONFIG_COMPAT;  long (*compat_ioctl32) (struct file *, unsigned int, unsigned long);#endif;  unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long);  int (*mmap) (struct file *, struct vm_area_struct *);  int (*open) (struct file *);  int (*release) (struct file *);};



  • v4l2_ioctl_ops

//IOCTL操作函数struct v4l2_ioctl_ops {    ......    int (*vidioc_overlay)(struct file *file, void *fh, unsigned int i);    int (*vidioc_g_fbuf)(struct file *file, void *fh, struct v4l2_framebuffer *a);    int (*vidioc_s_fbuf)(struct file *file, void *fh, const struct v4l2_framebuffer *a);    int (*vidioc_streamon)(struct file *file, void *fh, enum v4l2_buf_type i);    int (*vidioc_streamoff)(struct file *file, void *fh, enum v4l2_buf_type i);    ......    int (*vidioc_enum_framesizes)(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize);    int (*vidioc_enum_frameintervals)(struct file *file, void *fh, struct v4l2_frmivalenum *fival);    int (*vidioc_s_dv_timings)(struct file *file, void *fh, struct v4l2_dv_timings *timings);    int (*vidioc_g_dv_timings)(struct file *file, void *fh, struct v4l2_dv_timings *timings);    int (*vidioc_query_dv_timings)(struct file *file, void *fh, struct v4l2_dv_timings *timings);    int (*vidioc_enum_dv_timings)(struct file *file, void *fh, struct v4l2_enum_dv_timings *timings);    int (*vidioc_dv_timings_cap)(struct file *file, void *fh, struct v4l2_dv_timings_cap *cap);    int (*vidioc_g_edid)(struct file *file, void *fh, struct v4l2_edid *edid);    int (*vidioc_s_edid)(struct file *file, void *fh, struct v4l2_edid *edid);    int (*vidioc_subscribe_event)(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub);    int (*vidioc_unsubscribe_event)(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub);    long (*vidioc_default)(struct file *file, void *fh, bool valid_prio, unsigned int cmd, void *arg);};



  • v4l2_subdev_ops

//subdev操作函数struct v4l2_subdev_ops {    const struct v4l2_subdev_core_ops       *core;     //subdev核心操作回调    const struct v4l2_subdev_tuner_ops      *tuner;    //radio模式打开v4l设备时的操作回调    const struct v4l2_subdev_audio_ops      *audio;    //音频相关设置回调    const struct v4l2_subdev_video_ops      *video;    //video模式打开v4l设备时的操作回调    const struct v4l2_subdev_vbi_ops        *vbi;    //通过vbi设备节点以video模式打开v4l设备时的操作回调    const struct v4l2_subdev_ir_ops         *ir;     //IR(红外)设备操作函数    const struct v4l2_subdev_sensor_ops     *sensor; //sensor操作函数    const struct v4l2_subdev_pad_ops        *pad;    //pad操作函数};

v4l2可以用于很多类型的设备,所以上面的回调只需根据实际设备的需要实现部分即可。

上面的v4l2_ioctl_ops中实现的部分ioctl最终会调用到v4l2_subdev_ops中的回调函数。



API函数

//注册/注销video_deviceint video_register_device(struct video_device *vdev, enum vfl_devnode_type type, int nr)void video_unregister_device(struct video_device *vdev)//分配/释放video_devicestruct video_device * __must_check video_device_alloc(void);void video_device_release(struct video_device *vdev)
//注册/注销v4l2_deviceint v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
//注册/注销v4l2_subdev(关联v4l2_device和v4l2_subdev)int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd)void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
//初始化v4l2_subdevvoid v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops);
/********************************I2C subdev*************************************///初始化v4l2_subdev, 该subdev是I2C设备void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, const struct v4l2_subdev_ops *ops)

/*******************************SPI subdev************************************************///初始化v4l2_subdev, 该subdev是SPI设备void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi, const struct v4l2_subdev_ops *ops)



Camera驱动分析

Linux版本:4.19

Sensor: OV13850


(1)装载和卸载函数

//DTS匹配表static const struct of_device_id ov13850_of_match[] = {  {.compatible = "omnivision,ov13850-v4l2-i2c-subdev"},  {},};
MODULE_DEVICE_TABLE(i2c, ov13850_id);
static struct i2c_driver ov13850_i2c_driver = { .driver = { .name = ov13850_DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = ov13850_of_match }, .probe = ov13850_probe, .remove = ov13850_remove, .id_table = ov13850_id,};
module_i2c_driver(ov13850_i2c_driver);

OV13850是使用I2C接口进行控制,所以使用i2c_driver进行注册。



(2)probe()

static int ov13850_probe(struct i2c_client *client,  const struct i2c_device_id *id){  dev_info(&client->dev, "probing...\n");
ov13850_filltimings(&ov13850_custom_config); //填充时序信息 v4l2_i2c_subdev_init(&ov13850.sd, client, &ov13850_camera_module_ops); //初始化v4l2_subdev ov13850.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ov13850.custom = ov13850_custom_config;
mutex_init(&ov13850.lock); dev_info(&client->dev, "probing successful\n"); return 0;}

上面主要是根据全局变量ov13850_custom_config中的信息填充时序信息。然后初始化v4l2_subdev, ov13850是I2C接口,所以使用v4l2_i2c_subdev_init 进行初始化。v4l2_i2c_subdev_init就是对v4l2_subdev_init的封装。


//v4l2_subdev_opsstatic struct v4l2_subdev_ops ov13850_camera_module_ops = {  .core = &ov13850_camera_module_core_ops,    //核心操作  .video = &ov13850_camera_module_video_ops,  //video操作  .pad = &ov13850_camera_module_pad_ops};
static struct ov_camera_module_custom_config ov13850_custom_config = { .start_streaming = ov13850_start_streaming, //sensor开始输出数据流 .stop_streaming = ov13850_stop_streaming, //sensor停止输出数据流 .s_ctrl = ov13850_s_ctrl, .s_ext_ctrls = ov13850_s_ext_ctrls, //sensor控制(设置自动曝光控制) .g_ctrl = ov13850_g_ctrl, .g_timings = ov13850_g_timings, //获取sensor时序 .check_camera_id = ov13850_check_camera_id, //读取Sensor ID .s_vts = ov13850_auto_adjust_fps, //自动调节刷新率 .set_flip = ov13850_set_flip, //设置sensor镜像#ifdef OV13850_ONE_LANE .configs = ov13850_onelane_configs, //单lane的配置信息(分辨率,刷新率等) .num_configs = ARRAY_SIZE(ov13850_onelane_configs),
#else .configs = ov13850_configs, //多lane的配置信息 .num_configs = ARRAY_SIZE(ov13850_configs),#endif .power_up_delays_ms = {5, 20, 0}, /* *0: Exposure time valid fileds; 曝光时间 *1: Exposure gain valid fileds; 曝光增益 *(2 fileds == 1 frames) */ .exposure_valid_frame = {4, 4}};

上面设置的回调基本都是去设置寄存器。



(3)打开数据流

static int ov13850_start_streaming(struct ov_camera_module *cam_mod){  int ret = 0;
ov_camera_module_pr_debug(cam_mod, "active config=%s\n", cam_mod->active_config->name);
ret = ov13850_g_VTS(cam_mod, &cam_mod->vts_min); if (IS_ERR_VALUE(ret)) goto err;
mutex_lock(&cam_mod->lock); ret = ov_camera_module_write_reg(cam_mod, 0x0100, 1); //写0x0100寄存器, 选择streaming模式 0:standby 1:streaming mutex_unlock(&cam_mod->lock); if (IS_ERR_VALUE(ret)) goto err;
msleep(25);
return 0;err: ov_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); return ret;}

主要就是操作寄存器,开启数据流传输。其他的一些操作函数也基本类似。



总结

    我们从上面的内容中可以看出,sensor端的驱动没有特别复杂,主要是一些参数和控制相关的内容。sensor主要是生产数据,而数据的处理主要交给ISP。


end


一口Linux 


关注,回复【1024】海量Linux资料赠送

精彩文章合集


文章推荐

【专辑】ARM
【专辑】粉丝问答
【专辑】所有原创
专辑linux入门
专辑计算机网络
专辑Linux驱动
【干货】嵌入式驱动工程师学习路线
【干货】Linux嵌入式所有知识点-思维导图
一口Linux 写点代码,写点人生!
评论 (0)
  •   有效数据智能分拣系统平台深度解析   一、系统概述   北京华盛恒辉有效数据智能分拣系统平台融合人工智能、机器视觉、物联网及大数据分析技术,为物流包裹、数据信息等提供高效精准的智能化分拣处理方案。通过自动化设备与智能算法协同运作,取代传统人工分拣模式,显著提升分拣效率、降低错误率,满足电商、快递及供应链不断增长的业务需求。   应用案例   目前,已有多个有效数据智能分拣系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润有效数据智能分拣系统。这些成功案例为有效数据智能分
    华盛恒辉l58ll334744 2025-04-21 16:22 124浏览
  • 北京贞光科技有限公司作为紫光同芯授权代理商,深耕电子元器件领域数十载,专为汽车与工业客户提供车规级安全芯片及配套服务。公司整合硬件供应、软件SDK与技术支持为一体,配备专业团队提供选型咨询与现场指导,助力客户实现完整的芯片应用解决方案。在全球芯片供应链重构的大背景下,我国车规级芯片产业正迎来前所未有的发展机遇。北京贞光科技有限公司作为紫光同芯授权代理商,深耕电子元器件领域数十载,专为汽车与工业客户提供车规级安全芯片及配套服务。公司整合硬件供应、软件SDK与技术支持为一体,配备专业团队提供选型咨询
    贞光科技 2025-04-21 16:10 98浏览
  •   北京华盛恒辉机场保障能力评估系统软件深度解析   在航空运输业快速发展的背景下,机场保障任务愈发复杂,传统人工评估方式已无法满足高效精准的管理需求。机场保障能力评估系统软件作为提升机场运行效率、保障飞行安全的关键工具,其重要性日益凸显。   应用案例   目前,已有多个机场保障能力评估系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润机场保障能力评估系统。这些成功案例为机场保障能力评估系统的推广和应用提供了有力支持。   一、系统功能模块   数据采集与整合模块  
    华盛恒辉l58ll334744 2025-04-22 10:28 40浏览
  •   有效数据智能分拣系统详解   北京华盛恒辉有效数据智能分拣系统融合人工智能、大数据分析与机器学习等前沿技术,实现海量数据自动化分类、筛选、整理及分配。凭借强大的数据处理效能,助力企业精准提取关键信息,优化决策流程,提升运营效率。以下从系统架构、核心功能、技术特性、应用场景及发展趋势展开解读。   应用案例   目前,已有多个有效数据智能分拣系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润有效数据智能分拣系统。这些成功案例为有效数据智能分拣系统的推广和应用提供了有力支持。
    华盛恒辉l58ll334744 2025-04-21 16:46 110浏览
  • 引言:老龄化社会的健康守护需求随着全球老龄化进程加速,老年人的健康管理与生活质量成为社会焦点。记忆衰退、用药混乱、日程遗漏等问题频发,催生了智能健康设备的市场需求。WTR096录音语音芯片,凭借其高度集成的录放音、计时时钟与计划管理功能,为老年人量身打造了一站式健康管理方案,重新定义智能语音时钟的价值。功能亮点:1. 用药安全守护:多维度提醒,拒绝遗忘多时段精准提醒:支持一天内设置多个用药时间(如早、中、晚),适配复杂用药需求。个性化语音定制:家属可录制专属提醒语音(如“上午9点,请服用降压药”
    广州唯创电子 2025-04-22 08:41 67浏览
  • 职场烂摊子,每个人都难免遇上如果你在职场待久了,总会碰到一些让人无奈的情况:比如刚接手的项目混乱不堪、前任同事留下的任务一团乱麻,甚至有时因为自己的疏忽造成麻烦。面对这种烂摊子,烦躁、焦虑、甚至怀疑人生的情绪都会扑面而来。但如果你冷静想想,会发现真正消耗你的,往往不是工作本身,而是持续不断的心理内耗。那么问题来了,如何摆脱内耗,快速有效地“自救”?摆脱内耗,从情绪中抽离我曾经历过一个典型的职场烂摊子:前任项目负责人突然辞职,项目资料缺失严重,进度远远落后,客户抱怨不断。当时接手后的第一反应就是慌
    优思学院 2025-04-21 18:21 35浏览
  • 导读在当今快速发展的智能通讯领域,时间敏感网络(TSN)已成为确保网络通信高可靠性和低延迟的关键技术。IEEE 802.1 Qci作为TSN的一个重要组成部分,提供了一套强大的机制来管理网络流量,确保关键数据流的优先级和带宽得到保障。本文将深入探讨IEEE 802.1 Qci协议的基本概念、工作原理以及虹科提供的Qci解决方案,帮您理解如何通过精确的流量控制来提升网络的稳定性和效率。虹科TSN解决方案01# 技术简介时间敏感网络(TSN)通过IEEE 802.1 Qci标准定义了一种关
    虹科工业智能互联 2025-04-21 16:17 90浏览
  • 引言:工业安全与智能化需求的双重驱动在工业安全、环境保护及家庭安防领域,气体泄漏引发的安全事故始终是重大隐患。随着传感器技术、物联网及语音交互的快速发展,气体检测报警器正朝着智能化、低成本、高可靠的方向演进。WT588F02B-8S语音芯片,以“离在线语音更换+多协议通信”为核心优势,为气体检测报警器提供了一套高效、灵活的低成本语音解决方案,助力开发者快速响应市场需求。产品功能与市场需求1. 核心功能:从监测到预警的全流程覆盖实时气体监测:支持一氧化碳、臭氧、硫化氢等多种气体浓度检测,精度可达p
    广州唯创电子 2025-04-22 09:14 8浏览
  • 在汽车行业的变革浪潮中,智界汽车的诞生备受瞩目。作为华为与奇瑞两大巨头携手合作的结晶,智界汽车自孕育之初便承载着众人的期待,被视为融合前沿科技与卓越制造的典范,有望在竞争激烈的新能源汽车市场中开辟出一片新天地。2024年,智界品牌首款车型智界S7正式上市,凭借华为的技术赋能,如先进的鸿蒙智能座舱、强大的HUAWEI ADS高阶智能驾驶辅助系统,以及奇瑞多年积累的深厚造车底蕴,在上市前赚足了眼球。智界S7的亮相,犹如一颗投入平静湖面的石子,激起了层层涟漪,消费者对其充满了好奇与期待,行业内也纷纷将
    用户1742991715177 2025-04-21 20:28 54浏览
  • 在消费金融的赛道上,马上消费曾是备受瞩目的明星企业。自2015年成立以来,它以年均 30% 的净利润增速一路狂奔,成为持牌消费金融公司的标杆,2023年更是斩获19.82亿元净利润,风光无限。然而,2024年却成了马上消费的一道分水岭。2024年上半年,其营收为77.38亿元,同比下降2.11%;净利润更是同比骤降20.66%,仅为10.68亿元,创下历史最大跌幅 。与此同时,不良贷款率攀升至2.5%,不良余额高达16.54亿元,核心资本充足率降至12.72%,融资
    用户1742991715177 2025-04-21 21:29 62浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦