summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc/rtems-fdt/rtems-fdt.c
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2017-08-16 14:22:23 +1000
committerChris Johns <chrisj@rtems.org>2017-08-20 11:11:46 +1000
commit6b7efdb2ed77760e3c68c5cf450a9620a1cd6562 (patch)
tree7af6121eedd638496f7dc8c2ad380e9917692f14 /cpukit/libmisc/rtems-fdt/rtems-fdt.c
parentdev/i2c: Add I2C device support for FPGA Slave, LM25066A, TMP112, ADS1113, AD... (diff)
downloadrtems-6b7efdb2ed77760e3c68c5cf450a9620a1cd6562.tar.bz2
libmisc/rtems-fdt: Add RTEMS FDT wrapper and shell command to libmisc.
- Provide application support for handling FDT blobs in RTEMS. This is useful when interfacing FPGA fabrics. - Provide a shell command to list a blob as well as provide read and write access to addresses in the FTB. Closes #3099.
Diffstat (limited to 'cpukit/libmisc/rtems-fdt/rtems-fdt.c')
-rw-r--r--cpukit/libmisc/rtems-fdt/rtems-fdt.c1191
1 files changed, 1191 insertions, 0 deletions
diff --git a/cpukit/libmisc/rtems-fdt/rtems-fdt.c b/cpukit/libmisc/rtems-fdt/rtems-fdt.c
new file mode 100644
index 0000000000..cbdb656fcc
--- /dev/null
+++ b/cpukit/libmisc/rtems-fdt/rtems-fdt.c
@@ -0,0 +1,1191 @@
+/*
+ * COPYRIGHT (c) 2013-2017 Chris Johns <chrisj@rtems.org>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <libfdt.h>
+#include <zlib.h>
+
+#include <rtems/rtems-fdt.h>
+#include <rtems/libio_.h>
+
+/**
+ * An index for quick access to the FDT by name or offset.
+ */
+
+typedef struct
+{
+ const char * name; /**< The full path of the FDT item. */
+ int offset; /**< The offset of the item in the FDT blob. */
+} rtems_fdt_index_entry;
+
+typedef struct
+{
+ int num_entries; /**< The number of entries in this index. */
+ rtems_fdt_index_entry* entries; /**< An ordered set of entries which we
+ * can binary search. */
+ char* names; /**< Storage allocated for all the path names. */
+} rtems_fdt_index;
+
+
+/**
+ * A blob descriptor.
+ */
+struct rtems_fdt_blob
+{
+ rtems_chain_node node; /**< The node's link in the chain. */
+ const void* blob; /**< The FDT blob. */
+ const char* name; /**< The name of the blob. */
+ int refs; /**< The number of active references of the blob. */
+ rtems_fdt_index index; /**< The index used for quick access to items in the blob. */
+};
+
+/**
+ * The global FDT data. This structure is allocated on the heap when the first
+ * call to load a FDT blob is made and released when all blobs have been
+ * unloaded..
+ */
+typedef struct
+{
+ rtems_id lock; /**< The FDT lock id */
+ rtems_chain_control blobs; /**< List if loaded blobs. */
+ const char* paths; /**< Search paths for blobs. */
+} rtems_fdt_data;
+
+/**
+ * Semaphore configuration to create a mutex.
+ */
+#define RTEMS_MUTEX_ATTRIBS \
+ (RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | \
+ RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL)
+
+/**
+ * The FDT data.
+ */
+static rtems_fdt_data* fdt_data;
+
+static bool
+rtems_fdt_unlock (void)
+{
+ /*
+ * Not sure any error should be returned or an assert.
+ */
+ rtems_status_code sc;
+ sc = rtems_semaphore_release (fdt_data->lock);
+ if ((sc != RTEMS_SUCCESSFUL) && (errno == 0))
+ {
+ errno = EINVAL;
+ return false;
+ }
+ return true;
+}
+
+static bool
+rtems_fdt_data_init (void)
+{
+ /*
+ * Lock the FDT. We only create a lock if a call is made. First we test if a
+ * lock is present. If one is present we lock it. If not the libio lock is
+ * locked and we then test the lock again. If not present we create the lock
+ * then release libio lock.
+ */
+ if (!fdt_data)
+ {
+ rtems_libio_lock ();
+
+ if (!fdt_data)
+ {
+ rtems_status_code sc;
+ rtems_id lock;
+
+ /*
+ * Always in the heap.
+ */
+ fdt_data = malloc (sizeof (rtems_fdt_data));
+ if (!fdt_data)
+ {
+ errno = ENOMEM;
+ return false;
+ }
+
+ *fdt_data = (rtems_fdt_data) { 0 };
+
+ /*
+ * Create the FDT lock.
+ */
+ sc = rtems_semaphore_create (rtems_build_name ('F', 'D', 'T', ' '),
+ 1, RTEMS_MUTEX_ATTRIBS,
+ RTEMS_NO_PRIORITY, &lock);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ free (fdt_data);
+ return false;
+ }
+
+ sc = rtems_semaphore_obtain (lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ rtems_semaphore_delete (lock);
+ free (fdt_data);
+ return false;
+ }
+
+ fdt_data->lock = lock;
+
+ /*
+ * Initialise the blob list.
+ */
+ rtems_chain_initialize_empty (&fdt_data->blobs);
+ }
+
+ rtems_libio_unlock ();
+
+ rtems_fdt_unlock ();
+ }
+
+ return true;
+}
+
+static rtems_fdt_data*
+rtems_fdt_lock (void)
+{
+ rtems_status_code sc;
+
+ if (!rtems_fdt_data_init ())
+ return NULL;
+
+ sc = rtems_semaphore_obtain (fdt_data->lock,
+ RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return fdt_data;
+}
+
+/**
+ * Create an index based on the contents of an FDT blob.
+ */
+static int
+rtems_fdt_init_index (rtems_fdt_handle* fdt, rtems_fdt_blob* blob)
+{
+ rtems_fdt_index_entry* entries = NULL;
+ int num_entries = 0;
+ int entry = 0;
+ size_t total_name_memory = 0;
+ char node_path[256];
+ int depth_path[32];
+ int start_offset = 0;
+ int start_depth = 0;
+ int offset = 0;
+ int depth = 0;
+ char* names = NULL;
+ char* names_pos = NULL;
+
+ /*
+ * Count the number of entries in the blob first.
+ */
+ memset(&node_path, 0, sizeof(node_path));
+ strcpy(node_path, "/");
+ depth_path[0] = strlen(node_path);
+
+ start_offset = fdt_path_offset(fdt->blob->blob, node_path);
+ if (start_offset < 0)
+ {
+ return start_offset;
+ }
+
+ start_offset = fdt_next_node(fdt->blob->blob, start_offset, &start_depth);
+ if (start_offset < 0)
+ {
+ return start_offset;
+ }
+
+ offset = start_offset;
+ depth = start_depth;
+
+ while (depth > 0)
+ {
+ /*
+ * Construct the node path name.
+ */
+ int namelen = 0;
+ const char* name = fdt_get_name(blob->blob, offset, &namelen);
+ strncpy(&node_path[depth_path[depth-1]],
+ name,
+ sizeof(node_path) - depth_path[depth-1] - 1);
+
+ total_name_memory += strlen(node_path) + 1;
+ num_entries++;
+
+ if (depth_path[depth-1] + namelen + 2 <= (int)sizeof(node_path))
+ {
+ strcpy(&node_path[depth_path[depth-1] + namelen], "/");
+ }
+
+ depth_path[depth] = depth_path[depth-1] + namelen + 1;
+
+ /*
+ * Get the next node.
+ */
+ offset = fdt_next_node(fdt->blob->blob, offset, &depth);
+ if (offset < 0)
+ {
+ return offset;
+ }
+ }
+
+ /*
+ * Create the index.
+ */
+ entries = calloc(num_entries, sizeof(rtems_fdt_index_entry));
+ if (!entries)
+ {
+ return -RTEMS_FDT_ERR_NO_MEMORY;
+ }
+
+ names = calloc(1, total_name_memory);
+ if (!entries)
+ {
+ free(entries);
+ return -RTEMS_FDT_ERR_NO_MEMORY;
+ }
+
+ /*
+ * Populate the index.
+ */
+ offset = start_offset;
+ depth = start_depth;
+ entry = 0;
+ names_pos = names;
+
+ while (depth > 0)
+ {
+ /*
+ * Record this node in an entry.
+ */
+ int namelen = 0;
+ const char* name = fdt_get_name(blob->blob, offset, &namelen);
+ strncpy(&node_path[depth_path[depth-1]],
+ name,
+ sizeof(node_path) - depth_path[depth-1] - 1);
+ strcpy(names_pos, node_path);
+
+ entries[entry].name = names_pos;
+ entries[entry].offset = offset;
+
+ names_pos += strlen(node_path) + 1;
+ entry++;
+
+ if (depth_path[depth-1] + namelen + 2 <= (int)sizeof(node_path))
+ {
+ strcpy(&node_path[depth_path[depth-1] + namelen], "/");
+ }
+
+ depth_path[depth] = depth_path[depth-1] + namelen + 1;
+
+ /*
+ * Get the next node.
+ */
+ offset = fdt_next_node(fdt->blob->blob, offset, &depth);
+ if (offset < 0)
+ {
+ free(entries);
+ free(names);
+ return offset;
+ }
+ }
+
+ fdt->blob->index.entries = entries;
+ fdt->blob->index.num_entries = num_entries;
+ fdt->blob->index.names = names;
+
+ return 0;
+}
+
+/**
+ * Release the contents of the index, freeing memory.
+ */
+static void
+rtems_fdt_release_index (rtems_fdt_index* index)
+{
+ if (index->entries)
+ {
+ free(index->entries);
+ free(index->names);
+
+ index->num_entries = 0;
+ index->entries = NULL;
+ index->names = NULL;
+ }
+}
+
+/**
+ * For a given FDT path, find the corresponding offset.
+ * Returns -1 if not found;
+ */
+static int
+rtems_fdt_index_find_by_name(rtems_fdt_index* index,
+ const char* name)
+{
+ int min = 0;
+ int max = index->num_entries;
+ char path[256];
+ const char* cmp_name = name;
+
+ /*
+ * Handle trailing slash case.
+ */
+ int namelen = strlen(name);
+ if (namelen > 0 && name[namelen-1] == '/')
+ {
+ namelen--;
+
+ if (namelen >= (int)sizeof(path) - 1)
+ {
+ namelen = sizeof(path) - 1;
+ }
+
+ strncpy(path, name, namelen);
+ path[namelen] = 0;
+ cmp_name = path;
+ }
+
+ /* Binary search for the name. */
+ while (min < max)
+ {
+ int middle = (min + max) / 2;
+ int cmp = strcmp(cmp_name, index->entries[middle].name);
+ if (cmp < 0)
+ {
+ /* Look lower than here. */
+ max = middle;
+ }
+ else if (cmp > 0)
+ {
+ /* Look higher than here. */
+ min = middle + 1;
+ }
+ else
+ {
+ /* Found it. */
+ return index->entries[middle].offset;
+ }
+ }
+
+ /* Didn't find it. */
+ return -FDT_ERR_NOTFOUND;
+}
+
+/**
+ * For a given FDT offset, find the corresponding path name.
+ */
+static const char *
+rtems_fdt_index_find_name_by_offset(rtems_fdt_index* index,
+ int offset)
+{
+ int min = 0;
+ int max = index->num_entries;
+
+ /*
+ * Binary search for the offset.
+ */
+ while (min < max)
+ {
+ int middle = (min + max) / 2;
+ if (offset < index->entries[middle].offset)
+ {
+ /* Look lower than here. */
+ max = middle;
+ }
+ else if (offset > index->entries[middle].offset)
+ {
+ /* Look higher than here. */
+ min = middle + 1;
+ }
+ else
+ {
+ /* Found it. */
+ return index->entries[middle].name;
+ }
+ }
+
+ /* Didn't find it. */
+ return NULL;
+}
+
+void
+rtems_fdt_init_handle (rtems_fdt_handle* handle)
+{
+ if (handle)
+ handle->blob = NULL;
+}
+
+void
+rtems_fdt_dup_handle (rtems_fdt_handle* from, rtems_fdt_handle* to)
+{
+ if (from && to)
+ {
+ (void) rtems_fdt_lock ();
+ to->blob = from->blob;
+ ++to->blob->refs;
+ rtems_fdt_unlock ();
+ }
+}
+
+void
+rtems_fdt_release_handle (rtems_fdt_handle* handle)
+{
+ if (handle && handle->blob)
+ {
+ rtems_fdt_data* fdt;
+ rtems_chain_node* node;
+
+ fdt = rtems_fdt_lock ();
+
+ node = rtems_chain_first (&fdt->blobs);
+
+ while (!rtems_chain_is_tail (&fdt->blobs, node))
+ {
+ rtems_fdt_blob* blob = (rtems_fdt_blob*) node;
+ if (handle->blob == blob)
+ {
+ if (blob->refs)
+ --blob->refs;
+ break;
+ }
+ node = rtems_chain_next (node);
+ }
+
+ rtems_fdt_unlock ();
+
+ handle->blob = NULL;
+ }
+}
+
+bool
+rtems_fdt_valid_handle (const rtems_fdt_handle* handle)
+{
+ if (handle && handle->blob)
+ {
+ rtems_fdt_data* fdt;
+ rtems_chain_node* node;
+
+ fdt = rtems_fdt_lock ();
+
+ node = rtems_chain_first (&fdt->blobs);
+
+ while (!rtems_chain_is_tail (&fdt->blobs, node))
+ {
+ rtems_fdt_blob* blob = (rtems_fdt_blob*) node;
+ if (handle->blob == blob)
+ {
+ rtems_fdt_unlock ();
+ return true;
+ }
+ node = rtems_chain_next (node);
+ }
+
+ rtems_fdt_unlock ();
+ }
+
+ return false;
+}
+
+int
+rtems_fdt_find_path_offset (rtems_fdt_handle* handle, const char* path)
+{
+ rtems_fdt_data* fdt;
+ rtems_chain_node* node;
+
+ rtems_fdt_release_handle (handle);
+
+ fdt = rtems_fdt_lock ();
+
+ node = rtems_chain_first (&fdt->blobs);
+
+ while (!rtems_chain_is_tail (&fdt->blobs, node))
+ {
+ rtems_fdt_handle temp_handle;
+ int offset;
+
+ temp_handle.blob = (rtems_fdt_blob*) node;
+
+ offset = rtems_fdt_path_offset (&temp_handle, path);
+
+ if (offset >= 0)
+ {
+ ++temp_handle.blob->refs;
+ handle->blob = temp_handle.blob;
+ rtems_fdt_unlock ();
+ return offset;
+ }
+
+ node = rtems_chain_next (node);
+ }
+
+ rtems_fdt_unlock ();
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+int
+rtems_fdt_load (const char* filename, rtems_fdt_handle* handle)
+{
+ rtems_fdt_data* fdt;
+ rtems_fdt_blob* blob;
+ size_t bsize;
+ int bf;
+ ssize_t r;
+ size_t name_len;
+ int fe;
+ struct stat sb;
+ uint8_t gzip_id[2];
+ uint8_t* cdata;
+ size_t size;
+
+ rtems_fdt_release_handle (handle);
+
+ if (stat (filename, &sb) < 0)
+ {
+ return -RTEMS_FDT_ERR_NOT_FOUND;
+ }
+
+ bf = open(filename, O_RDONLY);
+ if (bf < 0)
+ {
+ return -RTEMS_FDT_ERR_READ_FAIL;
+ }
+
+ r = read(bf, &gzip_id, sizeof(gzip_id));
+ if (r < 0)
+ {
+ close(bf);
+ return -RTEMS_FDT_ERR_READ_FAIL;
+ }
+
+ if ((gzip_id[0] == 0x1f) && (gzip_id[1] == 0x8b))
+ {
+ size_t offset;
+
+ cdata = malloc(sb.st_size);
+ if (!cdata)
+ {
+ close (bf);
+ return -RTEMS_FDT_ERR_NO_MEMORY;
+ }
+
+ if (lseek(bf, 0, SEEK_SET) < 0)
+ {
+ free(cdata);
+ close(bf);
+ return -RTEMS_FDT_ERR_READ_FAIL;
+ }
+
+ size = sb.st_size;
+ offset = 0;
+ while (size)
+ {
+ r = read(bf, cdata + offset, size);
+ if (r < 0)
+ {
+ free(cdata);
+ close(bf);
+ return -RTEMS_FDT_ERR_READ_FAIL;
+ }
+ size -= r;
+ offset += r;
+ }
+
+ offset = sb.st_size - 4;
+ bsize = ((cdata[offset + 3] << 24) | (cdata[offset + 2] << 16) |
+ (cdata[offset + 1] << 8) | cdata[offset + 0]);
+ }
+ else
+ {
+ cdata = NULL;
+ bsize = sb.st_size;
+ }
+
+ name_len = strlen (filename) + 1;
+
+ blob = malloc(sizeof (rtems_fdt_blob) + name_len + bsize);
+ if (!blob)
+ {
+ free(cdata);
+ close (bf);
+ return -RTEMS_FDT_ERR_NO_MEMORY;
+ }
+
+ blob->name = (const char*) (blob + 1);
+ blob->blob = blob->name + name_len + 1;
+
+ strcpy ((char*) blob->name, filename);
+
+ if ((gzip_id[0] == 0x1f) && (gzip_id[1] == 0x8b))
+ {
+ z_stream stream;
+ int err;
+ stream.next_in = (Bytef*) cdata;
+ stream.avail_in = (uInt) sb.st_size;
+ stream.next_out = (void*) (blob->name + name_len + 1);
+ stream.avail_out = (uInt) bsize;
+ stream.zalloc = (alloc_func) 0;
+ stream.zfree = (free_func) 0;
+ err = inflateInit(&stream);
+ if (err == Z_OK)
+ err = inflateReset2(&stream, 31);
+ if (err == Z_OK)
+ err = inflate(&stream, Z_FINISH);
+ if ((err == Z_OK) || (err == Z_STREAM_END))
+ err = inflateEnd(&stream);
+ if ((err != Z_OK) || (bsize != stream.total_out))
+ {
+ free (blob);
+ free(cdata);
+ close (bf);
+ return -RTEMS_FDT_ERR_READ_FAIL;
+ }
+ free(cdata);
+ cdata = NULL;
+ }
+ else
+ {
+ char* buf = (char*) blob->name + name_len + 1;
+ size = bsize;
+ while (size)
+ {
+ r = read (bf, buf, size);
+ if (r < 0)
+ {
+ free (blob);
+ close (bf);
+ return -RTEMS_FDT_ERR_READ_FAIL;
+ }
+ r -= size;
+ buf += r;
+ }
+ }
+
+ fe = fdt_check_header(blob->blob);
+ if (fe < 0)
+ {
+ free (blob);
+ close (bf);
+ return fe;
+ }
+
+ fdt = rtems_fdt_lock ();
+
+ rtems_chain_append_unprotected (&fdt->blobs, &blob->node);
+
+ blob->refs = 1;
+
+ rtems_fdt_unlock ();
+
+ handle->blob = blob;
+
+ fe = rtems_fdt_init_index(handle, blob);
+ if (fe < 0)
+ {
+ free (blob);
+ close (bf);
+ return fe;
+ }
+
+ return 0;
+}
+
+int
+rtems_fdt_register (const void* dtb, rtems_fdt_handle* handle)
+{
+ rtems_fdt_data* fdt;
+ rtems_fdt_blob* blob;
+ int fe;
+
+ rtems_fdt_release_handle (handle);
+
+ fe = fdt_check_header(dtb);
+ if (fe < 0)
+ {
+ return fe;
+ }
+
+ blob = malloc(sizeof (rtems_fdt_blob));
+ if (!blob)
+ {
+ return -RTEMS_FDT_ERR_NO_MEMORY;
+ }
+
+ blob->blob = dtb;
+ blob->name = NULL;
+ fdt = rtems_fdt_lock ();
+
+ rtems_chain_append_unprotected (&fdt->blobs, &blob->node);
+
+ blob->refs = 1;
+
+ rtems_fdt_unlock ();
+
+ handle->blob = blob;
+
+ fe = rtems_fdt_init_index(handle, blob);
+ if (fe < 0)
+ {
+ free(blob);
+ return -RTEMS_FDT_ERR_NO_MEMORY;
+ }
+
+ return 0;
+}
+
+int
+rtems_fdt_unload (rtems_fdt_handle* handle)
+{
+ (void) rtems_fdt_lock ();
+
+ if (!rtems_fdt_valid_handle (handle))
+ {
+ rtems_fdt_unlock ();
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ }
+
+ if (handle->blob->refs > 1)
+ {
+ rtems_fdt_unlock ();
+ return -RTEMS_FDT_ERR_REFERENCED;
+ }
+
+ rtems_chain_extract_unprotected (&handle->blob->node);
+
+ free (handle->blob);
+
+ handle->blob = NULL;
+
+ rtems_fdt_unlock ();
+
+ rtems_fdt_release_index(&handle->blob->index);
+
+ return 0;
+}
+
+int
+rtems_fdt_num_mem_rsv (rtems_fdt_handle* handle)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_num_mem_rsv (handle->blob->blob);
+}
+
+int
+rtems_fdt_get_mem_rsv (rtems_fdt_handle* handle,
+ int n,
+ uint64_t* address,
+ uint64_t* size)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_get_mem_rsv (handle->blob->blob, n, address, size);
+}
+
+int
+rtems_fdt_subnode_offset_namelen (rtems_fdt_handle* handle,
+ int parentoffset,
+ const char* name,
+ int namelen)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_subnode_offset_namelen (handle->blob->blob,
+ parentoffset,
+ name,
+ namelen);
+}
+
+int
+rtems_fdt_subnode_offset (rtems_fdt_handle* handle,
+ int parentoffset,
+ const char* name)
+{
+ char full_name[256];
+ const char *path;
+
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+
+ path = rtems_fdt_index_find_name_by_offset(&handle->blob->index, parentoffset);
+ snprintf(full_name, sizeof(full_name), "%s/%s", path, name);
+
+ return rtems_fdt_index_find_by_name(&handle->blob->index, full_name);
+}
+
+int
+rtems_fdt_path_offset (rtems_fdt_handle* handle, const char* path)
+{
+ return rtems_fdt_index_find_by_name(&handle->blob->index, path);
+}
+
+const char*
+rtems_fdt_get_name (rtems_fdt_handle* handle, int nodeoffset, int* length)
+{
+ if (!handle->blob)
+ return NULL;
+
+ const char *name = rtems_fdt_index_find_name_by_offset(&handle->blob->index, nodeoffset);
+ if (name && length)
+ {
+ *length = strlen(name);
+ }
+
+ return name;
+}
+
+const void*
+rtems_fdt_getprop_namelen (rtems_fdt_handle* handle,
+ int nodeoffset,
+ const char* name,
+ int namelen,
+ int* length)
+{
+ if (!handle->blob)
+ return NULL;
+ return fdt_getprop_namelen (handle->blob->blob,
+ nodeoffset,
+ name,
+ namelen,
+ length);
+}
+
+const void*
+rtems_fdt_getprop (rtems_fdt_handle* handle,
+ int nodeoffset,
+ const char* name,
+ int* length)
+{
+ if (!handle->blob)
+ return NULL;
+ return fdt_getprop (handle->blob->blob,
+ nodeoffset,
+ name,
+ length);
+}
+
+uint32_t
+rtems_fdt_get_phandle (rtems_fdt_handle* handle, int nodeoffset)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_get_phandle (handle->blob->blob, nodeoffset);
+}
+
+const char*
+rtems_fdt_get_alias_namelen (rtems_fdt_handle* handle,
+ const char* name,
+ int namelen)
+{
+ if (!handle->blob)
+ return NULL;
+ return fdt_get_alias_namelen (handle->blob->blob, name, namelen);
+}
+
+const char*
+rtems_fdt_get_alias (rtems_fdt_handle* handle, const char* name)
+{
+ if (!handle->blob)
+ return NULL;
+ return fdt_get_alias (handle->blob->blob, name);
+}
+
+int
+rtems_fdt_get_path (rtems_fdt_handle* handle,
+ int nodeoffset,
+ char* buf,
+ int buflen)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_get_path (handle->blob->blob, nodeoffset, buf, buflen);
+}
+
+int
+rtems_fdt_supernode_atdepth_offset (rtems_fdt_handle* handle,
+ int nodeoffset,
+ int supernodedepth,
+ int* nodedepth)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_supernode_atdepth_offset(handle,
+ nodeoffset,
+ supernodedepth,
+ nodedepth);
+}
+
+int
+rtems_fdt_node_depth (rtems_fdt_handle* handle, int nodeoffset)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_node_depth (handle->blob->blob, nodeoffset);
+}
+
+int
+rtems_fdt_parent_offset (rtems_fdt_handle* handle, int nodeoffset)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_parent_offset (handle->blob->blob, nodeoffset);
+}
+
+int
+rtems_fdt_node_offset_by_prop_value (rtems_fdt_handle* handle,
+ int startoffset,
+ const char* propname,
+ const void* propval,
+ int proplen)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_node_offset_by_prop_value (handle,
+ startoffset,
+ propname,
+ propval,
+ proplen);
+}
+
+int
+rtems_fdt_node_offset_by_phandle (rtems_fdt_handle* handle, uint32_t phandle)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_node_offset_by_phandle (handle->blob->blob, phandle);
+}
+
+int
+rtems_fdt_node_check_compatible (rtems_fdt_handle* handle,
+ int nodeoffset,
+ const char* compatible)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_node_check_compatible (handle, nodeoffset, compatible);
+}
+
+int
+rtems_fdt_node_offset_by_compatible (rtems_fdt_handle* handle,
+ int startoffset,
+ const char* compatible)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_node_offset_by_compatible (handle->blob->blob,
+ startoffset,
+ compatible);
+}
+
+int
+rtems_fdt_next_node (rtems_fdt_handle* handle, int offset, int* depth)
+{
+ if (!handle->blob)
+ return -RTEMS_FDT_ERR_INVALID_HANDLE;
+ return fdt_next_node (handle->blob->blob, offset, depth);
+}
+
+const char*
+rtems_fdt_strerror (int errval)
+{
+ const char* errors[] = {
+ "invalid handle",
+ "no memory",
+ "file not found",
+ "DTB read fail",
+ "blob has references"
+ };
+ if (errval > -RTEMS_FDT_ERR_RTEMS_MIN)
+ return fdt_strerror (errval);
+ if (errval < -RTEMS_FDT_ERR_MAX)
+ return "invalid error code";
+ return errors[(-errval) - RTEMS_FDT_ERR_RTEMS_MIN];
+}
+
+int
+rtems_fdt_prop_value(const char* const path,
+ const char* const propname,
+ void* value,
+ size_t* size)
+{
+ rtems_fdt_handle fdt;
+ int node;
+ const void* prop;
+ int length;
+
+ rtems_fdt_init_handle (&fdt);
+
+ node = rtems_fdt_find_path_offset (&fdt, path);
+ if (node < 0)
+ return node;
+
+ prop = rtems_fdt_getprop(&fdt, node, propname, &length);
+ if (length < 0)
+ {
+ rtems_fdt_release_handle (&fdt);
+ return length;
+ }
+
+ if (length > (int) *size)
+ {
+ rtems_fdt_release_handle (&fdt);
+ return RTEMS_FDT_ERR_BADPATH;
+ }
+
+ *size = length;
+
+ memcpy (value, prop, length);
+
+ return 0;
+}
+
+int
+rtems_fdt_prop_map(const char* const path,
+ const char* const propname,
+ const char* const names[],
+ uint32_t* values,
+ size_t count)
+{
+ rtems_fdt_handle fdt;
+ int node;
+ size_t item;
+
+ rtems_fdt_init_handle (&fdt);
+
+ node = rtems_fdt_find_path_offset (&fdt, path);
+ if (node < 0)
+ return node;
+
+ for (item = 0; item < count; item++)
+ {
+ const void* prop;
+ const uint8_t* p;
+ int length;
+ int subnode;
+
+ subnode = rtems_fdt_subnode_offset (&fdt, node, names[item]);
+ if (subnode < 0)
+ {
+ rtems_fdt_release_handle (&fdt);
+ return subnode;
+ }
+
+ prop = rtems_fdt_getprop(&fdt, subnode, propname, &length);
+ if (length < 0)
+ {
+ rtems_fdt_release_handle (&fdt);
+ return length;
+ }
+
+ if (length != sizeof (uint32_t))
+ {
+ rtems_fdt_release_handle (&fdt);
+ return RTEMS_FDT_ERR_BADPATH;
+ }
+
+ p = prop;
+
+ values[item] = ((((uint32_t) p[0]) << 24) |
+ (((uint32_t) p[1]) << 16) |
+ (((uint32_t) p[2]) << 8) |
+ (uint32_t) p[3]);
+ }
+
+ return 0;
+}
+
+uint32_t
+rtems_fdt_get_uint32 (const void* prop)
+{
+ const uint8_t* p = prop;
+ uint32_t value;
+ value = ((((uint32_t) p[0]) << 24) |
+ (((uint32_t) p[1]) << 16) |
+ (((uint32_t) p[2]) << 8) |
+ (uint32_t) p[3]);
+ return value;
+}
+
+int
+rtems_fdt_get_value (const char* path,
+ const char* property,
+ size_t size,
+ uint32_t* value)
+{
+ rtems_fdt_handle fdt;
+ const void* prop;
+ int node;
+ int length;
+
+ rtems_fdt_init_handle (&fdt);
+
+ node = rtems_fdt_find_path_offset (&fdt, path);
+ if (node < 0)
+ {
+ rtems_fdt_release_handle (&fdt);
+ return node;
+ }
+
+ prop = rtems_fdt_getprop(&fdt, node, property, &length);
+ if (length < 0)
+ {
+ rtems_fdt_release_handle (&fdt);
+ return length;
+ }
+
+ if (length == sizeof (uint32_t))
+ *value = rtems_fdt_get_uint32 (prop);
+ else
+ *value = 0;
+
+ rtems_fdt_release_handle (&fdt);
+
+ return 0;
+}
+
+/**
+ * Get the number of entries in an FDT handle.
+ */
+int
+rtems_fdt_num_entries(rtems_fdt_handle* handle)
+{
+ return handle->blob->index.num_entries;
+}
+
+/**
+ * Get the numbered entry name. Note that the id isn't the same as
+ * the offset - it's numbered 0, 1, 2 ... num_entries-1
+ */
+const char *
+rtems_fdt_entry_name(rtems_fdt_handle* handle, int id)
+{
+ return handle->blob->index.entries[id].name;
+}
+
+/**
+ * Get the numbered entry offset. Note that the id isn't the same as
+ * the offset - it's numbered 0, 1, 2 ... num_entries-1
+ */
+int
+rtems_fdt_entry_offset(rtems_fdt_handle* handle, int id)
+{
+ return handle->blob->index.entries[id].offset;
+}