Linux驱动 | 如何加载驱动模块?
0x00 前期准备
主机/work/mydrv/目录下,已经写好了驱动层的C文件:mydrv.c,应用层的C文件:mytest.c,以及用来把mydrv.c生成驱动模块(.ko文件)的Makefile。本实验的目的是:把已经写好的LED的驱动程序mydrv.c编译后加载到开发板上;mytest.c编译及运行,实现通过对设备文件的操作来控制开发板上LED的亮灭。
0x01 编译出驱动模块
先进入/work/mydrv/目录查看一下$ cd /work/mydrv
$ ls
Makefile mydrv.c mytest.c
输入make,开始编译驱动模块
$ make
等待编译完成,再查看一下
$ ls
Makefile Module.symvers mydrv.c mydrv.ko mydrv.mod.c mydrv.mod.o mydrv.o mytest.c
0x02 编译应用层程序
$ arm-linux-gcc mytest.c -o mytest
查看一下目录下是否生成mytest:
$ ls
Makefile Module.symvers mydrv.c mydrv.ko mydrv.mod.c mydrv.mod.o mydrv.o mytest mytest.c
可见经过以上两步已经编译出来了驱动模块mydrv.ko和用来测试的应用层程序mytest。
0x03 把驱动模块和测试程序转移到开发板上
这里,我们使用/work/nfs_root/作为网络根文件系统来,所以我们只需要把程序放到这个目录,就相当于放到开发板上面去了。
$ cp ./mydrv.ko ./mytest /work/nfs_root
$ cd /work/nfs_root
$ ls mydrv.ko mytest -l
现在,这两个文件已经放上去了,我们就可以通过串口控制开发板来使用它们了!
0x04 加载驱动
先加载驱动模块,然后查看是否已加载,命令如下:# insmod mydrv.ko
# lsmod
通常我们还会经常使用到这样两个命令:“cat /proc/devices”,以及“ls /dev/$(DRVICE) -l”,它们分别用来查看向内核注册的设备、设备下的设备文件(应用程序操作的就是
这些文件)。比如说,在这个例子中,我在mydrv.c里面就创建了一个名为“mydrv”的设备,同时创建了一个名为“Hamburger”的设备文件,所以这里的$(DRVICE)应该换为Hamburger。
我们来试一下:
# cat /proc/devices
我们可以看到mydrv就在其中了,它前面的“252”这个数字表示的就是它的“主设备号”。
# ls /dev/Hamburger -l
以上就可以得知驱动模块已加载好了。
0x05 卸载驱动
卸载驱动用的是rmmod这个命令,这里先卸载后查看:
# rmmod mydrv
# lsmod
可见驱动已经卸载了。再来查看一下/proc/devices和设备文件:
# cat /proc/devices
# ls /dev/Hamburger -l
以上就可以得知驱动已经卸载成功了。
0x06 测试驱动
现在先把驱动模块加载上去:
# insmod mydrv.ko
接着运行测试程序mytest:
# ./mytest on
可以看见开发板上面的LED点亮了
# ./mytest off
执行之后LED灭了
以上说明我们的驱动模块符合我们的预期,测试成功!
0x07 mknod
说一下mknod这个命令,它其实就是在/dev/目录下创建一个设备文件,同时指定设备类型,主设备号,次设备号,比如:
# mknod /dev/Coke c 252 1
这时我们就创建了一个名为Coke的设备文件,并指定为字符类设备,主设备号为252,次设备号为1。
在/dev/目录查看一下:
# ls /dev/Coke /dev/Hamburger -l
如果要删除它,就像操作普通的文件一样:
# rm -f /dev/Coke
再说一下次设备号,它其实是可以由我们任意分配的,比如说:把Coke的次设备号也设为0。
# mknod /dev/Coke c 252 0
# ls /dev/Coke /dev/Hamburger -l
可见两者并不冲突。
0x08 两个问题
/dev/Hamburger这个设备文件,我们并没有使用mknod命令去创建它,那它是怎么来的呢?
是这样的,在mydrv.c的代码,有下面两行,它调用了内核的class_create和class_device_create函数,创建了一个名为“mydrv”的class,并且在这个class之下创建了一个名为“Hamburger”的class_dev:
mydrv_class = class_create(THIS_MODULE, "mydrv");
mydrv_class_dev = class_device_create(mydrv_class, NULL, MKDEV(major, 0), NULL, "Hamburger");
那么当mydrv_class和mydrv_class_dev这两个数据结构传入到内核后,内核busybox的mdev(把它看作内核的一个工具就可以)就会自动帮我们去做“mknod /dev/Hamburger c 252 0”了!
主设备号为252,它又是怎么来的呢?
它是系统给我们分配的,同样,在驱动代码里调用了内核的register_chrdev这个函数向内核注册驱动,函数的返回值就是主设备号
major = register_chrdev(0, "mydrv", &mydrv_fops);
0x09 总结
这个例子总结的框图如下:从中我们也可以看出嵌入式开发的特点:代码都是在Linux主机上编译的,而加载、卸载和执行都被放到了嵌入式系统(开发板)上。
上一篇: 查看oracle日志
下一篇: 温度传感器DS18B20实验