第一种方法:

把驱动编译成模块,然后使用命令把驱动加载到内核里面

第二种方法:

直接把驱动编译到内核 

编译成模块

第一步:先写一个Makefile

obj-m +=helloworld.o #obj-m表示把驱动编译成模块,生成的中间文件名字为helloworld.o

KDIR:=/home/topeet/topeet/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga #指定内核路径

PWD?=$(shell pwd) #获取当前目录变量

all:
    make -C $(KDIR) M=$(PWD) modules #make进入到内核源码路径因为编译需要这个环境,把当前路径代码编译成模块

第二步:编译驱动

编译驱动之前需要注意的问题:

1、内核源码一定要先编译通过

2、我们编译驱动模块用的内核源码一定要和我们开发板上运行的内核镜像是同一套

3、看一下我们Ubuntu的环境是不是arm。 

打开ubuntu内核源码路径下输入

make menuconfig

android内核驱动如何编译 驱动编译进内核_android内核驱动如何编译

 

 此处查看到ubuntu是x86,需要改成arm

输入

export ARCH=arm

再查看

make menuconfig

android内核驱动如何编译 驱动编译进内核_开发板_02

 此时变为arm。

上述过程也可直接通过下面指令查看

echo $ARCH

对于3的总结:编译的终端窗口要设置ARCH和CROSS_COMPILE

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

 

 然后把helloworld.c拷贝到Makefile路径下执行make即可编译。

helloworld.ko文件就是编译的驱动模块。

接下来在开发板上测试编译好的驱动模块:

在开发板上加载驱动模块在开发板上输入以下命令

insmod helloworld.ko

 在开发板上也可以查看我们加载的模块

lsmod

卸载驱动模块,使用rmmod命令,注意没有ko的后缀

rmmod helloworld

再使用lsmod也可以看到卸载成功。

以上几步现象如下图:

android内核驱动如何编译 驱动编译进内核_加载_03

 

 可以看到现象,加载和卸载模块时打印出了上一小节中的hello world 和byebye语句。

 

编译到内核

Kconfig讲解

Kconfig截取一段代码作为例子

android内核驱动如何编译 驱动编译进内核_android内核驱动如何编译_04

 

 

 1、source "drivers/redled/Kconfig"

  它会包含drivers/redled/路径下的驱动文件,方便我们对菜单进行管理

2、config LED_4412

  配置选项的名称,CONFIG_LED_4412 (大写的CONFIG省略了)

3、tristate表示驱动的状态,把驱动编译成模块,把驱动编译到内核,不编译

  与之对应的还有bool分别是编译到内核,不编译

  "Led Support for GPIO Led" make menuconfig显示的名字

  A depends on B

  表示只有在选择B的时候才可以选择A

4、比如我想直接去掉LED相关的驱动,我们直接改.config文件可以吗?

  可以,但是不推荐。如果有依赖的话,直接修改.config是不成功的。

5、select

  反向依赖,该选项被选中时,后面的定义也会被选中。

6、help

  This option enable support for led

  帮助信息

 

实验:把01小节中helloworld.c驱动编译到内核

以下操作目录都基于内核根目录的相对路径

1、在/drivers/char/路径建立一个hello文件夹

  hello文件夹中需要有Makefile、Kconfig、helloworld.c

  helloworld.c是01小节的驱动。  

#include <linux/init.h>   //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件    

static int hello_init(void)
{
    printk("hello world\n"); 
    return 0;
}


static int hello_exit(void)
{
    printk("bye bye\n"); 
    return 0;
}  

module_init(hello_init);//模块入口
module_exit(hello_exit);//模块出口

MODULE_LICENSE("GPL");

 

  Makefile内容如下

  

android内核驱动如何编译 驱动编译进内核_开发板_05

 

   obj-$(CONFIG_HELLO)表示可以在make menuconfig界面通过空格选择是编译到内核或者编译成模块或者不编译。

  如果选择*就是obj-y,编译到内核,如果选择M就是obj-m,编译成模块。

  CONFIG_HELLO是根据Kconfig中的 第一行config HELLO来的,Kconfig中省略了CONFIG_

  Kconfig内容如下:

  

android内核驱动如何编译 驱动编译进内核_加载_06

 

2、要让hello上一级/drivers/char/Makefile包含这个hello文件夹,以便编译

android内核驱动如何编译 驱动编译进内核_驱动模块_07

 

 此处注意hello是文件夹,所以后面要加"/",否则编译出错。

3、要让上一级文件夹的/drivers/char/Kconfig文件包含hello文件夹的Kconfig

 

android内核驱动如何编译 驱动编译进内核_加载_08

 

 4、到内核根目录执行make menuconfig,将hello world设置为编译到内核,然后保存

android内核驱动如何编译 驱动编译进内核_驱动模块_09

 

 保存后可以查看.config文件,发现CONFIG_HELLO已经配置进去了

android内核驱动如何编译 驱动编译进内核_android内核驱动如何编译_10

 CONFIG_HELLO=y 即刚刚设置的编译到内核,如果选择的是编译成模块则显示 CONFIG_HELLO=m

 5、重新编译内核

   重新编译内核之前可以通过以下命令清除之前编译的所有内容

make distclean

   之后编译内核即可。

   编译后发现arch/arm/boot下面生成了zImage。编译成功。

6、烧录到开发板观察现象

  发现开机启动信息中打印了驱动程序中的hello world,说明成功编译到内核。

android内核驱动如何编译 驱动编译进内核_加载_11