0417 of LINUX device driver development


The source file first generates an intermediate object file, and then generates an executable file from the intermediate object file.
To sum up, the source file will first generate the intermediate object file, and then the intermediate object file will generate the execution file.
At compile time, the compiler only checks the program syntax, and whether functions and variables are declared.
If the function is not declared, the compiler will give a warning, but can generate Object File.
When linking the program, the linker will look for the implementation of the function in all Object Files. If it can't find it, it will report a linker error code (Linker Error).
Under VC, this kind of error is generally: Link 2001 Error, which means that the linker could not find the implementation of the function. You need to specify the Object File of the function.


1) If the project has not been compiled, then all our C files must be compiled and linked.
2) If some C files of this project are modified, then we only compile the modified C files and link the target program.
3) If the header file of this project has been changed, then we need to compile the C file that references these header files and link the target program.


A target is also a target file, which can be an Object File or an executable file.
prerequisites are the files or targets required to generate that target.
command is the command that make needs to execute. (arbitrary shell command)


If a project has 3 header files and 8 C files
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




In the default mode, that is, we only enter the make command. Then,
1. make will look for a file named "Makefile" or "makefile" in the current directory.
2. If found, it will find the first target file (target) in the file. In the above example, it will find the "edit" file and use this file as the final target file.
3. If the edit file does not exist, or the modification time of the .o file that edit depends on is newer than the edit file, then he will execute the command defined later to generate the edit file.
4. If the .o file that edit depends on also exists, then make will find the dependency of the target .o file in the current file. If it is found, the .o file will be generated according to that rule. (This is a bit like a stack process)
5. Of course, your C files and H files exist, so make will generate .o files, and then use the .o files to live the ultimate task of make, which is to execute the file edit .


So, in order to make the makefile easier to maintain, we can use variables in the makefile. The variable of the makefile is also a string, which may be better understood as a macro in the C language.


Modified compiled file:
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)


5. Let make deduce automatically


As long as make sees a [.o] file, it will automatically Add the [.c] file to the dependencies.
If make finds a whatever.o, then whatever.c will be a dependency file of whatever.o.
And cc -c whatever.c will also be deduced, so our makefiles don't have to be so complicated anymore.
Our new makefile is out again.


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)


This method is the "implicit rule" of make. In the above file content, ".PHONY" indicates that clean is a pseudo target file.


6. Alternative style 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)


7 , The rules for clearing object files


clean:
rm edit $(objects)


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


and adding a small minus sign in front of the rm command means that there may be problems with some files, but Don't worry, keep doing the rest.


1. What is in the Makefile?


Makefile mainly contains five things: explicit rules, implicit rules, variable definitions, file instructions and comments.


1. Explicit rules.
Explicit rules describe how to generate one or more object files. This is clearly pointed out by the author of the Makefile, the file to be generated, the file's dependencies, the generated command.


2. Obscure rules.
Since our make has the function of automatic derivation, the implicit rules allow us to write Makefiles roughly and briefly, which is supported by make.


3. Definition of variables.
In the Makefile, we need to define a series of variables. The variables are generally strings. This is a bit like a macro in your C language. When the Makefile is executed, the variables in it will be expanded to the corresponding reference position.


4. Document instructions.
It includes three parts, one is to refer to another Makefile in one Makefile, just like include in C language; the
other is to specify valid parts in Makefile according to certain situations, just like precompile in C language #if is the same;
there is also the definition of a multi-line command. I will talk about this part in the next part.


5. Notes.
There are only line comments in the Makefile. Like the UNIX Shell script, the comments are made with the "#" character, which is like "//" in C/C++. If you want to use the "#" character in your Makefile, you can escape it with a backslash, eg: "\#".


Finally, it is also worth mentioning that the commands in the Makefile must start with the [Tab] key.


Second, the file name of the Makefile


By default , the make command will search for the file named "GNUmakefile", "makefile", and "Makefile" in the current directory in order, and find the explanation of this file.
Among the three file names, it is best to use the file name "Makefile", because the first character of this file name is capitalized, which has a conspicuous feeling.
It is best not to use "GNUmakefile", this file is recognized by GNU make. There are other makes that are only sensitive to the all-lowercase "makefile" filename, but basically most make supports both "makefile" and "Makefile" by default.


3. Refer to other


Makefiles Use the include keyword in Makefiles to include other Makefiles, which is very similar to #include in C language, and the included files will be placed in the include location of the current file as they are.
The syntax of include is: include <filename>


For example, you have several Makefiles: a.mk, b.mk, c.mk, and a file called foo.make, and a variable $(bar) whose Including e.mk and f.mk, then the following statement:
include foo.make *.mk $(bar) is
equivalent to:
include foo.make a.mk b.mk c.mk e.mk f.mk


1. If there is a "-I" or "--include-dir" parameter when make is executed, then make will search in
the .


2. If the directory <prefix>/include (usually: /usr/local/bin or /usr/include) exists, make will also look for it.
make generates a warning if any files are not found, but not an immediate fatal error.
It will continue to load other files. Once the makefile has been read, make will retry the files it cannot find or cannot read. If it still fails, make will display a fatal message.
If you want make to ignore unreadable files and continue execution, you can add a minus sign "-" before include.


Such as: -include <filename>
It means that no matter what error occurs during the include process, do not report an error and continue to execute. A related command that is compatible with other versions of make is sinclude, which does the same thing as this one.


4. Environment variable MAKEFILES


5. How make works


The execution steps when GNU make works are as follows:
1. Read all Makefiles.
2. Read other Makefiles that are included.
3. Initialize the variables in the file.
4. Derive implicit rules and analyze all rules.
5. Create dependency chains for all object files.
6. Based on dependencies, decide which targets to regenerate.
7. Execute the build command.


The fifth part, writing rules The


rules include two parts, one is the dependency, the other is the method of generating the target.


1. Example of rules


foo.o : foo.c defs.h # foo module
cc -c -g foo.c


1. File dependencies, foo.o depends on the files of foo.c and defs.h, if foo. The file date of c and defs.h is newer than the
file date of foo.o, or foo.o does not exist, then the dependency occurs.


2. If the foo.o file is generated (or updated). That is, the cc command, which explains
how to generate the file foo.o. (Of course the foo.c file includes the defs.h file)


Second, the syntax of the rules


targets : prerequisites
command
...
or this:
targets : prerequisites ; command
command
...


In general, make will execute commands in the standard UNIX shell, which is /bin/sh.




In-depth understanding of LINUX kernel


LINUX device driver


 The main modules (or components) of the Linux kernel are divided into the following parts: storage management, CPU and process management, file system, device management and driver, network communication, and system initialization (boot), system calls, etc.
Image image file


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


1. In flash, they are aligned according to page_size
2. Second is optional (second_size==0->no second)
3. Loading To each member to its designated physical address
4. Prepare tags in tags_addr. The commandline of the kernel is supplemented on this basis.
5. r0=0, r1=MACHINE_TYPE, r2=tags_addr
6. If second_size!=0, then Jump to the second_addr address; otherwise, jump to kernel_addr


The Android boot loader on the Qualcomm platform uses LK.
LK is the abbreviation of (L)ittle (K)ernel, it is only the boot part of the whole system,
so it does not exist independently. LK is a functional and powerful bootloader,
But now only supports arm and x86 platforms. A notable feature of LK is that it implements a simple thread mechanism (thread),
and deep customization and use of Qualcomm processors.


The main tasks of LK are:
hardware initialization,
establishment of vector table, MMU, cache, initialization of peripheral devices, storage, USB, encryption, etc.
Booting boot.img from storage
supports burning image files and recovery mode
recovery flag or fastboot buttons are not set Read the header of boot.img
from  MMC, parse the size of each image and load boot.img into the temporary partition,
base addr is defined in rules.mk Reload
the kernel from the temporary partition to KERNEL_ADDR Reload
the ramdisk partition from the temporary partition to RAMDISK_ADDR
Find the correct device tree and load it into TAGS_ADDR
Update the cmdline and organize the information obtained in LK In the form of cmdline
Update content of the device tree:
get the '/memory' node and the offset of the '/chosen' node
Add HLOS memory regions (starting address and size) as the 'reg' parameter to the '/memory' node
Add cmdline to In the 'bootargs' attribute of the '/chosen' node
Add the properties of the RAM disk as 'linux, initrd-start', 'linux, initrd-end' to the '/chosen' node,
close the display, cache, MMU, and then jump to the kernel




kernel Linux 2.6 Loading the driver generally takes the following steps:
(1) Find the board-level file suitable for the development board in the arch\arm\mach-XXX directory
(2) Find the board-level initialization function board_init in the board-level file
(3) Register in board_init to add peripherals
(4) The new architecture of registering the driver


Device Tree in drivers\XXX is a little bit: Essentially, Device Tree has changed the original method of embedding HW configuration information into the kernel , and instead uses the bootloader to pass a DB form. With the widespread application of ARM in consumer electronics (even desktop systems and server systems), we expect ARM to support multiple platforms with a single kernel image like X86. In this case, if we think that the kernel is a black box, then its input parameters should include: (1) platform identification information (2) runtime configuration parameters (3) device topology and characteristics For embedded systems, In the system startup phase, the bootloader will load the kernel and transfer control to the kernel. In addition, the information of the above three parameters needs to be passed to kenel, so that the kernel can













There is greater flexibility. In the linux kernel, Device Tree is designed to be like this.


Features:
(1) It is essentially a data structure in bytecode format, which is loaded into memory when the kernel is started
(2) It does not specify what content can be placed in it and where to place it. The kernel can search the device
tree for arbitrary paths and parameters. It is up to the programmer to decide which configuration to put into the device tree as parameters, and
where to place it.
(3) If a standard tree structure is adopted, a set of convenient APIs can be used for the kernel to operate.


Is the image an executable?


(1) dts: DT source files are called dts files, Ascii text files, generally one dts file corresponds to one
Machine, and the dts file under the ARM architecture is stored in the arch/arm/boot/dts/ directory.


(2) dtsi: Similar to the header file of C language, it is a dt file common to multiple Machine/SoC, i stands for inlude.
The method of use is to include the .dtsi file in the dts file.


(3) dtc: It is a compilation tool, which can compile .dts files into .dtb files.
In the arch/arm/boot/dts/Makefile of the kernel, if a certain SOC is selected, all
dtb files related to it will be compiled. Under linux, make dtbs can compile dtb separately.


(4) dtb: DTC compiles the binary file generated by .dts. When the bootloader boots the kernel, it will read
.dtb to the memory in advance, and then be parsed by the kernel.


(5) dt.img: Multiple dtb files are packaged to form dt.img to adapt to multiple Machines. The structure of dts/dtb is
standardized , and dt.img consists of header information and multiple dtbs, because there is no unified standard , the header information of different manufacturers
may be different.











Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326050804&siteId=291194637