edtlib: type annotate Node
This requires adding a private constructor so that mypy can tell what all the final instance state is going to be. Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
parent
cae8b6567d
commit
d89f974760
1 changed files with 181 additions and 89 deletions
|
@ -70,7 +70,8 @@ bindings_from_paths() helper function.
|
|||
from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, List, NoReturn, Optional, TYPE_CHECKING, Union
|
||||
from typing import Any, Callable, Dict, List, NoReturn, \
|
||||
Optional, TYPE_CHECKING, Tuple, Union
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
@ -84,6 +85,8 @@ except ImportError:
|
|||
from yaml import Loader # type: ignore
|
||||
|
||||
from devicetree.dtlib import DT, DTError, to_num, to_nums, Type
|
||||
from devicetree.dtlib import Node as dtlib_Node
|
||||
from devicetree.dtlib import Property as dtlib_Property
|
||||
from devicetree.grutils import Graph
|
||||
from devicetree._private import _slice_helper
|
||||
|
||||
|
@ -980,13 +983,38 @@ class Node:
|
|||
list is empty if the node does not hog any GPIOs. Only relevant for GPIO hog
|
||||
nodes.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
dt_node: dtlib_Node,
|
||||
edt: 'EDT',
|
||||
compats: List[str]):
|
||||
'''
|
||||
For internal use only; not meant to be used outside edtlib itself.
|
||||
'''
|
||||
# Public, some of which are initialized properly later:
|
||||
self.edt: 'EDT' = edt
|
||||
self.dep_ordinal: int = -1
|
||||
self.matching_compat: Optional[str] = None
|
||||
self.binding_path: Optional[str] = None
|
||||
self.compats: List[str] = compats
|
||||
self.ranges: List[Range] = []
|
||||
self.regs: List[Register] = []
|
||||
self.props: Dict[str, Property] = {}
|
||||
self.interrupts: List[ControllerAndData] = []
|
||||
self.pinctrls: List[PinCtrl] = []
|
||||
self.bus_node: Optional['Node'] = None
|
||||
|
||||
# Private, don't touch outside the class:
|
||||
self._node: dtlib_Node = dt_node
|
||||
self._binding: Optional[Binding] = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
def name(self) -> str:
|
||||
"See the class docstring"
|
||||
return self._node.name
|
||||
|
||||
@property
|
||||
def unit_addr(self):
|
||||
def unit_addr(self) -> Optional[int]:
|
||||
"See the class docstring"
|
||||
|
||||
# TODO: Return a plain string here later, like dtlib.Node.unit_addr?
|
||||
|
@ -1002,36 +1030,36 @@ class Node:
|
|||
return _translate(addr, self._node)
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
def description(self) -> Optional[str]:
|
||||
"See the class docstring."
|
||||
if self._binding:
|
||||
return self._binding.description
|
||||
return None
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
def path(self) -> str:
|
||||
"See the class docstring"
|
||||
return self._node.path
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
def label(self) -> Optional[str]:
|
||||
"See the class docstring"
|
||||
if "label" in self._node.props:
|
||||
return self._node.props["label"].to_string()
|
||||
return None
|
||||
|
||||
@property
|
||||
def labels(self):
|
||||
def labels(self) -> List[str]:
|
||||
"See the class docstring"
|
||||
return self._node.labels
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
def parent(self) -> Optional['Node']:
|
||||
"See the class docstring"
|
||||
return self.edt._node2enode.get(self._node.parent)
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
def children(self) -> Dict[str, 'Node']:
|
||||
"See the class docstring"
|
||||
# Could be initialized statically too to preserve identity, but not
|
||||
# sure if needed. Parent nodes being initialized before their children
|
||||
|
@ -1039,7 +1067,7 @@ class Node:
|
|||
return {name: self.edt._node2enode[node]
|
||||
for name, node in self._node.nodes.items()}
|
||||
|
||||
def child_index(self, node):
|
||||
def child_index(self, node) -> int:
|
||||
"""Get the index of *node* in self.children.
|
||||
Raises KeyError if the argument is not a child of this node.
|
||||
"""
|
||||
|
@ -1048,24 +1076,25 @@ class Node:
|
|||
# method is callable to handle parents needing to be
|
||||
# initialized before their chidlren. By the time we
|
||||
# return from __init__, 'self.children' is callable.
|
||||
self._child2index = {}
|
||||
for index, child in enumerate(self.children.values()):
|
||||
self._child2index[child] = index
|
||||
self._child2index: Dict[str, int] = {}
|
||||
for index, child_path in enumerate(child.path for child in
|
||||
self.children.values()):
|
||||
self._child2index[child_path] = index
|
||||
|
||||
return self._child2index[node]
|
||||
return self._child2index[node.path]
|
||||
|
||||
@property
|
||||
def required_by(self):
|
||||
def required_by(self) -> List['Node']:
|
||||
"See the class docstring"
|
||||
return self.edt._graph.required_by(self)
|
||||
|
||||
@property
|
||||
def depends_on(self):
|
||||
def depends_on(self) -> List['Node']:
|
||||
"See the class docstring"
|
||||
return self.edt._graph.depends_on(self)
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
def status(self) -> str:
|
||||
"See the class docstring"
|
||||
status = self._node.props.get("status")
|
||||
|
||||
|
@ -1080,31 +1109,31 @@ class Node:
|
|||
return as_string
|
||||
|
||||
@property
|
||||
def read_only(self):
|
||||
def read_only(self) -> bool:
|
||||
"See the class docstring"
|
||||
return "read-only" in self._node.props
|
||||
|
||||
@property
|
||||
def aliases(self):
|
||||
def aliases(self) -> List[str]:
|
||||
"See the class docstring"
|
||||
return [alias for alias, node in self._node.dt.alias2node.items()
|
||||
if node is self._node]
|
||||
|
||||
@property
|
||||
def buses(self):
|
||||
def buses(self) -> List[str]:
|
||||
"See the class docstring"
|
||||
if self._binding:
|
||||
return self._binding.buses
|
||||
return []
|
||||
|
||||
@property
|
||||
def on_buses(self):
|
||||
def on_buses(self) -> List[str]:
|
||||
"See the class docstring"
|
||||
bus_node = self.bus_node
|
||||
return bus_node.buses if bus_node else []
|
||||
|
||||
@property
|
||||
def flash_controller(self):
|
||||
def flash_controller(self) -> 'Node':
|
||||
"See the class docstring"
|
||||
|
||||
# The node path might be something like
|
||||
|
@ -1119,14 +1148,17 @@ class Node:
|
|||
|
||||
controller = self.parent.parent
|
||||
if controller.matching_compat == "soc-nv-flash":
|
||||
if controller.parent is None:
|
||||
_err(f"flash controller '{controller.path}' cannot be the root node")
|
||||
return controller.parent
|
||||
return controller
|
||||
|
||||
@property
|
||||
def spi_cs_gpio(self):
|
||||
def spi_cs_gpio(self) -> Optional[ControllerAndData]:
|
||||
"See the class docstring"
|
||||
|
||||
if not ("spi" in self.on_buses
|
||||
and self.bus_node
|
||||
and "cs-gpios" in self.bus_node.props):
|
||||
return None
|
||||
|
||||
|
@ -1135,18 +1167,26 @@ class Node:
|
|||
"chip select index for SPI")
|
||||
|
||||
parent_cs_lst = self.bus_node.props["cs-gpios"].val
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(parent_cs_lst, list)
|
||||
|
||||
# cs-gpios is indexed by the unit address
|
||||
cs_index = self.regs[0].addr
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(cs_index, int)
|
||||
|
||||
if cs_index >= len(parent_cs_lst):
|
||||
_err(f"index from 'regs' in {self!r} ({cs_index}) "
|
||||
"is >= number of cs-gpios in "
|
||||
f"{self.bus_node!r} ({len(parent_cs_lst)})")
|
||||
|
||||
return parent_cs_lst[cs_index]
|
||||
ret = parent_cs_lst[cs_index]
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(ret, ControllerAndData)
|
||||
return ret
|
||||
|
||||
@property
|
||||
def gpio_hogs(self):
|
||||
def gpio_hogs(self) -> List[ControllerAndData]:
|
||||
"See the class docstring"
|
||||
|
||||
if "gpio-hog" not in self.props:
|
||||
|
@ -1171,14 +1211,14 @@ class Node:
|
|||
|
||||
return res
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
if self.binding_path:
|
||||
binding = "binding " + self.binding_path
|
||||
else:
|
||||
binding = "no binding"
|
||||
return f"<Node {self.path} in '{self.edt.dts_path}', {binding}>"
|
||||
|
||||
def _init_binding(self):
|
||||
def _init_binding(self) -> None:
|
||||
# Initializes Node.matching_compat, Node._binding, and
|
||||
# Node.binding_path.
|
||||
#
|
||||
|
@ -1236,19 +1276,19 @@ class Node:
|
|||
# No binding found
|
||||
self._binding = self.binding_path = self.matching_compat = None
|
||||
|
||||
def _binding_from_properties(self):
|
||||
def _binding_from_properties(self) -> None:
|
||||
# Sets up a Binding object synthesized from the properties in the node.
|
||||
|
||||
if self.compats:
|
||||
_err(f"compatible in node with inferred binding: {self.path}")
|
||||
|
||||
# Synthesize a 'raw' binding as if it had been parsed from YAML.
|
||||
raw = {
|
||||
raw: Dict[str, Any] = {
|
||||
'description': 'Inferred binding from properties, via edtlib.',
|
||||
'properties': {},
|
||||
}
|
||||
for name, prop in self._node.props.items():
|
||||
pp = {}
|
||||
pp: Dict[str, str] = {}
|
||||
if prop.type == Type.EMPTY:
|
||||
pp["type"] = "boolean"
|
||||
elif prop.type == Type.BYTES:
|
||||
|
@ -1280,7 +1320,7 @@ class Node:
|
|||
self.compats = []
|
||||
self._binding = Binding(None, {}, raw=raw, require_compatible=False)
|
||||
|
||||
def _binding_from_parent(self):
|
||||
def _binding_from_parent(self) -> Optional[Binding]:
|
||||
# Returns the binding from 'child-binding:' in the parent node's
|
||||
# binding.
|
||||
|
||||
|
@ -1296,7 +1336,8 @@ class Node:
|
|||
|
||||
return None
|
||||
|
||||
def _bus_node(self, support_fixed_partitions_on_any_bus = True):
|
||||
def _bus_node(self, support_fixed_partitions_on_any_bus: bool = True
|
||||
) -> Optional['Node']:
|
||||
# Returns the value for self.bus_node. Relies on parent nodes being
|
||||
# initialized before their children.
|
||||
|
||||
|
@ -1319,7 +1360,8 @@ class Node:
|
|||
# Same bus node as parent (possibly None)
|
||||
return self.parent.bus_node
|
||||
|
||||
def _init_props(self, default_prop_types=False, err_on_deprecated=False):
|
||||
def _init_props(self, default_prop_types: bool = False,
|
||||
err_on_deprecated: bool = False) -> None:
|
||||
# Creates self.props. See the class docstring. Also checks that all
|
||||
# properties on the node are declared in its binding.
|
||||
|
||||
|
@ -1345,7 +1387,8 @@ class Node:
|
|||
None, err_on_deprecated)
|
||||
self.props[name] = Property(prop_spec, val, self)
|
||||
|
||||
def _init_prop(self, prop_spec, err_on_deprecated):
|
||||
def _init_prop(self, prop_spec: PropertySpec,
|
||||
err_on_deprecated: bool) -> None:
|
||||
# _init_props() helper for initializing a single property.
|
||||
# 'prop_spec' is a PropertySpec object from the node's binding.
|
||||
|
||||
|
@ -1383,8 +1426,11 @@ class Node:
|
|||
|
||||
self.props[name] = Property(prop_spec, val, self)
|
||||
|
||||
def _prop_val(self, name, prop_type, deprecated, required, default,
|
||||
specifier_space, err_on_deprecated):
|
||||
def _prop_val(self, name: str, prop_type: str,
|
||||
deprecated: bool, required: bool,
|
||||
default: PropertyValType,
|
||||
specifier_space: Optional[str],
|
||||
err_on_deprecated: bool) -> PropertyValType:
|
||||
# _init_prop() helper for getting the property's value
|
||||
#
|
||||
# name:
|
||||
|
@ -1431,7 +1477,7 @@ class Node:
|
|||
# format has already been checked in
|
||||
# _check_prop_by_type().
|
||||
if prop_type == "uint8-array":
|
||||
return bytes(default)
|
||||
return bytes(default) # type: ignore
|
||||
return default
|
||||
|
||||
return False if prop_type == "boolean" else None
|
||||
|
@ -1487,7 +1533,7 @@ class Node:
|
|||
# to have a 'type: ...'. No Property object is created for it.
|
||||
return None
|
||||
|
||||
def _check_undeclared_props(self):
|
||||
def _check_undeclared_props(self) -> None:
|
||||
# Checks that all properties are declared in the binding
|
||||
|
||||
for prop_name in self._node.props:
|
||||
|
@ -1500,12 +1546,15 @@ class Node:
|
|||
"interrupt-parent", "interrupts-extended", "device_type"}:
|
||||
continue
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert self._binding
|
||||
|
||||
if prop_name not in self._binding.prop2specs:
|
||||
_err(f"'{prop_name}' appears in {self._node.path} in "
|
||||
f"{self.edt.dts_path}, but is not declared in "
|
||||
f"'properties:' in {self.binding_path}")
|
||||
|
||||
def _init_ranges(self):
|
||||
def _init_ranges(self) -> None:
|
||||
# Initializes self.ranges
|
||||
node = self._node
|
||||
|
||||
|
@ -1514,17 +1563,17 @@ class Node:
|
|||
if "ranges" not in node.props:
|
||||
return
|
||||
|
||||
child_address_cells = node.props.get("#address-cells")
|
||||
raw_child_address_cells = node.props.get("#address-cells")
|
||||
parent_address_cells = _address_cells(node)
|
||||
if child_address_cells is None:
|
||||
if raw_child_address_cells is None:
|
||||
child_address_cells = 2 # Default value per DT spec.
|
||||
else:
|
||||
child_address_cells = child_address_cells.to_num()
|
||||
child_size_cells = node.props.get("#size-cells")
|
||||
if child_size_cells is None:
|
||||
child_address_cells = raw_child_address_cells.to_num()
|
||||
raw_child_size_cells = node.props.get("#size-cells")
|
||||
if raw_child_size_cells is None:
|
||||
child_size_cells = 1 # Default value per DT spec.
|
||||
else:
|
||||
child_size_cells = child_size_cells.to_num()
|
||||
child_size_cells = raw_child_size_cells.to_num()
|
||||
|
||||
# Number of cells for one translation 3-tuple in 'ranges'
|
||||
entry_cells = child_address_cells + parent_address_cells + child_size_cells
|
||||
|
@ -1567,7 +1616,7 @@ class Node:
|
|||
parent_bus_cells, parent_bus_addr,
|
||||
length_cells, length))
|
||||
|
||||
def _init_regs(self):
|
||||
def _init_regs(self) -> None:
|
||||
# Initializes self.regs
|
||||
|
||||
node = self._node
|
||||
|
@ -1601,7 +1650,7 @@ class Node:
|
|||
|
||||
_add_names(node, "reg", self.regs)
|
||||
|
||||
def _init_pinctrls(self):
|
||||
def _init_pinctrls(self) -> None:
|
||||
# Initializes self.pinctrls from any pinctrl-<index> properties
|
||||
|
||||
node = self._node
|
||||
|
@ -1629,7 +1678,7 @@ class Node:
|
|||
|
||||
_add_names(node, "pinctrl", self.pinctrls)
|
||||
|
||||
def _init_interrupts(self):
|
||||
def _init_interrupts(self) -> None:
|
||||
# Initializes self.interrupts
|
||||
|
||||
node = self._node
|
||||
|
@ -1646,7 +1695,11 @@ class Node:
|
|||
|
||||
_add_names(node, "interrupt", self.interrupts)
|
||||
|
||||
def _standard_phandle_val_list(self, prop, specifier_space):
|
||||
def _standard_phandle_val_list(
|
||||
self,
|
||||
prop: dtlib_Property,
|
||||
specifier_space: Optional[str]
|
||||
) -> List[Optional[ControllerAndData]]:
|
||||
# Parses a property like
|
||||
#
|
||||
# <prop.name> = <phandle cell phandle cell ...>;
|
||||
|
@ -1696,7 +1749,7 @@ class Node:
|
|||
# if there is no specifier space in _check_prop_by_type().
|
||||
specifier_space = prop.name[:-1]
|
||||
|
||||
res = []
|
||||
res: List[Optional[ControllerAndData]] = []
|
||||
|
||||
for item in _phandle_val_list(prop, specifier_space):
|
||||
if item is None:
|
||||
|
@ -1720,7 +1773,12 @@ class Node:
|
|||
|
||||
return res
|
||||
|
||||
def _named_cells(self, controller, data, basename):
|
||||
def _named_cells(
|
||||
self,
|
||||
controller: 'Node',
|
||||
data: bytes,
|
||||
basename: str
|
||||
) -> Dict[str, int]:
|
||||
# Returns a dictionary that maps <basename>-cells names given in the
|
||||
# binding for 'controller' to cell values. 'data' is the raw data, as a
|
||||
# byte array.
|
||||
|
@ -1730,7 +1788,7 @@ class Node:
|
|||
f"for {self._node!r} lacks binding")
|
||||
|
||||
if basename in controller._binding.specifier2cells:
|
||||
cell_names = controller._binding.specifier2cells[basename]
|
||||
cell_names: List[str] = controller._binding.specifier2cells[basename]
|
||||
else:
|
||||
# Treat no *-cells in the binding the same as an empty *-cells, so
|
||||
# that bindings don't have to have e.g. an empty 'clock-cells:' for
|
||||
|
@ -2111,13 +2169,11 @@ class EDT:
|
|||
for dt_node in self._dt.node_iter():
|
||||
# Warning: We depend on parent Nodes being created before their
|
||||
# children. This is guaranteed by node_iter().
|
||||
node = Node()
|
||||
node.edt = self
|
||||
node._node = dt_node
|
||||
if "compatible" in node._node.props:
|
||||
node.compats = node._node.props["compatible"].to_strings()
|
||||
if "compatible" in dt_node.props:
|
||||
compats = dt_node.props["compatible"].to_strings()
|
||||
else:
|
||||
node.compats = []
|
||||
compats = []
|
||||
node = Node(dt_node, self, compats)
|
||||
node.bus_node = node._bus_node(self._fixed_partitions_no_bus)
|
||||
node._init_binding()
|
||||
node._init_regs()
|
||||
|
@ -2577,13 +2633,10 @@ def _check_prop_by_type(prop_name: str,
|
|||
f"which has type {prop_type}")
|
||||
|
||||
|
||||
def _translate(addr, node):
|
||||
def _translate(addr: int, node: dtlib_Node) -> int:
|
||||
# Recursively translates 'addr' on 'node' to the address space(s) of its
|
||||
# parent(s), by looking at 'ranges' properties. Returns the translated
|
||||
# address.
|
||||
#
|
||||
# node:
|
||||
# dtlib.Node instance
|
||||
|
||||
if not node.parent or "ranges" not in node.parent.props:
|
||||
# No translation
|
||||
|
@ -2628,11 +2681,11 @@ def _translate(addr, node):
|
|||
return addr
|
||||
|
||||
|
||||
def _add_names(node, names_ident, objs):
|
||||
def _add_names(node: dtlib_Node, names_ident: str, objs: Any) -> None:
|
||||
# Helper for registering names from <foo>-names properties.
|
||||
#
|
||||
# node:
|
||||
# edtlib.Node instance
|
||||
# Node which has a property that might need named elements.
|
||||
#
|
||||
# names-ident:
|
||||
# The <foo> part of <foo>-names, e.g. "reg" for "reg-names"
|
||||
|
@ -2659,12 +2712,12 @@ def _add_names(node, names_ident, objs):
|
|||
obj.name = None
|
||||
|
||||
|
||||
def _interrupt_parent(node):
|
||||
def _interrupt_parent(start_node: dtlib_Node) -> dtlib_Node:
|
||||
# Returns the node pointed at by the closest 'interrupt-parent', searching
|
||||
# the parents of 'node'. As of writing, this behavior isn't specified in
|
||||
# the DT spec., but seems to match what some .dts files except.
|
||||
|
||||
start_node = node
|
||||
node: Optional[dtlib_Node] = start_node
|
||||
|
||||
while node:
|
||||
if "interrupt-parent" in node.props:
|
||||
|
@ -2675,7 +2728,7 @@ def _interrupt_parent(node):
|
|||
f"nor any of its parents has an 'interrupt-parent' property")
|
||||
|
||||
|
||||
def _interrupts(node):
|
||||
def _interrupts(node: dtlib_Node) -> List[Tuple[dtlib_Node, bytes]]:
|
||||
# Returns a list of (<controller>, <data>) tuples, with one tuple per
|
||||
# interrupt generated by 'node'. <controller> is the destination of the
|
||||
# interrupt (possibly after mapping through an 'interrupt-map'), and <data>
|
||||
|
@ -2684,8 +2737,15 @@ def _interrupts(node):
|
|||
# Takes precedence over 'interrupts' if both are present
|
||||
if "interrupts-extended" in node.props:
|
||||
prop = node.props["interrupts-extended"]
|
||||
return [_map_interrupt(node, iparent, spec)
|
||||
for iparent, spec in _phandle_val_list(prop, "interrupt")]
|
||||
|
||||
ret: List[Tuple[dtlib_Node, bytes]] = []
|
||||
for entry in _phandle_val_list(prop, "interrupt"):
|
||||
if entry is None:
|
||||
_err(f"node '{node.path}' interrupts-extended property "
|
||||
"has an empty element")
|
||||
iparent, spec = entry
|
||||
ret.append(_map_interrupt(node, iparent, spec))
|
||||
return ret
|
||||
|
||||
if "interrupts" in node.props:
|
||||
# Treat 'interrupts' as a special case of 'interrupts-extended', with
|
||||
|
@ -2701,7 +2761,11 @@ def _interrupts(node):
|
|||
return []
|
||||
|
||||
|
||||
def _map_interrupt(child, parent, child_spec):
|
||||
def _map_interrupt(
|
||||
child: dtlib_Node,
|
||||
parent: dtlib_Node,
|
||||
child_spec: bytes
|
||||
) -> Tuple[dtlib_Node, bytes]:
|
||||
# Translates an interrupt headed from 'child' to 'parent' with data
|
||||
# 'child_spec' through any 'interrupt-map' properties. Returns a
|
||||
# (<controller>, <data>) tuple with the final destination after mapping.
|
||||
|
@ -2733,7 +2797,12 @@ def _map_interrupt(child, parent, child_spec):
|
|||
return (parent, raw_spec[4*own_address_cells(parent):])
|
||||
|
||||
|
||||
def _map_phandle_array_entry(child, parent, child_spec, basename):
|
||||
def _map_phandle_array_entry(
|
||||
child: dtlib_Node,
|
||||
parent: dtlib_Node,
|
||||
child_spec: bytes,
|
||||
basename: str
|
||||
) -> Tuple[dtlib_Node, bytes]:
|
||||
# Returns a (<controller>, <data>) tuple with the final destination after
|
||||
# mapping through any '<basename>-map' (e.g. gpio-map) properties. See
|
||||
# _map_interrupt().
|
||||
|
@ -2750,7 +2819,14 @@ def _map_phandle_array_entry(child, parent, child_spec, basename):
|
|||
require_controller=False)
|
||||
|
||||
|
||||
def _map(prefix, child, parent, child_spec, spec_len_fn, require_controller):
|
||||
def _map(
|
||||
prefix: str,
|
||||
child: dtlib_Node,
|
||||
parent: dtlib_Node,
|
||||
child_spec: bytes,
|
||||
spec_len_fn: Callable[[dtlib_Node], int],
|
||||
require_controller: bool
|
||||
) -> Tuple[dtlib_Node, bytes]:
|
||||
# Common code for mapping through <prefix>-map properties, e.g.
|
||||
# interrupt-map and gpio-map.
|
||||
#
|
||||
|
@ -2820,11 +2896,16 @@ def _map(prefix, child, parent, child_spec, spec_len_fn, require_controller):
|
|||
return _map(prefix, parent, map_parent, parent_spec, spec_len_fn,
|
||||
require_controller)
|
||||
|
||||
_err(f"child specifier for {child!r} ({child_spec}) "
|
||||
_err(f"child specifier for {child!r} ({child_spec!r}) "
|
||||
f"does not appear in {map_prop!r}")
|
||||
|
||||
|
||||
def _mask(prefix, child, parent, child_spec):
|
||||
def _mask(
|
||||
prefix: str,
|
||||
child: dtlib_Node,
|
||||
parent: dtlib_Node,
|
||||
child_spec: bytes
|
||||
) -> bytes:
|
||||
# Common code for handling <prefix>-mask properties, e.g. interrupt-mask.
|
||||
# See _map() for the parameters.
|
||||
|
||||
|
@ -2841,7 +2922,13 @@ def _mask(prefix, child, parent, child_spec):
|
|||
return _and(child_spec, mask)
|
||||
|
||||
|
||||
def _pass_thru(prefix, child, parent, child_spec, parent_spec):
|
||||
def _pass_thru(
|
||||
prefix: str,
|
||||
child: dtlib_Node,
|
||||
parent: dtlib_Node,
|
||||
child_spec: bytes,
|
||||
parent_spec: bytes
|
||||
) -> bytes:
|
||||
# Common code for handling <prefix>-map-thru properties, e.g.
|
||||
# interrupt-pass-thru.
|
||||
#
|
||||
|
@ -2867,7 +2954,7 @@ def _pass_thru(prefix, child, parent, child_spec, parent_spec):
|
|||
return res[-len(parent_spec):]
|
||||
|
||||
|
||||
def _raw_unit_addr(node):
|
||||
def _raw_unit_addr(node: dtlib_Node) -> bytes:
|
||||
# _map_interrupt() helper. Returns the unit address (derived from 'reg' and
|
||||
# #address-cells) as a raw 'bytes'
|
||||
|
||||
|
@ -2884,7 +2971,7 @@ def _raw_unit_addr(node):
|
|||
return node.props['reg'].value[:addr_len]
|
||||
|
||||
|
||||
def _and(b1, b2):
|
||||
def _and(b1: bytes, b2: bytes) -> bytes:
|
||||
# Returns the bitwise AND of the two 'bytes' objects b1 and b2. Pads
|
||||
# with ones on the left if the lengths are not equal.
|
||||
|
||||
|
@ -2894,7 +2981,7 @@ def _and(b1, b2):
|
|||
b2.rjust(maxlen, b'\xff')))
|
||||
|
||||
|
||||
def _or(b1, b2):
|
||||
def _or(b1: bytes, b2: bytes) -> bytes:
|
||||
# Returns the bitwise OR of the two 'bytes' objects b1 and b2. Pads with
|
||||
# zeros on the left if the lengths are not equal.
|
||||
|
||||
|
@ -2904,14 +2991,17 @@ def _or(b1, b2):
|
|||
b2.rjust(maxlen, b'\x00')))
|
||||
|
||||
|
||||
def _not(b):
|
||||
def _not(b: bytes) -> bytes:
|
||||
# Returns the bitwise not of the 'bytes' object 'b'
|
||||
|
||||
# ANDing with 0xFF avoids negative numbers
|
||||
return bytes(~x & 0xFF for x in b)
|
||||
|
||||
|
||||
def _phandle_val_list(prop, n_cells_name):
|
||||
def _phandle_val_list(
|
||||
prop: dtlib_Property,
|
||||
n_cells_name: str
|
||||
) -> List[Optional[Tuple[dtlib_Node, bytes]]]:
|
||||
# Parses a '<phandle> <value> <phandle> <value> ...' value. The number of
|
||||
# cells that make up each <value> is derived from the node pointed at by
|
||||
# the preceding <phandle>.
|
||||
|
@ -2923,15 +3013,13 @@ def _phandle_val_list(prop, n_cells_name):
|
|||
# The <name> part of the #<name>-cells property to look for on the nodes
|
||||
# the phandles point to, e.g. "gpio" for #gpio-cells.
|
||||
#
|
||||
# Returns a list[Optional[tuple]].
|
||||
#
|
||||
# Each tuple in the list is a (<node>, <value>) pair, where <node>
|
||||
# Each tuple in the return value is a (<node>, <value>) pair, where <node>
|
||||
# is the node pointed at by <phandle>. If <phandle> does not refer
|
||||
# to a node, the entire list element is None.
|
||||
|
||||
full_n_cells_name = f"#{n_cells_name}-cells"
|
||||
|
||||
res = []
|
||||
res: List[Optional[Tuple[dtlib_Node, bytes]]] = []
|
||||
|
||||
raw = prop.value
|
||||
while raw:
|
||||
|
@ -2961,25 +3049,29 @@ def _phandle_val_list(prop, n_cells_name):
|
|||
return res
|
||||
|
||||
|
||||
def _address_cells(node):
|
||||
def _address_cells(node: dtlib_Node) -> int:
|
||||
# Returns the #address-cells setting for 'node', giving the number of <u32>
|
||||
# cells used to encode the address in the 'reg' property
|
||||
if TYPE_CHECKING:
|
||||
assert node.parent
|
||||
|
||||
if "#address-cells" in node.parent.props:
|
||||
return node.parent.props["#address-cells"].to_num()
|
||||
return 2 # Default value per DT spec.
|
||||
|
||||
|
||||
def _size_cells(node):
|
||||
def _size_cells(node: dtlib_Node) -> int:
|
||||
# Returns the #size-cells setting for 'node', giving the number of <u32>
|
||||
# cells used to encode the size in the 'reg' property
|
||||
if TYPE_CHECKING:
|
||||
assert node.parent
|
||||
|
||||
if "#size-cells" in node.parent.props:
|
||||
return node.parent.props["#size-cells"].to_num()
|
||||
return 1 # Default value per DT spec.
|
||||
|
||||
|
||||
def _interrupt_cells(node):
|
||||
def _interrupt_cells(node: dtlib_Node) -> int:
|
||||
# Returns the #interrupt-cells property value on 'node', erroring out if
|
||||
# 'node' has no #interrupt-cells property
|
||||
|
||||
|
@ -2988,11 +3080,11 @@ def _interrupt_cells(node):
|
|||
return node.props["#interrupt-cells"].to_num()
|
||||
|
||||
|
||||
def _slice(node, prop_name, size, size_hint):
|
||||
def _slice(node, prop_name, size, size_hint) -> List[bytes]:
|
||||
return _slice_helper(node, prop_name, size, size_hint, EDTError)
|
||||
|
||||
|
||||
def _check_dt(dt):
|
||||
def _check_dt(dt: DT) -> None:
|
||||
# Does devicetree sanity checks. dtlib is meant to be general and
|
||||
# anything-goes except for very special properties like phandle, but in
|
||||
# edtlib we can be pickier.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue