|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
内核驱动不仅可以将驱动编译到内核中,还可以动态的编译内核驱动。本文档介绍如何以
# p1 T) p/ ^6 q9 d' `* e模块的方式编译内核驱动。
$ s: e- R6 d/ E4 |( A! G# N8 b% U. ?以 module 的方式编译驱动,需要以下几个部分:- f. E; Y2 X8 i2 g
1 内核成功编译过;( R: z' g' j. G5 e
2 找到内核的 ARM 编译器;
L" A' v. b: t" }3 编译简单驱动;
* L* ~9 z) b6 ~4 编译简单的 Makefile 文件,Makefile 文件中需要指向内核源码目录(成功编译过的内核源码目录);
* `* W# H6 C6 j5 V; M$ I) g7 P和文档在一起的有“Makefile”、c 文件和 ko 文件,大家可以用来测试。
" R+ N( q+ P/ m% W2 | b要动态的编译内核,首先需要将内核源码编译通过,内核的编译请参考使用手册第五章。
7 K8 D2 w# { G- t- V9 M1. 内核和编译器路径3 D& [" H2 f) J0 o3 ], C) [9 v
本节介绍内核路径、编译器路径。无论是 Qt 和 Ubuntu 的内核源码,都是在 android 源码包中,所以必须先解压 android 源码到 Ubuntu14.04 中。
' k8 {8 V4 k# c如下图所示,作者的 android 源码在“/home/iMX6Q/iTOP-iMX6_android6.0.1”目录下,内核源码在其中的“kernel_imx”目录下。 ; z6 H1 D, m- @; t. B) x
进入“kernel_imx”目录,查看“build_android_kernel.sh”中的脚本文件,如下图所示。3 d3 D( K6 x3 ~' v1 T8 P4 U' A I
![]()
& {( [* `) x# o如上图所示,我们可以得到一些信息,在后面编译内核模块的时候,需要设置编译目标平台为 arm,“export ARCH=arm”;4 V& J9 g& M) \/ D4 J
编译器的路径为“$(pwd)/../prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-”。理论上,应该使用这个编译器,但是实际上以$ q6 k3 A7 ^5 J) k! P/ r
modules 的方式编译内核驱动的时候,使用这个编译器,是无法编译的!!
; V2 t4 F! x8 X* P应该使用“../prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-”这个编译器才行,如下图所示。
! n( c% Y8 D' u$ H) B5 X4 O- F![]()
7 W! O1 S3 o5 C7 n' Y8 [( f编译器路径为内核源码目录对应的../prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-”,这是作者测试出来的,作者没有太多时间深入研究编译脚本,但是这个编译器是可以的。前面红色部分介绍的编译器,会提示报错,对于这个报错,飞思卡尔官方给出的是简单的回复“你使用了 android 的编译器”,没有提供更多的解释,也没有提示方法,不过作者测试了几个内核驱动,都是可以正常 insmod 和 rmmod 的。
9 [9 [9 z+ D9 M% M! m6 i0 r) Y
. h: U/ w9 m- ^5 S4 |/ x f' ?/ b2 f2. Makefile 和测试驱动源码以及编译 b; m. D. _8 u T% W. K F
作者在“/home/imx6”目录下新建一个“imx_driver_modules”目录,将要编译的驱动和 Makefile 文件放到这个目录下。7 q1 N, C" E' ^+ j: f7 d$ Y) _
- t2 i# k( I. J3 _* @! ~" d
2.1 Makefile
2 O3 n2 o- r7 p9 d7 J- E& {/ AMakefile 脚本文件:/ \0 n# {, b8 g: r. g8 n
obj-m += iTOP_IMX6_treedriver_hello.o
. U3 S# s5 H4 @' ~# M' P6 {KDIR =/home/iMX6Q/iTOP-iMX6_android6.0.1/kernel_imx& i. h) {; `1 V( F
PWD ?= $(shell pwd)
- A+ ^2 p: s( T) S& H" A7 `all:# k/ V7 g5 \- N( Q* B1 _
make -C $(KDIR) M=$(PWD) modules modules ARCH=arm
7 ]6 C' k/ z% C; f dCROSS_COMPILE=$(KDIR)/../prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
- J1 c( q+ P1 m8 N9 bclean:
% s, f2 ^* F/ A$ a) Z yrm -RF modules.order *.o workqueue.o Module.symvers *.mod.c *.ko
+ z6 V: k2 C1 t脚本中:2 @0 w' M$ r3 m0 E H3 o6 m
第一行 bj-m += iTOP_IMX6_treedriver_hello.o 表示编译的源文件为iTOP_IMX6_treedriver_hello.c,如果源文件名有变化,则需要修改成对应的。
3 W- H! V+ {1 B- F _0 h" ^第二行:KDIR 参数指向对应的内核源码目录。作者的内核源码是在/home/iMX6Q/iTOP-iMX6_android6.0.1/kernel_imxx 目录下,用户要根据自己的具体情况来修改。
* h! @; m: f% Q7 v3 f. E第三行:PWD ?= $(shell pwd)表示将当前目录的路径赋值给 PWD 变量,也就是/home/imx6_tree_driver/iTOP_IMX6_treedriver_hello。作者将会把 Makefile 文件和驱动源码放到这个目录下编译。& s) B( `. N3 ^9 f3 |5 J Y7 `
第五行:其中 make -C $(KDIR) M=$(PWD) modules,表示将当前目录下的文件编译为模块,并且制定了内核源码的路径;7 m; p9 v- v- ]& M& Z
其中 ARCH=arm 表示设置目标 CPU 类别为 arm,也就是编译的依赖内核和驱动模块目标 CPU 为 ARM;8 ?4 L2 S: {3 T; F
其中 CROSS_COMPILE=$(KDIR)/../prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi- ,这里的路径,指向内核编译器的路径。( W" \& N6 H$ x8 b" B. ~4 R2 X
2.2 简单驱动源码/ t; a/ ^$ {! n5 S( U* i
驱动文件名称为:iTOP_IMX6_treedriver_hello.c,源码如下:
4 _* g) t" s+ L" z+ x3 x* ^3 i
6 \. |2 A- T' f7 B8 y6 X#include m* W1 [& K4 D; K+ g% y
#include& z# h0 _' t& y! P
MODULE_LICENSE("Dual BSD/GPL"); b9 [/ M, Q, @
MODULE_AUTHOR("iTOPEET_dz");! {; g0 M# V# l9 }$ i
static int hello_init(void)8 n: ], X( \9 }. o- o9 ^4 o
{7 z, u7 ]' a) }0 L
printk(KERN_EMERG "Hello World enter!\n");
4 A" k; i) p" Y" T( Y4 ]' Greturn 0;. L7 `' {* D' O9 w+ n5 G2 ?, L
}
! S* K& G) w _/ g) r( ?static void hello_exit(void)
- c1 ?3 a t8 C! U( ]{ p" P6 |- c- q: Z
printk(KERN_EMERG "Hello world exit!\n");- C; N5 [0 y# \8 p
}
1 J6 z% V5 j4 ~4 O# ?4 [) C+ n' T4 {module_init(hello_init);
6 J4 g' G( a( V: k7 e' @( t5 hmodule_exit(hello_exit);
# g* g. S' a* H驱动源码只有基本的入口和出口函数。加载和卸载的时候分别打印“Hello Worldenter!”和“Hello world exit!”。
7 q. F( ?5 u9 D/ M x/ G) h9 w" P* F! F' B4 c
2.3 编译8 S* j% n: M' @- q) l
将源码和 Makefile 文件拷贝到 Ubuntu14 系统下。
& C0 H6 G! n( A5 \8 C* Y使用命令“make”,如下图所示,可以看到有“iTOP_IMX6_treedriver_hello.ko”文件生成。
* ~: |; A' @" {: r
" y; B, u' E1 F# L3 G % B# m) ^8 C1 ^5 X& a
使用命令“make clean”,可以删除中间文件。
1 I g1 \1 |0 \# L7 b# A, D( m3.模块编译常见问题
% `: o. ~1 s' A8 F5 B" y在以模块的方式编译驱动的过程中,新手可能会以下问题。" @1 h4 d' `0 p( ?6 g0 z# u1 m
1.内核源码没有编译或者内核源码路径设置不正确。: S" R* I/ e" P2 Y/ L) x' P
如果内核源码没有编译,那么模块将会提示缺少库之类的错误;如果路径设置不正确,会提示找不到内核。) u- `, q) C) C; l
2.源码和 Makefile 文件在 Windows 下编写,然后拷贝到 Ubuntu 上,由于编辑器不同导致转码错误。0 d: _, x+ K6 f5 g. q8 W
这种错误比较容易解决,Make 编译之后,系统会提示 Makefile 或者驱动文件具体某一行出现问题。使用 vim 编辑器打开查看一下,就能找出一些乱码,使用 vim 编辑器修正一下再编译即可。
* v' U& ~/ [" @/ R" y
8 t0 \. D$ r! C4. 模块加载和卸载
( m* V9 Q1 n' F: J9 A# X4 X作者这里使用最小 linux 系统来测试模块的加载和卸载,最小系统在使用手册第十三章有介绍。在编译模块前,内核源码必须要编译通过,作者这里是在最小系统是加载模块,那么内核源码也必须编译为 qt 的内核(最小系统使用的是 qt 的内核),否则是无法加载的。
{) K; h j7 G i( [+ a& F如下图所示,将驱动模块拷贝到开发板(作者采用的是 nfs 共享目录的方式,关于 nfs 大家可以参考群共享中 nfs 相关的文档,设备树和非设备的 Ubuntu 都通用。也可以用 tf 卡或者 U 盘)。5 v, C/ o7 ^- |: N" v3 a9 Z
5 K5 C. x$ n* e1 C$ H" T
* x8 d2 o- Q$ k; z# h9 X( e! n- l0 @
然后使用命令“insmod iTOP_IMX6_treedriver_hello.ko”加载驱动模块,如下图示,打印出“Hello World enter!”,表明模块驱动加载成功。
- \' y4 Y0 b5 ^
) B! Y! x: |7 S* M: o" O0 r % l9 v% G, n/ M8 [+ u5 z
接着使用命令“rmmod iTOP_IMX6_treedriver_hello”卸载模块,如下图所示,发现提示没有目录 4.1.15,这里我们新建“/lib/modules/4.1.15”。, I$ o P8 ^- N# j# m: |2 ]
5 Q% N% s! q" B( L5 ]. x4 ]
. n. l, Y8 m5 ?) j& I( l% C
如下图所示,使用命令“mkdir /lib/modules/4.1.15”新建目录,再次使用命令“rmmod iTOP_IMX6_treedriver_helloello”卸载驱动模块。0 v* F& t0 z8 S/ W2 F
8 _6 J) T5 l5 j. p 5 g# N( s" V7 w8 {1 {
发现打印信息“Hello world exit!”,模块卸载成功。3 I ~* n. e+ h% k5 Y, d
只要重新烧写系统,这些新建目录只需要建立一次即可。+ ]2 Z! ~( a1 }& C; B' Q5 T
& h/ f2 q4 ^% S7 l7 M
* o: V% l: P/ ?. U
|
|