diff --git a/Buildscripts/devicetree-compiler/.gitignore b/Buildscripts/devicetree-compiler/.gitignore new file mode 100644 index 00000000..5923bb7a --- /dev/null +++ b/Buildscripts/devicetree-compiler/.gitignore @@ -0,0 +1,4 @@ +.venv/ +.idea/ +__pycache__/ +build/ \ No newline at end of file diff --git a/Buildscripts/devicetree-compiler/compile.py b/Buildscripts/devicetree-compiler/compile.py new file mode 100644 index 00000000..7902c4ac --- /dev/null +++ b/Buildscripts/devicetree-compiler/compile.py @@ -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) + diff --git a/Buildscripts/devicetree-compiler/source/binding_files.py b/Buildscripts/devicetree-compiler/source/binding_files.py new file mode 100644 index 00000000..2b9c9226 --- /dev/null +++ b/Buildscripts/devicetree-compiler/source/binding_files.py @@ -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 \ No newline at end of file diff --git a/Buildscripts/devicetree-compiler/source/binding_parser.py b/Buildscripts/devicetree-compiler/source/binding_parser.py new file mode 100644 index 00000000..765ea922 --- /dev/null +++ b/Buildscripts/devicetree-compiler/source/binding_parser.py @@ -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 + ) diff --git a/Buildscripts/devicetree-compiler/source/config.py b/Buildscripts/devicetree-compiler/source/config.py new file mode 100644 index 00000000..806ce0ec --- /dev/null +++ b/Buildscripts/devicetree-compiler/source/config.py @@ -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 diff --git a/Buildscripts/devicetree-compiler/source/files.py b/Buildscripts/devicetree-compiler/source/files.py new file mode 100644 index 00000000..1e262672 --- /dev/null +++ b/Buildscripts/devicetree-compiler/source/files.py @@ -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 diff --git a/Buildscripts/devicetree-compiler/source/generator.py b/Buildscripts/devicetree-compiler/source/generator.py new file mode 100644 index 00000000..07ff78b7 --- /dev/null +++ b/Buildscripts/devicetree-compiler/source/generator.py @@ -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 + + #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) diff --git a/Buildscripts/devicetree-compiler/source/grammar.lark b/Buildscripts/devicetree-compiler/source/grammar.lark new file mode 100644 index 00000000..dff0ddfd --- /dev/null +++ b/Buildscripts/devicetree-compiler/source/grammar.lark @@ -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+ diff --git a/Buildscripts/devicetree-compiler/source/main.py b/Buildscripts/devicetree-compiler/source/main.py new file mode 100644 index 00000000..a5285bed --- /dev/null +++ b/Buildscripts/devicetree-compiler/source/main.py @@ -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) diff --git a/Buildscripts/devicetree-compiler/source/models.py b/Buildscripts/devicetree-compiler/source/models.py new file mode 100644 index 00000000..9b0d2895 --- /dev/null +++ b/Buildscripts/devicetree-compiler/source/models.py @@ -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 diff --git a/Buildscripts/devicetree-compiler/source/printing.py b/Buildscripts/devicetree-compiler/source/printing.py new file mode 100644 index 00000000..67b2ad80 --- /dev/null +++ b/Buildscripts/devicetree-compiler/source/printing.py @@ -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) diff --git a/Buildscripts/devicetree-compiler/source/transformer.py b/Buildscripts/devicetree-compiler/source/transformer.py new file mode 100644 index 00000000..6c77249d --- /dev/null +++ b/Buildscripts/devicetree-compiler/source/transformer.py @@ -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) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 39e07512..a596f4a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 "") diff --git a/Devices/lilygo-tlora-pager/CMakeLists.txt b/Devices/lilygo-tlora-pager/CMakeLists.txt index 02e89f02..568dfe09 100644 --- a/Devices/lilygo-tlora-pager/CMakeLists.txt +++ b/Devices/lilygo-tlora-pager/CMakeLists.txt @@ -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) diff --git a/Devices/lilygo-tlora-pager/Source/drivers/tlora_pager.c b/Devices/lilygo-tlora-pager/Source/drivers/tlora_pager.c new file mode 100644 index 00000000..b4b341d5 --- /dev/null +++ b/Devices/lilygo-tlora-pager/Source/drivers/tlora_pager.c @@ -0,0 +1,18 @@ +#include "tlora_pager.h" +#include +#include + +#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; +} diff --git a/Devices/lilygo-tlora-pager/Source/drivers/tlora_pager.h b/Devices/lilygo-tlora-pager/Source/drivers/tlora_pager.h new file mode 100644 index 00000000..1efa20d9 --- /dev/null +++ b/Devices/lilygo-tlora-pager/Source/drivers/tlora_pager.h @@ -0,0 +1,22 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// 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 diff --git a/Devices/lilygo-tlora-pager/devicetree.yaml b/Devices/lilygo-tlora-pager/devicetree.yaml new file mode 100644 index 00000000..13354fc0 --- /dev/null +++ b/Devices/lilygo-tlora-pager/devicetree.yaml @@ -0,0 +1,4 @@ +dependencies: + - Drivers/drivers-esp +bindings: ./ +dts: lilygo,tlora_pager.dts diff --git a/Devices/lilygo-tlora-pager/lilygo,tlora-pager.yaml b/Devices/lilygo-tlora-pager/lilygo,tlora-pager.yaml new file mode 100644 index 00000000..a4fa32ee --- /dev/null +++ b/Devices/lilygo-tlora-pager/lilygo,tlora-pager.yaml @@ -0,0 +1,3 @@ +description: LilyGO T-Lora Pager + +include: ["root.yaml"] diff --git a/Devices/lilygo-tlora-pager/lilygo,tlora_pager.dts b/Devices/lilygo-tlora-pager/lilygo,tlora_pager.dts new file mode 100644 index 00000000..213463dd --- /dev/null +++ b/Devices/lilygo-tlora-pager/lilygo,tlora_pager.dts @@ -0,0 +1,22 @@ +/dts-v1/; + +#include +#include +#include + +/ { + 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>; + }; +}; diff --git a/Devices/simulator/devicetree.yaml b/Devices/simulator/devicetree.yaml new file mode 100644 index 00000000..2e2eecee --- /dev/null +++ b/Devices/simulator/devicetree.yaml @@ -0,0 +1,4 @@ +dependencies: + - Drivers/drivers-abstract +bindings: ./ +dts: simulator.dts diff --git a/Devices/simulator/simulator.dts b/Devices/simulator/simulator.dts new file mode 100644 index 00000000..a1714425 --- /dev/null +++ b/Devices/simulator/simulator.dts @@ -0,0 +1,9 @@ +/dts-v1/; + +#include +#include + +/ { + model = "Simulator"; + compatible = "root"; +}; diff --git a/Drivers/drivers-abstract/CMakeLists.txt b/Drivers/drivers-abstract/CMakeLists.txt new file mode 100644 index 00000000..08bf50ee --- /dev/null +++ b/Drivers/drivers-abstract/CMakeLists.txt @@ -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 () diff --git a/Drivers/drivers-abstract/bindings/gpio-controller.yaml b/Drivers/drivers-abstract/bindings/gpio-controller.yaml new file mode 100644 index 00000000..e3a2f8bd --- /dev/null +++ b/Drivers/drivers-abstract/bindings/gpio-controller.yaml @@ -0,0 +1,6 @@ +properties: + gpio-count: + type: int + required: true + description: | + The number of available GPIOs. \ No newline at end of file diff --git a/Drivers/drivers-abstract/bindings/i2c-controller.yaml b/Drivers/drivers-abstract/bindings/i2c-controller.yaml new file mode 100644 index 00000000..5f14e180 --- /dev/null +++ b/Drivers/drivers-abstract/bindings/i2c-controller.yaml @@ -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 diff --git a/Drivers/drivers-abstract/bindings/i2c-device.yaml b/Drivers/drivers-abstract/bindings/i2c-device.yaml new file mode 100644 index 00000000..cb1b52a5 --- /dev/null +++ b/Drivers/drivers-abstract/bindings/i2c-device.yaml @@ -0,0 +1,6 @@ +on-bus: i2c + +properties: + register: + required: true + description: device address on the bus \ No newline at end of file diff --git a/Drivers/drivers-abstract/bindings/root.yaml b/Drivers/drivers-abstract/bindings/root.yaml new file mode 100644 index 00000000..6a1cb5c2 --- /dev/null +++ b/Drivers/drivers-abstract/bindings/root.yaml @@ -0,0 +1,4 @@ +properties: + model: + required: true + description: the name of hardware, usually vendor and model name diff --git a/Drivers/drivers-abstract/devicetree.yaml b/Drivers/drivers-abstract/devicetree.yaml new file mode 100644 index 00000000..3f9c0837 --- /dev/null +++ b/Drivers/drivers-abstract/devicetree.yaml @@ -0,0 +1 @@ +bindings: bindings diff --git a/Drivers/drivers-abstract/include/drivers/gpio.h b/Drivers/drivers-abstract/include/drivers/gpio.h new file mode 100644 index 00000000..46199176 --- /dev/null +++ b/Drivers/drivers-abstract/include/drivers/gpio.h @@ -0,0 +1,84 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#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 diff --git a/Drivers/drivers-abstract/include/drivers/gpio_controller.h b/Drivers/drivers-abstract/include/drivers/gpio_controller.h new file mode 100644 index 00000000..4ce58b55 --- /dev/null +++ b/Drivers/drivers-abstract/include/drivers/gpio_controller.h @@ -0,0 +1,35 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#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 diff --git a/Drivers/drivers-abstract/include/drivers/i2c_controller.h b/Drivers/drivers-abstract/include/drivers/i2c_controller.h new file mode 100644 index 00000000..60192667 --- /dev/null +++ b/Drivers/drivers-abstract/include/drivers/i2c_controller.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#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 diff --git a/Drivers/drivers-abstract/include/drivers/root.h b/Drivers/drivers-abstract/include/drivers/root.h new file mode 100644 index 00000000..73a35152 --- /dev/null +++ b/Drivers/drivers-abstract/include/drivers/root.h @@ -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 diff --git a/Drivers/drivers-abstract/source/gpio_controller.c b/Drivers/drivers-abstract/source/gpio_controller.c new file mode 100644 index 00000000..ce4be481 --- /dev/null +++ b/Drivers/drivers-abstract/source/gpio_controller.c @@ -0,0 +1,17 @@ +#include + +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); +} diff --git a/Drivers/drivers-abstract/source/i2c_controller.c b/Drivers/drivers-abstract/source/i2c_controller.c new file mode 100644 index 00000000..4eaf381e --- /dev/null +++ b/Drivers/drivers-abstract/source/i2c_controller.c @@ -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 diff --git a/Drivers/drivers-abstract/source/root.c b/Drivers/drivers-abstract/source/root.c new file mode 100644 index 00000000..cb850b66 --- /dev/null +++ b/Drivers/drivers-abstract/source/root.c @@ -0,0 +1,3 @@ +#include + +const struct root_api root_api_instance = {}; diff --git a/Drivers/drivers-core/CMakeLists.txt b/Drivers/drivers-core/CMakeLists.txt new file mode 100644 index 00000000..086a915b --- /dev/null +++ b/Drivers/drivers-core/CMakeLists.txt @@ -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 () diff --git a/Drivers/drivers-core/include/device.h b/Drivers/drivers-core/include/device.h new file mode 100644 index 00000000..69236490 --- /dev/null +++ b/Drivers/drivers-core/include/device.h @@ -0,0 +1,88 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +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 diff --git a/Drivers/drivers-core/source/device.c b/Drivers/drivers-core/source/device.c new file mode 100644 index 00000000..323d4e76 --- /dev/null +++ b/Drivers/drivers-core/source/device.c @@ -0,0 +1,119 @@ +#include "device.h" + +#include +#include +#include + +#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; +} + diff --git a/Drivers/drivers-esp/CMakeLists.txt b/Drivers/drivers-esp/CMakeLists.txt new file mode 100644 index 00000000..9bd1f6b8 --- /dev/null +++ b/Drivers/drivers-esp/CMakeLists.txt @@ -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 () \ No newline at end of file diff --git a/Drivers/drivers-esp/bindings/espressif,esp32-gpio.yaml b/Drivers/drivers-esp/bindings/espressif,esp32-gpio.yaml new file mode 100644 index 00000000..1ed56aa4 --- /dev/null +++ b/Drivers/drivers-esp/bindings/espressif,esp32-gpio.yaml @@ -0,0 +1,3 @@ +description: ESP32 GPIO Controller + +include: ["gpio-controller.yaml"] diff --git a/Drivers/drivers-esp/bindings/espressif,esp32-i2c.yaml b/Drivers/drivers-esp/bindings/espressif,esp32-i2c.yaml new file mode 100644 index 00000000..eab2be4a --- /dev/null +++ b/Drivers/drivers-esp/bindings/espressif,esp32-i2c.yaml @@ -0,0 +1,3 @@ +description: ESP32 GPIO Controller + +include: ["i2c-controller.yaml"] diff --git a/Drivers/drivers-esp/devicetree.yaml b/Drivers/drivers-esp/devicetree.yaml new file mode 100644 index 00000000..17f25eb7 --- /dev/null +++ b/Drivers/drivers-esp/devicetree.yaml @@ -0,0 +1,3 @@ +dependencies: + - Drivers/drivers-abstract +bindings: bindings diff --git a/Drivers/drivers-esp/include/drivers-esp/esp32_gpio.h b/Drivers/drivers-esp/include/drivers-esp/esp32_gpio.h new file mode 100644 index 00000000..02993c26 --- /dev/null +++ b/Drivers/drivers-esp/include/drivers-esp/esp32_gpio.h @@ -0,0 +1,19 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#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 diff --git a/Drivers/drivers-esp/include/drivers-esp/esp32_i2c.h b/Drivers/drivers-esp/include/drivers-esp/esp32_i2c.h new file mode 100644 index 00000000..4bceb224 --- /dev/null +++ b/Drivers/drivers-esp/include/drivers-esp/esp32_i2c.h @@ -0,0 +1,22 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// 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 diff --git a/Drivers/drivers-esp/source/esp32_gpio.c b/Drivers/drivers-esp/source/esp32_gpio.c new file mode 100644 index 00000000..4d1cfad7 --- /dev/null +++ b/Drivers/drivers-esp/source/esp32_gpio.c @@ -0,0 +1,48 @@ +#include +#include +#include +// ESP +#include +#include + +#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; +} diff --git a/Drivers/drivers-esp/source/esp32_i2c.c b/Drivers/drivers-esp/source/esp32_i2c.c new file mode 100644 index 00000000..1603aec9 --- /dev/null +++ b/Drivers/drivers-esp/source/esp32_i2c.c @@ -0,0 +1,31 @@ +#include "drivers-esp/esp32_i2c.h" +#include +#include +#include + +#define TAG "esp32_i2c" + +#define GET_CONFIG(dev) static_cast(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; +} diff --git a/Firmware/.gitignore b/Firmware/.gitignore new file mode 100644 index 00000000..673b74ec --- /dev/null +++ b/Firmware/.gitignore @@ -0,0 +1 @@ +Generated/ diff --git a/Firmware/CMakeLists.txt b/Firmware/CMakeLists.txt index eb5d9ff9..97f591b3 100644 --- a/Firmware/CMakeLists.txt +++ b/Firmware/CMakeLists.txt @@ -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) diff --git a/Firmware/Source/Main.cpp b/Firmware/Source/Main.cpp index 926da64d..c6dc7db1 100644 --- a/Firmware/Source/Main.cpp +++ b/Firmware/Source/Main.cpp @@ -1,4 +1,5 @@ #include +#include #ifdef ESP_PLATFORM #include @@ -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 \ No newline at end of file diff --git a/Libraries/lv_screenshot/CMakeLists.txt b/Libraries/lv_screenshot/CMakeLists.txt index d6c43221..d9ebe225 100644 --- a/Libraries/lv_screenshot/CMakeLists.txt +++ b/Libraries/lv_screenshot/CMakeLists.txt @@ -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) diff --git a/Libraries/minmea/CMakeLists.txt b/Libraries/minmea/CMakeLists.txt index 060e07f4..dad65397 100644 --- a/Libraries/minmea/CMakeLists.txt +++ b/Libraries/minmea/CMakeLists.txt @@ -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) diff --git a/Tactility/CMakeLists.txt b/Tactility/CMakeLists.txt index b4895b72..ba0395f4 100644 --- a/Tactility/CMakeLists.txt +++ b/Tactility/CMakeLists.txt @@ -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() diff --git a/Tactility/Include/Tactility/Tactility.h b/Tactility/Include/Tactility/Tactility.h index bd229f9e..04a51fa8 100644 --- a/Tactility/Include/Tactility/Tactility.h +++ b/Tactility/Include/Tactility/Tactility.h @@ -4,6 +4,7 @@ #include #include #include +#include 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. diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 4f4cd4ed..9111c7bd 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -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 diff --git a/TactilityC/CMakeLists.txt b/TactilityC/CMakeLists.txt index 97c81c4a..5efe25e2 100644 --- a/TactilityC/CMakeLists.txt +++ b/TactilityC/CMakeLists.txt @@ -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}) diff --git a/TactilityCore/CMakeLists.txt b/TactilityCore/CMakeLists.txt index b57398a1..63598a76 100644 --- a/TactilityCore/CMakeLists.txt +++ b/TactilityCore/CMakeLists.txt @@ -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=) diff --git a/TactilityFreeRtos/CMakeLists.txt b/TactilityFreeRtos/CMakeLists.txt index 072cb4f9..b9b957e6 100644 --- a/TactilityFreeRtos/CMakeLists.txt +++ b/TactilityFreeRtos/CMakeLists.txt @@ -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})