Skip to content

Commit ff1f752

Browse files
ulfalizergalak
authored andcommitted
dts: edtlib: Support giving missing properties a default value
For missing optional properties, it can be handy to generate a default value instead of no value, to cut down on #ifdefs. Allow a default value to be specified in the binding, via a new 'default: <default value>' setting for properties in bindings. Defaults are supported for both scalar and array types. YAML arrays are used to specify the value for array types. 'default:' also appears in json-schema, with the same meaning. Include misc. sanity checks, like the 'default' value matching 'type'. The documentation changes in binding-template.yaml explain the syntax. Suggested by Peter A. Bigot in #17829. Fixes: #17829 Signed-off-by: Ulf Magnusson <[email protected]>
1 parent c333e8e commit ff1f752

File tree

5 files changed

+191
-12
lines changed

5 files changed

+191
-12
lines changed

dts/binding-template.yaml

+38
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ sub-node:
8787
# ...
8888
# - <itemN>
8989
# const: <string | int>
90+
# default: <default>
9091
#
9192
# 'type: uint8-array' is for what the device tree specification calls
9293
# 'bytestring'. Properties of type 'uint8-array' should be set like this:
@@ -121,6 +122,18 @@ sub-node:
121122
# foo = <&label1 &label2>, <&label3 &label4>; // Okay for 'type: phandles'
122123
# foo = <&label1 1 2>, <&label2 3 4>; // Okay for 'type: phandle-array'
123124
# etc.
125+
#
126+
# The optional 'default:' setting gives a value that will be used if the
127+
# property is missing from the device tree node. If 'default: <default>' is
128+
# given for a property <prop> and <prop> is missing, then the output will be as
129+
# if '<prop> = <default>' had appeared (except YAML data types are used for the
130+
# default value).
131+
#
132+
# Note that it only makes sense to combine 'default:' with 'required: false'.
133+
# Combining it with 'required: true' will raise an error.
134+
#
135+
# See below for examples of 'default:'. Putting 'default:' on any property type
136+
# besides those used in the examples will raise an error.
124137
properties:
125138
# Describes a property like 'current-speed = <115200>;'. We pretend that
126139
# it's obligatory for the example node and set 'required: true'.
@@ -154,6 +167,31 @@ properties:
154167
required: true
155168
const: 1
156169

170+
int-with-default:
171+
type: int
172+
required: false
173+
default: 123
174+
175+
array-with-default:
176+
type: array
177+
required: false
178+
default: [1, 2, 3] # Same as 'array-with-default = <1 2 3>'
179+
180+
string-with-default:
181+
type: string
182+
required: false
183+
default: "foo"
184+
185+
string-array-with-default:
186+
type: string-array
187+
required: false
188+
default: ["foo", "bar"] # Same as 'string-array-with-default = "foo", "bar"'
189+
190+
uint8-array-with-default:
191+
type: uint8-array
192+
required: false
193+
default: [0x12, 0x34] # Same as 'uint8-array-with-default = [12 34]'
194+
157195
# If the binding describes an interrupt controller, GPIO controller, pinmux
158196
# device, or any other node referenced by other nodes, then #cells should be
159197
# given.

scripts/dts/edtlib.py

+99-12
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,8 @@ def _init_prop(self, name, options):
591591

592592
val = self._prop_val(
593593
name, prop_type,
594-
options.get("required") or options.get("category") == "required")
594+
options.get("required") or options.get("category") == "required",
595+
options.get("default"))
595596

596597
if val is None:
597598
# 'required: false' property that wasn't there, or a property type
@@ -629,8 +630,21 @@ def _init_prop(self, name, options):
629630

630631
self.props[name] = prop
631632

632-
def _prop_val(self, name, prop_type, required):
633+
def _prop_val(self, name, prop_type, required, default):
633634
# _init_prop() helper for getting the property's value
635+
#
636+
# name:
637+
# Property name from binding
638+
#
639+
# prop_type:
640+
# Property type from binding (a string like "int")
641+
#
642+
# optional:
643+
# True if the property isn't required to exist
644+
#
645+
# default:
646+
# Default value to use when the property doesn't exist, or None if
647+
# the binding doesn't give a default value
634648

635649
node = self._node
636650
prop = node.props.get(name)
@@ -650,6 +664,15 @@ def _prop_val(self, name, prop_type, required):
650664
"does not appear in {!r}".format(
651665
name, self.binding_path, node))
652666

667+
if default is not None:
668+
# YAML doesn't have a native format for byte arrays. We need to
669+
# convert those from an array like [0x12, 0x34, ...]. The
670+
# format has already been checked in
671+
# _check_prop_type_and_default().
672+
if prop_type == "uint8-array":
673+
return bytes(default)
674+
return default
675+
653676
return None
654677

655678
if prop_type == "int":
@@ -683,14 +706,13 @@ def _prop_val(self, name, prop_type, required):
683706
.format(name, node.path, node.dt.filename, prop))
684707
return None
685708

686-
if prop_type == "compound":
687-
# Dummy type for properties like that don't fit any of the patterns
688-
# above, so that we can require all entries in 'properties:' to
689-
# have a 'type: ...'. No Property object is created for it.
690-
return None
691-
692-
_err("'{}' in 'properties:' in {} has unknown type '{}'"
693-
.format(name, self.binding_path, prop_type))
709+
# prop_type == "compound". We have already checked that the 'type:'
710+
# value is valid, in _check_binding().
711+
#
712+
# 'compound' is a dummy type for properties that don't fit any of the
713+
# patterns above, so that we can require all entries in 'properties:'
714+
# to have a 'type: ...'. No Property object is created for it.
715+
return None
694716

695717
def _init_regs(self):
696718
# Initializes self.regs
@@ -1315,7 +1337,6 @@ def _check_binding(binding, binding_path):
13151337
if prop not in binding:
13161338
_err("missing '{}' property in {}".format(prop, binding_path))
13171339

1318-
for prop in "title", "description":
13191340
if not isinstance(binding[prop], str) or not binding[prop]:
13201341
_err("missing, malformed, or empty '{}' in {}"
13211342
.format(prop, binding_path))
@@ -1356,7 +1377,7 @@ def _check_binding_properties(binding, binding_path):
13561377
return
13571378

13581379
ok_prop_keys = {"description", "type", "required", "category",
1359-
"constraint", "enum", "const"}
1380+
"constraint", "enum", "const", "default"}
13601381

13611382
for prop_name, options in binding["properties"].items():
13621383
for key in options:
@@ -1373,6 +1394,11 @@ def _check_binding_properties(binding, binding_path):
13731394
key, prop_name, binding_path,
13741395
", ".join(ok_prop_keys)))
13751396

1397+
_check_prop_type_and_default(
1398+
prop_name, options.get("type"),
1399+
options.get("required") or options.get("category") == "required",
1400+
options.get("default"), binding_path)
1401+
13761402
if "required" in options and not isinstance(options["required"], bool):
13771403
_err("malformed 'required:' setting '{}' for '{}' in 'properties' "
13781404
"in {}, expected true/false"
@@ -1392,6 +1418,67 @@ def _check_binding_properties(binding, binding_path):
13921418
.format(binding_path, prop_name))
13931419

13941420

1421+
def _check_prop_type_and_default(prop_name, prop_type, required, default,
1422+
binding_path):
1423+
# _check_binding() helper. Checks 'type:' and 'default:' for the property
1424+
# named 'prop_name'
1425+
1426+
if prop_type is None:
1427+
_err("missing 'type:' for '{}' in 'properties' in {}"
1428+
.format(prop_name, binding_path))
1429+
1430+
ok_types = {"boolean", "int", "array", "uint8-array", "string",
1431+
"string-array", "phandle", "phandles", "phandle-array",
1432+
"compound"}
1433+
1434+
if prop_type not in ok_types:
1435+
_err("'{}' in 'properties:' in {} has unknown type '{}', expected one "
1436+
"of {}".format(prop_name, binding_path, prop_type,
1437+
", ".join(ok_types)))
1438+
1439+
# Check default
1440+
1441+
if default is None:
1442+
return
1443+
1444+
if required:
1445+
_err("'default:' for '{}' in 'properties:' in {} is meaningless in "
1446+
"combination with 'required: true'"
1447+
.format(prop_name, binding_path))
1448+
1449+
if prop_type in {"boolean", "compound", "phandle", "phandles",
1450+
"phandle-array"}:
1451+
_err("'default:' can't be combined with 'type: {}' for '{}' in "
1452+
"'properties:' in {}".format(prop_type, prop_name, binding_path))
1453+
1454+
def ok_default():
1455+
# Returns True if 'default' is an okay default for the property's type
1456+
1457+
if prop_type == "int" and isinstance(default, int) or \
1458+
prop_type == "string" and isinstance(default, str):
1459+
return True
1460+
1461+
# array, uint8-array, or string-array
1462+
1463+
if not isinstance(default, list):
1464+
return False
1465+
1466+
if prop_type == "array" and \
1467+
all(isinstance(val, int) for val in default):
1468+
return True
1469+
1470+
if prop_type == "uint8-array" and \
1471+
all(isinstance(val, int) and 0 <= val <= 255 for val in default):
1472+
return True
1473+
1474+
# string-array
1475+
return all(isinstance(val, str) for val in default)
1476+
1477+
if not ok_default():
1478+
_err("'default: {}' is invalid for '{}' in 'properties:' in {}, which "
1479+
"has type {}".format(default, prop_name, binding_path, prop_type))
1480+
1481+
13951482
def _translate(addr, node):
13961483
# Recursively translates 'addr' on 'node' to the address space(s) of its
13971484
# parent(s), by looking at 'ranges' properties. Returns the translated
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
3+
title: Property default value test
4+
description: Property default value test
5+
6+
compatible: "defaults"
7+
8+
properties:
9+
int:
10+
type: int
11+
required: false
12+
default: 123
13+
14+
array:
15+
type: array
16+
required: false
17+
default: [1, 2, 3]
18+
19+
uint8-array:
20+
type: uint8-array
21+
required: false
22+
default: [0x89, 0xAB, 0xCD]
23+
24+
string:
25+
type: string
26+
required: false
27+
default: "hello"
28+
29+
string-array:
30+
type: string-array
31+
required: false
32+
default: ["hello", "there"]
33+
34+
default-not-used:
35+
type: int
36+
required: false
37+
default: 123

scripts/dts/test.dts

+10
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,16 @@
311311
};
312312
};
313313

314+
//
315+
// For testing Device.props with 'default:' values in binding
316+
//
317+
318+
defaults {
319+
compatible = "defaults";
320+
// Should override the 'default:' in the binding
321+
default-not-used = <234>;
322+
};
323+
314324
//
315325
// Parent with 'sub-node:' in binding
316326
//

scripts/dts/testedtlib.py

+7
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ def verify_streq(actual, expected):
122122
verify_streq(edt.get_dev("/props").props,
123123
r"{'nonexistent-boolean': <Property, name: nonexistent-boolean, type: boolean, value: False>, 'existent-boolean': <Property, name: existent-boolean, type: boolean, value: True>, 'int': <Property, name: int, type: int, value: 1>, 'array': <Property, name: array, type: array, value: [1, 2, 3]>, 'uint8-array': <Property, name: uint8-array, type: uint8-array, value: b'\x124'>, 'string': <Property, name: string, type: string, value: 'foo'>, 'string-array': <Property, name: string-array, type: string-array, value: ['foo', 'bar', 'baz']>, 'phandle-ref': <Property, name: phandle-ref, type: phandle, value: <Device /props/node in 'test.dts', no binding>>, 'phandle-refs': <Property, name: phandle-refs, type: phandles, value: [<Device /props/node in 'test.dts', no binding>, <Device /props/node2 in 'test.dts', no binding>]>}")
124124

125+
#
126+
# Test property default values given in bindings
127+
#
128+
129+
verify_streq(edt.get_dev("/defaults").props,
130+
r"{'int': <Property, name: int, type: int, value: 123>, 'array': <Property, name: array, type: array, value: [1, 2, 3]>, 'uint8-array': <Property, name: uint8-array, type: uint8-array, value: b'\x89\xab\xcd'>, 'string': <Property, name: string, type: string, value: 'hello'>, 'string-array': <Property, name: string-array, type: string-array, value: ['hello', 'there']>, 'default-not-used': <Property, name: default-not-used, type: int, value: 234>}")
131+
125132
#
126133
# Test having multiple directories with bindings, with a different .dts file
127134
#

0 commit comments

Comments
 (0)