ofw_subr.c revision 1.56 1 /* $NetBSD: ofw_subr.c,v 1.56 2021/02/04 20:19:09 thorpej Exp $ */
2
3 /*
4 * Copyright 1998
5 * Digital Equipment Corporation. All rights reserved.
6 *
7 * This software is furnished under license and may be used and
8 * copied only in accordance with the following terms and conditions.
9 * Subject to these conditions, you may download, copy, install,
10 * use, modify and distribute this software in source and/or binary
11 * form. No title or ownership is transferred hereby.
12 *
13 * 1) Any source code used, modified or distributed must reproduce
14 * and retain this copyright notice and list of conditions as
15 * they appear in the source file.
16 *
17 * 2) No right is granted to use any trade name, trademark, or logo of
18 * Digital Equipment Corporation. Neither the "Digital Equipment
19 * Corporation" name nor any trademark or logo of Digital Equipment
20 * Corporation may be used to endorse or promote products derived
21 * from this software without the prior written permission of
22 * Digital Equipment Corporation.
23 *
24 * 3) This software is provided "AS-IS" and any express or implied
25 * warranties, including but not limited to, any implied warranties
26 * of merchantability, fitness for a particular purpose, or
27 * non-infringement are disclaimed. In no event shall DIGITAL be
28 * liable for any damages whatsoever, and in particular, DIGITAL
29 * shall not be liable for special, indirect, consequential, or
30 * incidental damages or damages for lost profits, loss of
31 * revenue or loss of use, whether such damages arise in contract,
32 * negligence, tort, under statute, in equity, at law or otherwise,
33 * even if advised of the possibility of such damage.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ofw_subr.c,v 1.56 2021/02/04 20:19:09 thorpej Exp $");
38
39 #include <sys/param.h>
40 #include <sys/device.h>
41 #include <sys/kmem.h>
42 #include <sys/systm.h>
43 #include <dev/ofw/openfirm.h>
44
45 #define OFW_MAX_STACK_BUF_SIZE 256
46 #define OFW_PATH_BUF_SIZE 512
47
48 /*
49 * int of_decode_int(p)
50 *
51 * This routine converts OFW encoded-int datums
52 * into the integer format of the host machine.
53 *
54 * It is primarily used to convert integer properties
55 * returned by the OF_getprop routine.
56 *
57 * Arguments:
58 * p pointer to unsigned char array which is an
59 * OFW-encoded integer.
60 *
61 * Return Value:
62 * Decoded integer value of argument p.
63 *
64 * Side Effects:
65 * None.
66 */
67 int
68 of_decode_int(const unsigned char *p)
69 {
70 unsigned int i = *p++ << 8;
71 i = (i + *p++) << 8;
72 i = (i + *p++) << 8;
73 return (i + *p);
74 }
75
76 /*
77 * int of_compatible(phandle, strings)
78 *
79 * This routine checks an OFW node's "compatible" entry to see if
80 * it matches any of the provided strings.
81 *
82 * of_compatible_match() is the preferred way to perform driver
83 * compatibility match. However, this routine that deals with
84 * only strings is useful in some situations and is provided for
85 * convenience.
86 *
87 * Arguments:
88 * phandle OFW phandle of device to be checked for
89 * compatibility.
90 * strings Array of containing expected "compatibility"
91 * property values, presence of any of which
92 * indicates compatibility.
93 *
94 * Return Value:
95 * 0 if none of the strings are found in phandle's "compatibility"
96 * property, or the reverse index of the matching string in the
97 * phandle's "compatibility" property plus 1.
98 *
99 * Side Effects:
100 * None.
101 */
102 int
103 of_compatible(int phandle, const char * const *strings)
104 {
105 char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE];
106 const char *cp;
107 int proplen, match = 0;
108
109 proplen = OF_getproplen(phandle, "compatible");
110 if (proplen <= 0) {
111 return 0;
112 }
113
114 prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP);
115
116 if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) {
117 goto out;
118 }
119
120 for (; (cp = *strings) != NULL; strings++) {
121 if ((match = strlist_match(prop, proplen, cp)) != 0) {
122 break;
123 }
124 }
125
126 out:
127 kmem_tmpbuf_free(prop, proplen, propbuf);
128 return match;
129 }
130
131 /*
132 * int of_compatible_match(phandle, compat_data)
133 *
134 * This routine searches an array of device_compatible_entry structures
135 * for a matching "compatible" entry matching the supplied OFW node,
136 * and returns a weighted match value corresponding to which string
137 * from the "compatible" property was matched, which more weight given
138 * to the first string than the last.
139 *
140 * It should be used when determining whether a driver can drive
141 * a particular device.
142 *
143 * Arguments:
144 * phandle OFW phandle of device to be checked for
145 * compatibility.
146 * compat_data Array of possible compat entry strings and
147 * associated metadata. The last entry in the
148 * list should have a "compat" of NULL to terminate
149 * the list.
150 *
151 * Return Value:
152 * 0 if none of the strings are found in phandle's "compatibility"
153 * property, or a positive number based on the reverse index of the
154 * matching string in the phandle's "compatibility" property, plus 1.
155 *
156 * Side Effects:
157 * None.
158 */
159 int
160 of_compatible_match(int phandle,
161 const struct device_compatible_entry *compat_data)
162 {
163 char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE];
164 int proplen, match = 0;
165
166 proplen = OF_getproplen(phandle, "compatible");
167 if (proplen <= 0) {
168 return 0;
169 }
170
171 prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP);
172
173 if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) {
174 goto out;
175 }
176
177 match = device_compatible_match_strlist(prop, proplen, compat_data);
178
179 out:
180 kmem_tmpbuf_free(prop, proplen, propbuf);
181 return match;
182 }
183
184 /*
185 * const struct device_compatible_entry *of_compatible_lookup(phandle,
186 * compat_data)
187 *
188 * This routine searches an array of device_compatible_entry structures
189 * for a "compatible" entry matching the supplied OFW node.
190 *
191 * Arguments:
192 * phandle OFW phandle of device to be checked for
193 * compatibility.
194 * compat_data Array of possible compat entry strings and
195 * associated metadata. The last entry in the
196 * list should have a "compat" of NULL to terminate
197 * the list.
198 *
199 * Return Value:
200 * The first matching compat_data entry in the array. If no matches
201 * are found, NULL is returned.
202 *
203 * Side Effects:
204 * None.
205 */
206 const struct device_compatible_entry *
207 of_compatible_lookup(int phandle,
208 const struct device_compatible_entry *compat_data)
209 {
210 char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE];
211 const struct device_compatible_entry *match = NULL;
212 int proplen;
213
214 proplen = OF_getproplen(phandle, "compatible");
215 if (proplen <= 0) {
216 return 0;
217 }
218
219 prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP);
220
221 if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) {
222 goto out;
223 }
224
225 match = device_compatible_lookup_strlist(prop, proplen, compat_data);
226
227 out:
228 kmem_tmpbuf_free(prop, proplen, propbuf);
229 return match;
230 }
231
232 /*
233 * int of_packagename(phandle, buf, bufsize)
234 *
235 * This routine places the last component of an OFW node's name
236 * into a user-provided buffer.
237 *
238 * It can be used during autoconfiguration to make printing of
239 * device names more informative.
240 *
241 * Arguments:
242 * phandle OFW phandle of device whose name name is
243 * desired.
244 * buf Buffer to contain device name, provided by
245 * caller. (For now, must be at least 4
246 * bytes long.)
247 * bufsize Length of buffer referenced by 'buf', in
248 * bytes.
249 *
250 * Return Value:
251 * -1 if the device path name could not be obtained or would
252 * not fit in the allocated temporary buffer, or zero otherwise
253 * (meaning that the leaf node name was successfully extracted).
254 *
255 * Side Effects:
256 * If the leaf node name was successfully extracted, 'buf' is
257 * filled in with at most 'bufsize' bytes of the leaf node
258 * name. If the leaf node was not successfully extracted, a
259 * somewhat meaningful string is placed in the buffer. In
260 * either case, the contents of 'buf' will be NUL-terminated.
261 */
262 int
263 of_packagename(int phandle, char *buf, int bufsize)
264 {
265 char *pbuf;
266 const char *lastslash;
267 int l, rv;
268
269 pbuf = kmem_alloc(OFW_PATH_BUF_SIZE, KM_SLEEP);
270 l = OF_package_to_path(phandle, pbuf, OFW_PATH_BUF_SIZE);
271
272 /* check that we could get the name, and that it's not too long. */
273 if (l < 0 ||
274 (l == OFW_PATH_BUF_SIZE && pbuf[OFW_PATH_BUF_SIZE - 1] != '\0')) {
275 if (bufsize >= 25)
276 snprintf(buf, bufsize, "??? (phandle 0x%x)", phandle);
277 else if (bufsize >= 4)
278 strlcpy(buf, "???", bufsize);
279 else
280 panic("of_packagename: bufsize = %d is silly",
281 bufsize);
282 rv = -1;
283 } else {
284 pbuf[l] = '\0';
285 lastslash = strrchr(pbuf, '/');
286 strlcpy(buf, (lastslash == NULL) ? pbuf : (lastslash + 1),
287 bufsize);
288 rv = 0;
289 }
290
291 kmem_free(pbuf, OFW_PATH_BUF_SIZE);
292 return (rv);
293 }
294
295 /*
296 * Find the first child of a given node that matches name. Does not recurse.
297 */
298 int
299 of_find_firstchild_byname(int node, const char *name)
300 {
301 char namex[32];
302 int nn;
303
304 for (nn = OF_child(node); nn; nn = OF_peer(nn)) {
305 memset(namex, 0, sizeof(namex));
306 if (OF_getprop(nn, "name", namex, sizeof(namex)) == -1)
307 continue;
308 if (strcmp(name, namex) == 0)
309 return nn;
310 }
311 return -1;
312 }
313
314 /*
315 * Find a child node that is compatible with str. Recurses, starting at node.
316 */
317 int
318 of_find_bycompat(int node, const char *str)
319 {
320 const char * compatible[] = { str, NULL };
321 int child, ret;
322
323 for (child = OF_child(node); child; child = OF_peer(child)) {
324 if (of_compatible(child, compatible))
325 return child;
326 ret = of_find_bycompat(child, str);
327 if (ret != -1)
328 return ret;
329 }
330
331 return -1;
332 }
333
334 /*
335 * Find a give node by name. Recurses, and seems to walk upwards too.
336 */
337
338 int
339 of_getnode_byname(int start, const char *target)
340 {
341 int node, next;
342 char name[64];
343
344 if (start == 0)
345 start = OF_peer(0);
346
347 for (node = start; node; node = next) {
348 memset(name, 0, sizeof name);
349 OF_getprop(node, "name", name, sizeof name - 1);
350 if (strcmp(name, target) == 0)
351 break;
352
353 if ((next = OF_child(node)) != 0)
354 continue;
355
356 while (node) {
357 if ((next = OF_peer(node)) != 0)
358 break;
359 node = OF_parent(node);
360 }
361 }
362
363 /* XXX is this correct? */
364 return node;
365 }
366
367 /*
368 * Create a uint32_t integer property from an OFW node property.
369 */
370
371 bool
372 of_to_uint32_prop(prop_dictionary_t dict, int node, const char *ofname,
373 const char *propname)
374 {
375 uint32_t prop;
376
377 if (OF_getprop(node, ofname, &prop, sizeof(prop)) != sizeof(prop))
378 return FALSE;
379
380 return(prop_dictionary_set_uint32(dict, propname, prop));
381 }
382
383 /*
384 * Create a data property from an OFW node property. Max size of 256bytes.
385 */
386
387 bool
388 of_to_dataprop(prop_dictionary_t dict, int node, const char *ofname,
389 const char *propname)
390 {
391 int len;
392 uint8_t prop[256];
393
394 len = OF_getprop(node, ofname, prop, 256);
395 if (len < 1)
396 return FALSE;
397
398 return prop_dictionary_set_data(dict, propname, prop, len);
399 }
400
401 /*
402 * look at output-device, see if there's a Sun-typical video mode specifier as
403 * in screen:r1024x768x60 attached. If found copy it into *buffer, otherwise
404 * return NULL
405 */
406
407 char *
408 of_get_mode_string(char *buffer, int len)
409 {
410 int options;
411 char *pos, output_device[256];
412
413 /*
414 * finally, let's see if there's a video mode specified in
415 * output-device and pass it on so there's at least some way
416 * to program video modes
417 */
418 options = OF_finddevice("/options");
419 if ((options == 0) || (options == -1))
420 return NULL;
421 if (OF_getprop(options, "output-device", output_device, 256) == 0)
422 return NULL;
423
424 /* find the mode string if there is one */
425 pos = strstr(output_device, ":r");
426 if (pos == NULL)
427 return NULL;
428 strncpy(buffer, pos + 2, len);
429 return buffer;
430 }
431
432 /*
433 * Returns true if the specified property is present.
434 */
435 bool
436 of_hasprop(int node, const char *prop)
437 {
438 return OF_getproplen(node, prop) >= 0;
439 }
440
441 /*
442 * Get the value of a uint32 property, compensating for host byte order.
443 * Returns 0 on success, non-zero on failure.
444 */
445 int
446 of_getprop_uint32(int node, const char *prop, uint32_t *val)
447 {
448 uint32_t v;
449 int len;
450
451 len = OF_getprop(node, prop, &v, sizeof(v));
452 if (len != sizeof(v))
453 return -1;
454
455 *val = be32toh(v);
456 return 0;
457 }
458
459 int
460 of_getprop_uint32_array(int node, const char *prop, uint32_t *array, int n)
461 {
462 uint32_t *v = array;
463 int len;
464
465 len = OF_getprop(node, prop, array, n * sizeof(*v));
466 if (len < (int)(n * sizeof(*v)))
467 return -1;
468
469 for (; n > 0; n--) {
470 BE32TOH(*v);
471 v++;
472 }
473
474 return 0;
475 }
476 /*
477 * Get the value of a uint64 property, compensating for host byte order.
478 * Returns 0 on success, non-zero on failure.
479 */
480 int
481 of_getprop_uint64(int node, const char *prop, uint64_t *val)
482 {
483 uint64_t v;
484 int len;
485
486 len = OF_getprop(node, prop, &v, sizeof(v));
487 if (len != sizeof(v))
488 return -1;
489
490 *val = be64toh(v);
491 return 0;
492 }
493