/*
<rvvm/rvvm_fdt.h> - Flattened Device Tree API
Copyright (C) 2020-2026 LekKit <github.com/LekKit>
                        cerg2010cerg2010 <github.com/cerg2010cerg2010>

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

#ifndef _RVVM_FDT_API_H
#define _RVVM_FDT_API_H

#include <rvvm/rvvm_base.h>

RVVM_EXTERN_C_BEGIN

/**
 * @defgroup rvvm_fdt_api Flattened Device Tree API
 * @addtogroup rvvm_fdt_api
 * @{
 */

/**
 * Create FDT node
 *
 * If name is NULL, a root FDT node is created
 *
 * \param name FDT node name, or NULL for a root node
 * \return     FDT node handle
 */
RVVM_PUBLIC rvvm_fdt_node_t* rvvm_fdt_init(const char* name);

/**
 * Create FDT node with device region suffix, like <device@10000>
 *
 * \param name Device name
 * \param addr Device address
 * \return     FDT node handle
 */
RVVM_PUBLIC rvvm_fdt_node_t* rvvm_fdt_init_reg(const char* name, uint64_t addr);

/**
 * Attach child FDT node
 *
 * The root node recursively owns the entire device tree
 *
 * Removes the node from its previous parent, if any
 *
 * Attaching to NULL frees child node tree
 *
 * \param parent FDT parent node handle
 * \param child  FDT child node handle
 *
 * This function is thread-safe
 */
RVVM_PUBLIC void rvvm_fdt_attach(rvvm_fdt_node_t* parent, rvvm_fdt_node_t* child);

/**
 * Free FDT node tree
 *
 * Removes the node from its parent, if any
 *
 * \param node FDT node handle
 *
 * This function is thread-safe
 */
static inline void rvvm_fdt_free(rvvm_fdt_node_t* node)
{
    rvvm_fdt_attach(NULL, node);
}

/**
 * Serialize FDT into a buffer
 *
 * If buffer is NULL, no data is written and required size is returned
 *
 * \param node FDT node handle
 * \param buff Buffer to serialize to or NULL
 * \param size Buffer size
 * \return     Serialized FDT size
 *
 * This function is thread-safe
 */
RVVM_PUBLIC size_t rvvm_fdt_serialize(rvvm_fdt_node_t* node, void* buff, size_t size);

/**
 * Get required buffer size for serialized FDT
 *
 * \param node FDT node handle
 * \return     FDT structure size
 *
 * This function is thread-safe
 */
static inline size_t rvvm_fdt_size(rvvm_fdt_node_t* node)
{
    return rvvm_fdt_serialize(node, NULL, 0);
}

/**
 * Look up child FDT node by name
 *
 * \param node FDT node handle
 * \param name Child FDT node name
 * \return     Child FDT node handle or NULL
 *
 * This function is thread-safe
 */
RVVM_PUBLIC rvvm_fdt_node_t* rvvm_fdt_find(rvvm_fdt_node_t* node, const char* name);

/**
 * Look up child FDT node by name with hex address, like <device@10000>
 *
 * \param node FDT node handle
 * \param name Child FDT node device name
 * \param addr Child FDT node device address
 * \return     Child FDT node handle or NULL
 *
 * This function is thread-safe
 */
RVVM_PUBLIC rvvm_fdt_node_t* rvvm_fdt_find_reg(rvvm_fdt_node_t* node, const char* name, uint64_t addr);

/**
 * Look up child FDT node by name with any address
 *
 * \param node FDT node handle
 * \param name Child FDT node device name
 * \return     Child FDT node handle or NULL
 *
 * This function is thread-safe
 */
static inline rvvm_fdt_node_t* rvvm_fdt_find_reg_any(rvvm_fdt_node_t* node, const char* name)
{
    return rvvm_fdt_find_reg(node, name, -1);
}

/**
 * Obtain FDT node phandle
 *
 * \param node FDT node handle
 * \return     FDT node phandle
 * \note       Allocates phandles, must be attached from the FDT root
 *
 * This function is thread-safe
 */
RVVM_PUBLIC uint32_t rvvm_fdt_phandle(rvvm_fdt_node_t* node);

/**
 * Set arbitrary byte data property on FDT node
 *
 * Overwrites previous property with same name
 *
 * \param node FDT node handle
 * \param name Property name
 * \param data Property data, copied internally
 * \param size Property size
 *
 * This function is thread-safe
 */
RVVM_PUBLIC void rvvm_fdt_prop_set(rvvm_fdt_node_t* node, const char* name, const void* data, size_t size);

/**
 * Set flag property without data payload on FDT node, like "dma-coherent"
 *
 * \param node FDT node handle
 * \param name Property name
 *
 * This function is thread-safe
 */
static inline void rvvm_fdt_prop_set_flag(rvvm_fdt_node_t* node, const char* name)
{
    rvvm_fdt_prop_set(node, name, NULL, 0);
}

/**
 * Set multi-cell property on FDT node
 *
 * \param node  FDT node handle
 * \param name  Property name
 * \param cells Property cells (u32, host order)
 * \param count Property cells count
 *
 * This function is thread-safe
 */
RVVM_PUBLIC void rvvm_fdt_prop_set_cells(rvvm_fdt_node_t* node, const char* name, const uint32_t* cells, size_t count);

/**
 * Set single-cell u32 property on FDT node
 *
 * \param node FDT node handle
 * \param name Property name
 * \param val  Property value
 *
 * This function is thread-safe
 */
static inline void rvvm_fdt_prop_set_u32(rvvm_fdt_node_t* node, const char* name, uint32_t val)
{
    rvvm_fdt_prop_set_cells(node, name, &val, 1);
}

/**
 * Set double-cell u64 property on FDT node
 *
 * \param node FDT node handle
 * \param name Property name
 * \param val  Property value
 *
 * This function is thread-safe
 */
static inline void rvvm_fdt_prop_set_u64(rvvm_fdt_node_t* node, const char* name, uint64_t val)
{
    uint32_t cells[2] = {(uint32_t)(val >> 32), (uint32_t)val};
    rvvm_fdt_prop_set_cells(node, name, cells, 2);
}

/**
 * Set region range property on FDT node (addr cells: 2, size cells: 2)
 *
 * \param node FDT node handle
 * \param name Property name
 * \param addr Property region address
 * \param size Property region size
 *
 * This function is thread-safe
 */
static inline void rvvm_fdt_prop_set_reg(rvvm_fdt_node_t* node, const char* name, uint64_t addr, uint64_t size)
{
    uint32_t cells[4] = {(uint32_t)(addr >> 32), (uint32_t)addr, (uint32_t)(size >> 32), (uint32_t)size};
    rvvm_fdt_prop_set_cells(node, name, cells, 4);
}

/**
 * Set string property on FDT node
 *
 * \param node FDT node handle
 * \param name Property name
 * \param str  Property string
 *
 * This function is thread-safe
 */
RVVM_PUBLIC void rvvm_fdt_prop_set_str(rvvm_fdt_node_t* node, const char* name, const char* str);

/**
 * Set string list property on FDT node, like multiple compatible strings
 *
 * List must be a compile-time string literal, separated by NUL bytes (\0)
 *
 * \param node FDT node handle
 * \param name Property name
 * \param list String list literal in the form "one\0two\0three"
 *
 * This function is thread-safe
 */
#define rvvm_fdt_prop_set_str_list(node, name, list)                                                                   \
    do {                                                                                                               \
        static const char rvvm_tmp_prop_list[] = list;                                                                 \
        rvvm_fdt_prop_set(node, name, rvvm_tmp_prop_list, sizeof(rvvm_tmp_prop_list));                                 \
    } while (0)

/**
 * Delete property from FDT node
 *
 * \param node FDT node handle
 * \param name Property name
 *
 * This function is thread-safe
 */
RVVM_PUBLIC void rvvm_fdt_prop_del(rvvm_fdt_node_t* node, const char* name);

/**
 * Get FDT node property
 *
 * \param node FDT node handle
 * \param name Property name
 * \param size Pointer to return property size
 * \return     Property data (Valid until property is changed) or NULL
 *
 * This function is thread-safe
 */
RVVM_PUBLIC const void* rvvm_fdt_prop_get(rvvm_fdt_node_t* node, const char* name, size_t* size);

/** @}*/

RVVM_EXTERN_C_END

#endif
