LINUX设备驱动开发之0417


源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。
总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。
在编译时,编译器只检测程序语法,和函数、变量是否被声明。
如果函数未被声明,编译器会给出一个警告,但可以生成Object File。
而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),
在VC下,这种错误一般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object File.


1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程。
3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。


target也就是一个目标文件,可以是Object File,也可以是执行文件。
prerequisites就是,要生成那个target所需要的文件或是目标。
command也就是make需要执行的命令。(任意的Shell命令)


如果一个工程有3个头文件,和8个C文件
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o




在默认的方式下,也就是我们只输入make命令。那么,
1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
3、如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。
4、如果edit所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
5、当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件edit了。


所以,为了makefile的易维护,在makefile中我们可以使用变量。makefile的变量也就是一个字符串,理解成C语言中的宏可能会更好。


修改后的编译文件:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit $(objects)


五、让make自动推导


只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,
如果make找到一个whatever.o,那么whatever.c,就会是whatever.o的依赖文件。
并且 cc -c whatever.c 也会被推导出来,于是,我们的makefile再也不用写得这么复杂。
我们的是新的makefile又出炉了。


objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean
clean :
rm edit $(objects)


这种方法,也就是make的“隐晦规则”。上面文件内容中,“.PHONY”表示,clean是个伪目标文件。


六、另类风格的makefile


objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
.PHONY : clean
clean :
rm edit $(objects)


七、清空目标文件的规则


clean:
rm edit $(objects)


.PHONY : clean
clean :
-rm edit $(objects)


而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。


一、Makefile里有什么?


Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。


1、显式规则。
显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。


2、隐晦规则。
由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。


3、变量的定义。
在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。


4、文件指示。
其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;
另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;
还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。


5、注释。
Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“\#”。


最后,还值得一提的是,在Makefile中的命令,必须要以[Tab]键开始。


二、Makefile的文件名


默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。
在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。
最好不要用“GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。


三、引用其它的Makefile


在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。
include的语法是: include <filename>


举个例子,你有这样几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mk和f.mk,那么,下面的语句:
include foo.make *.mk $(bar)
等价于:
include foo.make a.mk b.mk c.mk e.mk f.mk


1、如果make执行时,有“-I”或“--include-dir”参数,那么make就会在这个参数
所指定的目录下去寻找。


2、如果目录<prefix>/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。
如果有文件没有找到的话,make会生成一条警告信息,但不会马上出现致命错误。
它会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或是不能读取的文件,如果还是不行,make才会出现一条致命信息。
如果你想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号“-”。


如: -include <filename>
其表示,无论include过程中出现什么错误,都不要报错继续执行。和其它版本make兼 容的相关命令是sinclude,其作用和这一个是一样的。


四、环境变量 MAKEFILES


五、make的工作方式


GNU的make工作时的执行步骤入下:
1、读入所有的Makefile。
2、读入被include的其它Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令。


第五部分、书写规则


规则包含两个部分,一个是依赖关系,一个是生成目标的方法。


一、规则举例


foo.o : foo.c defs.h # foo模块
cc -c -g foo.c


1、文件的依赖关系,foo.o依赖于foo.c和defs.h的文件,如果foo.c和defs.h的
文件日期要比foo.o文件日期要新,或是foo.o不存在,那么依赖关系发生。


2、如果生成(或更新)foo.o文件。也就是那个cc命令,其说明了,
如何生成foo.o这个文件。(当然foo.c文件include了defs.h文件)


二、规则的语法


targets : prerequisites
command
...
或是这样:
targets : prerequisites ; command
command
...


一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。




深入理解LINUX内核


LINUX设备驱动程序


 Linux内核的主要模块(或组件)分以下几个部分:存储管理、CPU和进程管理、文件系统、设备管理和驱动、网络通信,以及系统的初始化(引导)、系统调用等。
Image 镜像文件


boot header  1 page
kernel       n pages
ramdisk      m pages
second stage o pages
device tree  p pages


1、在flash中都是按照page_size对齐
2、Second是可选项(second_size==0->no second)
3、加载到每个成员到其指定的物理地址
4、在tags_addr中准备tags,kernel的commandline是在此基础上增补而成
5、r0=0,r1=MACHINE_TYPE,r2=tags_addr
6、如果second_size!=0则跳转到second_addr地址;否则跳转到kernel_addr中


高通平台上Android的boot loader是用的LK。
LK是(L)ittle (K)ernel的缩写,它只是整个系统的引导部分,
所以它不是独立存在。LK是一个功能及其强大的bootloader,
但现在只支持arm和x86平台。 LK的一个显著的特点就是它实现了一个简单的线程机制(thread),
和对高通处理器的深度定制和使用。


LK主要工作有:
硬件初始化
建立矢量表、MMU、cache、初始化外围设备、存储、USB、加密等。
从storage中引导boot.img
支持烧录image文件以及recovery模式
recovery flag 或 fastboot 的按键没有设置
从MMC中读取boot.img的header,解析各个image大小并将boot.img加载到临时分区里, 
base addr在rules.mk中定义
将kernel从临时分区重新加载到KERNEL_ADDR中
将ramdisk分区从临时分区重新加载到RAMDISK_ADDR中
找到正确的device tree并将其加载到TAGS_ADDR中
更新cmdline, 将LK中获取的信息整理成cmdline形式
更新device tree内容:
获取’/memory’节点以及’/chosen’节点的偏移
添加HLOS的memory regions(起始地址和大小)作为’reg’参数到’/memory’节点
将cmdline添加到’/chosen’节点的‘bootargs’属性中
将RAM disk的属性作为‘linux, initrd-start’‘linux, initrd-end’添加到’/chosen’节点
关闭display, cache, MMU, 然后跳转到kernel




内核Linux 2.6加载驱动一般要以下几步:
(1)在arch\arm\mach-XXX目录找到与开发板适配的板级文件
(2)在板级文件中找到板级初始化函数board_init
(3)在board_init中注册要添加外设
(4)在drivers\XXX中注册驱动


Devicd Tree
新架构有点:
本质上,Device Tree 改变了原来用hardcode方式将HW配置信息嵌入到
内核代码的方法,改用bootloader传递一个DB的形式。随着ARM在消费类
电子上的广泛应用(甚至桌面系统、服务器系统),我们期望ARM能够像
X86那样用一个kernel image来支持多个platform。在这种情况下,如果
我们认为kernel是一个black box,那么其输入参数应该包括:
(1)识别platform的信息
(2)runtime的配置参数
(3)设备的拓扑结构以及特性


对于嵌入式系统,在系统启动阶段,bootloader会加载内核并将控制权转交
给内核,此外,还需要把上述的三个参数的信息传递给kenel,以便kernel可以
有较大的灵活性。在linux kernel中,Device Tree的设计目标就是如此。


特点:
(1)本质上是一个字节码格式的数据结构,在内核启动时被加载到内存中
(2)没有规定哪些内容可以放置其中以及放置的位置。内核可以搜索设备
树总的任意路径和参数。程序员来决定哪些配置作为参数放进设备树里,以及
放置在什么地方。
(3)采取标准的树结构,则可用一套方便的API来供内核操作。


镜像是可执行文件吗?


(1)dts:DT源文件称为dts文件,Ascii文本文件,一般一个dts文件对应一个
Machine,ARM架构下dts文件存放于arch/arm/boot/dts/目录下。


(2)dtsi:类似于C语言的头文件,是多个Machine/SoC公用的dt文件,i代表inlude.
使用方法是在dts文件中进行include .dtsi文件。


(3)dtc:为编译工具,它可以将.dts文件编译成.dtb文件。
在内核的arch/arm/boot/dts/Makefile中,若选中某种SOC,则与其对应相关的所有
dtb文件都将编译出来,在linux下,make dtbs可单独编译dtb。


(4)dtb: DTC编译.dts生成的二进制文件,bootloader在引导内核时,会预先读取
.dtb到内存,进而由内核解析。


(5)dt.img:多个dtb文件打包形成dt.img,以适配多个Machine,dts/dtb的结构是
标准化的,dt.img有头信息和多个dtb组成,因为没有统一的标准,不同的厂商头信息
可能是不同。











猜你喜欢

转载自blog.csdn.net/baiyibin0530/article/details/79983763