dts: dtlib/edtlib: Add a syntax-based type-checking system
Property type-checking has been pretty rudimentary until now, only checking things like the length being divisible by 4 for 'type: array', and strings being null-terminated. In particular, no checking was done for 'type: uint8-array', letting jedec-id = < 0xc8 0x28 0x17 >; slip through when jedec-id = [ 0xc8 0x28 0x17 ]; was intended. Fix it by adding a syntax-based type checker: 1. Add Property.type, which gives a high-level type for the property, derived from the markers added in the previous commit. This includes types like TYPE_EMPTY ('foo;'), TYPE_NUM ('foo = < 3 >;'), TYPE_BYTES ('foo = [ 01 02 ];'), TYPE_STRINGS ('foo = "bar", "baz"'), TYPE_PHANDLE ('foo = < &bar >;'), and TYPE_COMPOUND (everything not recognized). See the Property.type docstring in dtlib for more info. 2. Use the high-level type in Property.to_num()/to_string()/to_node()/etc. to verify that the property was assigned in an expected way for the type. If the assignment looks bad, give a helpful error: expected property 'nums' on /foo/bar in some.dts to be assigned with 'nums = < (number) (number) ... >', not 'nums = "oops";' Some other related changes are included as well: - There's a new Property.to_bytes() function that works like accessing Property.bytes, except with an added check for the value being assigned like 'foo = [ ... ]'. This function solves problems like the jedec-id one. - There's a new Property.to_path() function for fetching the referenced node for assignments like 'foo = &node;', with type checking. (Strings are accepted too, as long as they give the path to an existing node.) This function is used for /chosen and /aliases. - A new 'type: phandle' type can now be given in bindings, for properties that are assigned like 'foo = < &node >;'. - Property.__str__() now displays phandles and path references as they were written (e.g. '< &foo >' instead of '< 0x1 >', if the allocated phandle happened to be 1). - Property.to_num() and Property.to_nums() no longer take a 'length' parameter, because it makes no sense with the type checking. - The global dtlib.to_string() and dtlib.to_strings() functions were removed, because they're not that useful. - More tests were added, along with misc. minor cleanup in various places. - Probably other stuff I forgot. The more strict type checking in dtlib indirectly makes some parts of edtlib more strict as well (wherever Property.to_*() is used). Fixes: #18131 Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
This commit is contained in:
parent
c9ac5e60e3
commit
06b746cc58
11 changed files with 596 additions and 337 deletions
|
@ -56,7 +56,7 @@ sub-node:
|
|||
#
|
||||
# <property name>:
|
||||
# category: <required | optional>
|
||||
# type: <string | int | boolean | array | uint8-array | string-array | compound>
|
||||
# type: <string | int | boolean | array | uint8-array | string-array | phandle | compound>
|
||||
# description: <description of the property>
|
||||
# enum:
|
||||
# - <item1>
|
||||
|
@ -64,12 +64,20 @@ sub-node:
|
|||
# ...
|
||||
# - <itemN>
|
||||
#
|
||||
# 'uint8-array' is our name for what the device tree specification calls
|
||||
# 'type: uint8-array' is for what the device tree specification calls
|
||||
# 'bytestring'. Properties of type 'uint8-array' should be set like this:
|
||||
#
|
||||
# foo = [89 AB CD];
|
||||
#
|
||||
# Each value is a byte in hex.
|
||||
#
|
||||
# 'phandle' is for properties that are assigned a single phandle, like this:
|
||||
#
|
||||
# foo = <&label>;
|
||||
#
|
||||
# 'compound' is a catch-all for more complex types, e.g.
|
||||
#
|
||||
# foo = <&label1 1 2 &label2 7>;
|
||||
properties:
|
||||
# An entry for 'compatible' must appear, as it's used to map nodes to
|
||||
# bindings
|
||||
|
|
|
@ -29,7 +29,7 @@ properties:
|
|||
manual defines a shared FIFO size.
|
||||
|
||||
phys:
|
||||
type: array
|
||||
type: phandle
|
||||
category: optional
|
||||
description: PHY provider specifier
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ properties:
|
|||
manual defines a shared FIFO size.
|
||||
|
||||
phys:
|
||||
type: array
|
||||
type: phandle
|
||||
category: optional
|
||||
description: PHY provider specifier
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ properties:
|
|||
the pull-up resistor on USB Data Positive signal.
|
||||
|
||||
phys:
|
||||
type: array
|
||||
type: phandle
|
||||
category: optional
|
||||
description: PHY provider specifier
|
||||
|
||||
|
|
|
@ -1069,15 +1069,8 @@ class DT:
|
|||
"include only characters from [0-9a-z-]"
|
||||
.format(prop.name))
|
||||
|
||||
# Any errors this generates will already point to the property
|
||||
# and /aliases
|
||||
path = prop.to_string()
|
||||
|
||||
try:
|
||||
alias2node[prop.name] = self.get_node(path)
|
||||
except DTError as e:
|
||||
raise DTError("/aliases: bad path for '{}': {}"
|
||||
.format(prop.name, e))
|
||||
# Property.to_path() already checks that the node exists
|
||||
alias2node[prop.name] = prop.to_path()
|
||||
|
||||
self.alias2node = alias2node
|
||||
|
||||
|
@ -1354,6 +1347,32 @@ class Property:
|
|||
|
||||
See the to_*() methods for converting the value to other types.
|
||||
|
||||
type:
|
||||
The type of the property, inferred from the syntax used in the
|
||||
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
|
||||
|
||||
*Anything else* includes properties mixing (<&label>) and node path
|
||||
(&label) references with other data.
|
||||
|
||||
Data labels in the property value do not influence the type.
|
||||
|
||||
labels:
|
||||
A list with all labels pointing to the property, in the same order as the
|
||||
labels appear, but with duplicates removed.
|
||||
|
@ -1394,88 +1413,218 @@ class Property:
|
|||
# to be patched in after parsing.
|
||||
self._markers = []
|
||||
|
||||
def to_num(self, length=4, signed=False):
|
||||
def to_num(self, signed=False):
|
||||
"""
|
||||
Returns the property value interpreted as a number.
|
||||
Returns the value of the property as a number.
|
||||
|
||||
length (default: 4):
|
||||
The expected length of the value in bytes. Raises DTError if it has a
|
||||
different length. This is provided as a simple type check.
|
||||
Raises DTError if the property was not assigned with this syntax (has
|
||||
Property.type TYPE_NUM):
|
||||
|
||||
Four bytes is the length of a cell, so the value of e.g.
|
||||
'x = < 73 >;' can be fetched with a plain prop.to_num().
|
||||
|
||||
If 'length' is None, the entire property value is used, with no
|
||||
length check.
|
||||
foo = < 1 >;
|
||||
|
||||
signed (default: False):
|
||||
If True, the value will be interpreted as signed rather than
|
||||
unsigned.
|
||||
"""
|
||||
try:
|
||||
return to_num(self.value, length, signed)
|
||||
except DTError as e:
|
||||
self._err_with_context(e)
|
||||
if self.type is not TYPE_NUM:
|
||||
raise DTError("expected property '{0}' on {1} in {2} to be "
|
||||
"assigned with '{0} = < (number) >;', not '{3}'"
|
||||
.format(self.name, self.node.path,
|
||||
self.node.dt.filename, self))
|
||||
|
||||
def to_nums(self, length=4, signed=False):
|
||||
return int.from_bytes(self.value, "big", signed=signed)
|
||||
|
||||
def to_nums(self, signed=False):
|
||||
"""
|
||||
Returns the property value interpreted as a list of numbers.
|
||||
Returns the value of the property as a list of numbers.
|
||||
|
||||
length (default: 4):
|
||||
The length in bytes of each number. Raises DTError if the length of
|
||||
the value is not a multiple of 'length'.
|
||||
Raises DTError if the property was not assigned with this syntax (has
|
||||
Property.type TYPE_NUM or TYPE_NUMS):
|
||||
|
||||
foo = < 1 2 ... >;
|
||||
|
||||
signed (default: False):
|
||||
If True, the values will be interpreted as signed rather than
|
||||
unsigned.
|
||||
"""
|
||||
try:
|
||||
return to_nums(self.value, length, signed)
|
||||
except DTError as e:
|
||||
self._err_with_context(e)
|
||||
if self.type not in (TYPE_NUM, TYPE_NUMS):
|
||||
raise DTError("expected property '{0}' on {1} in {2} to be "
|
||||
"assigned with '{0} = < (number) (number) ... >', "
|
||||
"not '{3}'"
|
||||
.format(self.name, self.node.path,
|
||||
self.node.dt.filename, self))
|
||||
|
||||
return [int.from_bytes(self.value[i:i + 4], "big", signed=signed)
|
||||
for i in range(0, len(self.value), 4)]
|
||||
|
||||
def to_bytes(self):
|
||||
"""
|
||||
Returns the value of the property as a raw 'bytes', like
|
||||
Property.value, except with added type checking.
|
||||
|
||||
Raises DTError if the property was not assigned with this syntax (has
|
||||
Property.type TYPE_BYTES):
|
||||
|
||||
foo = [ 01 ... ];
|
||||
"""
|
||||
if self.type is not TYPE_BYTES:
|
||||
raise DTError("expected property '{0}' on {1} in {2} to be "
|
||||
"assigned with '{0} = [ (byte) (byte) ... ]', "
|
||||
"not '{3}'".format(self.name, self.node.path,
|
||||
self.node.dt.filename, self))
|
||||
|
||||
return self.value
|
||||
|
||||
def to_string(self):
|
||||
"""
|
||||
Returns the property value interpreted as a string.
|
||||
Returns the value of the property as a string.
|
||||
|
||||
Raises DTError if the value is not valid UTF-8, is not null-terminated,
|
||||
or if contains more than one null terminator (the null terminator is
|
||||
stripped from the returned string). Strings in Device Tree (e.g., 'x =
|
||||
"foo"') are implicitly null-terminated.
|
||||
Raises DTError if the property was not assigned with this syntax (has
|
||||
Property.type TYPE_STRING):
|
||||
|
||||
foo = "string";
|
||||
|
||||
This function might also raise UnicodeDecodeError if the string is
|
||||
not valid UTF-8.
|
||||
"""
|
||||
if self.type is not TYPE_STRING:
|
||||
raise DTError("expected property '{0}' on {1} in {2} to be "
|
||||
"assigned with '{0} = \"string\"', not '{3}'"
|
||||
.format(self.name, self.node.path,
|
||||
self.node.dt.filename, self))
|
||||
|
||||
try:
|
||||
return to_string(self.value)
|
||||
except DTError as e:
|
||||
self._err_with_context(e)
|
||||
return self.value.decode("utf-8")[:-1] # Strip null
|
||||
except UnicodeDecodeError:
|
||||
raise DTError("value of property '{}' ({}) on {} in {} is not "
|
||||
"valid UTF-8"
|
||||
.format(self.name, self.value, self.node.path,
|
||||
self.node.dt.filename))
|
||||
|
||||
def to_strings(self):
|
||||
"""
|
||||
Returns the property value interpreted as a list of strings.
|
||||
Returns the value of the property as a list of strings.
|
||||
|
||||
Raises DTError if the value is not valid UTF-8 or is not
|
||||
null-terminated (the null terminators are stripped from the returned
|
||||
string). Strings in Device Tree (e.g., 'x = "foo"') are implicitly
|
||||
null-terminated.
|
||||
Raises DTError if the property was not assigned with this syntax (has
|
||||
Property.type TYPE_STRING or TYPE_STRINGS):
|
||||
|
||||
foo = "string", "string", ... ;
|
||||
|
||||
Also raises DTError if any of the strings are not valid UTF-8.
|
||||
"""
|
||||
if self.type not in (TYPE_STRING, TYPE_STRINGS):
|
||||
raise DTError("expected property '{0}' on {1} in {2} to be "
|
||||
"assigned with '{0} = \"string\", \"string\", ...', "
|
||||
"not {3}"
|
||||
.format(self.name, self.node.path,
|
||||
self.node.dt.filename, self))
|
||||
|
||||
try:
|
||||
return to_strings(self.value)
|
||||
except DTError as e:
|
||||
self._err_with_context(e)
|
||||
return self.value.decode("utf-8").split("\0")[:-1]
|
||||
except UnicodeDecodeError:
|
||||
raise DTError("value of property '{}' ({}) on {} in {} is not "
|
||||
"valid UTF-8"
|
||||
.format(self.name, self.value, self.node.path,
|
||||
self.node.dt.filename))
|
||||
|
||||
def to_node(self):
|
||||
"""
|
||||
Interprets the property value as a phandle and returns the
|
||||
corresponding Node.
|
||||
Returns the Node the phandle in the property points to.
|
||||
|
||||
Raises DTError if the value is not a valid phandle or if no node with
|
||||
that phandle exists.
|
||||
Raises DTError if the property was not assigned with either of these
|
||||
syntaxes (has Property.type TYPE_PHANDLE or TYPE_NUM).
|
||||
|
||||
foo = < &bar >;
|
||||
foo = < 1 >;
|
||||
|
||||
For the second case, DTError is raised if the phandle does not exist.
|
||||
"""
|
||||
phandle = self.to_num()
|
||||
if self.type not in (TYPE_PHANDLE, TYPE_NUM):
|
||||
raise DTError("expected property '{0}' on {1} in {2} to be "
|
||||
"assigned with either '{0} = < &foo >' or "
|
||||
"'{0} = < (valid phandle number) >', not {3}"
|
||||
.format(self.name, self.node.path,
|
||||
self.node.dt.filename, self))
|
||||
|
||||
phandle = int.from_bytes(self.value, "big")
|
||||
node = self.node.dt.phandle2node.get(phandle)
|
||||
if not node:
|
||||
self._err_with_context("non-existent phandle " + str(phandle))
|
||||
raise DTError("the phandle given in property '{}' ({}) on {} in "
|
||||
"{} does not exist"
|
||||
.format(self.name, phandle, self.node.path,
|
||||
self.node.dt.filename))
|
||||
return node
|
||||
|
||||
|
||||
def to_path(self):
|
||||
"""
|
||||
Returns the Node referenced by the path stored in the property.
|
||||
|
||||
Raises DTError if the property was not assigned with either of these
|
||||
syntaxes (has Property.type TYPE_PATH or TYPE_STRING):
|
||||
|
||||
foo = &bar;
|
||||
foo = "/bar";
|
||||
|
||||
For the second case, DTError is raised if the path does not exist.
|
||||
"""
|
||||
if self.type not in (TYPE_PATH, TYPE_STRING):
|
||||
raise DTError("expected property '{0}' on {1} in {2} to be "
|
||||
"assigned with either '{0} = &foo' or "
|
||||
"'{0} = \"/path/to/node\"', not '{3}'"
|
||||
.format(self.name, self.node.path,
|
||||
self.node.dt.filename, self))
|
||||
|
||||
try:
|
||||
path = self.value.decode("utf-8")[:-1]
|
||||
except UnicodeDecodeError:
|
||||
raise DTError("value of property '{}' ({}) on {} in {} is not "
|
||||
"valid UTF-8"
|
||||
.format(self.name, self.value, self.node.path,
|
||||
self.node.dt.filename))
|
||||
|
||||
try:
|
||||
return self.node.dt.get_node(path)
|
||||
except DTError:
|
||||
raise DTError("property '{}' on {} in {} points to the "
|
||||
'non-existent node "{}"'
|
||||
.format(self.name, self.node.path,
|
||||
self.node.dt.filename, path))
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""
|
||||
See the class docstring.
|
||||
"""
|
||||
# Data labels (e.g. 'foo = label: <3>') are irrelevant, so filter them
|
||||
# out
|
||||
types = [marker[1] for marker in self._markers
|
||||
if marker[1] != _REF_LABEL]
|
||||
|
||||
if not types:
|
||||
return TYPE_EMPTY
|
||||
|
||||
if types == [_TYPE_UINT8]:
|
||||
return TYPE_BYTES
|
||||
|
||||
if types == [_TYPE_UINT32]:
|
||||
return TYPE_NUM if len(self.value) == 4 else TYPE_NUMS
|
||||
|
||||
# Treat 'foo = <1 2 3>, <4 5>, ...' as TYPE_NUMS too
|
||||
if set(types) == {_TYPE_UINT32}:
|
||||
return TYPE_NUMS
|
||||
|
||||
if set(types) == {_TYPE_STRING}:
|
||||
return TYPE_STRING if len(types) == 1 else TYPE_STRINGS
|
||||
|
||||
if types == [_REF_PATH]:
|
||||
return TYPE_PATH
|
||||
|
||||
if types == [_TYPE_UINT32, _REF_PHANDLE] and len(self.value) == 4:
|
||||
return TYPE_PHANDLE
|
||||
|
||||
return TYPE_COMPOUND
|
||||
|
||||
def __str__(self):
|
||||
s = "".join(label + ": " for label in self.labels) + self.name
|
||||
if not self.value:
|
||||
|
@ -1492,19 +1641,27 @@ class Property:
|
|||
# End of current marker
|
||||
end = next_marker[0] if next_marker else len(self.value)
|
||||
|
||||
if marker_type in (_TYPE_STRING, _REF_PATH):
|
||||
if marker_type is _TYPE_STRING:
|
||||
# end - 1 to strip off the null terminator
|
||||
s += ' "{}"'.format(_decode_and_escape(
|
||||
self.value[pos:end - 1]))
|
||||
if end != len(self.value):
|
||||
s += ","
|
||||
elif marker_type is _REF_PATH:
|
||||
s += " &" + ref
|
||||
if end != len(self.value):
|
||||
s += ","
|
||||
else:
|
||||
# Raw data (<>/[])
|
||||
# <> or []
|
||||
|
||||
if marker_type is _REF_LABEL:
|
||||
s += " {}:".format(ref)
|
||||
elif marker_type is not _REF_PHANDLE:
|
||||
# marker_type is _TYPE_UINT_*
|
||||
elif marker_type is _REF_PHANDLE:
|
||||
s += " &" + ref
|
||||
pos += 4
|
||||
# Subtle: There might be more data between the phandle and
|
||||
# the next marker, so we can't 'continue' here
|
||||
else: # marker_type is _TYPE_UINT*
|
||||
elm_size = _TYPE_TO_N_BYTES[marker_type]
|
||||
s += _N_BYTES_TO_START_STR[elm_size]
|
||||
|
||||
|
@ -1526,7 +1683,6 @@ class Property:
|
|||
if pos != len(self.value):
|
||||
s += ","
|
||||
|
||||
|
||||
return s + ";"
|
||||
|
||||
|
||||
|
@ -1566,14 +1722,15 @@ class Property:
|
|||
|
||||
def to_num(data, length=None, signed=False):
|
||||
"""
|
||||
Like Property.to_num(), but takes an arbitrary 'bytes' array. The value is
|
||||
assumed to be in big-endian format, which is standard in Device Tree.
|
||||
Converts the 'bytes' array 'data' to a number. The value is expected to be
|
||||
in big-endian format, which is standard in Device Tree.
|
||||
|
||||
length (default: None):
|
||||
The expected length of the value in bytes. See Property.to_num().
|
||||
The expected length of the value in bytes, as a simple type check. If
|
||||
None, the length check is skipped.
|
||||
|
||||
Unlike for Property.to_num(), 'length' defaults to None, meaning to skip
|
||||
the length check and use the entire property value.
|
||||
signed (default: False):
|
||||
If True, the value will be interpreted as signed rather than unsigned.
|
||||
"""
|
||||
_check_is_bytes(data)
|
||||
if length is not None:
|
||||
|
@ -1601,35 +1758,20 @@ def to_nums(data, length=4, signed=False):
|
|||
for i in range(0, len(data), length)]
|
||||
|
||||
|
||||
def to_string(data):
|
||||
"""
|
||||
Like Property.to_string(), but takes an arbitrary 'bytes' array. The string
|
||||
should be null-terminated, which is standard in Device Tree. The
|
||||
null terminator is stripped from the returned value.
|
||||
"""
|
||||
strings = to_strings(data)
|
||||
if len(strings) != 1:
|
||||
raise DTError("{} contains more than one string".format(data))
|
||||
return strings[0]
|
||||
#
|
||||
# Public constants
|
||||
#
|
||||
|
||||
|
||||
def to_strings(data):
|
||||
"""
|
||||
Like Property.to_strings(), but takes an arbitrary 'bytes' array. The
|
||||
strings should be null-terminated, which is standard in Device Tree. The
|
||||
null terminators are stripped from the returned value.
|
||||
"""
|
||||
_check_is_bytes(data)
|
||||
|
||||
try:
|
||||
s = data.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
raise DTError("{} is not valid UTF-8".format(data))
|
||||
|
||||
if not s.endswith("\0"):
|
||||
raise DTError("{} is not null-terminated".format(data))
|
||||
|
||||
return s.split("\0")[:-1]
|
||||
# See Property.type
|
||||
TYPE_EMPTY = 0
|
||||
TYPE_BYTES = 1
|
||||
TYPE_NUM = 2
|
||||
TYPE_NUMS = 3
|
||||
TYPE_STRING = 4
|
||||
TYPE_STRINGS = 5
|
||||
TYPE_PATH = 6
|
||||
TYPE_PHANDLE = 7
|
||||
TYPE_COMPOUND = 8
|
||||
|
||||
|
||||
def _check_is_bytes(data):
|
||||
|
@ -1640,7 +1782,7 @@ def _check_is_bytes(data):
|
|||
|
||||
def _check_length_positive(length):
|
||||
if length < 1:
|
||||
raise DTError("'size' must be greater than zero, was " + str(length))
|
||||
raise DTError("'length' must be greater than zero, was " + str(length))
|
||||
|
||||
|
||||
def _append_no_dup(lst, elm):
|
||||
|
|
|
@ -29,7 +29,7 @@ import sys
|
|||
|
||||
import yaml
|
||||
|
||||
from dtlib import DT, DTError, to_num, to_nums
|
||||
from dtlib import DT, DTError, to_num, to_nums, TYPE_EMPTY
|
||||
|
||||
# NOTE: testedtlib.py is the test suite for this library. It can be run
|
||||
# directly.
|
||||
|
@ -137,12 +137,8 @@ class EDT:
|
|||
if name not in chosen.props:
|
||||
return None
|
||||
|
||||
path = chosen.props[name].to_string()
|
||||
try:
|
||||
return self._node2dev[self._dt.get_node(path)]
|
||||
except DTError:
|
||||
_err("{} in /chosen points to {}, which does not exist"
|
||||
.format(name, path))
|
||||
# to_path() checks that the node exists
|
||||
return self._node2dev[chosen.props[name].to_path()]
|
||||
|
||||
def _init_compat2binding(self, bindings_dirs):
|
||||
# Creates self._compat2binding. This is a dictionary that maps
|
||||
|
@ -224,15 +220,29 @@ class EDT:
|
|||
self.devices = []
|
||||
|
||||
for node in self._dt.node_iter():
|
||||
# Warning: Device.__init__() relies on parent Devices being created
|
||||
# before their children. This is guaranteed by node_iter().
|
||||
dev = Device(self, node)
|
||||
# Warning: We depend on parent Devices being created before their
|
||||
# children. This is guaranteed by node_iter().
|
||||
dev = Device()
|
||||
dev.edt = self
|
||||
dev._node = node
|
||||
dev._init_binding()
|
||||
dev._init_regs()
|
||||
dev._set_instance_no()
|
||||
|
||||
self.devices.append(dev)
|
||||
self._node2dev[node] = dev
|
||||
|
||||
for dev in self.devices:
|
||||
# These depend on all Device objects having been created, so we do
|
||||
# them separately
|
||||
# Device._init_props() depends on all Device objects having been
|
||||
# created, due to 'type: phandle', so we run it separately.
|
||||
# Property.val is set to the pointed-to Device instance for
|
||||
# phandles, which must exist.
|
||||
dev._init_props()
|
||||
|
||||
for dev in self.devices:
|
||||
# These also depend on all Device objects having been created, and
|
||||
# might also depend on all Device.props having been initialized
|
||||
# (_init_clocks() does as of writing).
|
||||
dev._init_interrupts()
|
||||
dev._init_gpios()
|
||||
dev._init_pwms()
|
||||
|
@ -440,21 +450,6 @@ class Device:
|
|||
"binding " + self.binding_path if self.binding_path
|
||||
else "no binding")
|
||||
|
||||
def __init__(self, edt, node):
|
||||
"Private constructor. Not meant to be called by clients."
|
||||
|
||||
# Interrupts, GPIOs, PWMs, io-channels, and clocks are
|
||||
# initialized separately, because they depend on all Devices
|
||||
# existing
|
||||
|
||||
self.edt = edt
|
||||
self._node = node
|
||||
|
||||
self._init_binding()
|
||||
self._init_props()
|
||||
self._init_regs()
|
||||
self._set_instance_no()
|
||||
|
||||
def _init_binding(self):
|
||||
# Initializes Device.matching_compat, Device._binding, and
|
||||
# Device.binding_path.
|
||||
|
@ -578,12 +573,17 @@ class Device:
|
|||
# _init_prop() helper for getting the property's value
|
||||
|
||||
node = self._node
|
||||
prop = node.props.get(name)
|
||||
|
||||
if prop_type == "boolean":
|
||||
# True/False
|
||||
return name in node.props
|
||||
if not prop:
|
||||
return False
|
||||
if prop.type is not TYPE_EMPTY:
|
||||
_err("'{0}' in {1!r} is defined with 'type: boolean' in {2}, "
|
||||
"but is assigned a value ('{3}') instead of being empty "
|
||||
"('{0};')".format(name, node, self.binding_path, prop))
|
||||
return True
|
||||
|
||||
prop = node.props.get(name)
|
||||
if not prop:
|
||||
if not optional and self.enabled:
|
||||
_err("'{}' is marked as required in 'properties:' in {}, but "
|
||||
|
@ -599,7 +599,7 @@ class Device:
|
|||
return prop.to_nums()
|
||||
|
||||
if prop_type == "uint8-array":
|
||||
return prop.value # Plain 'bytes'
|
||||
return prop.to_bytes()
|
||||
|
||||
if prop_type == "string":
|
||||
return prop.to_string()
|
||||
|
@ -607,6 +607,9 @@ class Device:
|
|||
if prop_type == "string-array":
|
||||
return prop.to_strings()
|
||||
|
||||
if prop_type == "phandle":
|
||||
return self.edt._node2dev[prop.to_node()]
|
||||
|
||||
_err("'{}' in 'properties:' in {} has unknown type '{}'"
|
||||
.format(name, self.binding_path, prop_type))
|
||||
|
||||
|
@ -1013,8 +1016,9 @@ class Property:
|
|||
if missing. Trailing whitespace (including newlines) is removed.
|
||||
|
||||
val:
|
||||
The value of the property, with the format determined by the 'type:'
|
||||
key from the binding
|
||||
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.
|
||||
|
||||
enum_index:
|
||||
The index of the property's value in the 'enum:' list in the binding, or
|
||||
|
|
|
@ -154,6 +154,10 @@ def write_props(dev):
|
|||
if prop.name[0] == "#" or prop.name.endswith("-map"):
|
||||
continue
|
||||
|
||||
# Skip phandles
|
||||
if isinstance(prop.val, edtlib.Device):
|
||||
continue
|
||||
|
||||
# Skip properties that we handle elsewhere
|
||||
if prop.name in {"reg", "interrupts", "compatible", "interrupt-controller",
|
||||
"gpio-controller"}:
|
||||
|
|
|
@ -8,6 +8,12 @@ properties:
|
|||
constraint: "props"
|
||||
type: string-array
|
||||
|
||||
nonexistent-boolean:
|
||||
type: boolean
|
||||
|
||||
existent-boolean:
|
||||
type: boolean
|
||||
|
||||
int:
|
||||
type: int
|
||||
|
||||
|
@ -22,3 +28,6 @@ properties:
|
|||
|
||||
string-array:
|
||||
type: string-array
|
||||
|
||||
phandle-ref:
|
||||
type: phandle
|
||||
|
|
|
@ -290,14 +290,19 @@
|
|||
|
||||
props {
|
||||
compatible = "props";
|
||||
existent-boolean;
|
||||
int = <1>;
|
||||
array = <1 2 3>;
|
||||
uint8-array = [ 12 34 ];
|
||||
string = "foo";
|
||||
string-array = "foo", "bar", "baz";
|
||||
phandle-ref = < &{/props/node} >;
|
||||
// Does not appear in the binding, so won't create an entry in
|
||||
// Device.props
|
||||
not-speced = <0>;
|
||||
|
||||
node {
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
|
|
|
@ -422,7 +422,7 @@ l3: &l1 {
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
a = l01: l02: < l03: 0x1 l04: l05: 0x2 l06: l07: l08: >, [ l09: 03 l10: l11: 04 l12: l13: l14: ], "A";
|
||||
a = l01: l02: < l03: &node l04: l05: 0x2 l06: l07: l08: >, [ l09: 03 l10: l11: 04 l12: l13: l14: ], "A";
|
||||
b = < 0x0 l23: l24: >;
|
||||
node: node {
|
||||
phandle = < 0x1 >;
|
||||
|
@ -504,13 +504,13 @@ l3: &l1 {
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
a = "/abc";
|
||||
b = [ 01 ], "/abc";
|
||||
c = [ 01 ], "/abc", < 0x2 >;
|
||||
d = "/abc";
|
||||
a = &label;
|
||||
b = [ 01 ], &label;
|
||||
c = [ 01 ], &label, < 0x2 >;
|
||||
d = &{/abc};
|
||||
label: abc {
|
||||
e = "/abc";
|
||||
f = "/abc";
|
||||
e = &label;
|
||||
f = &{/abc};
|
||||
};
|
||||
};
|
||||
""")
|
||||
|
@ -541,7 +541,6 @@ l3: &l1 {
|
|||
# Test phandles
|
||||
#
|
||||
|
||||
# Check that existing phandles are used (and not reused)
|
||||
verify_parse("""
|
||||
/dts-v1/;
|
||||
|
||||
|
@ -571,7 +570,7 @@ l3: &l1 {
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
x = < 0x2 0x4 0xff >;
|
||||
x = < &a &{/b} &c >;
|
||||
dummy1 {
|
||||
phandle = < 0x1 >;
|
||||
};
|
||||
|
@ -617,10 +616,10 @@ l3: &l1 {
|
|||
phandle = < 0x1 >;
|
||||
};
|
||||
a {
|
||||
foo: phandle = < 0x2 >;
|
||||
foo: phandle = < &{/a} >;
|
||||
};
|
||||
label: b {
|
||||
bar: phandle = < 0x3 >;
|
||||
bar: phandle = < &label >;
|
||||
};
|
||||
};
|
||||
""")
|
||||
|
@ -765,7 +764,7 @@ l3: &l1 {
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
x = [ FF FF ], "/abc", < 0xff 0x1 0xff 0x1 >, "/abc", [ FF FF ], "abc";
|
||||
x = [ FF FF ], &abc, < 0xff &abc 0xff &abc >, &abc, [ FF FF ], "abc";
|
||||
abc: abc {
|
||||
phandle = < 0x1 >;
|
||||
};
|
||||
|
@ -1007,7 +1006,7 @@ y /include/ "via-include-path-1"
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
x = < 0x1 >, "/referenced2";
|
||||
x = < &{/referenced} >, &referenced2;
|
||||
referenced {
|
||||
phandle = < 0x1 >;
|
||||
};
|
||||
|
@ -1299,7 +1298,7 @@ foo
|
|||
alias1 = &l1;
|
||||
alias2 = &l2;
|
||||
alias3 = &{/sub/node3};
|
||||
alias4 = [2F 6E 6F 64 65 34 00]; // "/node4";
|
||||
alias4 = &{/node4};
|
||||
};
|
||||
|
||||
l1: node1 {
|
||||
|
@ -1323,7 +1322,6 @@ foo
|
|||
verify_alias_target("alias1", "node1")
|
||||
verify_alias_target("alias2", "node2")
|
||||
verify_alias_target("alias3", "node3")
|
||||
verify_alias_target("alias4", "node4")
|
||||
verify_path_is("alias4/node5", "node5")
|
||||
|
||||
verify_path_error("alias4/node5/node6",
|
||||
|
@ -1332,6 +1330,28 @@ foo
|
|||
verify_error("""
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
a = [ 00 ];
|
||||
};
|
||||
};
|
||||
""",
|
||||
"expected property 'a' on /aliases in .tmp.dts to be assigned with either 'a = &foo' or 'a = \"/path/to/node\"', not 'a = [ 00 ];'")
|
||||
|
||||
verify_error(r"""
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
a = "\xFF";
|
||||
};
|
||||
};
|
||||
""",
|
||||
r"value of property 'a' (b'\xff\x00') on /aliases in .tmp.dts is not valid UTF-8")
|
||||
|
||||
verify_error("""
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
A = "/aliases";
|
||||
|
@ -1343,58 +1363,132 @@ foo
|
|||
verify_error(r"""
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
a = "\xFF";
|
||||
};
|
||||
};
|
||||
""",
|
||||
r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
|
||||
|
||||
verify_error(r"""
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
a = [ 41 ]; // "A"
|
||||
};
|
||||
};
|
||||
""",
|
||||
"b'A' is not null-terminated (for property 'a' on /aliases)")
|
||||
|
||||
verify_error(r"""
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
a = "/missing";
|
||||
};
|
||||
};
|
||||
""",
|
||||
"/aliases: bad path for 'a': component 1 ('missing') in path '/missing' does not exist")
|
||||
"property 'a' on /aliases in .tmp.dts points to the non-existent node \"/missing\"")
|
||||
|
||||
#
|
||||
# Test to_{num,nums,string,strings,node}()
|
||||
# Test Property.type
|
||||
#
|
||||
|
||||
def verify_to_num(prop, size, signed, expected):
|
||||
def verify_type(prop, expected):
|
||||
actual = dt.root.props[prop].type
|
||||
if actual != expected:
|
||||
fail("expected {} to have type {}, had type {}"
|
||||
.format(prop, expected, actual))
|
||||
|
||||
dt = parse("""
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
empty;
|
||||
bytes1 = [ ];
|
||||
bytes2 = [ 01 ];
|
||||
bytes3 = [ 01 02 ];
|
||||
bytes4 = foo: [ 01 bar: 02 ];
|
||||
bytes5 = /bits/ 8 < 1 2 3 >;
|
||||
num = < 1 >;
|
||||
nums1 = < >;
|
||||
nums2 = < >, < >;
|
||||
nums3 = < 1 2 >;
|
||||
nums4 = < 1 2 >, < 3 >, < 4 >;
|
||||
string = "foo";
|
||||
strings = "foo", "bar";
|
||||
phandle1 = < &node >;
|
||||
phandle2 = < &{/node} >;
|
||||
path1 = &node;
|
||||
path2 = &{/node};
|
||||
compound1 = < 1 >, [ 02 ];
|
||||
compound2 = "foo", < >;
|
||||
compound3 = < 1 &{/node} 2>;
|
||||
|
||||
node: node {
|
||||
};
|
||||
};
|
||||
""")
|
||||
|
||||
verify_type("empty", dtlib.TYPE_EMPTY)
|
||||
verify_type("bytes1", dtlib.TYPE_BYTES)
|
||||
verify_type("bytes2", dtlib.TYPE_BYTES)
|
||||
verify_type("bytes3", dtlib.TYPE_BYTES)
|
||||
verify_type("bytes4", dtlib.TYPE_BYTES)
|
||||
verify_type("bytes5", dtlib.TYPE_BYTES)
|
||||
verify_type("num", dtlib.TYPE_NUM)
|
||||
verify_type("nums1", dtlib.TYPE_NUMS)
|
||||
verify_type("nums2", dtlib.TYPE_NUMS)
|
||||
verify_type("nums3", dtlib.TYPE_NUMS)
|
||||
verify_type("nums4", dtlib.TYPE_NUMS)
|
||||
verify_type("string", dtlib.TYPE_STRING)
|
||||
verify_type("strings", dtlib.TYPE_STRINGS)
|
||||
verify_type("phandle1", dtlib.TYPE_PHANDLE)
|
||||
verify_type("phandle2", dtlib.TYPE_PHANDLE)
|
||||
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}()
|
||||
#
|
||||
|
||||
dt = parse(r"""
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
u = < 1 >;
|
||||
s = < 0xFFFFFFFF >;
|
||||
u8 = /bits/ 8 < 1 >;
|
||||
u16 = /bits/ 16 < 1 2 >;
|
||||
u64 = /bits/ 64 < 1 >;
|
||||
bytes = [ 01 02 03 ];
|
||||
empty;
|
||||
zero = < >;
|
||||
two_u = < 1 2 >;
|
||||
two_s = < 0xFFFFFFFF 0xFFFFFFFE >;
|
||||
three_u = < 1 2 3 >;
|
||||
three_u_split = < 1 >, < 2 >, < 3 >;
|
||||
empty_string = "";
|
||||
string = "foo\tbar baz";
|
||||
invalid_string = "\xff";
|
||||
strings = "foo", "bar", "baz";
|
||||
invalid_strings = "foo", "\xff", "bar";
|
||||
ref = <&{/target}>;
|
||||
manualref = < 100 >;
|
||||
missingref = < 123 >;
|
||||
path = &{/target};
|
||||
manualpath = "/target";
|
||||
missingpath = "/missing";
|
||||
|
||||
target {
|
||||
phandle = < 100 >;
|
||||
};
|
||||
};
|
||||
""")
|
||||
|
||||
# Test Property.to_num()
|
||||
|
||||
def verify_to_num(prop, signed, expected):
|
||||
try:
|
||||
actual = dt.root.props[prop].to_num(size, signed)
|
||||
actual = dt.root.props[prop].to_num(signed)
|
||||
except dtlib.DTError as e:
|
||||
fail("failed to convert {} to {} number with {} bytes: {}"
|
||||
.format(prop, "a signed" if signed else "an unsigned", size,
|
||||
e))
|
||||
fail("failed to convert '{}' to {} number: {}"
|
||||
.format(prop, "a signed" if signed else "an unsigned", e))
|
||||
|
||||
if actual != expected:
|
||||
fail("expected {} to have the {} numeric value {:#x}, had the "
|
||||
"value {:#x}".format(prop, "signed" if signed else "unsigned",
|
||||
expected, actual))
|
||||
|
||||
def verify_to_num_error(prop, size, msg):
|
||||
prefix = "expected {} converted from {} bytes to generate the error " \
|
||||
"'{}', generated".format(prop, size, msg)
|
||||
def verify_to_num_error(prop, msg):
|
||||
prefix = "expected fetching '{}' as a number to generate the error " \
|
||||
"'{}', generated".format(prop, msg)
|
||||
try:
|
||||
dt.root.props[prop].to_num(size)
|
||||
dt.root.props[prop].to_num()
|
||||
fail(prefix + " no error")
|
||||
except dtlib.DTError as e:
|
||||
if str(e) != msg:
|
||||
|
@ -1402,23 +1496,36 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
|
|||
except Exception as e:
|
||||
fail("{} the non-DTError '{}'".format(prefix, e))
|
||||
|
||||
def verify_to_nums(prop, size, signed, expected):
|
||||
verify_to_num("u", False, 1)
|
||||
verify_to_num("u", True, 1)
|
||||
verify_to_num("s", False, 0xFFFFFFFF)
|
||||
verify_to_num("s", True, -1)
|
||||
|
||||
verify_to_num_error("two_u", "expected property 'two_u' on / in .tmp.dts to be assigned with 'two_u = < (number) >;', not 'two_u = < 0x1 0x2 >;'")
|
||||
verify_to_num_error("u8", "expected property 'u8' on / in .tmp.dts to be assigned with 'u8 = < (number) >;', not 'u8 = [ 01 ];'")
|
||||
verify_to_num_error("u16", "expected property 'u16' on / in .tmp.dts to be assigned with 'u16 = < (number) >;', not 'u16 = /bits/ 16 < 0x1 0x2 >;'")
|
||||
verify_to_num_error("u64", "expected property 'u64' on / in .tmp.dts to be assigned with 'u64 = < (number) >;', not 'u64 = /bits/ 64 < 0x1 >;'")
|
||||
verify_to_num_error("string", "expected property 'string' on / in .tmp.dts to be assigned with 'string = < (number) >;', not 'string = \"foo\\tbar baz\";'")
|
||||
|
||||
# Test Property.to_nums()
|
||||
|
||||
def verify_to_nums(prop, signed, expected):
|
||||
try:
|
||||
actual = dt.root.props[prop].to_nums(size, signed)
|
||||
actual = dt.root.props[prop].to_nums(signed)
|
||||
except dtlib.DTError as e:
|
||||
fail("failed to convert {} to {} numbers with {} bytes each: {}"
|
||||
.format(prop, "signed" if signed else "unsigned", size, e))
|
||||
fail("failed to convert '{}' to {} numbers: {}"
|
||||
.format(prop, "signed" if signed else "unsigned", e))
|
||||
|
||||
if actual != expected:
|
||||
fail("expected {} to give the {} numbers {} for size {}, gave {}"
|
||||
fail("expected {} to give the {} numbers {}, gave {}"
|
||||
.format(prop, "signed" if signed else "unsigned", expected,
|
||||
size, actual))
|
||||
actual))
|
||||
|
||||
def verify_to_nums_error(prop, size, msg):
|
||||
prefix = "expected {} converted to numbers with {} bytes each to " \
|
||||
"generate the error '{}', generated".format(prop, size, msg)
|
||||
def verify_to_nums_error(prop, msg):
|
||||
prefix = "expected converting '{}' to numbers to generate the error " \
|
||||
"'{}', generated".format(prop, msg)
|
||||
try:
|
||||
dt.root.props[prop].to_nums(size)
|
||||
dt.root.props[prop].to_nums()
|
||||
fail(prefix + " no error")
|
||||
except dtlib.DTError as e:
|
||||
if str(e) != msg:
|
||||
|
@ -1426,11 +1533,35 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
|
|||
except Exception as e:
|
||||
fail("{} the non-DTError '{}'".format(prefix, e))
|
||||
|
||||
def verify_raw_to_num_error(fn, data, size, msg):
|
||||
prefix = "expected {}() called with data='{}', size='{}' to generate " \
|
||||
"the error '{}', generated".format(fn.__name__, data, size, msg)
|
||||
verify_to_nums("zero", False, [])
|
||||
verify_to_nums("u", False, [1])
|
||||
verify_to_nums("two_u", False, [1, 2])
|
||||
verify_to_nums("two_u", True, [1, 2])
|
||||
verify_to_nums("two_s", False, [0xFFFFFFFF, 0xFFFFFFFE])
|
||||
verify_to_nums("two_s", True, [-1, -2])
|
||||
verify_to_nums("three_u", False, [1, 2, 3])
|
||||
verify_to_nums("three_u_split", False, [1, 2, 3])
|
||||
|
||||
verify_to_nums_error("empty", "expected property 'empty' on / in .tmp.dts to be assigned with 'empty = < (number) (number) ... >', not 'empty;'")
|
||||
verify_to_nums_error("string", "expected property 'string' on / in .tmp.dts to be assigned with 'string = < (number) (number) ... >', not 'string = \"foo\\tbar baz\";'")
|
||||
|
||||
# Test Property.to_bytes()
|
||||
|
||||
def verify_to_bytes(prop, expected):
|
||||
try:
|
||||
dtlib.to_num(data, size)
|
||||
actual = dt.root.props[prop].to_bytes()
|
||||
except dtlib.DTError as e:
|
||||
fail("failed to convert '{}' to bytes: {}".format(prop, e))
|
||||
|
||||
if actual != expected:
|
||||
fail("expected {} to give the bytes {}, gave {}"
|
||||
.format(prop, expected, actual))
|
||||
|
||||
def verify_to_bytes_error(prop, msg):
|
||||
prefix = "expected converting '{}' to bytes to generate the error " \
|
||||
"'{}', generated".format(prop, msg)
|
||||
try:
|
||||
dt.root.props[prop].to_bytes()
|
||||
fail(prefix + " no error")
|
||||
except dtlib.DTError as e:
|
||||
if str(e) != msg:
|
||||
|
@ -1438,18 +1569,26 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
|
|||
except Exception as e:
|
||||
fail("{} the non-DTError '{}'".format(prefix, e))
|
||||
|
||||
verify_to_bytes("u8", b"\x01")
|
||||
verify_to_bytes("bytes", b"\x01\x02\x03")
|
||||
|
||||
verify_to_bytes_error("u16", "expected property 'u16' on / in .tmp.dts to be assigned with 'u16 = [ (byte) (byte) ... ]', not 'u16 = /bits/ 16 < 0x1 0x2 >;'")
|
||||
verify_to_bytes_error("empty", "expected property 'empty' on / in .tmp.dts to be assigned with 'empty = [ (byte) (byte) ... ]', not 'empty;'")
|
||||
|
||||
# Test Property.to_string()
|
||||
|
||||
def verify_to_string(prop, expected):
|
||||
try:
|
||||
actual = dt.root.props[prop].to_string()
|
||||
except dtlib.DTError as e:
|
||||
fail("failed to convert {} to string: {}".format(prop, e))
|
||||
fail("failed to convert '{}' to string: {}".format(prop, e))
|
||||
|
||||
if actual != expected:
|
||||
fail("expected {} to have the value '{}', had the value '{}'"
|
||||
.format(prop, expected, actual))
|
||||
|
||||
def verify_to_string_error(prop, msg):
|
||||
prefix = "expected converting {} to string to generate the error " \
|
||||
prefix = "expected converting '{}' to string to generate the error " \
|
||||
"'{}', generated".format(prop, msg)
|
||||
try:
|
||||
dt.root.props[prop].to_string()
|
||||
|
@ -1460,33 +1599,30 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
|
|||
except Exception as e:
|
||||
fail("{} the non-DTError '{}'".format(prefix, e))
|
||||
|
||||
def verify_raw_to_string_error(data, msg):
|
||||
prefix = "expected to_string() called with data='{}' to generate " \
|
||||
"the error '{}', generated".format(data, msg)
|
||||
try:
|
||||
dtlib.to_string(data)
|
||||
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_string("empty_string", "")
|
||||
verify_to_string("string", "foo\tbar baz")
|
||||
|
||||
verify_to_string_error("u", "expected property 'u' on / in .tmp.dts to be assigned with 'u = \"string\"', not 'u = < 0x1 >;'")
|
||||
verify_to_string_error("strings", "expected property 'strings' on / in .tmp.dts to be assigned with 'strings = \"string\"', not 'strings = \"foo\", \"bar\", \"baz\";'")
|
||||
verify_to_string_error("invalid_string", r"value of property 'invalid_string' (b'\xff\x00') on / in .tmp.dts is not valid UTF-8")
|
||||
|
||||
# Test Property.to_strings()
|
||||
|
||||
def verify_to_strings(prop, expected):
|
||||
try:
|
||||
actual = dt.root.props[prop].to_strings()
|
||||
except dtlib.DTError as e:
|
||||
fail("failed to convert {} to strings: {}".format(prop, e))
|
||||
fail("failed to convert '{}' to strings: {}".format(prop, e))
|
||||
|
||||
if actual != expected:
|
||||
fail("expected {} to have the value '{}', had the value '{}'"
|
||||
.format(prop, expected, actual))
|
||||
|
||||
def verify_to_strings_error(prop, msg):
|
||||
prefix = "expected converting {} to strings to generate the error " \
|
||||
prefix = "expected converting '{}' to strings to generate the error " \
|
||||
"'{}', generated".format(prop, msg)
|
||||
try:
|
||||
dt.root.props[prop].to_string()
|
||||
dt.root.props[prop].to_strings()
|
||||
fail(prefix + " no error")
|
||||
except dtlib.DTError as e:
|
||||
if str(e) != msg:
|
||||
|
@ -1494,18 +1630,27 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
|
|||
except Exception as e:
|
||||
fail("{} the non-DTError '{}'".format(prefix, e))
|
||||
|
||||
verify_to_strings("empty_string", [""])
|
||||
verify_to_strings("string", ["foo\tbar baz"])
|
||||
verify_to_strings("strings", ["foo", "bar", "baz"])
|
||||
|
||||
verify_to_strings_error("u", "expected property 'u' on / in .tmp.dts to be assigned with 'u = \"string\", \"string\", ...', not u = < 0x1 >;")
|
||||
verify_to_strings_error("invalid_strings", r"value of property 'invalid_strings' (b'foo\x00\xff\x00bar\x00') on / in .tmp.dts is not valid UTF-8")
|
||||
|
||||
# Test Property.to_node()
|
||||
|
||||
def verify_to_node(prop, path):
|
||||
try:
|
||||
actual = dt.root.props[prop].to_node().path
|
||||
except dtlib.DTError as e:
|
||||
fail("failed to convert {} to node: {}".format(prop, e))
|
||||
fail("failed to convert '{}' to node: {}".format(prop, e))
|
||||
|
||||
if actual != path:
|
||||
fail("expected {} to point to {}, pointed to {}"
|
||||
.format(prop, path, actual))
|
||||
|
||||
def verify_to_node_error(prop, msg):
|
||||
prefix = "expected converting {} to node to generate the error " \
|
||||
prefix = "expected converting '{}' to a node to generate the error " \
|
||||
"'{}', generated".format(prop, msg)
|
||||
try:
|
||||
dt.root.props[prop].to_node()
|
||||
|
@ -1516,140 +1661,82 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
|
|||
except Exception as e:
|
||||
fail("{} the non-DTError '{}'".format(prefix, e))
|
||||
|
||||
dt = parse(r"""
|
||||
/dts-v1/;
|
||||
verify_to_node("ref", "/target")
|
||||
verify_to_node("manualref", "/target")
|
||||
|
||||
/ {
|
||||
empty;
|
||||
u1 = /bits/ 8 < 0x01 >;
|
||||
u2 = /bits/ 8 < 0x01 0x02 >;
|
||||
u3 = /bits/ 8 < 0x01 0x02 0x03 >;
|
||||
u4 = /bits/ 8 < 0x01 0x02 0x03 0x04 >;
|
||||
s1 = /bits/ 8 < 0xFF >;
|
||||
s2 = /bits/ 8 < 0xFF 0xFE >;
|
||||
s3 = /bits/ 8 < 0xFF 0xFF 0xFD >;
|
||||
s4 = /bits/ 8 < 0xFF 0xFF 0xFF 0xFC >;
|
||||
empty_string = "";
|
||||
string = "foo\tbar baz";
|
||||
invalid_string = "\xff";
|
||||
non_null_terminated_string = [ 41 ]; // A
|
||||
strings = "foo", "bar", "baz";
|
||||
invalid_strings = "foo", "\xff", "bar";
|
||||
non_null_terminated_strings = "foo", "bar", [ 01 ];
|
||||
ref = <&{/target}>;
|
||||
missingref = < 123 >;
|
||||
badref;
|
||||
verify_to_node_error("string", "expected property 'string' on / in .tmp.dts to be assigned with either 'string = < &foo >' or 'string = < (valid phandle number) >', not string = \"foo\\tbar baz\";")
|
||||
verify_to_node_error("missingref", "the phandle given in property 'missingref' (123) on / in .tmp.dts does not exist")
|
||||
|
||||
target {
|
||||
};
|
||||
};
|
||||
""")
|
||||
# Test Property.to_path()
|
||||
|
||||
# Test to_num()
|
||||
def verify_to_path(prop, path):
|
||||
try:
|
||||
actual = dt.root.props[prop].to_path().path
|
||||
except dtlib.DTError as e:
|
||||
fail("failed to convert '{}' to path: {}".format(prop, e))
|
||||
|
||||
verify_to_num("u1", 1, False, 0x01)
|
||||
verify_to_num("u2", 2, False, 0x0102)
|
||||
verify_to_num("u3", 3, False, 0x010203)
|
||||
verify_to_num("u4", 4, False, 0x01020304)
|
||||
verify_to_num("s1", 1, False, 0xFF)
|
||||
verify_to_num("s2", 2, False, 0xFFFE)
|
||||
verify_to_num("s3", 3, False, 0xFFFFFD)
|
||||
verify_to_num("s4", 4, False, 0xFFFFFFFC)
|
||||
if actual != path:
|
||||
fail("expected {} to contain the path {}, contained {}"
|
||||
.format(prop, path, actual))
|
||||
|
||||
verify_to_num("u1", 1, True, 0x01)
|
||||
verify_to_num("u2", 2, True, 0x0102)
|
||||
verify_to_num("u3", 3, True, 0x010203)
|
||||
verify_to_num("u4", 4, True, 0x01020304)
|
||||
verify_to_num("s1", 1, True, -1)
|
||||
verify_to_num("s2", 2, True, -2)
|
||||
verify_to_num("s3", 3, True, -3)
|
||||
verify_to_num("s4", 4, True, -4)
|
||||
def verify_to_path_error(prop, msg):
|
||||
prefix = "expected converting '{}' to a path to generate the error " \
|
||||
"'{}', generated".format(prop, msg)
|
||||
try:
|
||||
dt.root.props[prop].to_path()
|
||||
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_num("u1", None, False, 0x01)
|
||||
verify_to_num("u2", None, False, 0x0102)
|
||||
verify_to_num("u3", None, False, 0x010203)
|
||||
verify_to_num("u4", None, False, 0x01020304)
|
||||
verify_to_num("s1", None, False, 0xFF)
|
||||
verify_to_num("s2", None, False, 0xFFFE)
|
||||
verify_to_num("s3", None, False, 0xFFFFFD)
|
||||
verify_to_num("s4", None, False, 0xFFFFFFFC)
|
||||
verify_to_path("path", "/target")
|
||||
verify_to_path("manualpath", "/target")
|
||||
|
||||
verify_to_num("u1", None, True, 0x01)
|
||||
verify_to_num("u2", None, True, 0x0102)
|
||||
verify_to_num("u3", None, True, 0x010203)
|
||||
verify_to_num("u4", None, True, 0x01020304)
|
||||
verify_to_num("s1", None, True, -1)
|
||||
verify_to_num("s2", None, True, -2)
|
||||
verify_to_num("s3", None, True, -3)
|
||||
verify_to_num("s4", None, True, -4)
|
||||
verify_to_path_error("u", "expected property 'u' on / in .tmp.dts to be assigned with either 'u = &foo' or 'u = \"/path/to/node\"', not 'u = < 0x1 >;'")
|
||||
verify_to_path_error("missingpath", "property 'missingpath' on / in .tmp.dts points to the non-existent node \"/missing\"")
|
||||
|
||||
verify_to_num_error("u1", 0, "'size' must be greater than zero, was 0 (for property 'u1' on /)")
|
||||
verify_to_num_error("u1", -1, "'size' must be greater than zero, was -1 (for property 'u1' on /)")
|
||||
verify_to_num_error("u1", 2, r"b'\x01' is 1 bytes long, expected 2 (for property 'u1' on /)")
|
||||
verify_to_num_error("u2", 1, r"b'\x01\x02' is 2 bytes long, expected 1 (for property 'u2' on /)")
|
||||
# Test top-level to_num() and to_nums()
|
||||
|
||||
def verify_raw_to_num(fn, prop, length, signed, expected):
|
||||
try:
|
||||
actual = fn(dt.root.props[prop].value, length, signed)
|
||||
except dtlib.DTError as e:
|
||||
fail("failed to convert '{}' to {} number(s) with {}: {}"
|
||||
.format(prop, "signed" if signed else "unsigned",
|
||||
fn.__name__, e))
|
||||
|
||||
if actual != expected:
|
||||
fail("expected {}(<{}>, {}, {}) to be {}, was {}"
|
||||
.format(fn.__name__, prop, length, signed, expected, actual))
|
||||
|
||||
def verify_raw_to_num_error(fn, data, length, msg):
|
||||
prefix = "expected {}() called with data='{}', length='{}' to " \
|
||||
"generate the error '{}', generated" \
|
||||
.format(fn.__name__, data, length, msg)
|
||||
try:
|
||||
fn(data, length)
|
||||
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_raw_to_num(dtlib.to_num, "u", None, False, 1)
|
||||
verify_raw_to_num(dtlib.to_num, "u", 4, False, 1)
|
||||
verify_raw_to_num(dtlib.to_num, "s", None, False, 0xFFFFFFFF)
|
||||
verify_raw_to_num(dtlib.to_num, "s", None, True, -1)
|
||||
verify_raw_to_num(dtlib.to_nums, "empty", 4, False, [])
|
||||
verify_raw_to_num(dtlib.to_nums, "u16", 2, False, [1, 2])
|
||||
verify_raw_to_num(dtlib.to_nums, "two_s", 4, False, [0xFFFFFFFF, 0xFFFFFFFE])
|
||||
verify_raw_to_num(dtlib.to_nums, "two_s", 4, True, [-1, -2])
|
||||
|
||||
verify_raw_to_num_error(dtlib.to_num, 0, 0, "'0' has type 'int', expected 'bytes'")
|
||||
verify_raw_to_num_error(dtlib.to_num, b"", 0, "'size' must be greater than zero, was 0")
|
||||
|
||||
# Test to_nums()
|
||||
|
||||
verify_to_nums("empty", 1, False, [])
|
||||
verify_to_nums("u1", 1, False, [1])
|
||||
verify_to_nums("u2", 1, False, [1, 2])
|
||||
verify_to_nums("u3", 1, False, [1, 2, 3])
|
||||
verify_to_nums("u4", 1, False, [1, 2, 3, 4])
|
||||
verify_to_nums("s1", 1, False, [0xFF])
|
||||
verify_to_nums("s2", 1, False, [0xFF, 0xFE])
|
||||
verify_to_nums("s3", 1, False, [0xFF, 0xFF, 0xFD])
|
||||
verify_to_nums("s4", 1, False, [0xFF, 0xFF, 0xFF, 0xFC])
|
||||
|
||||
verify_to_nums("u2", 2, False, [0x0102])
|
||||
verify_to_nums("u4", 2, False, [0x0102, 0x0304])
|
||||
|
||||
verify_to_nums("u1", 1, True, [1])
|
||||
verify_to_nums("u2", 1, True, [1, 2])
|
||||
verify_to_nums("u3", 1, True, [1, 2, 3])
|
||||
verify_to_nums("u4", 1, True, [1, 2, 3, 4])
|
||||
verify_to_nums("s1", 1, True, [-1])
|
||||
verify_to_nums("s2", 1, True, [-1, -2])
|
||||
verify_to_nums("s3", 1, True, [-1, -1, -3])
|
||||
verify_to_nums("s4", 1, True, [-1, -1, -1, -4])
|
||||
|
||||
verify_to_nums("s2", 2, True, [-2])
|
||||
verify_to_nums("s4", 2, True, [-1, -4])
|
||||
|
||||
verify_to_nums_error("u1", 0, "'size' must be greater than zero, was 0 (for property 'u1' on /)")
|
||||
verify_to_nums_error("u1", 2, r"b'\x01' is 1 bytes long, expected a length that's a multiple of 2 (for property 'u1' on /)")
|
||||
verify_to_nums_error("u2", 3, r"b'\x01\x02' is 2 bytes long, expected a length that's a multiple of 3 (for property 'u2' on /)")
|
||||
|
||||
verify_raw_to_num_error(dtlib.to_num, b"", 0, "'length' must be greater than zero, was 0")
|
||||
verify_raw_to_num_error(dtlib.to_nums, 0, 0, "'0' has type 'int', expected 'bytes'")
|
||||
verify_raw_to_num_error(dtlib.to_nums, b"", 0, "'size' must be greater than zero, was 0")
|
||||
|
||||
# Test to_string()
|
||||
|
||||
verify_to_string("empty_string", "")
|
||||
verify_to_string("string", "foo\tbar baz")
|
||||
|
||||
verify_to_string_error("invalid_string", r"b'\xff\x00' is not valid UTF-8 (for property 'invalid_string' on /)")
|
||||
verify_to_string_error("non_null_terminated_string", "b'A' is not null-terminated (for property 'non_null_terminated_string' on /)")
|
||||
|
||||
verify_raw_to_string_error(0, "'0' has type 'int', expected 'bytes'")
|
||||
|
||||
# Test to_strings()
|
||||
|
||||
verify_to_strings("empty_string", [""])
|
||||
verify_to_strings("string", ["foo\tbar baz"])
|
||||
verify_to_strings("strings", ["foo", "bar", "baz"])
|
||||
|
||||
verify_to_strings_error("invalid_strings", r"b'foo\x00\xff\x00bar\x00' is not valid UTF-8 (for property 'invalid_strings' on /)")
|
||||
verify_to_strings_error("non_null_terminated_strings", r"b'foo\x00bar\x00\x01' is not null-terminated (for property 'non_null_terminated_strings' on /)")
|
||||
|
||||
# Test to_node()
|
||||
|
||||
verify_to_node("ref", "/target")
|
||||
|
||||
verify_to_node_error("missingref", "non-existent phandle 123 (for property 'missingref' on /)")
|
||||
verify_to_node_error("badref", "b'' is 0 bytes long, expected 4 (for property 'badref' on /)")
|
||||
verify_raw_to_num_error(dtlib.to_nums, b"", 0, "'length' must be greater than zero, was 0")
|
||||
|
||||
#
|
||||
# Test duplicate label error
|
||||
|
@ -1820,7 +1907,7 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
|
|||
|
||||
/ {
|
||||
label: foo {
|
||||
x = "/foo", "/foo", < 0x1 >;
|
||||
x = &{/foo}, &label, < &label >;
|
||||
phandle = < 0x1 >;
|
||||
};
|
||||
};
|
||||
|
@ -1907,7 +1994,7 @@ l1: l2: /memreserve/ 0x0000000000000002 0x0000000000000004;
|
|||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
aA0,._+*#?- = "/aA0,._+*#?@-", "/aA0,._+*#?@-";
|
||||
aA0,._+*#?- = &_, &{/aA0,._+*#?@-};
|
||||
+ = [ 00 ];
|
||||
* = [ 02 ];
|
||||
- = [ 01 ];
|
||||
|
|
|
@ -120,7 +120,7 @@ def run():
|
|||
#
|
||||
|
||||
verify_streq(edt.get_dev("/props").props,
|
||||
r"{'compatible': <Property, name: compatible, value: ['props']>, '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']>}")
|
||||
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>>}")
|
||||
|
||||
#
|
||||
# Test having multiple directories with bindings, with a different .dts file
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue