import RT-Thread@9217865c without bsp, libcpu and components/net
This commit is contained in:
commit
e2376a3709
1414 changed files with 390370 additions and 0 deletions
17
components/drivers/fdt/src/SConscript
Normal file
17
components/drivers/fdt/src/SConscript
Normal file
|
@ -0,0 +1,17 @@
|
|||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('*.c')
|
||||
list = os.listdir(cwd)
|
||||
CPPPATH = [cwd + '/../inc' , cwd + '/libfdt']
|
||||
objs = []
|
||||
|
||||
group = DefineGroup('FDT', src, depend = ['RT_USING_FDT'], CPPPATH = CPPPATH)
|
||||
|
||||
for d in list:
|
||||
path = os.path.join(cwd, d)
|
||||
if os.path.isfile(os.path.join(path, 'SConscript')):
|
||||
objs = objs + SConscript(os.path.join(d, 'SConscript'))
|
||||
objs = objs + group
|
||||
|
||||
Return('objs')
|
648
components/drivers/fdt/src/dtb_access.c
Normal file
648
components/drivers/fdt/src/dtb_access.c
Normal file
|
@ -0,0 +1,648 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "libfdt.h"
|
||||
#include "dtb_node.h"
|
||||
|
||||
/* "/aliaes" node */
|
||||
static struct dtb_node *fdt_aliases;
|
||||
|
||||
/**
|
||||
* of_find_property_value_of_size() - find property of given size
|
||||
*
|
||||
* Search for a property in a device node and validate the requested size.
|
||||
*
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
* @len: requested length of property value
|
||||
*
|
||||
* @return the property value on success, -EINVAL if the property does not
|
||||
* exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
|
||||
* property data isn't large enough.
|
||||
*/
|
||||
static void *dtb_node_find_property_value_of_size(const struct dtb_node *dn,
|
||||
const char *propname, uint32_t len)
|
||||
{
|
||||
struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
|
||||
|
||||
if (!prop)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (!prop->value)
|
||||
return ERR_PTR(-ENODATA);
|
||||
if (len > prop->size)
|
||||
return ERR_PTR(-EOVERFLOW);
|
||||
return prop->value;
|
||||
}
|
||||
|
||||
int dtb_node_read_u32(const struct dtb_node *dn, const char *propname, uint32_t *outp)
|
||||
{
|
||||
const uint32_t *val;
|
||||
|
||||
debug("%s: %s: \n", __func__, propname);
|
||||
if (!dn)
|
||||
return -EINVAL;
|
||||
val = dtb_node_find_property_value_of_size(dn, propname, sizeof(*outp));
|
||||
if (IS_ERR(val))
|
||||
{
|
||||
debug("(not found)\n");
|
||||
return PTR_ERR(val);
|
||||
}
|
||||
|
||||
*outp = fdt32_to_cpu(*val);
|
||||
debug("%#x (%d)\n", *outp, *outp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t dtb_node_read_u32_default(const struct dtb_node *node, const char *propname, uint32_t def)
|
||||
{
|
||||
dtb_node_read_u32(node, propname, &def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
int dtb_node_read_u32_array(const struct dtb_node *dn, const char *propname,
|
||||
uint32_t *out_values, size_t sz)
|
||||
{
|
||||
const uint32_t *val;
|
||||
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
val = dtb_node_find_property_value_of_size(dn, propname,
|
||||
sz * sizeof(*out_values));
|
||||
|
||||
if (IS_ERR(val))
|
||||
return PTR_ERR(val);
|
||||
|
||||
debug("size %zd, val:%d\n", sz, *val);
|
||||
while (sz--)
|
||||
*out_values++ = fdt32_to_cpu(*val++);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t dtb_node_read_u32_index_default(const struct dtb_node *node, const char *propname, int index,
|
||||
uint32_t def)
|
||||
{
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
dtb_node_read_u32_index(node, propname, index, &def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
int dtb_node_read_s32_default(const struct dtb_node *node, const char *propname, int32_t def)
|
||||
{
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
dtb_node_read_u32(node, propname, (uint32_t *)&def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
int dtb_node_read_u32_index(const struct dtb_node *dn, const char *propname,
|
||||
int index, uint32_t *outp)
|
||||
{
|
||||
const uint32_t *val;
|
||||
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
if (!dn)
|
||||
return -EINVAL;
|
||||
|
||||
val = dtb_node_find_property_value_of_size(dn, propname,
|
||||
sizeof(*outp) * (index + 1));
|
||||
if (IS_ERR(val))
|
||||
{
|
||||
debug("(not found)\n");
|
||||
return PTR_ERR(val);
|
||||
}
|
||||
|
||||
*outp = fdt32_to_cpu(val[index]);
|
||||
debug("%#x (%d)\n", *outp, *outp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dtb_node_read_u64(const struct dtb_node *dn, const char *propname, uint64_t *outp)
|
||||
{
|
||||
const uint64_t *val;
|
||||
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
if (!dn)
|
||||
return -EINVAL;
|
||||
val = dtb_node_find_property_value_of_size(dn, propname, sizeof(*outp));
|
||||
if (IS_ERR(val))
|
||||
{
|
||||
debug("(not found)\n");
|
||||
return PTR_ERR(val);
|
||||
}
|
||||
|
||||
*outp = fdt64_to_cpu(*val);
|
||||
debug("%#llx (%lld)\n", (unsigned long long)*outp,
|
||||
(unsigned long long)*outp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t dtb_node_read_u64_default(const struct dtb_node *node, const char *propname, uint64_t def)
|
||||
{
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
dtb_node_read_u64(node, propname, &def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
int dtb_node_n_addr_cells(const struct dtb_node *dn)
|
||||
{
|
||||
const uint32_t *ip;
|
||||
|
||||
do
|
||||
{
|
||||
if (dn->parent)
|
||||
dn = dn->parent;
|
||||
ip = dtb_node_get_dtb_node_property_value(dn, "#address-cells", NULL);
|
||||
if (ip)
|
||||
return fdt32_to_cpu(*ip);
|
||||
} while (dn->parent);
|
||||
|
||||
/* No #address-cells property for the root node */
|
||||
return DEV_ROOT_NODE_ADDR_CELLS_DEFAULT;
|
||||
}
|
||||
|
||||
int dtb_node_n_size_cells(const struct dtb_node *dn)
|
||||
{
|
||||
const uint32_t *ip;
|
||||
|
||||
do
|
||||
{
|
||||
if (dn->parent)
|
||||
dn = dn->parent;
|
||||
ip = dtb_node_get_dtb_node_property_value(dn, "#size-cells", NULL);
|
||||
if (ip)
|
||||
return fdt32_to_cpu(*ip);
|
||||
} while (dn->parent);
|
||||
|
||||
/* No #size-cells property for the root node */
|
||||
return DEV_ROOT_NODE_SIZE_CELLS_DEFAULT;
|
||||
}
|
||||
|
||||
int dtb_node_simple_addr_cells(const struct dtb_node *dn)
|
||||
{
|
||||
const uint32_t *ip;
|
||||
|
||||
ip = dtb_node_get_dtb_node_property_value(dn, "#address-cells", NULL);
|
||||
if (ip)
|
||||
return fdt32_to_cpu(*ip);
|
||||
|
||||
/* Return a default of 2 to match fdt_address_cells()*/
|
||||
return 2;
|
||||
}
|
||||
|
||||
int dtb_node_simple_size_cells(const struct dtb_node *dn)
|
||||
{
|
||||
const uint32_t *ip;
|
||||
|
||||
ip = dtb_node_get_dtb_node_property_value(dn, "#size-cells", NULL);
|
||||
if (ip)
|
||||
return fdt32_to_cpu(*ip);
|
||||
|
||||
/* Return a default of 2 to match fdt_size_cells()*/
|
||||
return 2;
|
||||
}
|
||||
|
||||
struct dtb_property *dtb_node_get_dtb_node_property(const struct dtb_node *dtb_node, const char *property_name, int *property_size)
|
||||
{
|
||||
struct dtb_property *dtb_property = NULL;
|
||||
|
||||
if (dtb_node != NULL && property_name != NULL)
|
||||
{
|
||||
dtb_property = dtb_node->properties;
|
||||
|
||||
while (dtb_property != NULL)
|
||||
{
|
||||
if (!strcmp(dtb_property->name, property_name))
|
||||
{
|
||||
if (property_size != NULL)
|
||||
{
|
||||
*property_size = dtb_property->size;
|
||||
}
|
||||
return dtb_property;
|
||||
}
|
||||
dtb_property = dtb_property->next;
|
||||
}
|
||||
}
|
||||
|
||||
return dtb_property;
|
||||
}
|
||||
|
||||
#define for_each_property_of_node(dn, pp) \
|
||||
for (pp = dn->properties; pp != NULL; pp = pp->next)
|
||||
|
||||
struct dtb_node *dtb_node_find_node_opts_by_path(const char *path,
|
||||
const char **opts)
|
||||
{
|
||||
struct dtb_node *np = NULL;
|
||||
struct dtb_property *pp;
|
||||
const char *separator = strchr(path, ':');
|
||||
|
||||
if (opts)
|
||||
*opts = separator ? separator + 1 : NULL;
|
||||
|
||||
if (strcmp(path, "/") == 0)
|
||||
return dtb_node_get(get_dtb_node_head());
|
||||
|
||||
/* The path could begin with an alias */
|
||||
if (*path != '/')
|
||||
{
|
||||
int len;
|
||||
const char *p = separator;
|
||||
|
||||
if (!p)
|
||||
p = strchrnul(path, '/');
|
||||
len = p - path;
|
||||
|
||||
/* of_aliases must not be NULL */
|
||||
if (!fdt_aliases)
|
||||
return NULL;
|
||||
|
||||
for_each_property_of_node(fdt_aliases, pp)
|
||||
{
|
||||
if (strlen(pp->name) == len && !strncmp(pp->name, path,
|
||||
len))
|
||||
{
|
||||
np = dtb_node_find_node_by_path(pp->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!np)
|
||||
return NULL;
|
||||
path = p;
|
||||
}
|
||||
|
||||
/* Step down the tree matching path components */
|
||||
if (!np)
|
||||
np = dtb_node_get(get_dtb_node_head());
|
||||
while (np && *path == '/')
|
||||
{
|
||||
struct dtb_node *tmp = np;
|
||||
|
||||
path++; /* Increment past '/' delimiter */
|
||||
np = dtb_node_get_dtb_node_by_path(np, path);
|
||||
dtb_node_put(tmp);
|
||||
path = strchrnul(path, '/');
|
||||
if (separator && separator < path)
|
||||
break;
|
||||
}
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_find_compatible_node(struct dtb_node *from, const char *compatible)
|
||||
{
|
||||
struct dtb_node *dn;
|
||||
|
||||
for_each_of_allnodes_from(from, dn)
|
||||
{
|
||||
if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
|
||||
dtb_node_get(dn))
|
||||
break;
|
||||
}
|
||||
dtb_node_put(from);
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
void *dtb_node_get_dtb_node_property_value(const struct dtb_node *dtb_node, const char *property_name, int *property_size)
|
||||
{
|
||||
struct dtb_property *dtb_property = dtb_node_get_dtb_node_property(dtb_node, property_name, NULL);
|
||||
|
||||
if (!dtb_property || !dtb_property->value || !dtb_property->size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (property_size != NULL)
|
||||
{
|
||||
*property_size = dtb_property->size;
|
||||
}
|
||||
|
||||
return dtb_property->value;
|
||||
}
|
||||
|
||||
const struct dtb_node *dtb_node_find_node_by_prop_value(struct dtb_node *from,
|
||||
const char *propname,
|
||||
const void *propval, int proplen)
|
||||
{
|
||||
struct dtb_node *np;
|
||||
void *value;
|
||||
for_each_of_allnodes_from(from, np)
|
||||
{
|
||||
value = dtb_node_get_dtb_node_property_value(np, propname, &proplen);
|
||||
if (!memcmp(value, propval, proplen) && dtb_node_get(np))
|
||||
break;
|
||||
}
|
||||
dtb_node_put(from);
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_find_all_nodes(const struct dtb_node *prev)
|
||||
{
|
||||
const struct dtb_node *dn;
|
||||
|
||||
if (!prev)
|
||||
{
|
||||
dn = get_dtb_node_head();
|
||||
}
|
||||
else if (prev->child)
|
||||
{
|
||||
dn = prev->child;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Walk back up looking for a sibling, or the end of the
|
||||
* structure
|
||||
*/
|
||||
dn = prev;
|
||||
while (dn->parent && !dn->sibling)
|
||||
dn = dn->parent;
|
||||
dn = dn->sibling; /* Might be null at the end of the tree */
|
||||
}
|
||||
|
||||
return (struct dtb_node *)dn;
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_device_is_available(const struct dtb_node *device)
|
||||
{
|
||||
const char *status;
|
||||
int statlen;
|
||||
|
||||
if (!device)
|
||||
return RT_FALSE;
|
||||
|
||||
status = dtb_node_get_dtb_node_property_value(device, "status", &statlen);
|
||||
if (status == NULL)
|
||||
return RT_TRUE;
|
||||
|
||||
if (statlen > 0)
|
||||
{
|
||||
if (!strcmp(status, "okay"))
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_parent(const struct dtb_node *node)
|
||||
{
|
||||
const struct dtb_node *dn;
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
dn = dtb_node_get(node->parent);
|
||||
|
||||
return (struct dtb_node *)dn;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_find_node_by_phandle(phandle handle)
|
||||
{
|
||||
struct dtb_node *dn;
|
||||
|
||||
if (!handle)
|
||||
return NULL;
|
||||
|
||||
for_each_of_allnodes(dn) if (dn->handle == handle) break;
|
||||
(void)dtb_node_get(dn);
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
int dtb_node_property_match_string(const struct dtb_node *dn, const char *propname,
|
||||
const char *string)
|
||||
{
|
||||
const struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
|
||||
size_t l;
|
||||
int i;
|
||||
const char *p, *end;
|
||||
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
if (!prop->value)
|
||||
return -ENODATA;
|
||||
|
||||
p = prop->value;
|
||||
end = p + prop->size;
|
||||
|
||||
for (i = 0; p < end; i++, p += l)
|
||||
{
|
||||
l = strnlen(p, end - p) + 1;
|
||||
if (p + l > end)
|
||||
return -EILSEQ;
|
||||
debug("comparing %s with %s\n", string, p);
|
||||
if (strcmp(string, p) == 0)
|
||||
return i; /* Found it; return index */
|
||||
}
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_property_read_string_helper() - Utility helper for parsing string properties
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
* @out_strs: output array of string pointers.
|
||||
* @sz: number of array elements to read.
|
||||
* @skip: Number of strings to skip over at beginning of list.
|
||||
*
|
||||
* Don't call this function directly. It is a utility helper for the
|
||||
* of_property_read_string*() family of functions.
|
||||
*/
|
||||
int dtb_node_property_read_string_helper(const struct dtb_node *dn,
|
||||
const char *propname, const char **out_strs,
|
||||
size_t sz, int skip)
|
||||
{
|
||||
const struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
|
||||
int l = 0, i = 0;
|
||||
const char *p, *end;
|
||||
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
if (!prop->value)
|
||||
return -ENODATA;
|
||||
p = prop->value;
|
||||
end = p + prop->size;
|
||||
|
||||
for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l)
|
||||
{
|
||||
l = strnlen(p, end - p) + 1;
|
||||
if (p + l > end)
|
||||
return -EILSEQ;
|
||||
if (out_strs && i >= skip)
|
||||
*out_strs++ = p;
|
||||
}
|
||||
i -= skip;
|
||||
return i <= 0 ? -ENODATA : i;
|
||||
}
|
||||
|
||||
static int __dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
|
||||
const char *list_name,
|
||||
const char *cells_name,
|
||||
int cell_count, int index,
|
||||
struct fdt_phandle_args *out_args)
|
||||
{
|
||||
const uint32_t *list, *list_end;
|
||||
int rc = 0, cur_index = 0;
|
||||
uint32_t count = 0;
|
||||
struct dtb_node *node = NULL;
|
||||
phandle phandle;
|
||||
int size;
|
||||
|
||||
/* Retrieve the phandle list property */
|
||||
list = dtb_node_get_dtb_node_property_value(dn, list_name, &size);
|
||||
if (!list)
|
||||
return -ENOENT;
|
||||
list_end = list + size / sizeof(*list);
|
||||
|
||||
/* Loop over the phandles until all the requested entry is found */
|
||||
while (list < list_end)
|
||||
{
|
||||
rc = -EINVAL;
|
||||
count = 0;
|
||||
|
||||
/*
|
||||
* If phandle is 0, then it is an empty entry with no
|
||||
* arguments. Skip forward to the next entry.
|
||||
*/
|
||||
phandle = fdt32_to_cpu(*(list++));
|
||||
if (phandle)
|
||||
{
|
||||
/*
|
||||
* Find the provider node and parse the #*-cells
|
||||
* property to determine the argument length.
|
||||
*
|
||||
* This is not needed if the cell count is hard-coded
|
||||
* (i.e. cells_name not set, but cell_count is set),
|
||||
* except when we're going to return the found node
|
||||
* below.
|
||||
*/
|
||||
if (cells_name || cur_index == index)
|
||||
{
|
||||
node = dtb_node_find_node_by_phandle(phandle);
|
||||
if (!node)
|
||||
{
|
||||
debug("%s: could not find phandle\n",
|
||||
dn->path);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (cells_name)
|
||||
{
|
||||
if (dtb_node_read_u32(node, cells_name, &count))
|
||||
{
|
||||
debug("%s: could not get %s for %s\n",
|
||||
dn->path, cells_name,
|
||||
node->path);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
count = cell_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure that the arguments actually fit in the
|
||||
* remaining property data length
|
||||
*/
|
||||
if (list + count > list_end)
|
||||
{
|
||||
debug("%s: arguments longer than property\n",
|
||||
dn->path);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* All of the error cases above bail out of the loop, so at
|
||||
* this point, the parsing is successful. If the requested
|
||||
* index matches, then fill the out_args structure and return,
|
||||
* or return -ENOENT for an empty entry.
|
||||
*/
|
||||
rc = -ENOENT;
|
||||
if (cur_index == index)
|
||||
{
|
||||
if (!phandle)
|
||||
goto err;
|
||||
|
||||
if (out_args)
|
||||
{
|
||||
int i;
|
||||
if (count > FDT_MAX_PHANDLE_ARGS)
|
||||
count = FDT_MAX_PHANDLE_ARGS;
|
||||
out_args->np = node;
|
||||
out_args->args_count = count;
|
||||
for (i = 0; i < count; i++)
|
||||
out_args->args[i] =
|
||||
fdt32_to_cpu(*(list++));
|
||||
}
|
||||
else
|
||||
{
|
||||
dtb_node_put(node);
|
||||
}
|
||||
|
||||
/* Found it! return success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
dtb_node_put(node);
|
||||
node = NULL;
|
||||
list += count;
|
||||
cur_index++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock node before returning result; will be one of:
|
||||
* -ENOENT : index is for empty phandle
|
||||
* -EINVAL : parsing error on data
|
||||
* [1..n] : Number of phandle (count mode; when index = -1)
|
||||
*/
|
||||
rc = index < 0 ? cur_index : -ENOENT;
|
||||
err:
|
||||
if (node)
|
||||
dtb_node_put(node);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_parse_phandle(const struct dtb_node *dn,
|
||||
const char *phandle_name, int index)
|
||||
{
|
||||
struct fdt_phandle_args args;
|
||||
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
if (__dtb_node_parse_phandle_with_args(dn, phandle_name, NULL, 0, index,
|
||||
&args))
|
||||
return NULL;
|
||||
|
||||
return args.np;
|
||||
}
|
||||
|
||||
int dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
|
||||
const char *list_name, const char *cells_name,
|
||||
int index, struct fdt_phandle_args *out_args)
|
||||
{
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return __dtb_node_parse_phandle_with_args(dn, list_name, cells_name, 0,
|
||||
index, out_args);
|
||||
}
|
||||
|
||||
int dtb_node_count_phandle_with_args(const struct dtb_node *dn,
|
||||
const char *list_name, const char *cells_name)
|
||||
{
|
||||
return __dtb_node_parse_phandle_with_args(dn, list_name, cells_name, 0,
|
||||
-1, NULL);
|
||||
}
|
57
components/drivers/fdt/src/dtb_addr.c
Normal file
57
components/drivers/fdt/src/dtb_addr.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "libfdt.h"
|
||||
#include "dtb_node.h"
|
||||
|
||||
/* Max address size we deal with */
|
||||
#define FDT_MAX_ADDR_CELLS 4
|
||||
#define FDT_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= FDT_MAX_ADDR_CELLS)
|
||||
#define FDT_CHECK_COUNTS(na, ns) (FDT_CHECK_ADDR_COUNT(na) && (ns) > 0)
|
||||
|
||||
static void dtb_node_default_count_cells(const struct dtb_node *dn,
|
||||
int *addrc, int *sizec)
|
||||
{
|
||||
if (addrc)
|
||||
*addrc = dtb_node_n_addr_cells(dn);
|
||||
if (sizec)
|
||||
*sizec = dtb_node_n_size_cells(dn);
|
||||
}
|
||||
|
||||
const uint32_t *dtb_node_get_address(const struct dtb_node *dev, int index,
|
||||
uint64_t *size, unsigned int *flags)
|
||||
{
|
||||
const uint32_t *prop;
|
||||
int psize;
|
||||
struct dtb_node *parent;
|
||||
int onesize, i, na, ns;
|
||||
|
||||
/* Get parent */
|
||||
parent = dtb_node_get_parent(dev);
|
||||
if (parent == NULL)
|
||||
return NULL;
|
||||
|
||||
dtb_node_default_count_cells(dev, &na, &ns);
|
||||
if (!FDT_CHECK_ADDR_COUNT(na))
|
||||
return NULL;
|
||||
|
||||
/* Get "reg" or "assigned-addresses" property */
|
||||
prop = dtb_node_get_dtb_node_property_value(dev, "reg", &psize);
|
||||
if (prop == NULL)
|
||||
return NULL;
|
||||
psize /= 4;
|
||||
|
||||
onesize = na + ns;
|
||||
for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
|
||||
if (i == index)
|
||||
{
|
||||
if (size)
|
||||
*size = dtb_node_read_number(prop + na, ns);
|
||||
if (flags)
|
||||
*flags = 0x200;
|
||||
return prop;
|
||||
}
|
||||
return NULL;
|
||||
}
|
574
components/drivers/fdt/src/dtb_base.c
Normal file
574
components/drivers/fdt/src/dtb_base.c
Normal file
|
@ -0,0 +1,574 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "dtb_node.h"
|
||||
#include "libfdt.h"
|
||||
#include "libfdt_env.h"
|
||||
#include <ctype.h>
|
||||
|
||||
static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
|
||||
{
|
||||
if (*base == 0)
|
||||
{
|
||||
if (s[0] == '0')
|
||||
{
|
||||
if (tolower(s[1]) == 'x' && isxdigit((int)s[2]))
|
||||
*base = 16;
|
||||
else
|
||||
*base = 8;
|
||||
}
|
||||
else
|
||||
*base = 10;
|
||||
}
|
||||
if (*base == 16 && s[0] == '0' && tolower(s[1]) == 'x')
|
||||
s += 2;
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned long simple_strtoul(const char *cp, char **endp,
|
||||
unsigned int base)
|
||||
{
|
||||
unsigned long result = 0;
|
||||
unsigned long value;
|
||||
|
||||
cp = _parse_integer_fixup_radix(cp, &base);
|
||||
|
||||
while (isxdigit((int)*cp) && (value = isdigit((int)*cp) ? *cp-'0' : (islower((int)*cp)
|
||||
? toupper(*cp) : *cp)-'A'+10) < base)
|
||||
{
|
||||
result = result*base + value;
|
||||
cp++;
|
||||
}
|
||||
|
||||
if (endp)
|
||||
*endp = (char *)cp;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
|
||||
{
|
||||
char *tail;
|
||||
unsigned long val;
|
||||
size_t len;
|
||||
|
||||
*res = 0;
|
||||
len = strlen(cp);
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
val = simple_strtoul(cp, &tail, base);
|
||||
if (tail == cp)
|
||||
return -EINVAL;
|
||||
|
||||
if ((*tail == '\0') ||
|
||||
((len == (size_t)(tail - cp) + 1) && (*tail == '\n')))
|
||||
{
|
||||
*res = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
long simple_strtol(const char *cp, char **endp, unsigned int base)
|
||||
{
|
||||
if (*cp == '-')
|
||||
return -simple_strtoul(cp + 1, endp, base);
|
||||
|
||||
return simple_strtoul(cp, endp, base);
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname)
|
||||
{
|
||||
const void *prop;
|
||||
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
|
||||
prop = dtb_node_get_property(node, propname, NULL);
|
||||
|
||||
debug("%s\n", prop ? "true" : "false");
|
||||
|
||||
return prop ? RT_TRUE : RT_FALSE;
|
||||
}
|
||||
|
||||
const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep)
|
||||
{
|
||||
const char *val = NULL;
|
||||
int len;
|
||||
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
debug("%s: %s: ", __func__, propname);
|
||||
|
||||
struct dtb_property *prop = dtb_node_get_dtb_node_property(node, propname, &len);
|
||||
|
||||
if (prop)
|
||||
{
|
||||
val = prop->value;
|
||||
len = prop->size;
|
||||
}
|
||||
|
||||
if (!val)
|
||||
{
|
||||
debug("<not found>\n");
|
||||
if (sizep)
|
||||
*sizep = -FDT_ERR_NOTFOUND;
|
||||
return NULL;
|
||||
}
|
||||
if (sizep)
|
||||
*sizep = len;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
const char *dtb_node_read_string(const struct dtb_node *node, const char *propname)
|
||||
{
|
||||
const char *str;
|
||||
int len;
|
||||
|
||||
str = dtb_node_read_prop(node, propname, &len);
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
if (strnlen(str, len) >= len)
|
||||
{
|
||||
debug("<invalid>\n");
|
||||
return NULL;
|
||||
}
|
||||
debug("%s\n", str);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name)
|
||||
{
|
||||
const struct dtb_node *subnode;
|
||||
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
debug("%s: %s: ", __func__, subnode_name);
|
||||
|
||||
for (node = node->child; node; node = node->sibling)
|
||||
{
|
||||
if (!strcmp(subnode_name, node->name))
|
||||
break;
|
||||
}
|
||||
subnode = node;
|
||||
|
||||
debug("%s\n", dtb_node_valid(subnode) ?\
|
||||
dtb_node_get_name(subnode) : "<none>");
|
||||
|
||||
return subnode;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node)
|
||||
{
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
|
||||
return node->child;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node)
|
||||
{
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
|
||||
return node->sibling;
|
||||
}
|
||||
|
||||
const char *dtb_node_get_name(const struct dtb_node *node)
|
||||
{
|
||||
if (!dtb_node_valid(node))
|
||||
{
|
||||
debug("%s node not valid\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return strrchr(node->path, '/') + 1;
|
||||
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_by_phandle(uint32_t phandle)
|
||||
{
|
||||
if (dtb_node_active())
|
||||
return dtb_node_find_node_by_phandle(phandle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dtb_node_read_size(const struct dtb_node *node, const char *propname)
|
||||
{
|
||||
struct dtb_property *prop = dtb_node_get_dtb_node_property( node, propname, NULL);
|
||||
|
||||
if (prop)
|
||||
return prop->size;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int dtb_node_get_addr_and_size_by_index(const struct dtb_node *node, int index, size_t *addr, size_t *size)
|
||||
{
|
||||
const uint32_t *prop;
|
||||
int psize;
|
||||
int onesize, na, ns;
|
||||
|
||||
na = dtb_node_n_addr_cells(node);
|
||||
ns = dtb_node_n_size_cells(node);
|
||||
|
||||
prop = dtb_node_get_dtb_node_property_value(node, "reg", &psize);
|
||||
if (prop == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
psize /= 4;
|
||||
onesize = na + ns;
|
||||
|
||||
if (psize >= (index + 1) * onesize)
|
||||
{
|
||||
prop += index * onesize;
|
||||
|
||||
if (addr)
|
||||
{
|
||||
*addr = dtb_node_read_number(prop, na);
|
||||
}
|
||||
if (size)
|
||||
{
|
||||
*size = dtb_node_read_number(prop + na, ns);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t dtb_node_get_addr_index(const struct dtb_node *node, int index)
|
||||
{
|
||||
int na;
|
||||
size_t size;
|
||||
|
||||
const uint32_t *prop_val;
|
||||
uint flags;
|
||||
|
||||
prop_val = dtb_node_get_address(node, index,
|
||||
(uint64_t *)&size, &flags);
|
||||
if (!prop_val)
|
||||
return -1;
|
||||
|
||||
na = dtb_node_n_addr_cells(node);
|
||||
|
||||
return dtb_node_read_number(prop_val, na);
|
||||
}
|
||||
|
||||
size_t dtb_node_get_addr(const struct dtb_node *node)
|
||||
{
|
||||
return dtb_node_get_addr_index(node, 0);
|
||||
}
|
||||
|
||||
int dtb_node_stringlist_search(const struct dtb_node *node, const char *property,
|
||||
const char *string)
|
||||
{
|
||||
return dtb_node_property_match_string(node, property, string);
|
||||
}
|
||||
|
||||
int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index,
|
||||
const char **outp)
|
||||
{
|
||||
return dtb_node_property_read_string_index(node, property, index, outp);
|
||||
}
|
||||
|
||||
int dtb_node_read_string_count(const struct dtb_node *node, const char *property)
|
||||
{
|
||||
return dtb_node_property_count_strings(node, property);
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_path(const char *path)
|
||||
{
|
||||
if (dtb_node_active())
|
||||
return dtb_node_find_node_by_path(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *dtb_node_get_chosen_prop(const char *name)
|
||||
{
|
||||
const struct dtb_node *chosen_node;
|
||||
|
||||
chosen_node = (const struct dtb_node *)dtb_node_path("/chosen");
|
||||
|
||||
return dtb_node_read_string(chosen_node, name);
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_chosen_node(const char *name)
|
||||
{
|
||||
const char *prop;
|
||||
|
||||
prop = dtb_node_get_chosen_prop(name);
|
||||
if (!prop)
|
||||
return NULL;
|
||||
|
||||
return dtb_node_path(prop);
|
||||
}
|
||||
|
||||
const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp)
|
||||
{
|
||||
return dtb_node_get_dtb_node_property_value(node, propname, lenp);
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_is_available(const struct dtb_node *node)
|
||||
{
|
||||
return dtb_node_device_is_available(node);
|
||||
}
|
||||
|
||||
size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property,
|
||||
size_t *sizep)
|
||||
{
|
||||
int na, ns;
|
||||
int psize;
|
||||
const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, property, &psize);
|
||||
|
||||
if (!prop)
|
||||
return -1;
|
||||
na = dtb_node_n_addr_cells(node);
|
||||
ns = dtb_node_n_size_cells(node);
|
||||
*sizep = dtb_node_read_number(prop + na, ns);
|
||||
|
||||
return dtb_node_read_number(prop, na);
|
||||
}
|
||||
|
||||
const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname,
|
||||
size_t sz)
|
||||
{
|
||||
int psize;
|
||||
const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, propname, &psize);
|
||||
|
||||
if (!prop || sz != psize)
|
||||
return NULL;
|
||||
return (uint8_t *)prop;
|
||||
}
|
||||
|
||||
int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num)
|
||||
{
|
||||
const struct dtb_node *dn;
|
||||
int num = 0;
|
||||
for_each_of_allnodes_from(from, dn)
|
||||
{
|
||||
if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
|
||||
dtb_node_get(dn))
|
||||
{
|
||||
|
||||
num++;
|
||||
*node_table = (struct dtb_node *)dn;
|
||||
node_table++;
|
||||
if (num >= max_num)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*node_num = num;
|
||||
dtb_node_put(from);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len,
|
||||
const void *value)
|
||||
{
|
||||
struct dtb_property *pp;
|
||||
struct dtb_property *pp_last = NULL;
|
||||
struct dtb_property *new;
|
||||
|
||||
if (!dtb_node_active())
|
||||
return -ENOSYS;
|
||||
|
||||
if (!node)
|
||||
return -EINVAL;
|
||||
|
||||
for (pp = node->properties; pp; pp = pp->next)
|
||||
{
|
||||
if (strcmp(pp->name, propname) == 0)
|
||||
{
|
||||
/* Property exists -> change value */
|
||||
pp->value = (void *)value;
|
||||
pp->size = len;
|
||||
return 0;
|
||||
}
|
||||
pp_last = pp;
|
||||
}
|
||||
|
||||
if (!pp_last)
|
||||
return -ENOENT;
|
||||
|
||||
/* Property does not exist -> append new property */
|
||||
new = (struct dtb_property *)malloc(sizeof(struct dtb_property));
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
new->name = strdup(propname);
|
||||
if (!new->name)
|
||||
{
|
||||
free(new);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
new->value = (void *)value;
|
||||
new->size = len;
|
||||
new->next = NULL;
|
||||
|
||||
pp_last->next = new;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value)
|
||||
{
|
||||
if (!dtb_node_active())
|
||||
return -ENOSYS;
|
||||
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
|
||||
debug("%s: %s = %s", __func__, propname, value);
|
||||
|
||||
return dtb_node_write_prop(node, propname, strlen(value) + 1, value);
|
||||
}
|
||||
|
||||
int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value)
|
||||
{
|
||||
if (!dtb_node_active())
|
||||
return -ENOSYS;
|
||||
|
||||
RT_ASSERT(dtb_node_valid(node));
|
||||
|
||||
if (value)
|
||||
return dtb_node_write_string(node, "status", "okay");
|
||||
else
|
||||
return dtb_node_write_string(node, "status", "disable");
|
||||
}
|
||||
|
||||
/**
|
||||
* dtb_node_irq_find_parent - Given a device node, find its interrupt parent node
|
||||
* @child: pointer to device node
|
||||
*
|
||||
* Returns a pointer to the interrupt parent node, or NULL if the interrupt
|
||||
* parent could not be determined.
|
||||
*/
|
||||
static struct dtb_node *dtb_node_irq_find_parent(struct dtb_node *child)
|
||||
{
|
||||
struct dtb_node *p;
|
||||
phandle parent;
|
||||
|
||||
if (!dtb_node_get(child))
|
||||
return NULL;
|
||||
do
|
||||
{
|
||||
if (dtb_node_read_u32_array(child, "interrupt-parent", &parent, 1))
|
||||
{
|
||||
p = dtb_node_get_parent(child);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = dtb_node_get_by_phandle(parent);
|
||||
}
|
||||
dtb_node_put(child);
|
||||
child = p;
|
||||
} while (p && dtb_node_get_property(p, "#interrupt-cells", NULL) == NULL);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int dtb_node_irq_get(struct dtb_node *dev, int index)
|
||||
{
|
||||
int rc = 0;
|
||||
struct fdt_phandle_args out_irq;
|
||||
struct dtb_node *p;
|
||||
uint32_t intsize;
|
||||
int res, i;
|
||||
|
||||
p = dtb_node_irq_find_parent(dev);
|
||||
if (p == NULL)
|
||||
return -EINVAL;
|
||||
/* Get size of interrupt specifier */
|
||||
if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
|
||||
{
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
|
||||
|
||||
/* Copy intspec into irq structure */
|
||||
out_irq.np = p;
|
||||
out_irq.args_count = intsize;
|
||||
for (i = 0; i < intsize; i++)
|
||||
{
|
||||
res = dtb_node_read_u32_index(dev, "interrupts",
|
||||
(index * 3 + i),
|
||||
out_irq.args + i);
|
||||
if (res)
|
||||
goto out;
|
||||
}
|
||||
rc = out_irq.args[1];
|
||||
out:
|
||||
dtb_node_put(p);
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* dtb_node_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
|
||||
* @dev: pointer to device tree node
|
||||
* @name: IRQ name
|
||||
*
|
||||
* Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
|
||||
* -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
|
||||
* of any other failure.
|
||||
*/
|
||||
int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
|
||||
index = dtb_node_stringlist_search(dev, "interrupt-names", name);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
return dtb_node_irq_get(dev, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* dtb_node_irq_count - Count the number of IRQs a node uses
|
||||
* @dev: pointer to device tree node
|
||||
*/
|
||||
int dtb_node_irq_count(struct dtb_node *device)
|
||||
{
|
||||
struct dtb_node *p;
|
||||
uint32_t intsize;
|
||||
int nr = 0, res = 0;
|
||||
|
||||
p = dtb_node_irq_find_parent(device);
|
||||
if (p == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* Get size of interrupt specifier */
|
||||
if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
|
||||
{
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
|
||||
|
||||
res = dtb_node_read_size(device, "interrupts");
|
||||
if (res < 0)
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
nr = res / (intsize * 4);
|
||||
out:
|
||||
dtb_node_put(p);
|
||||
|
||||
return nr;
|
||||
}
|
820
components/drivers/fdt/src/dtb_get.c
Normal file
820
components/drivers/fdt/src/dtb_get.c
Normal file
|
@ -0,0 +1,820 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "libfdt.h"
|
||||
#include "dtb_node.h"
|
||||
|
||||
static struct
|
||||
{
|
||||
const char *ptr;
|
||||
const char *end;
|
||||
char *cur;
|
||||
} paths_buf = {NULL, NULL};
|
||||
static void *current_fdt;
|
||||
|
||||
int fdt_exec_status = FDT_RET_GET_OK;
|
||||
|
||||
int dtb_node_get_exec_status()
|
||||
{
|
||||
return fdt_exec_status;
|
||||
}
|
||||
|
||||
static int _dtb_node_get_dtb_properties_list(struct dtb_property *dtb_property, off_t node_off)
|
||||
{
|
||||
/* caller alrealy checked current_fdt */
|
||||
off_t property_off = fdt_first_property_offset(current_fdt, node_off);
|
||||
struct fdt_property *fdt_property;
|
||||
|
||||
if (property_off < 0)
|
||||
{
|
||||
return FDT_RET_GET_EMPTY;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
fdt_property = (struct fdt_property *)fdt_get_property_by_offset(current_fdt, property_off, &dtb_property->size);
|
||||
if (fdt_property != NULL)
|
||||
{
|
||||
dtb_property->name = fdt_string(current_fdt, fdt32_to_cpu(fdt_property->nameoff));
|
||||
dtb_property->value = fdt_property->data;
|
||||
dtb_property->size = fdt32_to_cpu(fdt_property->len);
|
||||
}
|
||||
|
||||
property_off = fdt_next_property_offset(current_fdt, property_off);
|
||||
if (property_off >= 0)
|
||||
{
|
||||
dtb_property->next = (struct dtb_property *)malloc(sizeof(struct dtb_property));
|
||||
if (dtb_property->next == NULL)
|
||||
{
|
||||
return FDT_RET_NO_MEMORY;
|
||||
}
|
||||
dtb_property = dtb_property->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
dtb_property->next = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return FDT_RET_GET_OK;
|
||||
}
|
||||
|
||||
static int _dtb_node_get_dtb_nodes_list(struct dtb_node *dtb_node_head, struct dtb_node *dtb_node, const char *pathname)
|
||||
{
|
||||
off_t root_off;
|
||||
off_t node_off;
|
||||
int pathname_sz;
|
||||
int node_name_sz;
|
||||
|
||||
/* caller alrealy checked current_fdt */
|
||||
if ((root_off = fdt_path_offset(current_fdt, pathname)) >= 0)
|
||||
{
|
||||
pathname_sz = strlen(pathname);
|
||||
node_off = fdt_first_subnode(current_fdt, root_off);
|
||||
|
||||
if (node_off < 0)
|
||||
{
|
||||
return FDT_RET_GET_EMPTY;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
dtb_node->parent = dtb_node_head;
|
||||
dtb_node->sibling = NULL;
|
||||
dtb_node->name = fdt_get_name(current_fdt, node_off, &node_name_sz);
|
||||
|
||||
/* parent_path + name + '/' + '\0' */
|
||||
if (paths_buf.cur + pathname_sz + node_name_sz + 2 < paths_buf.end)
|
||||
{
|
||||
dtb_node->path = (const char *)paths_buf.cur;
|
||||
strncpy(paths_buf.cur, pathname, pathname_sz);
|
||||
paths_buf.cur += pathname_sz;
|
||||
strncpy(paths_buf.cur, (char *)dtb_node->name, node_name_sz);
|
||||
paths_buf.cur += node_name_sz;
|
||||
*paths_buf.cur++ = '/';
|
||||
*paths_buf.cur++ = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
dtb_node->path = NULL;
|
||||
rt_kprintf("\033[31m\rERROR: `FDT_DTB_ALL_NODES_PATH_SIZE' = %d bytes is configured too low.\033[0m\n", FDT_DTB_ALL_NODES_PATH_SIZE);
|
||||
return FDT_RET_NO_MEMORY;
|
||||
}
|
||||
|
||||
dtb_node->handle = fdt_get_phandle(current_fdt, node_off);
|
||||
dtb_node->properties = (struct dtb_property *)malloc(sizeof(struct dtb_property));
|
||||
dtb_node->child = (struct dtb_node *)malloc(sizeof(struct dtb_node));
|
||||
|
||||
if (dtb_node->properties == NULL || dtb_node->child == NULL)
|
||||
{
|
||||
return FDT_RET_NO_MEMORY;
|
||||
}
|
||||
|
||||
fdt_exec_status = _dtb_node_get_dtb_properties_list(dtb_node->properties, node_off);
|
||||
if (fdt_exec_status == FDT_RET_GET_EMPTY)
|
||||
{
|
||||
free(dtb_node->properties);
|
||||
dtb_node->properties = NULL;
|
||||
}
|
||||
else if (fdt_exec_status != FDT_RET_GET_OK)
|
||||
{
|
||||
return fdt_exec_status;
|
||||
}
|
||||
|
||||
fdt_exec_status = _dtb_node_get_dtb_nodes_list(dtb_node, dtb_node->child, dtb_node->path);
|
||||
if (fdt_exec_status == FDT_RET_GET_EMPTY)
|
||||
{
|
||||
free(dtb_node->child);
|
||||
dtb_node->child = NULL;
|
||||
}
|
||||
else if (fdt_exec_status != FDT_RET_GET_OK)
|
||||
{
|
||||
return fdt_exec_status;
|
||||
}
|
||||
|
||||
node_off = fdt_next_subnode(current_fdt, node_off);
|
||||
if (node_off >= 0)
|
||||
{
|
||||
dtb_node->sibling = (struct dtb_node *)malloc(sizeof(struct dtb_node));
|
||||
if (dtb_node->sibling == NULL)
|
||||
{
|
||||
return FDT_RET_NO_MEMORY;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FDT_RET_GET_OK;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_list(void *fdt)
|
||||
{
|
||||
int root_off;
|
||||
struct dtb_node *dtb_node_head = NULL;
|
||||
|
||||
if (fdt == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_LOADED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
current_fdt = fdt;
|
||||
|
||||
if ((dtb_node_head = (struct dtb_node *)malloc(sizeof(struct dtb_node))) == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (paths_buf.ptr == NULL)
|
||||
{
|
||||
paths_buf.ptr = (char *)malloc(FDT_DTB_ALL_NODES_PATH_SIZE);
|
||||
|
||||
if (paths_buf.ptr == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
paths_buf.cur = (char *)paths_buf.ptr;
|
||||
paths_buf.end = (char *)(paths_buf.ptr + FDT_DTB_ALL_NODES_PATH_SIZE);
|
||||
}
|
||||
|
||||
root_off = fdt_path_offset(fdt, "/");
|
||||
|
||||
if ((dtb_node_head->header = (struct dtb_header *)malloc(sizeof(struct dtb_header))) == NULL)
|
||||
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
((struct dtb_header *)dtb_node_head->header)->root = '/';
|
||||
((struct dtb_header *)dtb_node_head->header)->zero = '\0';
|
||||
((struct dtb_header *)dtb_node_head->header)->memreserve_sz = fdt_num_mem_rsv(fdt);
|
||||
|
||||
if (dtb_node_head->header->memreserve_sz > 0)
|
||||
{
|
||||
int i;
|
||||
int memreserve_sz = dtb_node_head->header->memreserve_sz;
|
||||
uint32_t off_mem_rsvmap = fdt_off_mem_rsvmap(fdt);
|
||||
struct fdt_reserve_entry *rsvmap = (struct fdt_reserve_entry *)((char *)fdt + off_mem_rsvmap);
|
||||
|
||||
((struct dtb_header *)dtb_node_head->header)->memreserve = (struct dtb_memreserve *)malloc(sizeof(struct dtb_memreserve) * memreserve_sz);
|
||||
if (dtb_node_head->header->memreserve == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
for (i = 0; i < memreserve_sz; ++i)
|
||||
{
|
||||
((struct dtb_header *)dtb_node_head->header)->memreserve[i].address = fdt64_to_cpu(rsvmap[i].address);
|
||||
((struct dtb_header *)dtb_node_head->header)->memreserve[i].size = fdt64_to_cpu(rsvmap[i].size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
((struct dtb_header *)dtb_node_head->header)->memreserve = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
dtb_node_head->path = paths_buf.ptr;
|
||||
*paths_buf.cur++ = '/';
|
||||
*paths_buf.cur++ = '\0';
|
||||
dtb_node_head->parent = NULL;
|
||||
dtb_node_head->sibling = NULL;
|
||||
|
||||
dtb_node_head->handle = fdt_get_phandle(fdt, root_off);
|
||||
dtb_node_head->properties = (struct dtb_property *)malloc(sizeof(struct dtb_property));
|
||||
dtb_node_head->child = (struct dtb_node *)malloc(sizeof(struct dtb_node));
|
||||
|
||||
if (dtb_node_head->properties == NULL || dtb_node_head->child == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((fdt_exec_status = _dtb_node_get_dtb_properties_list(dtb_node_head->properties, root_off)) != FDT_RET_GET_OK)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((fdt_exec_status = _dtb_node_get_dtb_nodes_list(dtb_node_head, dtb_node_head->child, dtb_node_head->path)) != FDT_RET_GET_OK)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* paths_buf.ptr addr save in the dtb_node_head's path */
|
||||
paths_buf.ptr = NULL;
|
||||
paths_buf.cur = NULL;
|
||||
|
||||
return dtb_node_head;
|
||||
|
||||
fail:
|
||||
if (dtb_node_head != NULL)
|
||||
{
|
||||
dtb_node_free_dtb_list(dtb_node_head);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _dtb_node_free_dtb_node(struct dtb_node *dtb_node)
|
||||
{
|
||||
struct dtb_node *dtb_node_last;
|
||||
struct dtb_property *dtb_property;
|
||||
struct dtb_property *dtb_property_last;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
dtb_property = dtb_node->properties;
|
||||
while (dtb_property != NULL)
|
||||
{
|
||||
dtb_property_last = dtb_property;
|
||||
dtb_property = dtb_property->next;
|
||||
free(dtb_property_last);
|
||||
}
|
||||
|
||||
_dtb_node_free_dtb_node(dtb_node->child);
|
||||
|
||||
dtb_node_last = dtb_node;
|
||||
dtb_node = dtb_node->sibling;
|
||||
free(dtb_node_last);
|
||||
}
|
||||
}
|
||||
|
||||
void dtb_node_free_dtb_list(struct dtb_node *dtb_node_head)
|
||||
{
|
||||
if (dtb_node_head == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* only root node can free all path buffer */
|
||||
if (dtb_node_head->parent == NULL || (dtb_node_head->path != NULL && !strcmp(dtb_node_head->path, "/")))
|
||||
{
|
||||
if (dtb_node_head->path != NULL)
|
||||
{
|
||||
free((void *)dtb_node_head->path);
|
||||
}
|
||||
if (dtb_node_head->header != NULL)
|
||||
{
|
||||
if (dtb_node_head->header->memreserve != NULL)
|
||||
{
|
||||
free((void *)dtb_node_head->header->memreserve);
|
||||
}
|
||||
free((void *)dtb_node_head->header);
|
||||
}
|
||||
}
|
||||
|
||||
_dtb_node_free_dtb_node(dtb_node_head);
|
||||
}
|
||||
|
||||
static void _dtb_node_printf_depth(int depth)
|
||||
{
|
||||
int i = depth;
|
||||
while (i --> 0)
|
||||
{
|
||||
rt_kputs("\t");
|
||||
}
|
||||
}
|
||||
|
||||
rt_bool_t _dtb_node_test_string_list(const void *value, int size)
|
||||
{
|
||||
const char *str = value;
|
||||
const char *str_start, *str_end;
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
/* string end with '\0' */
|
||||
if (str[size - 1] != '\0')
|
||||
{
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
/* get string list end */
|
||||
str_end = str + size;
|
||||
|
||||
while (str < str_end)
|
||||
{
|
||||
str_start = str;
|
||||
/* before string list end, not '\0' and a printable characters */
|
||||
while (str < str_end && *str && ((unsigned char)*str >= ' ' && (unsigned char)*str <= '~'))
|
||||
{
|
||||
++str;
|
||||
}
|
||||
|
||||
/* not zero, or not increased */
|
||||
if (*str != '\0' || str == str_start)
|
||||
{
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
/* next string */
|
||||
++str;
|
||||
}
|
||||
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
static void _dtb_node_printf_dtb_node_info(struct dtb_node *dtb_node)
|
||||
{
|
||||
static int depth = 0;
|
||||
struct dtb_property *dtb_property;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
rt_kputs("\n");
|
||||
_dtb_node_printf_depth(depth);
|
||||
rt_kputs(dtb_node->name);
|
||||
rt_kputs(" {\n");
|
||||
++depth;
|
||||
|
||||
dtb_property = dtb_node->properties;
|
||||
while (dtb_property != NULL)
|
||||
{
|
||||
_dtb_node_printf_depth(depth);
|
||||
|
||||
rt_kputs(dtb_property->name);
|
||||
|
||||
if (dtb_property->size > 0)
|
||||
{
|
||||
int size = dtb_property->size;
|
||||
char *value = dtb_property->value;
|
||||
|
||||
rt_kputs(" = ");
|
||||
if (_dtb_node_test_string_list(value, size) == RT_TRUE)
|
||||
{
|
||||
/* print string list */
|
||||
char *str = value;
|
||||
do
|
||||
{
|
||||
rt_kprintf("\"%s\"", str);
|
||||
str += strlen(str) + 1;
|
||||
rt_kputs(", ");
|
||||
} while (str < value + size);
|
||||
rt_kputs("\b\b");
|
||||
}
|
||||
else if ((size % 4) == 0)
|
||||
{
|
||||
/* print addr and size cell */
|
||||
int i;
|
||||
fdt32_t *cell = (fdt32_t *)value;
|
||||
|
||||
rt_kputs("<");
|
||||
for (i = 0, size /= 4; i < size; ++i)
|
||||
{
|
||||
rt_kprintf("0x%x ", fdt32_to_cpu(cell[i]));
|
||||
}
|
||||
rt_kputs("\b>");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* print bytes array */
|
||||
int i;
|
||||
uint8_t *byte = (uint8_t *)value;
|
||||
|
||||
rt_kputs("[");
|
||||
for (i = 0; i < size; ++i)
|
||||
{
|
||||
rt_kprintf("%02x ", *byte++);
|
||||
}
|
||||
rt_kputs("\b]");
|
||||
}
|
||||
}
|
||||
rt_kputs(";\n");
|
||||
dtb_property = dtb_property->next;
|
||||
}
|
||||
|
||||
_dtb_node_printf_dtb_node_info(dtb_node->child);
|
||||
dtb_node = dtb_node->sibling;
|
||||
|
||||
--depth;
|
||||
_dtb_node_printf_depth(depth);
|
||||
rt_kputs("};\n");
|
||||
}
|
||||
}
|
||||
|
||||
void dtb_node_get_dts_dump(struct dtb_node *dtb_node_head)
|
||||
{
|
||||
if (dtb_node_head != NULL)
|
||||
{
|
||||
int i = dtb_node_head->header->memreserve_sz;
|
||||
|
||||
rt_kputs("/dts-v1/;\n");
|
||||
while (i --> 0)
|
||||
{
|
||||
rt_kprintf("\n/memreserve/\t0x%lx 0x%zx;", dtb_node_head->header->memreserve[i].address, dtb_node_head->header->memreserve[i].size);
|
||||
}
|
||||
|
||||
_dtb_node_printf_dtb_node_info(dtb_node_head);
|
||||
}
|
||||
}
|
||||
|
||||
static void _dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node, void (callback(struct dtb_node *dtb_node)))
|
||||
{
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
callback(dtb_node);
|
||||
_dtb_node_get_enum_dtb_node(dtb_node->child, callback);
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
}
|
||||
|
||||
void dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node_head, void (callback(struct dtb_node *dtb_node)))
|
||||
{
|
||||
if (dtb_node_head == NULL || callback == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_dtb_node_get_enum_dtb_node(dtb_node_head, callback);
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename)
|
||||
{
|
||||
struct dtb_node *dtb_node_child;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (!strcmp(nodename, dtb_node->name))
|
||||
{
|
||||
return dtb_node;
|
||||
}
|
||||
|
||||
dtb_node_child = dtb_node_get_dtb_node_by_name_DFS(dtb_node->child, nodename);
|
||||
|
||||
if (dtb_node_child != NULL)
|
||||
{
|
||||
return dtb_node_child;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename)
|
||||
{
|
||||
if (dtb_node != NULL)
|
||||
{
|
||||
struct dtb_node *dtb_node_child, *dtb_node_head = dtb_node;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (!strcmp(nodename, dtb_node->name))
|
||||
{
|
||||
return dtb_node;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
|
||||
dtb_node = dtb_node_head;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
dtb_node_child = dtb_node_get_dtb_node_by_name_BFS(dtb_node->child, nodename);
|
||||
|
||||
if (dtb_node_child != NULL)
|
||||
{
|
||||
return dtb_node_child;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname)
|
||||
{
|
||||
int i = 0;
|
||||
char *node_name;
|
||||
char *pathname_clone;
|
||||
int pathname_sz;
|
||||
|
||||
if (pathname == NULL || dtb_node == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* skip '/' */
|
||||
if (*pathname == '/')
|
||||
{
|
||||
++pathname;
|
||||
}
|
||||
|
||||
/* root not have sibling, so skip */
|
||||
if (dtb_node->parent == NULL || !strcmp(dtb_node->path, "/"))
|
||||
{
|
||||
dtb_node = dtb_node->child;
|
||||
}
|
||||
|
||||
pathname_sz = strlen(pathname) + 1;
|
||||
pathname_clone = (char *)malloc(pathname_sz);
|
||||
if (pathname_clone == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
strncpy(pathname_clone, pathname, pathname_sz);
|
||||
node_name = pathname_clone;
|
||||
|
||||
while (pathname_clone[i])
|
||||
{
|
||||
if (pathname_clone[i] == '/')
|
||||
{
|
||||
/* set an end of name that can used to strcmp */
|
||||
pathname_clone[i] = '\0';
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (!strcmp(dtb_node->name, node_name))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
if (dtb_node == NULL)
|
||||
{
|
||||
free(pathname_clone);
|
||||
return NULL;
|
||||
}
|
||||
dtb_node = dtb_node->child;
|
||||
node_name = &pathname_clone[i + 1];
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
/*
|
||||
* found the end node and it's name:
|
||||
* (pathname[pathname_sz - 1]) is '\0'
|
||||
* (&pathname_clone[i] - node_name) is the node_name's length
|
||||
*/
|
||||
node_name = &((char *)pathname)[(pathname_sz - 1) - (&pathname_clone[i] - node_name)];
|
||||
free(pathname_clone);
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (!strcmp(dtb_node->name, node_name))
|
||||
{
|
||||
return dtb_node;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle)
|
||||
{
|
||||
struct dtb_node *dtb_node_child;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (dtb_node->handle == handle)
|
||||
{
|
||||
return dtb_node;
|
||||
}
|
||||
|
||||
dtb_node_child = dtb_node_get_dtb_node_by_phandle_DFS(dtb_node->child, handle);
|
||||
|
||||
if (dtb_node_child != NULL)
|
||||
{
|
||||
return dtb_node_child;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dtb_node *dtb_node_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle)
|
||||
{
|
||||
if (dtb_node != NULL)
|
||||
{
|
||||
struct dtb_node *dtb_node_child, *dtb_node_head = dtb_node;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
if (dtb_node->handle == handle)
|
||||
{
|
||||
return dtb_node;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
|
||||
dtb_node = dtb_node_head;
|
||||
|
||||
while (dtb_node != NULL)
|
||||
{
|
||||
dtb_node_child = dtb_node_get_dtb_node_by_phandle_BFS(dtb_node->child, handle);
|
||||
|
||||
if (dtb_node_child != NULL)
|
||||
{
|
||||
return dtb_node_child;
|
||||
}
|
||||
dtb_node = dtb_node->sibling;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dtb_node_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells)
|
||||
{
|
||||
if (dtb_node != NULL && addr_cells != NULL && size_cells != NULL)
|
||||
{
|
||||
struct dtb_property *dtb_property;
|
||||
*addr_cells = -1;
|
||||
*size_cells = -1;
|
||||
|
||||
/* if couldn't found, check parent */
|
||||
while ((dtb_node = dtb_node->parent) != NULL)
|
||||
{
|
||||
dtb_property = dtb_node->properties;
|
||||
while (dtb_property != NULL)
|
||||
{
|
||||
if (!strcmp(dtb_property->name, "#address-cells"))
|
||||
{
|
||||
*addr_cells = fdt32_to_cpu(*(int *)dtb_property->value);
|
||||
}
|
||||
else if (!strcmp(dtb_property->name, "#size-cells"))
|
||||
{
|
||||
*size_cells = fdt32_to_cpu(*(int *)dtb_property->value);
|
||||
}
|
||||
if (*addr_cells != -1 && *size_cells != -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
dtb_property = dtb_property->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (*addr_cells == -1)
|
||||
{
|
||||
*addr_cells = FDT_ROOT_ADDR_CELLS_DEFAULT;
|
||||
}
|
||||
if (*size_cells == -1)
|
||||
{
|
||||
*size_cells = FDT_ROOT_SIZE_CELLS_DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct dtb_memreserve *dtb_node_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size)
|
||||
{
|
||||
if (dtb_node != NULL && memreserve_size != NULL)
|
||||
{
|
||||
struct dtb_node *dtb_node_root = dtb_node;
|
||||
|
||||
while (dtb_node_root != NULL)
|
||||
{
|
||||
if (!strcmp(dtb_node_root->path, "/"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
dtb_node_root = dtb_node_root->parent;
|
||||
}
|
||||
if(dtb_node_root == NULL) return NULL;
|
||||
|
||||
*memreserve_size = dtb_node_root->header->memreserve_sz;
|
||||
|
||||
return dtb_node_root->header->memreserve;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_get_dtb_node_status(const struct dtb_node *dtb_node)
|
||||
{
|
||||
if (dtb_node != NULL)
|
||||
{
|
||||
char *status = dtb_node_get_dtb_node_property_value(dtb_node, "status", NULL);
|
||||
if (status != NULL)
|
||||
{
|
||||
return (!strcmp(status, "okay") || !strcmp(status, "ok")) ? RT_TRUE : RT_FALSE;
|
||||
}
|
||||
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_get_dtb_node_compatible_match(const struct dtb_node *dtb_node, const char *compatibles)
|
||||
{
|
||||
if (dtb_node != NULL)
|
||||
{
|
||||
if (compatibles != NULL)
|
||||
{
|
||||
char *str_ptr;
|
||||
int prop_sz;
|
||||
|
||||
for_each_property_string(dtb_node, "compatible", str_ptr, prop_sz)
|
||||
{
|
||||
if (!strcmp(compatibles, str_ptr))
|
||||
{
|
||||
return RT_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RT_FALSE;
|
||||
}
|
||||
|
||||
char *dtb_node_get_dtb_string_list_value(void *value, int size, int index)
|
||||
{
|
||||
int i = 0;
|
||||
char *str = value;
|
||||
|
||||
if (str != NULL)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (i++ == index)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
str += strlen(str) + 1;
|
||||
} while (str < (char *)value + size);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *dtb_node_get_dtb_string_list_value_next(void *value, void *end)
|
||||
{
|
||||
char *str = value;
|
||||
|
||||
if (str != NULL)
|
||||
{
|
||||
str += strlen(str) + 1;
|
||||
if (str < (char *)end)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t dtb_node_get_dtb_cell_value(void *value)
|
||||
{
|
||||
return fdt32_to_cpu(*(fdt32_t *)value);
|
||||
}
|
||||
|
||||
uint8_t dtb_node_get_dtb_byte_value(void *value)
|
||||
{
|
||||
return *(uint8_t *)value;
|
||||
}
|
41
components/drivers/fdt/src/dtb_head.c
Normal file
41
components/drivers/fdt/src/dtb_head.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "dtb_node.h"
|
||||
|
||||
static void *dtb_root = NULL;
|
||||
static struct dtb_node *dtb_node_list = NULL;
|
||||
void *get_fdt_blob(void)
|
||||
{
|
||||
return dtb_root;
|
||||
}
|
||||
|
||||
struct dtb_node *get_dtb_node_head(void)
|
||||
{
|
||||
return dtb_node_list;
|
||||
}
|
||||
|
||||
rt_bool_t dtb_node_active(void)
|
||||
{
|
||||
return dtb_node_list != NULL;
|
||||
}
|
||||
|
||||
int device_tree_setup(void *mem_addr)
|
||||
{
|
||||
if(mem_addr)
|
||||
{
|
||||
if ((dtb_root = dtb_node_load_from_memory(mem_addr,1)) != NULL)
|
||||
{
|
||||
dtb_node_list = dtb_node_get_dtb_list(dtb_root);
|
||||
if (dtb_node_list != NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
107
components/drivers/fdt/src/dtb_load.c
Normal file
107
components/drivers/fdt/src/dtb_load.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "libfdt.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "dtb_node.h"
|
||||
|
||||
extern int fdt_exec_status;
|
||||
|
||||
rt_bool_t dtb_node_check(void *fdt)
|
||||
{
|
||||
return fdt_check_header(fdt) == 0 ? RT_TRUE : RT_FALSE;
|
||||
}
|
||||
|
||||
void *dtb_node_load_from_fs(char *dtb_filename)
|
||||
{
|
||||
void *fdt = NULL;
|
||||
size_t dtb_sz;
|
||||
int fd = -1;
|
||||
|
||||
if (dtb_filename == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_GET_EMPTY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
fd = open(dtb_filename, O_RDONLY, 0);
|
||||
|
||||
if (fd == -1)
|
||||
{
|
||||
rt_kprintf("File `%s' not found.\n", dtb_filename);
|
||||
fdt_exec_status = FDT_RET_GET_EMPTY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dtb_sz = lseek(fd, 0, SEEK_END);
|
||||
|
||||
if (dtb_sz > 0)
|
||||
{
|
||||
if ((fdt = (struct fdt_header *)malloc(sizeof(uint8_t) * dtb_sz)) == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
read(fd, fdt, sizeof(uint8_t) * dtb_sz);
|
||||
|
||||
if (dtb_node_check(fdt) == RT_FALSE)
|
||||
{
|
||||
free(fdt);
|
||||
fdt=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (fd != -1)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return fdt;
|
||||
}
|
||||
|
||||
void *dtb_node_load_from_memory(void *dtb_ptr, rt_bool_t is_clone)
|
||||
{
|
||||
void *fdt = NULL;
|
||||
|
||||
if (dtb_ptr == NULL)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_GET_EMPTY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (dtb_node_check(dtb_ptr) == RT_FALSE)
|
||||
{
|
||||
fdt_exec_status = FDT_RET_GET_EMPTY;
|
||||
fdt = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (is_clone)
|
||||
{
|
||||
size_t dtb_sz = fdt_totalsize(dtb_ptr);
|
||||
if (dtb_sz > 0)
|
||||
{
|
||||
if ((fdt = (size_t *)malloc(dtb_sz)) != NULL)
|
||||
{
|
||||
memcpy(fdt, dtb_ptr, dtb_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
fdt_exec_status = FDT_RET_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fdt = dtb_ptr;
|
||||
}
|
||||
|
||||
end:
|
||||
return fdt;
|
||||
}
|
166
components/drivers/fdt/src/dtb_set.c
Normal file
166
components/drivers/fdt/src/dtb_set.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "libfdt.h"
|
||||
#include "dtb_node.h"
|
||||
|
||||
static off_t dtb_node_find_and_add_subnode(void *fdt, char* name)
|
||||
{
|
||||
off_t chosen_offset = 0;
|
||||
|
||||
chosen_offset = fdt_subnode_offset(fdt, 0, name);
|
||||
|
||||
if (chosen_offset == -FDT_ERR_NOTFOUND)
|
||||
{
|
||||
chosen_offset = fdt_add_subnode(fdt, 0, name);
|
||||
}
|
||||
|
||||
return chosen_offset;
|
||||
}
|
||||
|
||||
size_t dtb_node_set_linux_cmdline(void *fdt, char *cmdline)
|
||||
{
|
||||
off_t chosen_offset;
|
||||
size_t cmdline_size;
|
||||
|
||||
if (cmdline == NULL || fdt == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
chosen_offset = dtb_node_find_and_add_subnode(fdt, "chosen");
|
||||
cmdline_size = strlen(cmdline);
|
||||
|
||||
/* install bootargs */
|
||||
if (chosen_offset >= 0 || chosen_offset == -FDT_ERR_EXISTS)
|
||||
{
|
||||
if (fdt_setprop(fdt, chosen_offset, "bootargs", cmdline, cmdline_size) < 0)
|
||||
{
|
||||
fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_DTB_PAD_SIZE);
|
||||
fdt_setprop(fdt, chosen_offset, "bootargs", cmdline, cmdline_size);
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return fdt_totalsize(fdt);
|
||||
}
|
||||
|
||||
size_t dtb_node_set_linux_initrd(void *fdt, uint64_t initrd_addr, size_t initrd_size)
|
||||
{
|
||||
uint64_t addr, size_ptr;
|
||||
off_t chosen_offset;
|
||||
int i;
|
||||
|
||||
if (fdt == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
chosen_offset = dtb_node_find_and_add_subnode(fdt, "chosen");
|
||||
|
||||
/* update the entry */
|
||||
for (i = fdt_num_mem_rsv(fdt) - 1; i >= 0; --i)
|
||||
{
|
||||
fdt_get_mem_rsv(fdt, i, &addr, &size_ptr);
|
||||
if (addr == initrd_addr)
|
||||
{
|
||||
fdt_del_mem_rsv(fdt, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* add the memory */
|
||||
if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0)
|
||||
{
|
||||
/* move the memory */
|
||||
fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_DTB_PAD_SIZE);
|
||||
if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* install initrd */
|
||||
if (chosen_offset >= 0 || chosen_offset == -FDT_ERR_EXISTS)
|
||||
{
|
||||
chosen_offset = fdt_path_offset(fdt, "/chosen");
|
||||
|
||||
if (IN_64BITS_MODE)
|
||||
{
|
||||
fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-start", initrd_addr);
|
||||
fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-end", initrd_addr + initrd_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
fdt_setprop_u32(fdt, chosen_offset, "linux,initrd-start", initrd_addr);
|
||||
fdt_setprop_u32(fdt, chosen_offset, "linux,initrd-end", initrd_addr + initrd_size);
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return fdt_totalsize(fdt);
|
||||
}
|
||||
|
||||
size_t dtb_node_set_dtb_property(void *fdt, char *pathname, char *property_name, uint32_t *cells, size_t cells_size)
|
||||
{
|
||||
int node_off;
|
||||
|
||||
if (fdt == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
node_off = fdt_path_offset(fdt, pathname);
|
||||
|
||||
if (node_off >= 0 && cells_size != 0)
|
||||
{
|
||||
fdt_setprop(fdt, node_off, property_name, cells, cells_size);
|
||||
}
|
||||
|
||||
end:
|
||||
return fdt_totalsize(fdt);
|
||||
}
|
||||
|
||||
size_t dtb_node_add_dtb_memreserve(void *fdt, uint64_t address, uint64_t size)
|
||||
{
|
||||
if (fdt == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
fdt_add_mem_rsv(fdt, address, size);
|
||||
|
||||
end:
|
||||
return fdt_totalsize(fdt);
|
||||
}
|
||||
|
||||
size_t dtb_node_del_dtb_memreserve(void *fdt, uint64_t address)
|
||||
{
|
||||
int i;
|
||||
int num_mem_rsvmap;
|
||||
uint32_t off_mem_rsvmap;
|
||||
struct fdt_reserve_entry *rsvmap;
|
||||
|
||||
if (fdt == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
num_mem_rsvmap = fdt_num_mem_rsv(fdt);
|
||||
off_mem_rsvmap = fdt_off_mem_rsvmap(fdt);
|
||||
rsvmap = (struct fdt_reserve_entry *)((char *)fdt + off_mem_rsvmap);
|
||||
|
||||
for (i = 0; i < num_mem_rsvmap; ++i)
|
||||
{
|
||||
if (address == fdt64_to_cpu(rsvmap[i].address))
|
||||
{
|
||||
fdt_del_mem_rsv(fdt, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return fdt_totalsize(fdt);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue