Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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)

AltStyle によって変換されたページ (->オリジナル) /