msm8996平台的 camera 框架笔记

本文深入解析MSM8996平台的Camera HAL架构,涵盖Kernel层V4L2框架、HAL层接口及状态机机制、ZSL Session与Stream流程、Poll机制等关键组件。揭示了QCOM相机驱动模型、硬件资源管理策略及其与AOSP的接口适配。

msm8996平台的 camera 框架笔记

标签(空格分隔): camera


0 前言

本文档主要记录在msm8996平台下camera hal层框架。

1 kernel中的camera结构

qcom的camera driver采用v4l2框架。

1.1 qcom的v4l2节点结构

qcom_v4l2.jpg-675.7kB

qcom这里的操作有几点需要记录一下:

1、关于msm_cam设备
msm_cam设备在dts文件中被定义,从driver代码上来分析,该设备的定位是对所有qcom camera相关设备的管理。qcom把camera系统分为了camera和sensor两部分,camera这部分由msm_cam设备作为video设备与userspace进行交互,qcom自己的mipi、isp、cpp等硬件设备都属于camera部分,该部分目前理解为是qcom soc的片上ip core;sensor部分则为外部设备,例如一个感光sensor,每个sensor都作为一个subdev注册到msm_cam设备上,同时会创建一个对应的video设备。

2、关于s_init设备

目前来看,最主要的作用就是执行msm_sensor_driver_cmd()--->msm_sensor_driver_probe(),这里就是针对各sensor进行注册subdev和创建video的入口。

3、关于camera与sensor设备

这里的camera是指的一个video设备,sensor则是一个sensor设备。sensor设备作为subdev挂载在msm-cam设备下,在挂载每个sensor前会创建该sensor的一个video设备,一个video设备作为一个session通过v4l2接口与userspace交互数据,目前来看camera设备主要提供了一个v4l2的操作接口,实际执行过程都在别处执行。例如camera_v4l2_s_parm消息,camera设备通过video的消息接口收到该消息后直接是把这个消息以及参数又post event出去了,这里猜测在qcom camera的sensor driver的逻辑全部在hal层中,hal层收到camera post出来的set param event后根据param通过sensor对应的subdev去写sensor的寄存器,来完成对sensor的配置。这一点在sensor给v4l2提供的回调函数中可以看出一些踪迹,例如sensor给v4l2的一个回调:

msm_sensor_subdev_ioctl()--->
s_ctrl->func_tbl->sensor_config32(s_ctrl, argp)---> //(s_ctrl->func_tbl = &msm_sensor_func_tbl;)
msm_sensor_config32()--->
s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table()---> //s_ctrl->sensor_i2c_client->i2c_func_tbl =
				                                               //     &msm_sensor_qup_func_tbl;
msm_camera_qup_i2c_write_table()--->i2c_transfer()

所以这里认为创建sensor的video的目的是为了使hal层使用上更符合v4l2的接口习惯,为每个sensor分配一个video,hal层在操作camera时操作该设备对应的video就行了,但是这里由于qcom camera hal层框架的原因这里的video仅仅成为了一个接口,没有任何真正对硬件产生效果的逻辑操作。

这里试着分析一下qcom这样做的原因,可能有如下几点:
1、对自己的操作进行保护,因为hal层是不用提供源码的
2、从结构上来说,多个摄像头是公用vfe、cpp这些硬件资源的,所以这些硬件资源的管理不适合放到每个camera中去管理,所以就都挂到msm-cam设备下,每个硬件资源自己负责管理自己,并且自己解决多路复用的问题,这样这些逻辑就都放到了各个硬件自己的driver中(无论是在hal层还是kernel层)。
3、更好的架构一致性,所有的硬件资源的操作都由自己的subdev接口进行操作。

1.2 一个zsl的session和stream

v4l2的结构下图:

qcom_v4l2_bufs.jpg-448.5kB

一个zsl操作这里总共打开了6个stream,stream 5大概率是callback stream,有什么用暂时还不太清楚,但是启动之后stream 5立马就停了,所以这里未标出,stream 0好像是一个管理通道?这里也不太确定,但是从跟踪与分析来看跟数据流没什么关系,所以这里也未画出;stream 1的作用暂时不太清楚,猜测跟buf管理有关。

简单来说,一帧preview/viewfinder的数据经过如下部分:

sensor ---> csiphy ---> csid ---> vfe ---> aix ---> hal ---> cpp ---> hal

2 hal层中的camera结构

qcom camera hal层的代码分为三部分:

一部分是cpp组成,以QCameraxxxx.cpp命名,例如QCamera2HWI.cpp,主要提供hal层的接口函数以及回调接口函数,主要负责hal层对aosp的接口适配以及qcom hal层接口框架逻辑。

另一部分代码以mm_xxx_xxx.c命名,例如mm_camera_stream.c,该部分是qcom hal层的硬件资源接口,主要是对camera硬件状态、硬件资源的管理以及对硬件操作的封装。

还有一部分是qcom闭源的,应该跟vfe、cpp等硬件的控制逻辑以及资源逻辑相关,该部分定义:

#define SHIMLAYER_LIB "/system/vendor/lib/libmmcamera2_mct_shimlayer.so"

推测这一部分与session的最终操作相关,一个session具体如何控制,具体需要哪些硬件协同工作都在这个so里面。

2.1 aosp的camera接口

boot_connect.jpg-334.9kB

这里主要分为两部分:
1、load
在cameraService被创建后会调用其OnFirstReg()函数,该函数加载QCamera的so文件,获得mModule句柄,同时获取其camera数量。

2、connect
当启动一个camera时camserver发起connect操作,这时会创建一个camera client并且为其填写mHardware数据,该数据是对硬件操作的入口,mHardware中的mDevice则是qcom cam返回的camera入口,mHardware的填充由openCamera()函数完成,具体过程如上图所示。

2.2 一个preview的启动过程

2.2.1 qcom hal层模型

cbNotifier.jpg-148.4kB

该图表示qcamera中Notifier的过程,一个notifier中有两个链表,一个cmd链表,一个data链表,一个notifier被触发都是cmdThread从cmd链表中取触发指令,然后执行相应的操作,如果操作内省是DO NEXT JOB,则会再从数据链表中取数据,然后调用数据处理的回调函数来对其进行处理。


mm_camera module.jpg-108.9kB

上图表示hal层mm_xxx.c相关的处理过程,中对该部分的操作全部是通过mm_camera_interface.c提供的接口来完成的,然后mm_camera.c根据操作类型以mm_xxx.c内部接口的标准将数据传送给最终处理的单元。

这里mm_camera_interface.c的作用是给外部提供调用接口,同时根据外部请求,来调用mm_camera.c中对应的操作;mm_camera.c的作用是响应外部请求,并翻译成mm_xxx.c内部接口模型。这里的内部接口模型主要是因为在每个模型中抽象了一个状态机模块,或者说每个内部模型都有一个状态机进行组合。

2.2.2 模型中的状态机

qcameraOps_stateMachine.jpg-180.4kB

状态机的创建是在openLegacy()被调用,new QCamera2HardwareInterface()时创建的,外部对qcom camera的所有操作都会以事件的方式来调用,最终由camera的状态机来决定消息的执行情况。

以上是QCameraxxx.cpp这一层的状态机,在mm_xxx.c这一层中,每个session和stream都有自己的状态机,工作机制与上述类似,详见上一节。。

2.2.3 start preview

startPreview.jpg-295.7kB

2.3 camera中的poll机制

camera_poll_system.jpg-236.6kB

这里的poll机制里面是嵌套了一个pipe机制在里面。
每次添加一个poll thread时,会给这个poll thread创建一个pipe,对于这个poll thread来说pipe也好open的dev也好,都是一个文件fd,都适用于poll,调用一次poll时可以传递给该函数一个fd数组,poll会去查看每一个fd,一旦哪个fd的poll有返回,则该poll thread就会对其进行处理。那么qcom这里期望做一个可以动态添加poll fd的机制,他们利用了了pipe,这个pipe就是fds[0],也就是每个poll thread自带fds[0],该fd响应添加新fd的操作,所以当open一个dev就可以把这个dev的fd通过write fds[0]添加进来。

2.4 关于callbacks

callbacks.jpg-454.5kB

<think> 我们正在处理一个Linux内核驱动中的I²C错误,具体在`cam_sensor_i2c_modes_util`函数的第581行,错误为“Failed to random write I2C settings: -22”。错误代码-22对应EINVAL(无效参数)。同时,用户还提到了应用设置失败的问题,但根据引用[2]的内容,这似乎是另一个关于Kubernetes部署Jenkins的问题,与当前的I²C错误没有直接关联。因此,我们主要关注I²C错误。 结合引用[1]中提到的类似问题(在NT98560平台上驱动gc8613相机时,I²C有信号但相机无响应),我们可以推断这个问题可能与硬件连接、I²C配置或驱动代码有关。 ### 错误原因分析 - **核心问题**:在尝试随机写入I²C设置时,传递了无效的参数(可能是地址、寄存器或数据格式不正确)。 - **可能原因**: 1. **I²C地址或寄存器无效**:传感器地址配置错误,或者寄存器地址超出范围。 2. **I²C时序或协议不匹配**:时钟频率、时序模式(如标准模式、快速模式)与传感器要求不符。 3. **硬件问题**:如连接不良、电源问题或传感器损坏(如引用[1]中示波器有信号但传感器无响应)[^1]。 4. **驱动参数错误**:在驱动代码中,传递给I²C写入函数的数据长度、对齐方式或格式不正确。 ### 逐步解决方案 #### **步骤1: 验证I²C地址和传感器配置** - 使用`i2cdetect`工具扫描I²C总线,确认传感器地址是否被正确识别: ```bash i2cdetect -y <bus_number> # 例如:i2cdetect -y 0 ``` 如果传感器地址没有出现在扫描结果中(如引用[1]的情况),则可能是硬件连接问题或设备树配置错误[^1]。 - **检查设备树(Device Tree)配置**:确保设备树中配置的I²C地址与传感器手册一致。例如,如果传感器手册指定7位地址为0x1A,则设备树中应设置为0x1A(内核驱动内部会将其左移1位)。 #### **步骤2: 检查硬件连接和信号质量** - **使用示波器检查**(如引用[1]的方法): - 测量SCL(时钟)和SDA(数据)线路,确认信号质量(无噪声、无电压跌落)。 - 检查时钟频率是否符合传感器要求(标准模式100kHz,快速模式400kHz等)。 - **硬件排查**: - 重新插拔连接器,确保接触良好。 - 验证传感器供电电压(通常为3.3V或1.8V)是否稳定。 #### **步骤3: 调试驱动和内核日志** - **查看完整内核日志**: ```bash dmesg | grep -i "i2c\|sensor\|camera" ``` 查找与`cam_sensor_i2c_modes_util`相关的其他错误信息,特别是参数传递的细节。 - **检查驱动代码**: 定位到驱动源码中`cam_sensor_i2c_modes_util`函数的第581行(或附近),检查随机写入操作(如`i2c_transfer`或`i2c_smbus_write_*`)的参数: - 确认寄存器地址(reg_addr)的位宽(16位?)是否正确。 - 检查数据缓冲区(data_buf)的长度和对齐方式。 - 示例:若使用`i2c_smbus_write_word_data`,则数据必须是16位(2字节),且寄存器地址为8位或16位。 #### **步骤4: 使用I²C工具模拟写入** - 尝试用`i2cset`命令手动写入寄存器,验证传感器是否响应: ```bash i2cset -y <bus> <sensor_address> <register> <value> ``` 例如(假设地址0x1A,寄存器0x0D,值0x01): ```bash i2cset -y 0 0x1a 0x0d 0x01 ``` 如果返回错误`Error: Write failed`(错误码-22),则说明参数可能无效(寄存器地址超出范围?)。 #### **步骤5: 更新或修复驱动** - **升级内核**:某些旧内核版本存在I²C驱动Bug,尝试升级到较新的LTS版本。 - **添加调试打印**:在驱动代码中临时添加打印,输出传递的参数值: ```c pr_info("I2C write: addr=0x%x, reg=0x%x, data_len=%d\n", addr, reg_addr, data_len); ``` - **参考社区补丁**:搜索Linux内核邮件列表(LKML)或GitHub中类似错误的修复(关键词:`cam_sensor_i2c_modes_util -22`)。 ### 关联问题排查 - **应用设置失败问题**:从引用[2]的内容看,用户可能同时遇到了Kubernetes部署Jenkins的问题(如PVC/PV绑定、服务端口配置)。如果需要解决此问题,请提供更具体的错误描述,例如Jenkins日志中的错误信息。 ### 注意事项 - **引用[1]的关联**:硬件无响应是常见现象,优先确保传感器能被`i2cdetect`检测到[^1]。 - **交叉验证**:如果可能,在相同硬件上测试已知正常的传感器(或在不同平台测试此传感器),以隔离问题。 如果以上步骤仍无法解决,请提供以下信息进一步分析: 1. 内核版本(`uname -a`)。 2. 传感器型号和数据手册。 3. 完整的设备树I²C节点配置。 4. `dmesg`中与相机驱动相关的完整日志。 §§相关问题§§ 1. 如何调试Linux内核中的I²C通信失败错误? 2. 在设备树中正确配置I²C传感器地址的方法是什么? 3. Linux错误码EINVAL(-22)的常见原因和解决方案有哪些? 4. 如何用示波器诊断I²C总线问题? 5. 相机传感器驱动开发中的常见陷阱如何避免? [^1]: daisy.skye在NT98560平台上驱动gc8613时,i2cdetect有信号但camera无响应,可能涉及地址或硬件问题。 [^2]: Kubernetes部署Jenkins的检查步骤,包括PVC、PV、Deployment和Service的状态确认。 </think>### 解决`cam_sensor_i2c_modes_util: 581: Failed to random write I2C settings: -22`错误及关联应用问题 #### 错误分析 1. **核心问题**: - 错误`-22`对应Linux内核的`EINVAL`(无效参数),表明I²C随机写入操作传递了非法参数 - 常见于相机传感器驱动初始化阶段,涉及寄存器地址、数据格式或时序配置错误 - 关联引用[1]中描述的I²C有信号但传感器无响应现象是典型表现[^1] 2. **根本原因**: - **I²C参数无效**:地址/寄存器超出范围、数据对齐错误(如16位地址用8位传输) - **硬件问题**:传感器供电不稳(<3.3V)、信号噪声超标(>10% $V_{DD}$)、连接器接触不良 - **驱动缺陷**:`cam_sensor_i2c_modes_util`函数581行附近的数据包构建错误 - **平台兼容性**:如引用[1]中NT98560与gc8613的兼容问题[^1] #### 解决方案(分步骤执行) --- ##### **阶段1:硬件与基础验证** 1. **I²C总线扫描** ```bash # 扫描所有I²C总线(需root权限) for bus in $(seq 0 9); do echo "Scanning bus $bus:"; i2cdetect -y $bus | grep -v UU; done ``` - ✅ **期望结果**:传感器地址(如0x1A)显示为数值(非`--`或`UU`) - ❌ **问题处理**:若地址未显示,检查设备树配置(参考阶段2) 2. **电源与信号测量** | 测试点 | 标准值 | 测量工具 | |--------------|---------------|-------------| | 传感器VCC | 3.3V±5% | 万用表 | | SCL/SDA高电平 | >0.7×$V_{DD}$ | 示波器 | | 信号上升时间 | <1μs (100kHz) | 示波器 | - 异常处理:添加10kΩ上拉电阻或更换电源模块 --- ##### **阶段2:驱动与配置修复** 1. **设备树修正**(以gc8613为例) ```dts &i2c3 { status = "okay"; clock-frequency = <100000>; // 确保与传感器手册一致 camera_sensor: gc8613@1a { compatible = "galaxycore,gc8613"; reg = <0x1a>; // 7位I²C地址 vdd-supply = <&cam_3v3>; // 明确供电节点 // 添加复位和使能引脚控制 reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; }; }; ``` 2. **驱动代码调试**(定位581行) - 在`drivers/media/platform/qcom/cam_sensor_io/cam_sensor_i2c_util.c`中: ```c // 在cam_sensor_i2c_modes_util函数前添加调试 pr_info("I2C Write: addr=0x%x, reg=0x%x, data_len=%zd", write_setting->addr, write_setting->reg_addr, write_setting->size); // 581行附近检查数据有效性 if (write_setting->size > I2C_MAX_DATA_LEN) { pr_err("Data size %zd exceeds max %d", write_setting->size, I2C_MAX_DATA_LEN); return -EINVAL; // 可能触发-22的错误点 } ``` --- ##### **阶段3:系统级验证** 1. **I²C写入测试** ```bash # 模拟随机写入(地址0x1A,寄存器0x0D,值0x01) i2cset -y 3 0x1a 0x0d 0x01 wp ``` - 成功:返回0 - 失败:检查`dmesg | tail` 输出错误详情 2. **内核日志过滤** ```bash journalctl -k --since "5 min ago" | grep -E "i2c|cam_sensor|gc8613" ``` 关键错误模式示例: ``` i2c i2c-3: sendbytes: error -22 // 地址无效 cam_sensor: reg 0xffff size error // 寄存器越界 ``` --- ##### **阶段4:关联应用问题排查**(参考引用[2][^2]) 针对Kubernetes应用部署问题: 1. **PVC挂载验证** ```bash kubectl describe pod/jenkins-xxx -n jenkins | grep -A 10 Mounts ``` - 确认`jenkins-pvc`状态为`Bound` - 检查存储类是否支持`RWX`(ReadWriteMany) 2. **服务端口冲突检测** ```bash netstat -tulnp | grep -E '30002|31935' # 检查NodePort占用 ``` - 冲突处理:修改`service.yaml`中的`nodePort`值 --- #### 修复效果验证 1. **成功标志**: - I²C扫描显示传感器地址正常响应 - 内核日志无`-22`错误 - 相机初始化成功(`dmesg | grep "sensor probed"`) - Jenkins应用可访问(`curl http://<IP>:30002`) 2. **失败回退**: ```bash # 卸载问题驱动 rmmod cam_sensor_io # 回滚到旧版驱动 insmod /lib/backup/cam_sensor_io.ko ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值