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 58204431Srajstatic int _fdt_nodename_eq(const void *fdt, int offset, 59204431Sraj const char *s, int len) 60204431Sraj{ 61204431Sraj const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); 62204431Sraj 63204431Sraj if (! p) 64204431Sraj /* short match */ 65204431Sraj return 0; 66204431Sraj 67204431Sraj if (memcmp(p, s, len) != 0) 68204431Sraj return 0; 69204431Sraj 70204431Sraj if (p[len] == '\0') 71204431Sraj return 1; 72204431Sraj else if (!memchr(s, '@', len) && (p[len] == '@')) 73204431Sraj return 1; 74204431Sraj else 75204431Sraj return 0; 76204431Sraj} 77204431Sraj 78204431Srajconst char *fdt_string(const void *fdt, int stroffset) 79204431Sraj{ 80204431Sraj return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 81204431Sraj} 82204431Sraj 83204433Srajstatic int _fdt_string_eq(const void *fdt, int stroffset, 84204433Sraj const char *s, int len) 85204433Sraj{ 86204433Sraj const char *p = fdt_string(fdt, stroffset); 87204433Sraj 88204433Sraj return (strlen(p) == len) && (memcmp(p, s, len) == 0); 89204433Sraj} 90204433Sraj 91318102Sgonzouint32_t fdt_get_max_phandle(const void *fdt) 92318102Sgonzo{ 93318102Sgonzo uint32_t max_phandle = 0; 94318102Sgonzo int offset; 95318102Sgonzo 96318102Sgonzo for (offset = fdt_next_node(fdt, -1, NULL);; 97318102Sgonzo offset = fdt_next_node(fdt, offset, NULL)) { 98318102Sgonzo uint32_t phandle; 99318102Sgonzo 100318102Sgonzo if (offset == -FDT_ERR_NOTFOUND) 101318102Sgonzo return max_phandle; 102318102Sgonzo 103318102Sgonzo if (offset < 0) 104318102Sgonzo return (uint32_t)-1; 105318102Sgonzo 106318102Sgonzo phandle = fdt_get_phandle(fdt, offset); 107318102Sgonzo if (phandle == (uint32_t)-1) 108318102Sgonzo continue; 109318102Sgonzo 110318102Sgonzo if (phandle > max_phandle) 111318102Sgonzo max_phandle = phandle; 112318102Sgonzo } 113318102Sgonzo 114318102Sgonzo return 0; 115318102Sgonzo} 116318102Sgonzo 117204431Srajint fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 118204431Sraj{ 119204431Sraj FDT_CHECK_HEADER(fdt); 120204431Sraj *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); 121204431Sraj *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); 122204431Sraj return 0; 123204431Sraj} 124204431Sraj 125204431Srajint fdt_num_mem_rsv(const void *fdt) 126204431Sraj{ 127204431Sraj int i = 0; 128204431Sraj 129204431Sraj while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) 130204431Sraj i++; 131204431Sraj return i; 132204431Sraj} 133204431Sraj 134238742Simpstatic int _nextprop(const void *fdt, int offset) 135238742Simp{ 136238742Simp uint32_t tag; 137238742Simp int nextoffset; 138238742Simp 139238742Simp do { 140238742Simp tag = fdt_next_tag(fdt, offset, &nextoffset); 141238742Simp 142238742Simp switch (tag) { 143238742Simp case FDT_END: 144238742Simp if (nextoffset >= 0) 145238742Simp return -FDT_ERR_BADSTRUCTURE; 146238742Simp else 147238742Simp return nextoffset; 148238742Simp 149238742Simp case FDT_PROP: 150238742Simp return offset; 151238742Simp } 152238742Simp offset = nextoffset; 153238742Simp } while (tag == FDT_NOP); 154238742Simp 155238742Simp return -FDT_ERR_NOTFOUND; 156238742Simp} 157238742Simp 158204431Srajint fdt_subnode_offset_namelen(const void *fdt, int offset, 159204431Sraj const char *name, int namelen) 160204431Sraj{ 161204431Sraj int depth; 162204431Sraj 163204431Sraj FDT_CHECK_HEADER(fdt); 164204431Sraj 165204431Sraj for (depth = 0; 166204433Sraj (offset >= 0) && (depth >= 0); 167204433Sraj offset = fdt_next_node(fdt, offset, &depth)) 168204433Sraj if ((depth == 1) 169204433Sraj && _fdt_nodename_eq(fdt, offset, name, namelen)) 170204431Sraj return offset; 171204431Sraj 172204433Sraj if (depth < 0) 173204433Sraj return -FDT_ERR_NOTFOUND; 174204431Sraj return offset; /* error */ 175204431Sraj} 176204431Sraj 177204431Srajint fdt_subnode_offset(const void *fdt, int parentoffset, 178204431Sraj const char *name) 179204431Sraj{ 180204431Sraj return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 181204431Sraj} 182204431Sraj 183318102Sgonzoint fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) 184204431Sraj{ 185318102Sgonzo const char *end = path + namelen; 186204431Sraj const char *p = path; 187204431Sraj int offset = 0; 188204431Sraj 189204431Sraj FDT_CHECK_HEADER(fdt); 190204431Sraj 191204433Sraj /* see if we have an alias */ 192204433Sraj if (*path != '/') { 193318102Sgonzo const char *q = memchr(path, '/', end - p); 194204431Sraj 195204433Sraj if (!q) 196204433Sraj q = end; 197204433Sraj 198204433Sraj p = fdt_get_alias_namelen(fdt, p, q - p); 199204433Sraj if (!p) 200204433Sraj return -FDT_ERR_BADPATH; 201204433Sraj offset = fdt_path_offset(fdt, p); 202204433Sraj 203204433Sraj p = q; 204204433Sraj } 205204433Sraj 206318102Sgonzo while (p < end) { 207204431Sraj const char *q; 208204431Sraj 209318102Sgonzo while (*p == '/') { 210204431Sraj p++; 211318102Sgonzo if (p == end) 212318102Sgonzo return offset; 213318102Sgonzo } 214318102Sgonzo q = memchr(p, '/', end - p); 215204431Sraj if (! q) 216204431Sraj q = end; 217204431Sraj 218204431Sraj offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 219204431Sraj if (offset < 0) 220204431Sraj return offset; 221204431Sraj 222204431Sraj p = q; 223204431Sraj } 224204431Sraj 225204431Sraj return offset; 226204431Sraj} 227204431Sraj 228318102Sgonzoint fdt_path_offset(const void *fdt, const char *path) 229318102Sgonzo{ 230318102Sgonzo return fdt_path_offset_namelen(fdt, path, strlen(path)); 231318102Sgonzo} 232318102Sgonzo 233204431Srajconst char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 234204431Sraj{ 235204431Sraj const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); 236204431Sraj int err; 237204431Sraj 238204431Sraj if (((err = fdt_check_header(fdt)) != 0) 239204431Sraj || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 240204431Sraj goto fail; 241204431Sraj 242204431Sraj if (len) 243204431Sraj *len = strlen(nh->name); 244204431Sraj 245204431Sraj return nh->name; 246204431Sraj 247204431Sraj fail: 248204431Sraj if (len) 249204431Sraj *len = err; 250204431Sraj return NULL; 251204431Sraj} 252204431Sraj 253238742Simpint fdt_first_property_offset(const void *fdt, int nodeoffset) 254204431Sraj{ 255238742Simp int offset; 256238742Simp 257238742Simp if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) 258238742Simp return offset; 259238742Simp 260238742Simp return _nextprop(fdt, offset); 261238742Simp} 262238742Simp 263238742Simpint fdt_next_property_offset(const void *fdt, int offset) 264238742Simp{ 265238742Simp if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) 266238742Simp return offset; 267238742Simp 268238742Simp return _nextprop(fdt, offset); 269238742Simp} 270238742Simp 271238742Simpconst struct fdt_property *fdt_get_property_by_offset(const void *fdt, 272238742Simp int offset, 273238742Simp int *lenp) 274238742Simp{ 275238742Simp int err; 276204431Sraj const struct fdt_property *prop; 277204431Sraj 278238742Simp if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { 279238742Simp if (lenp) 280238742Simp *lenp = err; 281238742Simp return NULL; 282238742Simp } 283204431Sraj 284238742Simp prop = _fdt_offset_ptr(fdt, offset); 285204431Sraj 286238742Simp if (lenp) 287238742Simp *lenp = fdt32_to_cpu(prop->len); 288204431Sraj 289238742Simp return prop; 290238742Simp} 291204431Sraj 292238742Simpconst struct fdt_property *fdt_get_property_namelen(const void *fdt, 293238742Simp int offset, 294238742Simp const char *name, 295238742Simp int namelen, int *lenp) 296238742Simp{ 297238742Simp for (offset = fdt_first_property_offset(fdt, offset); 298238742Simp (offset >= 0); 299238742Simp (offset = fdt_next_property_offset(fdt, offset))) { 300238742Simp const struct fdt_property *prop; 301238742Simp 302238742Simp if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { 303238742Simp offset = -FDT_ERR_INTERNAL; 304204431Sraj break; 305204431Sraj } 306238742Simp if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), 307238742Simp name, namelen)) 308238742Simp return prop; 309238742Simp } 310204431Sraj 311204431Sraj if (lenp) 312238742Simp *lenp = offset; 313204431Sraj return NULL; 314204431Sraj} 315204431Sraj 316204433Srajconst struct fdt_property *fdt_get_property(const void *fdt, 317204433Sraj int nodeoffset, 318204433Sraj const char *name, int *lenp) 319204431Sraj{ 320204433Sraj return fdt_get_property_namelen(fdt, nodeoffset, name, 321204433Sraj strlen(name), lenp); 322204433Sraj} 323204433Sraj 324204433Srajconst void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 325204433Sraj const char *name, int namelen, int *lenp) 326204433Sraj{ 327204431Sraj const struct fdt_property *prop; 328204431Sraj 329204433Sraj prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); 330204431Sraj if (! prop) 331204431Sraj return NULL; 332204431Sraj 333204431Sraj return prop->data; 334204431Sraj} 335204431Sraj 336238742Simpconst void *fdt_getprop_by_offset(const void *fdt, int offset, 337238742Simp const char **namep, int *lenp) 338238742Simp{ 339238742Simp const struct fdt_property *prop; 340238742Simp 341238742Simp prop = fdt_get_property_by_offset(fdt, offset, lenp); 342238742Simp if (!prop) 343238742Simp return NULL; 344238742Simp if (namep) 345238742Simp *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 346238742Simp return prop->data; 347238742Simp} 348238742Simp 349204433Srajconst void *fdt_getprop(const void *fdt, int nodeoffset, 350204433Sraj const char *name, int *lenp) 351204433Sraj{ 352204433Sraj return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 353204433Sraj} 354204433Sraj 355204431Srajuint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 356204431Sraj{ 357261215Simp const fdt32_t *php; 358204431Sraj int len; 359204431Sraj 360204433Sraj /* FIXME: This is a bit sub-optimal, since we potentially scan 361204433Sraj * over all the properties twice. */ 362204433Sraj php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 363204433Sraj if (!php || (len != sizeof(*php))) { 364204433Sraj php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 365204433Sraj if (!php || (len != sizeof(*php))) 366204433Sraj return 0; 367204433Sraj } 368204431Sraj 369204431Sraj return fdt32_to_cpu(*php); 370204431Sraj} 371204431Sraj 372204433Srajconst char *fdt_get_alias_namelen(const void *fdt, 373204433Sraj const char *name, int namelen) 374204433Sraj{ 375204433Sraj int aliasoffset; 376204433Sraj 377204433Sraj aliasoffset = fdt_path_offset(fdt, "/aliases"); 378204433Sraj if (aliasoffset < 0) 379204433Sraj return NULL; 380204433Sraj 381204433Sraj return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 382204433Sraj} 383204433Sraj 384204433Srajconst char *fdt_get_alias(const void *fdt, const char *name) 385204433Sraj{ 386204433Sraj return fdt_get_alias_namelen(fdt, name, strlen(name)); 387204433Sraj} 388204433Sraj 389204431Srajint fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 390204431Sraj{ 391204431Sraj int pdepth = 0, p = 0; 392204431Sraj int offset, depth, namelen; 393204431Sraj const char *name; 394204431Sraj 395204431Sraj FDT_CHECK_HEADER(fdt); 396204431Sraj 397204431Sraj if (buflen < 2) 398204431Sraj return -FDT_ERR_NOSPACE; 399204431Sraj 400204431Sraj for (offset = 0, depth = 0; 401204431Sraj (offset >= 0) && (offset <= nodeoffset); 402204431Sraj offset = fdt_next_node(fdt, offset, &depth)) { 403204431Sraj while (pdepth > depth) { 404204431Sraj do { 405204431Sraj p--; 406204431Sraj } while (buf[p-1] != '/'); 407204431Sraj pdepth--; 408204431Sraj } 409204431Sraj 410204433Sraj if (pdepth >= depth) { 411204433Sraj name = fdt_get_name(fdt, offset, &namelen); 412204433Sraj if (!name) 413204433Sraj return namelen; 414204433Sraj if ((p + namelen + 1) <= buflen) { 415204433Sraj memcpy(buf + p, name, namelen); 416204433Sraj p += namelen; 417204433Sraj buf[p++] = '/'; 418204433Sraj pdepth++; 419204433Sraj } 420204431Sraj } 421204431Sraj 422204431Sraj if (offset == nodeoffset) { 423204431Sraj if (pdepth < (depth + 1)) 424204431Sraj return -FDT_ERR_NOSPACE; 425204431Sraj 426204431Sraj if (p > 1) /* special case so that root path is "/", not "" */ 427204431Sraj p--; 428204431Sraj buf[p] = '\0'; 429204433Sraj return 0; 430204431Sraj } 431204431Sraj } 432204431Sraj 433204431Sraj if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 434204431Sraj return -FDT_ERR_BADOFFSET; 435204431Sraj else if (offset == -FDT_ERR_BADOFFSET) 436204431Sraj return -FDT_ERR_BADSTRUCTURE; 437204431Sraj 438204431Sraj return offset; /* error from fdt_next_node() */ 439204431Sraj} 440204431Sraj 441204431Srajint fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 442204431Sraj int supernodedepth, int *nodedepth) 443204431Sraj{ 444204431Sraj int offset, depth; 445204431Sraj int supernodeoffset = -FDT_ERR_INTERNAL; 446204431Sraj 447204431Sraj FDT_CHECK_HEADER(fdt); 448204431Sraj 449204431Sraj if (supernodedepth < 0) 450204431Sraj return -FDT_ERR_NOTFOUND; 451204431Sraj 452204431Sraj for (offset = 0, depth = 0; 453204431Sraj (offset >= 0) && (offset <= nodeoffset); 454204431Sraj offset = fdt_next_node(fdt, offset, &depth)) { 455204431Sraj if (depth == supernodedepth) 456204431Sraj supernodeoffset = offset; 457204431Sraj 458204431Sraj if (offset == nodeoffset) { 459204431Sraj if (nodedepth) 460204431Sraj *nodedepth = depth; 461204431Sraj 462204431Sraj if (supernodedepth > depth) 463204431Sraj return -FDT_ERR_NOTFOUND; 464204431Sraj else 465204431Sraj return supernodeoffset; 466204431Sraj } 467204431Sraj } 468204431Sraj 469204431Sraj if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 470204431Sraj return -FDT_ERR_BADOFFSET; 471204431Sraj else if (offset == -FDT_ERR_BADOFFSET) 472204431Sraj return -FDT_ERR_BADSTRUCTURE; 473204431Sraj 474204431Sraj return offset; /* error from fdt_next_node() */ 475204431Sraj} 476204431Sraj 477204431Srajint fdt_node_depth(const void *fdt, int nodeoffset) 478204431Sraj{ 479204431Sraj int nodedepth; 480204431Sraj int err; 481204431Sraj 482204431Sraj err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 483204431Sraj if (err) 484204431Sraj return (err < 0) ? err : -FDT_ERR_INTERNAL; 485204431Sraj return nodedepth; 486204431Sraj} 487204431Sraj 488204431Srajint fdt_parent_offset(const void *fdt, int nodeoffset) 489204431Sraj{ 490204431Sraj int nodedepth = fdt_node_depth(fdt, nodeoffset); 491204431Sraj 492204431Sraj if (nodedepth < 0) 493204431Sraj return nodedepth; 494204431Sraj return fdt_supernode_atdepth_offset(fdt, nodeoffset, 495204431Sraj nodedepth - 1, NULL); 496204431Sraj} 497204431Sraj 498204431Srajint fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 499204431Sraj const char *propname, 500204431Sraj const void *propval, int proplen) 501204431Sraj{ 502204431Sraj int offset; 503204431Sraj const void *val; 504204431Sraj int len; 505204431Sraj 506204431Sraj FDT_CHECK_HEADER(fdt); 507204431Sraj 508204431Sraj /* FIXME: The algorithm here is pretty horrible: we scan each 509204431Sraj * property of a node in fdt_getprop(), then if that didn't 510204431Sraj * find what we want, we scan over them again making our way 511204431Sraj * to the next node. Still it's the easiest to implement 512204431Sraj * approach; performance can come later. */ 513204431Sraj for (offset = fdt_next_node(fdt, startoffset, NULL); 514204431Sraj offset >= 0; 515204431Sraj offset = fdt_next_node(fdt, offset, NULL)) { 516204431Sraj val = fdt_getprop(fdt, offset, propname, &len); 517204431Sraj if (val && (len == proplen) 518204431Sraj && (memcmp(val, propval, len) == 0)) 519204431Sraj return offset; 520204431Sraj } 521204431Sraj 522204431Sraj return offset; /* error from fdt_next_node() */ 523204431Sraj} 524204431Sraj 525204431Srajint fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 526204431Sraj{ 527204433Sraj int offset; 528204433Sraj 529204431Sraj if ((phandle == 0) || (phandle == -1)) 530204431Sraj return -FDT_ERR_BADPHANDLE; 531204433Sraj 532204433Sraj FDT_CHECK_HEADER(fdt); 533204433Sraj 534204433Sraj /* FIXME: The algorithm here is pretty horrible: we 535204433Sraj * potentially scan each property of a node in 536204433Sraj * fdt_get_phandle(), then if that didn't find what 537204433Sraj * we want, we scan over them again making our way to the next 538204433Sraj * node. Still it's the easiest to implement approach; 539204433Sraj * performance can come later. */ 540204433Sraj for (offset = fdt_next_node(fdt, -1, NULL); 541204433Sraj offset >= 0; 542204433Sraj offset = fdt_next_node(fdt, offset, NULL)) { 543204433Sraj if (fdt_get_phandle(fdt, offset) == phandle) 544204433Sraj return offset; 545204433Sraj } 546204433Sraj 547204433Sraj return offset; /* error from fdt_next_node() */ 548204431Sraj} 549204431Sraj 550261215Simpint fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 551204431Sraj{ 552204431Sraj int len = strlen(str); 553204431Sraj const char *p; 554204431Sraj 555204431Sraj while (listlen >= len) { 556204431Sraj if (memcmp(str, strlist, len+1) == 0) 557204431Sraj return 1; 558204431Sraj p = memchr(strlist, '\0', listlen); 559204431Sraj if (!p) 560204431Sraj return 0; /* malformed strlist.. */ 561204431Sraj listlen -= (p-strlist) + 1; 562204431Sraj strlist = p + 1; 563204431Sraj } 564204431Sraj return 0; 565204431Sraj} 566204431Sraj 567318102Sgonzoint fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) 568318102Sgonzo{ 569318102Sgonzo const char *list, *end; 570318102Sgonzo int length, count = 0; 571318102Sgonzo 572318102Sgonzo list = fdt_getprop(fdt, nodeoffset, property, &length); 573318102Sgonzo if (!list) 574318102Sgonzo return length; 575318102Sgonzo 576318102Sgonzo end = list + length; 577318102Sgonzo 578318102Sgonzo while (list < end) { 579318102Sgonzo length = strnlen(list, end - list) + 1; 580318102Sgonzo 581318102Sgonzo /* Abort if the last string isn't properly NUL-terminated. */ 582318102Sgonzo if (list + length > end) 583318102Sgonzo return -FDT_ERR_BADVALUE; 584318102Sgonzo 585318102Sgonzo list += length; 586318102Sgonzo count++; 587318102Sgonzo } 588318102Sgonzo 589318102Sgonzo return count; 590318102Sgonzo} 591318102Sgonzo 592318102Sgonzoint fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, 593318102Sgonzo const char *string) 594318102Sgonzo{ 595318102Sgonzo int length, len, idx = 0; 596318102Sgonzo const char *list, *end; 597318102Sgonzo 598318102Sgonzo list = fdt_getprop(fdt, nodeoffset, property, &length); 599318102Sgonzo if (!list) 600318102Sgonzo return length; 601318102Sgonzo 602318102Sgonzo len = strlen(string) + 1; 603318102Sgonzo end = list + length; 604318102Sgonzo 605318102Sgonzo while (list < end) { 606318102Sgonzo length = strnlen(list, end - list) + 1; 607318102Sgonzo 608318102Sgonzo /* Abort if the last string isn't properly NUL-terminated. */ 609318102Sgonzo if (list + length > end) 610318102Sgonzo return -FDT_ERR_BADVALUE; 611318102Sgonzo 612318102Sgonzo if (length == len && memcmp(list, string, length) == 0) 613318102Sgonzo return idx; 614318102Sgonzo 615318102Sgonzo list += length; 616318102Sgonzo idx++; 617318102Sgonzo } 618318102Sgonzo 619318102Sgonzo return -FDT_ERR_NOTFOUND; 620318102Sgonzo} 621318102Sgonzo 622318102Sgonzoconst char *fdt_stringlist_get(const void *fdt, int nodeoffset, 623318102Sgonzo const char *property, int idx, 624318102Sgonzo int *lenp) 625318102Sgonzo{ 626318102Sgonzo const char *list, *end; 627318102Sgonzo int length; 628318102Sgonzo 629318102Sgonzo list = fdt_getprop(fdt, nodeoffset, property, &length); 630318102Sgonzo if (!list) { 631318102Sgonzo if (lenp) 632318102Sgonzo *lenp = length; 633318102Sgonzo 634318102Sgonzo return NULL; 635318102Sgonzo } 636318102Sgonzo 637318102Sgonzo end = list + length; 638318102Sgonzo 639318102Sgonzo while (list < end) { 640318102Sgonzo length = strnlen(list, end - list) + 1; 641318102Sgonzo 642318102Sgonzo /* Abort if the last string isn't properly NUL-terminated. */ 643318102Sgonzo if (list + length > end) { 644318102Sgonzo if (lenp) 645318102Sgonzo *lenp = -FDT_ERR_BADVALUE; 646318102Sgonzo 647318102Sgonzo return NULL; 648318102Sgonzo } 649318102Sgonzo 650318102Sgonzo if (idx == 0) { 651318102Sgonzo if (lenp) 652318102Sgonzo *lenp = length - 1; 653318102Sgonzo 654318102Sgonzo return list; 655318102Sgonzo } 656318102Sgonzo 657318102Sgonzo list += length; 658318102Sgonzo idx--; 659318102Sgonzo } 660318102Sgonzo 661318102Sgonzo if (lenp) 662318102Sgonzo *lenp = -FDT_ERR_NOTFOUND; 663318102Sgonzo 664318102Sgonzo return NULL; 665318102Sgonzo} 666318102Sgonzo 667204431Srajint fdt_node_check_compatible(const void *fdt, int nodeoffset, 668204431Sraj const char *compatible) 669204431Sraj{ 670204431Sraj const void *prop; 671204431Sraj int len; 672204431Sraj 673204431Sraj prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 674204431Sraj if (!prop) 675204431Sraj return len; 676318102Sgonzo 677318102Sgonzo return !fdt_stringlist_contains(prop, len, compatible); 678204431Sraj} 679204431Sraj 680204431Srajint fdt_node_offset_by_compatible(const void *fdt, int startoffset, 681204431Sraj const char *compatible) 682204431Sraj{ 683204431Sraj int offset, err; 684204431Sraj 685204431Sraj FDT_CHECK_HEADER(fdt); 686204431Sraj 687204431Sraj /* FIXME: The algorithm here is pretty horrible: we scan each 688204431Sraj * property of a node in fdt_node_check_compatible(), then if 689204431Sraj * that didn't find what we want, we scan over them again 690204431Sraj * making our way to the next node. Still it's the easiest to 691204431Sraj * implement approach; performance can come later. */ 692204431Sraj for (offset = fdt_next_node(fdt, startoffset, NULL); 693204431Sraj offset >= 0; 694204431Sraj offset = fdt_next_node(fdt, offset, NULL)) { 695204431Sraj err = fdt_node_check_compatible(fdt, offset, compatible); 696204431Sraj if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 697204431Sraj return err; 698204431Sraj else if (err == 0) 699204431Sraj return offset; 700204431Sraj } 701204431Sraj 702204431Sraj return offset; /* error from fdt_next_node() */ 703204431Sraj} 704