/* $NetBSD: ofw_subr.c,v 1.60 2022/01/22 11:49:18 thorpej Exp $ */ /* * Copyright (c) 2021 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright 1998 * Digital Equipment Corporation. All rights reserved. * * This software is furnished under license and may be used and * copied only in accordance with the following terms and conditions. * Subject to these conditions, you may download, copy, install, * use, modify and distribute this software in source and/or binary * form. No title or ownership is transferred hereby. * * 1) Any source code used, modified or distributed must reproduce * and retain this copyright notice and list of conditions as * they appear in the source file. * * 2) No right is granted to use any trade name, trademark, or logo of * Digital Equipment Corporation. Neither the "Digital Equipment * Corporation" name nor any trademark or logo of Digital Equipment * Corporation may be used to endorse or promote products derived * from this software without the prior written permission of * Digital Equipment Corporation. * * 3) This software is provided "AS-IS" and any express or implied * warranties, including but not limited to, any implied warranties * of merchantability, fitness for a particular purpose, or * non-infringement are disclaimed. In no event shall DIGITAL be * liable for any damages whatsoever, and in particular, DIGITAL * shall not be liable for special, indirect, consequential, or * incidental damages or damages for lost profits, loss of * revenue or loss of use, whether such damages arise in contract, * negligence, tort, under statute, in equity, at law or otherwise, * even if advised of the possibility of such damage. */ #include __KERNEL_RCSID(0, "$NetBSD: ofw_subr.c,v 1.60 2022/01/22 11:49:18 thorpej Exp $"); #include #include #include #include #include #include #define OFW_MAX_STACK_BUF_SIZE 256 #define OFW_PATH_BUF_SIZE 512 /* * OpenFirmware device handle support. */ static device_call_t of_devhandle_lookup_device_call(devhandle_t handle, const char *name, devhandle_t *call_handlep) { __link_set_decl(of_device_calls, struct device_call_descriptor); struct device_call_descriptor * const *desc; __link_set_foreach(desc, of_device_calls) { if (strcmp((*desc)->name, name) == 0) { return (*desc)->call; } } return NULL; } static const struct devhandle_impl of_devhandle_impl = { .type = DEVHANDLE_TYPE_OF, .lookup_device_call = of_devhandle_lookup_device_call, }; devhandle_t devhandle_from_of(devhandle_t super_handle, int phandle) { devhandle_type_t super_type = devhandle_type(super_handle); devhandle_t handle = { 0 }; if (super_type == DEVHANDLE_TYPE_OF) { handle.impl = super_handle.impl; } else { KASSERT(super_type == DEVHANDLE_TYPE_INVALID); handle.impl = &of_devhandle_impl; } handle.integer = phandle; return handle; } int devhandle_to_of(devhandle_t const handle) { KASSERT(devhandle_type(handle) == DEVHANDLE_TYPE_OF); return handle.integer; } static int of_device_enumerate_children(device_t dev, devhandle_t call_handle, void *v) { struct device_enumerate_children_args *args = v; int phandle = devhandle_to_of(call_handle); int child; for (child = OF_child(phandle); child != 0; child = OF_peer(child)) { if (!args->callback(dev, devhandle_from_of(call_handle, child), args->callback_arg)) { break; } } return 0; } OF_DEVICE_CALL_REGISTER(DEVICE_ENUMERATE_CHILDREN_STR, of_device_enumerate_children) /* * int of_decode_int(p) * * This routine converts OFW encoded-int datums * into the integer format of the host machine. * * It is primarily used to convert integer properties * returned by the OF_getprop routine. * * Arguments: * p pointer to unsigned char array which is an * OFW-encoded integer. * * Return Value: * Decoded integer value of argument p. * * Side Effects: * None. */ int of_decode_int(const unsigned char *p) { unsigned int i = *p++ << 8; i = (i + *p++) << 8; i = (i + *p++) << 8; return (i + *p); } /* * int of_compatible(phandle, strings) * * This routine checks an OFW node's "compatible" entry to see if * it matches any of the provided strings. * * of_compatible_match() is the preferred way to perform driver * compatibility match. However, this routine that deals with * only strings is useful in some situations and is provided for * convenience. * * Arguments: * phandle OFW phandle of device to be checked for * compatibility. * strings Array of containing expected "compatibility" * property values, presence of any of which * indicates compatibility. * * Return Value: * 0 if none of the strings are found in phandle's "compatibility" * property, or the reverse index of the matching string in the * phandle's "compatibility" property plus 1. * * Side Effects: * None. */ int of_compatible(int phandle, const char * const *strings) { char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE]; const char *cp; int proplen, match = 0; proplen = OF_getproplen(phandle, "compatible"); if (proplen <= 0) { return 0; } prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP); if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) { goto out; } for (; (cp = *strings) != NULL; strings++) { if ((match = strlist_match(prop, proplen, cp)) != 0) { break; } } out: kmem_tmpbuf_free(prop, proplen, propbuf); return match; } /* * int of_compatible_match(phandle, compat_data) * * This routine searches an array of device_compatible_entry structures * for a matching "compatible" entry matching the supplied OFW node, * and returns a weighted match value corresponding to which string * from the "compatible" property was matched, which more weight given * to the first string than the last. * * It should be used when determining whether a driver can drive * a particular device. * * Arguments: * phandle OFW phandle of device to be checked for * compatibility. * compat_data Array of possible compat entry strings and * associated metadata. The last entry in the * list should have a "compat" of NULL to terminate * the list. * * Return Value: * 0 if none of the strings are found in phandle's "compatibility" * property, or a positive number based on the reverse index of the * matching string in the phandle's "compatibility" property, plus 1. * * Side Effects: * None. */ int of_compatible_match(int phandle, const struct device_compatible_entry *compat_data) { char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE]; int proplen, match = 0; proplen = OF_getproplen(phandle, "compatible"); if (proplen <= 0) { return 0; } prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP); if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) { goto out; } match = device_compatible_match_strlist(prop, proplen, compat_data); out: kmem_tmpbuf_free(prop, proplen, propbuf); return match; } /* * const struct device_compatible_entry *of_compatible_lookup(phandle, * compat_data) * * This routine searches an array of device_compatible_entry structures * for a "compatible" entry matching the supplied OFW node. * * Arguments: * phandle OFW phandle of device to be checked for * compatibility. * compat_data Array of possible compat entry strings and * associated metadata. The last entry in the * list should have a "compat" of NULL to terminate * the list. * * Return Value: * The first matching compat_data entry in the array. If no matches * are found, NULL is returned. * * Side Effects: * None. */ const struct device_compatible_entry * of_compatible_lookup(int phandle, const struct device_compatible_entry *compat_data) { char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE]; const struct device_compatible_entry *match = NULL; int proplen; proplen = OF_getproplen(phandle, "compatible"); if (proplen <= 0) { return 0; } prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP); if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) { goto out; } match = device_compatible_lookup_strlist(prop, proplen, compat_data); out: kmem_tmpbuf_free(prop, proplen, propbuf); return match; } /* * int of_packagename(phandle, buf, bufsize) * * This routine places the last component of an OFW node's name * into a user-provided buffer. * * It can be used during autoconfiguration to make printing of * device names more informative. * * Arguments: * phandle OFW phandle of device whose name name is * desired. * buf Buffer to contain device name, provided by * caller. (For now, must be at least 4 * bytes long.) * bufsize Length of buffer referenced by 'buf', in * bytes. * * Return Value: * -1 if the device path name could not be obtained or would * not fit in the allocated temporary buffer, or zero otherwise * (meaning that the leaf node name was successfully extracted). * * Side Effects: * If the leaf node name was successfully extracted, 'buf' is * filled in with at most 'bufsize' bytes of the leaf node * name. If the leaf node was not successfully extracted, a * somewhat meaningful string is placed in the buffer. In * either case, the contents of 'buf' will be NUL-terminated. */ int of_packagename(int phandle, char *buf, int bufsize) { char *pbuf; const char *lastslash; int l, rv; pbuf = kmem_alloc(OFW_PATH_BUF_SIZE, KM_SLEEP); l = OF_package_to_path(phandle, pbuf, OFW_PATH_BUF_SIZE); /* check that we could get the name, and that it's not too long. */ if (l < 0 || (l == OFW_PATH_BUF_SIZE && pbuf[OFW_PATH_BUF_SIZE - 1] != '\0')) { if (bufsize >= 25) snprintf(buf, bufsize, "??? (phandle 0x%x)", phandle); else if (bufsize >= 4) strlcpy(buf, "???", bufsize); else panic("of_packagename: bufsize = %d is silly", bufsize); rv = -1; } else { pbuf[l] = '\0'; lastslash = strrchr(pbuf, '/'); strlcpy(buf, (lastslash == NULL) ? pbuf : (lastslash + 1), bufsize); rv = 0; } kmem_free(pbuf, OFW_PATH_BUF_SIZE); return (rv); } /* * Find the first child of a given node that matches name. Does not recurse. */ int of_find_firstchild_byname(int node, const char *name) { char namex[32]; int nn; for (nn = OF_child(node); nn; nn = OF_peer(nn)) { memset(namex, 0, sizeof(namex)); if (OF_getprop(nn, "name", namex, sizeof(namex)) == -1) continue; if (strcmp(name, namex) == 0) return nn; } return -1; } /* * Find a child node that is compatible with str. Recurses, starting at node. */ int of_find_bycompat(int node, const char *str) { const char * compatible[] = { str, NULL }; int child, ret; for (child = OF_child(node); child; child = OF_peer(child)) { if (of_compatible(child, compatible)) return child; ret = of_find_bycompat(child, str); if (ret != -1) return ret; } return -1; } /* * Find a give node by name. Recurses, and seems to walk upwards too. */ int of_getnode_byname(int start, const char *target) { int node, next; char name[64]; if (start == 0) start = OF_peer(0); for (node = start; node; node = next) { memset(name, 0, sizeof name); OF_getprop(node, "name", name, sizeof name - 1); if (strcmp(name, target) == 0) break; if ((next = OF_child(node)) != 0) continue; while (node) { if ((next = OF_peer(node)) != 0) break; node = OF_parent(node); } } /* XXX is this correct? */ return node; } /* * Create a uint32_t integer property from an OFW node property. */ bool of_to_uint32_prop(prop_dictionary_t dict, int node, const char *ofname, const char *propname) { uint32_t prop; if (OF_getprop(node, ofname, &prop, sizeof(prop)) != sizeof(prop)) return FALSE; return(prop_dictionary_set_uint32(dict, propname, prop)); } /* * Create a data property from an OFW node property. Max size of 256bytes. */ bool of_to_dataprop(prop_dictionary_t dict, int node, const char *ofname, const char *propname) { int len; uint8_t prop[256]; len = OF_getprop(node, ofname, prop, 256); if (len < 1) return FALSE; return prop_dictionary_set_data(dict, propname, prop, len); } /* * look at output-device, see if there's a Sun-typical video mode specifier as * in screen:r1024x768x60 attached. If found copy it into *buffer, otherwise * return NULL */ char * of_get_mode_string(char *buffer, int len) { int options; char *pos, output_device[256]; /* * finally, let's see if there's a video mode specified in * output-device and pass it on so there's at least some way * to program video modes */ options = OF_finddevice("/options"); if ((options == 0) || (options == -1)) return NULL; if (OF_getprop(options, "output-device", output_device, 256) == 0) return NULL; /* find the mode string if there is one */ pos = strstr(output_device, ":r"); if (pos == NULL) return NULL; strncpy(buffer, pos + 2, len); return buffer; } /* * of_device_from_phandle -- * * Return a device_t associated with the specified phandle. * * This is expected to be used rarely, so we don't care if * it's fast. Also, it can only find devices that have * gone through of_device_register() (obviously). */ device_t of_device_from_phandle(int phandle) { devhandle_t devhandle; deviter_t di; device_t dev; for (dev = deviter_first(&di, DEVITER_F_ROOT_FIRST); dev != NULL; dev = deviter_next(&di)) { devhandle = device_handle(dev); if (devhandle_type(devhandle) == DEVHANDLE_TYPE_OF) { if (devhandle_to_of(devhandle) == phandle) { /* Found it! */ break; } } } deviter_release(&di); return dev; } /* * Returns true if the specified property is present. */ bool of_hasprop(int node, const char *prop) { return OF_getproplen(node, prop) >= 0; } /* * Get the value of a uint32 property, compensating for host byte order. * Returns 0 on success, non-zero on failure. */ int of_getprop_uint32(int node, const char *prop, uint32_t *val) { uint32_t v; int len; len = OF_getprop(node, prop, &v, sizeof(v)); if (len != sizeof(v)) return -1; *val = be32toh(v); return 0; } int of_getprop_uint32_array(int node, const char *prop, uint32_t *array, int n) { uint32_t *v = array; int len; len = OF_getprop(node, prop, array, n * sizeof(*v)); if (len < (int)(n * sizeof(*v))) return -1; for (; n > 0; n--) { BE32TOH(*v); v++; } return 0; } /* * Get the value of a uint64 property, compensating for host byte order. * Returns 0 on success, non-zero on failure. */ int of_getprop_uint64(int node, const char *prop, uint64_t *val) { uint64_t v; int len; len = OF_getprop(node, prop, &v, sizeof(v)); if (len != sizeof(v)) return -1; *val = be64toh(v); return 0; }