自己编译内核驱动,比较麻烦,而且需要重新烧写系统,不是很方便。所以讨论一下通过模块的方式加载驱动。但在编译驱动模块前,需要将ARM的内核源码在Ubuntu上先编译一遍,这样才能在Ubuntu顺利make并生成.ko文件。
编译环境说明:
- 主机操作系统:Ubuntu16.04
- 编译的linux(ARM)内核:linux-2.6.38
- 嵌入式系统:tiny6410
一、Ubuntu上编译ARM内核源码:(先make distclean清理一下)
1、推荐使用make menuconfig ARCH=arm进行配置,因为菜单化配置比较直观。但是我们也无需全部是手动配置,直接拷贝光盘自带的config文件进行编译。如下图所示:
2、在内核代码文件夹内,将上述config_mini6410_a70复制一份,取名.config(这个是隐藏文件):cp config_mini6410_a70 .config。有了这个.config配置文件以后,再重新make menuconfig ARCH=arm,自己也可以根据需要修改,然后保存退出。
3、编译内核:make uImage ARCH=arm CROSS_COMPILE=arm-linux-
在编译过程中可能会出现错误:
(1) Can't use 'defined(@array)' (Maybe youshould just omit the defined()?) at kernel/timeconst.pl line…
解决办法:http://www.linuxdiyf.com/linux/24289.html
其实,提示的错误信息已经明确告诉你了,你应该省略defined()。这里,我们打开vim kernel/timeconst.pl。
@val =@{$canned_values{$hz}};
if(defined(@val)) {
@val =compute_values($hz);
}
output($hz,@val);
将if (defined(@val))改为if (@val),再次编译就可以通过了
(2) "mkiamge"command not found -U-Boot iamges will not be built…
解决办法:
这是系统中没有安装mkimage工具。如果Ubuntu用户可以 apt-get install xxx(具体名字可以上网查),安装好后再重新编译就行了。这里我直接提供一个mkimage可执行文件,将它以root复制到/bin/目录下,再重新编译内核即可。成功之后,会看见提示:Image arch/arm/boot/uImage is ready.
mkimage可执行文件:http://download.youkuaiyun.com/detail/capture_bbom/9825953
二、Ubuntu上编译内核模块:驱动mem_drv.c这里不做介绍,但讲下Makefile
1、在Ubuntu上使用make编译驱动模块,这里解释一下与make配套的Makefile。
obj-m :=mem_drv.o
KDIR :=/home/share_6410/Files/app_exe_test/linux-2.6.38
all:
make -C $(KDIR) M=$(PWD) modulesCROSS_COMPILE=arm-linux- ARCH=arm
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers*.bak *.order
——————————————————————————————————
(1):生成的驱动源文件名,即把.c改为.o就行了
(2):变量KIDR表示:Ubuntu中所放的与你开发板上使用的linux(ARM)版本相同的linux(ARM)内核源码的位置。这里的ARM内核源码,是需要经过第一步编译后的源码。
2、上述1步完成后,再将生成的mem_drv.ko模块拷贝到Tiny6410的目录下。并在ARM中安装该模块:insmodmem_drv.ko
在安装模块过程中可能会出现错误:
(1)insmod: can'tinsert 'xx.ko': invalid module format
解决办法:http://blog.youkuaiyun.com/zengxianyang/article/details/50505998
这可能是正运行在Tiny6410上的内核配置,与我们上述第一步中在Ubuntu编译时的配置不一致造成。我的解决办法是:将第一步中在Ubuntu生成的映像安装到Tiny6410中(替换Tiny6410的内核)让两者使用相同的内核映像(自然两者内核也就是一样的配置了)。Ubuntu生成的映像是linux-2.6.38/arch/arm/boot/zImage。
3、insmod成功之后,在ARM上可以用lsmod查看该模块。再用cat /proc/devices查看我们在mem_drv.c中自动创建的主设备号。
mem_drv.c中创建设备号语句:alloc_chrdev_region(&dev_n, 0, 2, “first_mem_dev”);
Tiny6410上cat查看到的设备号情况:253 first_mem_dev
4、ARM上创建字符设备文件:mknod /dev/first_dev c 253 0。为了在应用程序中通过系统调用来使用到底层的驱动程序,我们必须设置中间的字符设备文件主设备号与底层自动分配的驱动设备号一致,即253。这里的/dev/first_dev就是应用程序中open的文件名,c是字符设备简称。三者关系如下:
应用程序open使用:fd=open(“/dev/first_dev”, O_RDWR|O_CREAT, 666);
5、在Ubuntu上用arm-linux-gcc交叉编译应用程序(如果编译提示找不到库,可使用静态编译),之后再将可执行文件复制到Tiny6410上运行即可。
6、如果要卸载模块,则使用:rmmod mem_drv。这里的mem_drv就是你用lsmod查看到的模块名称。注意后面不加.ko了,否则会卸载失败。一般我们可以在init和exit里面printk一些提示信息,方便自己查看是否加载和卸载成功。卸载后,可以再用lsmod查看之前的驱动模块是否还在。
在卸载模块之后,用lsmod应该找不到mem_drv模块了,这样才能再次insmod(如果没有卸载成功的话,是不能再次insmod的)。但是rmmod后,可能报错:
rmmod: module 'mem_drv' not found
解决办法:http://www.cnblogs.com/feisky/archive/2010/05/29/1746888.html
即使报错,我的mem_drv也是卸载掉了,因为用lsmod找不到mem_drv模块了。在ubuntu上使用如下源码生成rmmod命令,就可以没有任何提示的卸载该模块了#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
const char *modname = argv[1];
int ret = -1;
int maxtry = 10;
while (maxtry-- > 0) {
ret = delete_module(modname, O_NONBLOCK | O_EXCL);//系统调用sys_delete_module
if (ret < 0 && errno == EAGAIN)
usleep(500000);
else
break;
}
if (ret != 0)
printf("Unable to unload driver module \"%s\": %s\n",
modname, strerror(errno));
}
之后在Ubuntu上编译 rmmod.c生成rmmod,并把rmmod复制到tiny6410的/sbin目录下(复制之前,把原本的rmmod备份改名:mv rmmod rmmod.bak)arm-linux-gcc -static -o rmmod rmmod.c
arm-linux-strip -s rmmod
如果在加载时,还遇到其他问题,可参考下面文章:
参考资料:http://www.arm9home.net/read.php?tid-3328-fpage-0-toread--page-1.html
参考资料:http://www.linuxidc.com/Linux/2013-12/93634.htm