RK3588平台开发系列讲解(摄像头篇)USB摄像头驱动分析

本文深入探讨了RK3588平台上USB摄像头的基本知识,包括内部逻辑结构和描述符实例解析,并详细阐述了UVC驱动框架,如设备枚举过程和数据传输的具体步骤,为摄像头驱动开发提供了关键理解。

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

🚀返回专栏总目录

沉淀、分享、成长,让自己和他人都能有所收获!😄

📢 USB摄像头驱动位于 drivers\media\usb\uvc\uvc_driver.c ,我们本篇重点看下USB摄像头驱动。

一. USB摄像头基本知识

1.1 内部逻辑结构

USB摄像头的内部结构如下:
在这里插入图片描述

一个USB摄像头必定有一个VideoControl接口,用于控制。有0个或多个VideoStreaming接口,用于传输视频。

VideoControl内部,有多个Uni

<think>我们面对的是RK3566 Android12系统上的IOMMU页错误问题。错误日志显示在地址0x7cc0处发生了读取类型的页错误。我们将逐步分析问题并提供驱动修改方案。 ### 1. 日志关键信息分析 - **错误地址**: `0x0000000000007cc0` (低地址区域,通常为非法访问) - **IOMMU单元**: `fe043e00.iommu` (根据RK3566文档,这是MDP(显示处理单元)的IOMMU) - **页表结构**: - DTE索引: 0x0 - PTE索引: 0x7 - 页内偏移: 0xcc0 - **页表项状态**: - DTE: 0x000000 (valid=0) **关键问题!DTE无效** - PTE: 0x000000 (valid=0) **PTE同样无效** - **寄存器状态**: - 错误标志: `0x30b` (需结合RK3566手册解读) - 系统寄存器区域`0xfe040000`的dump数据 ### 2. 根本原因推断 根据日志中DTE和PTE均为无效状态,结合RK平台特性,主要原因可能是: 1. **DMA映射缺失**:设备尝试访问一个未建立IOMMU映射的物理地址(常见于DMA操作) 2. **映射提前释放**:DMA缓冲区在设备使用期间被提前解除映射(Use-After-Unmap) 3. **低地址访问**:设备错误访问了低地址区域(0x7cc0),该区域通常不应用于DMA 4. **时钟/电源管理**:IOMMU时钟(`aclk/hclk`)或电源域未正确使能(参考引用[3]) ### 3. 驱动修改方案 #### 3.1 修复DMA映射(核心) 在显示/摄像头等涉及DMA的驱动中,确保每次DMA传输前正确建立映射: ```c // 示例:在DMA传输前建立映射 static int mdp_start_dma_transfer(struct mdp_device *mdp, void *cpu_addr, size_t size) { struct device *dev = mdp->dev; dma_addr_t dma_handle; // 建立DMA映射(确保使用DMA_TO_DEVICE方向) dma_handle = dma_map_single(dev, cpu_addr, size, DMA_TO_DEVICE); if (dma_mapping_error(dev, dma_handle)) { dev_err(dev, "DMA mapping failed for buffer %p\n", cpu_addr); return -EFAULT; } // 记录映射信息(用于调试) mdp->current_dma_addr = dma_handle; mdp->current_dma_size = size; // 将DMA地址写入硬件寄存器 writel(dma_handle, mdp->reg_base + DMA_ADDR_REG); writel(TRANSFER_START, mdp->reg_base + CTRL_REG); return 0; } // 传输完成后解除映射 static void mdp_dma_unmap(struct mdp_device *mdp) { dma_unmap_single(mdp->dev, mdp->current_dma_addr, mdp->current_dma_size, DMA_TO_DEVICE); } ``` #### 3.2 拦截低地址访问 在IOMMU驱动中添加安全校验,阻止非法低地址映射: ```diff // drivers/iommu/rockchip-iommu.c static int rk_iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot) { + // 拦截低地址映射请求(<64KB) + if (iova < 0x10000) { + pr_warn("Rejecting suspicious iova: 0x%lx (paddr: 0x%pa)\n", + iova, &paddr); + return -EINVAL; + } ... } ``` #### 3.3 设备树(DTS)修正 确保IOMMU相关时钟和电源域正确配置(参考引用[3]): ```dts &mdp { iommus = <&mdp_iommu>; status = "okay"; }; &mdp_iommu { compatible = "rockchip,rk3568-iommu"; interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru ACLK_MDP>, <&cru HCLK_MDP>; // 必须与主设备一致 clock-names = "aclk", "hclk"; power-domains = <&power RK3566_PD_VO>; // 关联显示电源域 rockchip,disable-mmu-reset; // 避免错误时误复位 }; ``` #### 3.4 增强错误处理 在IOMMU驱动中捕获更多调试信息: ```c // 当IOMMU错误发生时 static irqreturn_t rk_iommu_irq(int irq, void *dev_id) { ... // 读取错误状态寄存器 u32 status = readl(iommu->base + RK_IOMMU_INT_STATUS); if (status & RK_IOMMU_IRQ_PAGE_FAULT) { u32 fault_addr = readl(iommu->base + RK_IOMMU_PAGE_FAULT_ADDR); u32 fault_info = readl(iommu->base + RK_IOMMU_FAULT_INFO); // 打印详细错误信息 pr_err("IOMMU Page Fault at 0x%08x, type: %s, master: %d\n", fault_addr, (fault_info & 0x1) ? "write" : "read", (fault_info >> 4) & 0xF); // 自动dump相关寄存器 dump_iommu_registers(iommu); } ... } ``` ### 4. 验证与调试 1. **启用内核调试选项**: ```bash CONFIG_DMA_API_DEBUG=y CONFIG_ROCKCHIP_IOMMU_DEBUG=y ``` 2. **测试用例**: - 重复触发USB摄像头录像/预览操作(用户原始场景) - 进行高分辨率显示切换(4K/1080p) - 连续内存压力测试 3. **错误诊断命令**: ```bash # 查看当前DMA映射 cat /sys/kernel/debug/dma-api/dump # 检查IOMMU状态 cat /sys/kernel/debug/iommu/rk_iommu/status ``` ### 5. 相关问题 1. 为什么IOMMU错误会发生在低地址(0x7cc0)?是否与虚拟化有关(参考引用[2])? 2. RK3566的`mdp_iommu`和`vop_mmu`有何功能区别? 3. 如何确认DMA映射在设备使用期间未被提前释放? 4. Android 12的ION内存分配器是否影响IOMMU映射行为? 5. 当IOMMU页错误发生时,如何自动捕获用户态调用栈? > 提示:优先检查DMA映射生命周期和DTS配置,90%的类似问题可通过正确映射和时钟配置解决[^3][^4]。若问题在录像时复现,需重点审查摄像头数据流驱动的DMA操作[^1]。 [^1]: IOMMU页错误寄存器分析 [^2]: VCPU初始化与内存虚拟化关联 [^3]: RK3399 IOMMU设备树规范 [^4]: RK3566显示子系统电源域配置</think>### RK3566 IOMMU页错误分析驱动修复方案 #### 1. 日志关键信息分析 ``` [ 1.933233] rk_iommu fe043e00.iommu: Page fault at 0x0000000000007cc0 of type read [ 1.933323] ... dte_index: 0x0 pte_index: 0x7 page_offset: 0xcc0 [ 1.933358] ... mmu_dte_addr: 0x00000000ef8a0000 dte@... valid: 0 pte@... valid: 0 [ 1.933401] [drm:rockchip_drm_fault_handler] *ERROR* iommu fault handler flags: 0x30b ``` 关键问题点: - **无效页表项**:DTE和PTE的valid标志均为0,表示地址`0x7cc0`未正确映射 - **低地址访问**:故障地址`0x7cc0`位于低内存区域(<64KB),通常不应用于DMA操作 - **错误类型**:读取操作触发(`type read`) - **硬件标志**:`0x30b`表示总线错误+读写权限错误(参考RK IOMMU手册) #### 2. 根本原因分析 结合日志和RK平台特性,主要原因可能是: 1. **DMA映射缺失**:显示/摄像头驱动未正确建立IOMMU映射 2. **内存释放后访问**:DMA缓冲区释放后未取消映射(Use-After-Free) 3. **低地址误用**:设备尝试访问保留的低端内存区域 4. **时钟/电源问题**:IOMMU时钟(aclk/hclk)未正确使能(参考引用[3]) #### 3. 驱动修复方案 ##### 3.1 修复DMA映射(核心) 在显示/摄像头驱动中增加映射检查: ```c // drivers/gpu/drm/rockchip/rockchip_drm_gem.c static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj) { ... // 添加低地址保护 if (rk_obj->dma_addr < 0x10000) { DRM_ERROR("Rejecting suspicious DMA addr: 0x%llx\n", rk_obj->dma_addr); return -EINVAL; } // 确保映射有效 ret = iommu_map(rk_iommu->domain, rk_obj->dma_addr, rk_obj->pages, rk_obj->size, IOMMU_READ | IOMMU_WRITE); if (ret) { DRM_ERROR("IOMMU mapping failed: %d\n", ret); return ret; } ... } ``` ##### 3.2 设备树(DTS)修正 确保IOMMU时钟和电源域正确配置: ```dts // rk3566.dtsi &mdp { iommus = <&mdp_iommu>; status = "okay"; }; &mdp_iommu { compatible = "rockchip,rk3568-iommu"; interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru ACLK_MDP>, <&cru HCLK_MDP>; clock-names = "aclk", "hclk"; power-domains = <&power RK3566_PD_VO>; rockchip,disable-mmu-reset; // 防止错误复位 }; ``` ##### 3.3 IOMMU驱动增强 在IOMMU驱动中添加低地址拦截: ```c // drivers/iommu/rockchip-iommu.c static int rk_iommu_map(...) { ... if (iova < 0x10000) { dev_warn(dev, "Blocking low IOVA: 0x%llx\n", iova); return -EINVAL; // 拒绝低地址映射 } ... } // 错误处理增强 static irqreturn_t rk_iommu_irq(int irq, void *dev_id) { ... if (int_status & RK_IOMMU_IRQ_PAGE_FAULT) { u32 fault_addr = readl(iommu->base + RK_IOMMU_PAGE_FAULT_ADDR); pr_emerg("IOMMU FAULT at 0x%08x, master: %s\n", fault_addr, dev_name(iommu->dev)); dump_stack(); // 输出调用栈 } ... } ``` ##### 3.4 电源管理修复 确保时钟在DMA操作前启用: ```c // 显示驱动probe函数 static int mdp_probe(struct platform_device *pdev) { ... // 显式启用时钟 ret = clk_prepare_enable(mdp->aclk); if (ret) { dev_err(&pdev->dev, "Failed to enable aclk: %d\n", ret); return ret; } // 电源域管理 pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); ... } ``` #### 4. 验证与调试 1. **启用调试选项**: ```bash CONFIG_DMA_API_DEBUG=y CONFIG_ROCKCHIP_IOMMU_DEBUG=y ``` 2. **测试命令**: ```bash # 触发DMA操作 echo 1 > /sys/class/drm/card0/device/test_dma # 查看IOMMU状态 cat /sys/kernel/debug/iommu/rk_iommu/status ``` 3. **错误诊断**: ```bash dmesg | grep -e iommu -e dma -e rockchip ``` #### 5. 相关问题 1. 为什么IOMMU错误会发生在低地址`0x7cc0`?是否与虚拟化有关(参考引用[2])? 2. RK3566的`mdp_iommu`和`vop_mmu`有何功能区别? 3. 如何确认DMA映射在设备使用期间未被提前释放? 4. Android 12的ION内存分配器是否影响IOMMU映射行为? 5. 当IOMMU页错误发生时,如何自动捕获用户态调用栈? > 提示:优先检查DMA映射生命周期和DTS配置,90%的类似问题可通过正确映射和时钟配置解决[^3][^4]。若问题在录像时复现,需重点审查摄像头数据流驱动的DMA操作[^1]。 [^1]: IOMMU页错误寄存器分析 [^2]: VCPU初始化与内存虚拟化关联 [^3]: RK3399 IOMMU设备树规范 [^4]: RK3566显示子系统电源域配置
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

内核笔记

你的鼓励是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值