为什么编译驱动程序之前要先编译内核?
⚫ 驱动程序要用到内核文件:
比如驱动程序中这样包含头文件:#include <asm/io.h>,其中的 asm 是
一个链接文件,指向 asm-arm 或 asm-mips,这需要先配置、编译内核才会生成asm 这个链接文件。
⚫ 编译驱动时用的内核、开发板上运行到内核,要一致:
开发板上运行到内核是出厂时烧录的,你编译驱动时用的内核是你自己编译的,这两个内核不一致时会导致一些问题。所以我们编译驱动程序前,要把自己编译出来到内核放到板子上去,替代原来的内核。
⚫ 更换板子上的内核后,板子上的其他驱动也要更换:
板子使用新编译出来的内核时,板子上原来的其他驱动也要更换为新编译出来的。所以在编译我们自己的第 1 个驱动程序之前,要先编译内核、模块,并且放到板子上去。
编译内核
不 同 的 开 发 板 对 应 不 同 的 配 置 文 件 , 配 置 文 件 位 于 内 核 源 码arch/arm/configs/目录。kernel 的编译过程如下:
cd /home/book/100ask_imx6ull-sdk/Linux-4.9.88
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ make mrproper
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ make 100ask_imx6ull_defconfig
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ make zImage -j4
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ make dtbs
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ cp arch/arm/boot/zImage ~/nfs_rootfs
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs
编译步骤参考如图 5.1,编译完成 zImage 后才可编译设备树文件
。
编译完成后生成的文件如图 5.2 所示:
编译完成后,在 arch/arm/boot 目录下生成 zImage 内核文件
, 在arch/arm/boot/dts 目录下生成设备树的二进制文件 100ask_imx6ull-14x14.dtb
。把这 2 个文件复制到/home/book/nfs_rootfs 目录下备用,如图5.3:
编译内核模块
内核模块:内核模块是一种动态链接的机制,它可以在运行时加载到内核,从而扩展内核的功能。内核模块可以是设备驱动程序、系统调用函数、文件系统等,它能够通过内核提供的机制来进行操作系统的扩展与改进。
内核模块的编译过程相对于内核的编译而言,要简单得多。内核模块的编译是将一个独立的源文件编译成一个动态链接库文件(.ko文件)的过程。一般情况下,Linux内核源码集成了一个叫做Makefile的工具,可以实现内核模块的编译。
进入内核源码目录后,就可以编译内核模块了:
book@100ask: cd ~/100ask_imx6ull-sdk/Linux-4.9.88/
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ make modules
内核模块编译完成后如图 5.4 所示:
安装内核模块到 Ubuntu 某个目录下备用可以先把内核模块安装到 nfs 目录(/home/book/nfs_rootfs)。
注意:后面会使用 tree 命令查看目录结构,如果提示没有该命令,需要执行以下命令安装 tree 命令:
sudo apt install tree
执行以下命令安装模块:
book@100ask:~$ cd ~/100ask_imx6ull-sdk/Linux-4.9.88/
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ make ARCH=arm INSTALL_MOD_PATH=/home/book/nfs_rootfs modules_install
如图 5.5,把模块安装在 nfs 目录“/home/book/nfs_rootfs/”下:
安装好驱动后的/home/book/nfs_rootfs/目录结构如图 5.6 所示:
安装内核和模块到开发板上(重要)
假设:在 Ubuntu 的/home/book/nfs_rootfs 目录下,已经有了 zImage、dtb 文件,并且有 lib/modules 子目录(里面含有各种内核模块,也就是ko驱动文件)。
接下来要把这些文件复制到开发板上。假设 Ubuntu IP 为192.168.5.11,在开发板上执行以下命令:
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
cp /mnt/zImage /boot
cp /mnt/100ask_imx6ull-14x14.dtb /boot
cp /mnt/lib/modules /lib -rfd
sync
最后重启开发板,它就使用新的 zImage、dtb、模块了。
编译 led 驱动
led 驱动
首 先 , 进 入 驱动文件的目 录 , 修 改 Makefile 文 件“KERN_DIR
”为自己的内核所在路径。如图 5.7 红框所示,如果你的内核源码不在此目录则根据你的实际情况进行修改:
注意:有些并不按照前面的文档编译内核,而是使用 Buildroot 来编译内核,那么 led 驱动程序的 Makefile 中要指定 KERN_DIR 为如下目录:
KERN_DIR = /home/book/100ask_imx6ull-sdk/Buildroot_2020.02.x/output/build/linux-origin_master
修改完内核所在目录后,就可以编译 led 模块驱动了,如图 5.8 红框 1 所示,执行“make all
”命令就可以编译,编译完成后会生成 100ask_led.ko ,ledtest 两个文件。
此时,把这两个文件拷贝到 Ubuntu nfs 目录下备用:
cp 100ask_led.ko ledtest ~/nfs_rootfs
在开发板安装驱动模块
下载驱动程序
开发板启动后通过 nfs 挂载 Ubuntu 目录的方式,将相应的文件拷贝到开发板内。如果你使用的是 VMware 桥接方式,假设 Ubuntu IP 为192.168.5.11,在开发板上执行以下命令:
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
[root@100ask:~]# cp /mnt/100ask_led.ko ./
[root@100ask:~]# cp /mnt/ledtest ./
关闭系统默认 cpu 状态灯
可能开发板上的 led 灯已被设置为 CPU 状态灯(可以看到绿色 LED 在闪),我们需要将其关闭,才可以使用我们的驱动对其进行操作,关闭方法如下所示:
[root@100ask:~]# echo none > /sys/class/leds/cpu/trigger
我们后面出厂的开发板,已经把 CPU 状态灯禁止了,所以上述命令可能出错:这没影响。
安装驱动模块
在开发板串口终端上执行如下命令,即可安装相应的驱动模块。
[root@100ask:~]# insmod 100ask_led.ko
安装完成后可以执行 lsmod 命令来查看是否安装成功,如图 5.9 所示:
如果你没有更新板子上的内核,会出现类似如图 5.10 所示错误:
可以强行安装驱动程序,比如使用“insmod -f hello_drv.ko”这样的命令,它会提示说“内核已经被污染了”,但是不影响学习、不影响使用,如图 5.11 所示:
执行测试程序
驱动模块安装成功后,就可以使用测试程序来控制 led 灯的状态,如图 5.12所示,操作 led 灯时可同时观察开发板串口旁的灯是否有亮灭的变化。
[root@100ask:~]# chmod +x ./ledtest
[root@100ask:~]# ./ledtest
Usage: ./ledtest <dev> <on | off>
[root@100ask:~]# ./ledtest /dev/100ask_led0 on //打开 led0 灯
[root@100ask:~]# ./ledtest /dev/100ask_led0 off //关闭 led0 灯