1204431Sraj/* 2204431Sraj * libfdt - Flat Device Tree manipulation 3204431Sraj * Copyright (C) 2006 David Gibson, IBM Corporation. 4204431Sraj * 5204431Sraj * libfdt is dual licensed: you can use it either under the terms of 6204431Sraj * the GPL, or the BSD license, at your option. 7204431Sraj * 8204431Sraj * a) This library is free software; you can redistribute it and/or 9204431Sraj * modify it under the terms of the GNU General Public License as 10204431Sraj * published by the Free Software Foundation; either version 2 of the 11204431Sraj * License, or (at your option) any later version. 12204431Sraj * 13204431Sraj * This library is distributed in the hope that it will be useful, 14204431Sraj * but WITHOUT ANY WARRANTY; without even the implied warranty of 15204431Sraj * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16204431Sraj * GNU General Public License for more details. 17204431Sraj * 18204431Sraj * You should have received a copy of the GNU General Public 19204431Sraj * License along with this library; if not, write to the Free 20204431Sraj * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21204431Sraj * MA 02110-1301 USA 22204431Sraj * 23204431Sraj * Alternatively, 24204431Sraj * 25204431Sraj * b) Redistribution and use in source and binary forms, with or 26204431Sraj * without modification, are permitted provided that the following 27204431Sraj * conditions are met: 28204431Sraj * 29204431Sraj * 1. Redistributions of source code must retain the above 30204431Sraj * copyright notice, this list of conditions and the following 31204431Sraj * disclaimer. 32204431Sraj * 2. Redistributions in binary form must reproduce the above 33204431Sraj * copyright notice, this list of conditions and the following 34204431Sraj * disclaimer in the documentation and/or other materials 35204431Sraj * provided with the distribution. 36204431Sraj * 37204431Sraj * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38204431Sraj * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39204431Sraj * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40204431Sraj * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41204431Sraj * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42204431Sraj * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43204431Sraj * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44204431Sraj * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45204431Sraj * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46204431Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47204431Sraj * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48204431Sraj * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49204431Sraj * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50204431Sraj */ 51204431Sraj#include "libfdt_env.h" 52204431Sraj 53204431Sraj#include <fdt.h> 54204431Sraj#include <libfdt.h> 55204431Sraj 56204431Sraj#include "libfdt_internal.h" 57204431Sraj 58328459Skevansstatic int fdt_nodename_eq_(const void *fdt, int offset, 59204431Sraj const char *s, int len) 60204431Sraj{ 61328824Skevans int olen; 62328824Skevans const char *p = fdt_get_name(fdt, offset, &olen); 63204431Sraj 64328824Skevans if (!p || olen < len) 65204431Sraj /* short match */ 66204431Sraj return 0; 67204431Sraj 68204431Sraj if (memcmp(p, s, len) != 0) 69204431Sraj return 0; 70204431Sraj 71204431Sraj if (p[len] == '\0') 72204431Sraj return 1; 73204431Sraj else if (!memchr(s, '@', len) && (p[len] == '@')) 74204431Sraj return 1; 75204431Sraj else 76204431Sraj return 0; 77204431Sraj} 78204431Sraj 79204431Srajconst char *fdt_string(const void *fdt, int stroffset) 80204431Sraj{ 81204431Sraj return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 82204431Sraj} 83204431Sraj 84328459Skevansstatic int fdt_string_eq_(const void *fdt, int stroffset, 85204433Sraj const char *s, int len) 86204433Sraj{ 87204433Sraj const char *p = fdt_string(fdt, stroffset); 88204433Sraj 89204433Sraj return (strlen(p) == len) && (memcmp(p, s, len) == 0); 90204433Sraj} 91204433Sraj 92328459Skevansuint32_t fdt_get_max_phandle(const void *fdt) 93328459Skevans{ 94328459Skevans uint32_t max_phandle = 0; 95328459Skevans int offset; 96328459Skevans 97328459Skevans for (offset = fdt_next_node(fdt, -1, NULL);; 98328459Skevans offset = fdt_next_node(fdt, offset, NULL)) { 99328459Skevans uint32_t phandle; 100328459Skevans 101328459Skevans if (offset == -FDT_ERR_NOTFOUND) 102328459Skevans return max_phandle; 103328459Skevans 104328459Skevans if (offset < 0) 105328459Skevans return (uint32_t)-1; 106328459Skevans 107328459Skevans phandle = fdt_get_phandle(fdt, offset); 108328459Skevans if (phandle == (uint32_t)-1) 109328459Skevans continue; 110328459Skevans 111328459Skevans if (phandle > max_phandle) 112328459Skevans max_phandle = phandle; 113328459Skevans } 114328459Skevans 115328459Skevans return 0; 116328459Skevans} 117328459Skevans 118204431Srajint fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 119204431Sraj{ 120204431Sraj FDT_CHECK_HEADER(fdt); 121328459Skevans *address = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->address); 122328459Skevans *size = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->size); 123204431Sraj return 0; 124204431Sraj} 125204431Sraj 126204431Srajint fdt_num_mem_rsv(const void *fdt) 127204431Sraj{ 128204431Sraj int i = 0; 129204431Sraj 130328459Skevans while (fdt64_to_cpu(fdt_mem_rsv_(fdt, i)->size) != 0) 131204431Sraj i++; 132204431Sraj return i; 133204431Sraj} 134204431Sraj 135328459Skevansstatic int nextprop_(const void *fdt, int offset) 136238742Simp{ 137238742Simp uint32_t tag; 138238742Simp int nextoffset; 139238742Simp 140238742Simp do { 141238742Simp tag = fdt_next_tag(fdt, offset, &nextoffset); 142238742Simp 143238742Simp switch (tag) { 144238742Simp case FDT_END: 145238742Simp if (nextoffset >= 0) 146238742Simp return -FDT_ERR_BADSTRUCTURE; 147238742Simp else 148238742Simp return nextoffset; 149238742Simp 150238742Simp case FDT_PROP: 151238742Simp return offset; 152238742Simp } 153238742Simp offset = nextoffset; 154238742Simp } while (tag == FDT_NOP); 155238742Simp 156238742Simp return -FDT_ERR_NOTFOUND; 157238742Simp} 158238742Simp 159204431Srajint fdt_subnode_offset_namelen(const void *fdt, int offset, 160204431Sraj const char *name, int namelen) 161204431Sraj{ 162204431Sraj int depth; 163204431Sraj 164204431Sraj FDT_CHECK_HEADER(fdt); 165204431Sraj 166204431Sraj for (depth = 0; 167204433Sraj (offset >= 0) && (depth >= 0); 168204433Sraj offset = fdt_next_node(fdt, offset, &depth)) 169204433Sraj if ((depth == 1) 170328459Skevans && fdt_nodename_eq_(fdt, offset, name, namelen)) 171204431Sraj return offset; 172204431Sraj 173204433Sraj if (depth < 0) 174204433Sraj return -FDT_ERR_NOTFOUND; 175204431Sraj return offset; /* error */ 176204431Sraj} 177204431Sraj 178204431Srajint fdt_subnode_offset(const void *fdt, int parentoffset, 179204431Sraj const char *name) 180204431Sraj{ 181204431Sraj return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 182204431Sraj} 183204431Sraj 184328459Skevansint fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) 185204431Sraj{ 186328459Skevans const char *end = path + namelen; 187204431Sraj const char *p = path; 188204431Sraj int offset = 0; 189204431Sraj 190204431Sraj FDT_CHECK_HEADER(fdt); 191204431Sraj 192204433Sraj /* see if we have an alias */ 193204433Sraj if (*path != '/') { 194328459Skevans const char *q = memchr(path, '/', end - p); 195204431Sraj 196204433Sraj if (!q) 197204433Sraj q = end; 198204433Sraj 199204433Sraj p = fdt_get_alias_namelen(fdt, p, q - p); 200204433Sraj if (!p) 201204433Sraj return -FDT_ERR_BADPATH; 202204433Sraj offset = fdt_path_offset(fdt, p); 203204433Sraj 204204433Sraj p = q; 205204433Sraj } 206204433Sraj 207328459Skevans while (p < end) { 208204431Sraj const char *q; 209204431Sraj 210328459Skevans while (*p == '/') { 211204431Sraj p++; 212328459Skevans if (p == end) 213328459Skevans return offset; 214328459Skevans } 215328459Skevans q = memchr(p, '/', end - p); 216204431Sraj if (! q) 217204431Sraj q = end; 218204431Sraj 219204431Sraj offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 220204431Sraj if (offset < 0) 221204431Sraj return offset; 222204431Sraj 223204431Sraj p = q; 224204431Sraj } 225204431Sraj 226204431Sraj return offset; 227204431Sraj} 228204431Sraj 229328459Skevansint fdt_path_offset(const void *fdt, const char *path) 230328459Skevans{ 231328459Skevans return fdt_path_offset_namelen(fdt, path, strlen(path)); 232328459Skevans} 233328459Skevans 234204431Srajconst char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 235204431Sraj{ 236328459Skevans const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); 237328824Skevans const char *nameptr; 238204431Sraj int err; 239204431Sraj 240204431Sraj if (((err = fdt_check_header(fdt)) != 0) 241328459Skevans || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) 242204431Sraj goto fail; 243204431Sraj 244328824Skevans nameptr = nh->name; 245328824Skevans 246328824Skevans if (fdt_version(fdt) < 0x10) { 247328824Skevans /* 248328824Skevans * For old FDT versions, match the naming conventions of V16: 249328824Skevans * give only the leaf name (after all /). The actual tree 250328824Skevans * contents are loosely checked. 251328824Skevans */ 252328824Skevans const char *leaf; 253328824Skevans leaf = strrchr(nameptr, '/'); 254328824Skevans if (leaf == NULL) { 255328824Skevans err = -FDT_ERR_BADSTRUCTURE; 256328824Skevans goto fail; 257328824Skevans } 258328824Skevans nameptr = leaf+1; 259328824Skevans } 260328824Skevans 261204431Sraj if (len) 262328824Skevans *len = strlen(nameptr); 263204431Sraj 264328824Skevans return nameptr; 265204431Sraj 266204431Sraj fail: 267204431Sraj if (len) 268204431Sraj *len = err; 269204431Sraj return NULL; 270204431Sraj} 271204431Sraj 272238742Simpint fdt_first_property_offset(const void *fdt, int nodeoffset) 273204431Sraj{ 274238742Simp int offset; 275238742Simp 276328459Skevans if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) 277238742Simp return offset; 278238742Simp 279328459Skevans return nextprop_(fdt, offset); 280238742Simp} 281238742Simp 282238742Simpint fdt_next_property_offset(const void *fdt, int offset) 283238742Simp{ 284328459Skevans if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) 285238742Simp return offset; 286238742Simp 287328459Skevans return nextprop_(fdt, offset); 288238742Simp} 289238742Simp 290328824Skevansstatic const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, 291328824Skevans int offset, 292328824Skevans int *lenp) 293238742Simp{ 294238742Simp int err; 295204431Sraj const struct fdt_property *prop; 296204431Sraj 297328459Skevans if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) { 298238742Simp if (lenp) 299238742Simp *lenp = err; 300238742Simp return NULL; 301238742Simp } 302204431Sraj 303328459Skevans prop = fdt_offset_ptr_(fdt, offset); 304204431Sraj 305238742Simp if (lenp) 306238742Simp *lenp = fdt32_to_cpu(prop->len); 307204431Sraj 308238742Simp return prop; 309238742Simp} 310204431Sraj 311328824Skevansconst struct fdt_property *fdt_get_property_by_offset(const void *fdt, 312328824Skevans int offset, 313328824Skevans int *lenp) 314238742Simp{ 315328824Skevans /* Prior to version 16, properties may need realignment 316328824Skevans * and this API does not work. fdt_getprop_*() will, however. */ 317328824Skevans 318328824Skevans if (fdt_version(fdt) < 0x10) { 319328824Skevans if (lenp) 320328824Skevans *lenp = -FDT_ERR_BADVERSION; 321328824Skevans return NULL; 322328824Skevans } 323328824Skevans 324328824Skevans return fdt_get_property_by_offset_(fdt, offset, lenp); 325328824Skevans} 326328824Skevans 327328824Skevansstatic const struct fdt_property *fdt_get_property_namelen_(const void *fdt, 328328824Skevans int offset, 329328824Skevans const char *name, 330328824Skevans int namelen, 331328824Skevans int *lenp, 332328824Skevans int *poffset) 333328824Skevans{ 334238742Simp for (offset = fdt_first_property_offset(fdt, offset); 335238742Simp (offset >= 0); 336238742Simp (offset = fdt_next_property_offset(fdt, offset))) { 337238742Simp const struct fdt_property *prop; 338238742Simp 339328824Skevans if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) { 340238742Simp offset = -FDT_ERR_INTERNAL; 341204431Sraj break; 342204431Sraj } 343328459Skevans if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff), 344328824Skevans name, namelen)) { 345328824Skevans if (poffset) 346328824Skevans *poffset = offset; 347238742Simp return prop; 348328824Skevans } 349238742Simp } 350204431Sraj 351204431Sraj if (lenp) 352238742Simp *lenp = offset; 353204431Sraj return NULL; 354204431Sraj} 355204431Sraj 356328824Skevans 357328824Skevansconst struct fdt_property *fdt_get_property_namelen(const void *fdt, 358328824Skevans int offset, 359328824Skevans const char *name, 360328824Skevans int namelen, int *lenp) 361328824Skevans{ 362328824Skevans /* Prior to version 16, properties may need realignment 363328824Skevans * and this API does not work. fdt_getprop_*() will, however. */ 364328824Skevans if (fdt_version(fdt) < 0x10) { 365328824Skevans if (lenp) 366328824Skevans *lenp = -FDT_ERR_BADVERSION; 367328824Skevans return NULL; 368328824Skevans } 369328824Skevans 370328824Skevans return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, 371328824Skevans NULL); 372328824Skevans} 373328824Skevans 374328824Skevans 375204433Srajconst struct fdt_property *fdt_get_property(const void *fdt, 376204433Sraj int nodeoffset, 377204433Sraj const char *name, int *lenp) 378204431Sraj{ 379204433Sraj return fdt_get_property_namelen(fdt, nodeoffset, name, 380204433Sraj strlen(name), lenp); 381204433Sraj} 382204433Sraj 383204433Srajconst void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 384204433Sraj const char *name, int namelen, int *lenp) 385204433Sraj{ 386328824Skevans int poffset; 387204431Sraj const struct fdt_property *prop; 388204431Sraj 389328824Skevans prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, 390328824Skevans &poffset); 391328459Skevans if (!prop) 392204431Sraj return NULL; 393204431Sraj 394328824Skevans /* Handle realignment */ 395328824Skevans if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && 396328824Skevans fdt32_to_cpu(prop->len) >= 8) 397328824Skevans return prop->data + 4; 398204431Sraj return prop->data; 399204431Sraj} 400204431Sraj 401238742Simpconst void *fdt_getprop_by_offset(const void *fdt, int offset, 402238742Simp const char **namep, int *lenp) 403238742Simp{ 404238742Simp const struct fdt_property *prop; 405238742Simp 406328824Skevans prop = fdt_get_property_by_offset_(fdt, offset, lenp); 407238742Simp if (!prop) 408238742Simp return NULL; 409238742Simp if (namep) 410238742Simp *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 411328824Skevans 412328824Skevans /* Handle realignment */ 413328824Skevans if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && 414328824Skevans fdt32_to_cpu(prop->len) >= 8) 415328824Skevans return prop->data + 4; 416238742Simp return prop->data; 417238742Simp} 418238742Simp 419204433Srajconst void *fdt_getprop(const void *fdt, int nodeoffset, 420204433Sraj const char *name, int *lenp) 421204433Sraj{ 422204433Sraj return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 423204433Sraj} 424204433Sraj 425204431Srajuint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 426204431Sraj{ 427328459Skevans const fdt32_t *php; 428204431Sraj int len; 429204431Sraj 430204433Sraj /* FIXME: This is a bit sub-optimal, since we potentially scan 431204433Sraj * over all the properties twice. */ 432204433Sraj php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 433204433Sraj if (!php || (len != sizeof(*php))) { 434204433Sraj php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 435204433Sraj if (!php || (len != sizeof(*php))) 436204433Sraj return 0; 437204433Sraj } 438204431Sraj 439204431Sraj return fdt32_to_cpu(*php); 440204431Sraj} 441204431Sraj 442204433Srajconst char *fdt_get_alias_namelen(const void *fdt, 443204433Sraj const char *name, int namelen) 444204433Sraj{ 445204433Sraj int aliasoffset; 446204433Sraj 447204433Sraj aliasoffset = fdt_path_offset(fdt, "/aliases"); 448204433Sraj if (aliasoffset < 0) 449204433Sraj return NULL; 450204433Sraj 451204433Sraj return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 452204433Sraj} 453204433Sraj 454204433Srajconst char *fdt_get_alias(const void *fdt, const char *name) 455204433Sraj{ 456204433Sraj return fdt_get_alias_namelen(fdt, name, strlen(name)); 457204433Sraj} 458204433Sraj 459204431Srajint fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 460204431Sraj{ 461204431Sraj int pdepth = 0, p = 0; 462204431Sraj int offset, depth, namelen; 463204431Sraj const char *name; 464204431Sraj 465204431Sraj FDT_CHECK_HEADER(fdt); 466204431Sraj 467204431Sraj if (buflen < 2) 468204431Sraj return -FDT_ERR_NOSPACE; 469204431Sraj 470204431Sraj for (offset = 0, depth = 0; 471204431Sraj (offset >= 0) && (offset <= nodeoffset); 472204431Sraj offset = fdt_next_node(fdt, offset, &depth)) { 473204431Sraj while (pdepth > depth) { 474204431Sraj do { 475204431Sraj p--; 476204431Sraj } while (buf[p-1] != '/'); 477204431Sraj pdepth--; 478204431Sraj } 479204431Sraj 480204433Sraj if (pdepth >= depth) { 481204433Sraj name = fdt_get_name(fdt, offset, &namelen); 482204433Sraj if (!name) 483204433Sraj return namelen; 484204433Sraj if ((p + namelen + 1) <= buflen) { 485204433Sraj memcpy(buf + p, name, namelen); 486204433Sraj p += namelen; 487204433Sraj buf[p++] = '/'; 488204433Sraj pdepth++; 489204433Sraj } 490204431Sraj } 491204431Sraj 492204431Sraj if (offset == nodeoffset) { 493204431Sraj if (pdepth < (depth + 1)) 494204431Sraj return -FDT_ERR_NOSPACE; 495204431Sraj 496204431Sraj if (p > 1) /* special case so that root path is "/", not "" */ 497204431Sraj p--; 498204431Sraj buf[p] = '\0'; 499204433Sraj return 0; 500204431Sraj } 501204431Sraj } 502204431Sraj 503204431Sraj if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 504204431Sraj return -FDT_ERR_BADOFFSET; 505204431Sraj else if (offset == -FDT_ERR_BADOFFSET) 506204431Sraj return -FDT_ERR_BADSTRUCTURE; 507204431Sraj 508204431Sraj return offset; /* error from fdt_next_node() */ 509204431Sraj} 510204431Sraj 511204431Srajint fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 512204431Sraj int supernodedepth, int *nodedepth) 513204431Sraj{ 514204431Sraj int offset, depth; 515204431Sraj int supernodeoffset = -FDT_ERR_INTERNAL; 516204431Sraj 517204431Sraj FDT_CHECK_HEADER(fdt); 518204431Sraj 519204431Sraj if (supernodedepth < 0) 520204431Sraj return -FDT_ERR_NOTFOUND; 521204431Sraj 522204431Sraj for (offset = 0, depth = 0; 523204431Sraj (offset >= 0) && (offset <= nodeoffset); 524204431Sraj offset = fdt_next_node(fdt, offset, &depth)) { 525204431Sraj if (depth == supernodedepth) 526204431Sraj supernodeoffset = offset; 527204431Sraj 528204431Sraj if (offset == nodeoffset) { 529204431Sraj if (nodedepth) 530204431Sraj *nodedepth = depth; 531204431Sraj 532204431Sraj if (supernodedepth > depth) 533204431Sraj return -FDT_ERR_NOTFOUND; 534204431Sraj else 535204431Sraj return supernodeoffset; 536204431Sraj } 537204431Sraj } 538204431Sraj 539204431Sraj if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 540204431Sraj return -FDT_ERR_BADOFFSET; 541204431Sraj else if (offset == -FDT_ERR_BADOFFSET) 542204431Sraj return -FDT_ERR_BADSTRUCTURE; 543204431Sraj 544204431Sraj return offset; /* error from fdt_next_node() */ 545204431Sraj} 546204431Sraj 547204431Srajint fdt_node_depth(const void *fdt, int nodeoffset) 548204431Sraj{ 549204431Sraj int nodedepth; 550204431Sraj int err; 551204431Sraj 552204431Sraj err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 553204431Sraj if (err) 554204431Sraj return (err < 0) ? err : -FDT_ERR_INTERNAL; 555204431Sraj return nodedepth; 556204431Sraj} 557204431Sraj 558204431Srajint fdt_parent_offset(const void *fdt, int nodeoffset) 559204431Sraj{ 560204431Sraj int nodedepth = fdt_node_depth(fdt, nodeoffset); 561204431Sraj 562204431Sraj if (nodedepth < 0) 563204431Sraj return nodedepth; 564204431Sraj return fdt_supernode_atdepth_offset(fdt, nodeoffset, 565204431Sraj nodedepth - 1, NULL); 566204431Sraj} 567204431Sraj 568204431Srajint fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 569204431Sraj const char *propname, 570204431Sraj const void *propval, int proplen) 571204431Sraj{ 572204431Sraj int offset; 573204431Sraj const void *val; 574204431Sraj int len; 575204431Sraj 576204431Sraj FDT_CHECK_HEADER(fdt); 577204431Sraj 578204431Sraj /* FIXME: The algorithm here is pretty horrible: we scan each 579204431Sraj * property of a node in fdt_getprop(), then if that didn't 580204431Sraj * find what we want, we scan over them again making our way 581204431Sraj * to the next node. Still it's the easiest to implement 582204431Sraj * approach; performance can come later. */ 583204431Sraj for (offset = fdt_next_node(fdt, startoffset, NULL); 584204431Sraj offset >= 0; 585204431Sraj offset = fdt_next_node(fdt, offset, NULL)) { 586204431Sraj val = fdt_getprop(fdt, offset, propname, &len); 587204431Sraj if (val && (len == proplen) 588204431Sraj && (memcmp(val, propval, len) == 0)) 589204431Sraj return offset; 590204431Sraj } 591204431Sraj 592204431Sraj return offset; /* error from fdt_next_node() */ 593204431Sraj} 594204431Sraj 595204431Srajint fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 596204431Sraj{ 597204433Sraj int offset; 598204433Sraj 599204431Sraj if ((phandle == 0) || (phandle == -1)) 600204431Sraj return -FDT_ERR_BADPHANDLE; 601204433Sraj 602204433Sraj FDT_CHECK_HEADER(fdt); 603204433Sraj 604204433Sraj /* FIXME: The algorithm here is pretty horrible: we 605204433Sraj * potentially scan each property of a node in 606204433Sraj * fdt_get_phandle(), then if that didn't find what 607204433Sraj * we want, we scan over them again making our way to the next 608204433Sraj * node. Still it's the easiest to implement approach; 609204433Sraj * performance can come later. */ 610204433Sraj for (offset = fdt_next_node(fdt, -1, NULL); 611204433Sraj offset >= 0; 612204433Sraj offset = fdt_next_node(fdt, offset, NULL)) { 613204433Sraj if (fdt_get_phandle(fdt, offset) == phandle) 614204433Sraj return offset; 615204433Sraj } 616204433Sraj 617204433Sraj return offset; /* error from fdt_next_node() */ 618204431Sraj} 619204431Sraj 620328459Skevansint fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 621204431Sraj{ 622204431Sraj int len = strlen(str); 623204431Sraj const char *p; 624204431Sraj 625204431Sraj while (listlen >= len) { 626204431Sraj if (memcmp(str, strlist, len+1) == 0) 627204431Sraj return 1; 628204431Sraj p = memchr(strlist, '\0', listlen); 629204431Sraj if (!p) 630204431Sraj return 0; /* malformed strlist.. */ 631204431Sraj listlen -= (p-strlist) + 1; 632204431Sraj strlist = p + 1; 633204431Sraj } 634204431Sraj return 0; 635204431Sraj} 636204431Sraj 637328459Skevansint fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) 638328459Skevans{ 639328459Skevans const char *list, *end; 640328459Skevans int length, count = 0; 641328459Skevans 642328459Skevans list = fdt_getprop(fdt, nodeoffset, property, &length); 643328459Skevans if (!list) 644328459Skevans return length; 645328459Skevans 646328459Skevans end = list + length; 647328459Skevans 648328459Skevans while (list < end) { 649328459Skevans length = strnlen(list, end - list) + 1; 650328459Skevans 651328459Skevans /* Abort if the last string isn't properly NUL-terminated. */ 652328459Skevans if (list + length > end) 653328459Skevans return -FDT_ERR_BADVALUE; 654328459Skevans 655328459Skevans list += length; 656328459Skevans count++; 657328459Skevans } 658328459Skevans 659328459Skevans return count; 660328459Skevans} 661328459Skevans 662328459Skevansint fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, 663328459Skevans const char *string) 664328459Skevans{ 665328459Skevans int length, len, idx = 0; 666328459Skevans const char *list, *end; 667328459Skevans 668328459Skevans list = fdt_getprop(fdt, nodeoffset, property, &length); 669328459Skevans if (!list) 670328459Skevans return length; 671328459Skevans 672328459Skevans len = strlen(string) + 1; 673328459Skevans end = list + length; 674328459Skevans 675328459Skevans while (list < end) { 676328459Skevans length = strnlen(list, end - list) + 1; 677328459Skevans 678328459Skevans /* Abort if the last string isn't properly NUL-terminated. */ 679328459Skevans if (list + length > end) 680328459Skevans return -FDT_ERR_BADVALUE; 681328459Skevans 682328459Skevans if (length == len && memcmp(list, string, length) == 0) 683328459Skevans return idx; 684328459Skevans 685328459Skevans list += length; 686328459Skevans idx++; 687328459Skevans } 688328459Skevans 689328459Skevans return -FDT_ERR_NOTFOUND; 690328459Skevans} 691328459Skevans 692328459Skevansconst char *fdt_stringlist_get(const void *fdt, int nodeoffset, 693328459Skevans const char *property, int idx, 694328459Skevans int *lenp) 695328459Skevans{ 696328459Skevans const char *list, *end; 697328459Skevans int length; 698328459Skevans 699328459Skevans list = fdt_getprop(fdt, nodeoffset, property, &length); 700328459Skevans if (!list) { 701328459Skevans if (lenp) 702328459Skevans *lenp = length; 703328459Skevans 704328459Skevans return NULL; 705328459Skevans } 706328459Skevans 707328459Skevans end = list + length; 708328459Skevans 709328459Skevans while (list < end) { 710328459Skevans length = strnlen(list, end - list) + 1; 711328459Skevans 712328459Skevans /* Abort if the last string isn't properly NUL-terminated. */ 713328459Skevans if (list + length > end) { 714328459Skevans if (lenp) 715328459Skevans *lenp = -FDT_ERR_BADVALUE; 716328459Skevans 717328459Skevans return NULL; 718328459Skevans } 719328459Skevans 720328459Skevans if (idx == 0) { 721328459Skevans if (lenp) 722328459Skevans *lenp = length - 1; 723328459Skevans 724328459Skevans return list; 725328459Skevans } 726328459Skevans 727328459Skevans list += length; 728328459Skevans idx--; 729328459Skevans } 730328459Skevans 731328459Skevans if (lenp) 732328459Skevans *lenp = -FDT_ERR_NOTFOUND; 733328459Skevans 734328459Skevans return NULL; 735328459Skevans} 736328459Skevans 737204431Srajint fdt_node_check_compatible(const void *fdt, int nodeoffset, 738204431Sraj const char *compatible) 739204431Sraj{ 740204431Sraj const void *prop; 741204431Sraj int len; 742204431Sraj 743204431Sraj prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 744204431Sraj if (!prop) 745204431Sraj return len; 746328459Skevans 747328459Skevans return !fdt_stringlist_contains(prop, len, compatible); 748204431Sraj} 749204431Sraj 750204431Srajint fdt_node_offset_by_compatible(const void *fdt, int startoffset, 751204431Sraj const char *compatible) 752204431Sraj{ 753204431Sraj int offset, err; 754204431Sraj 755204431Sraj FDT_CHECK_HEADER(fdt); 756204431Sraj 757204431Sraj /* FIXME: The algorithm here is pretty horrible: we scan each 758204431Sraj * property of a node in fdt_node_check_compatible(), then if 759204431Sraj * that didn't find what we want, we scan over them again 760204431Sraj * making our way to the next node. Still it's the easiest to 761204431Sraj * implement approach; performance can come later. */ 762204431Sraj for (offset = fdt_next_node(fdt, startoffset, NULL); 763204431Sraj offset >= 0; 764204431Sraj offset = fdt_next_node(fdt, offset, NULL)) { 765204431Sraj err = fdt_node_check_compatible(fdt, offset, compatible); 766204431Sraj if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 767204431Sraj return err; 768204431Sraj else if (err == 0) 769204431Sraj return offset; 770204431Sraj } 771204431Sraj 772204431Sraj return offset; /* error from fdt_next_node() */ 773204431Sraj} 774