linux 内核驱动开发

一、为什么要学习内核?

有些人要学习内核,而有些人则可以不学习它。你如果以后要从事系统研发或驱动开发的话,就要学习内核。

刚刚接触内核,主要学习内核的接口函数。不要深入的去读内核,因为你读也读不懂,内核代码庞大如野兽一般不可驾驭。

学习内核主要掌握层次学习法,即从头开始学习,一环紧扣一环。

内核学习的四步学习法:1、核心理论学习-概念与函数原型2、范例程序分析3、思维导图设计4、亲自编写代码

二、linux内核简介

1、linux体系结构

分为两部分:用户空间和内核空间

\

2、linux为什么要分为用户空间和内核空间

现代CPU通常实现了不同的工作模式,以ARM为例,实现了7中工作模式。X86实现了4中不同的级别:Ring0-Ring3. Ring0下可以执行特权指令,可以访问IO设备等,在Ring3则有很多限制。linux系统利用CPU的这一特性,使用了其中两个级别分别运行linux内核与应用程序,这样使操作系统本身得到充分的保护。例如:如果使用X86,用户代码运行在Ring3,内核代码运行在Ring0.内核空间与用户空间是程序执行的两种不同状态,通过系统调用和硬件中断能够完成从用户空间到内核空间的转移。

3.linux的内核构架

\

系统调用接口

SCI层为用户空间提供了一套标准的系统调用函数来访问Linux内核,搭起了用户空间到内核空间的桥梁。

进程管理(PM)是创建进程,停止进程,并控制它们之间的通信。进程管理还包括控制活动进程如何共享CPU,即进程调度。

内存管理(MM)的主要作用是控制多个进程安全地共享内存区域。

网络协议栈(Network Stack)为linux提供了丰富的网络协议实现。

虚拟文件系统(VFS)隐藏各个文件系统的具体细节,为文件操作提供统一的接口。

\

设备驱动(DD):linux内核中有大量代码都在设备驱动程序中,它们控制特定的硬件设备。

三、linux内核源代码结构

1、下载源代码地址www.kernel.org

2、linux内核源代码采用树形结构进行组织,非常合理地把功能相关的文件都放在同一个子目录下,使得程序更具可读性。

\

arch目录:arch是architecture的缩写。内核所支持的每种CPU体系,在该目录下都有对应的子目录。每个cpu的子目录,又进一步分解为boot,mm,kernel等子目录,分别包含控制系统引导,内存管理,系统调用等。/* X86英特尔cpu与之相兼容体系结构的子目录:boot 引导程序compressed内核解压缩 tools生成压缩内核映像的程序 kernel相关内核特性实现方式,如信号处理、时钟处理 lib 硬件相关工具函数*/

documentation内核文档

drivers设备驱动文档

include内核所需要的头文件。与平台无关的头文件在include/linux子目录下,与平台有关的头文件则放在相应的子目录中。

fs目录存放各种文件系统的实现代码。每个子目录对应一种文件系统的实现,公用的源程序用于实现虚拟文件系统vfs

"|--devpts是/dev/pts虚拟文件系统

||--ext2是第二扩展文件系统

||--fat是MS的fat32文件系统

||--isofs 是IsO9660光盘cd-rom上的文件系统

net是网络协议的实现代码

||--802        802无线通讯协议核心支持代码

||--appletalk 与苹果系统连网的协议

||--ax25  AX25无线INTERNET协议

||--bridge 桥接设备

||--ipv4 IP协议族V4版32位寻址模式

||--ipv6 IP协议族V6版

四、linux内核的配置和编译

代码是如何转化为烧写或安装到硬件平台中的系统映像文件的?

1、为什么要配置内核

选出需要的,去掉不要的!1、硬件的需求2、软件的需求

下载内核后要在linux中解压缩,不要在windows下解压缩因为windows下不区分大小写,而linux操作系统区分大小写。

进入内核的文件下:

2、内核的配置:

make config:基于文本模式的交互式配置

make menuconfig:基于文本模式的菜单型配置

\

<*>文件经过编译由.c文件到.o文件,最后链接压缩为内核镜像,它存放在内存。

<M>内核模块,同上经过编译后会把.o文件安装到硬盘。

< >表示不选择该功能

配置结果文件是隐藏文件,可以用ls -a 在内核文件下查看.config version

上面介绍的是一种方法,不过作为初学者我们往往是在一个已有的配置文件基础上,通过修改得到新的配置文件,linux内核提供了一系列可供参考的内核配置文件,位于Arch/cpu/configs

接下来,我们利用虚拟机上的linux系统的配置文件来创建自己的升级版内核,并在虚拟机上运行该内核。

图:

3、编译内核(编译内核、编译内核模块、制作ramdisk)

3.1、编译内核

make zImage只能编译小于512K的内核

make bzImage我们一般会使用这种方法编译内核

如需获取详细编译信息,可使用:

make zImage V=1

make bzImage V=1

编译好的内核位于arch/cpu/boot/目录下***

3.2编译内核模块

make modules 编译内核模块

make modules_install 将编译好的内核模块,从内核源代码目录复制到/lib/modules下**,为打包做好准备

经过第一步编译后,散落在各个文件下的.ko文件为内存模块。需要集中移动到/lib/modules这个就由make modules_install来完成

3.3制作init ramdisk

方法:mkinitrd initrd-$version $version

例如:mkinitrd initrd-2.6.32 2.6.32

通过uname -r 获得正在运行的内核版本

*$version可以通过查询/lib/modules下的目录得到

4、安装内核

1、cp arch/x86/boot/bzImage

/boot/vmlinuz-$version

2、cp initrd-$version /boot/

3、修改/etc/grub.conf的后四行

5、清理内核

make clean 清理编译内核生产的.o文件

make distclean 清理编译内核生产的.o文件和.config $version

记忆几个命令

rpm -qa | grep kernel 找到内核包信息

rpm -e kernel-内核包名

 

linux设备驱动归纳总结(一):内核的相关基础概念

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


.linux设备驱动的作用

内核:用于管理软硬件资源,并提供运行环境。如分配4G虚拟空间等。

linux设备驱动:是连接硬件和内核之间的桥梁。

linux系统按个人理解可按下划分:

应用层:包括POSIX接口,LIBC,图形库等,用于给用户提供访问内核的接口。属于用户态,ARM运行在用户模式(usr)者系统模式(sys)下。

内核层:应用程序调用相关接口后,会通过系统调用,执行SWI令切换ARM的工作模式到超级用户(svc)模式下,根据用户函数的要求执行相应的操作。

硬件层:硬件设备,当用户需要操作硬件时,内核会根据驱动接口操作硬件设备

图结构如下:

举一个相对比较邪恶的类比:

在深圳的酒店经常会在门缝看到一些卡片,上面说可以通过打电话送货上门提供某中服务。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


.内核代码树介绍

linux-2.6.29

|-arch        :包含和硬件体系结构相关的代码

|-block       :硬盘调度算法,不是驱动

|-firmware    :固件,如BOIS

|-Documentation:标准官方文档

|-dirver      : linux设备驱动

|-fs          :内核所支持的文件体系

|-include    :头文件。linux/module.h linux/init.h常用库。

|-init       :库文件代码,C库函数在内核中的实现。

init/main.c->start_kernel->内核执行第一条代码

|-ipc         :进程件通信

|-mm         :内存管理

|-kernel      :内核核心部分,包括进程调度等

|-net        :网络协议

|-sound       :所有音频相关

其中,跟设备驱动有关并且经常查阅的文件夹有:

init

include : linux, asm-arm

drivers:

arch:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


.内核补丁:

补丁一般都是基于某个版本内核生成的,用于升级旧内核。

打补丁需要注意:

1.对应版本的补丁只能用于对应版本的内核。

2.如果在已打补丁的内核再打补丁,需要先卸载原来补丁。

打补丁的方法:

1.制作补丁:

diff-Nur linux-2.6.30/ linux-2.6.30.1/ > linux-2.6.30.1.patch

//N为新加的文件全部修改

//linux-2.6.30旧版本

//linux-2.6.30.1新版本

//目标补丁

2.打补丁:

cd linux-2.6.30 //!!注意在原文件夹的目录中打补丁

patch-p1 < ../linux-2.6.30.1.patch //-p1是忽略一级目录

3.恢复:

cd linux-2.6.30             //!!注意在原文件夹的目录中打补丁

patch-R < ../linux-2.6.30.1.patch //撤销补丁


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


.内核中的Makefile

对于内核,Makefile分为5类:

Documentation/kbuild/makefiles.txt描述如下:

50 The Makefiles have five parts:

51

52     Makefile                         Makefile,控制内核的编译

53     .config                            内核配置文件,配置内核时生成,make menuconfig

54     arch/$(ARCH)/Makefile 对应体系结构的Makefile

55     scripts/Makefile.*            Makefile共用的规则

56     kbuild Makefiles             各子目录下的Makefile,被上层的Makefile调用。

简单来说,编译内核会执行以下两步骤,它们分别干了以下的事情。

1一般的,我们会拷贝一个对应体系结构的配置文件到主目录下并改名为.config,这样就在make menuconfig生成的图形配置中已经有了一些默认的配置,减少用户的劳动量。不过这一步不做也没关系的。

2.make menuconfig

2.1、由总Makefile决定编译的体系结构(ARCH).编译工具(CROSS_COMPILE),并知道需要进去哪些内核根下的哪些目录进行编译。

2.2、由arch/$(ARCH)/Makefile,决定arch/$(ARCH)下还有的哪些目录和文件需要编译。

2.3、知道了需要编译的目录后,递归的进入哪些目录下,读取每一个Kconfig的信息,生成了图形配置的界面。

2.4、通过我们在图形配置界面中选项为[*][M]或者[]

2.5、保存并退出配置,会根据配置生成一份新的配置文件.config,并在同时生成include/config/auto.conf(这是.config的去注释版)。文件里面保存着CONFIG_XXXX等变量应该取y还是取m

3.make

3.1、根据Makefile包含的目录和配置文件的要求,进去个子目录进行编译,最后会在各子目录下生成一个.o或者.a文件,然后总Makefile指定的连接脚本arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通过压缩编程bzImage,或者按要求在对应的子目录下编译成模块。。

但是,具体是怎么生成配置文件的呢?

注:我使用的内核是2.6.29

1.在总Makefile中,根据以下语句进入需要编译的目录

470 # Objects we will link into vmlinux / subdirs we need to visit

471 init-y          := init/

472 drivers-y       := drivers/ sound/ firmware/

473 net-y           := net/

474 libs-y          := lib/

475 core-y          := usr/

476 endif # KBUILD_EXTMOD

639 core-y          += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

上面说明了,根目录下的initdriversoundfirmwarenetlibusr等目录,在编译时都会进去读取目录下的Makefile并进行编译。

2.在总Makefile中包含的目录还是不够的,内核还需要根据对应的CPU体系架构,

决定还需要将哪些子目录将要编译进内核。在总Makefile中有一个语句:

529 include $(srctree)/arch/$(SRCARCH)/Makefile      //在这里,我定义SRCARCH = arm

可以看出,在总Makefile中进去读取相应体系结构的Makefile->arch/$(SRCARCH)/Makefile

arch/$(SRCARCH)/Makefile中指定arch/$(SRCARCH)路径下的哪些子目录需要被编译。

arch/arm/Makefile下:

95 head-y          := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o

187 # If we have a machine-specific directory, then include it in the build.

188 core-y                          += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/

189 core-y                          += $(machdirs) $(platdirs)

190 core-$(CONFIG_FPE_NWFPE)        += arch/arm/nwfpe/

191 core-$(CONFIG_FPE_FASTFPE)      += $(FASTFPE_OBJ)

192 core-$(CONFIG_VFP)              += arch/arm/vfp/

193

194 drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/

195

196 libs-y                          := arch/arm/lib/ $(libs-y)

上面看到,指定需要进入arch/arm/kernel/arch/arm/mm/arch/arm/common/等目录编译,至于core-ycore-$(CONFIG_FPE_NWFPE)这些是什么东西呢?


其中,y表示编译成模块,m表示编译进内核(上面没有,因为默认情况下ARM全部编译进内核),但$(CONFIG_OPROFILE)又是什么呢?这些是根据用户在make menuconfig中设置后,生成的值赋给了CONFIG_OPROFILE


3.make menuconfig后的配置信息是怎么来的?

这是由各子目录下的Kconfig提供选项功用户选择并配置。

arch/arm/Kconfig所有的配置都是根据arch/$(ARCH)/Kconfig文件通过Kconfig的语法source读取各个包含的子目录Kconfig来生成一个配置界面。每个Makefile目录下都有一个对应的Kconfig文件,用于生成配置界面来给用户决定内核如何配置,配置后会确定一个。CONFIG_XXX的的值(如上面的CONFIG_OPROFILE),来决定编译进内核,还是编译成模块或者不编译。

如在arch/arm/Kconfig下:

595 source "arch/arm/mach-clps711x/Kconfig"

596

597 source "arch/arm/mach-ep93xx/Kconfig"

598

599 source "arch/arm/mach-footbridge/Kconfig"

600

601 source "arch/arm/mach-integrator/Kconfig"

602

603 source "arch/arm/mach-iop32x/Kconfig"

604

605 source "arch/arm/mach-iop33x/Kconfig"

这些就是用来指定,需要读取以下目录下的Kconfig文件来生成一个使用make menuconfig时的配置界面。

至于子目录下的Kconfig是怎么样的,待会介绍。

总结Kconfig的作用:

3.1.make menuconfig下可以配置选项;

3.2..config中确定CONFIG_XXX的的值。


4.只是读取以上的两个Makefile还是不够了,内核还会把包含的子目录一层一层的读取它里面的MakefileKconfig


上面啰啰嗦嗦地讲了这么久,无非就是想说,内核的编译并不是一个Makefile搞定的,需要通过根目录下的总Makefile来包含一下子Makefile(不管是根目录下的子目录还是/arch/arm中的子目录)。而Kconfig,为用户提供一个交互界面来选择如何配置并生成配置选项。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


五、子目录下的MakefileKconfig


上面我一直介绍的都是两个比较大的Makefile——Makefilearch/$(ARCH)/Makefile。接下来看一下实例。


一、makefile中,y表示编译进内核,m表示编译成模块,不写代表不编译。所以,配置最简单的方法就是,直接修改子目录的Makefile

先看看arch/arm/Makefile

/*arch/arm/mach-s3c2440/Makefile*/

12 obj-$(CONFIG_CPU_S3C2440)   += s3c2440.o dsc.o

13 obj-$(CONFIG_CPU_S3C2440)   += irq.o

14 obj-$(CONFIG_CPU_S3C2440)   += clock.o //配置2440的时钟进入模块

15 obj-$(CONFIG_S3C2440_DMA)   += dma.o

如果我要取消s3c2440的时钟(当然这是必须要开的,只是举例)可以直接修改arch/arm/mach-s3c2440/Makefileobj-$(CONFIG_CPU_S3C2440)   += clock.o改为

obj-   += clock.o

如果你想编译成模块也可以修改成:

obj-m += clock.o

在这里CONFIG_CPU_S3C2440的值默认是y,所以内核是要将时钟编译进内核的。也许有人会问,那我直接修改CONFIG_CPU_S3C2440的值为m不就可以将时钟编译成模块了,何必修改Makefile这么麻烦呢?的确是这样,只要我们通过在”make menuconfig”的界面中配置后就能够改变CONFIG_CPU_S3C2440的值。接下来看看如何实现。


二、在一般的编译内核时,我们都是通过”make menuconfig”进入图形界面面配置的,接下来我实现一下如何将一个选项加入到图形配置界面中。

看看具体实现的步骤:

以下的执行环境是在PC机上,我使用的内核是linux-2.6.29

2.1.进入内核目录

cd linux-2.6.29

2.2. driver目录下模拟一个名为test1驱动的文件夹

mkdir driver/test1

2.3.在目录下随便些一个C文件,只要不报错。

vim test1.c             

我的test1.c如下:

1 void foo()

2 {

3         ;

4 }

2.4vim Makefile                                        //在目录下编写一个简单的Makefile

Makefile文件编写如下:

obj-$(CONFIG_TEST1) += test1.o

CONFIG_TEST1是决定test1是否编译进内核或者编译成模块的。这就是通过同一目录下的Kconfig来在配置界面中生成选项,由用户在make menuconfig中选择。

2.5所以还要同一目录下写一个Kconfig

vim Kconfig                                       

Kconfig修改如下:

menu "test1 driver here"                      //这是在图形配置显示的

config TEST1

bool "xiaobai test1 driver"                   //这同样也是在图形配置显示的

help

This is test1                                          //这个也是在图形配置显示的。

说白了,就是在图形配置的driver下多了一个配置选项,用户配置后将CONFIG_TEST1的值存放在.config中,Makefile通过读取.config的去注释版include/config/auto.conf读取到CONFIG_TEST的值,再进行编译。


但是,以上几步还不能达到目的,因为虽然在总Makefile中已经包含了目录driver,但是driver目录的Makefile中并没有包含test目录。因此需要在driver/Makefile中添加:

103 obj-$(CONFIG_PPC_PS3)       += ps3/

104 obj-$(CONFIG_OF)        += of/

105 obj-$(CONFIG_SSB)       += ssb/

106 obj-$(CONFIG_VIRTIO)        += virtio/

107 obj-$(CONFIG_STAGING)       += staging/

108 obj-y               += platform/

109 obj-$(CONFIG_TEST1)     += test1/ //这是我添加的

虽然Makefile中已经包含了,但这样还是不行。因为当需要配置ARM时,ARM结构下的Kconfig并没有包含testKconfig这样的话就不会出现在图形配置界面中,因此在arch/arm/Kconfig中添加:

1230 menu "Device Drivers" //要在Device Drivers这个选项里面添加

1231

1232 source "drivers/base/Kconfig"

1233

1234 source "drivers/connector/Kconfig"

。。。。。。。。

1330 source "drivers/test/Kconfig" //这是我添加的

1331

1332 endmenu

大功告成!

这样,make menuconfig界面写的Driver Devices下就多了一个"test1 friver here"的目录,里面有一个配置选项"xiaobai test1 driver"

Kconfig文件的语法在documentation/kbuild/kconfig-language.txt文件中有详细的讲解,上面我只是简单实现了一下,都是皮毛。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


.内核和模块的编译

编译内核很简单,只需要配置完毕后执行make命令,将指定的文件编译进内核

bzImage或者编译成模块。

make = make bzImage + make modules

因此如果值编译内核,即只编译配置文件中-y选项,可以直接用命令

make bzImage

如果值编译模块,即只编译配置文件中的-m选项,可以之直接使用命令

make modules

模块可以编译当然也可以清除,使用命令

make modules clean

如果只想单独编译一个模块,可以使用命令

make M=drivers/test/ modules         //只单独编译drivers/test中的.ko

make M=drivers/test/ modules clean //清除

上面的是在内核目录下的操作,但当我写驱动时,我并不可能在内核目录下编

写,但我编译时却要依赖内核中的规则和Makefile,所以就有了以下的方法,

同时这也是一般的编写驱动时Makefile的格式。

指定内核Makefile并单独编译

make-C /root/linux-2.6.29 M=`pwd` module

make-C /root/linux-2.6.29 M=`pwd` module clean

//-C指定内核Makefile的路径,可以使用相对路径。

//-M指定要编译的文件的路径,同样课使用相对路径。

编译生成的模块可以指定存放的目录

make-C /root/linux-2.6.29 M=`pwd` modules_install INSTALL_MOD_PATH=/nfsroot  


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


七、总结


说了这么久估计都说糊涂了,其实我只是想表达一下内核编译时大体上究竟是怎么样的一个过程。

我以编译S3C2440的内核为例再说一遍:

1一般我们会想将一份S3C2440的默认配置拷贝到内核跟目录下并改名为.config

2.make menuconfig

2.1、由总Makefile决定编译的体系结构(ARCH).编译工具(CROSS_COMPILE),并知道需要进去哪些内核根下的哪些目录进行编译。

2.2、由arch/$(ARCH)/Makefile,决定arch/$(ARCH)下还有的哪些目录和文件需要编译。

2.3、知道了需要编译的目录后,递归的进入哪些目录下,读取每一个Kconfig的信息,生成了图形配置的界面。

2.4、通过我们在图形配置界面中选项为[*][M]或者[]

2.5、保存并退出配置,会根据配置生成一份新的配置文件.config,并在同时生成include/config/auto.conf(这是.config的去注释版)。文件里面保存着CONFIG_XXXX等变量应该取y还是取m

3.make

3.1、根据Makefile包含的目录和配置文件的要求,进去个子目录进行编译,最后会在各子目录下生成一个.o或者.a文件,然后总Makefile指定的连接脚本arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通过压缩编程bzImage,或者按要求在对应的子目录下编译成模块

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

 

.概述:

linux内核中增加程序需要完成以下三项工作:

1.将编写的源代码复制到Linux内核源代码的相应目录

2.在目录的Kconfig文件中增加新源代码对应项目的编译配置选项

3.在目录的Makefile文件中增加对新源代码的编译条目

.实例

1.先把驱动代码usbtmc(文件夹)赋值到/usr/src/linux-headers-2.6.32-31-generic/drivers/char

首先你要清楚你的模块应在内核源代码树中处于何处。

1>设备驱动程序存放在内核源码树根目录drivers/的子目录下,在其内部,设备驱动文件进一步按照类别,类型等有序地组织起来。

a.字符设备存在于drivers/char/目录下

b.块设备存放在drivers/block/目录下

c.USB设备则存放在drivers/usb/目录下。

注意:

(1)此处的文件组织规则并非绝对不变,例如:USB设备也属于字符设备,也可以存放在drivers/usb/目录下。

(2)例如我们把驱动程序usbtmc存放在drivers/char/目录下,那么你要注意,在该目录下同时会存在大量的C源代码文件和许多其他目录。所有对于仅仅只有一两个源文件的设备驱动程序,可以直接存放在该目录下,但如果驱动程序包含许多源文件和其他辅助文件,那么可以创建一个新子目录。

此处,我们是把usbtmc目录放在了drivers/char目录下面

2.修改char目录下的KconfigMakefile

(1)修改Kconfig

sudogedit Kconfig

添加下面一句后

source"drivers/char/usbtmc/Kconfig"

它表示将usbtmc目录下的Kconfig挂载到char目录下的Kconfig里面(为了使本层的Kconfig文件能起作用,我们需要修改父目录的Kconfig文件,加入source语句)

1>对驱动程序而言,Kconfig通常和源代码处于同一目录。

2>如果你建立了一个新字目录,而且也希望Kconfig文件存在于该目录中的话,那么就必须在一个已存在的Kconfig文件中将它引入,需要用上面的

语句将其挂接在drivers/char目录中的Kconfig中。

(2)修改Makefile

添加一句话:

obj-$(CONFIG_USBTMC)+=usbtmc/

这行编译指令告诉模块构建系统在编译模块时需要进入usbtmc/子目录中。此时的驱动程序的编译取决于一个特殊配置CONFIG_USBTMC配置选项。

3.现在在我们自己些驱动程序文件夹中添加KconfigMakefile

(1)修改Kconfig

新建一个Kconfig添加下面的话

menu      "USBTMC"

comment"USBTMC Driver"

config     USBTMC

tristate   "USBTMC"

defaultn

help

Ifyou say Y here,support for the usbtmc with computer interface will becompiled into  he kernel and accessible via device node. You can also say M here and the driver will be built as a module named usbtmc.ko.

Ifunsure,say N.

endmenu

endmenu

正确配置好后,我们在源码下执行sudomake menuconfig后,在出现的LinuxKernel Configuration图形界面中选择DeviceDrivers下的characterdevcie,将会看到新加的USBTCM菜单,

(2)修改Makefile

新建一个Makefile,添加下面的话

obj-$(CONFIG_USBTMC)+=usbtmc.o

此时,构建系统运行就将会进入usbtmc/目录下,并且将usbtmc.c编译为usbtmc.ko模块

注:

如果驱动程序源文件可能不只有一个,可以把Makefile做如下修改:

obj-$(CONFIG_USBTMC)+=usbtmc.o

usbtmc-objs:=usbtmc-main.o  usbtmc-usb1.o

此时,usbtmc-main.cusbtmc-usb1.c就一起被编译和连接到了usbtmc.ko某块中。

4.现在已经Ok了,现在我们可以进入linux内核目录下通过menuconfig可以找到我们的USBTMC选项(在Device_Drivers下的characterdevices里可以找到USBTMC)对其进行选定。然后退出,编译内核,就搞定了。

5.删除:

删除也很简单,首先在drivers/char目录下删掉自己的驱动文件夹。其次再删除MakefileKconfig之前添加的东西,就搞定了

.详解:

MakefileKconfig和配置工具组成了Linux2.6内核的配置系统。
其中Makefile定义了Linux内核的编译规则,它是大型项目开发的产物。Linux环境下的大型项目开发中,系统被分为很多模块,而这些模块一般会经历几次修改,而在修改后的编译过程中,由于某些文件中存在依赖关系,人工编译效率低(有些文件不需要重新编译)且易出错,Makefile文件便应运而生。Makefile文件定义了模块间的依赖关系,指定文件的编译顺序,以及编译所使用的命令。它和make命令使得项目的源程序文件可以自动编译,提高了软件开发效率。到此,再谈一下make,它是用来维护程序模块关系和生成可执行程序的工具,它可以根据程序模块的修改情况重新编译链接生成中间代码或最终的可执行程序,省去那些重复的不必要的编译工作,提高编译效率。
Kconfig
给用户提供配置选择的功能。通常配置内核会有四种方法,makeconfig(字符界面配置),makemenuconfig(菜单界面配置),makexconfig(依赖QT),makegconfig(依赖GTK+)。makeconfig比较适合专业人员,像初学者比较适合makemenuconfig,让我们重点关注一下它。当我们运行makemenuconfig时,配置工具会首先分析与体系结构相对应的/arch/xxx/Kconfig文件(xxx为传入的arch参数),它里面包含了除一些与体系结构相关的配置项和配置菜单外,还通过source语句引入了一系列Kconfig,配置工具依据这些Kconfig包含的菜单和项目就可以描绘出一个分层结构。
例如当我们运行makezImaginemakebzImagine等生成映像的命令时,会先检索顶层的Makefie(在arch/xxx/目录下的Makefile为顶层Makefile补充体系结构相关的信息),顶层Makefile的两个主要任务是:产生内核映像文件和内核模块。接着顶层Makefile会去递归地进入内核的各个子目录,然后分别调用子目录中的Makefile(这些Makefile记录编译目标),而进入哪些子目录取决于内核的配置。
当使用makemenuconfigmakeconfig命令时,生成的.config会在源码目录下记录哪些部分被编译入内核,哪些部分被编译为内核模块。简而言之,它是保存内核配置结果的文件。当我们装上Linux系统时,第一次查看源码下的所有文件,会发现没有.config文件,那是因为从来没配置过内核。当你运行makemenuconfig保存并退出时,再次查看就有这个文件了。
配置工具,包括配置命令解释器(对配置脚本中使用的命令进行解释)和配置用户界面(提供字符界面和图形界面),配置工具都是用脚本语言编写的。

1.在进入menuconfig配置界面时,会发现每个配置项目为布尔型(要么编译入内核,要么不编译,选项为“Y”或“N”),菜单上为配置选项的名字例如:“XXXDriver”,help后面的内容为帮助信息。

1>除了布尔型的配置项目外,还存在一种三态型(tristate)配置选项,它意味着要么编译入内核,要么编译为内核模块,要么不编译,选项为“Y”,“M”或“N”

eg: obj-$(CONFIG_USBTMC) +=usbtmc.o

上面的脚本含义是:如果USBTMC选项被选择为“Y”或“M”,即obj-$(CONFIG_USBTMC)就等同于obj-yobj-m时,则编译usbtmc.c,选Y的情况直接会将生成的目标代码直接连接到内核,为“M”的情况则会生成模块usbtmc.ko,如果USBTMC配置选项被选择为“N”,即obj-$(CONFIG_USBTMC)等同于obj-n时,则不编译usbtmc.c

2.Makefile

对内核源代码各级子目录中的kbuildMakefile进行介绍,

(1)目标定义

目标定义用来定义哪些内容要作为模块编译,哪些要编译并连接进内核

(a)obj-y:=foo.o

表示要由foo.c或者foo.s文件编译得到foo.o并连接进内核,而obj-m则表示该文件要作为模块编译。处了y,m以外的obj-x形式的目标都不会被编译。

b)我们最常用的的做法是根据.config文件的CONFIG_变量来决定文件的编译方式:

eg:

obj-$(CONFIG_ISDN)+=isdn.o

(c)多个文件模块的定义

如果一个模块由多个文件组成,这时候应采用模块名加-objs后缀或者-y后缀的形式来定义模块的组成文件。

如:

obj-$(CONFIG_EXT2_FS)+=ext2.o

ext2-y:=balloc.o  bitmap.o

模块的名字是ext2,balloc.obitmap.o两个文件最终连接生成ext2.o直至ext2.ko文件。

3.Kconfig

内核配置脚本语法:

(1)大多数的内核配置选项都对应一个Kconfig中的一个菜单入口。

menu       "USBTMC"

comment  "USBTMC Driver"

config      USBTMC

tristate    "USBTMC"

default     n

endmenu

(a)“config”关键字定义新的配置选项,之后的几行定义了该配置选项的属性。配置选项的属性包括类型,数据,输入提示,依赖关系(及反向依赖关系),帮助信息和默认值等。

(b)每个配置选项都必须指定类型,其他类型都基于这两种基本类型。类型定义后可以紧跟输入提示,下面两个脚本是等价的

脚本1

bool“Networking support”

脚本2

bool

promt“Networking support”

输入提示的一般格式如下提示:

prompt  <prompt> [if <expr>]

其中可选的if用来表示该提示的依赖关系。

默认值的格式如下所示:

default<expr> [if <expr>]

一个配置选项可以存在任意多个默认值,这种情况下,只有第一个被定义的值是可用的。如果用户不设置对应的选项,配置选项的值就是默认值。

(c)依赖关系的格式如下所示:

dependson (或者 requires)<expr>

如果定义了多个依赖关系,它们之间用”&&”间隔。依赖关系也可以应用到该菜单中所有的其他选项中。

(4)反向依赖关系的格式如下所示:

select<symbol> [if <expr>]

A.depends能限定一个symbol的上限,即如果A依赖于B,则在B被配置为“Y”的情况下,A可以为“Y”,“M”和”N”;B被配置为“M”的情况下,A可以被配置为“M”或“N”B在被配置为“N”的情况下,A只能为”N”

B.select能限定一个symbol的下限,若A反向依赖于B,则A的配置值会高于或等于B(正好与depends相反)。如果symbol反向依赖于多个对象,则它的下限是这些对象的对大值。

(5)帮助信息的格式如下:

help(---help---)

开始

。。。

结束

帮组信息完全靠文本缩进识别结束。“---help---”和”help”的初衷在于将文件中的配置逻辑与给开发人员的提示分开。

3.菜单结构

菜单入口在菜单数结构中的位置可由两种方法决定。

(1)第一中方式如下所下:

menu“Network device support”

dependson NET

config NETDEVICES

endmenu

所有处于”menu”和”endmenu”之间的菜单入口都会成为“Networkdevice support”的子菜单。而且,所有子菜单选项都会继承父菜单的依赖关系,比如:“Networkdevice support”对“NET”的依赖被加到了配置选项NETDEVICES的依赖列表中。

(2)另一种方式是通过分析依赖关系生成菜单结构。如果菜单选项在一定程度上依赖于前面的选项,它就能成为该选项的子菜单。如果父选项为“N”,则子选项不可见;如果父选项为“Y”或“M”,则子选项可见。

Eg:

config MODULES

bool “Enable loadable module support”

config MODVERSIONS

bool “Set version information on all module symbole”

dependson MODULES

comment “module support disabled”

depends on  !MODULES

MODVERSIONS直接依赖MODULES,如果MODULES不为“N”,该选项才可见。

(3)除此之外,Kconfig中还可能使用“choices…...endchoice”,”comment”,”if....endif”这样的语法结构。

其中

choice

<choiceoptions>

<choiceblock>

enchoice

它定义一个选择群,其接受的选项(choiceoptions)可以是前面描述的任何属性。在一个硬件有多个驱动的情况下使用,使用选择可以实现最终只有一个驱动被编译进内核或模块。选择群还可以接受的另一个选项是“optional”,

这样菜单入口就被设置为“N”,没有被选中。

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值