4. Sensor驱动框架:高通平台下Sensor驱动文件结构、probe/remove回调函数
在高通Camera HAL层架构中,Sensor驱动是连接硬件传感器与上层ISP(图像信号处理器)Pipeline的桥梁。本章将深入剖析高通平台下Sensor驱动的标准文件结构、核心数据结构以及probe/remove回调函数的实现机制,帮助学员建立完整的驱动框架认知。
4.1 驱动文件结构总览
高通平台下的Sensor驱动通常遵循V4L2(Video for Linux 2)子设备驱动模型,并融合了高通特有的Camera Subsystem(CAMSS)框架。一个标准的Sensor驱动工程包含以下核心文件:
| 文件/目录 | 功能描述 | 关键内容 |
|---|---|---|
sensor_name.c |
主驱动源文件,实现V4L2子设备操作集 | probe、remove、s_power、s_stream |
sensor_name.h |
头文件,定义寄存器地址、结构体、枚举 | Sensor ID、模式表、曝光/增益寄存器映射 |
sensor_name_reg.c |
寄存器配置表,包含各分辨率/帧率的初始化序列 | 二维数组形式的寄存器-值对 |
Kconfig |
内核配置选项,使能/禁用该Sensor驱动 | config VIDEO_SENSOR_NAME |
Makefile |
编译规则,将驱动编译为模块或内置 | obj-$(CONFIG_VIDEO_SENSOR_NAME) += sensor_name.o |
dts/ |
设备树文件,描述硬件连接与电源时序 | I2C地址、GPIO引脚、时钟频率、电源域 |
实战提示: 在高通SM8250/8450等平台中,Sensor驱动通常位于
kernel/msm-5.x/drivers/media/platform/msm/camera_v2/sensor/ 目录下。调试时优先检查 dts 中的电源配置是否正确,这是 probe 失败的最常见原因。
4.2 核心数据结构:从设备树到驱动实例
高通Sensor驱动的核心数据结构围绕 v4l2_subdev 和 msm_sensor_ctrl_t 展开。下图展示了驱动实例化过程中各数据结构的关系:
4.3 probe回调函数:驱动的“构造函数”
probe 函数是Sensor驱动的入口点,当内核发现设备树中匹配的节点或I2C从设备时被调用。其核心职责包括:
- 解析设备树:获取电源、GPIO、时钟等资源信息。
- 分配私有数据结构:为
msm_sensor_ctrl_t分配内存。 - 初始化I2C通信:设置
i2c_client的地址和传输速率。 - 读取Sensor ID:通过I2C读取芯片ID寄存器,验证硬件是否存在。
- 注册V4L2子设备:调用
v4l2_i2c_subdev_init()或v4l2_spi_subdev_init()。 - 初始化电源管理:注册
regulator和gpio控制接口。
以下是一个简化但完整的 probe 函数代码示例:
static int sensor_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int rc = 0;
struct msm_sensor_ctrl_t *s_ctrl = NULL;
struct device *dev = &client->dev;
// 1. 分配私有数据结构
s_ctrl = devm_kzalloc(dev, sizeof(struct msm_sensor_ctrl_t), GFP_KERNEL);
if (!s_ctrl) {
dev_err(dev, "Failed to allocate sensor control\n");
return -ENOMEM;
}
// 2. 保存i2c_client指针
s_ctrl->sensor_i2c_client = client;
i2c_set_clientdata(client, s_ctrl);
// 3. 解析设备树电源配置
rc = msm_sensor_power_init(s_ctrl, dev);
if (rc < 0) {
dev_err(dev, "Power init failed: %d\n", rc);
goto power_fail;
}
// 4. 读取Sensor ID验证硬件
rc = sensor_read_id(s_ctrl);
if (rc < 0) {
dev_err(dev, "Sensor ID mismatch\n");
goto id_fail;
}
// 5. 初始化V4L2子设备
v4l2_i2c_subdev_init(&s_ctrl->sensor_v4l2_subdev, client,
&sensor_subdev_ops);
s_ctrl->sensor_v4l2_subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
// 6. 注册媒体实体
rc = media_entity_pads_init(&s_ctrl->sensor_v4l2_subdev.entity,
1, &s_ctrl->pad);
if (rc < 0) {
dev_err(dev, "Media entity init failed\n");
goto media_fail;
}
dev_info(dev, "Sensor probe successful\n");
return 0;
media_fail:
id_fail:
msm_sensor_power_deinit(s_ctrl);
power_fail:
devm_kfree(dev, s_ctrl);
return rc;
}
调试陷阱: 在probe函数中,
sensor_read_id() 是最容易失败的地方。常见原因包括:I2C地址错误(设备树与硬件不匹配)、电源时序未满足(需要先上电再读ID)、时钟频率超出Sensor规格。建议在读取ID前后增加 dev_dbg 打印寄存器值。
4.4 remove回调函数:驱动的“析构函数”
remove 函数在驱动卸载或设备热插拔时被调用,必须确保所有资源被正确释放,避免内存泄漏或内核崩溃。其标准执行流程如下:
| 步骤 | 操作 | 对应函数/宏 |
|---|---|---|
| 1 | 停止视频流(如果正在运行) | sensor_stream_off() |
| 2 | 关闭电源(regulator_disable、gpio_set_value) | msm_sensor_power_deinit() |
| 3 | 注销V4L2子设备 | v4l2_device_unregister_subdev() |
| 4 | 清理媒体实体 | media_entity_cleanup() |
| 5 | 释放私有数据结构内存 | devm_kfree() 或 kfree() |
典型实现代码:
static int sensor_remove(struct i2c_client *client)
{
struct msm_sensor_ctrl_t *s_ctrl = i2c_get_clientdata(client);
struct v4l2_subdev *subdev = &s_ctrl->sensor_v4l2_subdev;
if (!s_ctrl) {
dev_err(&client->dev, "Sensor control is NULL\n");
return -EINVAL;
}
// 1. 确保流已停止
if (s_ctrl->stream_state == STREAM_ON) {
sensor_stream_off(s_ctrl);
}
// 2. 关闭电源
msm_sensor_power_deinit(s_ctrl);
// 3. 注销V4L2子设备
v4l2_device_unregister_subdev(subdev);
media_entity_cleanup(&subdev->entity);
// 4. 释放私有数据(devm_* 系列可自动释放,但显式释放更安全)
devm_kfree(&client->dev, s_ctrl);
i2c_set_clientdata(client, NULL);
dev_info(&client->dev, "Sensor removed successfully\n");
return 0;
}
4.5 probe/remove的调用时序与依赖关系
理解 probe 和 remove 在系统启动/关闭过程中的调用顺序,对于调试电源管理问题至关重要。下图展示了从系统启动到Sensor可用的完整时序:
4.6 驱动注册与匹配机制
高通平台支持两种驱动注册方式:I2C驱动 和 平台驱动。下表对比了两种方式的适用场景与匹配逻辑:
| 注册方式 | 匹配依据 | 适用Sensor类型 | probe函数原型 |
|---|---|---|---|
| I2C驱动 | i2c_device_id 或 of_match_table |
标准I2C接口Sensor(OV、IMX系列) | probe(struct i2c_client *, const struct i2c_device_id *) |
| 平台驱动 | of_match_table 中的compatible字符串 |
内置CSI接口Sensor、SPI接口Sensor | probe(struct platform_device *) |
驱动注册的典型代码片段:
// I2C驱动注册示例
static const struct i2c_device_id sensor_id[] = {
{ "sensor_name", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, sensor_id);
static const struct of_device_id sensor_of_match[] = {
{ .compatible = "qcom,sensor_name" },
{ }
};
MODULE_DEVICE_TABLE(of, sensor_of_match);
static struct i2c_driver sensor_i2c_driver = {
.driver = {
.name = "sensor_name",
.of_match_table = sensor_of_match,
},
.probe = sensor_probe,
.remove = sensor_remove,
.id_table = sensor_id,
};
module_i2c_driver(sensor_i2c_driver);
实战要点: 在调试新Sensor时,务必确认设备树中的
compatible 字符串与驱动中的 of_match_table 完全一致(包括大小写)。使用 ls /sys/bus/i2c/devices/ 可以查看I2C设备是否被正确枚举;使用 cat /sys/kernel/debug/devices_deferred 可以查看probe被延迟的原因。
4.7 常见probe失败场景与排查方法
在实际调试中,probe 失败是最常见的问题。以下整理了三种典型失败场景及其排查思路:
- 场景一:I2C通信失败
- 现象:
sensor_read_id()返回 -EIO 或 -ENXIO。 - 排查:用示波器抓取I2C SCL/SDA波形;检查设备树中
reg地址是否为7位或10位模式;确认Sensor供电电压是否达到数据手册要求。
- 现象:
- 场景二:电源时序错误
- 现象:probe成功但
s_stream(on)时Sensor无输出。 - 排查:检查
msm_sensor_power_init()中regulator的使能顺序;确认GPIO复位时序(通常需要先拉低再拉高,保持至少1ms)。
- 现象:probe成功但
- 场景三:设备树匹配失败
- 现象:probe函数根本不被调用。
- 排查:使用
dtc -I dtb -O dts反编译设备树,确认节点是否存在;检查status = "okay"是否设置;确认pinctrl引脚复用配置正确。
通过掌握Sensor驱动的文件结构、probe/remove 回调函数的实现细节以及调试方法,学员可以快速定位并解决高通平台下Sensor驱动的适配问题,为后续的流控制、曝光增益调试打下坚实基础。