本帖最后由 仰望星空之云 于 2021-7-10 10:24 编辑
《【正点原子】I.MX6U嵌入式Linux驱动开发指南》教程里也有讲解到如何将驱动编译进内核,在内核移植部分 37.4 CPU 主频和网络驱动修改 和 第五十九章 Linux LCD 驱动实验 部分也讲到这个方法。驱动可以编译成.ko文件,也可以编译进内核,编译成.ko文件的,我们需要用 手动执行指令insmod或者 modprobe来加载这个模块,编译进内核的话就不用手动加载模块了。前者叫动态加载,后者叫静态加载。本次以 《【正点原子】I.MX6U嵌入式Linux驱动开发指南》教程里 第四十二章 新字符设备驱动实验 为例子来将 newchrled驱动编译进内核。
简单总结一下操作步骤:
1、添加驱动.c文件到内核源码的drivers下对应的目录下 2、修改drivers下对应目录下的Kconfig文件 3、修改drivers下对应目录下的Mkefile文件 4、修改设备树 5、执行make distclean、make defconfig相关的指令,然后执行make menuconfig,在menuconfig菜单中选中我们新添加的驱动,目的是将其编译进内核,最后执行make进行编译。(简单概括就是进行内核的配置和编译) 6、将编译好的内核放到开发板上运行,进行验证。
下面我们按照以上简单的步骤进行操作。 PS:我使用的内核源码是我自己根据教程移植的,大家用自己的内核源码的话,配置指令和编译指令就根据自己的配置文件来。 一、添加驱动.c文件到内核源码的drivers下对应的目录下 本实验对应的例程路径为:开发板光盘-> 2、Linux 驱动例程-> 3_newchrled,我们在A盘找到驱动源文件newchrled.c,将其拷贝到内核源码的/drivers/leds/下。
内核源码的drivers目录中是系统中所有的设备驱动源程序存放的地方,其下面有分类很多目录,比如/drivers/net下是和网络有关的驱动程序目录,/drivers/sound下的是和声卡有关的, 其实也可以通过目录下的字眼大概辨别出来里边的目录对应的是什么的。这里,新字符设备驱动操作的是一个LED灯的实验,我就把newchrled.c放在了和LED相关的目录下了。
二、修改/drivers/leds/下的Kconfig文件
Kconfig下有很多的config,每一个“config”关键字后面表示定义一个新的配置选项,比如config LEDS_SYSCON这个配置选项,后面的几行代码表示这个配置选项的一些属性信息,属性会包含有 类型、数据范围、输入提示、依赖关系、选择关系及帮助信息、默认值等信息。如bool就是表示一个类型,help就是表示一些帮助信息,depends on表示依赖关系,tristate表示三态选择器,三 态可以有y、n、m,(y就是yes,n就是no,m就是model的意思)意思就是在菜单配置时,有三种选择的情况,分别是编译进内核、不编译、编译成模块驱动。关于Kconfig的配置,大家可以看看教程和视频以及网上查询更加详细的资料介绍。
如下图,添加一个config LEDS_TEST,配置选项,我就照葫芦画瓢了,复制里边现成的然后简单改一下,这个是我添加的一个config,修改好了保存退出。 - config LEDS_TEST
- tristate "LED test"
- depends on LEDS_CLASS
- select REGMAP
- help
- This option enables support for the test.
复制代码
三、修改/drivers/leds/下对应目录下的Makefile文件
添加这行: - obj-$(CONFIG_LEDS_TEST) += newchrled.o
复制代码
配置的时候注意格式和套路,CONFIG_LEDS_TEST其实就是在Kconfig下配置好的config LEDS_TEST,将它们写成大写,newchrled.o就是编译newchrled.c以后生成的文件, $表示一个取值符号,$(CONFIG_LEDS_TEST)这个就是一个整体,表示取CONFIG_LEDS_TEST的值,CONFIG_LEDS_TEST可以是y(表示编译进内核),也可以是m(表示编译成模块), 或者是n(其实就是不编译,没编译进内核也没编译成模块),到底它是y?是m?或者是是n呢?这个由我们自己在menuconfig菜单配置那里定下来,修改好了保存退出。
四、修改设备树 因为我做的这个实验是点亮和灭掉LED灯的,从原理图上看出这个灯是接在了GPIO1 IO3上,这个灯其实在设备树下有被配置成了呼吸灯了,并且其默认状态是开启的,也就是灯会 亮(呼吸灯就是在文件系统挂载以后灯会一闪一闪的,有被配置成其它灯的也最好关掉吧),这个会影响我的测试,所以我要在设备数下将对应的节点整个注释掉。 - /*
- ledsss {
- compatible = "gpio-leds";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_gpio_leds>;
- status = "disabled";//add by lhy
- led-user {
- label = "led-s";
- gpios = <&gpio1 3 0>;
- linux,default-trigger = "heartbeat";
- default-state = "on";
- };
- };
- */
复制代码或者可以不用整段注释掉。修改 default-state = "on";为 default-state = "off";也是可以的
五、进行内核的配置和编译 (1)清理内核: - make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
复制代码
(2)配置内核: - make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_alientek_emmc_defconfig
- make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
复制代码
在执行menuconfig的时候会出来配置菜单,找到Device Drivers了按下回车进入
在Device Drivers下找到LED Support,按下回车键入,然后在LED Support下找到LED test ,看到没,这个LED test 就是我们之前在 Kconfig下配置的tristate "LED test"。我们按下键盘下的Y键就出现 了*号了,这个表示选中此选项并编译进内核的意思,按下键盘的M键就会显示M,表示选中并编译成模块。(后面我会啰嗦两句讲一下编译成模块后怎么操作)这里,按下Y键选择编译进内核。
保存并退出,进行内核编译。 (3)编译内核
- make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
复制代码
PS:这里要啰嗦两句,我们在执行make menuconfig指令,菜单栏配置好了选项,然后保存和退出菜单后会在imx_alientek_emmc_defconfig以及.config文件中找到对应的配置,可以找到CONFIG_LEDS_TEST=y这句。
这个是因为我们在munuconfig的时候,将LED test 这个配置选中并编译进内核了,所以当执行make menuconfig并配置完菜单,保存退出以后,imx_alientek_emmc_defconfig和.config下自然多出了这句话。 所以/drivers/leds/Mkefile下的obj-$(CONFIG_LEDS_TEST) += newchrled.o也就等于obj-y+= newchrled.o,即在执行编译内核的时候,内核会根据Makefile的配置信息将newchrled驱动编译进内核。如果我们 在make munuconfig的时候,选择将newchrled驱动编译成模块(也就是在菜单选择的时候选中M),那么CONFIG_LEDS_TEST=m,/drivers/leds/Mkefile下的obj-$(CONFIG_LEDS_TEST) += newchrled.o也就等于 obj-m+= newchrled.o,即在执行编译内核的时候,内核会根据Makefile的配置信息将newchrled驱动编译成模块。如果此时再执行distclean指令的话,这些配置就会被清掉了(我遇见有的小伙伴在操作教程 37.4.4 保存修改后的图形化配置文件 这节实验的时候,一不小心又执行了distclean指令,把之前的配置给清理掉了,然后在测试移植好的内核的时候,发现网络不通啦 。) 六、进行验证 将编译生成的内核zImage和设备树imx6ull-alientek-emmc.dtb拷贝到开发板上运行(这里我使用TFTP加载的内核和设备树,NFS挂载文件系统)。开发板启动设置环境变量: - setenv serverip 192.168.1.25
- setenv ipaddr 192.168.1.100
- setenv ethaddr 00:04:9f:04:d2:35
- setenv gatewayip 192.168.1.1
复制代码
192.168.1.25是我的ubuntu的IP地址,192.168.1.100是开发板的IP地址
- setenv bootcmd 'tftp ${loadaddr} zImage;tftp ${fdt_addr} imx6ull-alientek-emmc.dtb; bootz ${loadaddr} - ${fdt_addr};'
- setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.1.25:/home/MY/NFS/rootfs ip=192.168.1.110:192.168.1.25:192.168.1.1:255.255.255.0::eth0:off'
复制代码
/home/MY/NFS/rootfs是我ubuntu下NFS的文件系统目录
设置好环境变量以后保存,开发板重启。 按照教程编译ledApp然后将生成的ledApp拷贝到ubuntu中NFS目录下的文件系统根目录下 - arm-linux-gnueabihf-gcc ledApp.c -o ledApp
- cp ledApp /home/MY/NFS/rootfs
复制代码
开发板启动后,在文件系统的根目录下查看有了拷贝的ledApp文件,同时查看/dev下已经生成/dev/newchrled设备节点(以前我们在做这个实验的时候,是编译成模块然后通过执行 指令modprobe newchrled.ko加载模块后才能生成/dev/newchrdev这个设备节点,现在编译进内核了直接启动后就有了这个节点了)。
现在我们测试一下功能,执行如下指令: - ./ledApp /dev/newchrled 1
- ./ledApp /dev/newchrled 0
复制代码
发现当执行第一个指令的时候底板的DS0红灯会亮起,执行第二个指令的时候,底板的DS0红灯会灭掉,现象和我们教程上的一致,实验成功。
七、(啰嗦一下)菜单选中编译成模块的情况 如下如果将newchrled驱动编译成模块,菜单下选中为M(这个方法其实和教程里的方法类似的,只是在编译内核的时候也同时编译生成newchrled.ko文件了,或者之前编译了内核,可以只编译模块make modules来生成.ko文件):
编译完成以后,会发现在内核源码的/drivers/leds下生成newchrled.o文件和newchrled.ko文件,将生成的newchrled.ko文件拷贝到文件系统根目录下。 或者,如果文件系统在ubuntu下,比如NNFS挂载文件系统,可以执行 make modules_install INSTALL_MOD_PATH=/home/MY/NFS/myrootfs 来讲编译好的模块安装到文件系统中,这个INSTALL_MOD_PATH=后面写的文件系统的目录,就是要安装模块到文件系统里,这个目录根据情况来, 如果是要安装到TF卡里的文件系统,就把TF卡插在ubuntu上,这个目录就是/media/MY/roofs
然后用insmod指令加载驱动,执行 ./ledApp /dev/newchrled 1可以发现灯被点亮了,实验成功。
如果要用modprobe来加载模块驱动的话,记得先执行depmod指令,并且ledApp 文件和newchrled.ko文件最好拷贝到/lib/modules/4.1.15下进行测试。
PS:我们将驱动编译进内核的时候,有时候需要配置设备树,有时候不需要配置设备树,什么时候要配置设备树什么时候不需要就根据对应的驱动来,其实也就是多方面考虑的, 比如我这个实验里有呼吸灯占用了这个GPIO1 IO3,所以我要在设备树下将影响到我测试的节点注释掉,又或者教程 第五十九章 Linux LCD 驱动实验 中也是将LCD驱动编译进了内核, 这个是也是要配置设备树的,设备树里添加了节点和IOMUX,节点里有LCD相关的属性信息等等。对于设备树的理解,大家可以看看左工教程里有关于设备树的文档和视频。
在调试某个驱动的时候,我一般是先以编译成模块的方式来调试,当调试好了以后,如果想把这个驱动编译进内核的话再编译进内核,而不是一上来就直接用编译进内核的方式来调试, 毕竟编译进内核的话编译内核时间比较长,编译成模块的话时间比较快,大大节约了时间。
|