2、Camera设备节点与驱动基础:V4L2框架简介、Camera设备节点(/dev/video*)的创建与权限、Sensor驱动与ISP驱动的基本交互

好,我们进入第二章。这一章要聊的是Camera HAL层往下走的“地基”——设备节点和驱动。说白了,就是你的App调用Camera,数据到底是怎么从硬件摸到HAL层的。我当年刚接触这块时,也被一堆/dev/video*搞晕过,后来才发现,理解了V4L2,就等于拿到了打开Camera硬件大门的钥匙。

2.1 V4L2框架:Linux下视频设备的“通用语言”

V4L2,全称Video for Linux 2。你可以把它理解成Linux内核为视频设备制定的一套标准接口。为什么需要它?你想想看,市面上有海思的Sensor、有索尼的Sensor,ISP更是各家不同。如果没有一个统一的标准,HAL层开发者得为每一款Sensor写一套代码,那简直是灾难。

V4L2的作用,就是把这些差异都封装在驱动层。它定义了一套ioctl命令,比如打开设备、设置格式、申请缓冲区、开始/停止采集。HAL层只需要通过这些标准命令跟驱动对话,不用关心底层硬件具体怎么操作。

核心概念:V4L2把Camera设备抽象为一个“视频节点”。HAL层通过操作这个节点,就能控制Camera的采集流程。

我个人习惯把V4L2的架构分成三层:

  • 应用层(HAL层):调用open()、ioctl()、mmap()等系统调用。
  • 内核V4L2框架层:提供统一的v4l2_device、v4l2_fops等结构体,负责解析ioctl命令。
  • 具体设备驱动层:Sensor驱动、ISP驱动,实现真正的硬件操作。

嗯,这里要注意,V4L2不仅仅用于Camera。它也能处理视频输出、视频覆盖、甚至FM收音机。但在Android Camera HAL里,我们主要用到它的视频采集功能。

2.2 Camera设备节点:/dev/video* 的创建与权限

你在Android设备上执行 ls -l /dev/video*,会看到类似这样的输出:

crw-rw---- 1 camera camera 81, 0 2024-01-01 10:00 /dev/video0
crw-rw---- 1 camera camera 81, 1 2024-01-01 10:00 /dev/video1
crw-rw---- 1 camera camera 81, 2 2024-01-01 10:00 /dev/video2

这些节点是怎么来的?不是凭空产生的。它们由内核中的V4L2驱动框架在驱动加载时动态创建。具体来说,驱动调用 video_register_device() 函数,内核就会在 /dev 目录下生成对应的设备文件。

节点的主设备号是81,次设备号从0开始递增。每个节点对应一个Camera设备,或者一个Camera设备的不同功能单元。比如:

  • /dev/video0:主Camera(后置)
  • /dev/video1:前置Camera
  • /dev/video2:可能是一个Metadata节点,或者深度Camera

避坑指南:我曾经在一个项目里,发现HAL层打开Camera时总是返回权限错误。查了半天,原来是init.rc里没有给camera用户组赋予对/dev/video*的读写权限。记住,Android的权限管理很严格,HAL进程通常运行在camera用户组下,节点权限必须是 crw-rw---- camera camera

权限设置通常在init.rc或ueventd.rc中完成。比如:

# ueventd.rc
/dev/video*              0660   camera   camera

这样,只有camera用户组的进程才能访问这些节点。其他进程想碰?没门。

2.3 Sensor驱动与ISP驱动的基本交互

现在,我们聊聊驱动内部的事。一个典型的Camera硬件链路是这样的:

Sensor → MIPI接口 → ISP → 内存

Sensor负责采集光信号,转换成RAW数据。ISP负责把RAW数据加工成YUV或JPEG。这两者是怎么配合的?

在驱动层面,Sensor驱动和ISP驱动通常是两个独立的模块。它们通过V4L2的subdev机制进行交互。Sensor驱动注册为一个V4L2 subdev,ISP驱动也注册为一个V4L2 subdev。然后,一个“主”驱动(通常是ISP驱动)把它们串联起来。

交互流程大致如下:

  1. 初始化阶段:HAL层通过主设备节点(比如/dev/video0)发送S_FMT命令。主驱动解析命令,然后通过subdev接口,把格式信息传递给Sensor驱动和ISP驱动。
  2. 配置阶段:Sensor驱动根据格式信息,配置Sensor的寄存器(比如分辨率、帧率、曝光时间)。ISP驱动配置ISP的pipeline(比如去噪、色彩校正)。
  3. 启动阶段:HAL层发送STREAMON命令。主驱动先通知ISP驱动准备接收数据,再通知Sensor驱动开始输出数据。
  4. 数据传输:Sensor通过MIPI CSI接口把RAW数据推给ISP。ISP处理完后,通过DMA把数据写入内存。主驱动通过V4L2的buffer机制,把内存地址返回给HAL层。

关键点:Sensor驱动和ISP驱动之间,通常通过内核提供的v4l2_subdev_call()函数进行通信。比如ISP驱动调用v4l2_subdev_call(sensor_sd, video, s_stream, 1)来通知Sensor开始输出数据。

我举个例子,简化版的Sensor驱动启动代码:

static int sensor_s_stream(struct v4l2_subdev *sd, int enable)
{
    struct sensor_priv *priv = to_sensor_priv(sd);

    if (enable) {
        // 配置Sensor寄存器,开始输出MIPI数据
        reg_write(priv, REG_MIPI_ENABLE, 1);
        reg_write(priv, REG_STREAM_START, 1);
        dev_dbg(sd->dev, "Sensor stream started\n");
    } else {
        // 停止输出
        reg_write(priv, REG_STREAM_START, 0);
        reg_write(priv, REG_MIPI_ENABLE, 0);
    }

    return 0;
}

而ISP驱动这边,会注册一个media controller pipeline,把Sensor subdev和ISP subdev连接起来。HAL层通过media controller接口,可以查询到完整的pipeline拓扑。

注意:不同SoC厂商的实现差异很大。高通平台用CAMSS驱动,MTK平台有自己的ISP驱动架构。但万变不离其宗,底层都是V4L2 subdev这套机制。你只要理解了subdev的交互逻辑,换平台只是换API名字而已。

最后,我总结一下这一章的核心:

  • V4L2是HAL层与Camera驱动之间的桥梁,它定义了一套标准接口。
  • /dev/video*节点是HAL层操作Camera的入口,权限配置不当会导致打开失败。
  • Sensor驱动和ISP驱动通过V4L2 subdev机制协作,一个负责采集,一个负责处理。

下一章,我们会深入HAL层,看看它怎么通过V4L2 ioctl来打开Camera设备。到时候,你会看到这些理论知识是怎么变成代码的。