Skip to content

Commit 3627ad8

Browse files
committed
Add Device Tree section
This commit adds 'devicetree.c' and 'dt-overlay.dts', demonstrating the platform driver and DT interaction. Also, it adds comprehensive Device Tree section to documentation covering: * Introduction to Device Tree concepts * Integration with kernel modules * Property reading examples * Testing methods and common functions Close #35
1 parent 78a7265 commit 3627ad8

File tree

4 files changed

+274
-0
lines changed

4 files changed

+274
-0
lines changed

examples/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ obj-m += vkbd.o
3333
obj-m += static_key.o
3434
obj-m += led.o
3535
obj-m += dht11.o
36+
obj-m += devicetree.o
3637

3738
PWD := $(CURDIR)
3839

examples/devicetree.c

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/* devicetree.c - Demonstrates device tree interaction with kernel modules */
2+
3+
#include <linux/init.h>
4+
#include <linux/kernel.h>
5+
#include <linux/module.h>
6+
#include <linux/of.h>
7+
#include <linux/of_device.h>
8+
#include <linux/platform_device.h>
9+
10+
#define DRIVER_NAME "lkmpg_devicetree"
11+
12+
/* Structure to hold device-specific data */
13+
struct dt_device_data {
14+
const char *label;
15+
u32 reg_value;
16+
u32 custom_value;
17+
bool has_clock;
18+
};
19+
20+
/* Probe function - called when device tree node matches */
21+
static int dt_probe(struct platform_device *pdev)
22+
{
23+
struct device *dev = &pdev->dev;
24+
struct device_node *np = dev->of_node;
25+
struct dt_device_data *data;
26+
const char *string_prop;
27+
u32 value;
28+
int ret;
29+
30+
pr_info("%s: Device tree probe called for %s\n", DRIVER_NAME,
31+
np->full_name);
32+
33+
/* Allocate memory for device data */
34+
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
35+
if (!data)
36+
return -ENOMEM;
37+
38+
/* Read a string property */
39+
ret = of_property_read_string(np, "label", &string_prop);
40+
if (ret == 0) {
41+
data->label = string_prop;
42+
pr_info("%s: Found label property: %s\n", DRIVER_NAME, data->label);
43+
} else {
44+
data->label = "unnamed";
45+
pr_info("%s: No label property found, using default\n", DRIVER_NAME);
46+
}
47+
48+
/* Read a u32 property */
49+
ret = of_property_read_u32(np, "reg", &value);
50+
if (ret == 0) {
51+
data->reg_value = value;
52+
pr_info("%s: Found reg property: 0x%x\n", DRIVER_NAME, data->reg_value);
53+
}
54+
55+
/* Read a custom u32 property */
56+
ret = of_property_read_u32(np, "lkmpg,custom-value", &value);
57+
if (ret == 0) {
58+
data->custom_value = value;
59+
pr_info("%s: Found custom-value property: %u\n", DRIVER_NAME,
60+
data->custom_value);
61+
} else {
62+
data->custom_value = 42; /* Default value */
63+
pr_info("%s: No custom-value found, using default: %u\n", DRIVER_NAME,
64+
data->custom_value);
65+
}
66+
67+
/* Check for presence of a property */
68+
data->has_clock = of_property_read_bool(np, "lkmpg,has-clock");
69+
pr_info("%s: has-clock property: %s\n", DRIVER_NAME,
70+
data->has_clock ? "present" : "absent");
71+
72+
/* Store device data for later use */
73+
platform_set_drvdata(pdev, data);
74+
75+
pr_info("%s: Device probe successful\n", DRIVER_NAME);
76+
return 0;
77+
}
78+
79+
/* Remove function - called when device is removed */
80+
static int dt_remove(struct platform_device *pdev)
81+
{
82+
struct dt_device_data *data = platform_get_drvdata(pdev);
83+
84+
pr_info("%s: Removing device %s\n", DRIVER_NAME, data->label);
85+
/* Cleanup is handled automatically by devm_* functions */
86+
return 0;
87+
}
88+
89+
/* Device tree match table - defines compatible strings this driver supports */
90+
static const struct of_device_id dt_match_table[] = {
91+
{
92+
.compatible = "lkmpg,example-device",
93+
},
94+
{
95+
.compatible = "lkmpg,another-device",
96+
},
97+
{} /* Sentinel */
98+
};
99+
MODULE_DEVICE_TABLE(of, dt_match_table);
100+
101+
/* Platform driver structure */
102+
static struct platform_driver dt_driver = {
103+
.probe = dt_probe,
104+
.remove = dt_remove,
105+
.driver = {
106+
.name = DRIVER_NAME,
107+
.of_match_table = dt_match_table,
108+
},
109+
};
110+
111+
/* Module initialization */
112+
static int __init dt_init(void)
113+
{
114+
int ret;
115+
116+
pr_info("%s: Initializing device tree example module\n", DRIVER_NAME);
117+
118+
/* Register the platform driver */
119+
ret = platform_driver_register(&dt_driver);
120+
if (ret) {
121+
pr_err("%s: Failed to register platform driver\n", DRIVER_NAME);
122+
return ret;
123+
}
124+
125+
pr_info("%s: Module loaded successfully\n", DRIVER_NAME);
126+
return 0;
127+
}
128+
129+
/* Module cleanup */
130+
static void __exit dt_exit(void)
131+
{
132+
pr_info("%s: Cleaning up device tree example module\n", DRIVER_NAME);
133+
platform_driver_unregister(&dt_driver);
134+
}
135+
136+
module_init(dt_init);
137+
module_exit(dt_exit);
138+
139+
MODULE_LICENSE("GPL");
140+
MODULE_DESCRIPTION("Device tree interaction example for LKMPG");

examples/dt-overlay.dts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Device Tree Overlay for LKMPG Device Tree Example
3+
*
4+
* This overlay can be compiled and loaded on systems that support
5+
* runtime device tree overlays (like Raspberry Pi).
6+
*
7+
* Compile with:
8+
* dtc -@ -I dts -O dtb -o dt-overlay.dtbo dt-overlay.dts
9+
*
10+
* Load with (on Raspberry Pi):
11+
* sudo dtoverlay dt-overlay.dtbo
12+
*/
13+
14+
/dts-v1/;
15+
/plugin/;
16+
17+
/ {
18+
compatible = "brcm,bcm2835";
19+
20+
fragment@0 {
21+
target-path = "/";
22+
__overlay__ {
23+
lkmpg_device@0 {
24+
compatible = "lkmpg,example-device";
25+
reg = <0x40000000 0x1000>;
26+
label = "LKMPG Test Device";
27+
lkmpg,custom-value = <100>;
28+
lkmpg,has-clock;
29+
status = "okay";
30+
};
31+
32+
lkmpg_device@1 {
33+
compatible = "lkmpg,another-device";
34+
reg = <0x40001000 0x1000>;
35+
label = "LKMPG Secondary Device";
36+
lkmpg,custom-value = <200>;
37+
/* no has-clock property for this one */
38+
status = "okay";
39+
};
40+
};
41+
};
42+
};

lkmpg.tex

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2233,6 +2233,97 @@ \section{Standardizing the interfaces: The Device Model}
22332233

22342234
\samplec{examples/devicemodel.c}
22352235

2236+
\section{Device Tree}
2237+
\label{sec:device_tree}
2238+
\subsection{Introduction to Device Tree}
2239+
\label{sec:dt_intro}
2240+
Device Tree is a data structure that describes hardware components in a system, particularly in embedded systems and ARM-based platforms.
2241+
Instead of hard-coding hardware details in the kernel source, Device Tree provides a separate, human-readable description that the kernel can parse at boot time.
2242+
This separation allows the same kernel binary to support multiple hardware platforms,
2243+
making development and maintenance significantly easier.
2244+
2245+
Device Tree files (with \verb|.dts| extension for source files and \verb|.dtb| for compiled binary files) use a hierarchical structure similar to a filesystem to represent the hardware topology.
2246+
Each hardware component is represented as a node with properties that describe its characteristics,
2247+
such as memory addresses, interrupt numbers, and device-specific parameters.
2248+
2249+
\subsection{Device Tree and Kernel Modules}
2250+
\label{sec:dt_modules}
2251+
While Device Tree is primarily used during kernel initialization, kernel modules can also interact with Device Tree nodes through the platform device framework.
2252+
When the kernel parses the Device Tree at boot, it creates platform devices for nodes that have compatible strings.
2253+
Kernel modules can then register platform drivers that match these compatible strings, allowing them to be automatically probed when the corresponding hardware is detected.
2254+
2255+
The key concepts for Device Tree interaction in kernel modules include:
2256+
\begin{itemize}
2257+
\item \textbf{Compatible strings}: Unique identifiers that match Device Tree nodes to their drivers
2258+
\item \textbf{Property reading}: Functions to extract configuration data from Device Tree nodes
2259+
\item \textbf{Platform driver framework}: Infrastructure for binding drivers to devices described in Device Tree
2260+
\item \textbf{Device-specific data}: Custom properties that can be defined for specific hardware
2261+
\end{itemize}
2262+
2263+
\subsection{Example: Device Tree Module}
2264+
\label{sec:dt_example}
2265+
The following example demonstrates how a kernel module can interact with Device Tree nodes.
2266+
This module registers a platform driver that matches specific compatible strings and extracts properties from the matched Device Tree nodes.
2267+
2268+
\samplec{examples/devicetree.c}
2269+
2270+
\subsection{Device Tree Source Example}
2271+
\label{sec:dt_source}
2272+
To use the above module, you would need a Device Tree entry like this:
2273+
2274+
\begin{code}
2275+
/* Example device tree fragment */
2276+
lkmpg_device@0 {
2277+
compatible = "lkmpg,example-device";
2278+
reg = <0x40000000 0x1000>;
2279+
label = "LKMPG Test Device";
2280+
lkmpg,custom-value = <100>;
2281+
lkmpg,has-clock;
2282+
};
2283+
\end{code}
2284+
2285+
The properties in this Device Tree node would be read by the module's probe function when the device is matched.
2286+
The \verb|compatible| property is used to match the device with the driver, while other properties provide device-specific configuration.
2287+
2288+
\subsection{Testing Device Tree Modules}
2289+
\label{sec:dt_testing}
2290+
Testing Device Tree modules can be done in several ways:
2291+
2292+
\begin{enumerate}
2293+
\item \textbf{Using Device Tree overlays}: On systems that support it (like Raspberry Pi), you can load Device Tree overlays at runtime to add new devices without rebooting.
2294+
2295+
\item \textbf{Modifying the main Device Tree}: Add your device nodes to the system's main Device Tree source file and recompile it.
2296+
2297+
\item \textbf{Using QEMU}: For development and testing, QEMU can emulate systems with custom Device Trees, allowing you to test your modules without physical hardware.
2298+
\end{enumerate}
2299+
2300+
To check if your device was properly detected, you can examine the sysfs filesystem:
2301+
2302+
\begin{codebash}
2303+
# List all platform devices
2304+
ls /sys/bus/platform/devices/
2305+
2306+
# Check device tree nodes
2307+
ls /proc/device-tree/
2308+
\end{codebash}
2309+
2310+
\subsection{Common Device Tree Functions}
2311+
\label{sec:dt_functions}
2312+
Here are some commonly used Device Tree functions in kernel modules:
2313+
2314+
\begin{itemize}
2315+
\item \cpp|of_property_read_string()| - Read a string property
2316+
\item \cpp|of_property_read_u32()| - Read a 32-bit integer property
2317+
\item \cpp|of_property_read_bool()| - Check if a boolean property exists
2318+
\item \cpp|of_find_property()| - Find a property by name
2319+
\item \cpp|of_get_property()| - Get a property's raw value
2320+
\item \cpp|of_match_device()| - Match a device against a match table
2321+
\item \cpp|of_parse_phandle()| - Parse a phandle reference to another node
2322+
\end{itemize}
2323+
2324+
These functions provide a robust interface for extracting configuration data from Device Tree nodes,
2325+
allowing modules to be highly configurable without code changes.
2326+
22362327
\section{Optimizations}
22372328
\label{sec:optimization}
22382329
\subsection{Likely and Unlikely conditions}

0 commit comments

Comments
 (0)