火哥的嵌入式Linux/Android疑难杂症工作笔记

Android系统Cortex-A57 内核压力测试连续震荡性内存泄漏导致OOM Killer

  • 硬件平台
    公司自研 ARM Cortex-A57 4核 SOC 产品板

  • 软件环境
    系统: Android-P
    Linux内核版本: 4.9
    运行公司内部的Linux内核压力测试脚本,通过内存工具抓取物理内存使用率的情况

  • 现象描述
    系统内存使用率的情况如图,该图是连续24小时Linux 内核压力测试的物理内存使用率的情况,蓝色的点代表不运行压力测试的时候正常的内存使用率,红色的点代表运行内核压力测试时物理内存使用率上下震荡,缓慢上升,在16小时左右发生了low memory killer (oom killer) 导致系统很多后台服务被杀死,内存使用率断崖式下滑


    compare.png
  • Root Cause
    Linux内核某个模块的驱动代码是这样写的,
int bug_driver(struct bug_dev *dev)
{
  struct foo *bar = NULL;
  bar = kzalloc(sizeof(struct foo)) ;
  if (bar) {
    kfree(dev->spec);
    dev->spec = bar;
    bar = NULL;
    return 0;
   } 

    return -1;
}

这段代码看起来没有导致内存泄漏,因为旧的dev->spec指针先kfree()之后,重新保存了kzalloc()分配的内存,下次运行到这里的时候,会先释放旧的,再获取新的内存,不会泄漏。
但是大公司,分工比较细,一个驱动模块可能很多个人进来修改过,另外一个工程师在这个函数上层的某个地方写了一段这样的代码:

struct bug_dev dev;

bug_driver(&dev);

....................一大段代码省略...................................

memset(&dev, 0x00, sizeof(struct bug_dev));
dev.spec = kmalloc(sizeof(struct bug_dev))

因为这段代码直接把通过bug_driver()函数获取的bug_dev->spec指针意外得memset()再重新分配了,因而下次再调用bug_driver(&dev) 函数时,在里面kfree(dev->spec)的指针并不是上一次的kzalloc()的bar指针,上一次kzalloc()的bar指针彻底变成孤魂野鬼未引用对象存在内核里面,久而久之内存使用率就上去了,造成了内存泄漏
该内存泄漏导致oom killer需要16小时才复现,每次泄漏2KB其实很少,而且该内核压力测试脚本本身运行时就有内存使用上下震荡波动,因而几分钟之内的实验是看不出有内存泄漏的

  • 解决过程总结
  1. 刚开始跑压力测试的工程师反馈回来的是oom killer的问题,需要16小时复现, 我只能一边分析log一边拿着脚本继续跑复现。
  2. 我看着log有一大段内存使用率的打印,于是突发奇想用最近学的python matplotlib画图功能把内存使用率的数据都抓出来,画成散点图, 看看能不能拟合出什么,画出来之后就看到了内存使用率缓慢震荡上升的趋势
  3. 在内核中打开了kmemleak工具,改了一下压力测试脚本,每跑一次压力测试之后,就scan一次kememleak的情况,记录log, 通过kmemleak工具打印出来的stack找到了内存泄漏的函数
  4. 一开始看泄漏的函数,发现第一段代码挺正常,有记得free之前的指针,看不出内存泄漏的情况,直到后面加了一点打印,打出每次kzalloc()分配的指针的具体地址和kfree()释放的具体指针地址,发现与差异,然后全局检查代码其它地方的调用,终于发现这套函数调用逻辑的bug所在,不同的工程师写的代码,互相之间不知道对方做了哪些细节调用,遂导致这个内存泄漏。

Android系统Cortex-A57 Android Framework 反复stop/start 导致内存碎片,内存占用率连续震荡性升高最终引发oom killer

  • 硬件平台
    公司自研 ARM Cortex-A57 4核 SOC 产品板

  • 软件环境
    系统: Android-P
    Linux内核版本: 4.9
    运行公司内部的Linux内核压力测试脚本,通过内存工具抓取物理内存使用率的情况

  • 现象描述
    系统内存使用率的情况如图,物理内存使用率上下震荡,缓慢上升,该图是连续24小时Linux 内核压力测试的物理内存使用率的情况,通过缩小范围,修复内存泄露问题之后,发现压力测试反复调用Android Framework的stop / start 命令引发内存使用率升高,导致oom killer。从该图得到的另外的信息是,stop / start 压力测试停止之后(停止震荡上升的后面蓝色直线),内存使用率就维持一个很高的比例,不会降低


    oom_killer.png
  • Root Cause
    跑Android Framework stop /start 反复kill 和启动FW的核心进程,会导致物理内存的碎片化,而Android核心进程启动又需要大块的内存页,从 cat /proc/buddyinfo 可以看出刚开始跑测试,系统还要很多较大块的内存页,结果到后来,大块内存页越来越少,碎片页得不到释放合并,FW 的server 重新启动又不得不去分配超过需求的内存大页,引发内存使用率不断上升,最后oom killer

  • 解决过程总结

  1. 一开始处理这个问题,大家的焦点都在内存泄露上,而且内核驱动确实有内存泄露的情况,所以大家花很多精力去debug内存泄露问题,而忽略内存碎片对内存使用率升高的影响。
  2. 在花费很多精力,解决了一个非常隐蔽的重要内存泄露问题时,内存使用率还是会在压力测试中攀升。
  3. 经过缩小debug范围,发现是反复执行stop / start命令导致的,而且在缩小模拟测试的时候,并没有严重的内核内存泄露发生,并且用户态的旧进程也在不断被kill, 再重新启动新进程,用top -s 10 排序观察用户态进程的状态,似乎也没有看出哪个进程内存使用率明显升高的迹象
  4. 后面把注意力放到内存碎片上,cat /proc/buddyinfo 发现内存碎片化非常严重
  5. 在stop 之后加入清理pages cache, inode节点和压缩碎片内存页的命令
    echo 3 > /proc/sys/vm/drop_caches
    echo 1 >/proc/sys/vm/compact_memory
    内存碎片导致内存使用率不断升高的问题得到根本性解决。解决后的内存使用率如下图:


    with_pages_cache_and_mcm_compact.png

Android系统Cortex-A57 Coresight PTM ARM官方驱动不能正常工作

  • 硬件平台
    公司自研 ARM Cortex-A57 4核 SOC 参考板

  • 软件环境
    系统: Android-N
    Linux内核版本: 4.4
    驱动: linux/drivers/hwtracing/coresight

  • 现象描述
    将Linux-4.4 upstream 官方内核ARM提交的的CPU coresight PTM驱动移植到板子上,替换公司私有的coresight PTM驱动,寄存器和pipeline的dts配置都正常,但是ARM官方的coresight PTM驱动不能正常工作,而公司私有驱动可以正常工作

  • Root Cause
    跑Android 系统 ARM Cortex-A57 CPU有用于低功耗电源管理的dynamic power gating功能,该功能会以一定频率不停地运行。在系统运行dynamic power gating的时候,会把coresight PTM的寄存器全部清零,而如果没有保存之前寄存器的值,则cpu从dynamic power gating恢复之后,寄存器的值一直是0,之前配置的值没有恢复导致不能正常工作

  • 解决过程总结

  1. 第一次接触Android项目对低功耗管理,dynamic power gating的side effect完全没有概念,一直在花时间配置coresight PTM驱动的寄存器和pipeline dts上,但是一直不工作
    2.突发奇想地把私有可以工作的驱动和ARM官方upstream的驱动一起加载的情况下,ARM官方upstream的coresight PTM驱动却可以正常工作
  2. 通过分析旧的私有驱动,发现里面有注册一个dynamic power gating 事件的notifier chain通知链, 每当dynamic power gating 事件发生时,会通过通知链调用callback函数保存寄存器的当前值,dynamic power gating 事件恢复的的时候再恢复寄存器的值
  3. 将该通知链的业务逻辑和流程移植到ARM官方的upstream的coresight PTM驱动,稍微优化一下就解决了该问题

Android系统 Bring up 由电源管理造成的security问题导致系统无法正常启动

  • 硬件平台
    公司自研 ARM Cortex-A57 4核 SOC CostDown版本参考板
    在之前的参考板基础上,内存从8G缩减到4G, 更换了PMIC, eMMC从32G缩减到16G
    之前的板子是已经量产,完全能够正常工作,SOC芯片和CostDown的参考板完全一样

  • 软件环境
    系统: Android-N
    Linux内核版本: 4.4

  • 现象描述
    Android系统启动Zygote SystemServer的时候,在尝试启动其它服务进程的时候,那些服务进程都因为SELinux security的原因,被kill掉,在尝试多次启动服务和被Kill之后,系统最后关机重启

  • Root Cause
    PMIC 的SOC Thermal 加入了限流功能,一旦启动的时候开启限流,SOC的CPU就会降频,一旦CPU降频, 启动过程就会减慢,Security固件加载就会延后,一旦Security固件加载延后,就会在Zygote 启动各种系统服务的时候,引发SELinux的安全保护机制,导致各个系统服务因为Security原因被Kill掉,进而关机。可谓是一个小问题引发一连串问题

  • 解决过程总结

  1. 一开始做Bring up, 发现这些系统Server都是因为Security的原因被Kill掉的,都去抓Security相关的Log检查,没有注意到系统整体的原因,后来改了一些代码,延时了系统Server启动时间到Security固件加载之后才启动,这样用WorkAround方法解决了Security原因导致服务被Kill掉错误,治标不治本,继续Bring up
  2. 虽然不会有Security问题导致系统服务被Kill, 但是整个系统启动时间很慢,而且运行一段时间之后,GPU驱动会导致系统Crash
  3. 为了检查系统启动慢的原因,我们检查了CPU和GPU的时钟频率,发现其远低于正常的工作频率
  4. 和硬件还有芯片的工程师讨论之后,发现这款芯片有温度保护限流SOC降频保护的功能,而新的PMIC Thermal不支持该温度保护相关的功能,因而我们去掉了限流降频相关的dts配置,这样CPU就能以正常频率启动,之前的延时的WorkAround去掉之后也不会再引发Security固件加载慢导致系统服务被Kill的现象了

Linux 内核 eMMC 5.1 存储驱动添加异步I/O新命令特性时关于内核线程同步问题

  • 硬件平台
    公司自研 ARM Cortex-A57 4核 SOC 产品板
    eMMC使用SanDisk新一代 eMMC 5.1存储器,有扩展指令支持device report 等温度信息、flash读写次数等芯片状态

  • 软件环境
    系统: Android-P
    Linux内核版本: 4.9

  • 现象描述
    想在sysfs中增加一个文件,通过eMMC芯片的异步I/O读取eMMC芯片的温度信息,但是总是因为各种原因读取的时候直接卡死或者失败

  • Root Cause
    eMMC设备驱动不同于led, 电源管理芯片等普通的字符设备驱动,可以直接异步I/O读取。因为在Linux内核里面存储相关的设备驱动上层有负责块Block设备队列管理的内核线程在实时占用设备并与文件系统数据同步,如果不加同步互斥的方法直接异步I/O读取eMMC的device report状态信息,那么将与其eMMC驱动上层的Block设备队列管理的内核线程发生干扰和冲突,进而会卡死或者引发意想不到的side effect。
    另一方面,由于Android系统有动态电源,时钟等功耗管理,及时此时Block设备队列很空闲,没有做同步,但是由于电源管理的dynamic clock gating等特性,没有加锁互斥去情况下异步I/O读取的时候,eMMC可能会由于dynamic clock gating导致暂时休眠而读不到数据

  • 解决过程总结

  1. 仔细阅读芯片手册,发现Linux 4.9内核MMC框架只支持标准的eMMC 4.1指令,我做的eMMC 5.1 的新特性指令需要自己添加
  2. 添加自己的指令,实现好驱动接口之后,异步I/O读取eMMC device report信息不是卡死就是失败
  3. 通过 dump_stack()追踪eMMC控制器驱动函数的调用流程,发现dynamic clock gating功能导致芯片低功耗休眠无法读取数据,加了一些workaround方法,关闭休眠,再次尝试读取,发现会卡死
  4. 继续研究emmc驱动代码,发现emmc设备驱动上层被Block设备队列管理内核线程实时调用,所以认为这不是简单的异步I/O读取功能,应该要加锁做通读
  5. 研究了一下eMMC一些异步 ioctl的实现方法,找到了给emmc设备加互斥锁独占的方法,解决了该问题

嵌入式Linux系统 NandFlash的rootfs挂载遇到概率性Kernel Panic

  • 硬件平台
    公司自研ARM Cortex-A9 / MIPS 32 SOC
    NorFlash 启动bootloader,加载 Nandflash 中的Linux 内核与Rootfs

  • 软件环境

  1. 嵌入式Linux系统, Buildroot构建ubifs格式Rootfs
  2. 公司自研windows平台烧写工具
  • 现象描述
    不同平台分别测试3块demo板,有一些板子挂载rootfs的时候出现kernel panic, 有一些没有,看上去和arm 还是 mips cpu无关, arm mips都有出现,是概率性问题,不是必现

  • Root Cause
    烧写工具标记Nandflash坏块记录BBT表的功能出现bug, 导致烧写的时候某些坏块没有跳过,而rootfs如果正好烧写到某些坏块上,则出现kernel Panic
    不要认为烧写工具是绝对可靠, 没有Bug的

  • 解决过程总结
    1.刚入职对BBT坏块表的概念不是很清楚,以为烧写工具是绝对没问题的,所以不敢怀疑。

  1. 由于是概率性出现的问题,没有找到复现问题的规律,因而研究了很多代码但是都没有头绪
  2. 老大根据猜测可能是Nandflash有问题,让我用uboot读取Nandflash坏块信息,再和烧写工具打印的Nandflash坏块信息对比发现有不一致。
  3. 选择一块可以复现问题的板,通过uboot打印的坏块信息,发现坏块点比较靠前,调整rootfs的大小,当rootfs大小可以覆盖坏块点的时候,问题复现,当rootfs大小无法覆盖坏块点的时候,问题不复现,从而证明可Kernel Panic是坏块导致的
  4. 进而对比烧写工具打印的Nandflash坏块信息,发现该坏块点没有被标记存入BBT表,证明Bug出现在烧写工具坏块标记与BBT表更新。

嵌入式Linux Nandflash Only 启动的方案,客户更换不同型号的nand会有一定概率无法正常启动

  • 硬件平台
    公司自研 MIPS 32 SOC, 低成本方案,平台只有Nandflash, 没有NorFlash, 才有Nandflash启动的方式
  • 软件环境
  1. 嵌入式Linux系统, Buildroot构建ubifs格式Rootfs
  2. 公司自研windows平台烧写工具
  3. 启动流程: bootrom --> Nandflash中的uboot -->Linux内核 --> rootfs
  • 现象描述
    客户更换的某些型号的NandFlash 产品板有一定概率无法启动Uboot

  • Root Cause
    这一代SOC芯片的bootrom 固件程序对某些NandFlash较长位数的ECC校验存在Bug
    不要认为芯片本身是绝对可靠,没有Bug的

  • 解决过程总结

  1. 对BootRom 程序的实现不了解,没有敢怀疑芯片本身存在的问题。有了之前的经验,先开始怀疑烧写工具,和负责烧写工具的工程师一起调试,但是没有什么结果
  2. 在调试烧写工具的时候,注意到ECC校验码的一些信息有异常
  3. 在芯片部门BootRom工程师指点下,客户板子上fused的芯片换成内部unfused的芯片,观察BootRom的打印信息,发现对NandFlash ECC校验有问题
  4. 更换客户的NandFlash 为我们Demo板的Nandflash之后发现ECC校验正常
  5. 怀疑Nandflash有问题,找客户要了不同Nandflash的产品板各2块,发现某几个型号的Nandflash能准确复现ECC校验出错
  6. 最后Bootrom工程师通过手动计算ECC校验码与程序计算对比,发现这几款NandFlash的ECC位数比较长,会触发BootRom代码逻辑里的Bug,但是这一代芯片已经卖出去很多了
  7. 给出WorkAround的解决方法,以后这一代芯片使用带有Bug的BootRom程序的,只出NorFlash启动的方案,不出Nand Only的方案,建议客户将Nandflash Only的方案换成Nor + Nand, 采用Norflash启动

嵌入式Linux 客户通过某认证的平台的MP3音频播放出现异常错误

  • 硬件平台
    公司自研 MIPS 32 SOC + 客户通过某安全认证的产品板

  • 软件环境
    嵌入式Linux系统, 客户基于公司Buildroot 构建rootfs移植了自己的系统,应用以及安全认证相关的第三方软件库,rootfs是ramdisk readonly的, 启动先挂载ramdisk rootfs再挂载客户自己的应用分区

  • 现象描述
    运行公司提供的SDK音乐播放测试用例,播放mp3文件出现exception异常,该异常来自于glibc,客户声称没有对sdk底层代码进行修改

  • Root Cause
    客户为通过第三方安全认证的specification规定glibc库的要求,强行替换升级了板子里面rootfs的glibc库的版本重新编译。公司发布给客户的SDK里面有不开源的固件(比如MP3硬解码器插件)是基于旧的glibc编译的,客户替换升级glibc版本的时候,无法重新编译替换不开源的插件,从而导致兼容性问题

  • 解决过程总结

  1. 因为对SDK里面的各种不开源插件不是很了解,加上错误莫名其妙,看代码和错误提示看不出端倪。
  2. 尝试remount 挂载我们自己的rootfs,则该问题无法复现,如果采用客户的rootfs则可以复现
  3. 把问题范围缩小到rootfs里面,进而发现了公司的rootfs和客户的rootfs的libc版本的差异,通过和客户沟通了解到他们过安全认证的时候替换过glibc重新编译
  4. 最后升级我们的glibc重新给客户发布了固件

嵌入式Linux MIPS 32位平台内存由256MB升级512MB之后,PVR长时间录制压力测试导致系统死机

  • 硬件平台
    公司自研 MIPS 32 SOC demo板, DRAM由256MB升级到512MB

  • 软件环境

  1. 嵌入式Linux系统, Buildroot构建ubifs格式Rootfs
  2. 出于对内容保护的安全性考虑,PVR录像程序是在内核态使用内核地址空间进行录像并保存的,用户态地址空间是拿不到音视频数据的
  • 现象描述
    PVR做整晚的录像和删除压力测试(录像文件超过某个大小,前面的帧会被删除), 用以测试新的512MB内存是否能够全部利用正常工作,结果压力测试导致系统死机

  • Root Cause
    MIPS 32位架构的CPU的物理内存,普通内存是0~256MB, 高端内存默认从256MB往上。由于使用了512MB的DRAM, 重新调整了某些IP的memory CMA的地址空间,导致PVR录像模块使用了高端内存,而MIPS高端内存映射是不稳定不保证物理地址连续的,而录像一般需要连续的物理地址,从而导致系统问题死机

  • 解决过程总结

  1. 处理这个项目的时候,对Linux内核,cpu体系结构,高端内存尤其是MIPS架构的高端内存概念没有深刻的认识,认为理所当然做下去应该没问题
  2. 压力测试出现异常之后,保存log但是没有分析出头绪,后来和一个内核专家一起讨论,找资料,在MIPS社区找到高端内存相关的一些资料
  3. 重新调整了cma的memory map空间,在512MB的DRAM平台上,将PVR IP的地址限定在256MB之内的地址空间,和之前出现死机的平台再次进行对比压力测试,发现限制PVR限制在256MB地址空间之内的平台,录像压力测试不死机,而没有限制的则出现死机,证明了确实是高端内存物理地址不连续,映射不稳定导致的问题
  4. 在512MB DRAM的平台上重新调整了CMA Memory MAP的地址空间,解决死机问题

嵌入式Linux ARM Cortex-A7 科大讯飞在线翻译压力测试概率性段错误

  • 硬件平台
    全志R16 SOC Cortext-A7 四核处理器,公司产品板(某IPC+智能音箱产品)

  • 软件环境
    嵌入式Linux OpenWRT系统, 使用科大讯飞的SDK做离线的NLP语音识别,运行流程如下:
    麦克风采集的语音信息 --> 讯飞远端服务器 --> 识别语音信息,转换成文字 --> 根据识别的文字生成对答的文字 --> 返回识别的文字与对答的文字的字符串数据(json格式)

  • 现象描述
    一晚上不停输入语音信息,做压力测试,会概率性出现段错误,找不到规律性复现段错误的方法

  • Root Cause
    之前历史遗留代码代码中,有一段从返回关于“今天天气如何?/今天天气怎么样?”的对答语句中提取天气信息的代码,该代码在提取信息格式时用了一些hardcode编码,而服务器端返回相关对答信息的文字格式和组合会有一些变化,使用hardcode导致解析信息时指针越界段错误

  • 解决过程总结

  1. 这个问题最大的难点是找不到规律的复现方法,最后我接手这个问题建议把ARM平台的gdb调试器编译烧写进去,其实现在的Cortex-A 系列SOC嵌入式平台用gdb本底调试完全没有问题
  2. 打开gdb调试最终语音对话模块应用,继续做压力测试,出现段错误时通过bt指令分析打印栈,发现天气相关的信息,缩小范围,通过手机录音一段"今天天气怎么样?"的语音,不断输入设备做压力测试,发现复现概率明显增加
  3. 再添加一些调试信息,根据段错误打印的函数调用栈,追溯到段错误的具体函数,发现了这个hardcode导致的指针越界段错误问题,并且代码存在多处hardcode(可能是因为前期代码开发猛操快,不注意代码规范)
  4. 通过打印讯飞服务器返回的语音识别和对答信息,发现"今天天气怎么样?"这样一句简单的问题,服务器端返回的对答信息有一定较小概率出现不同格式的答案(可能是对方NLP人工智能为了模拟人说话的多样性,而不是机械般地重复某些回答),所以做在线语音识别一定不能想当然假定服务器端针对某句语音对答的问题返回的结果是绝对确定,一模一样的, 避免hardcode去解析不确定的信息

嵌入式Linux ARM Cortex-A7 音频应用压力测试导致OOM Killer

  • 硬件平台
    全志R16 SOC Cortext-A7 四核处理器,公司产品板(某IPC+智能音箱产品)

  • 软件环境
    嵌入式Linux OpenWRT系统

  • 现象描述
    晚上烧机压力测试,发现系统核心的业务进程被OOM Killer杀死,而根据抓取的Log显示系统应该还剩余20%左右的物理内存,应该不至于触发Linux内核的oom killer机制

  • Root Cause
    全志提供的Linux内核可能和Android版的Linux内核一起维护或者混用,在OpenWRT系统的Linux内核配置中有一些Android Linux内核的配置,其中Android内核使用的Low Memory Killer模块被选中,而Low Memory Killer 对最少物理内存的容忍程度比普通Linux内核的oom killer低,在还剩20%左右物理内存的情况下触发killer, 杀死了进程

  • 解决过程总结

  1. 因为之前没有做过Android相关的项目,对Android和Linux 内核的差异不是很了解,认为Android的Linux内核和普通Linux内核差不多,因而该问题一直挂着很久没解决
  2. 后面又兴趣看了一下Android的书,发现了Android和Linux差异中提到了Low Memory Killer不同于Linux官方的oom Killer, 于是检查了一下配置,关闭Android Low Memory Killer的选项,果然该问题就不再复现

嵌入式Linux ARM Cortex-A7 用户空间内存碎片化问题

  • 硬件平台
    全志R16 SOC Cortext-A7 四核处理器,公司产品板(某IPC+智能音箱产品)

  • 软件环境
    嵌入式Linux OpenWRT系统, 交叉工具链使用arm-linux-uclibc-gcc

  • 现象描述
    晚上烧机压力测试, 发现系统应用程序空间出现了较为严重的内存碎片,后面可能导致一些应用进程申请内存失败

  • Root Cause
    系统使用了基于较低版本的uclibc的交叉工具链,该uclibc库的内存分配和管理的函数对对避免内存碎片这块做的很差,换成较高版本的glibc工具链,则不会复现这个问题

  • 解决过程总结

  1. 这个问题刚开始不是我处理,那个负责的工程师总认为内存碎片管理是Linux内核做得事情,因而犯了方向性错误,拼命地在内核内存管理,虚拟地址空间管理等代码上加调试,结果一筹莫展。
  2. 我有原厂相关的工作经验,之前做过工具链升级的工作,对工具链,libc库这些基础设施有天然的敏感性,而且知道malloc这类函数在glibc中有通过内存池,缓冲区等方法避免碎片,因而我认为应该问题不在Linux内核,而在用户态,是libc库的malloc底层的内存管理出来问题
  3. 通过替换升级工具链和库,成功验证了我猜想的正确性,最后找到一些文章和资料证明低版本的uclibc库在内存管理与分配上做得太简陋,容易碎片化。

嵌入式Linux USB摄像头无法兼容高版本Linux内核UVC驱动

  • 硬件平台
    Tiny4412 开发板, 三星Exynos-4412 Cortex-A9 四核处理器
    使用台湾沛成科技iP297X 系列UVC转换芯片的USB摄像头

  • 软件环境
    嵌入式Linux Builroot制作的rootfs, Linux内核采用 Linux-3.5/Linux-4.4等较高版本内核

  • 现象描述
    根据原厂提供的移植手册,在Linux较高版的内核(3.5/4.4), 将官方补丁加入Linux内核开源的UVC驱动中,USB摄像头不能正常工作,而在Linux-2.6 版本的内核中,加入官方补丁之后,USB摄像头是可以正常工作的

  • Root Cause
    该台湾小厂的UVC摄像头USB转换芯片存在Bug, 看起来没有拿到USB UVC官方组织的认证和ID号,在补丁中PID, VID都是手动加上去的(不是USB UVC Class官方自带兼容的)。
    这个未认证的USB UVC芯片,它枚举出来的最高带宽endpoint是无法在iso模式下正常工作的,而Linux-2.6 内核的UVC驱动可以获取摄像头512Bytes的次高带宽Endpoint进行ISO模式的数据传输,而Linux 3.5/4.4等高版本的内核中采用的Endpoint选择方法都是一定要找到最高带宽Endpoint来在ISO模式下传输,最后选择了无法正常工作的最高带宽Endpoint

  • 解决过程总结

  1. 通过Dump 出插入USB 摄像头时USB Host 控制器枚举的device endpoint信息,再在开源UVC驱动中加入一些打印,通过Linux-2.6 与 Linux-3.5 内核的打印信息对比,发现他们选择了不同Endpoint, 而Linux-3.5内核选择的高带宽Endpoint无法正常工作
  2. 在UVC驱动中加入一些WorkAround, 如果枚举的VID PID信息显示是这款特殊的摄像头,则在选择Endpoint时强制设置成512Bytesd带宽的节点
  3. 总结: 小公司未通过认证的USB芯片可能有Bug, 与通用开源驱动的兼容性可能有一些未经测试过的问题,廉价模块和芯片总有挖坑的地方。

Android系统 电源regulator动态调压响应测试ovp/uvp欠压过压保护导致系统死机

  • 硬件平台
    公司自研 ARM Cortex-A57 4核 SOC 参考板

  • 软件环境
    系统: Android-N
    Linux内核版本: 4.4

  • 现象描述
    Android系统为了支持低功耗动态调压调频,板子上有很多regulator动态调压芯片,对regulator调压做稳定性压力测试,在规定的调压范围内,自动脚本通过驱动随机发出regulator调压指令,调节相关IP的regulator电压,发行某个IP在该regulator动态调压压力测试中容易导致系统死机

  • Root Cause
    Android系统板的SOC有ovp/uvp欠压保护功能,某些regulator芯片调压范围太广,当前电压和regulator设置的电压的delta值过大,而由于板子上电容,电感的影响,电压变化曲线太平缓,没有及时升压到指定的电压而导致系统过压欠压保护引发死机

  • 解决过程总结

  1. 该自动测试脚本设置的regulator调压值是在某范围内随机产生的,因而该开始死机有一定概率性,没有找到重复性地复现规律
  2. 后来在专家的指导下,在脚本加入一些regulator调压范围限制和偏置值,发现把regulator调压范围缩小到某个区域的时候有很高的复现概率,找到复现规律,所以猜测和电压变化曲线有关
  3. 拿着示波器测试脚本运行时的电压曲线,果然在某范围内上升曲线较慢,容易触发uvp欠压保护
  4. 和硬件商议修改了板子上的一些电容的值,于是解决了这个regulator稳定性问题

Android系统 外设芯片i2c地址的小改动导致ADB不能使用

  • 硬件平台
    RK3399产品板, USB ADB口用的是USB type-C

  • 软件环境
    系统: Android-M
    Linux内核版本: 4.4

  • 现象描述
    客户根据开发板稍微修改的产品板,板子的USB上电检测,烧写固件没有问题,刷入Android-M系统,开机能正常启动进入系统,但是adb 功能不能正常工作,
    adb devices没有反应

  • Root Cause
    硬件工程师对USB Type-C电路的原理和结构理解有误,不知道USB Type-C和以前USB2.0有区别,type-C除了本身的USB协议热插拔电路功能另外还有控制芯片与系统进行i2c通信,系统USB驱动通过i2c发命令给Type-C的控制芯片设置USB的工作模式(如充电, ADB等)。硬件工程师将该Type-C的控制芯片的i2c地址换,设备树没有配置,i2c时钟频率也没有想过配置,导致无法设置成ADB模式

  • 解决过程总结

  1. 之前硬件工程师看到板子能通过USB口上电检测,烧写就以为USB硬件电路上没有问题,认为是软件问题
  2. 我检查了设备树配置,发现某个i2c master控制器下面有一个芯片似乎与USB Type-C有关
  3. 在我对硬件工程师的追问下,发现他为了走线方便,并且以为USB和i2c总线似乎没有关系,改动了该芯片的i2c控制器和地址
  4. 重新配置了该改芯片的i2c地址核时钟频率,ADB就能正常工作

你可能感兴趣的:(火哥的嵌入式Linux/Android疑难杂症工作笔记)