2
* Copyright (c) 2006 Jakub Jermar
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
9
* - Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* - Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
* - The name of the author may not be used to endorse or promote products
15
* derived from this software without specific prior written permission.
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
#define MAX_PATH_LEN 256
38
static ofw_tree_node_t *ofw_tree_node_alloc(void)
40
return balloc(sizeof(ofw_tree_node_t), sizeof(ofw_tree_node_t));
43
static ofw_tree_property_t *ofw_tree_properties_alloc(unsigned count)
45
return balloc(count * sizeof(ofw_tree_property_t),
46
sizeof(ofw_tree_property_t));
49
static void *ofw_tree_space_alloc(size_t size)
54
* What we do here is a nasty hack :-)
55
* Problem: string property values that are allocated via this
56
* function typically do not contain the trailing '\0'. This
57
* is very uncomfortable for kernel, which is supposed to deal
58
* with the properties.
59
* Solution: when allocating space via this function, we always
60
* allocate space for the extra '\0' character that we store
61
* behind the requested memory.
63
addr = balloc(size + 1, size);
69
/** Transfer information from one OpenFirmware node into its memory
72
* Transfer entire information from the OpenFirmware device tree 'current' node
73
* to its memory representation in 'current_node'. This function recursively
74
* processes all node's children. Node's peers are processed iteratively in
75
* order to prevent stack from overflowing.
77
* @param current_node Pointer to uninitialized ofw_tree_node structure that
78
* will become the memory represenation of 'current'.
79
* @param parent_node Parent ofw_tree_node structure or NULL in case of root
81
* @param current OpenFirmware phandle to the current device tree node.
83
static void ofw_tree_node_process(ofw_tree_node_t *current_node,
84
ofw_tree_node_t *parent_node, phandle current)
86
static char path[MAX_PATH_LEN + 1];
87
static char name[OFW_TREE_PROPERTY_MAX_NAMELEN];
88
static char name2[OFW_TREE_PROPERTY_MAX_NAMELEN];
94
while (current_node) {
98
current_node->parent = parent_node;
99
current_node->peer = NULL;
100
current_node->child = NULL;
101
current_node->node_handle = current;
102
current_node->properties = 0;
103
current_node->property = NULL;
104
current_node->device = NULL;
107
* Get the disambigued name.
109
len = ofw_package_to_path(current, path, MAX_PATH_LEN);
114
for (i = len - 1; i >= 0 && path[i] != '/'; i--)
116
i++; /* do not include '/' */
120
/* add space for trailing '\0' */
121
current_node->da_name = ofw_tree_space_alloc(len + 1);
122
if (!current_node->da_name)
125
memcpy(current_node->da_name, &path[i], len);
126
current_node->da_name[len] = '\0';
129
* Recursively process the potential child node.
131
child = ofw_get_child_node(current);
132
if (child != 0 && child != -1) {
133
ofw_tree_node_t *child_node;
135
child_node = ofw_tree_node_alloc();
137
ofw_tree_node_process(child_node, current_node,
139
current_node->child = child_node;
147
while (ofw_next_property(current, name, name2) == 1) {
148
current_node->properties++;
149
memcpy(name, name2, OFW_TREE_PROPERTY_MAX_NAMELEN);
152
if (!current_node->properties)
158
current_node->property =
159
ofw_tree_properties_alloc(current_node->properties);
160
if (!current_node->property)
164
for (i = 0; ofw_next_property(current, name, name2) == 1; i++) {
167
if (i == current_node->properties)
170
memcpy(name, name2, OFW_TREE_PROPERTY_MAX_NAMELEN);
171
memcpy(current_node->property[i].name, name,
172
OFW_TREE_PROPERTY_MAX_NAMELEN);
173
current_node->property[i].name[
174
OFW_TREE_PROPERTY_MAX_NAMELEN] = '\0';
176
size = ofw_get_proplen(current, name);
177
current_node->property[i].size = size;
181
buf = ofw_tree_space_alloc(size);
182
if (current_node->property[i].value = buf) {
184
* Copy property value to memory node.
186
(void) ofw_get_property(current, name,
190
current_node->property[i].value = NULL;
194
/* Just in case we ran out of memory. */
195
current_node->properties = i;
198
* Iteratively process the next peer node.
199
* Note that recursion is a bad idea here.
200
* Due to the topology of the OpenFirmware device tree,
201
* the nesting of peer nodes could be to wide and the
202
* risk of overflowing the stack is too real.
204
peer = ofw_get_peer_node(current);
205
if (peer != 0 && peer != -1) {
206
ofw_tree_node_t *peer_node;
208
peer_node = ofw_tree_node_alloc();
210
current_node->peer = peer_node;
211
current_node = peer_node;
214
* Process the peer in next iteration.
220
* No more peers on this level.
226
/** Construct memory representation of OpenFirmware device tree.
228
* @return NULL on failure or pointer to the root node.
230
ofw_tree_node_t *ofw_tree_build(void)
232
ofw_tree_node_t *root;
234
ofw_tree_node_t *ssm;
236
root = ofw_tree_node_alloc();
238
ofw_tree_node_process(root, NULL, ofw_root);
241
* The firmware client interface does not automatically include the
242
* "ssm" node in the list of children of "/". A nasty yet working
243
* solution is to explicitly stick "ssm" to the OFW tree.
245
ssm_node = ofw_find_device("/ssm@0,0");
246
if (ssm_node != -1) {
247
ssm = ofw_tree_node_alloc();
249
ofw_tree_node_process(ssm, root,
250
ofw_find_device("/ssm@0,0"));
251
ssm->peer = root->child;