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驱动)把它们串联起来。
交互流程大致如下:
- 初始化阶段:HAL层通过主设备节点(比如/dev/video0)发送S_FMT命令。主驱动解析命令,然后通过subdev接口,把格式信息传递给Sensor驱动和ISP驱动。
- 配置阶段:Sensor驱动根据格式信息,配置Sensor的寄存器(比如分辨率、帧率、曝光时间)。ISP驱动配置ISP的pipeline(比如去噪、色彩校正)。
- 启动阶段:HAL层发送STREAMON命令。主驱动先通知ISP驱动准备接收数据,再通知Sensor驱动开始输出数据。
- 数据传输: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设备。到时候,你会看到这些理论知识是怎么变成代码的。