开发平台:
fedora10.0,内核版本2.6.30,
交叉编译器:天嵌公司提供的arm-linux-gcc 4.3.3,
内核树:/opt/EmbedSky/linux-2.6.30.4/
目标平台:
天嵌公司的TQ2440开发板ARM9,内核版本2.6.30.4
1. 首先解压天嵌公司提供的内核源码包linux-2.6.30.4_20100531.tar.bz2,
- tar xvfj linux-2.6.30.4_20100531.tar.bz2 –C /
-C是指定解压目录,后面的/代表在根目录解压归档文件。
之后内核树会被解压到/opt/EmbedSky/linux-2.6.30.4/
编译内核:
- cd /opt/EmbedSky/linux-2.6.30.4
- cp config_EmbedSky_W35 .config
- make zImage
在此,config_EmbdeSky_W35为天嵌公司提供的内核配置文件,开始时可暂时拿来用。(PS:配置单要根据自己的情况选择)
至此,
内核树已经准备好了,这是编译内核驱动模块的前提工作,如果在自己的文件系统中没有准备好内核树,则无法构造可装载的模块。
2. 安装交叉编译器4.3.3
解压天嵌公司提供的EABI-4.3.3_EmbedSky_20100610.tar.bz2,
- tar xvfj EABI-4.3.3_EmbedSky_20100610.tar.bz2 –C /
之后交叉编译器会被解压到/opt/EmbedSky/4.3.3/,
修改环境变量配置文件/etc/profile
pathmunge /opt/EmbedSky/4.3.3/bin
或
使用命令
- export PATH=$PATH:/opt/EmbedSky/4.3.3/bin
保存重启即生效,若不想重启可使用
但是此命令只对该终端生效,所以若没其他问题,推荐重启
3.编写hello world 驱动模块
- cd /home/shanks/modules
- mkdir hello
- cd hello
驱动模块不一定要放在内核树里,只要你的Makefile指定了内核树的路径就没问题,所以我这里在/home/shanks/里建立了一个modules目录专门用来开发驱动模块
hello.c 源码:
- #include <linux/init.h>
- #include <linux/module.h>
- MODULE_LICENSE("Dual BSD/GPL");
-
- static int hello_init(void)
- {
- printk(KERN_ALERT "Hello, world\n");
- return 0;
- }
-
- static void hello_exit(void)
- {
- printk(KERN_ALERT "Goodbye, cruel world\n");
- return 0;
- }
-
- module_init(hello_init);
- module_exit(hello_exit);
Makefile:
- KERNELDIR=/opt/EmbedSky/linux-2.6.30.4
- PWD:=$(shell pwd)
- INSTALLDIR=$(PWD)
- CC=arm-linux-gcc
- obj-m := hello.o
- modules:
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
- clean:
- rm -rf *.o *.ko *.mod.c *.markers *.order *.symvers
- .PHONY:modules clean
这里有必要解释一下驱动模块的Makefile编写方法,需要注意的是:(shell pwd ) is
执行pwd这条指令, makefile 要shell pwd
扩展语法,说明有一个模块需要从目标文件hello.o中构造,模块名称为hello.ko
-C是改变make的执行目录,$(KERNELDIR)是你本机的kernel的路径地址,
M=$(PWD) modules这个是使makefile在试图生成模块之前,回到模块源码目录,说的在直接点就是把该makefile同目录下的 modules 给到M,执行make指令。
obj-m :=hello.o 目标文件依赖hell.o
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
上述命令
首先改变目录到-C选项指定位置(即目录源代码目录),其中保存有内核的顶层文件。M=选项让内核顶层的Makefile在构造modules目标之前返回到模块源代码目录。然后,开始第二次读取PWD中的Makefile,modules目标指向obj-m变量中设定的模块,而内核的Makefile负责真正构造模块。
然后,
make
生成了几个文件,其中hello.ko就是我们需要的用来加载的模块文件。
这里注意,make之前必须要先准备好内核树,否则make会出错。
连上开发板,这里我把PC的根目录挂载到板子的/mnt下,
- cp /mnt/home/shanks/modules/hello/hello.ko/tmp
- cd /tmp
好了,加载模块!
[root@EmbedSky /tmp]# insmod hello.ko
Hello, world
[root@EmbedSky /tmp]# rmmod hello
Goodbye, cruel world
完成!
insmod时可能出现的错误:
I.
hello: version magic '2.6.30.4 mod_unload ARMv4 ' should be'2.6.30.4-EmbedSky mod_unload ARMv4 '
insmod: cannot insert 'hello.ko': invalid module format
出错原因:模块版本与内核版本不匹配!
解决办法:导入预设配置信息,进入menuconfig 继续配置General config ->( )Local version -> 括号中加入如下内容:“-EmbedSky”退出保存为 .config ,重新编译内核、驱动模块,再来加载模块
ii.
hello: Unknown symbol__aeabi_unwind_cpp_pr0
insmod: cannot insert 'hello.ko': unknown symbol in module, or unknown
parameter
出错原因:模块和内核镜像不匹配
解决方法:尽量用和你同时用来编译驱动而生成的内核镜像zImage,zImage烧到板子上。Ldd3上有句话:尽管并不是必需的,但最好运行和模块对应的内核。
刚开始学驱动,终于成功加载了hello world 模块!
这是学习驱动的一小步,但是对我来说却是一大步!