mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-04-19 09:55:06 +00:00
Devicetree implementation WIP
This commit is contained in:
parent
c98cb2bf10
commit
e1a72ba9f0
4
Buildscripts/devicetree-compiler/.gitignore
vendored
Normal file
4
Buildscripts/devicetree-compiler/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.venv/
|
||||
.idea/
|
||||
__pycache__/
|
||||
build/
|
||||
27
Buildscripts/devicetree-compiler/compile.py
Normal file
27
Buildscripts/devicetree-compiler/compile.py
Normal file
@ -0,0 +1,27 @@
|
||||
import os
|
||||
|
||||
from source.printing import *
|
||||
from source.main import *
|
||||
|
||||
def print_help():
|
||||
print("Usage: python compile.py [in_file] [out_path] [arguments]\n")
|
||||
print(f"\t[in_file] the .dts file")
|
||||
print(f"\t[out_path] output folder for C file output")
|
||||
print("")
|
||||
print("Optional arguments:\n")
|
||||
print("\t--help prints this help text")
|
||||
print("\t--verbose output debug info")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "--help" in sys.argv:
|
||||
print_help()
|
||||
sys.exit()
|
||||
if len(sys.argv) < 3:
|
||||
print_error("Missing argument")
|
||||
print_help()
|
||||
sys.exit()
|
||||
is_verbose = "--verbose" in sys.argv
|
||||
devictree_yaml_config = sys.argv[1]
|
||||
output_path = sys.argv[2]
|
||||
main(devictree_yaml_config, output_path, is_verbose)
|
||||
|
||||
17
Buildscripts/devicetree-compiler/source/binding_files.py
Normal file
17
Buildscripts/devicetree-compiler/source/binding_files.py
Normal file
@ -0,0 +1,17 @@
|
||||
import os
|
||||
|
||||
def find_bindings(directory_path: str) -> list[str]:
|
||||
yaml_files = []
|
||||
for root, dirs, files in os.walk(directory_path):
|
||||
for file in files:
|
||||
if file.endswith(".yaml"):
|
||||
full_path = os.path.join(root, file)
|
||||
yaml_files.append(os.path.abspath(full_path))
|
||||
return yaml_files
|
||||
|
||||
def find_all_bindings(directory_paths: list[str]) -> list[str]:
|
||||
yaml_files = []
|
||||
for directory_path in directory_paths:
|
||||
new_paths = find_bindings(directory_path)
|
||||
yaml_files += new_paths
|
||||
return yaml_files
|
||||
54
Buildscripts/devicetree-compiler/source/binding_parser.py
Normal file
54
Buildscripts/devicetree-compiler/source/binding_parser.py
Normal file
@ -0,0 +1,54 @@
|
||||
import yaml
|
||||
import os
|
||||
from .models import Binding, BindingProperty
|
||||
|
||||
def parse_binding(file_path: str, binding_dirs: list[str]) -> Binding:
|
||||
with open(file_path, 'r') as f:
|
||||
data = yaml.safe_load(f)
|
||||
|
||||
description = data.get('description', '')
|
||||
bus = data.get('bus', None)
|
||||
properties_dict = {}
|
||||
|
||||
# Handle inclusions
|
||||
includes = data.get('include', [])
|
||||
for include_file in includes:
|
||||
include_path = None
|
||||
for binding_dir in binding_dirs:
|
||||
potential_path = os.path.join(binding_dir, include_file)
|
||||
if os.path.exists(potential_path):
|
||||
include_path = potential_path
|
||||
break
|
||||
|
||||
if not include_path:
|
||||
print(f"Warning: Could not find include file {include_file}")
|
||||
|
||||
parent_binding = parse_binding(include_path, binding_dirs)
|
||||
if not description and parent_binding.description:
|
||||
description = parent_binding.description
|
||||
if not bus and parent_binding.bus:
|
||||
bus = parent_binding.bus
|
||||
for prop in parent_binding.properties:
|
||||
properties_dict[prop.name] = prop
|
||||
|
||||
# Parse local properties
|
||||
properties_raw = data.get('properties', {})
|
||||
for name, details in properties_raw.items():
|
||||
prop = BindingProperty(
|
||||
name=name,
|
||||
type=details.get('type', 'unknown'),
|
||||
required=details.get('required', False),
|
||||
description=details.get('description', '').strip(),
|
||||
includes=includes
|
||||
)
|
||||
properties_dict[name] = prop
|
||||
|
||||
filename = os.path.basename(file_path)
|
||||
compatible = filename.removesuffix(".yaml").removesuffix(".yml")
|
||||
return Binding(
|
||||
filename=filename,
|
||||
compatible=compatible,
|
||||
description=description.strip(),
|
||||
properties=list(properties_dict.values()),
|
||||
bus=bus
|
||||
)
|
||||
49
Buildscripts/devicetree-compiler/source/config.py
Normal file
49
Buildscripts/devicetree-compiler/source/config.py
Normal file
@ -0,0 +1,49 @@
|
||||
from dataclasses import dataclass, field
|
||||
import yaml
|
||||
import os
|
||||
|
||||
@dataclass
|
||||
class DeviceTreeConfig:
|
||||
dependencies: list[str] = field(default_factory=list)
|
||||
bindings: list[str] = field(default_factory=list)
|
||||
dts: str = ""
|
||||
|
||||
def parse_config(file_path: str, project_root: str) -> list[DeviceTreeConfig]:
|
||||
"""
|
||||
Parses devicetree.yaml and recursively finds dependencies.
|
||||
Returns a list of DeviceTreeConfig objects in post-order (dependencies first).
|
||||
"""
|
||||
config = DeviceTreeConfig([], [], "")
|
||||
visited = set()
|
||||
|
||||
def _parse_recursive(current_path: str, is_root: bool):
|
||||
abs_path = os.path.abspath(current_path)
|
||||
if abs_path in visited:
|
||||
return
|
||||
visited.add(abs_path)
|
||||
|
||||
# Try to see if it's a directory and contains devicetree.yaml
|
||||
if os.path.isdir(abs_path):
|
||||
abs_path = os.path.join(abs_path, "devicetree.yaml")
|
||||
|
||||
with open(abs_path, 'r') as f:
|
||||
data = yaml.safe_load(f) or {}
|
||||
|
||||
# Handle dependencies before adding current config (post-order)
|
||||
deps = data.get("dependencies", [])
|
||||
for dep in deps:
|
||||
# Dependencies are relative to project_root
|
||||
dep_path = os.path.join(project_root, dep)
|
||||
_parse_recursive(dep_path, False)
|
||||
|
||||
if is_root:
|
||||
config.dependencies += deps
|
||||
dts_path = data.get("dts", "")
|
||||
config.dts = os.path.join(current_path, dts_path)
|
||||
|
||||
bindings = data.get("bindings", "")
|
||||
bindings_resolved = os.path.join(current_path, bindings)
|
||||
config.bindings.append(bindings_resolved)
|
||||
|
||||
_parse_recursive(file_path, True)
|
||||
return config
|
||||
9
Buildscripts/devicetree-compiler/source/files.py
Normal file
9
Buildscripts/devicetree-compiler/source/files.py
Normal file
@ -0,0 +1,9 @@
|
||||
def read_file(path: str):
|
||||
with open(path, "r") as file:
|
||||
result = file.read()
|
||||
return result
|
||||
|
||||
def write_file(path: str, content: str):
|
||||
with open(path, "w") as file:
|
||||
result = file.write(content)
|
||||
return result
|
||||
200
Buildscripts/devicetree-compiler/source/generator.py
Normal file
200
Buildscripts/devicetree-compiler/source/generator.py
Normal file
@ -0,0 +1,200 @@
|
||||
import os.path
|
||||
from textwrap import dedent
|
||||
|
||||
from source.models import *
|
||||
|
||||
def write_include(file, include: IncludeC, verbose: bool):
|
||||
if verbose:
|
||||
print("Processing include:")
|
||||
print(f" {include.statement}")
|
||||
file.write(include.statement)
|
||||
file.write('\n')
|
||||
|
||||
def get_device_identifier_safe(device: Device):
|
||||
if device.identifier == "/":
|
||||
return "root"
|
||||
else:
|
||||
return device.identifier
|
||||
|
||||
def get_device_type_name(device: Device, bindings: list[Binding]):
|
||||
device_binding = find_device_binding(device, bindings)
|
||||
if device_binding == None:
|
||||
raise Exception(f"Binding not found for {device.identifier}")
|
||||
compatible_safe = device_binding.compatible.split(",")[-1]
|
||||
return compatible_safe.replace("-", "_")
|
||||
|
||||
def find_device_property(device: Device, name: str) -> DeviceProperty:
|
||||
for property in device.properties:
|
||||
if property.name == name:
|
||||
return property
|
||||
return None
|
||||
|
||||
def find_binding_property(binding: Binding, name: str) -> BindingProperty:
|
||||
for property in binding.properties:
|
||||
if property.name == name:
|
||||
return property
|
||||
return None
|
||||
|
||||
def find_device_binding(device: Device, bindings: list[Binding]) -> Binding:
|
||||
compatible_property = find_device_property(device, "compatible")
|
||||
if compatible_property is None:
|
||||
raise Exception(f"property 'compatible' not found in device {device.identifier}")
|
||||
for binding in bindings:
|
||||
if binding.compatible == compatible_property.value:
|
||||
return binding
|
||||
return None
|
||||
|
||||
def find_binding(compatible: str, bindings: list[Binding]) -> Binding:
|
||||
for binding in bindings:
|
||||
if binding.compatible == compatible:
|
||||
return binding
|
||||
return None
|
||||
|
||||
def property_to_string(property: Property) -> str:
|
||||
type = property.type
|
||||
if type == "value":
|
||||
return property.value
|
||||
elif type == "text":
|
||||
return f"\"{property.value}\""
|
||||
elif type == "values":
|
||||
return "{ " + ",".join(property.value) + " }"
|
||||
else:
|
||||
raise Exception(f"property_to_string() has an unsupported type: {type}")
|
||||
|
||||
def resolve_parameters_from_bindings(device: Device, bindings: list[Binding]) -> list:
|
||||
compatible_property = find_binding_property(device, "compatible")
|
||||
if compatible_property is None:
|
||||
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
|
||||
device_binding = find_binding(compatible_property.value, bindings)
|
||||
if device_binding is None:
|
||||
raise Exception(f"Binding not found for {device.identifier} and compatible '{compatible_property.value}'")
|
||||
# Filter out system properties
|
||||
binding_properties = []
|
||||
for property in device_binding.properties:
|
||||
if property.name != "compatible":
|
||||
binding_properties.append(property)
|
||||
# Allocate total expected configuration arguments
|
||||
result = [0] * len(binding_properties)
|
||||
for index, binding_property in enumerate(binding_properties):
|
||||
device_property = find_device_property(device, binding_property.name)
|
||||
if device_property is None:
|
||||
if binding_property.required:
|
||||
raise Exception(f"device {device.identifier} doesn't have property '{binding_property.name}'")
|
||||
else:
|
||||
result[index] = '0'
|
||||
else:
|
||||
result[index] = property_to_string(device_property)
|
||||
return result
|
||||
|
||||
def write_config(file, device: Device, bindings: list[Binding], type_name: str):
|
||||
device_identifier = get_device_identifier_safe(device)
|
||||
file.write(f"const static struct {type_name}_config {device_identifier}_config_instance" " = {\n")
|
||||
config_params = resolve_parameters_from_bindings(device, bindings)
|
||||
# Indent all params
|
||||
for index, config_param in enumerate(config_params):
|
||||
config_params[index] = f"\t{config_param}"
|
||||
# Join with comman and newline
|
||||
if len(config_params) > 0:
|
||||
config_params_joined = ",\n".join(config_params)
|
||||
file.write(f"{config_params_joined}\n")
|
||||
file.write("};\n\n")
|
||||
|
||||
def write_device(file, device: Device, parent_device: Device, bindings: list[Binding], verbose: bool):
|
||||
if verbose:
|
||||
print(f"Processing device '{device.identifier}'")
|
||||
# Assemble some pre-requisites
|
||||
type_name = get_device_type_name(device, bindings)
|
||||
compatible_property = find_binding_property(device, "compatible")
|
||||
if compatible_property is None:
|
||||
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
|
||||
identifier = get_device_identifier_safe(device)
|
||||
# Write config struct
|
||||
write_config(file, device, bindings, type_name)
|
||||
# Type & instance names
|
||||
api_type_name = f"{type_name}_api"
|
||||
api_instance_name = f"{type_name}_api_instance"
|
||||
init_function_name = f"{type_name}_init"
|
||||
deinit_function_name = f"{type_name}_deinit"
|
||||
config_instance_name = f"{identifier}_config_instance"
|
||||
# Write device struct
|
||||
file.write(f"extern const struct {api_type_name} {api_instance_name};\n\n")
|
||||
file.write("static struct device " f"{identifier}" " = {\n")
|
||||
file.write("\t.name = \"" f"{identifier}" "\",\n")
|
||||
file.write(f"\t.config = &{config_instance_name},\n")
|
||||
file.write(f"\t.api = &{api_instance_name},\n")
|
||||
file.write("\t.state = { .init_result = 0, .initialized = false },\n")
|
||||
file.write("\t.data = nullptr,\n")
|
||||
file.write("\t.operations = { ")
|
||||
file.write(f".init = {init_function_name}, ")
|
||||
file.write(f".deinit = {deinit_function_name}")
|
||||
file.write("},\n")
|
||||
file.write("\t.metadata = {\n")
|
||||
file.write("\t\t.num_node_labels = 0,\n")
|
||||
file.write("\t\t.node_labels = nullptr\n")
|
||||
file.write("\t}\n")
|
||||
file.write("};\n\n")
|
||||
for child_device in device.devices:
|
||||
write_device(file, child_device, device, bindings, verbose)
|
||||
|
||||
def write_device_list_entry(file, device: Device):
|
||||
compatible_property = find_binding_property(device, "compatible")
|
||||
if compatible_property is None:
|
||||
raise Exception(f"Cannot find 'compatible' property for {device.identifier}")
|
||||
identifier = get_device_identifier_safe(device)
|
||||
file.write(f"\t&{identifier},\n")
|
||||
for child in device.devices:
|
||||
write_device_list_entry(file, child)
|
||||
|
||||
def write_device_list(file, devices: list[Device]):
|
||||
file.write("struct device* devices_builtin[] = {\n")
|
||||
for device in devices:
|
||||
write_device_list_entry(file, device)
|
||||
# Terminator
|
||||
file.write(f"\tnullptr\n")
|
||||
file.write("};\n\n")
|
||||
|
||||
def generate_devicetree_c(filename: str, items: list[object], bindings: list[Binding], verbose: bool):
|
||||
with open(filename, "w") as file:
|
||||
# Write all headers first
|
||||
for item in items:
|
||||
if type(item) is IncludeC:
|
||||
write_include(file, item, verbose)
|
||||
file.write("\n")
|
||||
# Then write all devices
|
||||
devices = []
|
||||
for item in items:
|
||||
if type(item) is Device:
|
||||
devices.append(item)
|
||||
write_device(file, item, None, bindings, verbose)
|
||||
write_device_list(file, devices)
|
||||
file.write(dedent('''\
|
||||
struct device** devices_builtin_get() {
|
||||
return devices_builtin;
|
||||
}
|
||||
'''))
|
||||
|
||||
def generate_devicetree_h(filename: str):
|
||||
with open(filename, "w") as file:
|
||||
file.write(dedent('''\
|
||||
#pragma once
|
||||
#include <device.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @return an array of device* where the last item in the array is nullptr
|
||||
*/
|
||||
struct device** devices_builtin_get();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
'''))
|
||||
|
||||
def generate(output_path: str, items: list[object], bindings: list[Binding], verbose: bool):
|
||||
devicetree_c_filename = os.path.join(output_path, "devicetree.c")
|
||||
generate_devicetree_c(devicetree_c_filename, items, bindings, verbose)
|
||||
devicetree_h_filename = os.path.join(output_path, "devicetree.h")
|
||||
generate_devicetree_h(devicetree_h_filename)
|
||||
47
Buildscripts/devicetree-compiler/source/grammar.lark
Normal file
47
Buildscripts/devicetree-compiler/source/grammar.lark
Normal file
@ -0,0 +1,47 @@
|
||||
%import common.DIGIT -> DIGIT
|
||||
%import common.LETTER -> LETTER
|
||||
%import common.HEXDIGIT -> HEXDIGIT
|
||||
%import common.SIGNED_INT -> SIGNED_INT
|
||||
%import common.WS -> WS
|
||||
%import common.SIGNED_NUMBER -> SIGNED_NUMBER
|
||||
%import common.ESCAPED_STRING -> ESCAPED_STRING
|
||||
%ignore WS
|
||||
|
||||
// Commment
|
||||
|
||||
COMMENT: /\/\*[^\/]*\*\//
|
||||
%ignore COMMENT
|
||||
|
||||
// Boolean
|
||||
|
||||
BOOLEAN: "true" | "false"
|
||||
|
||||
// Main
|
||||
|
||||
INCLUDE_C: /#include <[\w\/.\-]+>/
|
||||
|
||||
PROPERTY_NAME: /#?[a-zA-Z0-9_\-,]+/
|
||||
|
||||
hex_number: /0x[0-9a-fA-F]+/
|
||||
QUOTE: "\""
|
||||
QUOTED_TEXT: QUOTE /[^"]+/ QUOTE
|
||||
quoted_text_array: QUOTED_TEXT ("," " "* QUOTED_TEXT)+
|
||||
HEX_NUMBER: "0x" HEXDIGIT*
|
||||
NUMBER: SIGNED_NUMBER | HEX_NUMBER
|
||||
PHANDLE: /&[0-9a-zA-Z\-]+/
|
||||
C_VARIABLE: /[0-9a-zA-Z_]+/
|
||||
VALUE: NUMBER | PHANDLE | C_VARIABLE
|
||||
value: VALUE
|
||||
values: VALUE+
|
||||
array: NUMBER+
|
||||
|
||||
property_value: quoted_text_array | QUOTED_TEXT | "<" value ">" | "<" values ">" | "[" array "]"
|
||||
device_property: PROPERTY_NAME ["=" property_value] ";"
|
||||
|
||||
DEVICE_IDENTIFIER: /[a-zA-Z0-9_\-\/@]+/
|
||||
|
||||
device: DEVICE_IDENTIFIER "{" (device | device_property)* "};"
|
||||
|
||||
dts_version: /[0-9a-zA-Z\-]+/
|
||||
|
||||
start: "/" dts_version "/;" INCLUDE_C* device+
|
||||
47
Buildscripts/devicetree-compiler/source/main.py
Normal file
47
Buildscripts/devicetree-compiler/source/main.py
Normal file
@ -0,0 +1,47 @@
|
||||
import os
|
||||
from pprint import pprint
|
||||
|
||||
from lark import Lark
|
||||
|
||||
from source.files import *
|
||||
from source.transformer import *
|
||||
from source.generator import *
|
||||
from source.binding_files import find_all_bindings
|
||||
from source.binding_parser import parse_binding
|
||||
from source.config import *
|
||||
|
||||
def main(config_path: str, output_path: str, verbose: bool):
|
||||
print(f"Generating devicetree code\n config: {config_path}\n output: {output_path}")
|
||||
if not os.path.isdir(config_path):
|
||||
raise Exception(f"Not found: {config_path}")
|
||||
|
||||
config = parse_config(config_path, os.getcwd())
|
||||
if verbose:
|
||||
pprint(config)
|
||||
dts_file_path: str
|
||||
|
||||
project_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
grammar_path = os.path.join(project_dir, "grammar.lark")
|
||||
lark_data = read_file(grammar_path)
|
||||
dts_data = read_file(config.dts)
|
||||
lark = Lark(lark_data)
|
||||
parsed = lark.parse(dts_data)
|
||||
if verbose:
|
||||
print(parsed.pretty())
|
||||
transformed = DtsTransformer().transform(parsed)
|
||||
if verbose:
|
||||
pprint(transformed)
|
||||
binding_files = find_all_bindings(config.bindings)
|
||||
if verbose:
|
||||
print(f"Bindings found:")
|
||||
for binding_file in binding_files:
|
||||
print(f" {binding_file}")
|
||||
if verbose:
|
||||
print(f"Parsing bindings")
|
||||
bindings = []
|
||||
for binding_file in binding_files:
|
||||
bindings.append(parse_binding(binding_file, config.bindings))
|
||||
if verbose:
|
||||
for binding in bindings:
|
||||
pprint(binding)
|
||||
generate(output_path, transformed, bindings, verbose)
|
||||
42
Buildscripts/devicetree-compiler/source/models.py
Normal file
42
Buildscripts/devicetree-compiler/source/models.py
Normal file
@ -0,0 +1,42 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class DtsVersion:
|
||||
version: str
|
||||
|
||||
@dataclass
|
||||
class Device:
|
||||
identifier: str
|
||||
properties: list
|
||||
devices: list
|
||||
|
||||
@dataclass
|
||||
class Property:
|
||||
name: str
|
||||
type: str
|
||||
value: object
|
||||
|
||||
@dataclass
|
||||
class PropertyValue:
|
||||
type: str
|
||||
value: object
|
||||
|
||||
@dataclass
|
||||
class IncludeC:
|
||||
statement: str
|
||||
|
||||
@dataclass
|
||||
class BindingProperty:
|
||||
name: str
|
||||
type: str
|
||||
required: bool
|
||||
description: str
|
||||
includes: list[str]
|
||||
|
||||
@dataclass
|
||||
class Binding:
|
||||
filename: str
|
||||
compatible: str
|
||||
description: str
|
||||
properties: list[BindingProperty]
|
||||
bus: str = None
|
||||
20
Buildscripts/devicetree-compiler/source/printing.py
Normal file
20
Buildscripts/devicetree-compiler/source/printing.py
Normal file
@ -0,0 +1,20 @@
|
||||
import sys
|
||||
|
||||
if sys.platform == "win32":
|
||||
SHELL_COLOR_RED = ""
|
||||
SHELL_COLOR_ORANGE = ""
|
||||
SHELL_COLOR_RESET = ""
|
||||
else:
|
||||
SHELL_COLOR_RED = "\033[91m"
|
||||
SHELL_COLOR_ORANGE = "\033[93m"
|
||||
SHELL_COLOR_RESET = "\033[m"
|
||||
|
||||
def print_warning(message):
|
||||
print(f"{SHELL_COLOR_ORANGE}WARNING: {message}{SHELL_COLOR_RESET}")
|
||||
|
||||
def print_error(message):
|
||||
print(f"{SHELL_COLOR_RED}ERROR: {message}{SHELL_COLOR_RESET}")
|
||||
|
||||
def exit_with_error(message):
|
||||
print_error(message)
|
||||
sys.exit(1)
|
||||
64
Buildscripts/devicetree-compiler/source/transformer.py
Normal file
64
Buildscripts/devicetree-compiler/source/transformer.py
Normal file
@ -0,0 +1,64 @@
|
||||
from typing import List
|
||||
|
||||
from lark import Transformer
|
||||
from lark import Token
|
||||
from source.models import *
|
||||
|
||||
def flatten_token_array(tokens: List[Token], name: str):
|
||||
result_list = list()
|
||||
for token in tokens:
|
||||
result_list.append(token.value)
|
||||
return Token(name, result_list)
|
||||
|
||||
class DtsTransformer(Transformer):
|
||||
# Flatten the start node into a list
|
||||
def start(self, tokens):
|
||||
return tokens
|
||||
def dts_version(self, tokens: List[Token]):
|
||||
version = tokens[0].value
|
||||
if version != "dts-v1":
|
||||
raise Exception(f"Unsupported DTS version: {version}")
|
||||
return DtsVersion(version)
|
||||
def device(self, tokens: list):
|
||||
identifier = "UNKNOWN"
|
||||
properties = list()
|
||||
devices = list()
|
||||
for index, entry in enumerate(tokens):
|
||||
if index == 0:
|
||||
identifier = entry.value
|
||||
elif type(entry) is Property:
|
||||
properties.append(entry)
|
||||
elif type(entry) is Device:
|
||||
devices.append(entry)
|
||||
return Device(identifier, properties, devices)
|
||||
def device_property(self, objects: List[object]):
|
||||
assert len(objects) == 2
|
||||
if not type(objects[1]) is PropertyValue:
|
||||
raise Exception(f"Object was not converted to PropertyValue: {objects[1]}")
|
||||
return Property(objects[0], objects[1].type, objects[1].value)
|
||||
def property_value(self, tokens: List):
|
||||
token = tokens[0]
|
||||
if type(token) is Token:
|
||||
raise Exception(f"Failed to convert token to PropertyValue: {token}")
|
||||
return token
|
||||
def values(self, object):
|
||||
return PropertyValue(type="values", value=object)
|
||||
def value(self, object):
|
||||
return PropertyValue(type="value", value=object[0])
|
||||
def array(self, object):
|
||||
return PropertyValue(type="array", value=object)
|
||||
def VALUE(self, token: Token):
|
||||
return token.value
|
||||
def NUMBER(self, token: Token):
|
||||
return token.value
|
||||
def PROPERTY_NAME(self, token: Token):
|
||||
return token.value
|
||||
def QUOTED_TEXT(self, token: Token):
|
||||
return PropertyValue("text", token.value[1:-1])
|
||||
def quoted_text_array(self, tokens: List[Token]):
|
||||
result_list = list()
|
||||
for token in tokens:
|
||||
result_list.append(token.value)
|
||||
return PropertyValue("text_array", result_list)
|
||||
def INCLUDE_C(self, token: Token):
|
||||
return IncludeC(token.value)
|
||||
@ -1,7 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_ASM_COMPILE_OBJECT "${CMAKE_CXX_COMPILER_TARGET}")
|
||||
|
||||
include("Buildscripts/logo.cmake")
|
||||
@ -13,15 +12,21 @@ set(Cyan "${Esc}[36m")
|
||||
file(READ version.txt TACTILITY_VERSION)
|
||||
add_compile_definitions(TT_VERSION="${TACTILITY_VERSION}")
|
||||
|
||||
# Determine device identifier and project location
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
include("Buildscripts/device.cmake")
|
||||
init_tactility_globals("sdkconfig")
|
||||
get_property(TACTILITY_DEVICE_PROJECT GLOBAL PROPERTY TACTILITY_DEVICE_PROJECT)
|
||||
get_property(TACTILITY_DEVICE_ID GLOBAL PROPERTY TACTILITY_DEVICE_ID)
|
||||
else ()
|
||||
set(TACTILITY_DEVICE_PROJECT "Devices/simulator")
|
||||
set(TACTILITY_DEVICE_ID "simulator")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
message("Using ESP-IDF ${Cyan}v$ENV{ESP_IDF_VERSION}${ColorReset}")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
include("Buildscripts/device.cmake")
|
||||
|
||||
init_tactility_globals("sdkconfig")
|
||||
get_property(TACTILITY_DEVICE_PROJECT GLOBAL PROPERTY TACTILITY_DEVICE_PROJECT)
|
||||
|
||||
set(COMPONENTS Firmware)
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"Firmware"
|
||||
@ -75,6 +80,8 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
|
||||
add_subdirectory(Libraries/QRCode)
|
||||
add_subdirectory(Libraries/minitar)
|
||||
add_subdirectory(Libraries/minmea)
|
||||
add_subdirectory(Drivers/drivers-abstract)
|
||||
add_subdirectory(Drivers/drivers-core)
|
||||
|
||||
# FreeRTOS
|
||||
set(FREERTOS_CONFIG_FILE_DIRECTORY ${PROJECT_SOURCE_DIR}/Devices/simulator/Source CACHE STRING "")
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lcd ST7796 BQ25896 BQ27220 TCA8418 DRV2605 PwmBacklight driver esp_adc
|
||||
REQUIRES Tactility esp_lcd ST7796 BQ25896 BQ27220 TCA8418 DRV2605 PwmBacklight driver esp_adc drivers-esp
|
||||
)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c23)
|
||||
|
||||
18
Devices/lilygo-tlora-pager/Source/drivers/tlora_pager.c
Normal file
18
Devices/lilygo-tlora-pager/Source/drivers/tlora_pager.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include "tlora_pager.h"
|
||||
#include <esp_log.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TAG "tlora_pager"
|
||||
|
||||
const struct tlora_pager_api tlora_pager_api_instance = {
|
||||
};
|
||||
|
||||
int tlora_pager_init(const struct device* device) {
|
||||
ESP_LOGI(TAG, "init %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tlora_pager_deinit(const struct device* device) {
|
||||
ESP_LOGI(TAG, "deinit %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
22
Devices/lilygo-tlora-pager/Source/drivers/tlora_pager.h
Normal file
22
Devices/lilygo-tlora-pager/Source/drivers/tlora_pager.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/root.h>
|
||||
|
||||
// Inherit base config
|
||||
#define tlora_pager_config root_config
|
||||
|
||||
// Inherit base API
|
||||
#define tlora_pager_api root_api
|
||||
|
||||
int tlora_pager_init(const struct device* device);
|
||||
|
||||
int tlora_pager_deinit(const struct device* device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
4
Devices/lilygo-tlora-pager/devicetree.yaml
Normal file
4
Devices/lilygo-tlora-pager/devicetree.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
- Drivers/drivers-esp
|
||||
bindings: ./
|
||||
dts: lilygo,tlora_pager.dts
|
||||
3
Devices/lilygo-tlora-pager/lilygo,tlora-pager.yaml
Normal file
3
Devices/lilygo-tlora-pager/lilygo,tlora-pager.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
description: LilyGO T-Lora Pager
|
||||
|
||||
include: ["root.yaml"]
|
||||
22
Devices/lilygo-tlora-pager/lilygo,tlora_pager.dts
Normal file
22
Devices/lilygo-tlora-pager/lilygo,tlora_pager.dts
Normal file
@ -0,0 +1,22 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include <drivers/tlora_pager.h>
|
||||
#include <drivers-esp/esp32_gpio.h>
|
||||
#include <drivers-esp/esp32_i2c.h>
|
||||
|
||||
/ {
|
||||
model = "LilyGO T-Lora Pager";
|
||||
compatible = "lilygo,tlora-pager";
|
||||
|
||||
gpio0 {
|
||||
compatible = "espressif,esp32-gpio";
|
||||
gpio-count = <49>;
|
||||
};
|
||||
|
||||
i2c0 {
|
||||
compatible = "espressif,esp32-i2c";
|
||||
clock-frequency = <100000>;
|
||||
pin-sda = <&gpio0 3 GPIO_ACTIVE_HIGH>;
|
||||
pin-scl = <&gpio0 2 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
4
Devices/simulator/devicetree.yaml
Normal file
4
Devices/simulator/devicetree.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
dependencies:
|
||||
- Drivers/drivers-abstract
|
||||
bindings: ./
|
||||
dts: simulator.dts
|
||||
9
Devices/simulator/simulator.dts
Normal file
9
Devices/simulator/simulator.dts
Normal file
@ -0,0 +1,9 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/root.h>
|
||||
|
||||
/ {
|
||||
model = "Simulator";
|
||||
compatible = "root";
|
||||
};
|
||||
23
Drivers/drivers-abstract/CMakeLists.txt
Normal file
23
Drivers/drivers-abstract/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
file(GLOB_RECURSE SOURCES "source/*.c**")
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "include/"
|
||||
REQUIRES drivers-core
|
||||
)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c23)
|
||||
|
||||
else ()
|
||||
|
||||
add_library(drivers-abstract OBJECT)
|
||||
target_sources(drivers-abstract PRIVATE ${SOURCES})
|
||||
target_include_directories(drivers-abstract PUBLIC include/)
|
||||
target_link_libraries(drivers-abstract PUBLIC drivers-core)
|
||||
target_compile_options(drivers-abstract PRIVATE -std=c23)
|
||||
|
||||
endif ()
|
||||
6
Drivers/drivers-abstract/bindings/gpio-controller.yaml
Normal file
6
Drivers/drivers-abstract/bindings/gpio-controller.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
properties:
|
||||
gpio-count:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
The number of available GPIOs.
|
||||
10
Drivers/drivers-abstract/bindings/i2c-controller.yaml
Normal file
10
Drivers/drivers-abstract/bindings/i2c-controller.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
bus: i2c
|
||||
|
||||
properties:
|
||||
clock-frequency:
|
||||
type: int
|
||||
description: Initial clock frequency in Hz
|
||||
pin-sda:
|
||||
type: phandle-array
|
||||
pin-scl:
|
||||
type: phandle-array
|
||||
6
Drivers/drivers-abstract/bindings/i2c-device.yaml
Normal file
6
Drivers/drivers-abstract/bindings/i2c-device.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
on-bus: i2c
|
||||
|
||||
properties:
|
||||
register:
|
||||
required: true
|
||||
description: device address on the bus
|
||||
4
Drivers/drivers-abstract/bindings/root.yaml
Normal file
4
Drivers/drivers-abstract/bindings/root.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
properties:
|
||||
model:
|
||||
required: true
|
||||
description: the name of hardware, usually vendor and model name
|
||||
1
Drivers/drivers-abstract/devicetree.yaml
Normal file
1
Drivers/drivers-abstract/devicetree.yaml
Normal file
@ -0,0 +1 @@
|
||||
bindings: bindings
|
||||
84
Drivers/drivers-abstract/include/drivers/gpio.h
Normal file
84
Drivers/drivers-abstract/include/drivers/gpio.h
Normal file
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <device.h>
|
||||
|
||||
#define GPIO_OPTIONS_MASK 0x1f
|
||||
|
||||
#define GPIO_ACTIVE_HIGH (0 << 0)
|
||||
#define GPIO_ACTIVE_LOW (1 << 0)
|
||||
|
||||
#define GPIO_UNIDIRECTIONAL (0 << 1)
|
||||
#define GPIO_BIDIRECTIONAL (1 << 1)
|
||||
|
||||
#define GPIO_OPEN_DRAIN (GPIO_UNIDIRECTIONAL | (0 << 2))
|
||||
#define GPIO_OPEN_SOURCE (GPIO_UNIDIRECTIONAL | (1 << 2))
|
||||
|
||||
#define GPIO_PULL_UP (0 << 3)
|
||||
#define GPIO_PULL_DOWN (1 << 4)
|
||||
|
||||
#define GPIO_INTERRUPT_WAKE_UP (1 << 5)
|
||||
|
||||
/**
|
||||
* @brief Provides a type to hold a GPIO pin index.
|
||||
*
|
||||
* This reduced-size type is sufficient to record a pin number,
|
||||
* e.g. from a devicetree GPIOS property.
|
||||
*/
|
||||
typedef uint8_t gpio_pin_t;
|
||||
|
||||
/**
|
||||
* @brief Identifies a set of pins associated with a port.
|
||||
*
|
||||
* The pin with index n is present in the set if and only if the bit
|
||||
* identified by (1U << n) is set.
|
||||
*/
|
||||
typedef uint32_t gpio_pinset_t;
|
||||
|
||||
/**
|
||||
* @brief Provides a type to hold GPIO devicetree flags.
|
||||
*
|
||||
* All GPIO flags that can be expressed in devicetree fit in the low 16
|
||||
* bits of the full flags field, so use a reduced-size type to record
|
||||
* that part of a GPIOS property.
|
||||
*
|
||||
* The lower 8 bits are used for standard flags. The upper 8 bits are reserved
|
||||
* for SoC specific flags.
|
||||
*/
|
||||
typedef uint16_t gpio_flags_t;
|
||||
|
||||
/**
|
||||
* @brief Container for GPIO pin information specified in dts files
|
||||
*
|
||||
* This type contains a pointer to a GPIO device, pin identifier for a pin
|
||||
* controlled by that device, and the subset of pin configuration
|
||||
* flags which may be given in devicetree.
|
||||
*/
|
||||
struct gpio_pin_config {
|
||||
/** GPIO device controlling the pin */
|
||||
const struct device* port;
|
||||
/** The pin's number on the device */
|
||||
gpio_pin_t pin;
|
||||
/** The pin's configuration flags as specified in devicetree */
|
||||
gpio_flags_t dt_flags;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Validate that GPIO port is ready.
|
||||
*
|
||||
* @param spec GPIO specification from devicetree
|
||||
*
|
||||
* @retval true if the GPIO spec is ready for use.
|
||||
* @retval false if the GPIO spec is not ready for use.
|
||||
*/
|
||||
inline bool gpio_is_ready(const struct gpio_pin_config* pin_config) {
|
||||
return device_is_ready(pin_config->port);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
35
Drivers/drivers-abstract/include/drivers/gpio_controller.h
Normal file
35
Drivers/drivers-abstract/include/drivers/gpio_controller.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include "gpio.h"
|
||||
|
||||
struct gpio_controller_config {
|
||||
uint8_t gpio_count;
|
||||
};
|
||||
|
||||
struct gpio_controller_api {
|
||||
bool (*set_level)(const struct device*, gpio_pin_t pin, bool high);
|
||||
bool (*get_level)(const struct device*, gpio_pin_t pin, bool* high);
|
||||
bool (*set_options)(const struct device*, gpio_pin_t pin, gpio_flags_t options);
|
||||
bool (*get_options)(const struct device*, gpio_pin_t pin, gpio_flags_t* options);
|
||||
};
|
||||
|
||||
#define GPIO_API(dev) ((struct gpio_controller_api*)dev->api)
|
||||
#define GPIO_CONFIG(dev) ((struct gpio_controller_config*)dev->config)
|
||||
|
||||
bool gpio_controller_set_level(const struct device* dev, gpio_pin_t pin, bool high);
|
||||
bool gpio_controller_get_level(const struct device* dev, gpio_pin_t pin, bool* high);
|
||||
bool gpio_controller_set_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t options);
|
||||
bool gpio_controller_get_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t* options);
|
||||
|
||||
inline bool gpio_set_options_config(const struct gpio_pin_config* config) {
|
||||
return gpio_controller_set_options(config->port, config->pin, config->dt_flags);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
23
Drivers/drivers-abstract/include/drivers/i2c_controller.h
Normal file
23
Drivers/drivers-abstract/include/drivers/i2c_controller.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include "gpio.h"
|
||||
|
||||
struct i2c_controller_config {
|
||||
uint32_t clock_frequency;
|
||||
struct gpio_pin_config pin_sda;
|
||||
struct gpio_pin_config pin_scl;
|
||||
};
|
||||
|
||||
struct i2c_controller_api {
|
||||
bool (*master_read)(struct device* dev, uint8_t address, uint8_t* data, size_t dataSize); // TODO: add timeout
|
||||
bool (*master_write)(struct device* dev, uint8_t address, const uint8_t* data, uint16_t dataSize); // TODO: add timeout
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
19
Drivers/drivers-abstract/include/drivers/root.h
Normal file
19
Drivers/drivers-abstract/include/drivers/root.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct root_config {
|
||||
const char* model;
|
||||
};
|
||||
|
||||
struct root_api {
|
||||
};
|
||||
|
||||
#define root_init nullptr
|
||||
#define root_deinit nullptr
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
17
Drivers/drivers-abstract/source/gpio_controller.c
Normal file
17
Drivers/drivers-abstract/source/gpio_controller.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include <drivers/gpio_controller.h>
|
||||
|
||||
bool gpio_controller_set_level(const struct device* dev, gpio_pin_t pin, bool high) {
|
||||
return GPIO_API(dev)->set_level(dev, pin, high);
|
||||
}
|
||||
|
||||
bool gpio_controller_get_level(const struct device* dev, gpio_pin_t pin, bool* high) {
|
||||
return GPIO_API(dev)->get_level(dev, pin, high);
|
||||
}
|
||||
|
||||
bool gpio_controller_set_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t options) {
|
||||
return GPIO_API(dev)->set_options(dev, pin, options);
|
||||
}
|
||||
|
||||
bool gpio_controller_get_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t* options) {
|
||||
return GPIO_API(dev)->get_options(dev, pin, options);
|
||||
}
|
||||
5
Drivers/drivers-abstract/source/i2c_controller.c
Normal file
5
Drivers/drivers-abstract/source/i2c_controller.c
Normal file
@ -0,0 +1,5 @@
|
||||
#include "drivers/i2c_controller.h"
|
||||
|
||||
#if !defined(CONFIG_I2C_CONTROLLER_DEVICE_COUNT_LIMIT)
|
||||
#define CONFIG_I2C_CONTROLLER_DEVICE_COUNT_LIMIT 4
|
||||
#endif
|
||||
3
Drivers/drivers-abstract/source/root.c
Normal file
3
Drivers/drivers-abstract/source/root.c
Normal file
@ -0,0 +1,3 @@
|
||||
#include <drivers/root.h>
|
||||
|
||||
const struct root_api root_api_instance = {};
|
||||
21
Drivers/drivers-core/CMakeLists.txt
Normal file
21
Drivers/drivers-core/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
file(GLOB_RECURSE SOURCES "source/*.c**")
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "include/"
|
||||
)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c23)
|
||||
|
||||
else ()
|
||||
|
||||
add_library(drivers-core OBJECT)
|
||||
target_sources(drivers-core PRIVATE ${SOURCES})
|
||||
target_include_directories(drivers-core PUBLIC include/)
|
||||
target_compile_options(drivers-core PRIVATE -std=c23)
|
||||
|
||||
endif ()
|
||||
88
Drivers/drivers-core/include/device.h
Normal file
88
Drivers/drivers-core/include/device.h
Normal file
@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct device;
|
||||
|
||||
struct device_operations {
|
||||
/** Initialization function */
|
||||
int (*init)(const struct device* device);
|
||||
/** De-initialization function */
|
||||
int (*deinit)(const struct device* device);
|
||||
};
|
||||
|
||||
struct device_state {
|
||||
uint8_t init_result;
|
||||
bool initialized : 1;
|
||||
};
|
||||
|
||||
#define DEVICE_LABEL(key, value) #key":"#value
|
||||
#define DEVICE_LABEL_FOR_TYPE(_type) DEVICE_LABEL(type, _type)
|
||||
|
||||
struct device_metadata {
|
||||
/* @brief number of elements in the nodelabels array */
|
||||
size_t num_node_labels;
|
||||
/* @brief array of node labels as strings, exactly as they
|
||||
* appear in the final devicetree
|
||||
*/
|
||||
const char** node_labels;
|
||||
};
|
||||
|
||||
struct device {
|
||||
/** Name of the device as defined in the dts file */
|
||||
const char* name;
|
||||
/** Address of device instance configuration. This relates to the parameters that are specified in the dts file*/
|
||||
const void* config;
|
||||
/** Address of the API exposed by the device instance */
|
||||
const void* api;
|
||||
/** The device state */
|
||||
struct device_state state;
|
||||
/** Address of the device's private data */
|
||||
void* data;
|
||||
/** Device operations: used for initializing and deinitializing */
|
||||
struct device_operations operations;
|
||||
/** Device metadata */
|
||||
struct device_metadata metadata;
|
||||
};
|
||||
|
||||
uint8_t device_init(struct device* dev);
|
||||
|
||||
bool device_init_all(struct device** device_array);
|
||||
|
||||
uint8_t device_deinit(struct device* dev);
|
||||
|
||||
/**
|
||||
* Indicated whether the device is in a state where its API is available
|
||||
*
|
||||
* @param[in] dev non-null device pointer
|
||||
* @return true if the device is ready for use
|
||||
*/
|
||||
bool device_is_ready(const struct device* dev);
|
||||
|
||||
/**
|
||||
* Register a single device.
|
||||
*
|
||||
* @param[in] dev non-null device pointer
|
||||
*/
|
||||
bool device_add(const struct device* dev);
|
||||
|
||||
/**
|
||||
* Register all devices in the specified array.
|
||||
* The array must be null-terminated.
|
||||
*
|
||||
* @param[in] device_array non-null array of devices
|
||||
*/
|
||||
void device_add_all(struct device** device_array);
|
||||
|
||||
bool device_remove(const struct device* dev);
|
||||
|
||||
bool device_find_next(const char* label, const struct device** device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
119
Drivers/drivers-core/source/device.c
Normal file
119
Drivers/drivers-core/source/device.c
Normal file
@ -0,0 +1,119 @@
|
||||
#include "device.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CONFIG_DEVICE_INDEX_SIZE 64
|
||||
|
||||
// TODO: Automatically increase allocated size
|
||||
static const struct device* device_index[128] = { 0 };
|
||||
|
||||
uint8_t device_init(struct device* const dev) {
|
||||
assert(dev != nullptr);
|
||||
printf("device_init: %s\n", dev->name);
|
||||
|
||||
if (!dev->state.initialized) {
|
||||
if (dev->operations.init == nullptr) {
|
||||
dev->state.initialized = true;
|
||||
dev->state.init_result = 0U;
|
||||
} else {
|
||||
dev->state.init_result = dev->operations.init(dev);
|
||||
dev->state.initialized = dev->state.init_result == 0U;
|
||||
}
|
||||
}
|
||||
|
||||
return dev->state.init_result;
|
||||
}
|
||||
|
||||
bool device_init_all(struct device** const device_array) {
|
||||
assert(device_array != nullptr);
|
||||
struct device** current_device = device_array;
|
||||
bool all_succeeded = true;
|
||||
while (*current_device != nullptr) {
|
||||
struct device* device = *current_device;
|
||||
if (device_init(device) != 0U) {
|
||||
all_succeeded = false;
|
||||
}
|
||||
current_device++;
|
||||
}
|
||||
return all_succeeded;
|
||||
}
|
||||
|
||||
uint8_t device_deinit(struct device* const dev) {
|
||||
assert(dev != nullptr);
|
||||
|
||||
if (dev->state.initialized) {
|
||||
if (dev->operations.deinit != nullptr) {
|
||||
dev->state.init_result = dev->operations.deinit(dev);
|
||||
if (dev->state.init_result == 0U) {
|
||||
dev->state.initialized = false;
|
||||
}
|
||||
} else {
|
||||
dev->state.initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
return !dev->state.init_result;
|
||||
}
|
||||
|
||||
bool device_is_ready(const struct device* const dev) {
|
||||
assert(dev != nullptr);
|
||||
return dev->state.initialized && (dev->state.init_result == 0U);
|
||||
}
|
||||
|
||||
|
||||
bool device_add(const struct device* dev) {
|
||||
assert(dev != nullptr);
|
||||
for (int i = 0; i < CONFIG_DEVICE_INDEX_SIZE; i++) {
|
||||
if (device_index[i] == nullptr) {
|
||||
device_index[i] = dev;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void device_add_all(struct device** const device_array) {
|
||||
assert(device_array != nullptr);
|
||||
struct device** current_device = device_array;
|
||||
while (*current_device != nullptr) {
|
||||
struct device* device = *current_device;
|
||||
device_add(device);
|
||||
current_device++;
|
||||
}
|
||||
}
|
||||
|
||||
bool device_remove(const struct device* dev) {
|
||||
for (int i = 0; i < CONFIG_DEVICE_INDEX_SIZE; i++) {
|
||||
if (device_index[i] == dev) {
|
||||
device_index[i] = nullptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_find_next(const char* label, const struct device** device) {
|
||||
bool found_first = (device == nullptr);
|
||||
for (int device_idx = 0; device_idx < CONFIG_DEVICE_INDEX_SIZE; device_idx++) {
|
||||
auto indexed_device = device_index[device_idx];
|
||||
if (indexed_device != nullptr) {
|
||||
if (!found_first) {
|
||||
if (indexed_device == *device) {
|
||||
found_first = true;
|
||||
}
|
||||
} else {
|
||||
for (int label_idx = 0; label_idx< indexed_device->metadata.num_node_labels; ++label_idx) {
|
||||
const char* indexed_device_label = indexed_device->metadata.node_labels[label_idx];
|
||||
if (strcmp(indexed_device_label, label) == 0) {
|
||||
*device = indexed_device;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
23
Drivers/drivers-esp/CMakeLists.txt
Normal file
23
Drivers/drivers-esp/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
file(GLOB_RECURSE SOURCES "source/*.c**")
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "include/"
|
||||
REQUIRES drivers-abstract driver
|
||||
)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c23)
|
||||
|
||||
else ()
|
||||
|
||||
add_library(drivers-esp OBJECT)
|
||||
target_sources(drivers-esp PRIVATE ${SOURCES})
|
||||
target_include_directories(drivers-esp PUBLIC include/)
|
||||
target_link_libraries(drivers-esp PUBLIC drivers-abstract)
|
||||
target_compile_options(drivers-esp PRIVATE -std=c23)
|
||||
|
||||
endif ()
|
||||
3
Drivers/drivers-esp/bindings/espressif,esp32-gpio.yaml
Normal file
3
Drivers/drivers-esp/bindings/espressif,esp32-gpio.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
description: ESP32 GPIO Controller
|
||||
|
||||
include: ["gpio-controller.yaml"]
|
||||
3
Drivers/drivers-esp/bindings/espressif,esp32-i2c.yaml
Normal file
3
Drivers/drivers-esp/bindings/espressif,esp32-i2c.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
description: ESP32 GPIO Controller
|
||||
|
||||
include: ["i2c-controller.yaml"]
|
||||
3
Drivers/drivers-esp/devicetree.yaml
Normal file
3
Drivers/drivers-esp/devicetree.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
- Drivers/drivers-abstract
|
||||
bindings: bindings
|
||||
19
Drivers/drivers-esp/include/drivers-esp/esp32_gpio.h
Normal file
19
Drivers/drivers-esp/include/drivers-esp/esp32_gpio.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/gpio_controller.h>
|
||||
|
||||
#define esp32_gpio_config gpio_controller_config
|
||||
#define esp32_gpio_api gpio_controller_api
|
||||
|
||||
int esp32_gpio_init(const struct device* device);
|
||||
|
||||
int esp32_gpio_deinit(const struct device* device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
22
Drivers/drivers-esp/include/drivers-esp/esp32_i2c.h
Normal file
22
Drivers/drivers-esp/include/drivers-esp/esp32_i2c.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/i2c_controller.h>
|
||||
|
||||
// Inherit base config
|
||||
#define esp32_i2c_config i2c_controller_config
|
||||
|
||||
// Inherit base API
|
||||
#define esp32_i2c_api i2c_controller_api
|
||||
|
||||
int esp32_i2c_init(const struct device* device);
|
||||
|
||||
int esp32_i2c_deinit(const struct device* device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
48
Drivers/drivers-esp/source/esp32_gpio.c
Normal file
48
Drivers/drivers-esp/source/esp32_gpio.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include <drivers-esp/esp32_gpio.h>
|
||||
#include <drivers/gpio_controller.h>
|
||||
#include <drivers/gpio.h>
|
||||
// ESP
|
||||
#include <esp_log.h>
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define TAG "esp32_gpio"
|
||||
|
||||
static bool set_level(const struct device* dev, gpio_pin_t pin, bool high) {
|
||||
return gpio_set_level(pin, high) == ESP_OK;
|
||||
}
|
||||
|
||||
static bool get_level(const struct device* dev, gpio_pin_t pin, bool* high) {
|
||||
*high = gpio_get_level(pin) != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool set_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t options) {
|
||||
// TODO
|
||||
// gpio_set_direction()
|
||||
// gpio_set_pull_mode()
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_options(const struct device* dev, gpio_pin_t pin, gpio_flags_t* options) {
|
||||
// gpio_get_direction()
|
||||
// gpio_get_pull_mode()
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct esp32_gpio_api esp32_gpio_api_instance = {
|
||||
.set_level = set_level,
|
||||
.get_level = get_level,
|
||||
.set_options = set_options,
|
||||
.get_options = get_options
|
||||
};
|
||||
|
||||
int esp32_gpio_init(const struct device* device) {
|
||||
ESP_LOGI(TAG, "init %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int esp32_gpio_deinit(const struct device* device) {
|
||||
ESP_LOGI(TAG, "deinit %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
31
Drivers/drivers-esp/source/esp32_i2c.c
Normal file
31
Drivers/drivers-esp/source/esp32_i2c.c
Normal file
@ -0,0 +1,31 @@
|
||||
#include "drivers-esp/esp32_i2c.h"
|
||||
#include <drivers/i2c_controller.h>
|
||||
#include <esp_log.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TAG "esp32_i2c"
|
||||
|
||||
#define GET_CONFIG(dev) static_cast<struct i2c_esp_config>(dev->config)
|
||||
|
||||
static bool master_read(struct device* dev, uint8_t address, uint8_t* data, size_t dataSize) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool master_write(struct device* dev, uint8_t address, const uint8_t* data, uint16_t dataSize) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct esp32_i2c_api esp32_i2c_api_instance = {
|
||||
.master_read = master_read,
|
||||
.master_write = master_write
|
||||
};
|
||||
|
||||
int esp32_i2c_init(const struct device* device) {
|
||||
ESP_LOGI(TAG, "init %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int esp32_i2c_deinit(const struct device* device) {
|
||||
ESP_LOGI(TAG, "deinit %s", device->name);
|
||||
return 0;
|
||||
}
|
||||
1
Firmware/.gitignore
vendored
Normal file
1
Firmware/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
Generated/
|
||||
@ -1,29 +1,66 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
|
||||
file(GLOB_RECURSE SOURCE_FILES "Source/*.c*")
|
||||
|
||||
# Determine device identifier and project location
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
# Read device id/project
|
||||
include("../Buildscripts/device.cmake")
|
||||
init_tactility_globals("../sdkconfig")
|
||||
get_property(TACTILITY_DEVICE_PROJECT GLOBAL PROPERTY TACTILITY_DEVICE_PROJECT)
|
||||
else ()
|
||||
set(TACTILITY_DEVICE_PROJECT "Devices/simulator")
|
||||
endif ()
|
||||
|
||||
set(DEVICETREE_LOCATION "Devices/${TACTILITY_DEVICE_ID}")
|
||||
|
||||
if (NOT DEFINED ${COMPONENT_LIB})
|
||||
set(COMPONENT_LIB FirmwareSim)
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
REQUIRES ${DEVICE_COMPONENTS}
|
||||
REQUIRES Tactility TactilityC ${TACTILITY_DEVICE_PROJECT}
|
||||
REQUIRES Tactility TactilityC drivers-core drivers-abstract drivers-esp ${TACTILITY_DEVICE_PROJECT}
|
||||
)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -std=c23)
|
||||
|
||||
else ()
|
||||
|
||||
add_executable(FirmwareSim ${SOURCE_FILES})
|
||||
add_executable(FirmwareSim ${SOURCE_FILES} "${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.c")
|
||||
target_link_libraries(FirmwareSim
|
||||
PRIVATE Tactility
|
||||
PRIVATE TactilityCore
|
||||
PRIVATE TactilityFreeRtos
|
||||
PRIVATE Simulator
|
||||
PRIVATE SDL2::SDL2-static SDL2-static
|
||||
PRIVATE drivers-abstract
|
||||
PRIVATE drivers-core
|
||||
)
|
||||
|
||||
target_include_directories(FirmwareSim PRIVATE "${CMAKE_SOURCE_DIR}/Firmware/Generated")
|
||||
target_compile_options(FirmwareSim PRIVATE -std=c++23)
|
||||
|
||||
add_definitions(-D_Nullable=)
|
||||
add_definitions(-D_Nonnull=)
|
||||
|
||||
add_dependencies(FirmwareSim Generated)
|
||||
endif ()
|
||||
|
||||
# Generate devicetree code and attach to component
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.c ${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.h
|
||||
COMMAND mkdir ${CMAKE_SOURCE_DIR}/Firmware/Generated | pip install lark pyyaml && python Buildscripts/devicetree-compiler/compile.py ${DEVICETREE_LOCATION} Firmware/Generated
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(Generated DEPENDS ${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.c ${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.h)
|
||||
set_property(DIRECTORY "${CMAKE_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES "Firmware/Generated/devicetree.c" "/Firmware/Generated/devicetree.h")
|
||||
# Attach generated code to project
|
||||
target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_SOURCE_DIR}/Firmware/Generated/devicetree.c")
|
||||
target_include_directories(${COMPONENT_LIB} PRIVATE ${CMAKE_SOURCE_DIR}/Firmware/Generated)
|
||||
add_dependencies(${COMPONENT_LIB} Generated)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include <Tactility/Tactility.h>
|
||||
#include <devicetree.h>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include <tt_init.h>
|
||||
@ -24,7 +25,8 @@ void app_main() {
|
||||
tt_init_tactility_c(); // ELF bindings for side-loading on ESP32
|
||||
#endif
|
||||
|
||||
tt::run(config);
|
||||
auto devices = devices_builtin_get();
|
||||
tt::run(config, devices);
|
||||
}
|
||||
|
||||
} // extern
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
@ -27,7 +27,10 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
vfs
|
||||
fatfs
|
||||
lwip
|
||||
drivers-abstract
|
||||
drivers-core
|
||||
)
|
||||
|
||||
if ("${IDF_TARGET}" STREQUAL "esp32s3")
|
||||
list(APPEND REQUIRES_LIST esp_tinyusb)
|
||||
endif ()
|
||||
@ -49,7 +52,6 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
# Read-write
|
||||
fatfs_create_spiflash_image(data "${CMAKE_CURRENT_SOURCE_DIR}/../Data/data" FLASH_IN_PROJECT PRESERVE_TIME)
|
||||
endif ()
|
||||
|
||||
else()
|
||||
file(GLOB_RECURSE SOURCES "Source/*.c*")
|
||||
|
||||
@ -79,6 +81,8 @@ else()
|
||||
PUBLIC lv_screenshot
|
||||
PUBLIC minmea
|
||||
PUBLIC minitar
|
||||
PUBLIC drivers-abstract
|
||||
PUBLIC drivers-core
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <Tactility/app/AppManifest.h>
|
||||
#include <Tactility/hal/Configuration.h>
|
||||
#include <Tactility/service/ServiceManifest.h>
|
||||
#include <device.h>
|
||||
|
||||
namespace tt {
|
||||
|
||||
@ -21,7 +22,7 @@ struct Configuration {
|
||||
* Attempts to initialize Tactility and all configured hardware.
|
||||
* @param[in] config
|
||||
*/
|
||||
void run(const Configuration& config);
|
||||
void run(const Configuration& config, device** devices);
|
||||
|
||||
/**
|
||||
* While technically nullable, this instance is always set if tt_init() succeeds.
|
||||
|
||||
@ -306,7 +306,7 @@ void registerApps() {
|
||||
registerInstalledAppsFromSdCards();
|
||||
}
|
||||
|
||||
void run(const Configuration& config) {
|
||||
void run(const Configuration& config, device** devices) {
|
||||
LOGGER.info("Tactility v{} on {} ({})", TT_VERSION, CONFIG_TT_DEVICE_NAME, CONFIG_TT_DEVICE_ID);
|
||||
|
||||
assert(config.hardware);
|
||||
@ -315,6 +315,16 @@ void run(const Configuration& config) {
|
||||
// Assign early so starting services can use it
|
||||
config_instance = &config;
|
||||
|
||||
if (devices != nullptr) {
|
||||
device_add_all(devices);
|
||||
device_init_all(devices);
|
||||
|
||||
const struct device* i2c_controller = nullptr;
|
||||
const char* label = DEVICE_LABEL_FOR_TYPE(i2c_controller);
|
||||
while (device_find_next(label, &i2c_controller)) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
initEsp();
|
||||
#endif
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
@ -24,9 +24,7 @@ else()
|
||||
PRIVATE ${SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(TactilityCore SYSTEM
|
||||
PUBLIC Include/
|
||||
)
|
||||
target_include_directories(TactilityCore PUBLIC Include/)
|
||||
|
||||
add_definitions(-D_Nullable=)
|
||||
add_definitions(-D_Nonnull=)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user