前言
对于Linux驱动我已经是太久没有弄,已经生疏得不行了,而我当前的硬件上Linux应用项目又不可能不涉及到驱动部分,没办法只能靠自己到处查找资料,把以前忘掉的知识点补一点回来。
个人觉得通过这几天学习还是有一定的收获的,不过我还是得感谢以前在学校做过这样的东西,正应为有以前的了解,所以才让我在现在学习的过程中能够更快速全面的看清Linux驱动的流程,能够更加容易的理解哪些拗口概念。
这篇文章算是我这几天的一个小结,只是简单的介绍比较零散的Linux驱动开发的一些基础要点与理论,因为一两天的时间根本就不可能能够深入Linux驱动开发的细节。
Linux驱动开发模式
linux驱动加载有两种:一种是直接集成到内核中的,它时随内核的启动而加载;另一种是动态加载,在系统运行后,使用insmod等命令来加载。
相对的驱动开发也可以分两种,一种直接编译到内核中,另一种以模块形式编译,完成后将生成的驱动模块文件动态的加载到系统。显然后一种方法是更是和开发阶段使用,前一种更适合完成开发后release。
Linux驱动类型
Linux下驱动开发一般设备归为三类:字符设备、块设备、网络设备
1. 字符设备
所有能够像字节流一样访问的设备都可以认为字符设备,字符设备是必须串行顺序依次访问的,字符设备被映射为文件系统中的设备节点,通常在/dev/目录下,一般通过open、read、write等函数操作设备节点来控制。
2. 块设备
一般是指磁盘、内存、Flash等存储设备,它们不仅能像字符设备那样以字节流的方式访问,也可一次传输任意多的字节。
3. 网络设备
相对前两个算是特殊的设备,通过socket来操作。
设备号
设备号主要用来标示设备,由两部分组成:
- 主设备号:一般主设备号表示与同属一类的设备,可以共用同一驱动。
- 次设备号:当有多个设备属于同一类设备时,用次设备号区分
通过宏:MKDEV(int major, int minor),可以将主设备号和次设备号组合成一个设备号。
Linux设备驱动开发的基本流程(字符设备为例)
1. 找到内核源码
驱动程序是依赖于你的内核的,所以想面对哪个系统开发驱动,需要先找到它对应版本的源码。一般Linux系统都有将自己的内核源码保存到相应的目录。我们可以通过查看 /lib/modules目录下的内核版本信息,选择对应的版本,使用ll命令查看所选版本内核的原始路径在哪。
2. 编写Makefile
我们自己写的驱动源码不用再内核源码目录下,而可以放在我们自己指定的任意目录中,但是需要设置内核源码根目录的路径,同样也需要设置编译器,这些都可以在Makefile中设置。Makefile的写法一般都是固定格式的,可以参照别人驱动工程来写。
3. 编写驱动源码文件
驱动代码与一般的应用程序相比没有main函数,驱动模块的入口是设置被为“module_init”的函数。另外打印输出也不用printf,而使用“printk”函数,而printk函数对显示消息的重要性做了等级划分的,可以根据实际情况,设置不同的等级参数。
4. 创建设备节点
设备节点是我们Linux上层应用通过驱动操作硬件的接口,我们可以在通过通过命令手动创建,也可以在驱动中就自动创建出来。
5. 实现file_operation结构
“file_operation”是驱动程序中十分重要的结构,这个结构记录了我们在应用中操作设备所使用的open、read、write等API的对应的实际实现函数。file_operation不需要讲其成员全部都配置好,只需要配置我们驱动需要用的,不用的部分会自动赋值为NULL。
6. 编译加载
直接使用make编译驱动模块,生成.ko文件,使用“insmod”命令加载驱动,通过dmesg和tail结合显示详细的加载输出信息,如下:
1 | insmode test.ko |
也通过“lsmod”查看已加载成功的模块,如果不需要了的话可以通过“rmmod”命令卸载指定模块。
7. 编写应用测试
为了验证驱动是否能够正常工作,我们在通常需要自己编写一个小的Linux应用程序来使用用一下我们的驱动,测试其是否能正常工作。
其他
另外就是Linux驱动是一个很庞大的结构框架,出了上面说的三种驱动类型的基本分类外,Linux系统中还存在很多驱动子系统,这些子系统是建立在三种驱动类型的基础之上的,而很多设备驱动在需要在这些子系统的框架之下编写代码,这种的话就需要了解这些子系统的具体要求了。