整理:李肖遥
嵌入式驱动自学者的感受
学习嵌入式,或者说学习现代的计算机编程,如果你想学好,有一个比较要求,那就是你能接受它的设定、它的模式。反过来说,当你真正接受它的设定、它的模式,并记住它们时,我认为,你已经学好了。昨天,我又置之死地而后生了一次。最近一直在搞驱动,一个LCD驱动搞得我几乎要放弃继续走嵌入式这条路。昨夜,睡不觉,打开嵌入学习视频,躺在已关灯很久的房间的床上,大概凌晨3,4点吧。之前我一直都是学习着驱动自编源码的教学,是那种几乎和裸板程序没多大区别的编程方式,只是多使用了一些向内核注册的接口函数。而最近我想换一下,因为很多设备驱动,内核都是自带的,而且是各种平台的设备驱动都有,我想如果能熟悉掌握内核自带驱动的编程,那以后要做某个设备的驱动时,我只需要在自带驱动中修改一下便好了,通过学习LCD平台设备的驱动,我了解了其编程想法,同时也认同这种想法,甚至让我疑惑,学习资料中教自编驱动的意义,为何不直接教如果修改内核源码驱动?于是,继续按着书去修改内核驱动源码,但问题是,书中说他们这种修改,代码成功运行了,但我这,无论怎么调试都失败,我反复检测,我的修改是否与书中一致,检测了很多遍依然没发现哪一步不同,不过,有一点发现是,书中的内核源码和我内核使用的源码有一点点区别(当然书里并没有把所有的源码都贴上,只是修改部分附近会联带着一些,这就是发现,这些联带的没需要修改的源码和我的源码有点区别,比如,我的源码中多了一些设置(看似无关紧要的设置))。与书核对无误但失败后,我又与成功运行的自编驱动核对,我陆续发现我修改的内涵源码中,没有去启动设备,也更没有去点亮背光,而在显存分配后的寄存器设置似乎也有问题,因为这里的地址使用各种宏定义不同的累加或计算,最后算得地址和我的寄存器地址也不知是否吻合,因为驱动源码中最后计算得到的是虚拟地址。于是我对比自编驱动,一点点修改尝试,到睡觉前都没成功。我是想学得理直气壮一点的,最后是能一眼就能找到问题,并迅速轻松解决问题的,我也承认自己确实是有些浮躁。但是经过了昨晚床上的一点绝望的思考挣扎后,我好像想通了:为什么嵌入式学习视频老师要教自编驱动。程序简单简洁,它只能驱动特定的某个设备。如果设备换了需要支持另一款设备,那么你需要重新修改该驱动;如果需要系统同时支持两种LCD,那么它就会变成复杂并且对于内核驱动的简洁优势会削弱不少;如果你想驱动支持多种设备,那自编驱动,相对了内核驱动源码的简洁优势会变成了劣势,因为编程思想的适用范围不同而产生的结果。①从系统层次去考量,变量、宏定义使用多,甚至有些宏定义的值为了方便能让各种在不同的阶段需要不同的值调用,把简单的一个赋值调用变成了需要进行多次运算才能检测到该值是否满足使用要求,因为我们不是该驱动的编码者,不清楚这样做的好处,也或许是内核驱动源码的开发者从整个系统的编程简洁性去考量,这样做或许也是为了让整个系统代码更少,简洁的一种做法,因为每个设备你都给它赋具体的值的话,整个系统中有几百种驱动设备源码,给所有设备的这个位置参数都赋一个值的话,那各设备关于这个值的代码就要多了几百行了,所以还不如,让各设备根据各种平台去对某个宏进行各自的计算来得到合适的值,但某些计算中相同的算法的也整合在一起,这样就减少了系统不少行代码。所以系统中驱动源码是系统开发者对系统源码的整合,是基于系统层的整合。所以,对于我这种对单个设备驱动编码的人,就会觉得系统源码有好多不人性化的地方,会觉得简单的地方也被弄得很复杂。②内核自带驱动还有一些代码是为了兼容以前的版本而添加了,比如以前硬件内存资源稀少,需要使用调色板的方法来减少程序运行时的内存使用量,这也会真假代码的复杂性,这一步虽不是必要的,但如果没弄好,那LCD驱动也不能正常使用。③程序复杂,为了适用在多种设备型号,更简单地添加不同型号的设备驱动,内核对驱动抽象分离,把驱动分为平台管理部分,驱动代码部分(与硬件无关码),和设备代码部分(硬件相关代码)。用户添加新型号设备驱动时,只需要在平台管理部分检查添加设备的匹配信息,和提供一个硬件设备相关的代码(有格式)文件即可。①自编的驱动,简洁,要点明确。这个对于驱动开发者的用处就是:无论你使用的是哪个版本的内核,哪个芯片平台,你可以通过自编码比较简单方便地就可以确认硬件设备的情况,是否正常。如果自编码通过,那可以试用自编码上使用的参数去与内核进行核对、修改,然后再去测试。如果不成功,对于内核中多余的设置(这些大多可能是提供内核用做基本判断的变量)可以先屏蔽,编译出错了,根据提示,找到出错的位置修改添加。因为这些多余的设置,设置对了还行,设置错了,你又不好去定位错在哪。自编的驱动在此处的用处,调试时,可以让你排除多余的失败可能性问题,在较少的代码去查出错误位置,如果你确定你的设置满足了该设备的必需设置,还是失败,你可以比较放心地去怀疑是硬件问题了。如果自编码成功,那个又可以当做你修改内核驱动的一个标准。②内核驱动源码支持管理多种型号的设备的优势是我用使用它的原因。先了解本版本本平台的设备驱动结构,如果是添加型号支持,那就根据自编驱动的参数与设置即可,如果是第一次启动这类设备,你就还需要检测结构是完整性,如果结构完整,参数无误依旧错误,那就把内核驱动源码精简到自编码的简单粗暴设置吧。最终就变成了在基于内核驱动架构下的自编驱动。如果还不行,那无疑是结构性问题了。所以自编驱动,还是有其存在价值的。内核驱动源码内容会变,平台会变,但自编驱动是变得最小的一个,也是最容易实现驱动目的的一个。是一码打天下不可缺少的重要组成部分。
对于嵌入式驱动开发的建议
1) 为了今后的发展,你除了考虑广度以外,更重要的是注意知识的深度。譬如,做过网络驱动,那么是不是只停留在会写驱动的表层上,有没有对Linux内核的网络结构,TCP/IP协议作过深入的了解。2) 在Linux下开发很多时候都要利用现成的东西,没必要什么都自己搞。关键是变成自己的驱动后是否了解原作者编写时背后的一些东西。你应该不止是简单的让它工作。写驱动的时候就要考虑它的性能问题,并给出测试的方法(当然可以利用现成的许多工具,譬如测试网络性能的netperf等)。当你写过Flash驱动,可能会知道Flash的性能有时候有多重要。3) C程序的自我修炼,是否考虑到软件工程方面的一些东西,程序的可维护性和扩展性,譬如LCD驱动,是不是从Sharp到NEC的只需要集中修改很少的几个地方?对于不同品牌的Flash,如果使得Flash的驱动做的更具有灵活性。4) 如果有时间结余,可以关注Linux内核的发展。譬如LCD的驱动有没有考虑到V4L2通用架构,譬如网络驱动用到了NAPI了吗?当然在此之前,假设已经对LDD3, ULK2理解的比较熟了。5) 现在所作的这些驱动还算不得非常核心的东西。如果你想有更好的发展,可以考虑往audio,video,net方面发展,你应该多注意真个行业需要什么样的人才,上述每一项都需要很厚的底蕴,譬如video,需要了解MPEG4, H264等,怎么也要个1到2年才能算个入行阿,所以我建议不要只顾闷头做东西,要适当关注目前的一些应用。6) 对硬件知识的补给,做嵌入式Linux这一行不可能不读硬件的Spec,如果你对硬件的工作机制理解的比较透,会有助你写出性能好的驱动程序。顺便提一点,适时的提高你的英语水平,对你的职业生涯绝对有帮助。(不要等需要的时候再补,来不及)7) 如果有时间,平时注意对Linux应用程序编写的了解/积累,也将有助于你写出很好功能很好的驱动程序。8) 永远不能以为自己做了很多东西,就驱动而言,像TVIN/TVOUT, USB, SDIO等等,好多未知领域呢。在问题还没有解决之前很难说清是哪里不对了。有时候是datasheet里面的一句话没有注意,还有好几次调不出来最后查到是PCB的问题,所以有时候特别晕。‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧