V4L2 soc-camera 数据结构

本文详细解析了soc_camera_host与soc_camera_device在系统中的作用与联系,包括它们如何通过链表进行组织,以及如何通过各种函数进行注册与配置。重点介绍了每个结构体的成员变量及其在系统中的意义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

static LIST_HEAD(hosts)
所有的host都在hosts指向的链表上

static LIST_HEAD(devices);
所有soc camera device都挂在devices指向的链表上。

系统内可能有多个soc_camera_host,每个soc_camera_host可以对应1 ~ n个soc_camera_device。每个soc_camera_device通过soc_camera_video_start函数创建设备节点/dev/videoX

soc_camera_host对应着系统camera处理模块,尽管理论上可以有多个camera host,但是大部分系统仅有一个camera host

在soc_camera_host_register中调用v4l2_device_register为这个soc_camera_host注册一个v4l2_device设备。

在soc_camera_probe中调用soc_camera_init_i2c,为soc_camera_device注册一个v4l2_subdev,我们从soc_camera_probe代码可以看出,只有i2C的设备可以使用这种方式,对于非I2C设备来说,需要soc_camera_link提供add_device来增加子设备。


soc_camera_host, soc_camera_device,v4l2_device,v4l2_subdev关系如下:

  • 理论上系统内可以有多个soc_camera_host,物理上soc_camera_host就是系统的camera处理模块驱动
  • 一个soc_camera_host可以对应多个soc_camera_device,物理上soc_camera_device是一个camera接口,每个soc_camera_host对应一个v4l2_dev
  • 每个soc_camera_device,系统会为他们创建设备节点/dev/videoX。
  • 每个soc_camera_device有多个v4l2_subdev,物理上v4l2_subdev可以是sensor,video AD芯片
  • v4l2_subdev可以通过i2c挂接到v4l2_device,也可以通过soc_camera_link提供的add_device来增加,这依赖于sensor和video AD芯片挂接到MCU camera接口的方式。



struct soc_camera_device {
    struct list_head list;
    struct device dev;
    struct device *pdev;        /* Platform device */
    s32 user_width;
    s32 user_height;
    enum v4l2_colorspace colorspace;
    unsigned char iface;        /* Host number */
    unsigned char devnum;       /* Device number per host */
    struct soc_camera_sense *sense; /* See comment in struct definition */
    struct soc_camera_ops *ops;
    struct video_device *vdev;
    const struct soc_camera_format_xlate *current_fmt;
    struct soc_camera_format_xlate *user_formats;
    int num_user_formats;
    enum v4l2_field field;      /* Preserve field over close() */
    void *host_priv;        /* Per-device host private data */
    /* soc_camera.c private count. Only accessed with .video_lock held */
    int use_count;
    struct mutex video_lock;    /* Protects device data */
    struct file *streamer;      /* stream owner */
    struct videobuf_queue vb_vidq;
};

每一个soc_camera_device都会对应一个/dev/videoX设备节点,除非在soc_camera_probe时失败。

@list:soc_came_device通过这个成员连接到全局devices链表上

@pdev:每一个soc_camera_device,都会有一个platform device,pdev就是这个platform device结构的dev成员

@user_width, @user_height:这个camera的缺省width和height

@iface:camera bus id,也是host id,一个host可以对应多个soc_camera_device

@devnum:device number,每个soc_camera_device都会自动分配一个device number

@ops:操作集合,sensor,video AD(也许还有host)驱动要实现这个接口

@use_count:soc camera使用计数,每次打开这个设备加1,关闭则减1


soc_camera_host

struct soc_camera_host {
    struct v4l2_device v4l2_dev;
    struct list_head list;
    unsigned char nr;               /* Host number */
    void *priv;
    const char *drv_name;
    struct soc_camera_host_ops *ops;
}

@v4l2_dev:每个host都是一个v4l2_device

@list:soc_camera_host通过这个成员链接到全局hosts链表上

@nr:host number,每个host接口对应一个host

@priv:一般存放平台特定的camera参数,比如irq,DMA


struct soc_camera_ops {
    int (*suspend)(struct soc_camera_device *, pm_message_t state);
    int (*resume)(struct soc_camera_device *);
    unsigned long (*query_bus_param)(struct soc_camera_device *);
    int (*set_bus_param)(struct soc_camera_device *, unsigned long);
    int (*enum_input)(struct soc_camera_device *, struct v4l2_input *);
    const struct v4l2_queryctrl *controls;
    int num_controls;
};  
这个是由sensor,video AD或者host实现的回调函数集。

@suspend,@resume:系统休眠唤醒时的回调函数,如果不考虑电源管理,可以不实现这两个函数

@query_bus_param:获取sensor和host之间的总线信息,比如HSYNC VSYNC极性,数据总线宽度,数据线极性等

@set_bus_param:设置sensor和host之间的总线参数。

@enum_input:枚举给定的input number,上层通过@v4l2_input->index指定要查看哪个input,一般可以不实现。

@controls:controls

@num_controls:@controls数目


<think>好的,我现在需要帮助用户编写一个不基于I2C和MIPI接口的V4L2驱动程序。首先,我得回忆一下V4L2驱动的基本结构。用户已经排除了I2C和MIPI,所以可能需要考虑其他接口,比如并口摄像头或者USB,但根据用户的问题,可能是指其他类型的接口,比如直接连接到SoC的并行接口或者自定义硬件。 首先,我应该确定V4L2驱动框架的主要组成部分。根据引用[2],V4L2的驱动代码通常位于drivers\media目录下,其中platform目录存放不同SoC的驱动代码。这说明可能需要将驱动作为平台设备来注册,而不是I2C或MIPI子设备。 接下来,需要了解V4L2驱动的核心结构。通常需要实现v4l2_device、v4l2_subdev、以及各种操作函数,比如设置格式、缓冲区管理、流控制等。但如果是非I2C/MIPI,可能不需要子设备,而是直接与硬件寄存器交互。 然后,考虑硬件接口部分。如果设备不是通过I2C或MIPI连接,可能需要通过内存映射IO或者GPIO来控制。比如,并口摄像头可能需要直接读写数据总线,配合控制信号线如VSYNC、HSYNC等。这时候,驱动需要处理这些GPIO或内存区域的中断和DMA传输。 接着,注册V4L2设备。需要创建video_device结构体,并填充其v4l2_file_operations和v4l2_ioctl_ops。用户空间通过/dev/videoX节点访问驱动,所以必须正确实现open、release、ioctl等函数,尤其是查询能力、设置格式、请求缓冲区等ioctl命令。 然后,处理视频数据的捕获。这可能涉及DMA缓冲区设置,使用videobuf2框架来管理缓冲区。需要实现队列初始化、缓冲区入队出队、启动和停止流传输的函数。如果是非标准接口,可能需要自定义数据传输方式,比如轮询或中断驱动的方式获取数据。 另外,可能需要处理控制参数,比如亮度、对比度等,这需要实现v4l2_controls相关的操作。不过如果硬件不支持这些控制,可以忽略或返回默认值。 最后,测试驱动时,需要使用工具如v4l2-ctl或GStreamer的插件来验证功能,比如引用[4]中的gst-launch命令。确保驱动能够正确输出图像,并处理分辨率和帧率等参数。 在这个过程中,需要注意内核版本差异,确保使用的API与目标内核兼容。同时,调试可能需要查看dmesg输出,排查初始化或数据传输中的错误。 总结步骤应该是:定义平台设备、实现V4L2设备注册、填充操作函数、处理硬件接口的数据捕获,最后集成到内核并测试。需要参考内核文档中的V4L2部分,以及现有的平台驱动示例,如引用[2]中的platform目录下的代码。</think>### 如何编写非I2C非MIPI接口的Linux V4L2视频驱动程序? #### 1. **驱动框架分析** V4L2驱动核心是通过`video_device`结构体描述设备功能,并通过`v4l2_ioctl_ops`实现用户空间接口。非I2C/MIPI接口的驱动通常需要注册为**平台设备**(Platform Device),直接与SoC的硬件模块交互[^2]。 #### 2. **关键步骤实现** ##### (1) 定义平台设备 ```c // 在设备树(DTS)中定义硬件资源,如寄存器地址、中断号 my_camera: my-camera@0x12340000 { compatible = "my-camera-driver"; reg = <0x12340000 0x1000>; interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>; }; ``` ##### (2) 初始化V4L2设备 ```c struct v4l2_device *v4l2_dev; struct video_device *vdev; // 注册V4L2核心设备 v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL); video_set_drvdata(vdev, priv_data); // 填充video_device结构 vdev = video_device_alloc(); vdev->v4l2_dev = v4l2_dev; vdev->fops = &my_fops; vdev->ioctl_ops = &my_ioctl_ops; vdev->release = video_device_release; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ``` ##### (3) 实现操作函数集 ```c static const struct v4l2_ioctl_ops my_ioctl_ops = { .vidioc_querycap = my_querycap, .vidioc_enum_fmt_vid = my_enum_fmt, .vidioc_g_fmt_vid_cap = my_get_fmt, .vidioc_s_fmt_vid_cap = my_set_fmt, .vidioc_reqbufs = my_reqbufs, .vidioc_qbuf = my_qbuf, .vidioc_streamon = my_streamon, .vidioc_streamoff = my_streamoff, }; ``` ##### (4) 数据捕获实现 - **DMA/内存映射**: 使用`dma_alloc_coherent`分配物理连续内存 - **中断处理**: 在`probe()`中注册中断服务程序,处理帧同步信号 ```c static irqreturn_t my_camera_isr(int irq, void *dev_id) { struct my_priv *priv = dev_id; // 读取硬件状态,将数据从硬件缓冲区复制到V4L2缓冲区 schedule_work(&priv->work_queue); return IRQ_HANDLED; } ``` #### 3. **关键问题解决** - **硬件寄存器操作**: 使用`ioremap`映射寄存器物理地址到内核虚拟地址 - **格式协商**: 在`vidioc_s_fmt_vid_cap`中配置硬件分辨率(如1280x720)和像素格式(如YUYV) - **流控制**: 通过`vb2_queue`管理缓冲区队列,实现`start_streaming`和`stop_streaming` #### 4. **驱动测试** ```bash # 加载驱动后,使用v4l2-ctl验证 v4l2-ctl --list-devices v4l2-ctl --set-fmt-video=width=640,height=480,pixelformat=YUYV v4l2-ctl --stream-mmap --stream-count=100 --stream-to=test.raw ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值