dts: dtlib/edtlib: Add phandle and phandle+nums array types
Add two new type-checked property types 'phandles' and 'phandle-array'
to edtlib.
'phandles' is for pure lists of phandles, with no other data, like
foo = < &bar &baz ... >
'phandle-array' is for lists of phandles and (possibly) numbers, like
foo = < &bar 1 2 &baz 3 4 ... >
dt-schema also has the 'phandle-array' type.
Property.val (in edtlib) is set to an array of Device objects for the
'phandles' type.
For the 'phandle-array' type, no Property object is created. This type
is only used for type checking.
Also refactor how types that do not create a Property object
('phandle-array' and 'compound') are handled. Have _prop_val() return
None for them.
The new types are implemented with two new TYPE_PHANDLES and
TYPE_PHANDLES_AND_NUMS types at the dtlib level. There is also a new
Property.to_nodes() functions for fetching the Nodes for an array of
phandles, with type checking.
Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
diff --git a/scripts/dts/dtlib.py b/scripts/dts/dtlib.py
index 8dbdd28..e77f66a 100644
--- a/scripts/dts/dtlib.py
+++ b/scripts/dts/dtlib.py
@@ -1359,24 +1359,28 @@
assignment. This is one of the following constants (with example
assignments):
- Assignment | Property.type
- -------------------+------------------------
- foo; | dtlib.TYPE_EMPTY
- foo = [] | dtlib.TYPE_BYTES
- foo = [01 02] | dtlib.TYPE_BYTES
- foo = /bits/ 8 <1> | dtlib.TYPE_BYTES
- foo = <1> | dtlib.TYPE_NUM
- foo = <> | dtlib.TYPE_NUMS
- foo = <1 2 3> | dtlib.TYPE_NUMS
- foo = <1 2>, <3> | dtlib.TYPE_NUMS
- foo = "foo" | dtlib.TYPE_STRING
- foo = "foo", "bar" | dtlib.TYPE_STRINGS
- foo = <&label> | dtlib.TYPE_PHANDLE
- foo = &label | dtlib.TYPE_PATH
- *Anything else* | dtlib.TYPE_COMPOUND
+ Assignment | Property.type
+ ----------------------------+------------------------
+ foo; | dtlib.TYPE_EMPTY
+ foo = []; | dtlib.TYPE_BYTES
+ foo = [01 02]; | dtlib.TYPE_BYTES
+ foo = /bits/ 8 <1>; | dtlib.TYPE_BYTES
+ foo = <1>; | dtlib.TYPE_NUM
+ foo = <>; | dtlib.TYPE_NUMS
+ foo = <1 2 3>; | dtlib.TYPE_NUMS
+ foo = <1 2>, <3>; | dtlib.TYPE_NUMS
+ foo = "foo"; | dtlib.TYPE_STRING
+ foo = "foo", "bar"; | dtlib.TYPE_STRINGS
+ foo = <&l>; | dtlib.TYPE_PHANDLE
+ foo = <&l1 &l2 &l3>; | dtlib.TYPE_PHANDLES
+ foo = <&l1 &l2>, <&l3>; | dtlib.TYPE_PHANDLES
+ foo = <&l1 1 2 &l2 3 4>; | dtlib.TYPE_PHANDLES_AND_NUMS
+ foo = <&l1 1 2>, <&l2 3 4>; | dtlib.TYPE_PHANDLES_AND_NUMS
+ foo = &l; | dtlib.TYPE_PATH
+ *Anything else* | dtlib.TYPE_COMPOUND
- *Anything else* includes properties mixing (<&label>) and node path
- (&label) references with other data.
+ *Anything else* includes properties mixing phandle (<&label>) and node
+ path (&label) references with other data.
Data labels in the property value do not influence the type.
@@ -1550,6 +1554,34 @@
return self.node.dt.phandle2node[int.from_bytes(self.value, "big")]
+ def to_nodes(self):
+ """
+ Returns a list with the Nodes the phandles in the property point to.
+
+ Raises DTError if the property value contains anything other than
+ phandles. All of the following are accepted:
+
+ foo = < >
+ foo = < &bar >;
+ foo = < &bar &baz ... >;
+ foo = < &bar ... >, < &baz ... >;
+ """
+ def type_ok():
+ if self.type in (TYPE_PHANDLE, TYPE_PHANDLES):
+ return True
+ # Also accept 'foo = < >;'
+ return self.type is TYPE_NUMS and not self.value
+
+ if not type_ok():
+ raise DTError("expected property '{0}' on {1} in {2} to be "
+ "assigned with '{0} = < &foo &bar ... >;', not '{3}'"
+ .format(self.name, self.node.path,
+ self.node.dt.filename, self))
+
+ return [self.node.dt.phandle2node[int.from_bytes(self.value[i:i + 4],
+ "big")]
+ for i in range(0, len(self.value), 4)]
+
def to_path(self):
"""
Returns the Node referenced by the path stored in the property.
@@ -1617,6 +1649,13 @@
if types == [_TYPE_UINT32, _REF_PHANDLE] and len(self.value) == 4:
return TYPE_PHANDLE
+ if set(types) == {_TYPE_UINT32, _REF_PHANDLE}:
+ if len(self.value) == 4*types.count(_REF_PHANDLE):
+ # Array with just phandles in it
+ return TYPE_PHANDLES
+ # Array with both phandles and numbers
+ return TYPE_PHANDLES_AND_NUMS
+
return TYPE_COMPOUND
def __str__(self):
@@ -1765,7 +1804,9 @@
TYPE_STRINGS = 5
TYPE_PATH = 6
TYPE_PHANDLE = 7
-TYPE_COMPOUND = 8
+TYPE_PHANDLES = 8
+TYPE_PHANDLES_AND_NUMS = 9
+TYPE_COMPOUND = 10
def _check_is_bytes(data):
diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py
index a6e6ca7..9959d14 100644
--- a/scripts/dts/edtlib.py
+++ b/scripts/dts/edtlib.py
@@ -29,7 +29,8 @@
import yaml
-from dtlib import DT, DTError, to_num, to_nums, TYPE_EMPTY
+from dtlib import DT, DTError, to_num, to_nums, TYPE_EMPTY, TYPE_PHANDLE, \
+ TYPE_PHANDLES_AND_NUMS
# NOTE: testedtlib.py is the test suite for this library. It can be run
# directly.
@@ -532,17 +533,11 @@
if not prop_type:
_err("'{}' in {} lacks 'type'".format(name, self.binding_path))
- # "Dummy" type for properties like '...-gpios', so that we can require
- # all entries in 'properties:' to have a 'type: ...'. It might make
- # sense to have gpios in 'properties:' for other reasons, e.g. to set
- # 'category: required'.
- if prop_type == "compound":
- return
-
val = self._prop_val(name, prop_type,
options.get("category") == "optional")
if val is None:
- # 'category: optional' property that wasn't there
+ # 'category: optional' property that wasn't there, or a property
+ # type for which we store no data.
return
enum = options.get("enum")
@@ -616,6 +611,25 @@
if prop_type == "phandle":
return self.edt._node2dev[prop.to_node()]
+ if prop_type == "phandles":
+ return [self.edt._node2dev[node] for node in prop.to_nodes()]
+
+ if prop_type == "phandle-array":
+ # This property type only does a type check. No Property object is
+ # created for it.
+ if prop.type not in (TYPE_PHANDLE, TYPE_PHANDLES_AND_NUMS):
+ _err("expected property '{0}' in {1} in {2} to be assigned "
+ "with '{0} = < &foo 1 2 ... &bar 3 4 ... >' (a mix of "
+ "phandles and numbers), not '{3}'"
+ .format(name, node.path, node.dt.filename, prop))
+ return None
+
+ if prop_type == "compound":
+ # Dummy type for properties like that don't fit any of the patterns
+ # above, so that we can require all entries in 'properties:' to
+ # have a 'type: ...'. No Property object is created for it.
+ return None
+
_err("'{}' in 'properties:' in {} has unknown type '{}'"
.format(name, self.binding_path, prop_type))
@@ -1007,7 +1021,9 @@
Represents a property on a Device, as set in its DT node and with
additional info from the 'properties:' section of the binding.
- Only properties mentioned in 'properties:' get created.
+ Only properties mentioned in 'properties:' get created. Properties with
+ type 'phandle-array' or type 'compound' do not get Property instance. These
+ types only exist for type checking.
These attributes are available on Property objects:
@@ -1023,8 +1039,12 @@
val:
The value of the property, with the format determined by the 'type:' key
- from the binding. For 'type: phandle' properties, this is the pointed-to
- Device instance.
+ from the binding.
+
+ For 'type: phandle' properties, this is the pointed-to Device instance.
+
+ For 'type: phandles' properties, this is a list of the pointed-to Device
+ instances.
enum_index:
The index of the property's value in the 'enum:' list in the binding, or
diff --git a/scripts/dts/test-bindings/props.yaml b/scripts/dts/test-bindings/props.yaml
index e248c7a..bf45e30 100644
--- a/scripts/dts/test-bindings/props.yaml
+++ b/scripts/dts/test-bindings/props.yaml
@@ -33,3 +33,9 @@
phandle-ref:
type: phandle
+
+ phandle-refs:
+ type: phandles
+
+ phandle-refs-and-vals:
+ type: phandle-array
diff --git a/scripts/dts/test.dts b/scripts/dts/test.dts
index e90a615..5cb4e9d 100644
--- a/scripts/dts/test.dts
+++ b/scripts/dts/test.dts
@@ -297,12 +297,17 @@
string = "foo";
string-array = "foo", "bar", "baz";
phandle-ref = < &{/props/node} >;
+ phandle-refs = < &{/props/node} &{/props/node2} >;
+ phandle-refs-and-vals = < &{/props/node} 1 &{/props/node2} 2 >;
// Does not appear in the binding, so won't create an entry in
// Device.props
not-speced = <0>;
node {
};
+
+ node2 {
+ };
};
//
diff --git a/scripts/dts/testdtlib.py b/scripts/dts/testdtlib.py
index 1fd2648..029b865 100755
--- a/scripts/dts/testdtlib.py
+++ b/scripts/dts/testdtlib.py
@@ -1451,13 +1451,17 @@
nums4 = < 1 2 >, < 3 >, < 4 >;
string = "foo";
strings = "foo", "bar";
- phandle1 = < &node >;
- phandle2 = < &{/node} >;
path1 = &node;
path2 = &{/node};
+ phandle1 = < &node >;
+ phandle2 = < &{/node} >;
+ phandles1 = < &node &node >;
+ phandles2 = < &node >, < &node >;
+ phandle-and-nums-1 = < &node 1 >;
+ phandle-and-nums-2 = < &node 1 2 &node 3 4 >;
+ phandle-and-nums-3 = < &node 1 2 >, < &node 3 4 >;
compound1 = < 1 >, [ 02 ];
compound2 = "foo", < >;
- compound3 = < 1 &{/node} 2>;
node: node {
};
@@ -1479,11 +1483,15 @@
verify_type("strings", dtlib.TYPE_STRINGS)
verify_type("phandle1", dtlib.TYPE_PHANDLE)
verify_type("phandle2", dtlib.TYPE_PHANDLE)
+ verify_type("phandles1", dtlib.TYPE_PHANDLES)
+ verify_type("phandles2", dtlib.TYPE_PHANDLES)
+ verify_type("phandle-and-nums-1", dtlib.TYPE_PHANDLES_AND_NUMS)
+ verify_type("phandle-and-nums-2", dtlib.TYPE_PHANDLES_AND_NUMS)
+ verify_type("phandle-and-nums-3", dtlib.TYPE_PHANDLES_AND_NUMS)
verify_type("path1", dtlib.TYPE_PATH)
verify_type("path2", dtlib.TYPE_PATH)
verify_type("compound1", dtlib.TYPE_COMPOUND)
verify_type("compound2", dtlib.TYPE_COMPOUND)
- verify_type("compound3", dtlib.TYPE_COMPOUND)
#
# Test Property.to_{num,nums,string,strings,node}()
@@ -1511,6 +1519,8 @@
strings = "foo", "bar", "baz";
invalid_strings = "foo", "\xff", "bar";
ref = <&{/target}>;
+ refs = <&{/target} &{/target2}>;
+ refs2 = <&{/target}>, <&{/target2}>;
path = &{/target};
manualpath = "/target";
missingpath = "/missing";
@@ -1518,6 +1528,9 @@
target {
phandle = < 100 >;
};
+
+ target2 {
+ };
};
""")
@@ -1717,6 +1730,38 @@
verify_to_node_error("u", "expected property 'u' on / in .tmp.dts to be assigned with 'u = < &foo >;', not 'u = < 0x1 >;'")
verify_to_node_error("string", "expected property 'string' on / in .tmp.dts to be assigned with 'string = < &foo >;', not 'string = \"foo\\tbar baz\";'")
+ # Test Property.to_nodes()
+
+ def verify_to_nodes(prop, paths):
+ try:
+ actual = [node.path for node in dt.root.props[prop].to_nodes()]
+ except dtlib.DTError as e:
+ fail("failed to convert '{}' to nodes: {}".format(prop, e))
+
+ if actual != paths:
+ fail("expected {} to point to the paths {}, pointed to {}"
+ .format(prop, paths, actual))
+
+ def verify_to_nodes_error(prop, msg):
+ prefix = "expected converting '{}' to a nodes to generate the error " \
+ "'{}', generated".format(prop, msg)
+ try:
+ dt.root.props[prop].to_nodes()
+ fail(prefix + " no error")
+ except dtlib.DTError as e:
+ if str(e) != msg:
+ fail("{} the error '{}'".format(prefix, e))
+ except Exception as e:
+ fail("{} the non-DTError '{}'".format(prefix, e))
+
+ verify_to_nodes("zero", [])
+ verify_to_nodes("ref", ["/target"])
+ verify_to_nodes("refs", ["/target", "/target2"])
+ verify_to_nodes("refs2", ["/target", "/target2"])
+
+ verify_to_nodes_error("u", "expected property 'u' on / in .tmp.dts to be assigned with 'u = < &foo &bar ... >;', not 'u = < 0x1 >;'")
+ verify_to_nodes_error("string", "expected property 'string' on / in .tmp.dts to be assigned with 'string = < &foo &bar ... >;', not 'string = \"foo\\tbar baz\";'")
+
# Test Property.to_path()
def verify_to_path(prop, path):
diff --git a/scripts/dts/testedtlib.py b/scripts/dts/testedtlib.py
index 449aae7..40d1cfc 100755
--- a/scripts/dts/testedtlib.py
+++ b/scripts/dts/testedtlib.py
@@ -120,7 +120,7 @@
#
verify_streq(edt.get_dev("/props").props,
- r"{'compatible': <Property, name: compatible, value: ['props']>, 'nonexistent-boolean': <Property, name: nonexistent-boolean, value: False>, 'existent-boolean': <Property, name: existent-boolean, value: True>, 'int': <Property, name: int, value: 1>, 'array': <Property, name: array, value: [1, 2, 3]>, 'uint8-array': <Property, name: uint8-array, value: b'\x124'>, 'string': <Property, name: string, value: 'foo'>, 'string-array': <Property, name: string-array, value: ['foo', 'bar', 'baz']>, 'phandle-ref': <Property, name: phandle-ref, value: <Device /props/node in 'test.dts', no binding>>}")
+ r"{'compatible': <Property, name: compatible, value: ['props']>, 'nonexistent-boolean': <Property, name: nonexistent-boolean, value: False>, 'existent-boolean': <Property, name: existent-boolean, value: True>, 'int': <Property, name: int, value: 1>, 'array': <Property, name: array, value: [1, 2, 3]>, 'uint8-array': <Property, name: uint8-array, value: b'\x124'>, 'string': <Property, name: string, value: 'foo'>, 'string-array': <Property, name: string-array, value: ['foo', 'bar', 'baz']>, 'phandle-ref': <Property, name: phandle-ref, value: <Device /props/node in 'test.dts', no binding>>, 'phandle-refs': <Property, name: phandle-refs, value: [<Device /props/node in 'test.dts', no binding>, <Device /props/node2 in 'test.dts', no binding>]>}")
#
# Test having multiple directories with bindings, with a different .dts file