EDA365电子论坛网

标题: iTOP-iMX6开发板-设备树驱动-以module的方式编译驱动 [打印本页]

作者: 孤久厌闹    时间: 2020-10-15 11:44
标题: iTOP-iMX6开发板-设备树驱动-以module的方式编译驱动
内核驱动不仅可以将驱动编译到内核中,还可以动态的编译内核驱动。本文档介绍如何以
, L+ s8 d/ O" |  M, j/ M模块的方式编译内核驱动。5 f4 N3 m2 N; H
以 module 的方式编译驱动,需要以下几个部分:9 [) h) y( n; N0 X2 d
1 内核成功编译过;  S; R+ Y- Q2 h1 ?) Z
2 找到内核的 arm 编译器;) g7 J( Y9 h9 s8 Z2 K
3 编译简单驱动;
, q0 M; [6 m9 b* j* v- r! {4 编译简单的 Makefile 文件,Makefile 文件中需要指向内核源码目录(成功编译过的内核源码目录);
8 c0 ~- J5 X* v1 b和文档在一起的有“Makefile”、c 文件和 ko 文件,大家可以用来测试。
2 x/ D# o$ _5 M# ~要动态的编译内核,首先需要将内核源码编译通过,内核的编译请参考使用手册第五章。# p" r' g; E& d& n) ]1 D
1. 内核和编译器路径
. p) t2 p! E  M6 P) ^0 m' g& X本节介绍内核路径、编译器路径。无论是 Qt 和 Ubuntu 的内核源码,都是在 android 源码包中,所以必须先解压 android 源码到 Ubuntu14.04 中。
' K% Q7 w: P0 o; o5 V1 ^如下图所示,作者的 android 源码在“/home/iMX6Q/iTOP-iMX6_android6.0.1”目录下,内核源码在其中的“kernel_imx”目录下。
' ?7 T6 u+ a+ T, T8 E4 G: \5 d  h进入“kernel_imx”目录,查看“build_android_kernel.sh”中的脚本文件,如下图所示。
$ O8 N7 d  B& V. k7 g9 e- c' W2 f5 \3 N3 @# K
如上图所示,我们可以得到一些信息,在后面编译内核模块的时候,需要设置编译目标平台为 arm,“export ARCH=arm”;5 R& k$ N& T# j. P
编译器的路径为“$(pwd)/../prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-”。理论上,应该使用这个编译器,但是实际上以, ~7 c, L) |8 _  y; T& [
modules 的方式编译内核驱动的时候,使用这个编译器,是无法编译的!!& N- T' o! M1 R
应该使用“../prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-”这个编译器才行,如下图所示。
7 k/ `8 S8 Q' y/ G% Y2 _, X' Q2 U. J6 B) X8 @* ~4 b( e$ w* u, S( S
编译器路径为内核源码目录对应的../prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-”,这是作者测试出来的,作者没有太多时间深入研究编译脚本,但是这个编译器是可以的。前面红色部分介绍的编译器,会提示报错,对于这个报错,飞思卡尔官方给出的是简单的回复“你使用了 android 的编译器”,没有提供更多的解释,也没有提示方法,不过作者测试了几个内核驱动,都是可以正常 insmod 和 rmmod 的。
+ K9 Z) |4 c6 `/ J. _  P/ H0 M! L5 U+ `1 ?5 B& u9 k
2. Makefile 和测试驱动源码以及编译; Q" J4 v& V& J+ z! b+ t/ F
作者在“/home/imx6”目录下新建一个“imx_driver_modules”目录,将要编译的驱动和 Makefile 文件放到这个目录下。+ t& c0 p  K7 c3 A

6 N) P: `- ]. h# C. ]2.1 Makefile
( t$ n# y5 e# L, b$ ~7 y1 GMakefile 脚本文件:  @+ |8 C- [. M
obj-m += iTOP_IMX6_treedriver_hello.o# V$ H4 L9 g0 u4 \. x* W6 y
KDIR =/home/iMX6Q/iTOP-iMX6_android6.0.1/kernel_imx: ?$ x  X! r! P& |! L9 o. A
PWD ?= $(shell pwd)
6 }' B" q  F2 @7 t4 y; ^; zall:# e5 C0 k" n' l
make -C $(KDIR) M=$(PWD) modules modules ARCH=arm, n7 V& \+ Z$ e2 D2 H  t' ~
CROSS_COMPILE=$(KDIR)/../prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
# N5 ]1 ]8 q0 z$ Yclean:
! Q  n4 v7 X/ I; R" nrm -rf modules.order *.o workqueue.o Module.symvers *.mod.c *.ko
: j5 t! N" e, D0 b. C脚本中:  s" X7 O  j6 f; B9 [
第一行bj-m += iTOP_IMX6_treedriver_hello.o 表示编译的源文件为iTOP_IMX6_treedriver_hello.c,如果源文件名有变化,则需要修改成对应的。" ?6 V% ^) Q: T. I, \0 n
第二行:KDIR 参数指向对应的内核源码目录。作者的内核源码是在/home/iMX6Q/iTOP-iMX6_android6.0.1/kernel_imxx 目录下,用户要根据自己的具体情况来修改。( m: L/ P+ J% f! H" g
第三行:PWD ?= $(shell pwd)表示将当前目录的路径赋值给 PWD 变量,也就是/home/imx6_tree_driver/iTOP_IMX6_treedriver_hello。作者将会把 Makefile 文件和驱动源码放到这个目录下编译。
1 A; |: t9 ~6 z第五行:其中 make -C $(KDIR) M=$(PWD) modules,表示将当前目录下的文件编译为模块,并且制定了内核源码的路径;: \/ j, X( M+ y% z) G  A2 K
其中 ARCH=arm 表示设置目标 CPU 类别为 arm,也就是编译的依赖内核和驱动模块目标 CPU 为 ARM4 U, B- j& w& O: k+ J
其中 CROSS_COMPILE=$(KDIR)/../prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi- ,这里的路径,指向内核编译器的路径。, ?2 n6 b' |+ e* ^/ K
2.2 简单驱动源码8 F' Y2 I% S. M# z9 k
驱动文件名称为:iTOP_IMX6_treedriver_hello.c,源码如下:& |& x9 O/ G1 o- e+ }3 E2 U

5 r" N9 `& c0 i8 y1 L: E#include4 c5 Q& |# N2 H  o5 a) ]# O. x
#include4 Y2 y4 m) |; o  f( e3 k, S
MODULE_LICENSE("Dual BSD/GPL");6 I" C% {  n# @4 ~% y
MODULE_AUTHOR("iTOPEET_dz");% G% @6 l, }1 t" `% g! @
static int hello_init(void)
- e, v. ?1 k5 w1 F3 ]# ]( K% L+ i{
6 k& d* s3 q+ @+ d6 m' x0 n% Xprintk(KERN_EMERG "Hello World enter!\n");
9 H8 d1 e: u' w0 W  K! Nreturn 0;
8 x7 R: q( ~; C0 _0 X, y+ i}
+ v5 |: n5 m$ |( V; r% f$ xstatic void hello_exit(void)
9 G. [) @& F/ W0 X6 n6 n6 W0 F{' t4 ]9 k2 t5 N1 Q
printk(KERN_EMERG "Hello world exit!\n");
1 x* |% n2 ]2 O7 u8 w1 q/ q}
7 b" `% U% }0 ?8 t! H3 l+ I3 ^module_init(hello_init);* L9 k9 q) I  H% {% L( z6 E
module_exit(hello_exit);
( m5 g- y; |2 k2 S2 B  b( R驱动源码只有基本的入口和出口函数。加载和卸载的时候分别打印“Hello Worldenter!”和“Hello world exit!”。- _& N- y4 U7 ~

' I5 i. U1 _7 W  R6 ^; b2.3 编译
" Q* C$ F. m* _/ s" K将源码和 Makefile 文件拷贝到 Ubuntu14 系统下。8 g* h+ z3 \- J
使用命令“make”,如下图所示,可以看到有“iTOP_IMX6_treedriver_hello.ko”文件生成。0 J8 _0 c6 ?$ }) r
4 }3 t1 E9 v% \3 v/ f, U
& u9 ?' l6 {! r1 r2 ~% w7 K' }
使用命令“make clean”,可以删除中间文件。5 `/ G' \* s: b) Z$ L% X
3.模块编译常见问题/ Z# i* k3 g- U) r3 n) I7 k: k# S  V8 U
在以模块的方式编译驱动的过程中,新手可能会以下问题。* t! P  R6 v. ]3 M
1.内核源码没有编译或者内核源码路径设置不正确。+ I# A3 l  V! T$ @& H# r, X* [
如果内核源码没有编译,那么模块将会提示缺少库之类的错误;如果路径设置不正确,会提示找不到内核。
2 L* l& f+ U- b' H; L" Q1 ~1 R2.源码和 Makefile 文件在 Windows 下编写,然后拷贝到 Ubuntu 上,由于编辑器不同导致转码错误。- l, e1 x* ~  B4 w" \( }" y
这种错误比较容易解决,Make 编译之后,系统会提示 Makefile 或者驱动文件具体某一行出现问题。使用 vim 编辑器打开查看一下,就能找出一些乱码,使用 vim 编辑器修正一下再编译即可。3 V9 {" X" g, O4 N5 f
$ T2 z4 o7 s8 j& Q
4. 模块加载和卸载3 g# h5 o6 o- R  c  A; X" c7 b7 [
作者这里使用最小 linux 系统来测试模块的加载和卸载,最小系统在使用手册第十三章有介绍。在编译模块前,内核源码必须要编译通过,作者这里是在最小系统是加载模块,那么内核源码也必须编译为 qt 的内核(最小系统使用的是 qt 的内核),否则是无法加载的。% y! [) O! j/ [" |2 m
如下图所示,将驱动模块拷贝到开发板(作者采用的是 nfs 共享目录的方式,关于 nfs 大家可以参考群共享中 nfs 相关的文档,设备树和非设备的 Ubuntu 都通用。也可以用 tf 卡或者 U 盘)。
! b4 u' q7 Y  {8 c) y7 _4 z! T$ q
  Z7 R9 J( }" T2 {; W
8 _5 |) S3 b" j: f: o2 M1 Q然后使用命令“insmod iTOP_IMX6_treedriver_hello.ko”加载驱动模块,如下图示,打印出“Hello World enter!”,表明模块驱动加载成功。: V6 e1 [' R! E4 m! c9 A

& S1 @- i5 b+ j# D9 H. u% R& [0 \0 @, f( R
接着使用命令“rmmod iTOP_IMX6_treedriver_hello”卸载模块,如下图所示,发现提示没有目录 4.1.15,这里我们新建“/lib/modules/4.1.15”。; x  M  h3 x8 U  Y+ Z7 v( M8 |
# {$ b; S. a  E# c* h( ?
0 G3 {1 Y8 w( N$ a* G: Z0 k
如下图所示,使用命令“mkdir /lib/modules/4.1.15”新建目录,再次使用命令“rmmod iTOP_IMX6_treedriver_helloello”卸载驱动模块。& o7 k, y4 l$ c1 Y8 q/ i
9 ~; m- f4 B- o. Z2 t
' K- V; q# j! H8 G$ f' e6 j& V% C$ g
发现打印信息“Hello world exit!”,模块卸载成功。
9 x7 _7 b8 ^0 p0 C! g只要重新烧写系统,这些新建目录只需要建立一次即可。
1 ~+ V% U1 F- O: }8 H3 ^0 o3 l- H# ~) V9 G
, t& `7 E. E9 W5 l$ m) `

作者: cichishia    时间: 2020-10-15 13:12
来学习一下




欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/) Powered by Discuz! X3.2