In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-22 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)06/01 Report--
In this issue, the editor will bring you about how to make the unified model of Linux driver 11-Linux device driver. The article is rich in content and analyzes and describes for you from a professional point of view. I hope you can get something after reading this article.
1. Device tree concept 1.1. Perceptual knowledge of equipment tree
Device tree (Device Tree), which separates the word "device" from "tree", and the file that describes the device tree is called DTS (Device Tree Source). This DTS file uses a tree structure to describe board-level devices, such as the number of CPU, memory base address, which devices are connected to the IIC interface, which devices are connected to the SPI interface, and so on. The device tree is a tree data structure with nodes that describe the devices in the system. Each node has a key-value pair that describes the characteristics of the device it represents. Each node has only one parent node, while the root node has no parent node.
1.2.DTS 、 DTB 、 DTC
DTS: the source code file of the device tree; DTB: the binary file obtained after compiling DTS; the compilation tool of DTC:DTS, whose source code is in the kernel's scripts\ dtc directory. There are many CPU based on the same arm architecture, the same CPU will make many boards with different configurations, how to correctly compile the DTS file of the selected board? In arch/arm/boot/dts/Makefile of the kernel:
Dtb-$ (CONFIG_ARCH_XXX) + = xxx.dtb dtb-$ (CONFIG_ARCH_XXX) + = xxx-sip.dtb dtb-$ (CONFIG_ARCH_XXX) + = xxx.dtb dtb-$ (CONFIG_ARCH_XXX) + = xxx.dtb
For example, in the development board of xxxx, as long as you set CONFIG_ARCH_xxx=y, all DTS that uses this SOC will be compiled into DTB. If the SOC design development board is used later, just create a new DTS file and add the DTB file name of the corresponding name to dtb-$ (CONFIG_ARCH_xxx), and the DTS will be compiled into a binary DTB file when compiling the device tree.
1.3.Device Tree syntax
The following syntax analysis takes xxx.dts as an example.
1.3.1. Dtsi header file
The header file extension of the device tree is .dtsi. Take xxx.dts, for example, which contains the following header files.
# include "skeleton.dtsi" # include xxx.h "# include" xxx-clocks.dtsi "# include" xxx-pinctrl.dtsi "# include" xxx-camera.dtsi "
It is important to note that .dts files can reference not only .dtsi files, but also .h files and other .dts files. Q1: each .dtsi and .dts has its own root node, but only one root node is allowed in a device tree file. How does DTC handle it? Merge the root node, leaving the last level of the root node. The contents of the included header file are expanded between / memory and / cpus. (doubtless, compiled only with xxx.dts) Q2: what if there is a duplicate compatible,DTC in the inclusion process? No error will be reported at compile time, and two nodes with the same compatible attribute will be generated.
1.3.2. Device node
Each node in the device tree is named in the following format:
Node-name@unit-address
Node-name represents the name of the node, and its length should range from 1 to 31 characters and can be composed of the following characters:
Table 2-1 valid characters for node names
The node name should start with a lower or uppercase character and describe the general category of the device. The unit address of a node is specific to the type of bus in which the node is located. It consists of one or more ASCII characters in the character set in Table 2-1. The unit address must match the first address specified in the node's reg attribute. If the node does not have a reg attribute, @ unit-address must be omitted and the node name must be used alone to distinguish the node from other nodes at the same level in the tree. For reg format and unit address, the binding of a particular bus may specify additional more specific requirements. The root node does not have a node name or unit address. It is identified by a forward slash.
Figure 2-1 example of node name
In figure 2-1, two nodes named cpu are distinguished by uint-address 0 and 1, and two nodes named ethernet are distinguished by uint-address fe002000 and fe003000. You will often see the following device names in the device tree:
Watchdog: watchdog@04009800
Before the colon is the node label (label), and after the colon is the node name. The purpose of introducing label is to facilitate access to the node, which can be accessed directly through & label. For example, the above nodes can be accessed using & watchdog.
1.3.2.1. Common name recommendation
The name of the node should be somewhat generic, reflecting the function of the device, not its precise programming model. If applicable, the name should be one of the following choices:
Adc accelerometer atm audio-codec audio-controller backlight: bluetooth bus cache-controller camera can charger clock: clock-controller compact-flash cpu cpus crypto disk display dma-controller dsp eeprom efuse: mdio memory memory-controller mmc mmc-slot mouse nand-controller nvram oscillator parallel pc-card pci pcie Phy pinctrl pmic pmu port ports pwm1.3.2.2. Path name
Nodes in the devicetree can be uniquely identified by specifying the full path from the root node to the desired node (through all child nodes). The convention for specifying the device path is:
/ node-name-1/node-name-2/.../node-name-N
For example, in figure 2-1, the device path to cpu#1 is:
/ cpus/cpu@1
/ is the root node, and uint-address can be omitted on the premise that the complete path is clear.
1.3.3. Attribute
Each node in the device tree has attributes that describe the characteristics of the node. A property consists of a name and a value.
1.3.3.1. Attribute name
The length range of the attribute name should be 1 "31 characters and can be composed of the following characters:
A non-standard attribute name should specify a unique string prefix, such as a stock symbol, that identifies the name of the company or organization that defines the attribute. Example:
Xxx,pin-function =; fsl,channel-fifo-len linux,network-index ibm,ppc-interrupt-server#s1.3.3.2. Attribute value
An attribute value is an array of zero or more bytes containing the information associated with the attribute.
Big-endian and little-endian (big and small): big-endian: the low address side stores high-order bytes; little-endian: the high address side stores low-order bytes
1.3.3.3. Standard attribute
1.Compatible (compatible)
Example:
Compatible = "fsl,mpc8641", "ns16550"
In this example, the operating system will first try to find a device driver that supports fsl,mpc8641-uartmpc8641. If the driver is not found, it will then try to locate the more generic ns16550 device type drivers that are supported.
General driver files have an OF matching table, which holds some compatible values. If the value of the compatible attribute of the device node is equal to any value in the OF matching table, then the device can use the driver. For example, in the file drvier/misc/memctrl.c:
Static struct of_device_id_xxx_memctrl_of_match [] = {{.clients = "xxxx,memctrl",}, {},}
Correspondingly, in arch/arm/boot/dts/xxx.dts:
Memctrl: memctrl {compatible = "xxxx,memctrl"; reg =; clocks =,; clock-names = "sdram_bandwidth_clk", "mem_axi_clk"; interrupts =; interrupt-controller; # interrupt-cells =;}
2.Model (model number)
Example:
Model = "fsl,MPC8349EMITX"
3.Phandle (pointer handle)
Example:
Pic@10000000 {phandle =; interrupt-controller;}
Defines the phandle value of 1. Another device node can reference a pic node with a phandle value of 1:
Another-device-node {interrupt-parent =;}
4.Status
5.#address-cells and # size-cells
# address-cells =; # size-cells =
Indicates that a U32 in the reg attribute represents address, and there is no data representing the size of reg, so: reg =; that is, the starting address of reg is 0x0, and its size is not described.
# address-cells =; # size-cells =
Indicates that there is a U32 for address and a U32 for size in the reg attribute, so: reg =; that is, the starting address of reg is 0x00000000, and the size is 0x00040000
6.Reg
Example: suppose the device in the system chip contains two register blocks, a 32-byte block in SOC that offsets 0x3000 and a 256-byte block that offsets 0xFE00. The reg attribute is encoded as follows (assuming # address-cells and # size-cells values are 1):
Reg=
7.virtual-reg
8.Ranges
Example:
Soc {compatible = "simple-bus"; # address-cells =; # size-cells =; ranges =; serial {device_type = "serial"; compatible = "ns16550"; reg =; clock-frequency =; interrupts =; interrupt-parent =;};}
The soc node specifies; this attribute value specifies that for the address space of the 1024KB range, the child node addressed at the physical 0x0 maps to the parent address of the physical 0xe0000000. With this mapping, serial device nodes can be addressed by loading or storing 0xe0004600 addresses, offsets of 0x4600 (specified in the registry), and 0xe0000000 mappings specified in the range.
9.dma-ranges
10.Name (deprecated)
11.device_type
1.3.4. Basic device node type
All device tree files should contain a root file, and all device tree files should have the following nodes under the root node:
1 / cpus nod
At least one / memory node
Instructions for use: r = required, O = optional, OR = optional but recommended, SD = see definition, all other standard attributes are acceptable but optional
1.3.4.1. Root node
Devicetree has a single root node, and all other device nodes are its descendants. The full path to the root node is /.
1.3.4.2. / aliases node
The device tree file may have an alias node (/ aliases) that defines one or more alias attributes. The alias node should be at the root of the device tree and have a node name / alias. Each attribute of the / aliases node has an alias defined. The attribute name specifies an alias. Property value specifies the full path of the node in the device tree. For example, the attribute serial0 = "/ simple-bus@fe000000/serial@llc500" defines the alias serial0. The naming rules for aliases are as follows:
1.3.4.3. / memory node
All device trees require memory device nodes and describe the physical memory layout of the system. If the system has multiple ranges of memory, you can create multiple memory nodes, or you can specify a range in the reg property of a single memory node.
The attribute requirements of the / memory node are as follows:
In xxx.dts
Memory {reg =; starting address 0x40000000 length 0x10000000 (32MB)}; 1.3.4.4. / chosen node
The / chosen node does not represent the actual devices in the system, but describes the parameters selected or specified by the system firmware at run time. It should be a child of the root node.
Example:
Chosen {bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";}; 1.3.4.5. / cpus Node attribute
The / cpus/cpu node is required for all device trees. It does not represent the real devices in the system, but serves as a container for the child cpu nodes that represent the cpu of the system.
1.3.5. Interrupt mapping
In the device tree, there is a logical interrupt tree that represents the hierarchy and routing of platform hardware interrupts. In the device tree, the interrupt-parent attribute is used to indicate the physical connection between the interrupt source and the interrupt controller. The device node that represents the interrupt contains an interrupt parent attribute that has a virtual value pointing to the device (usually the interrupt controller) to which the interrupt to the device is routed.
If the device causing the interrupt does not have an interrupt parent attribute, its interrupt parent node is assumed to be its device parent node. Each interrupt generating device contains an interrupt attribute whose value describes one or more interrupt sources of the device. Each source is represented by a so-called interrupt descriptor. The format and meaning of the interrupt descriptor are specific to the interrupt domain, that is, it depends on the attributes of the node on the root node of the interrupt domain. The root of the interrupt domain uses the # interrupt-cells attribute to define the number of values required to encode the interrupt descriptor.
The interrupt domain is the context in which the interrupt descriptor is interpreted. The root of the interrupt domain can be an interrupt controller (interrupt controller) or an interrupt connector (interrupt nexus):
The interrupt controller is a physical device and requires a driver to handle interrupts routed through it. It may also cascade to another interrupt domain. The interrupt controller is specified by interrupt-controller on that node in the device tree.
The interrupt connector defines the conversion between one interrupt domain and another. Translate information based on a specific domain and bus. Use the interrupt-map property to convert between domains. For example, the PCI controller device node can be an interrupt connector that defines the transformation from the PCI interrupt namespace (INTA, INTB, and so on) to an interrupt controller with an interrupt request (IRQ) number.
1.3.5.1. Interrupts
Example:
Interrupts = 1.3.5.2. Interrupt-parent
Example:
Interrupt-parent = 1.3.5.3. Interrupts-extended
Example:
Interrupts-extended =,; 1.3.5.4. # interrupt-cells
1.3.5.5. Interrupt-controller
1.4.Device Tree binary format
The Devicetree Blob (DTB) format is a flat binary encoding of Devicetree data. It is used to exchange device data between software programs. For example, when booting the operating system, the firmware passes a DTB to the operating system kernel.
The DTB format encodes devicetree data into a single, linear, pointer-free data structure. It consists of a subtitle, followed by three variable-sized parts: memory retention blocks, building blocks, and string blocks, which should appear in this order in a flat devicetree.
therefore. When loaded into memory by address, the device tree structure as a whole. Will be similar to the chart in the figure.
1.4.1. Dt_header
The header of the device tree is defined by the following C structure. All fields are 32-bit integers and are stored in big-endian format.
Struct fdt_header {this field should contain the value 0xd00dfeed (big-endian) uint32_t magic; / * magic word FDT_MAGIC * / this field should contain the total size (in bytes) of the device data structure. The size should include all parts of the structure: headers, memory reserved blocks, structure blocks and string blocks, as well as free space gaps between blocks or after the final block. Uint32_t totalsize; / * totalsize of DT block * / this field should contain the byte offset of the block from the title uint32_t off_dt_struct; / * offset to structure * / this field should contain the byte offset of the string block from the title uint32_t off_dt_strings; / * offset to strings * / this field should contain the byte offset uint32_t off_mem_rsvmap of the memory reserved block from the title / * offset to memory reserve map * / this field should contain the version of the device data structure uint32_t version; / * format version * / this field should contain the backward compatible minimum version data structure uint32_t last_comp_version; / * last compatible version * / / * version 2 fields below * / this field should contain the physical ID of the system boot CPU. It should be the same as the physical ID given in the reg attribute of the CPU node in the device tree / * Which physical CPU id we're booting on * / / * version 3 fields below * / this field should contain the byte length of the string block part uint32_t size_dt_strings; / * size of the strings block * / / * version 17 fields below * / this field should contain the byte length uint32_t size_dt_struct of the structure block portion / * size of the structure block * /}; 1.4.2. Memory reservation block
The memory retention block provides the client program with a list of reserved areas of physical memory that are not used for general memory allocation in order to protect important data structures from being overwritten by the client program. This area includes several reserve memory descriptors. Each reserve memory descriptor is made up of address and size. Both address and size are described in U64:
Struct fdt_reserve_entry {uint64_t address; uint64_t size;}; 1.4.3. Structure block
The building block describes the structure and content of the device tree itself. It consists of a number of fragments, and each slice begins with a token saved to describe the attributes and contents of the fragment.
FDT_BEGIN_NODE (0x00000001): this token describes the starting position of a node, and next to the token is node name (including unit address)
FDT_END_NODE (0x00000002): this token describes the end position of a node
FDT_PROP (0x00000003): this token describes the starting position of a property, followed by two U32 data. They are followed by specific attribute value data of length len.
Struct {uint32_t len; represents the size of the property value data. Uint32_t nameoff; represents the offset value of the attribute string in device tree strings block}
FDT_NOP (0x00000004): ignored by the program that parsed the device tree and can be used to override other properties to delete it
FDT_END (0x00000009): marks the end of the building block, so the building block of an DTB might be as follows:
(optionally) any number of FDT_NOP tokens FDT_BEGIN_NODE token:-node's name-- paddings For each property of the node:-- FDT_NOP (optionally)-- FDT_PROP token-- property all child nodes in this format (optionally) any number of FDT_NOP tokens FDT_END_NODE token1.4.4. Strings Block
A string table that defines the attributes used in each node. Because many properties appear in multiple node, all the property strings make up a string block. This compresses the size of DTB.
1.5.Linux parsing device tree
The device tree describes the details of the device, including numeric type, string type, and array type, which we need to obtain when writing the driver. The Linux kernel provides a series of functions that start with of_ to get device tree information, and the prototypes of these functions are defined in include/linux/of.h. Devices are hung on the device tree in the form of nodes, and the Linux kernel uses device_node structures to describe a node, which is defined in include/linux/of.h:
Struct device_node {const char * name; device node name const char * type; corresponds to the attribute of device_type phandle phandle; corresponds to the phandle attribute of the node const char * full_name; starts from "/", indicating the full path Struct property * properties; of the node. If you need to delete some attributes, kernel is not really deleted, but is attached to the list struct property * deadprops of deadprops. / * removed properties * / parent, child and sibling connect all device node together Struct device_node * parent; Struct device_node * child; Struct device_node * sibling;. The next node Struct device_node * next; / * next device of same type * / of the same type can be obtained through this pointer. / the next node struct device_node * allnext; / * next in list of all nodes * / struct kobject kobj; unsigned long _ flags of node global list can be obtained through this pointer. Void * data; # if defined (CONFIG_SPARC) const char * path_component_name; unsigned int unique_id; struct of_irq_controller * irq_trans; # endif}; 1.5.1. Find the OF function of the node 1.5.1.1. Of_find_node_by_name
Function: Find a node by its "name" property function
Struct device_node * of_find_node_by_name (struct device_node * from, const char * name)
Parameters:
@ from: the node to start looking for. If NULL means to look up the entire device tree from the root node. The name of the node that @ name:: is looking for.
Return value: the node found. If it is NULL, the lookup failed.
1.5.1.2. Of_find_node_by_path
Function: Find a node matching a full OF path function:
Struct device_node * of_find_node_by_path (const char * path)
Parameter: @ path: the complete matching path returns the value: the node found. If it is NULL, the search failed.
1.5.1.3. Of_find_node_by_type
Function Find a node by its "device_type" property function
Struct device_node * of_find_node_by_type (struct device_node * from, const char * type)
Parameters.
@ from: the node to start looking for. If NULL means to search the entire device tree from the root node @ type: the type of node to be found.
Returns the node found by the value. If it is NULL, the lookup failed.
1.5.1.4. Of_find_compatible_node
Function to find the specified node function through device_type and compatible
Struct device_node * of_find_compatible_node (struct device_node * from,const char * type, const char * compatible)
Parameters.
@ from: the node to start the search. If NULL means to find the entire device tree from the root node @ type: the node device_type attribute @ compatible: the compatible attribute list of the node
Returns the node found by the value. If it is NULL, the lookup failed.
1.5.1.5. Of_find_node_with_property
Function to find the specified node function through the attribute name
Struct device_node * of_find_node_with_property (struct device_node * from,const char * prop_name)
Parameters.
@ from: the node to start looking for. If NULL means to search the entire device tree from the root node @ type: the name of the node attribute to be found.
Returns the node found by the value. If it is NULL, the lookup failed.
1.5.2. Find the OF function of the parent / child node 1.5.2.1. Of_get_parent
The function function is used to get the parent node (if any) of the specified node
Struct device_node * of_get_parent (const struct device_node * node)
Parameters.
@ node: the node to find the parent node
Returns the parent node found by the value
1.5.2.2. Of_get_next_available_child
Function to get child nodes and skip the node function of status = "disabled"
Struct device_node * of_get_next_available_child (const struct device_node * node,struct device_node * prev)
Parameters.
@ node: parent node @ prev: the last child node of the current parent node. If empty, the first child node is obtained.
Returns the child node found by the value
1.5.3. OF function for extracting attribute value
The Linux kernel uses struct property to hold the attributes of nodes, which are defined in / include/linux/of.h:
The name of the struct property {char * name; attribute the length of the int length; attribute the value of the void * value; attribute struct property * next; the next attribute unsigned long _ flags; unsigned int unique_id; struct bin_attribute attr;}; 1.5.3.1. Of_find_property
Function to find the specified property function
Struct property * of_find_property (const struct device_node * np, const char * name, int * lenp)
Parameters.
@ np: device node @ name: attribute name @ lenp: number of bytes of the attribute
Returns the property found by the value
1.5.3.2. Read array data of types U8, U16, U32, and U64 in attributes
When sz is set to 1, a data is read, which is also encapsulated by the Linux kernel.
Int of_property_read_u8_array (const struct device_node * np, const char * propname, U8 * out_values, size_t sz) int of_property_read_u16_array (const struct device_node * np, const char * propname, U16 * out_values, size_t sz) int of_property_read_u32_array (const struct device_node * np, const char * propname, U32 * out_values Size_t sz) int of_property_read_u64 (const struct device_node * np, const char * propname, U64 * out_value) 1.5.3.3. Of_property_read_string
Function to find and read the attribute string function
Int of_property_read_string (struct device_node * np, const char * propname,const char * * out_string)
Parameters.
@ np: device node @ propname: attribute name @ out_string: read string
Return value
0: read successfully-EINVAL: attribute does not exist-ENODATA: attribute does not have this value-EILSEQ: the string does not end with the empty character'\ 0'. Device tree parsing process 2.1. The kernel starts and gets the device tree
When uboot boots the kernel, it passes the physical starting memory address of the device tree in physical memory to the Linux kernel, and then the Linux kernel parses the device image in unflattern_device_tree and uses the scanned information to create a linked list made up of device node. The global variable of_allnodes points to the root node of the linked list, and each node of the device tree is corresponding to it by a struct device_node. Unflatten_device_tree means to unlock the device tree, and the function _ _ unflatten_device_tree is called in this function:
/ * * _ unflatten_device_tree-create tree of device_nodes from flat blob * * unflattens a device-tree, creating the * tree of struct device_node. It also fills the "name" and "type" * pointers of the nodes so the normal device-tree walking functions * can be used. * @ blob: The blob to expand * @ mynodes: The device_node tree created by the call * @ dt_alloc: An allocator that provides a virtual address to memory * for the resulting tree * / static void _ _ unflatten_device_tree (struct boot_param_header * blob, struct device_node * * mynodes, void * (* dt_alloc) (U64 size, U64 align))
So, so far, we have a struct * device_node named of_allnodes, which points to the expanded device_node tree of the device tree, and the subsequent operations are based on the device_node tree.
2.2. Create platform_device
The process from startup to device creation of the kernel is roughly as follows: in do_initcalls, level is passed to do_initcall_level to call initialization functions at different levels. The corresponding relationship of level is shown in line 196 of linux-3.10/include/linux/init.h. During this initialization process, a function of customize_machine is called.
2.3.Platform driver registration process
This section analyzes the registration process of Platform driver, taking memctrl-driven registration as an example. For the process analysis of the system call driver initialization function, refer to the chapter on automatic initialization mechanism. The analysis of this chapter starts with the xxx_init function of the device driver file.
2.3.1. Struct platform_driver
Platform_driver is a layer of encapsulation on top of device_driver, which is structured as follows:
Struct platform_driver {int (* probe) (struct platform_device *); probe function int (* remove) (struct platform_device *); execute void (* shutdown) (struct platform_device *) when driver is uninstalled; execute function int (* suspend) (struct platform_device *, pm_message_t state) when shutdown; suspend function int (* resume) (struct platform_device *); restore function struct device_driver driver The managed driver object const struct platform_device_id * id_table; is matched using}; 2.3.2. Struct device_driver
Struct device_driver is the basic driver structure provided by the system:
Struct device_driver {const char * name; driver name struct bus_type * bus; bus struct module * owner; module owner const char * mod_name; built-in module uses bool suppress_bind_attrs; to bind to sysfs const struct of_device_id * of_match_table; device tree matching table const struct acpi_device_id * acpi_match_table; ACPI matching table int (* probe) (struct device * dev) Probe device int (* remove) (struct device * dev); call void (* shutdown) (struct device * dev) when detached from the device; turn off device int (* suspend) (struct device * dev, pm_message_t state) when shutting down; call int (* resume) (struct device * dev) when putting the device into sleep mode; call const struct attribute_group * * groups when waking up the device Automatically created default attribute group const struct dev_pm_ops * pm; device power management struct driver_private * p; driver private data}; 2.3.3. Platform_driver_register
The registration interface of Platform_driver is platform_driver_register, which is defined as follows:
Int platform_driver_register (struct platform_driver * drv) {drv- > driver.bus = & platform_bus_type; sets the bus type if (drv- > probe) confirms that the probe function drv- > driver.probe = platform_drv_probe; is actually called by drv's probe function if (drv- > remove) drv- > driver.remove = platform_drv_remove; if (drv- > shutdown) drv- > driver.shutdown = platform_drv_shutdown Return driver_register (& drv- > driver);
The platform_driver_register interface makes some preparations for registering the bus driver, defines the bus type, sets part of the interface of driver, and finally driver_register registers the driver with the bus.
2.3.4. Driver_registerint driver_register (struct device_driver * drv) {int ret; struct device_driver * other; BUG_ON (! drv- > bus- > p); if ((drv- > bus- > probe & & drv- > probe) | | (drv- > bus- > remove & & drv- > remove) | (drv- > bus- > shutdown & & drv- > shutdown) printk (KERN_WARNING "Driver'% s'needs updating-please use"bus_type methods\ n", drv- > name) Other = driver_find (drv- > name, drv- > bus); check whether the driver has registered if (other) {printk (KERN_ERR "Error: Driver'% s'is already registered,"aborting...\ n", drv- > name); return-EBUSY;} ret = bus_add_driver (drv); driver_register 's main work is here if (ret) return ret; ret = driver_add_groups (drv, drv- > groups) Mainly add driver attributes if (ret) {bus_remove_driver (drv); return ret;} kobject_uevent (& drv- > p-> kobj, KOBJ_ADD) in sysfs; when it comes to uevent, don't analyze return ret;} 2.3.5 for the time being. Bus_add_driver
From the above analysis, the registration of drivers focuses on the bus_add_driver () function, which adds drivers to the bus:
Drivers/base/bus.c int bus_add_driver (struct device_driver * drv) {struct bus_type * bus; struct driver_private * priv; contains driver-related kobject and klist structures int error = 0; bus = bus_get (drv- > bus); get the bus type if (! bus) return-EINVAL; pr_debug ("bus:'% s': add driver% s\ n", bus- > name, drv- > name) to which the device belongs Priv = kzalloc (sizeof (* priv), GFP_KERNEL); if (! priv) {error =-ENOMEM; goto out_put_bus;} klist_init (& priv- > klist_devices, NULL, NULL); priv- > driver = drv; drv- > p = priv; priv- > kobj.kset = bus- > p-> drivers_kset; error = kobject_init_and_add (& priv- > kobj, & driver_ktype, NULL, "% s", drv- > name) If (error) goto out_unregister; klist_add_tail (& priv- > knode_bus, & bus- > p-> klist_drivers); if (drv- > bus- > p-> drivers_autoprobe) {if automatic detection error = driver_attach (drv); if (error) goto out_unregister;} module_add_driver (drv- > owner, drv); error = driver_create_file (drv, & driver_attr_uevent) If (error) {printk (KERN_ERR "% s: uevent attr (% s) failed\ n", _ _ func__, drv- > name);} error = driver_add_attrs (bus, drv); if (error) {/ * How the hell do we get out of this pickle? Give up * / printk (KERN_ERR "% s: driver_add_attrs (% s) failed\ n", _ _ func__, drv- > name);} if (! drv- > suppress_bind_attrs) {error = add_bind_files (drv); if (error) {/ * Ditto * / printk (KERN_ERR "% s: add_bind_files (% s) failed\ n", _ _ func__, drv- > name);}} return 0 Out_unregister: kobject_put (& priv- > kobj); kfree (drv- > p); drv- > p = NULL; out_put_bus: bus_put (bus); return error;} 2.3.6. Driver_attach
Driver_attach tries to bind the device and the driver. Compile all the devices on the bus, but the drivers try to match one by one. If driver_probe_device () returns 0 and @ dev- > driver is set, it means that a pair of compatible device drivers have been found.
Int driver_attach (struct device_driver * drv) {return bus_for_each_dev (drv- > bus, NULL, drv, _ _ driver_attach);} EXPORT_SYMBOL_GPL (driver_attach); 2.3.7. _ _ driver_attach
For each device on the bus, driver_attach calls _ _ driver_attach to try to match the driver.
Static int _ driver_attach (struct device * dev, void * data) {struct device_driver * drv = data; / * * Lock device and try to bind to it. We drop the error * here and always return 0, because we need to keep trying * to bind to devices and some drivers will return an error * simply if it didn't support the device. * * driver_probe_device () will spit a warning if there * is an error. * / if (! driver_match_device (drv, dev)) matches the device and driver, which is called platform_match return 0; if (dev- > parent) / * Needed for USB * / device_lock (dev- > parent); device_lock (dev) Set mutex to prevent other processes from accessing the device resource if (! dev- > driver) if the device does not have a driver, it is the device probe driver. This function is the same function called by the registered device driver_probe_device (drv, dev); device_unlock (dev); if (dev- > parent) device_unlock (dev- > parent); return 0;}
Call the really_probe function in driver_probe_device, and call the probe function in the driver file in really_probe. For the memctrl driver, it is the xxxx_memctrl_probe function. At this point, platfprm driver is registered.
The matching principle of 2.4.Platform Bus
From the above code analysis, _ _ device_attach-> driver_match_device will be called when registering platform device, and _ _ driver_attach-> driver_match_device will be called when registering platform driver, that is, this function will be called by both devices and drivers:
Static inline int driver_match_device (struct device_driver * drv, struct device * dev) {return drv- > bus- > match? Drv- > bus- > match (dev, drv): 1;}
Drv- > bus- > match, which is the matching function provided by the bus of the driver binding, where the platform bus device is registered, while the definition of the platform bus refers to 3.2.6 platform_bus_type. The match function corresponding to Platform is: platform_match:
Static int platform_match (struct device * dev, struct device_driver * drv) {struct platform_device * pdev = to_platform_device (dev); struct platform_driver * pdrv = to_platform_driver (drv); / * Attempt an OF style match first * / if (of_driver_match_device (dev, drv)) return 1; / * Then try ACPI style match * / if (acpi_driver_match_device (dev, drv) return 1 / * Then try to match against the id table * / if (pdrv- > id_table) return platform_match_id (pdrv- > id_table, pdev)! = NULL; / * fall-back to driver name match * / return (strcmp (pdev- > name, drv- > name) = 0);} 2.4.1. Of_driver_match_device
Judge whether there is a driver matching it according to the of_match_table of the driver. For memctrl drivers, the of_match_table is as follows:
Static struct of_device_id xxxx_memctrl_of_match [] = {{.clients = "xxxx,memctrl",}, {},}
The execution process of of_driver_match_device is as follows:
So the focus should be on the _ _ of_match_node function:
2.4.1.1. _ _ of_match_nodestatic const struct of_device_id * _ of_match_node (const struct of_device_id * matches, const struct device_node * node) {if (! matches) return NULL; while (matches- > name [0] | | matches- > type [0] | | matches- > compatible [0]) {int match = 1; if (matches- > name [0]) looks up the name match & = node- > name &! strcmp (matches- > name, node- > name) If (matches- > type [0]) looks up the type match & = node- > type & &! strcmp (matches- > type, node- > type); if (matches- > compatible [0]) looks for attributes to check whether the compatible of the node is consistent with the drive match & = _ of_device_is_compatible (node, matches- > compatible); if (match) return matches; matches++;} return NULL;} 3. Use equipment resources 4. Automatic initialization mechanism 4.1. Compiled to kernel 4.1.1. Module_ in Macro deployment
Each module in Linux has one and only one module_init function, which is defined as follows:
/ * * module_init ()-driver initialization entry point * @ x: function to be run at kernel boot time or module insertion * * module_init () will either be called during do_initcalls () (if * builtin) or at module insertion time (if a module). There can only * be one per module. * / # define module_init (x) _ initcall (x)
_ _ initcall (x) is defined as follows:
# define _ initcall (fn) device_initcall (fn)
Device_initcall (fn) is defined as follows:
# define device_initcall (fn) _ _ define_initcall (fn, 6)
_ _ define_initcall is defined as follows:
/ * initcalls are now grouped by functionality into separate * subsections. Ordering inside the subsections is determined * by link order. * For backwards compatibility, initcall () puts the call in * the device init subsection. * * The `id' arg to _ _ define_initcall () is needed so that multiple initcalls * can point at the same handler without causing duplicate-symbol build errors. * / # define_ _ define_initcall (fn, id)\ static initcall_t _ _ initcall_##fn##id _ _ used\ _ _ attribute__ ((_ _ section__ (".initcall" # id ".init")) = fn
Initcalls is now grouped into separate subsections by function. The order within the subsections is determined by the link order. For backward compatibility, initcall () places the call in the device init section. You need to define the 'id' parameter of initcall () so that multiple initcall can point to the same handler without causing duplicate symbol build errors. If you don't understand the usage of the above code, you can refer to the section usage of _ _ attribute__ and the usage of # and # # in the C language macro definition. So expanding _ _ define_initcall would be the following:
Suppose _ _ define_initcall (led_init, 6) Static initcall_t _ _ initcall_led_init6 _ _ used\ _ _ attribute__ ((_ _ section__ (".initcall6.init") = led_init
That is, a function pointer variable of type initcall_t, _ _ initcall_led_init6, is defined and assigned to led_init, which is linked to section (.initcall6.init) when linked.
4.1.2. Link script
In linux3.10/arch/arm/kernel/vmlinux.lds.S:
. SECTIONS / * line 54 * / {. .init.data: {/ * line 202 * / # ifndef CONFIG_XIP_KERNEL INIT_DATA # endif INIT_SETUP (16) INIT_CALLS CON_INITCALL SECURITY_INITCALL INIT_RAM_FS}. }
In linux3.10/include/asm-generic/vmlinux.lds.h:
# define VMLINUX_SYMBOL (x) _ VMLINUX_SYMBOL (x) # define _ VMLINUX_SYMBOL (x) x. / * line 664 * / # define INIT_CALLS_LEVEL (level)\ VMLINUX_SYMBOL (_ _ initcall##level##_start) =.;\ * (.initcall # # level##.init)\ * (.initcall # # level##s.init)\ # define INIT_CALLS\ VMLINUX_SYMBOL (_ _ initcall_start) =. \ * (.initcallearly.init)\ INIT_CALLS_LEVEL (0)\ INIT_CALLS_LEVEL (1)\ INIT_CALLS_LEVEL (2)\ INIT_CALLS_LEVEL (3)\ INIT_CALLS_LEVEL (4)\ INIT_CALLS_LEVEL (5)\ INIT_CALLS_LEVEL (rootfs)\ INIT_CALLS_LEVEL (6)\ INIT_CALLS_LEVEL (7)\ VMLINUX_SYMBOL (_ _ initcall_end) =. .
So INIT_CALLS_LEVEL (6) will expand into:
_ _ initcall6_start =.; * (.initcall6.init) * (.initcall6s.init)
So _ _ initcall_led_init6 will link to
Section (.initcall6.init) 4.1.3. Initialization
The kernel startup process is as follows:
The main contents of do_initcall_level are as follows:
/ * linux3.10/init/main.c line 744 * / static void _ init do_initcall_level (int level) {. For (fn = initcall_ levels; fn
< initcall_levels[level+1]; fn++) do_one_initcall(*fn); } 由代码可知,内核会依次调用level段存储的初始化函数。比如对于模块来说level等于6。 4.2.动态加载的模块(.ko)4.2.1. Module_init展开 如果设置为编译成动态加载的模块(.ko),module_init的展开形式与编译到内核不一样。 /* Each module must use one module_init(). */ #define module_init(initfn) \ static inline initcall_t __inittest(void) \ 检查定义的函数是否符合initcall_t类型 { return initfn; } \ int init_module(void) __attribute__((alias(#initfn))); alias属性是GCC的特有属性,将定义init_module为函数initfn的别名,所以module_init(initfn)的作用就是定义一个变量名 init_module,其地址和initfn是一样的。 4.2.2. *mod.c文件 编译成module的模块都会自动产生一个*.mod.c的文件,例如: struct module __this_module __attribute__((section(".gnu.linkonce.this_module"))) = { .name = KBUILD_MODNAME, .init = init_module, #ifdef CONFIG_MODULE_UNLOAD .exit = cleanup_module, #endif .arch = MODULE_ARCH_INIT, }; 即定义了一个类型为module的全局变量__this_module,其成员.init就是上文由module_init定义的init_module变量。并且__this_module会被链接到 section(".gnu.linkonce.this_module")。 4.2.3. 动态加载 insmod是busybox提供的用户层命令:路径busybox/modutils/ insmod.c insmod_main bb_init_module init_module 路径busybox/modutils/modutils.c: #define init_module(mod, len, opts) .\ syscall(__NR_init_module, mod, len, opts)该系统调用对应内核层的sys_init_module函数 路径:kernel/module.c SYSCALL_DEFINE3(init_module,…) //加载模块的ko文件,并解释各个section,重定位 mod = load_module(umod, len, uargs); //查找section(".gnu.linkonce.this_module") modindex = find_sec(hdr, sechdrs, secstrings,".gnu.linkonce.this_module"); //找到Hello_module.mod.c定义的module数据结构 mod = (void *)sechdrs[modindex].sh_addr; if (mod->Init! = NULL) ret = do_one_initcall (mod- > init); / / call the section usage of initfn.4.3.__attribute__
_ _ define_initcall uses the section subkey of many attributes of gcc's _ _ attribute__, which is used as follows:
_ attribute__ ((_ _ section__ ("section_name")
Its function is to put the function or data into a specified segment named "section_name".
4.4. The usage of # and # # in C language macro definition is 4.4.1. General usage
We use # to change the macro parameter into a string.
# define PRINT (FORMAT,VALUE)\ printf ("The value of" # VALUE "is" FORMAT "\ n", VALUE)
Call: printf ("% d", Xero3);-- > print: The value of Xero3 is 20
This is because "The value of" # VALUE "is" FORMAT "\ n" actually contains "The value of", # VALUE, "is", FORMAT, "\ n" five-part string, where VALUE and FORMAT are replaced by the actual value of the macro parameter.
Use # # to fit two macro parameters together
# define ADD_TO_SUM (sum_number,val) sum##sum_bumber+= (val)
Call: ADD_TO_SUM (2100);-- > print: sum2+= (100)
It should be noted that where the macro definition is useful'#'or'# 'where the macro parameter is no longer expanded.
4.4.2. Some special cases of'# 'and' #'
1. Merge anonymous variable names
# define _ _ ANONYMOUS1 (type, var, line) type var##line # define _ _ ANONYMOUS0 (type, line) _ _ ANONYMOUS1 (type, _ anonymous, line) # define ANONYMOUS (type) _ ANONYMOUS0 (type, _ LINE__)
Example: ANONYMOUS (static int); that is, static int _ anonymous70; 70 represents the line number; the first layer: ANONYMOUS (static int);-- > _ ANONYMOUS0 (static int, LINE); the second layer:-- > _ _ ANONYMOUS1 (static int, _ anonymous, 70); the third layer:-- > static int _ anonymous70;, that is, you can only unlock the macro of the current layer at a time, so _ _ LINE__ can be unlocked at the second layer.
two。 Filling structure
# define FILL (a) {a, # a} enum IDD {OPEN, CLOSE}; typedef struct MSG {IDD id; const char msg;} MSG; MSG _ msg [] = {FILL (OPEN), FILL (CLOSE)}
Equivalent to:
MSG _ msg [] = {{OPEN, OPEN}, {CLOSE, CLOSE}}
3. Record file name
# define _ GET_FILE_NAME (f) # f # define GET_FILE_NAME (f) _ GET_FILE_NAME (f) static char FILE_NAME [] = GET_FILE_NAME (_ _ FILE__)
4. Get the string buffer size corresponding to a numeric type
# define _ TYPE_BUF_SIZE (type) sizeof # type # define TYPE_BUF_SIZE (type) _ TYPE_BUF_SIZE (type) char buf [type _ BUF_SIZE (INT_MAX)];-- char buf [_ TYPE_BUF_SIZE (0x7fffffff)];-- char buf [sizeof 0x7fffffff]
This is equivalent to:
Char buf [11]; the above is the unified model of how to make Linux driver 11-Linux device driver shared by the editor. If you happen to have similar doubts, please refer to the above analysis to understand. If you want to know more about it, you are welcome to follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.