现在的位置: 首页 > 综合 > 正文

Linux设备驱动开发基础—新驱动程序添加到内核方法(2)

2013年07月14日 ⁄ 综合 ⁄ 共 8486字 ⁄ 字号 评论关闭

一 . 概述:

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

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

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

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

二 . 以mini2440中ADC驱动的添加为例来进行说明

ADC设备在Linux中可以看做是简单的字符设备,也可以当做是一混杂设备(misc设备),这里我们就看做是misc设备来实现ADC的驱动。

1. 先把驱动代码 s3c24xx-adc.h和mini2440_adc.c源代码复制linux-2.6.32.15\drivers\misc目录下。 

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

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

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

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

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

注意:

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

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

2. 修改 misc 目录下的 Kconfig 和 Makefile

(1) 在目录的Kconfig文件中增加ADC的编译配置选项,如下所示:

sudo gedit Kconfig

添加下面一句后

configMINI2440_ADC
bool "ADC driver for FriendlyARM Mini2440 development boards"
depends on MACH_MINI2440
default y if MACH_MINI2440
help
       this is ADC driver for FriendlyARMMini2440 development boards
       Notes: the touch-screen-driver requiredthis option

上述 Kconfig 文件的这段脚本意味着只有在MACH_MINI2440项目被配置的情况下,才会出现MINI2440_ADC配置项目,这个配置项目为布尔型(要么编译入内核,要么不编译,选项为“Y”或“N” ) ,菜单上显示的字符串为“ADC driver for FriendlyARMMini2440 development boards” ,“help”后面的内容为帮助信息。

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

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

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

(2) 在目录的Makefile文件中增加对mini2440_adc.c源代码的编译,如下所示:

添加一句话:

obj-$(CONFIG_MINI2440_ADC)+= mini2440_adc.o 

上述脚本意味着如果MINI2440_ADC配置选项被选择为“Y”或“M” ,即obj-$(CONFIG_MINI2440_ADC)等同于obj-y或obj-m时,则编译mini2440_adc.c,选“Y”的情况直接会将生成的目标代码直接连接到内核,为“M”的情况则会生成模块mini2440_adc.ko(由于MINI2440_ADC为布尔型,实际不会为“M”) ;如果 MINI2440_ADC 配置选项被选择为“N”,即obj-$(CONFIG_MINI2440_ADC)等同于obj-n时,则不编译mini2440_adc.c。 

3、正确配置好后,我们在源码下执行makemenuconfig 后,在出现的 LinuxKernel Configuration 图形界面中选择DeviceDrivers --->[*] Misc devices --->  ,将会看到新加的 ADCdriver for FriendlyARM Mini2440 development boards 选项

4、删除:

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

三 . 详解:

Makefile , Kconfig 和配置工具组成了 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 包含的菜单和项目就可以描绘出一个分层结构。 
例如当我们运行 makezImagine 、 makebzImagine 等生成映像的命令时,会先检索顶层的 Makefie (在 arch/xxx/ 目录下的 Makefile 为顶层Makefile 补充体系结构相关的信息),顶层 Makefile 的两个主要任务是:产生内核映像文件和内核模块。接着顶层 Makefile 会去递归地进入内核的各个子目录,然后分别调用子目录中的 Makefile (这些 Makefile 记录编译目标),而进入哪些子目录取决于内核的配置。 
当使用 makemenuconfig , makeconfig 命令时,生成的 .config 在源码目录下记录哪些部分被编译入内核,哪些部分被编译为内核模块。简而言之,它是保存内核配置结果的文件。当我们装上 Linux 系统时,第一次查看源码下的所有文件,会发现没有 .config 文件,那是因为从来没配置过内核。当你运行 makemenuconfig 保存并退出时,再次查看就有这个文件了。 
配置工具,包括配置命令解释器(对配置脚本中使用的命令进行解释)和配置用户界面(提供字符界面和图形界面),配置工具都是用脚本语言编写的。

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

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

eg:obj-$(CONFIG_FILENAME) +=filename.o

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

obj-$(CONFIG_ISDN_PPP_BSDCOMP)+= isdn_bsdcomp.o

除了obj-形式的目标以外,还有lib-ylibrary库、hostprogs-y主机程序等目标,但是基本都应用在特定的目录和场合下。

lib-y用来定义那些文件被编译成库文件,lib-y中定义的.o文件有当前目录下的.c或.S文件编译生成,他们被打包成当前目录下的一个库文件:lib.a。同事出现在obj-y、lib-y中的.o文件,不会被包含进lib.a中。要把这个lib.a编译进内核,需要在顶层Makefile中libs-y变量中列出当前目录。

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

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

eg:

obj-$(CONFIG_EXT2_FS)+= ext2.o

ext2-y :=balloc.o bitmap.o

ext2-$(CONFIG_EXT2_FS_XATTR)+= xattr.o

模块的名字为ext2,由balloc.o和bitmap.o两个目标文件最终连接生成 ext2.o 直至 ext2.ko 文件,是否包括 xattr.o 取决于内核配置文件的配置情况。

或者写成如-objs的形式:

obj-$(CONFIG_ISDN)+= isdn.o

isdn-objs:= isdn_net_lib.o isdn_v110.o isdn_common.o

(d)目录层次的迭代。

eg:

obj-$(CONFIG_EXT2_FS)+= ext2/

当CONFIG_EXT2_FS的值为y或m时, kbuild将会把ext2目录列入向下迭代的目标中,具体 ext2 目录下的文件是要作为模块编译还是链入内核由ext2目录下的Makefile文件的内容决定。

3.Kconfig

内核配置脚本文件的语法主要包括如下几个方面。

(1)菜单入口。

大多数的内核配置选项都对应Kconfig中的一个菜单入口,如下所示:

configMODVERSIONS

     bool "Set version information on allmodule symbols"

     depends on MODULES

     help

        Usually, modules have to be recompiledwhenever you switch to anew

        kernel. ...

“config”关键字定义新的配置选项,之后的几行定义了该配置选项的属性。配置选项的属性包括类型、数据范围、输入提示、依赖关系(及反向依赖关系) 、帮助信息和默认值等。 每个配置选项都必须指定类型,类型包括bool、tristate、string、hex 和 int,其中tristate 和string是两种基本的类型,其他类型都基于这两种基本类型。bool变量取值有两种:y和n;tristate取值有3种:y、n和m;string变量取值为字符串;hex变量取值为16进制的数据;int变量取值为10进制的数据。

类型定义后可以紧跟输入提示,下面的两段脚本是等价的。

脚本1:

bool"Networking support"

脚本2:

bool

prompt"Networking support"

输入提示信息的完整格式如下所示:

prompt<prompt> [if <expr>]

如果使用“if<expr>”,则当expre为真时,才显示提示信息。在实际使用中“prompt”关键字可以省略。其中可选的if用来表示该提示的依赖关系。

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

depends on(或者requires) <expr>

如果定义了多个依赖关系,它们之间用“&&”间隔。依赖关系也可以应用到该菜单中所有的其他选项中(这些选项同样可接受 if 表达式) ,下面的两段脚本是等价的。

脚本1:

bool "foo" if BAR

default y if BAR

脚本2:

depends on BAR

bool "foo"

default y

默认值的格式如下所示:

default <expr> [if<expr>]

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

select <symbol> [if<expr>]

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

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

帮助信息的格式如下:

help(或---help---)

  开始

  …

  结束

帮助信息完全靠文本缩进识别结束。 “---help---”和 “help”在作用上没有区别,设计“---help---”的初衷在于将文件中的配置逻辑与给开发人员的提示分开。

(2)菜单结构。

菜单入口在菜单树结构中的位置可由两种方法决定。第一种方式如下所示:

menu "Network devicesupport"

     depends on NET

config NETDEVICES

     …

endmenu

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

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

Eg:

configMODULES

bool“Enable loadable module support”

configMODVERSIONS

bool“Set version information on all module symbole”

dependson MODULES

comment“module support disabled”

dependson !MODULES

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

除此之外, Kconfig中还可能使用 “choices … endchoice” 、 “comment” 、 “if…endif”

这样的语法结构。其中“choices  … endchoice”的结构如下所示:

choice

<choice options>

<choice block>

endchoice

chioce条目将多个类似的配置选项组合在一起,供用户单选或多选。

Comment条目用于定义一些帮助信息,它在配置过程中出现在界面的第一行;并且这些帮助信息出现在帮助文件中(作为注释)。

source条目用于读入另一个kconfig文件。例如上级Kconfig文件要读入net/Kconfig文件:

source  “net/Kconfig”

四、实例:在内核中新增驱动代码目录和子目录

下面讲解一个综合实例,假设我们要在内核源代码drivers目录下为 ARM体系结

构新增如下用于test driver的树型目录:

|--test

    |-- cpu

        | -- cpu.c

    |-- test.c

    |-- test_client.c

    |-- test_ioctl.c

    |-- test_proc.c

    |-- test_queue.c

在内核中增加目录和子目录,我们需为相应的新增目录创建 Kconfig 和 Makefile

文件,而新增目录的父目录中的 Kconfig 和 Makefile 文件也需要修改,以便新增的

Kconfig和Makefile文件能被引用。

在新增的test目录下,应该包含如下Kconfig文件:

#

# TEST driver configuration

#

menu "TEST Driver "

comment " TEST Driver"

 

config CONFIG_TEST

     bool "TEST support "

config CONFIG_TEST_USER

     tristate "TEST user-spaceinterface"

     depends on CONFIG_TEST

 

endmenu

由于 TEST  driver 对于内核来说是新的功能,所以首先需要创建一个菜单 TEST Driver;然后显示“TEST support” ,等待用户选择;接下来判断用户是否选择了 TEST Driver,如果是(CONFIG_TEST=y) ,则进一步显示子功能:用户接口与 CPU功能支持;由于用户接口功能可以被编译成内核模块,所以这里的询问语句使用了tristate。

为了使这个Kconfig文件能起作用,需要修改arch/arm/Kconfig文件,增加以下内容:

source "drivers/test/Kconfig"

脚本中的source意味着引用新的Kconfig文件。

在新增的test目录下,应该包含如下Makefile文件:

# drivers/test/Makefile

#

# Makefile for the TEST.

#

obj-$(CONFIG_TEST) += test.o test_queue.o test_client.o

obj-$(CONFIG_TEST_USER) += test_ioctl.o

obj-$(CONFIG_PROC_FS) += test_proc.o

 

obj-$(CONFIG_TEST_CPU) += cpu/

该脚本根据配置变量的取值构建obj-*列表。 由于 test目录中包含一个子目录cpu,

当CONFIG_TEST_CPU=y时,需要将cpu目录加入列表。

test目录中的cpu子目录也需包含如下的Makefile文件:

# drivers/test/test/Makefile

#

# Makefile for the TEST CPU

#

obj-$(CONFIG_TEST_CPU) += cpu.o

为了使得整个 test 目录能够被编译命令作用到,test 目录父目录中的 Makefile 文件也需新增如下脚本:

obj-$(CONFIG_TEST) += test/

在drivers/Makefile中加入obj-$(CONFIG_TEST) += test/,使得用户在进行内核编译时能够进入 test目录。

增加了Kconfig和Makefile文件之后的新的 test树型目录如下所示:

test

├──cpu

│  ├── cpu.c

│  └── Makefile

├── test.c

├──test_client.c

├──test_ioctl.c

├──test_proc.c

├──test_queue.c

├──Makefile

└──Kconfig

抱歉!评论已关闭.