linux设备驱动是什么,我个人的理解是liunx有用户态和内核态,用户空间中是不能直接对设备的外设进行使用而内核态中却可以,这时我们需要在内核空间中将需要的外设驱动起来供用户空间使用。linux的驱动主要分为字符设备、块设备、和网络设备三类,在分别驱动时需要注意一下,其中驱动不一定单属于哪一类,一个驱动可能属于多种分类。
一、主备材料
可以根据自己的需要准备相应材料,以下是我自己使用的:
开发环境:VMware
操作系统:ubuntu
开发版:湃兔i2S-6UB
二、下载linux内核源码
1.ubuntu内核源码的下载方式,直接通过命令下载即可,下载后源码存放的地址在/usr/src
路径下
sudo apt-get install linux-source
经过的测试好像ubuntu不需要下载源码也是可以编写驱动的,具体教程见后面内容。
2.湃兔核的内核源码下载地址:http://i2som-zh.oss-cn-beijing.aliyuncs.com/i2SOM-iMX-Linux-706035a.tar.gz。
下载完成后解压到开发环境的路径下,具体路径根据个人爱好。
三、编写驱动
1.入口函数和出口函数
学习编程语言时,代码都会有开始运行的位置,也就是我们所知的入口函数'mian()',同样的原理linux驱动编写也是有入口函数的,只是linux驱动比较特殊,函数基本都是成对存在的,有入口便有出口,所以linux存在入口函数和出口函数,如下所示
module_init(x) //入口函数
module_exit(x) //出口函数
留意过内核源码的小伙伴都知道,入口和出口函数的定义是在liunx内核源码中的include/linux/init.h
文件中,如下图所示:
2.打印函数
linux的驱动中打印日志到控制台的函数和用户空间的打印函数函数名不一样,但是功能和使用方法差不多,linux内核的打印函数是printk()
。
打印函数的定义是在liunx内核源码中的include/linux/printk.h
文件中,如下图所示:
3.编写hello.文件
创建一个hello的驱动文件夹,然后创建hello.c
文件,文件内容如下
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
static int __init hello_init(void)
{
printk("hello_init\r\n");
return 0;
}
static void __exit hello_exit(void)
{
printk("hello_exit\r\n");
}
/*
*模块入口与出口函数
*/
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jiaozhu");
4.编写Makefile文件
编写完驱动程序后,需要创建Makefile文件,在这里需要注意x86和arm架构环境下的内容有所不同
x86架构下的Makefile内容如下所示
KERNELDIR := /lib/modules/5.8.0-59-generic/build
CURRENT_PATH := $(shell pwd)
obj-m := hello.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
注意:ubuntu的所需的内核源码路径在/lib/modules/5.8.0-59-generic/build
下
arm架构下的Makefile内容如下所示
KERNELDIR := /home/i2SOM-iMX-Linux
CURRENT_PATH := $(shell pwd)
obj-m := hello.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
注意:编写arm架构的驱动是需要设置交叉编译器,这里没在Makefile文件中设置交叉编译器的原因是我已经在源码的顶层路径Makefile文件中设置了。
5.编译
完成hello.c文件和Makefile文件的编写后,就可以进行编译了,使用make
命令进行编译,编译完成完成后会生成.ko文件,如下图所示:
到此驱动的编写已经完成了,细心的小伙伴可能已经注意到我加载和卸载驱动时没有日志输出,具体原因我也不知道,在arm开发板中测试时是有日志输出的。
四、测试
编译完成后生成的hello.ko驱动模块可以通过insmod
和modprobe
命令加载模块。
1.ubuntu中使用modprobe
命令加载模块时需要将.ko文件拷贝到/lib/modules/5.8.0-59-generic路径下,然后在进行加载
sudo cp hello.ko /lib/modules/5.8.0-59-generic -f
sudo depmod
sudo modprobe hello
注意:/lib/modules/5.8.0-59-generic
具体的路路径可能不一样,可以进入/lib/modules/目录下查看。
2.arm开发板中使用modprobe
命令加载模块时需要将.ko文件拷贝到/lib/modules/路径下,如果路径不存在直接创建相应的路径即可,arm一般测试方式都是通过nfs挂载根文件系统的形式进行测试。
在开发环境中将.ko文件拷贝至rootfs跟文件系统的/lib/modules路径下
sudo cp hello.ko /home/rootfs/lib/modules -f
然后启动开发板,执行加载命令
depmod
modprobe hello
3.查看加载的驱动和卸载驱动
在任意路径下使用lsmod
命令即可查看驱动模块,如下图所示:
完成后可以通过rmmod hello
卸载驱动模块,注意在ubuntu下可能需要通过root权限才能卸载。
到此我们简单的驱动模块已经编写完成,有写得不好的地方忘各位大佬支出。
参考文献
【驱动】linux设备驱动·入门:https://www.cnblogs.com/lcw/p/3159386.html。
正点原子视屏教程:https://www.bilibili.com/video/BV1fJ411i7PB?p=3。