刚开始学习驱动时, 导师就安排了点亮闪光灯的阶段目标,今天终于实现了。从看书到点亮实际的闪光灯,遇到不少问题。
书里的程序是为了解释字符设备驱动的通用结构和模型,所用的设备是虚拟的,只是一段内存区。我能理解程序,也能在虚拟机上实现对虚拟设备的操作,控制,但是对点亮实际的闪光灯——尤其是如何把创建的文件节点、write和ioctl方法与实际的闪光灯寄存器(不严格)控制对应起来很茫然.
于是在继续学习时穿插着看服务器上的闪光灯驱动源码,但是源码有用i2c实现的,有用PINCTRL实现的,都用ATTR方式自动创建的sys节点,这部分是学习计划里后面的学习内容。我想到的理想的实现方法是用书里的通用,简单结构加上源码里的与闪光灯寄存器有关操作的内容。
按照这个思路,我开始找合适的端口控制方法,了解到了gpio控制符合我的想法,于是找到用gpio实现闪光灯驱动的项目源代码,用自己的实验驱动替换掉原来的驱动,完整保留原有驱动gpio头文件和端口定义,相当于跳过gpio硬件相关部分的学习与GPIO配置的一步。
编译通过,烧进手机adb调试,发现节点写值不能打开闪光灯,于是了解如何查看log,在代码里加进调试信息打印,找到了类型转换的问题。echo 1 > /dev/ldst 写入的1 是char ‘1’而不是char 1 或者int 1 。
改 if(value==1)为if(value=='1')后抓到了“light on”调试信息,但紧接着是else里的“light off”信息,if和else都执行了。猜想是因为执行copy_from_user(&(dev->value), buf, 1)时,因为 buf是字符串,把字符 value 转换成了字符串,‘1’之后多了一个‘\0’,这样执行完if(value=='1')之后指针移动把else也执行了,相当于闪光灯开了一瞬间就关了。(((!!!存疑)))
把else改成了else if(value=='0') {关灯},再adb写值就点亮闪光灯了,但是对为什么在if (value=='1'){点亮}else{熄灭}中,if和else都执行到还是有疑问。
准备在copy_from_user(&(dev->value),buf, 1)之后,检测一下value的类型.
经验总结:
整个编译很费时间,如果只改了内核文件夹就只重编内核:
make -j24 kernel bootimage 2>&1 | tee kernel.log
编译、烧进手机,再adb调试很费时间,一次操作就得10分钟。测试完闪光灯确实能亮后,如果改动部分不涉及硬件表现,应该在虚拟机上测试改动部分。也就是以模块-m选项只make驱动,安装模块、创建节点,用虚拟设备测试驱动逻辑。