1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.hardware.deviceTree;
9
10 overlayType = lib.types.submodule {
11 options = {
12 name = lib.mkOption {
13 type = lib.types.str;
14 description = ''
15 Name of this overlay
16 '';
17 };
18
19 filter = lib.mkOption {
20 type = lib.types.nullOr lib.types.str;
21 default = null;
22 example = "*rpi*.dtb";
23 description = ''
24 Only apply to .dtb files matching glob expression.
25 '';
26 };
27
28 dtsFile = lib.mkOption {
29 type = lib.types.nullOr lib.types.path;
30 description = ''
31 Path to .dts overlay file, overlay is applied to
32 each .dtb file matching "compatible" of the overlay.
33 '';
34 default = null;
35 example = lib.literalExpression "./dts/overlays.dts";
36 };
37
38 dtsText = lib.mkOption {
39 type = lib.types.nullOr lib.types.str;
40 default = null;
41 description = ''
42 Literal DTS contents, overlay is applied to
43 each .dtb file matching "compatible" of the overlay.
44 '';
45 example = ''
46 /dts-v1/;
47 /plugin/;
48 / {
49 compatible = "raspberrypi";
50 };
51 &{/soc} {
52 pps {
53 compatible = "pps-gpio";
54 status = "okay";
55 };
56 };
57 '';
58 };
59
60 dtboFile = lib.mkOption {
61 type = lib.types.nullOr lib.types.path;
62 default = null;
63 description = ''
64 Path to .dtbo compiled overlay file.
65 '';
66 };
67 };
68 };
69
70 filterDTBs =
71 src:
72 if cfg.filter == null then
73 src
74 else
75 pkgs.runCommand "dtbs-filtered" { } ''
76 mkdir -p $out
77 cd ${src}
78 find . -type f -name '${cfg.filter}' -print0 \
79 | xargs -0 cp -v --no-preserve=mode --target-directory $out --parents
80 '';
81
82 filteredDTBs = filterDTBs cfg.dtbSource;
83
84 # Fill in `dtboFile` for each overlay if not set already.
85 # Existence of one of these is guarded by assertion below
86 withDTBOs =
87 xs:
88 lib.flip map xs (
89 o:
90 o
91 // {
92 dtboFile =
93 let
94 includePaths = [
95 "${lib.getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes"
96 ]
97 ++ cfg.dtboBuildExtraIncludePaths;
98 extraPreprocessorFlags = cfg.dtboBuildExtraPreprocessorFlags;
99 in
100 if o.dtboFile == null then
101 let
102 dtsFile = if o.dtsFile == null then (pkgs.writeText "dts" o.dtsText) else o.dtsFile;
103 in
104 pkgs.deviceTree.compileDTS {
105 name = "${o.name}-dtbo";
106 inherit includePaths extraPreprocessorFlags dtsFile;
107 }
108 else
109 o.dtboFile;
110 }
111 );
112
113in
114{
115 imports = [
116 (lib.mkRemovedOptionModule [
117 "hardware"
118 "deviceTree"
119 "base"
120 ] "Use hardware.deviceTree.kernelPackage instead")
121 ];
122
123 options = {
124 hardware.deviceTree = {
125 enable = lib.mkOption {
126 default = pkgs.stdenv.hostPlatform.linux-kernel.DTB or false;
127 type = lib.types.bool;
128 description = ''
129 Build device tree files. These are used to describe the
130 non-discoverable hardware of a system.
131 '';
132 };
133
134 kernelPackage = lib.mkOption {
135 default = config.boot.kernelPackages.kernel;
136 defaultText = lib.literalExpression "config.boot.kernelPackages.kernel";
137 example = lib.literalExpression "pkgs.linux_latest";
138 type = lib.types.path;
139 description = ''
140 Kernel package where device tree include directory is from. Also used as default source of dtb package to apply overlays to
141 '';
142 };
143
144 dtboBuildExtraPreprocessorFlags = lib.mkOption {
145 default = [ ];
146 example = lib.literalExpression "[ \"-DMY_DTB_DEFINE\" ]";
147 type = lib.types.listOf lib.types.str;
148 description = ''
149 Additional flags to pass to the preprocessor during dtbo compilations
150 '';
151 };
152
153 dtboBuildExtraIncludePaths = lib.mkOption {
154 default = [ ];
155 example = lib.literalExpression ''
156 [
157 ./my_custom_include_dir_1
158 ./custom_include_dir_2
159 ]
160 '';
161 type = lib.types.listOf lib.types.path;
162 description = ''
163 Additional include paths that will be passed to the preprocessor when creating the final .dts to compile into .dtbo
164 '';
165 };
166
167 dtbSource = lib.mkOption {
168 default = "${cfg.kernelPackage}/dtbs";
169 defaultText = lib.literalExpression "\${cfg.kernelPackage}/dtbs";
170 type = lib.types.path;
171 description = ''
172 Path to dtb directory that overlays and other processing will be applied to. Uses
173 device trees bundled with the Linux kernel by default.
174 '';
175 };
176
177 name = lib.mkOption {
178 default = null;
179 example = "some-dtb.dtb";
180 type = lib.types.nullOr lib.types.str;
181 description = ''
182 The name of an explicit dtb to be loaded, relative to the dtb base.
183 Useful in extlinux scenarios if the bootloader doesn't pick the
184 right .dtb file from FDTDIR.
185 '';
186 };
187
188 filter = lib.mkOption {
189 type = lib.types.nullOr lib.types.str;
190 default = null;
191 example = "*rpi*.dtb";
192 description = ''
193 Only include .dtb files matching glob expression.
194 '';
195 };
196
197 overlays = lib.mkOption {
198 default = [ ];
199 example = lib.literalExpression ''
200 [
201 { name = "pps"; dtsFile = ./dts/pps.dts; }
202 { name = "spi";
203 dtsText = "...";
204 }
205 { name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
206 ]
207 '';
208 type = lib.types.listOf (
209 lib.types.coercedTo lib.types.path (path: {
210 name = baseNameOf path;
211 filter = null;
212 dtboFile = path;
213 }) overlayType
214 );
215 description = ''
216 List of overlays to apply to base device-tree (.dtb) files.
217 '';
218 };
219
220 package = lib.mkOption {
221 default = null;
222 type = lib.types.nullOr lib.types.path;
223 internal = true;
224 description = ''
225 A path containing the result of applying `overlays` to `kernelPackage`.
226 '';
227 };
228 };
229 };
230
231 config = lib.mkIf (cfg.enable) {
232
233 assertions =
234 let
235 invalidOverlay = o: (o.dtsFile == null) && (o.dtsText == null) && (o.dtboFile == null);
236 in
237 lib.singleton {
238 assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
239 message = ''
240 deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
241 Offending overlay(s):
242 ${toString (map (o: o.name) (builtins.filter invalidOverlay cfg.overlays))}
243 '';
244 };
245
246 hardware.deviceTree.package =
247 if (cfg.overlays != [ ]) then
248 pkgs.deviceTree.applyOverlays filteredDTBs (withDTBOs cfg.overlays)
249 else
250 filteredDTBs;
251 };
252}