/* This file is part of the program psim. Copyright (C) 1994-1995, Andrew Cagney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _DEVICE_TREE_C_ #define _DEVICE_TREE_C_ #ifndef STATIC_INLINE_DEVICE_TREE #define STATIC_INLINE_DEVICE_TREE STATIC_INLINE #endif #include #include #include #include "basics.h" #include "device_tree.h" typedef enum { node_any = 0, node_device, node_integer, node_boolean, node_string } node_type; struct _device_tree { /* where i am */ device_tree *parent; device_tree *children; device_tree *sibling; /* what i am */ node_type type; const char *name; /* the value */ const device *device; int boolean; const char *string; signed_word integer; }; STATIC_INLINE_DEVICE_TREE device_tree * new_device_tree(device_tree *parent, const char *name, node_type type) { device_tree *new_node; new_node = ZALLOC(device_tree); new_node->parent = parent; new_node->name = strdup(name); new_node->type = type; if (parent != NULL) { device_tree **sibling = &parent->children; while ((*sibling) != NULL) sibling = &(*sibling)->sibling; *sibling = new_node; } return new_node; } /* find/create a node in the device tree */ typedef enum { device_tree_grow = 1, device_tree_return_null = 2, device_tree_abort = 3, } device_tree_action; STATIC_INLINE_DEVICE_TREE device_tree * device_tree_find_node(device_tree *root, const char *path, node_type type, device_tree_action action) { const char *chp; int name_len; device_tree *child; /* strip off any leading `/', `../' or `./' */ while (1) { if (strncmp(path, "/", strlen("/")) == 0) { while (root != NULL && root->parent != NULL) root = root->parent; path += strlen("/"); } else if (strncmp(path, "./", strlen("./")) == 0) { root = root; path += strlen("./"); } else if (strncmp(path, "../", strlen("../")) == 0) { if (root != NULL && root->parent != NULL) root = root->parent; path += strlen("../"); } else { break; } } /* find the qualified (with @) and unqualified names in the path, remembering to skip any "\/" */ chp = path; do { chp = strchr(chp+1, '/'); } while (chp != NULL && chp[-1] == '\\'); name_len = (chp == NULL ? strlen(path) : chp - path); /* leaf? and growing? */ if (root != NULL) { for (child = root->children; child != NULL; child = child->sibling) { if (strncmp(path, child->name, name_len) == 0 && (strlen(child->name) == name_len || (strchr(child->name, '@') == child->name + name_len))) { if (path[name_len] == '\0') { if (action == device_tree_grow) error("device_tree_find_node() node %s already present\n", path); if (type != node_any && child->type != type) { if (action == device_tree_return_null) return NULL; else error("device_tree_find_node() node %s does not match type %d\n", path, type); } else return child; } else return device_tree_find_node(child, path + name_len + 1, type, action); } } } /* search failed, take default action */ switch (action) { case device_tree_grow: if (path[name_len] != '\0') error("device_tree_find_node() not a leaf %s\n", path); return new_device_tree(root, path, type); case device_tree_return_null: return NULL; default: error("device_tree_find_node() invalid default action %d\n", action); return NULL; } } /* grow the device tree */ INLINE_DEVICE_TREE device_tree * device_tree_add_passthrough(device_tree *root, const char *path) { device_tree *new_node = device_tree_find_node(root, path, node_device, device_tree_grow); new_node->device = device_create_from(new_node->name, NULL, passthrough_device_callbacks(), new_node->parent->device); return new_node; } INLINE_DEVICE_TREE device_tree * device_tree_add_device(device_tree *root, const char *path, const device *dev) { device_tree *new_node = device_tree_find_node(root, path, node_device, device_tree_grow); new_node->device = dev; return new_node; } INLINE_DEVICE_TREE device_tree * device_tree_add_integer(device_tree *root, const char *path, signed_word integer) { device_tree *new_node = device_tree_find_node(root, path, node_integer, device_tree_grow); new_node->integer = integer; return new_node; } INLINE_DEVICE_TREE device_tree * device_tree_add_string(device_tree *root, const char *path, const char *string) { device_tree *new_node = device_tree_find_node(root, path, node_string, device_tree_grow); new_node->string = string; return new_node; } INLINE_DEVICE_TREE device_tree * device_tree_add_boolean(device_tree *root, const char *path, int boolean) { device_tree *new_node = device_tree_find_node(root, path, node_boolean, device_tree_grow); new_node->boolean = boolean; return new_node; } INLINE_DEVICE_TREE device_tree * device_tree_add_found_device(device_tree *root, const char *path) { device_tree *new_node = device_tree_add_device(root, path, NULL); new_node->device = device_create(new_node->name, new_node->parent->device); return new_node; } /* look up the device tree */ INLINE_DEVICE_TREE const device * device_tree_find_device(device_tree *root, const char *path) { device_tree *node = device_tree_find_node(root, path, node_device, device_tree_abort); return node->device; } INLINE_DEVICE_TREE signed_word device_tree_find_integer(device_tree *root, const char *path) { device_tree *node = device_tree_find_node(root, path, node_integer, device_tree_abort); return node->integer; } INLINE_DEVICE_TREE const char * device_tree_find_string(device_tree *root, const char *path) { device_tree *node = device_tree_find_node(root, path, node_string, device_tree_abort); return node->string; } INLINE_DEVICE_TREE int device_tree_find_boolean(device_tree *root, const char *path) { device_tree *node = device_tree_find_node(root, path, node_boolean, device_tree_abort); return node->boolean; } /* init all the devices */ STATIC_INLINE_DEVICE_TREE void device_tree_init_device(device_tree *root, void *data) { psim *system = (psim*)data; if (root->type == node_device) root->device->callback->init(root->device, system); } INLINE_DEVICE_TREE void device_tree_init(device_tree *root, psim *system) { device_tree_traverse(root, device_tree_init_device, NULL, system); } /* traverse a device tree applying prefix/postfix functions to it */ INLINE_DEVICE_TREE void device_tree_traverse(device_tree *root, device_tree_traverse_function *prefix, device_tree_traverse_function *postfix, void *data) { device_tree *child; if (prefix != NULL) prefix(root, data); for (child = root->children; child != NULL; child = child->sibling) { device_tree_traverse(child, prefix, postfix, data); } if (postfix != NULL) postfix(root, data); } /* dump out a device node and addresses */ INLINE_DEVICE_TREE void device_tree_dump(device_tree *device, void *ignore_data_argument) { printf_filtered("(device_tree@0x%x\n", device); printf_filtered(" (parent 0x%x)\n", device->parent); printf_filtered(" (children 0x%x)\n", device->children); printf_filtered(" (sibling 0x%x)\n", device->sibling); printf_filtered(" (type %d)\n", device->type); printf_filtered(" (name %s)\n", device->name); printf_filtered(" (device 0x%x)\n", device->device); printf_filtered(" (boolean %d)\n", device->boolean); printf_filtered(" (string %s)\n", device->string); printf_filtered(" (integer %d)\n", device->integer); printf_filtered(")\n"); } /* Parse a device name, various formats */ #ifndef __NetBSD__ #define strtouq strtoul #endif #define SCAN_INIT(START, END, COUNT, NAME) \ char *START = NULL; \ char *END = strchr(NAME, '@'); \ int COUNT = 0; \ if (END == NULL) \ return 0; \ START = END + 1 #define SCAN_U(START, END, COUNT, U) \ do { \ *U = strtouq(START, &END, 0); \ if (START == END) \ return COUNT; \ COUNT++; \ if (*END != ',') \ return COUNT; \ START = END + 1; \ } while (0) #define SCAN_P(START, END, COUNT, P) \ do { \ *P = (void*)(unsigned)strtouq(START, &END, 0); \ if (START == END) \ return COUNT; \ COUNT++; \ if (*END != ',') \ return COUNT; \ START = END + 1; \ } while (0) #define SCAN_C(START, END, COUNT, C) \ do { \ char *chp = C; \ END = START; \ while (*END != '\0' && *END != ',') { \ if (*END == '\\') \ END++; \ *chp = *END; \ chp += 1; \ END += 1; \ } \ *chp = '\0'; \ if (START == END) \ return COUNT; \ COUNT++; \ if (*END != ',') \ return COUNT; \ START = END + 1; \ } while (0) INLINE_DEVICE_TREE int scand_uw(const char *name, unsigned_word *uw1) { SCAN_INIT(start, end, count, name); SCAN_U(start, end, count, uw1); return count; } INLINE_DEVICE_TREE int scand_uw_u(const char *name, unsigned_word *uw1, unsigned *u2) { SCAN_INIT(start, end, count, name); SCAN_U(start, end, count, uw1); SCAN_U(start, end, count, u2); return count; } INLINE_DEVICE_TREE int scand_uw_u_u(const char *name, unsigned_word *uw1, unsigned *u2, unsigned *u3) { SCAN_INIT(start, end, count, name); SCAN_U(start, end, count, uw1); SCAN_U(start, end, count, u2); SCAN_U(start, end, count, u3); return count; } INLINE_DEVICE_TREE int scand_uw_uw_u(const char *name, unsigned_word *uw1, unsigned_word *uw2, unsigned *u3) { SCAN_INIT(start, end, count, name); SCAN_U(start, end, count, uw1); SCAN_U(start, end, count, uw2); SCAN_U(start, end, count, u3); return count; } INLINE_DEVICE_TREE int scand_c(const char *name, char *c1) { SCAN_INIT(start, end, count, name); SCAN_C(start, end, count, c1); return count; } INLINE_DEVICE_TREE int scand_c_uw_u(const char *name, char *c1, unsigned_word *uw2, unsigned *u3) { SCAN_INIT(start, end, count, name); SCAN_C(start, end, count, c1); SCAN_U(start, end, count, uw2); SCAN_U(start, end, count, u3); return count; } STATIC_INLINE_DEVICE_TREE void u_strcat(char *buf, unsigned_word uw) { if (MASKED64(uw, 32, 63) == uw || WITH_HOST_WORD_BITSIZE == 64) { char *end = strchr(buf, '\0'); sprintf(end, "0x%x", (unsigned)uw); } else { char *end = strchr(buf, '\0'); sprintf(end, "0x%x%08x", (unsigned)EXTRACTED64(uw, 0, 31), (unsigned)EXTRACTED64(uw, 32, 63)); } } STATIC_INLINE_DEVICE_TREE void c_strcat(char *buf, const char *c) { char *end = strchr(buf, '\0'); while (*c) { if (*c == '/' || *c == ',') *end++ = '\\'; *end++ = *c++; } *end = '\0'; } STATIC_INLINE_DEVICE_TREE int c_strlen(const char *c) { int len = 0; while (*c) { if (*c == '/' || *c == ',') len++; len++; c++; } return len; } enum { strlen_unsigned = 10, strlen_unsigned_word = 18, }; INLINE_DEVICE_TREE char * printd_uw_u(const char *name, unsigned_word uw1, unsigned u2) { int sizeof_buf = (strlen(name) + strlen("@") + strlen_unsigned_word + strlen(",") + strlen_unsigned + 1); char *buf = (char*)zalloc(sizeof_buf); strcpy(buf, name); strcat(buf, "@"); u_strcat(buf, uw1); strcat(buf, ","); u_strcat(buf, u2); ASSERT(strlen(buf) < sizeof_buf); return buf; } INLINE_DEVICE_TREE char * printd_uw_u_u(const char *name, unsigned_word uw1, unsigned u2, unsigned u3) { int sizeof_buf = (strlen(name) + strlen("@") + strlen_unsigned_word + strlen(",") + strlen_unsigned + strlen(",") + strlen_unsigned + 1); char *buf = (char*)zalloc(sizeof_buf); strcpy(buf, name); strcat(buf, "@"); u_strcat(buf, uw1); strcat(buf, ","); u_strcat(buf, u2); strcat(buf, ","); u_strcat(buf, u3); ASSERT(strlen(buf) < sizeof_buf); return buf; } INLINE_DEVICE_TREE char * printd_uw_u_u_c(const char *name, unsigned_word uw1, unsigned u2, unsigned u3, const char *c4) { int sizeof_buf = (strlen(name) + strlen("@") + strlen_unsigned_word + strlen(",") + strlen_unsigned + strlen(",") + strlen_unsigned + strlen(",") + c_strlen(c4) + 1); char *buf = (char*)zalloc(sizeof_buf); strcpy(buf, name); strcat(buf, "@"); u_strcat(buf, uw1); strcat(buf, ","); u_strcat(buf, u2); strcat(buf, ","); u_strcat(buf, u3); strcat(buf, ","); c_strcat(buf, c4); ASSERT(strlen(buf) < sizeof_buf); return buf; } INLINE_DEVICE_TREE char * printd_c(const char *name, const char *c1) { int sizeof_buf = (strlen(name) + strlen("@") + c_strlen(c1) + 1); char *buf = (char*)zalloc(sizeof_buf); strcpy(buf, name); strcat(buf, "@"); c_strcat(buf, c1); ASSERT(strlen(buf) < sizeof_buf); return buf; } INLINE_DEVICE_TREE char * printd_c_uw(const char *name, const char *c1, unsigned_word uw2) { int sizeof_buf = (strlen(name) + strlen("@") + c_strlen(c1) + strlen(",") + strlen_unsigned_word + 1); char *buf = (char*)zalloc(sizeof_buf); strcpy(buf, name); strcat(buf, "@"); c_strcat(buf, c1); strcat(buf, ","); u_strcat(buf, uw2); ASSERT(strlen(buf) < sizeof_buf); return buf; } #endif /* _DEVICE_TREE_C_ */