linux 驱动学习笔记01--Linux 内核的编译

时间:2023-06-04 17:33:08

由于用的学习材料是《linux设备驱动开发详解(第二版)》,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式。

#make config(基于文本的最为传统的配置界面,不推荐使用)
#make menuconfig(基于文本菜单的配置界面)
#make xconfig(要求 QT 被安装)
#make gconfig(要求 GTK+被安装)
在配置 Linux 2.6 内核所使用的 make config、 make menuconfig、 make xconfig 和 make gconfig
这 4 种方式中,最值得推荐的是 make menuconfig,它不依赖于 QT 或 GTK+,且非常直观。

make menuconfig的配置主界面:

linux 驱动学习笔记01--Linux 内核的编译

下图是Device Drive目录下的Real Time Clock菜单,图片对应的驱动位于source/linux-2.6.32-devkit8500/drivers/rtc下,

linux 驱动学习笔记01--Linux 内核的编译

跟上图相对应的Kconfig如下,可以发现这个界面中的所有内容都是通过Kconfig文件定义的。

Kconfig:

#
# RTC class/drivers configuration
# config RTC_LIB
tristate menuconfig RTC_CLASS
tristate "Real Time Clock"
default n
depends on !S390
select RTC_LIB
help
Generic RTC class support. If you say yes here, you will
be allowed to plug one or more RTCs to your system. You will
probably want to enable one or more of the interfaces below. This driver can also be built as a module. If so, the module
will be called rtc-core. if RTC_CLASS config RTC_HCTOSYS
bool "Set system time from RTC on startup and resume"
depends on RTC_CLASS = y
default y
help
If you say yes here, the system time (wall clock) will be set using
the value read from a specified RTC device. This is useful to avoid
unnecessary fsck runs at boot time, and to network better. config RTC_HCTOSYS_DEVICE
string "RTC used to set the system time"
depends on RTC_HCTOSYS = y
default "rtc0"
help
The RTC device that will be used to (re)initialize the system
clock, usually rtc0. Initialization is done when the system
starts up, and when it resumes from a low power state. This
device should record time in UTC, since the kernel won't do
timezone correction. The driver for this RTC device must be loaded before late_initcall
functions run, so it must usually be statically linked. This clock should be battery-backed, so that it reads the correct
time when the system boots from a power-off state. Otherwise, your
system will need an external clock source (like an NTP server). If the clock you specify here is not battery backed, it may still
be useful to reinitialize system time when resuming from system
sleep states. Do not specify an RTC here unless it stays powered
during all this system's supported sleep states. config RTC_DEBUG
bool "RTC debug support"
depends on RTC_CLASS = y
help
Say yes here to enable debugging support in the RTC framework
and individual RTC drivers. comment "RTC interfaces" config RTC_INTF_SYSFS
boolean "/sys/class/rtc/rtcN (sysfs)"
depends on SYSFS
default RTC_CLASS
help
Say yes here if you want to use your RTCs using sysfs interfaces,
/sys/class/rtc/rtc0 through /sys/.../rtcN. If unsure, say Y. config RTC_INTF_PROC
boolean "/proc/driver/rtc (procfs for rtc0)"
depends on PROC_FS
default RTC_CLASS
help
Say yes here if you want to use your first RTC through the proc
interface, /proc/driver/rtc. Other RTCs will not be available
through that API. If unsure, say Y. endif # RTC_CLASS

源文件将近千行,这里只截取前面部分进行分析,

Kconfig
内核配置脚本文件的语法比较简单,主要包括如下几个方面。
( 1 )菜单入口 。
大多数的内核配置选项都对应 Kconfig 中的一个菜单入口:
config MODVERSIONS
  bool "Module versioning support"

  help
     Usually, you have to use modules compiled with your kernel.
    Saying Y here makes it ...
“ config” 关键字定义新的配置选项,之后的几行定义了该配置选项的属性。配置选项的属性包括类型、数据范围、输入提示、依赖关系、选择关系及帮助信息和默认值等。
每个配置选项都必须指定类型,类型包括 bool、 tristate、 string、 hex 和 int,其中 tristate 和string 是两种基本的类型,其他类型都基于这两种基本类型。类型定义后可以紧跟输入提示,下面的两段脚本是等价的:
bool "Networking support"

bool
prompt "Networking support"
输入提示的一般格式为:
prompt <prompt> [if <expr>]
其中可选的 if 用来表示该提示的依赖关系。
默认值的格式为:
default <expr> [if <expr>]
一个配置选项可以存在任意多个默认值,这种情况下,只有第一个被定义的值是可用的。如果用户不设置对应的选项,配置选项的值就是默认值。
依赖关系的格式为:
depends on(或者 requires) <expr>
如果定义了多重依赖关系,它们之间用 “ &&” 间隔。依赖关系也可以应用到该菜单中所有的其他选项(同样接受 if 表达式),下面的两段脚本是等价的:
bool "foo" if BAR
default y if BAR

depends on BAR
bool "foo"
default y
选择关系(也称为反向依赖关系)的格式为:
select <symbol> [if <expr>]
A 如果选择了 B,则在 A 被选中的情况下, B 自动被选中。
kbuild Makefile 中的 expr(表达式)定义为:

<expr> ::= <symbol>
    <symbol> '=' <symbol>
   <symbol> '!=' <symbol>
   '(' <expr> ')'
   '!' <expr>
   <expr> '&&' <expr>
   <expr> '||' <expr>
也就是说 expr 是由 symbol、两个 symbol 相等、两个 symbol 不等以及 expr 的赋值、非、与
或运算构成。而 symbol 分为两类,一类是由菜单入口定义配置选项定义的非常数 symbol,另一
类是作为 expr 组成部分的常数 symbol。
数据范围的格式为:
range <symbol> <symbol> [if <expr>]
为 int 和 hex 类型的选项设置可以接受输入值范围,用户只能输入大于等于第一个 symbol,
小于等于第二个 symbol 的值。
帮助信息的格式为:
help(或---help---)
 开始

结束
帮助信息完全靠文本缩进识别结束。“---help---” 和“help” 在作用上没有区别,设计“---help---”
的初衷在于将文件中的配置逻辑与给开发人员的提示分开。
menuconfig 关键字的作用与 config 类似,但它在 config 的基础上要求所有的子选项作为独立
的行显示。
( 2)菜单结构。
菜单入口在菜单树结构中的位置可由两种方法决定。第一种方式为:
menu "Network device support"
 depends on NET
config NETDEVICES
 …
endmenu
所有处于“ menu” 和“ endmenu” 之间的菜单入口都会成为“ Network device support” 的子菜
单。而且,所有子菜单选项都会继承父菜单的依赖关系,比如,“ Network device support” 对“ NET”
的依赖会被加到了配置选项 NETDEVICES 的依赖列表中。
注意 menu 后面跟的“ Network device support”项目仅仅是 1 个菜单,没有对应真实的配置选
项,也不具备 3 种不同的状态。这是它和 config 的区别。
另一种方式是通过分析依赖关系生成菜单结构。如果菜单选项在一定程度上依赖于前面的选
项,它就能成为该选项的子菜单。如果父选项为“ N”,子选项不可见;如果父选项可见,子选项
才能可见。例如:
config MODULES
 bool "Enable loadable module support"
config MODVERSIONS
 bool "Set version information on all module symbols"
 depends on MODULES

comment "module support disabled"
depends on !MODULES
MODVERSIONS 直接依赖 MODULES,只有 MODULES 不为“ n”时,该选项才可见。
除此之外, Kconfig 中还可能使用“ choices … endchoice”、“ comment”、“ if…endif” 这样的语
法结构。其中 “ choices … endchoice” 的结构为:
choice
<choice options>
<choice block>
endchoice"
它定义一个选择群,其接受的选项(choice options)可以是前面描述的任何属性, 例如 LDD6410
的 VGA 输出分辨率可以是 1 024 !768 或者 800 !600,在 drivers/video/samsung/Kconfig 就定义了如
下的 choice:
choice
depends on FB_S3C_VGA
prompt "Select VGA Resolution for S3C Framebuffer"
default FB_S3C_VGA_1024 _768
config FB_S3C_VGA_1024 _768
bool "1 024 *768@60Hz"
---help---
TBA
config FB_S3C_VGA_640_480
bool "640*480@60Hz"
---help---
TBA
endchoic

下面介绍drivers/rtc下的Makefile:

#
# Makefile for RTC class/drivers.
# ifeq ($(CONFIG_RTC_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif obj-$(CONFIG_RTC_LIB) += rtc-lib.o
obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
obj-$(CONFIG_RTC_CLASS) += rtc-core.o
rtc-core-y := class.o interface.o obj-$(CONFIG_RTC_INTF_ALARM) += alarm.o
obj-$(CONFIG_RTC_INTF_ALARM_DEV) += alarm-dev.o
rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o # Keep the list ordered. obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o
obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o
obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o
obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o
obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o
obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o
obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o

这里主要对内核源代码各级子目录中的 kbuild( 内核的编译系统) Makefile 进行简单介绍,这部分是内核模块或设备驱动的开发者最常接触到的。
Makefile 的语法包括如下几个方面。
( 1 )目标定义。
目标定义就是用来定义哪些内容要作为模块编译,哪些要编译并连接进内核。
例如:
obj -y += foo.o
表示要由 foo.c 或者 foo.s 文件编译得到 foo.o 并连接进内核,而 obj-m 则表示该文件要作为模
块编译。除了 y、 m 以外的 obj-x 形式的目标都不会被编译。
而更常见的做法是根据.config 文件的 CONFIG_变量来决定文件的编译方式,如:
obj -$(CONFIG_ISDN) += isdn.o
obj -$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
除了 obj-形式的目标以外,还有 lib-y library 库, hostprogs-y 主机程序等目标,但是基本都应
用在特定的目录和场合下。
( 2)多文件模块的定义。
最简单的 Makefile 如上一节一句话的形式就够了,如果一个模块由多个文件组成,会稍微复
杂一些,这时候应采用模块名加-y 或-objs 后缀的形式来定义模块的组成文件。如以下例子:
#
# Makefile for the linux ext2-filesystem routines.
#
obj -$(CONFIG_EXT2 _FS) += ext2.o
ext2-y := balloc.o dir.o file.o fsync.o ialloc.o inode.o \
   ioctl.o namei.o super.o symlink.o
ext2-$(CONFIG_EXT2 _FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
ext2-$(CONFIG_EXT2 _FS_POSIX_ACL) += acl.o
ext2-$(CONFIG_EXT2 _FS_SECURITY) += xattr_security.o
ext2-$(CONFIG_EXT2 _FS_XIP) += xip.o
模块的名字为 ext2,由 balloc.o、 dir.o、 file.o 等多个目标文件最终链接生成 ext2.o 直至 ext2.ko
文件,并且是否包括 xattr.o、 acl.o 等则取决于内核配置文件的配置情况,例如, 如果 CONFIG_
EXT2
_FS_POSIX_ACL 被选择,则编译 acl.c 得到 acl.o 并最终链接进 ext2。

( 3)目录层次的迭代。
如下例:
obj -$(CONFIG_EXT2 _FS) += ext2/
当 CONFIG_EXT2_FS 的值为 y 或 m 时, kbuild 将会把 ext2 目录列入向下迭代的目标中。