平台环境

  • 开发板:正点原子I.MX6U Alpha V2.2开发板
  • 内核版本:Linux 4.1.15

实验回顾

字符设备驱动的开发一共做了6个实验,对应《正点原子驱动开发指南V1.6》中的第四十章至第四十五章。经过几章实验,开始入门Linux设备驱动的开发,对驱动开发有了初步认知。

可以察觉,这几章是按照一定的顺序编排的。

最初始的字符设备驱动开发实现了一个简单的向内核发送、从内核读取数据的demo,学习了驱动中设备操作函数和用户态函数的对应关系

接下来LED驱动开发实验,将驱动落在了实处,学习了如何在驱动中配置相应的硬件寄存器。这里直接将对应的寄存器地址宏定义在驱动源码中,将寄存器地址映射为指针,向对应指针写入对应值的方式完成硬件寄存器的配置。

通过ioremap、iounmap映射地址,通过readb/w/l或writeb/w/l分别进行8bit、16bit、32bit的内存读写操作。

第三个实验是新字符设备驱动开发,与前两个有所区别的地方在于,前两个实验中,设备文件(/dev目录下的设备文件)均需要自己通过mknod命令手动创建,而且设备号也是自己手动分配的。而第三个实验则实现自动分配设备号、自动创建设备文件,在卸载驱动时,自动释放设备号、删除设备文件等操作。不过自己在前两个实验中就实现了这些操作,所以第三个实验直接略过了。

老版本驱动使用的函数:register_chrdev和unregister_chrdev

新版本:alloc_chrdev_region和unregister_chrdev_region

第四个实验是对Linux设备树的认知。领略了设备树诞生的原因:“This whole ARM thing is a f*cking pain in the ass.”。对.dtsi、.dts、.dtb和DTC工具等有初步的了解,初步学习了设备树的语法知识。

第五个实验则是基于设备树方式完成GPIO驱动开发,点亮一个LED灯。

第六个实验介绍了pinctrl子系统和gpio子系统,并基于设备树、pinctrl子系统和gpio子系统实现了一个自选GPIO的拉高拉低。最终效果是实现了,但是存在一个gpio_get_value始终不变的问题,经查阅,貌似是使用字符设备 +gpio子系统调用的方法,没法使用pinctrl子系统,需要配合平台驱动才可以。【先挖一个坑】于是准备后面做到平台驱动时,再对比这解释这其中的原因。

这六个实验,逐步加深了自己对驱动的认知,一点点向下陷入、抽离。但到这里,还没有涉及到总线的概念,而是驱动直接对接设备。而且前5个实验,虽然是使用了设备树等,但也是需要寄存器地址进行自行配置,总体上还有些单片机开发的味道。驱动框架、分离分层思想并没有体现出来。但对驱动基本概念的理解、驱动开发的起点入门,可以有很好的帮助作用。

实验要点

知识向

  • 在用户态调用对应的系统调用,在驱动中会有一个与之对应的驱动函数。定义在include/linux/fs.h中的关键的file_operations结构体,此结构体为Linux内核驱动操作函数集合(图片展示了部分函数)。
  • Linux驱动有两种运行方式,第一种将驱动编译进Linux内核中,当Linux启动时自动运行驱动程序。第二种将驱动编译成模块,即.ko文件,在Linux内核启动后可以动态加载/卸载。

    使用insmod命令或modprobe命令,二者一个大的区别在于后者会自动分析依赖。

    常用的命令insmod rmmod lsmod modprobe depmod等

  • 设备号概念

    Linux中每个设备都会有一个设备号,由主设备号和次设备号组成,主设备号表示某一个具体的驱动,次设备号表示使用该驱动的各个设备。设备号有动态、静态两种分配方式,Linux社区推荐使用动态分配设备号。

  • printk根据日志级别对消息进行分类

    8个消息级别定义在include/linux/kernel_levels.h里面(见下图),如果开发自己的日志子系统,可以参考这8个消息级别的定义。

    printk("KERN_EMERG "gsmi:Log Shutdown Reason\n\r");   //使用方法
  • 字符设备驱动程序init和exit两个函数的实现过程,具体可见gitee代码仓库的demo。

  • Linux设备树概念

    设备树,Device Tree,描述设备树的文件叫DTS(Device Tree Source),采用树形结构描述板级设备。设备树信息分两级,.dtsi中描述SOC级信息(如CPU架构、主频、外设寄存器地址范围等),.dts描述板级信息(开发板上有哪些具体设备等)。

    DTC工具是编译设备树源码文件的工具,在Linux内核scripts/dtc目录下。

    DTS语法可以参考《Devicetree Specification》文件。

    其它重要概念:根节点、设备节点、标准属性(如compatible、model、status、#address-cells、#size-cells、reg等属性)。

    设备树在系统中的体现:/proc/device-tree目录。

  • 常用的OF函数

    Linux内核提供了一系列函数来获取设备树中的节点或属性信息,这一系列函数有统一前缀“of_",在很多地方被称为OF函数。函数原型定义在include/linux/of.h中。

    常用的有:of_find_node_by_name、of_find_node_by_type、of_find_compatible_node、of_find_matching_node_and_match、of_find_node_by_path、of_find_property、of_iomap等。

  • pinctrl子系统和gpio子系统在用完Platform驱动后,再总结。【再挖一个坑】

技巧向

  • 驱动开发所使用的内核文件和最终驱动要运行的系统内核一定要一致。

  • Linux内核文件中,Documents目录下提供了CodingStyle文件,可以通读,了解Linux内核的代码编码风格。

  • 内核开发、驱动开发调试的时候,一遍又一遍烧写系统不是一个好的选择。比较方便的做法是使用uboot通过nfs或tftp将kernel、dtb下载到内存中,然后通过网络挂载文件系统。

    在设置完uboot的ipaddr serverip gateway gatewayip ethaddr等网络属性后,

    设置bootargs

    setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.0.55:/home/sigmapoet/work/board/zdyz/I.MX6U_ALPHA/references/nfs/rootfs,proto=tcp rw ip=192.168.0.50:192.168.0.55:192.168.0.1:255.255.255.0::eth0:off'

    使用类似下列命令完成上述操作

    nfs 80800000 192.168.0.55:/home/sigmapoet/work/board/zdyz/I.MX6U_ALPHA/references/nfs/zImage

    nfs 83000000 192.168.0.55:/home/sigmapoet/work/board/zdyz/I.MX6U_ALPHA/references/nfs/imx6ull-alientek-emmc.dtb

    bootz 80800000 - 83000000

  • 在驱动开发中,同一个设备的不同属性定义成一个结构,是一个比较专业的写法。例如要将一个字符设备的class、cdev、det_t等类型的数据类型定义在一个设备结构体里面。

参考资料

  • 《正点原子驱动开发指南V1.6》
  • 《Linux设备驱动开发》 约翰马迪厄著 人民邮电出版社出版
  • 《Linux设备驱动开发详解》 宋宝华著 机械工业出版社出版

代码仓库

gitee代码仓库:https://gitee.com/sigmapoet/zdyz-driver

说点什么
请文明发言!
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...