Skip to content

Commit 3bce62e

Browse files
authored
Merge pull request #329 from sysprog21/device-tree
Add Device Tree section
2 parents c1817fb + 06cd0f4 commit 3bce62e

File tree

4 files changed

+285
-0
lines changed

4 files changed

+285
-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
KDIR ?= /lib/modules/$(shell uname -r)/build
3839
PWD := $(CURDIR)

examples/devicetree.c

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

22532253
\samplec{examples/devicemodel.c}
22542254

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

0 commit comments

Comments
 (0)