作者 |赵青窕
审校 |孙淑娟
在安卓驱动开发中,不少开发同事反馈的问题在定位后,我发现这些问题大多不是驱动本身的问题,而是开发者对驱动框架理解的不够透彻。比如有些开发者烧录到硬件板子中的镜像本身就不包含驱动的任何信息,有些开发者本地的代码中没有对应器件的驱动。
在这两种情况下,有些刚开始接触驱动的开发者已经开始进行基于硬件环境的调试,此时他们的硬件环境中是不存在对应的器件驱动的,该器件百分百是无法工作的。但经验不足的开发者甚至可能会消耗数天时间来排查器件不工作的原因,为了防止这种低端问题的再次出现,我整理了一份驱动检查表,其主要内容如下:
- 你当前的软件代码中是否存在器件对应的驱动和设备树信息
- 如不存在驱动和设备树,该如何处理
- 如何确保你的驱动和设备树已正常编译
- 如何确保你的镜像中包含正确使用的驱动和设备树
- 如何确保烧录镜像后的硬件环境中,存在对应的驱动和设备树信息
以上这五项是环环相扣的,只有在上一步正确的前提条件下,才能进行下一步。
对于初级开发人员来说,强烈建议按照上面的步骤进行开发调试,因为许多初级人员无法像高级开发人员那样,可以从机器的状态和节点信息,快速推断出可能出问题的模块或者环节。接下来,我将详细的描述如何确认每一个环节。
1.驱动和设备树信息确认及获取
对于安卓模式来说,项目中所用的大部分器件的驱动和设备树均需要从器件厂的FAE处获取,比如指纹,LCD,sensor,NFC。当我们拿到这类器件的设备树和驱动后,结合硬件原理图进行设备树的适配,把驱动和设备树放在本地代码相应位置处,然后进行编译配置进行编译即可。
除了上述类型的器件外,还有一类器件,比如SD卡驱动,按键驱动,指示灯驱动等会随着平台方(高通,展锐,MTK等)的基线进行释放,但也有例外,我最近从事的MTK平台的项目,其释放的基线中就不包含某一功能的驱动,最后我是从MTK平台索要的驱动代码,然后进行的功能调试。
对于一些特殊的定制化功能,可能就没有供应商,且平台默认也不支持这种功能,这种时候就需要自己编写驱动和设备树。
综上所述,假如当前代码中缺少对应平台的驱动和设备树,需要通过平台提供的渠道索要获取,其他驱动可以从器件供应商处获取。那么问题来了,我们如何判断当前代码是否已包含我们需要的驱动和设备树呢?
实际上,对于有经验的工程师来说,通常只看代码就能很快判断出是否缺少驱动或者设备树配置,毕竟他们对代码实现等已经很熟悉了。但对于新手来说,通常要先了解对应平台下某一模块代码架构和实现等,才能做出判断,下面是我入门时所采用的方式,供新手作为参考:
- 从平台方MTK,高通或者展锐等提供的渠道中获取对应的文档,如下图所示,是我获取的MTK文档,这种方式获取的文档是比较权威的。
- 通过网络搜索相关系列教程,网上的内容比较繁多,而且系列教程的比例是比较少的。对于需要入门的开发者来说,尽量查找系列类教程。
- 向别人请教,有些公司会有自己的代码架构,这种只能通过公司内部途径,或者自己看代码,文档来做进一步了解。
2.驱动和设备树编译
实际上,编译是个庞大的系统,但在本文不打算说明编译相关内容,仅仅用来说明我们的驱动和设备树是否被编译到。
对于驱动来说,当进行编译后,会在特定路径下生成对应的中间文件,我们可以通过查看是否有中间文件,或者中间文件的时间戳是否更新,从而确认驱动是否已经编译。
下图中显示了中间文件的路径为:out/target/product/(项目对应路径)/obj/KERNEL_OBJ/drivers (图中省略了个人或者项目信息)。
对于设备树来说,当进行编译后,同样也可以在out路径中找到,如下图是采用在设备树编译的中间文件中查找关键字的方式,采用的命令是grep “cd-gpios” .–rn,此处查找的cd-gpios是SD卡相关功能中设备树中的配置项,从下图中可以看出当前我的SD卡对应的设备树确实已经编译,且采用的cd检测引脚是GPIO4。假如在设备树中修改了cd检测引脚,那么可以在编译后重新采用下图中的方式来确认修改是否生效。
当发现驱动或者设备树没有编译,那就需要查看配置是否正确,此处要注意有些公司会对代码进行局部重构,重构后的代码对应的中间文件可能会因为编译脚本的原因而存在于其他路径,本文中仅仅说明上图中的两个标准路径,供大家学习了解。
3.镜像中是否包含修改
镜像本质上是打包压缩,因其进行了压缩,所以无法使用grep等类似方式来查询镜像中是否进行了修改,要想确认是否包含了修改,可以采用反编译的方式,且大多数平台代码路径编译out/host/下是有对应的反编译工具,比如反编译dtbo.img(设备树相关镜像),就需要用到out/host下的反编译工具mkdtimg 和dtc,但是该方法不常见,通常我就是采用时间戳来进行简单判断,刷机时采用最近的镜像即可。
4.判断硬件环境中是否有对应驱动和设备树信息
我们得感谢内核的sys架构,我们的驱动和设备树均会在/sys/bus下的对应路径创建对应的节点信息,只有在该路径下对应的总线下能找到驱动和设备树信息(比如在/sys/bus/platform/device和/sys/bus/platform/driver),我们才可以开始调试驱动,否则你所做的驱动调试工作将是徒劳的。
如下图所示是在SPI总线(/sys/bus/spi)的drivers路径下查找到对应的驱动信息和在devices下找到的设备树信息。
在进行查找的时候,首先我们需要知道驱动和设备树对应的总线信息。上图中对应的驱动代码在加载的时候采用spi_register_driver函数来把对应驱动注册到spi总线,所以是在spi下总线查找对应的信息的。倘若您的代码中采用的是platform_driver_register函数,那么就应该在/sys/bus/platform总线下查找。若采用i2c_register_driver注册驱动,那么就应该在/sys/bus/i2c总线下查找。总之,您代码中使用的驱动注册函数就决定了您的驱动对应的总线信息,在对应总线下查找信息即可。
假如您的设备树是在spi节点下,那就需要在/sys/bus/spi/device下查找对应的设备树信息。假如您的设备是在I2C节点下,那就需要在/sys/bus/i2c/device下查找对应的设备树信息。总之,设备树中添加节点的位置就决定了您设备树对应的总线信息,需要在对应的总线下查找对应的设备信息。
5.总结
本文没有谈及设计驱动和设备树的任何编写注意事项或者技巧,也并非代码调试技巧,而是说明了在代码调试过程中的几个关键注意点,只有在这些关键点全部没问题的情况下,才能开始代码调试。
作者介绍
赵青窕,51CTO社区编辑,从事多年驱动开发。研究兴趣包含安全OS和网络安全领域,发表过网络相关专利。