1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.hardware.deviceTree;
7
8 overlayType = types.submodule {
9 options = {
10 name = mkOption {
11 type = types.str;
12 description = lib.mdDoc ''
13 Name of this overlay
14 '';
15 };
16
17 filter = mkOption {
18 type = types.nullOr types.str;
19 default = null;
20 example = "*rpi*.dtb";
21 description = lib.mdDoc ''
22 Only apply to .dtb files matching glob expression.
23 '';
24 };
25
26 dtsFile = mkOption {
27 type = types.nullOr types.path;
28 description = lib.mdDoc ''
29 Path to .dts overlay file, overlay is applied to
30 each .dtb file matching "compatible" of the overlay.
31 '';
32 default = null;
33 example = literalExpression "./dts/overlays.dts";
34 };
35
36 dtsText = mkOption {
37 type = types.nullOr types.str;
38 default = null;
39 description = lib.mdDoc ''
40 Literal DTS contents, overlay is applied to
41 each .dtb file matching "compatible" of the overlay.
42 '';
43 example = ''
44 /dts-v1/;
45 /plugin/;
46 / {
47 compatible = "raspberrypi";
48 };
49 &{/soc} {
50 pps {
51 compatible = "pps-gpio";
52 status = "okay";
53 };
54 };
55 '';
56 };
57
58 dtboFile = mkOption {
59 type = types.nullOr types.path;
60 default = null;
61 description = lib.mdDoc ''
62 Path to .dtbo compiled overlay file.
63 '';
64 };
65 };
66 };
67
68 filterDTBs = src: if cfg.filter == null
69 then "${src}/dtbs"
70 else
71 pkgs.runCommand "dtbs-filtered" {} ''
72 mkdir -p $out
73 cd ${src}/dtbs
74 find . -type f -name '${cfg.filter}' -print0 \
75 | xargs -0 cp -v --no-preserve=mode --target-directory $out --parents
76 '';
77
78 filteredDTBs = filterDTBs cfg.kernelPackage;
79
80 # Compile single Device Tree overlay source
81 # file (.dts) into its compiled variant (.dtbo)
82 compileDTS = name: f: pkgs.callPackage({ stdenv, dtc }: stdenv.mkDerivation {
83 name = "${name}-dtbo";
84
85 nativeBuildInputs = [ dtc ];
86
87 buildCommand = ''
88 $CC -E -nostdinc -I${getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes -undef -D__DTS__ -x assembler-with-cpp ${f} | \
89 dtc -I dts -O dtb -@ -o $out
90 '';
91 }) {};
92
93 # Fill in `dtboFile` for each overlay if not set already.
94 # Existence of one of these is guarded by assertion below
95 withDTBOs = xs: flip map xs (o: o // { dtboFile =
96 if o.dtboFile == null then
97 if o.dtsFile != null then compileDTS o.name o.dtsFile
98 else compileDTS o.name (pkgs.writeText "dts" o.dtsText)
99 else o.dtboFile; } );
100
101in
102{
103 imports = [
104 (mkRemovedOptionModule [ "hardware" "deviceTree" "base" ] "Use hardware.deviceTree.kernelPackage instead")
105 ];
106
107 options = {
108 hardware.deviceTree = {
109 enable = mkOption {
110 default = pkgs.stdenv.hostPlatform.linux-kernel.DTB or false;
111 type = types.bool;
112 description = lib.mdDoc ''
113 Build device tree files. These are used to describe the
114 non-discoverable hardware of a system.
115 '';
116 };
117
118 kernelPackage = mkOption {
119 default = config.boot.kernelPackages.kernel;
120 defaultText = literalExpression "config.boot.kernelPackages.kernel";
121 example = literalExpression "pkgs.linux_latest";
122 type = types.path;
123 description = lib.mdDoc ''
124 Kernel package containing the base device-tree (.dtb) to boot. Uses
125 device trees bundled with the Linux kernel by default.
126 '';
127 };
128
129 name = mkOption {
130 default = null;
131 example = "some-dtb.dtb";
132 type = types.nullOr types.str;
133 description = lib.mdDoc ''
134 The name of an explicit dtb to be loaded, relative to the dtb base.
135 Useful in extlinux scenarios if the bootloader doesn't pick the
136 right .dtb file from FDTDIR.
137 '';
138 };
139
140 filter = mkOption {
141 type = types.nullOr types.str;
142 default = null;
143 example = "*rpi*.dtb";
144 description = lib.mdDoc ''
145 Only include .dtb files matching glob expression.
146 '';
147 };
148
149 overlays = mkOption {
150 default = [];
151 example = literalExpression ''
152 [
153 { name = "pps"; dtsFile = ./dts/pps.dts; }
154 { name = "spi";
155 dtsText = "...";
156 }
157 { name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
158 ]
159 '';
160 type = types.listOf (types.coercedTo types.path (path: {
161 name = baseNameOf path;
162 filter = null;
163 dtboFile = path;
164 }) overlayType);
165 description = lib.mdDoc ''
166 List of overlays to apply to base device-tree (.dtb) files.
167 '';
168 };
169
170 package = mkOption {
171 default = null;
172 type = types.nullOr types.path;
173 internal = true;
174 description = lib.mdDoc ''
175 A path containing the result of applying `overlays` to `kernelPackage`.
176 '';
177 };
178 };
179 };
180
181 config = mkIf (cfg.enable) {
182
183 assertions = let
184 invalidOverlay = o: (o.dtsFile == null) && (o.dtsText == null) && (o.dtboFile == null);
185 in lib.singleton {
186 assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
187 message = ''
188 deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
189 Offending overlay(s):
190 ${toString (map (o: o.name) (builtins.filter invalidOverlay cfg.overlays))}
191 '';
192 };
193
194 hardware.deviceTree.package = if (cfg.overlays != [])
195 then pkgs.deviceTree.applyOverlays filteredDTBs (withDTBOs cfg.overlays)
196 else filteredDTBs;
197 };
198}