1/* $NetBSD: fdt_openfirm.c,v 1.2 2015/12/16 12:17:45 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: fdt_openfirm.c,v 1.2 2015/12/16 12:17:45 jmcneill Exp $");
31
32#include <sys/param.h>
33
34#include <libfdt.h>
35#include <dev/fdt/fdtvar.h>
36
37int
38OF_peer(int phandle)
39{
40	const void *fdt_data = fdtbus_get_data();
41	int off, depth;
42
43	if (fdt_data == NULL) {
44		return -1;
45	}
46
47	if (phandle == 0) {
48		return fdtbus_offset2phandle(0);
49	}
50
51	off = fdtbus_phandle2offset(phandle);
52	if (off < 0) {
53		return 0;
54	}
55
56	depth = 1;
57	for (off = fdt_next_node(fdt_data, off, &depth);
58	     off >= 0 && depth >= 0;
59	     off = fdt_next_node(fdt_data, off, &depth)) {
60		if (depth == 1) {
61			return fdtbus_offset2phandle(off);
62		}
63	}
64
65	return 0;
66}
67
68int
69OF_child(int phandle)
70{
71	const void *fdt_data = fdtbus_get_data();
72	int off, depth;
73
74	if (fdt_data == NULL) {
75		return -1;
76	}
77
78	off = fdtbus_phandle2offset(phandle);
79	if (off < 0) {
80		return 0;
81	}
82
83	depth = 0;
84	for (off = fdt_next_node(fdt_data, off, &depth);
85	     off >= 0 && depth > 0;
86	     off = fdt_next_node(fdt_data, off, &depth)) {
87		if (depth == 1) {
88			return fdtbus_offset2phandle(off);
89		}
90	}
91
92	return 0;
93}
94
95int
96OF_parent(int phandle)
97{
98	const void *fdt_data = fdtbus_get_data();
99	int off;
100
101	if (fdt_data == NULL) {
102		return -1;
103	}
104
105	off = fdtbus_phandle2offset(phandle);
106	if (off < 0) {
107		return -1;
108	}
109
110	off = fdt_parent_offset(fdt_data, off);
111	if (off < 0) {
112		return -1;
113	}
114
115	return fdtbus_offset2phandle(off);
116}
117
118int
119OF_nextprop(int phandle, const char *prop, void *nextprop)
120{
121	const void *fdt_data = fdtbus_get_data();
122	const char *name;
123	const void *val;
124	int off, len;
125
126	if (fdt_data == NULL) {
127		return -1;
128	}
129
130	off = fdtbus_phandle2offset(phandle);
131	if (off < 0) {
132		return -1;
133	}
134
135	if (*prop == '\0') {
136		name = "name";
137	} else {
138		off = fdt_first_property_offset(fdt_data, off);
139		if (off < 0) {
140			return 0;
141		}
142		if (strcmp(prop, "name") != 0) {
143			while (off >= 0) {
144				val = fdt_getprop_by_offset(fdt_data, off,
145				    &name, &len);
146				if (val == NULL) {
147					return -1;
148				}
149				off = fdt_next_property_offset(fdt_data, off);
150				if (off < 0) {
151					return 0;
152				}
153				if (strcmp(name, prop) == 0)
154					break;
155			}
156		}
157		val = fdt_getprop_by_offset(fdt_data, off, &name, &len);
158		if (val == NULL) {
159			return -1;
160		}
161	}
162
163	strlcpy(nextprop, name, 33);
164
165	return 1;
166}
167
168int
169OF_getprop(int phandle, const char *prop, void *buf, int buflen)
170{
171	const void *fdt_data = fdtbus_get_data();
172	const char *name;
173	const void *val;
174	int off, len;
175
176	if (fdt_data == NULL) {
177		return -1;
178	}
179
180	off = fdtbus_phandle2offset(phandle);
181	if (off < 0) {
182		return -1;
183	}
184
185	if (strcmp(prop, "name") == 0) {
186		val = fdt_get_name(fdt_data, off, &len);
187		if (val) {
188			const char *p = strchr(val, '@');
189			if (p) {
190				len = (uintptr_t)p - (uintptr_t)val + 1;
191			} else {
192				len += 1;
193			}
194		}
195		if (val == NULL || len > buflen) {
196			return -1;
197		}
198		char *s = buf;
199		memcpy(buf, val, len - 1);
200		s[len - 1] = '\0';
201	} else {
202		off = fdt_first_property_offset(fdt_data, off);
203		if (off < 0) {
204			return -1;
205		}
206		while (off >= 0) {
207			val = fdt_getprop_by_offset(fdt_data, off, &name, &len);
208			if (val == NULL) {
209				return -1;
210			}
211			if (strcmp(name, prop) == 0) {
212				break;
213			}
214			off = fdt_next_property_offset(fdt_data, off);
215			if (off < 0) {
216				return -1;
217			}
218		}
219		if (val == NULL || len > buflen) {
220			return -1;
221		}
222		memcpy(buf, val, len);
223	}
224
225	return len;
226}
227
228int
229OF_getproplen(int phandle, const char *prop)
230{
231	const void *fdt_data = fdtbus_get_data();
232	const char *name;
233	const void *val;
234	int off, len;
235
236	if (fdt_data == NULL) {
237		return -1;
238	}
239
240	off = fdtbus_phandle2offset(phandle);
241	if (off < 0) {
242		return -1;
243	}
244
245	if (strcmp(prop, "name") == 0) {
246		val = fdt_get_name(fdt_data, off, &len);
247		if (val) {
248			const char *p = strchr(val, '@');
249			if (p) {
250				len = (uintptr_t)p - (uintptr_t)val + 1;
251			} else {
252				len += 1;
253			}
254		}
255	} else {
256		off = fdt_first_property_offset(fdt_data, off);
257		if (off < 0) {
258			return -1;
259		}
260		while (off >= 0) {
261			val = fdt_getprop_by_offset(fdt_data, off, &name, &len);
262			if (val == NULL) {
263				return -1;
264			}
265			if (strcmp(name, prop) == 0) {
266				break;
267			}
268			off = fdt_next_property_offset(fdt_data, off);
269			if (off < 0) {
270				return -1;
271			}
272		}
273	}
274	if (val == NULL) {
275		return -1;
276	}
277
278	return len;
279}
280
281int
282OF_setprop(int phandle, const char *prop, const void *buf, int buflen)
283{
284	return -1;
285}
286
287int
288OF_finddevice(const char *name)
289{
290	const void *fdt_data = fdtbus_get_data();
291	int off;
292
293	if (fdt_data == NULL) {
294		return -1;
295	}
296
297	off = fdt_path_offset(fdt_data, name);
298	if (off < 0) {
299		return -1;
300	}
301
302	return fdtbus_offset2phandle(off);
303}
304
305int
306OF_package_to_path(int phandle, char *buf, int buflen)
307{
308	const void *fdt_data = fdtbus_get_data();
309	int off;
310
311	if (fdt_data == NULL) {
312		return -1;
313	}
314
315	off = fdtbus_phandle2offset(phandle);
316	if (off < 0) {
317		return -1;
318	}
319
320	if (fdt_get_path(fdt_data, off, buf, buflen) != 0)
321		return -1;
322
323	return strlen(buf);
324}
325