Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Buildscripts/DevicetreeCompiler/source/binding_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def parse_binding(file_path: str, binding_dirs: list[str]) -> Binding:
description=details.get('description', '').strip(),
default=details.get('default', None),
element_type=details.get('element-type', None),
min=details.get('min', None),
max=details.get('max', None),
)
properties_dict[name] = prop
filename = os.path.basename(file_path)
Expand Down
21 changes: 21 additions & 0 deletions Buildscripts/DevicetreeCompiler/source/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,25 @@ def resolve_phandle_array_entries(device_property, devices):
entries.append(str(item))
return entries

def validate_property_range(device: Device, binding_property, value) -> None:
"""Enforces a binding's optional min/max on int-typed properties. Silently skips
values that aren't parseable as a plain integer literal (e.g. a passed-through
symbolic #define) - those can't be range-checked at compile time."""
if binding_property.min is None and binding_property.max is None:
return
try:
numeric_value = int(value, 0) if isinstance(value, str) else int(value)
except (TypeError, ValueError):
return
if binding_property.min is not None and numeric_value < binding_property.min:
raise DevicetreeException(
f"Device '{device.node_name}' property '{binding_property.name}' value {numeric_value} is below minimum {binding_property.min}"
)
if binding_property.max is not None and numeric_value > binding_property.max:
raise DevicetreeException(
f"Device '{device.node_name}' property '{binding_property.name}' value {numeric_value} is above maximum {binding_property.max}"
)

def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], devices: list[Device]) -> list:
compatible_property = find_device_property(device, "compatible")
if compatible_property is None:
Expand Down Expand Up @@ -168,6 +187,7 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de

if device_property is None:
if binding_property.default is not None:
validate_property_range(device, binding_property, binding_property.default)
temp_prop = DeviceProperty(
name=binding_property.name,
type=binding_property.type,
Expand All @@ -184,6 +204,7 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
else:
raise DevicetreeException(f"Device {device.node_name} doesn't have property '{binding_property.name}' and no default value is set")
else:
validate_property_range(device, binding_property, device_property.value)
result.append(property_to_string(device_property, devices))

return result, phandle_arrays
Expand Down
2 changes: 2 additions & 0 deletions Buildscripts/DevicetreeCompiler/source/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class BindingProperty:
description: str
default: object = None
element_type: str = None
min: object = None
max: object = None

@dataclass
class Binding:
Expand Down
140 changes: 139 additions & 1 deletion Buildscripts/DevicetreeCompiler/tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,139 @@ def test_compile_invalid_dts():
print("PASSED")
return True

def write_minmax_config(tmp_dir, device_property_line, binding_min=0, binding_max=3, binding_default=1):
config_dir = os.path.join(tmp_dir, "minmax_data")
bindings_dir = os.path.join(config_dir, "bindings")
os.makedirs(bindings_dir)

with open(os.path.join(config_dir, "devicetree.yaml"), "w") as f:
f.write("dts: test.dts\nbindings: bindings")

with open(os.path.join(config_dir, "test.dts"), "w") as f:
f.write(f"""/dts-v1/;

/ {{
compatible = "test,root";
model = "Test Model";

test-device@0 {{
compatible = "test,minmax-device";
{device_property_line}
}};
}};
""")

with open(os.path.join(bindings_dir, "test,root.yaml"), "w") as f:
f.write("description: Test root binding\ncompatible: \"test,root\"\nproperties:\n model:\n type: string\n")

with open(os.path.join(bindings_dir, "test,minmax-device.yaml"), "w") as f:
f.write(f"""description: Test min/max binding
compatible: "test,minmax-device"
properties:
ranged-prop:
type: int
default: {binding_default}
min: {binding_min}
max: {binding_max}
""")

return config_dir

def test_minmax_within_range_succeeds():
print("Running test_minmax_within_range_succeeds...")
with tempfile.TemporaryDirectory() as tmp_dir:
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <2>;")
output_dir = os.path.join(tmp_dir, "output")
os.makedirs(output_dir)

result = run_compiler(config_dir, output_dir)

if result.returncode != 0:
print(f"FAILED: Compilation should have succeeded: {result.stderr} {result.stdout}")
return False

print("PASSED")
return True

def test_minmax_below_minimum_fails():
print("Running test_minmax_below_minimum_fails...")
with tempfile.TemporaryDirectory() as tmp_dir:
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <-1>;")
output_dir = os.path.join(tmp_dir, "output")
os.makedirs(output_dir)

result = run_compiler(config_dir, output_dir)

if result.returncode == 0:
print("FAILED: Compilation should have failed for a below-minimum value")
return False

if "below minimum" not in result.stdout:
print(f"FAILED: Expected 'below minimum' error message, got: {result.stdout}")
return False

print("PASSED")
return True

def test_minmax_above_maximum_fails():
print("Running test_minmax_above_maximum_fails...")
with tempfile.TemporaryDirectory() as tmp_dir:
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <7>;")
output_dir = os.path.join(tmp_dir, "output")
os.makedirs(output_dir)

result = run_compiler(config_dir, output_dir)

if result.returncode == 0:
print("FAILED: Compilation should have failed for an above-maximum value")
return False

if "above maximum" not in result.stdout:
print(f"FAILED: Expected 'above maximum' error message, got: {result.stdout}")
return False

print("PASSED")
return True

def test_minmax_out_of_range_default_fails():
print("Running test_minmax_out_of_range_default_fails...")
with tempfile.TemporaryDirectory() as tmp_dir:
# Property omitted from the .dts entirely, so the (invalid) binding default is used.
config_dir = write_minmax_config(tmp_dir, "", binding_default=9)
output_dir = os.path.join(tmp_dir, "output")
os.makedirs(output_dir)

result = run_compiler(config_dir, output_dir)

if result.returncode == 0:
print("FAILED: Compilation should have failed for an out-of-range default value")
return False

if "above maximum" not in result.stdout:
print(f"FAILED: Expected 'above maximum' error message, got: {result.stdout}")
return False

print("PASSED")
return True

def test_minmax_symbolic_value_skips_validation():
print("Running test_minmax_symbolic_value_skips_validation...")
with tempfile.TemporaryDirectory() as tmp_dir:
# A passed-through symbolic constant can't be range-checked at compile time and must
# not be rejected just because a min/max is declared.
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <SOME_DEFINE>;")
output_dir = os.path.join(tmp_dir, "output")
os.makedirs(output_dir)

result = run_compiler(config_dir, output_dir)

if result.returncode != 0:
print(f"FAILED: Compilation should have succeeded for a symbolic value: {result.stderr} {result.stdout}")
return False

print("PASSED")
return True

def test_compile_missing_config():
print("Running test_compile_missing_config...")
with tempfile.TemporaryDirectory() as output_dir:
Expand All @@ -96,7 +229,12 @@ def test_compile_missing_config():
tests = [
test_compile_success,
test_compile_invalid_dts,
test_compile_missing_config
test_compile_missing_config,
test_minmax_within_range_succeeds,
test_minmax_below_minimum_fails,
test_minmax_above_maximum_fails,
test_minmax_out_of_range_default_fails,
test_minmax_symbolic_value_skips_validation
]

failed = 0
Expand Down
5 changes: 2 additions & 3 deletions Devices/cyd-2432s024r/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
file(GLOB_RECURSE SOURCE_FILES source/*.c*)

idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046 PwmBacklight driver vfs fatfs
REQUIRES TactilityKernel driver
)
21 changes: 0 additions & 21 deletions Devices/cyd-2432s024r/Source/Configuration.cpp

This file was deleted.

46 changes: 0 additions & 46 deletions Devices/cyd-2432s024r/Source/devices/Display.cpp

This file was deleted.

28 changes: 0 additions & 28 deletions Devices/cyd-2432s024r/Source/devices/Display.h

This file was deleted.

32 changes: 27 additions & 5 deletions Devices/cyd-2432s024r/cyd,2432s024r.dts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
#include <tactility/bindings/esp32_gpio.h>
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/display_placeholder.h>
#include <tactility/bindings/pointer_placeholder.h>
#include <tactility/bindings/esp32_uart.h>
#include <tactility/bindings/esp32_ledc_backlight.h>
#include <bindings/ili9341.h>
#include <bindings/xpt2046.h>

/ {
compatible = "root";
Expand All @@ -23,6 +24,15 @@
gpio-count = <40>;
};

display_backlight {
compatible = "espressif,esp32-ledc-backlight";
// Off by default so display power-on won't show the screen from before the last power loss.
// The display backlight is turned on during the boot process.
status = "disabled";
pin-backlight = <&gpio0 27 GPIO_FLAG_NONE>;
frequency-hz = <512>;
};

spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
Expand All @@ -33,11 +43,23 @@
<&gpio0 33 GPIO_FLAG_NONE>; // Touch

display@0 {
compatible = "display-placeholder";
compatible = "ilitek,ili9341";
horizontal-resolution = <240>;
vertical-resolution = <320>;
swap-xy;
mirror-x;
mirror-y;
pixel-clock-hz = <40000000>;
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
backlight = <&display_backlight>;
};

touch@1 {
compatible = "pointer-placeholder";
compatible = "xptek,xpt2046";
x-max = <240>;
y-max = <320>;
swap-xy;
mirror-y;
};
};

Expand All @@ -61,4 +83,4 @@
pin-tx = <&gpio0 1 GPIO_FLAG_NONE>;
pin-rx = <&gpio0 3 GPIO_FLAG_NONE>;
};
};
};
2 changes: 2 additions & 0 deletions Devices/cyd-2432s024r/device.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ hardware.target=ESP32
hardware.flashSize=4MB
hardware.spiRam=false

dependencies.useDeprecatedHal=false

storage.userDataLocation=SD

display.size=2.4"
Expand Down
2 changes: 2 additions & 0 deletions Devices/cyd-2432s024r/devicetree.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
dependencies:
- Platforms/platform-esp32
- Drivers/ili9341-module
- Drivers/xpt2046-module
dts: cyd,2432s024r.dts
Loading
Loading