1*d18719a4STom Rini /* 2*d18719a4STom Rini * libfdt - Flat Device Tree manipulation 3*d18719a4STom Rini * Copyright (C) 2006 David Gibson, IBM Corporation. 4*d18719a4STom Rini * 5*d18719a4STom Rini * libfdt is dual licensed: you can use it either under the terms of 6*d18719a4STom Rini * the GPL, or the BSD license, at your option. 7*d18719a4STom Rini * 8*d18719a4STom Rini * a) This library is free software; you can redistribute it and/or 9*d18719a4STom Rini * modify it under the terms of the GNU General Public License as 10*d18719a4STom Rini * published by the Free Software Foundation; either version 2 of the 11*d18719a4STom Rini * License, or (at your option) any later version. 12*d18719a4STom Rini * 13*d18719a4STom Rini * This library is distributed in the hope that it will be useful, 14*d18719a4STom Rini * but WITHOUT ANY WARRANTY; without even the implied warranty of 15*d18719a4STom Rini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16*d18719a4STom Rini * GNU General Public License for more details. 17*d18719a4STom Rini * 18*d18719a4STom Rini * You should have received a copy of the GNU General Public 19*d18719a4STom Rini * License along with this library; if not, write to the Free 20*d18719a4STom Rini * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21*d18719a4STom Rini * MA 02110-1301 USA 22*d18719a4STom Rini * 23*d18719a4STom Rini * Alternatively, 24*d18719a4STom Rini * 25*d18719a4STom Rini * b) Redistribution and use in source and binary forms, with or 26*d18719a4STom Rini * without modification, are permitted provided that the following 27*d18719a4STom Rini * conditions are met: 28*d18719a4STom Rini * 29*d18719a4STom Rini * 1. Redistributions of source code must retain the above 30*d18719a4STom Rini * copyright notice, this list of conditions and the following 31*d18719a4STom Rini * disclaimer. 32*d18719a4STom Rini * 2. Redistributions in binary form must reproduce the above 33*d18719a4STom Rini * copyright notice, this list of conditions and the following 34*d18719a4STom Rini * disclaimer in the documentation and/or other materials 35*d18719a4STom Rini * provided with the distribution. 36*d18719a4STom Rini * 37*d18719a4STom Rini * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38*d18719a4STom Rini * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39*d18719a4STom Rini * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40*d18719a4STom Rini * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41*d18719a4STom Rini * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42*d18719a4STom Rini * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43*d18719a4STom Rini * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44*d18719a4STom Rini * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45*d18719a4STom Rini * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46*d18719a4STom Rini * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47*d18719a4STom Rini * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48*d18719a4STom Rini * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49*d18719a4STom Rini * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50*d18719a4STom Rini */ 51*d18719a4STom Rini #include "libfdt_env.h" 52*d18719a4STom Rini 53*d18719a4STom Rini #include <fdt.h> 54*d18719a4STom Rini #include <libfdt.h> 55*d18719a4STom Rini 56*d18719a4STom Rini #include "libfdt_internal.h" 57*d18719a4STom Rini 58*d18719a4STom Rini static int _fdt_nodename_eq(const void *fdt, int offset, 59*d18719a4STom Rini const char *s, int len) 60*d18719a4STom Rini { 61*d18719a4STom Rini const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); 62*d18719a4STom Rini 63*d18719a4STom Rini if (! p) 64*d18719a4STom Rini /* short match */ 65*d18719a4STom Rini return 0; 66*d18719a4STom Rini 67*d18719a4STom Rini if (memcmp(p, s, len) != 0) 68*d18719a4STom Rini return 0; 69*d18719a4STom Rini 70*d18719a4STom Rini if (p[len] == '\0') 71*d18719a4STom Rini return 1; 72*d18719a4STom Rini else if (!memchr(s, '@', len) && (p[len] == '@')) 73*d18719a4STom Rini return 1; 74*d18719a4STom Rini else 75*d18719a4STom Rini return 0; 76*d18719a4STom Rini } 77*d18719a4STom Rini 78*d18719a4STom Rini const char *fdt_string(const void *fdt, int stroffset) 79*d18719a4STom Rini { 80*d18719a4STom Rini return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 81*d18719a4STom Rini } 82*d18719a4STom Rini 83*d18719a4STom Rini static int _fdt_string_eq(const void *fdt, int stroffset, 84*d18719a4STom Rini const char *s, int len) 85*d18719a4STom Rini { 86*d18719a4STom Rini const char *p = fdt_string(fdt, stroffset); 87*d18719a4STom Rini 88*d18719a4STom Rini return (strlen(p) == len) && (memcmp(p, s, len) == 0); 89*d18719a4STom Rini } 90*d18719a4STom Rini 91*d18719a4STom Rini uint32_t fdt_get_max_phandle(const void *fdt) 92*d18719a4STom Rini { 93*d18719a4STom Rini uint32_t max_phandle = 0; 94*d18719a4STom Rini int offset; 95*d18719a4STom Rini 96*d18719a4STom Rini for (offset = fdt_next_node(fdt, -1, NULL);; 97*d18719a4STom Rini offset = fdt_next_node(fdt, offset, NULL)) { 98*d18719a4STom Rini uint32_t phandle; 99*d18719a4STom Rini 100*d18719a4STom Rini if (offset == -FDT_ERR_NOTFOUND) 101*d18719a4STom Rini return max_phandle; 102*d18719a4STom Rini 103*d18719a4STom Rini if (offset < 0) 104*d18719a4STom Rini return (uint32_t)-1; 105*d18719a4STom Rini 106*d18719a4STom Rini phandle = fdt_get_phandle(fdt, offset); 107*d18719a4STom Rini if (phandle == (uint32_t)-1) 108*d18719a4STom Rini continue; 109*d18719a4STom Rini 110*d18719a4STom Rini if (phandle > max_phandle) 111*d18719a4STom Rini max_phandle = phandle; 112*d18719a4STom Rini } 113*d18719a4STom Rini 114*d18719a4STom Rini return 0; 115*d18719a4STom Rini } 116*d18719a4STom Rini 117*d18719a4STom Rini int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 118*d18719a4STom Rini { 119*d18719a4STom Rini FDT_CHECK_HEADER(fdt); 120*d18719a4STom Rini *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); 121*d18719a4STom Rini *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); 122*d18719a4STom Rini return 0; 123*d18719a4STom Rini } 124*d18719a4STom Rini 125*d18719a4STom Rini int fdt_num_mem_rsv(const void *fdt) 126*d18719a4STom Rini { 127*d18719a4STom Rini int i = 0; 128*d18719a4STom Rini 129*d18719a4STom Rini while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) 130*d18719a4STom Rini i++; 131*d18719a4STom Rini return i; 132*d18719a4STom Rini } 133*d18719a4STom Rini 134*d18719a4STom Rini static int _nextprop(const void *fdt, int offset) 135*d18719a4STom Rini { 136*d18719a4STom Rini uint32_t tag; 137*d18719a4STom Rini int nextoffset; 138*d18719a4STom Rini 139*d18719a4STom Rini do { 140*d18719a4STom Rini tag = fdt_next_tag(fdt, offset, &nextoffset); 141*d18719a4STom Rini 142*d18719a4STom Rini switch (tag) { 143*d18719a4STom Rini case FDT_END: 144*d18719a4STom Rini if (nextoffset >= 0) 145*d18719a4STom Rini return -FDT_ERR_BADSTRUCTURE; 146*d18719a4STom Rini else 147*d18719a4STom Rini return nextoffset; 148*d18719a4STom Rini 149*d18719a4STom Rini case FDT_PROP: 150*d18719a4STom Rini return offset; 151*d18719a4STom Rini } 152*d18719a4STom Rini offset = nextoffset; 153*d18719a4STom Rini } while (tag == FDT_NOP); 154*d18719a4STom Rini 155*d18719a4STom Rini return -FDT_ERR_NOTFOUND; 156*d18719a4STom Rini } 157*d18719a4STom Rini 158*d18719a4STom Rini int fdt_subnode_offset_namelen(const void *fdt, int offset, 159*d18719a4STom Rini const char *name, int namelen) 160*d18719a4STom Rini { 161*d18719a4STom Rini int depth; 162*d18719a4STom Rini 163*d18719a4STom Rini FDT_CHECK_HEADER(fdt); 164*d18719a4STom Rini 165*d18719a4STom Rini for (depth = 0; 166*d18719a4STom Rini (offset >= 0) && (depth >= 0); 167*d18719a4STom Rini offset = fdt_next_node(fdt, offset, &depth)) 168*d18719a4STom Rini if ((depth == 1) 169*d18719a4STom Rini && _fdt_nodename_eq(fdt, offset, name, namelen)) 170*d18719a4STom Rini return offset; 171*d18719a4STom Rini 172*d18719a4STom Rini if (depth < 0) 173*d18719a4STom Rini return -FDT_ERR_NOTFOUND; 174*d18719a4STom Rini return offset; /* error */ 175*d18719a4STom Rini } 176*d18719a4STom Rini 177*d18719a4STom Rini int fdt_subnode_offset(const void *fdt, int parentoffset, 178*d18719a4STom Rini const char *name) 179*d18719a4STom Rini { 180*d18719a4STom Rini return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 181*d18719a4STom Rini } 182*d18719a4STom Rini 183*d18719a4STom Rini int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) 184*d18719a4STom Rini { 185*d18719a4STom Rini const char *end = path + namelen; 186*d18719a4STom Rini const char *p = path; 187*d18719a4STom Rini int offset = 0; 188*d18719a4STom Rini 189*d18719a4STom Rini FDT_CHECK_HEADER(fdt); 190*d18719a4STom Rini 191*d18719a4STom Rini /* see if we have an alias */ 192*d18719a4STom Rini if (*path != '/') { 193*d18719a4STom Rini const char *q = memchr(path, '/', end - p); 194*d18719a4STom Rini 195*d18719a4STom Rini if (!q) 196*d18719a4STom Rini q = end; 197*d18719a4STom Rini 198*d18719a4STom Rini p = fdt_get_alias_namelen(fdt, p, q - p); 199*d18719a4STom Rini if (!p) 200*d18719a4STom Rini return -FDT_ERR_BADPATH; 201*d18719a4STom Rini offset = fdt_path_offset(fdt, p); 202*d18719a4STom Rini 203*d18719a4STom Rini p = q; 204*d18719a4STom Rini } 205*d18719a4STom Rini 206*d18719a4STom Rini while (p < end) { 207*d18719a4STom Rini const char *q; 208*d18719a4STom Rini 209*d18719a4STom Rini while (*p == '/') { 210*d18719a4STom Rini p++; 211*d18719a4STom Rini if (p == end) 212*d18719a4STom Rini return offset; 213*d18719a4STom Rini } 214*d18719a4STom Rini q = memchr(p, '/', end - p); 215*d18719a4STom Rini if (! q) 216*d18719a4STom Rini q = end; 217*d18719a4STom Rini 218*d18719a4STom Rini offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 219*d18719a4STom Rini if (offset < 0) 220*d18719a4STom Rini return offset; 221*d18719a4STom Rini 222*d18719a4STom Rini p = q; 223*d18719a4STom Rini } 224*d18719a4STom Rini 225*d18719a4STom Rini return offset; 226*d18719a4STom Rini } 227*d18719a4STom Rini 228*d18719a4STom Rini int fdt_path_offset(const void *fdt, const char *path) 229*d18719a4STom Rini { 230*d18719a4STom Rini return fdt_path_offset_namelen(fdt, path, strlen(path)); 231*d18719a4STom Rini } 232*d18719a4STom Rini 233*d18719a4STom Rini const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 234*d18719a4STom Rini { 235*d18719a4STom Rini const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); 236*d18719a4STom Rini int err; 237*d18719a4STom Rini 238*d18719a4STom Rini if (((err = fdt_check_header(fdt)) != 0) 239*d18719a4STom Rini || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 240*d18719a4STom Rini goto fail; 241*d18719a4STom Rini 242*d18719a4STom Rini if (len) 243*d18719a4STom Rini *len = strlen(nh->name); 244*d18719a4STom Rini 245*d18719a4STom Rini return nh->name; 246*d18719a4STom Rini 247*d18719a4STom Rini fail: 248*d18719a4STom Rini if (len) 249*d18719a4STom Rini *len = err; 250*d18719a4STom Rini return NULL; 251*d18719a4STom Rini } 252*d18719a4STom Rini 253*d18719a4STom Rini int fdt_first_property_offset(const void *fdt, int nodeoffset) 254*d18719a4STom Rini { 255*d18719a4STom Rini int offset; 256*d18719a4STom Rini 257*d18719a4STom Rini if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) 258*d18719a4STom Rini return offset; 259*d18719a4STom Rini 260*d18719a4STom Rini return _nextprop(fdt, offset); 261*d18719a4STom Rini } 262*d18719a4STom Rini 263*d18719a4STom Rini int fdt_next_property_offset(const void *fdt, int offset) 264*d18719a4STom Rini { 265*d18719a4STom Rini if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) 266*d18719a4STom Rini return offset; 267*d18719a4STom Rini 268*d18719a4STom Rini return _nextprop(fdt, offset); 269*d18719a4STom Rini } 270*d18719a4STom Rini 271*d18719a4STom Rini const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 272*d18719a4STom Rini int offset, 273*d18719a4STom Rini int *lenp) 274*d18719a4STom Rini { 275*d18719a4STom Rini int err; 276*d18719a4STom Rini const struct fdt_property *prop; 277*d18719a4STom Rini 278*d18719a4STom Rini if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { 279*d18719a4STom Rini if (lenp) 280*d18719a4STom Rini *lenp = err; 281*d18719a4STom Rini return NULL; 282*d18719a4STom Rini } 283*d18719a4STom Rini 284*d18719a4STom Rini prop = _fdt_offset_ptr(fdt, offset); 285*d18719a4STom Rini 286*d18719a4STom Rini if (lenp) 287*d18719a4STom Rini *lenp = fdt32_to_cpu(prop->len); 288*d18719a4STom Rini 289*d18719a4STom Rini return prop; 290*d18719a4STom Rini } 291*d18719a4STom Rini 292*d18719a4STom Rini const struct fdt_property *fdt_get_property_namelen(const void *fdt, 293*d18719a4STom Rini int offset, 294*d18719a4STom Rini const char *name, 295*d18719a4STom Rini int namelen, int *lenp) 296*d18719a4STom Rini { 297*d18719a4STom Rini for (offset = fdt_first_property_offset(fdt, offset); 298*d18719a4STom Rini (offset >= 0); 299*d18719a4STom Rini (offset = fdt_next_property_offset(fdt, offset))) { 300*d18719a4STom Rini const struct fdt_property *prop; 301*d18719a4STom Rini 302*d18719a4STom Rini if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { 303*d18719a4STom Rini offset = -FDT_ERR_INTERNAL; 304*d18719a4STom Rini break; 305*d18719a4STom Rini } 306*d18719a4STom Rini if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), 307*d18719a4STom Rini name, namelen)) 308*d18719a4STom Rini return prop; 309*d18719a4STom Rini } 310*d18719a4STom Rini 311*d18719a4STom Rini if (lenp) 312*d18719a4STom Rini *lenp = offset; 313*d18719a4STom Rini return NULL; 314*d18719a4STom Rini } 315*d18719a4STom Rini 316*d18719a4STom Rini const struct fdt_property *fdt_get_property(const void *fdt, 317*d18719a4STom Rini int nodeoffset, 318*d18719a4STom Rini const char *name, int *lenp) 319*d18719a4STom Rini { 320*d18719a4STom Rini return fdt_get_property_namelen(fdt, nodeoffset, name, 321*d18719a4STom Rini strlen(name), lenp); 322*d18719a4STom Rini } 323*d18719a4STom Rini 324*d18719a4STom Rini const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 325*d18719a4STom Rini const char *name, int namelen, int *lenp) 326*d18719a4STom Rini { 327*d18719a4STom Rini const struct fdt_property *prop; 328*d18719a4STom Rini 329*d18719a4STom Rini prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); 330*d18719a4STom Rini if (! prop) 331*d18719a4STom Rini return NULL; 332*d18719a4STom Rini 333*d18719a4STom Rini return prop->data; 334*d18719a4STom Rini } 335*d18719a4STom Rini 336*d18719a4STom Rini const void *fdt_getprop_by_offset(const void *fdt, int offset, 337*d18719a4STom Rini const char **namep, int *lenp) 338*d18719a4STom Rini { 339*d18719a4STom Rini const struct fdt_property *prop; 340*d18719a4STom Rini 341*d18719a4STom Rini prop = fdt_get_property_by_offset(fdt, offset, lenp); 342*d18719a4STom Rini if (!prop) 343*d18719a4STom Rini return NULL; 344*d18719a4STom Rini if (namep) 345*d18719a4STom Rini *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 346*d18719a4STom Rini return prop->data; 347*d18719a4STom Rini } 348*d18719a4STom Rini 349*d18719a4STom Rini const void *fdt_getprop(const void *fdt, int nodeoffset, 350*d18719a4STom Rini const char *name, int *lenp) 351*d18719a4STom Rini { 352*d18719a4STom Rini return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 353*d18719a4STom Rini } 354*d18719a4STom Rini 355*d18719a4STom Rini uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 356*d18719a4STom Rini { 357*d18719a4STom Rini const fdt32_t *php; 358*d18719a4STom Rini int len; 359*d18719a4STom Rini 360*d18719a4STom Rini /* FIXME: This is a bit sub-optimal, since we potentially scan 361*d18719a4STom Rini * over all the properties twice. */ 362*d18719a4STom Rini php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 363*d18719a4STom Rini if (!php || (len != sizeof(*php))) { 364*d18719a4STom Rini php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 365*d18719a4STom Rini if (!php || (len != sizeof(*php))) 366*d18719a4STom Rini return 0; 367*d18719a4STom Rini } 368*d18719a4STom Rini 369*d18719a4STom Rini return fdt32_to_cpu(*php); 370*d18719a4STom Rini } 371*d18719a4STom Rini 372*d18719a4STom Rini const char *fdt_get_alias_namelen(const void *fdt, 373*d18719a4STom Rini const char *name, int namelen) 374*d18719a4STom Rini { 375*d18719a4STom Rini int aliasoffset; 376*d18719a4STom Rini 377*d18719a4STom Rini aliasoffset = fdt_path_offset(fdt, "/aliases"); 378*d18719a4STom Rini if (aliasoffset < 0) 379*d18719a4STom Rini return NULL; 380*d18719a4STom Rini 381*d18719a4STom Rini return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 382*d18719a4STom Rini } 383*d18719a4STom Rini 384*d18719a4STom Rini const char *fdt_get_alias(const void *fdt, const char *name) 385*d18719a4STom Rini { 386*d18719a4STom Rini return fdt_get_alias_namelen(fdt, name, strlen(name)); 387*d18719a4STom Rini } 388*d18719a4STom Rini 389*d18719a4STom Rini int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 390*d18719a4STom Rini { 391*d18719a4STom Rini int pdepth = 0, p = 0; 392*d18719a4STom Rini int offset, depth, namelen; 393*d18719a4STom Rini const char *name; 394*d18719a4STom Rini 395*d18719a4STom Rini FDT_CHECK_HEADER(fdt); 396*d18719a4STom Rini 397*d18719a4STom Rini if (buflen < 2) 398*d18719a4STom Rini return -FDT_ERR_NOSPACE; 399*d18719a4STom Rini 400*d18719a4STom Rini for (offset = 0, depth = 0; 401*d18719a4STom Rini (offset >= 0) && (offset <= nodeoffset); 402*d18719a4STom Rini offset = fdt_next_node(fdt, offset, &depth)) { 403*d18719a4STom Rini while (pdepth > depth) { 404*d18719a4STom Rini do { 405*d18719a4STom Rini p--; 406*d18719a4STom Rini } while (buf[p-1] != '/'); 407*d18719a4STom Rini pdepth--; 408*d18719a4STom Rini } 409*d18719a4STom Rini 410*d18719a4STom Rini if (pdepth >= depth) { 411*d18719a4STom Rini name = fdt_get_name(fdt, offset, &namelen); 412*d18719a4STom Rini if (!name) 413*d18719a4STom Rini return namelen; 414*d18719a4STom Rini if ((p + namelen + 1) <= buflen) { 415*d18719a4STom Rini memcpy(buf + p, name, namelen); 416*d18719a4STom Rini p += namelen; 417*d18719a4STom Rini buf[p++] = '/'; 418*d18719a4STom Rini pdepth++; 419*d18719a4STom Rini } 420*d18719a4STom Rini } 421*d18719a4STom Rini 422*d18719a4STom Rini if (offset == nodeoffset) { 423*d18719a4STom Rini if (pdepth < (depth + 1)) 424*d18719a4STom Rini return -FDT_ERR_NOSPACE; 425*d18719a4STom Rini 426*d18719a4STom Rini if (p > 1) /* special case so that root path is "/", not "" */ 427*d18719a4STom Rini p--; 428*d18719a4STom Rini buf[p] = '\0'; 429*d18719a4STom Rini return 0; 430*d18719a4STom Rini } 431*d18719a4STom Rini } 432*d18719a4STom Rini 433*d18719a4STom Rini if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 434*d18719a4STom Rini return -FDT_ERR_BADOFFSET; 435*d18719a4STom Rini else if (offset == -FDT_ERR_BADOFFSET) 436*d18719a4STom Rini return -FDT_ERR_BADSTRUCTURE; 437*d18719a4STom Rini 438*d18719a4STom Rini return offset; /* error from fdt_next_node() */ 439*d18719a4STom Rini } 440*d18719a4STom Rini 441*d18719a4STom Rini int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 442*d18719a4STom Rini int supernodedepth, int *nodedepth) 443*d18719a4STom Rini { 444*d18719a4STom Rini int offset, depth; 445*d18719a4STom Rini int supernodeoffset = -FDT_ERR_INTERNAL; 446*d18719a4STom Rini 447*d18719a4STom Rini FDT_CHECK_HEADER(fdt); 448*d18719a4STom Rini 449*d18719a4STom Rini if (supernodedepth < 0) 450*d18719a4STom Rini return -FDT_ERR_NOTFOUND; 451*d18719a4STom Rini 452*d18719a4STom Rini for (offset = 0, depth = 0; 453*d18719a4STom Rini (offset >= 0) && (offset <= nodeoffset); 454*d18719a4STom Rini offset = fdt_next_node(fdt, offset, &depth)) { 455*d18719a4STom Rini if (depth == supernodedepth) 456*d18719a4STom Rini supernodeoffset = offset; 457*d18719a4STom Rini 458*d18719a4STom Rini if (offset == nodeoffset) { 459*d18719a4STom Rini if (nodedepth) 460*d18719a4STom Rini *nodedepth = depth; 461*d18719a4STom Rini 462*d18719a4STom Rini if (supernodedepth > depth) 463*d18719a4STom Rini return -FDT_ERR_NOTFOUND; 464*d18719a4STom Rini else 465*d18719a4STom Rini return supernodeoffset; 466*d18719a4STom Rini } 467*d18719a4STom Rini } 468*d18719a4STom Rini 469*d18719a4STom Rini if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 470*d18719a4STom Rini return -FDT_ERR_BADOFFSET; 471*d18719a4STom Rini else if (offset == -FDT_ERR_BADOFFSET) 472*d18719a4STom Rini return -FDT_ERR_BADSTRUCTURE; 473*d18719a4STom Rini 474*d18719a4STom Rini return offset; /* error from fdt_next_node() */ 475*d18719a4STom Rini } 476*d18719a4STom Rini 477*d18719a4STom Rini int fdt_node_depth(const void *fdt, int nodeoffset) 478*d18719a4STom Rini { 479*d18719a4STom Rini int nodedepth; 480*d18719a4STom Rini int err; 481*d18719a4STom Rini 482*d18719a4STom Rini err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 483*d18719a4STom Rini if (err) 484*d18719a4STom Rini return (err < 0) ? err : -FDT_ERR_INTERNAL; 485*d18719a4STom Rini return nodedepth; 486*d18719a4STom Rini } 487*d18719a4STom Rini 488*d18719a4STom Rini int fdt_parent_offset(const void *fdt, int nodeoffset) 489*d18719a4STom Rini { 490*d18719a4STom Rini int nodedepth = fdt_node_depth(fdt, nodeoffset); 491*d18719a4STom Rini 492*d18719a4STom Rini if (nodedepth < 0) 493*d18719a4STom Rini return nodedepth; 494*d18719a4STom Rini return fdt_supernode_atdepth_offset(fdt, nodeoffset, 495*d18719a4STom Rini nodedepth - 1, NULL); 496*d18719a4STom Rini } 497*d18719a4STom Rini 498*d18719a4STom Rini int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 499*d18719a4STom Rini const char *propname, 500*d18719a4STom Rini const void *propval, int proplen) 501*d18719a4STom Rini { 502*d18719a4STom Rini int offset; 503*d18719a4STom Rini const void *val; 504*d18719a4STom Rini int len; 505*d18719a4STom Rini 506*d18719a4STom Rini FDT_CHECK_HEADER(fdt); 507*d18719a4STom Rini 508*d18719a4STom Rini /* FIXME: The algorithm here is pretty horrible: we scan each 509*d18719a4STom Rini * property of a node in fdt_getprop(), then if that didn't 510*d18719a4STom Rini * find what we want, we scan over them again making our way 511*d18719a4STom Rini * to the next node. Still it's the easiest to implement 512*d18719a4STom Rini * approach; performance can come later. */ 513*d18719a4STom Rini for (offset = fdt_next_node(fdt, startoffset, NULL); 514*d18719a4STom Rini offset >= 0; 515*d18719a4STom Rini offset = fdt_next_node(fdt, offset, NULL)) { 516*d18719a4STom Rini val = fdt_getprop(fdt, offset, propname, &len); 517*d18719a4STom Rini if (val && (len == proplen) 518*d18719a4STom Rini && (memcmp(val, propval, len) == 0)) 519*d18719a4STom Rini return offset; 520*d18719a4STom Rini } 521*d18719a4STom Rini 522*d18719a4STom Rini return offset; /* error from fdt_next_node() */ 523*d18719a4STom Rini } 524*d18719a4STom Rini 525*d18719a4STom Rini int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 526*d18719a4STom Rini { 527*d18719a4STom Rini int offset; 528*d18719a4STom Rini 529*d18719a4STom Rini if ((phandle == 0) || (phandle == -1)) 530*d18719a4STom Rini return -FDT_ERR_BADPHANDLE; 531*d18719a4STom Rini 532*d18719a4STom Rini FDT_CHECK_HEADER(fdt); 533*d18719a4STom Rini 534*d18719a4STom Rini /* FIXME: The algorithm here is pretty horrible: we 535*d18719a4STom Rini * potentially scan each property of a node in 536*d18719a4STom Rini * fdt_get_phandle(), then if that didn't find what 537*d18719a4STom Rini * we want, we scan over them again making our way to the next 538*d18719a4STom Rini * node. Still it's the easiest to implement approach; 539*d18719a4STom Rini * performance can come later. */ 540*d18719a4STom Rini for (offset = fdt_next_node(fdt, -1, NULL); 541*d18719a4STom Rini offset >= 0; 542*d18719a4STom Rini offset = fdt_next_node(fdt, offset, NULL)) { 543*d18719a4STom Rini if (fdt_get_phandle(fdt, offset) == phandle) 544*d18719a4STom Rini return offset; 545*d18719a4STom Rini } 546*d18719a4STom Rini 547*d18719a4STom Rini return offset; /* error from fdt_next_node() */ 548*d18719a4STom Rini } 549*d18719a4STom Rini 550*d18719a4STom Rini int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 551*d18719a4STom Rini { 552*d18719a4STom Rini int len = strlen(str); 553*d18719a4STom Rini const char *p; 554*d18719a4STom Rini 555*d18719a4STom Rini while (listlen >= len) { 556*d18719a4STom Rini if (memcmp(str, strlist, len+1) == 0) 557*d18719a4STom Rini return 1; 558*d18719a4STom Rini p = memchr(strlist, '\0', listlen); 559*d18719a4STom Rini if (!p) 560*d18719a4STom Rini return 0; /* malformed strlist.. */ 561*d18719a4STom Rini listlen -= (p-strlist) + 1; 562*d18719a4STom Rini strlist = p + 1; 563*d18719a4STom Rini } 564*d18719a4STom Rini return 0; 565*d18719a4STom Rini } 566*d18719a4STom Rini 567*d18719a4STom Rini int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) 568*d18719a4STom Rini { 569*d18719a4STom Rini const char *list, *end; 570*d18719a4STom Rini int length, count = 0; 571*d18719a4STom Rini 572*d18719a4STom Rini list = fdt_getprop(fdt, nodeoffset, property, &length); 573*d18719a4STom Rini if (!list) 574*d18719a4STom Rini return length; 575*d18719a4STom Rini 576*d18719a4STom Rini end = list + length; 577*d18719a4STom Rini 578*d18719a4STom Rini while (list < end) { 579*d18719a4STom Rini length = strnlen(list, end - list) + 1; 580*d18719a4STom Rini 581*d18719a4STom Rini /* Abort if the last string isn't properly NUL-terminated. */ 582*d18719a4STom Rini if (list + length > end) 583*d18719a4STom Rini return -FDT_ERR_BADVALUE; 584*d18719a4STom Rini 585*d18719a4STom Rini list += length; 586*d18719a4STom Rini count++; 587*d18719a4STom Rini } 588*d18719a4STom Rini 589*d18719a4STom Rini return count; 590*d18719a4STom Rini } 591*d18719a4STom Rini 592*d18719a4STom Rini int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, 593*d18719a4STom Rini const char *string) 594*d18719a4STom Rini { 595*d18719a4STom Rini int length, len, idx = 0; 596*d18719a4STom Rini const char *list, *end; 597*d18719a4STom Rini 598*d18719a4STom Rini list = fdt_getprop(fdt, nodeoffset, property, &length); 599*d18719a4STom Rini if (!list) 600*d18719a4STom Rini return length; 601*d18719a4STom Rini 602*d18719a4STom Rini len = strlen(string) + 1; 603*d18719a4STom Rini end = list + length; 604*d18719a4STom Rini 605*d18719a4STom Rini while (list < end) { 606*d18719a4STom Rini length = strnlen(list, end - list) + 1; 607*d18719a4STom Rini 608*d18719a4STom Rini /* Abort if the last string isn't properly NUL-terminated. */ 609*d18719a4STom Rini if (list + length > end) 610*d18719a4STom Rini return -FDT_ERR_BADVALUE; 611*d18719a4STom Rini 612*d18719a4STom Rini if (length == len && memcmp(list, string, length) == 0) 613*d18719a4STom Rini return idx; 614*d18719a4STom Rini 615*d18719a4STom Rini list += length; 616*d18719a4STom Rini idx++; 617*d18719a4STom Rini } 618*d18719a4STom Rini 619*d18719a4STom Rini return -FDT_ERR_NOTFOUND; 620*d18719a4STom Rini } 621*d18719a4STom Rini 622*d18719a4STom Rini const char *fdt_stringlist_get(const void *fdt, int nodeoffset, 623*d18719a4STom Rini const char *property, int idx, 624*d18719a4STom Rini int *lenp) 625*d18719a4STom Rini { 626*d18719a4STom Rini const char *list, *end; 627*d18719a4STom Rini int length; 628*d18719a4STom Rini 629*d18719a4STom Rini list = fdt_getprop(fdt, nodeoffset, property, &length); 630*d18719a4STom Rini if (!list) { 631*d18719a4STom Rini if (lenp) 632*d18719a4STom Rini *lenp = length; 633*d18719a4STom Rini 634*d18719a4STom Rini return NULL; 635*d18719a4STom Rini } 636*d18719a4STom Rini 637*d18719a4STom Rini end = list + length; 638*d18719a4STom Rini 639*d18719a4STom Rini while (list < end) { 640*d18719a4STom Rini length = strnlen(list, end - list) + 1; 641*d18719a4STom Rini 642*d18719a4STom Rini /* Abort if the last string isn't properly NUL-terminated. */ 643*d18719a4STom Rini if (list + length > end) { 644*d18719a4STom Rini if (lenp) 645*d18719a4STom Rini *lenp = -FDT_ERR_BADVALUE; 646*d18719a4STom Rini 647*d18719a4STom Rini return NULL; 648*d18719a4STom Rini } 649*d18719a4STom Rini 650*d18719a4STom Rini if (idx == 0) { 651*d18719a4STom Rini if (lenp) 652*d18719a4STom Rini *lenp = length - 1; 653*d18719a4STom Rini 654*d18719a4STom Rini return list; 655*d18719a4STom Rini } 656*d18719a4STom Rini 657*d18719a4STom Rini list += length; 658*d18719a4STom Rini idx--; 659*d18719a4STom Rini } 660*d18719a4STom Rini 661*d18719a4STom Rini if (lenp) 662*d18719a4STom Rini *lenp = -FDT_ERR_NOTFOUND; 663*d18719a4STom Rini 664*d18719a4STom Rini return NULL; 665*d18719a4STom Rini } 666*d18719a4STom Rini 667*d18719a4STom Rini int fdt_node_check_compatible(const void *fdt, int nodeoffset, 668*d18719a4STom Rini const char *compatible) 669*d18719a4STom Rini { 670*d18719a4STom Rini const void *prop; 671*d18719a4STom Rini int len; 672*d18719a4STom Rini 673*d18719a4STom Rini prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 674*d18719a4STom Rini if (!prop) 675*d18719a4STom Rini return len; 676*d18719a4STom Rini 677*d18719a4STom Rini return !fdt_stringlist_contains(prop, len, compatible); 678*d18719a4STom Rini } 679*d18719a4STom Rini 680*d18719a4STom Rini int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 681*d18719a4STom Rini const char *compatible) 682*d18719a4STom Rini { 683*d18719a4STom Rini int offset, err; 684*d18719a4STom Rini 685*d18719a4STom Rini FDT_CHECK_HEADER(fdt); 686*d18719a4STom Rini 687*d18719a4STom Rini /* FIXME: The algorithm here is pretty horrible: we scan each 688*d18719a4STom Rini * property of a node in fdt_node_check_compatible(), then if 689*d18719a4STom Rini * that didn't find what we want, we scan over them again 690*d18719a4STom Rini * making our way to the next node. Still it's the easiest to 691*d18719a4STom Rini * implement approach; performance can come later. */ 692*d18719a4STom Rini for (offset = fdt_next_node(fdt, startoffset, NULL); 693*d18719a4STom Rini offset >= 0; 694*d18719a4STom Rini offset = fdt_next_node(fdt, offset, NULL)) { 695*d18719a4STom Rini err = fdt_node_check_compatible(fdt, offset, compatible); 696*d18719a4STom Rini if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 697*d18719a4STom Rini return err; 698*d18719a4STom Rini else if (err == 0) 699*d18719a4STom Rini return offset; 700*d18719a4STom Rini } 701*d18719a4STom Rini 702*d18719a4STom Rini return offset; /* error from fdt_next_node() */ 703*d18719a4STom Rini } 704