Index: arch/sparc/include/promlib.h =================================================================== RCS file: /cvsroot/src/sys/arch/sparc/include/promlib.h,v retrieving revision 1.26 diff -u -p -r1.26 promlib.h --- arch/sparc/include/promlib.h 10 May 2021 13:59:30 -0000 1.26 +++ arch/sparc/include/promlib.h 17 Aug 2021 01:10:44 -0000 @@ -103,6 +103,7 @@ struct promops { /* Device node traversal (OBP v0, v2, v3; but not sun4) */ int (*po_firstchild)(int); int (*po_nextsibling)(int); + int (*po_parent)(int); /* OpenFirmware only */ /* Device node properties */ int (*po_getproplen)(int, const char *); @@ -204,6 +205,7 @@ void prom_boot(char *) __attribute__((__ /* Node traversal */ #define prom_firstchild(node) ((*promops.po_firstchild)(node)) #define prom_nextsibling(node) ((*promops.po_nextsibling)(node)) +#define prom_parent(node) ((*promops.po_parent)(node)) #define prom_proplen(node,name) ((*promops.po_getproplen)(node, name)) #define _prom_getprop(node, name, buf, len) \ ((*promops.po_getprop)(node, name, buf, len)) Index: arch/sparc/sparc/promlib.c =================================================================== RCS file: /cvsroot/src/sys/arch/sparc/sparc/promlib.c,v retrieving revision 1.48 diff -u -p -r1.48 promlib.c --- arch/sparc/sparc/promlib.c 10 May 2021 13:59:30 -0000 1.48 +++ arch/sparc/sparc/promlib.c 17 Aug 2021 01:10:44 -0000 @@ -112,6 +112,17 @@ null_devhandle_to_prom(devhandle_t devha } #endif /* ! _STANDALONE */ +static int +nullnode(int node) +{ + /* + * This is the default routine for (*po_parent)(). OBP + * does not support this operation, so we just terminate + * the search by returning the null node. + */ + return 0; +} + /* * PROM entry points. * Note: only PROM functions we use ar represented here; add as required. @@ -155,6 +166,7 @@ struct promops promops = { (void *)notimplemented, /* firstchild */ (void *)notimplemented, /* nextsibling */ + (void *)nullnode, /* parent */ (void *)notimplemented, /* getproplen */ (void *)notimplemented, /* getprop */ @@ -278,6 +290,167 @@ obp_device_enumerate_children(device_t d } OBP_DEVICE_CALL_REGISTER("device-enumerate-children", obp_device_enumerate_children) + +static bool +obp_boolean_prop(int node, const char *prop, bool *valp) +{ + char buf[sizeof("false")] = { }; + int propsize; + + /* + * Alas, while this is the convention, apparently it is not + * universal, so we'll be liberal in what we accept. + */ +#if 0 + /* + * Naming convention is "propname?" is a boolean property + * with a "true" or "false" value. + */ + if (prop[strlen(prop) - 1] != '?') { + return false; + } +#endif + + propsize = _prom_getprop(node, prop, buf, sizeof(buf)); + if (propsize < 4) { + return false; + } + + if (propsize >= 4 && memcmp(buf, "true", 4) == 0) { + *valp = true; + return true; + } + if (propsize >= 5 && memcmp(buf, "false", 5) == 0) { + *valp = false; + return true; + } + return false; +} + +static int +obp_device_get_property(device_t dev, devhandle_t call_handle, void *v) +{ + struct device_get_property_args *args = v; + int node = devhandle_to_obp(call_handle); + int propsize, rv = -1; + int buflen; + bool boolval = true; + prop_type_t proptype = PROP_TYPE_UNKNOWN; + + again: + propsize = prom_getproplen(node, args->prop); + if (propsize < 0) { + if (args->flags & DEVICE_GETPROP_RECURSE) { + /* + * Get the parent node and try again. + * + * N.B. OpenBoot does not support parent traversal, + * and thus it is extremely unlikely that we'll be + * missing any recursively-inherited properties here. + * However, OpenFirmware does support it, so we'll + * make the effort. + */ + node = prom_parent(node); + if (node != 0) { + goto again; + } + } + return ENOENT; + } + + /* + * Check for the boolean property naming convention, and + * cache the value while we're at it. + */ + if (obp_boolean_prop(node, args->prop, &boolval)) { + proptype = PROP_TYPE_BOOL; + } + + if (args->buf == NULL) { + goto done; + } + KASSERT(args->buflen != 0); + + /* Sizes must fit in int. */ + if (args->buflen > INT_MAX) { + args->buflen = INT_MAX; + } + + /* We may have to make size adjustments... */ + buflen = (int)args->buflen; + + switch (args->reqtype) { + case PROP_TYPE_NUMBER: + if ((propsize == sizeof(uint32_t) || + propsize == sizeof(uint64_t)) && + buflen == propsize) { + rv = _prom_getprop(node, args->prop, args->buf, + buflen); + } else if (propsize == sizeof(uint32_t) && + buflen == sizeof(uint64_t)) { + uint32_t val32; + rv = _prom_getprop(node, args->prop, &val32, + sizeof(val32)); + if (rv == sizeof(uint32_t)) { + *(uint64_t *)args->buf = val32; + } + } else { + /* Looks like the wrong type. */ + return EFTYPE; + } + break; + + case PROP_TYPE_STRING: + /* + * If we're fetching as a string, we need to be careful + * with NUL-termination; the property may or may not be + * stored with the NUL already. So, first we pre-zero + * the entire buffer, and then decrement the buffer + * length by 1, thus ensuring that there will always + * by a NUL present. + */ + memset(args->buf, 0, buflen--); + if (__predict_false(buflen == 0)) { + /* Unlikely, but just in case... */ + rv = propsize; + break; + } + /* FALLTHROUGH */ + + case PROP_TYPE_DATA: + rv = _prom_getprop(node, args->prop, args->buf, buflen); + break; + + case PROP_TYPE_BOOL: + /* + * If we noticed the boolean property naming convention, + * use the cached value from earlier. Otherwise, presence + * of the property -> true. + */ + if (proptype == PROP_TYPE_BOOL) { + *(bool *)args->buf = boolval; + } else { + *(bool *)args->buf = true; + } + rv = propsize; + break; + + default: + return EFTYPE; + } + + if (rv != propsize) { + /* Something has gone off the rails. */ + return EIO; + } + + done: + args->propsize = propsize; + args->encoding = _BYTE_ORDER; /* i.e. _BIG_ENDIAN */ + args->type = proptype; + return 0; +} +OBP_DEVICE_CALL_REGISTER("device-get-property", obp_device_get_property) #endif /* ! _STANDALONE */ /* @@ -1372,6 +1545,7 @@ prom_init_opf(void) promops.po_firstchild = OF_child; promops.po_nextsibling = OF_peer; + promops.po_parent = OF_parent; promops.po_getproplen = OF_getproplen; promops.po_getprop = OF_getprop; promops.po_nextprop = opf_nextprop; Index: dev/acpi/acpi_util.c =================================================================== RCS file: /cvsroot/src/sys/dev/acpi/acpi_util.c,v retrieving revision 1.25 diff -u -p -r1.25 acpi_util.c --- dev/acpi/acpi_util.c 9 Aug 2021 20:49:09 -0000 1.25 +++ dev/acpi/acpi_util.c 17 Aug 2021 01:10:45 -0000 @@ -895,7 +895,7 @@ acpi_dsd_property(ACPI_HANDLE handle, co if (strcmp(propkey->String.Pointer, prop) != 0) continue; - if (propval->Type != type) { + if (type != ACPI_TYPE_ANY && propval->Type != type) { return AE_TYPE; } else { *ret = propval; @@ -945,6 +945,169 @@ acpi_dsd_string(ACPI_HANDLE handle, cons return rv; } +static bool +prop_type_to_acpi(prop_type_t type, ACPI_OBJECT_TYPE *out) +{ + switch (type) { + case PROP_TYPE_NUMBER: + case PROP_TYPE_BOOL: + *out = ACPI_TYPE_INTEGER; + break; + + case PROP_TYPE_DATA: + *out = ACPI_TYPE_BUFFER; + break; + + case PROP_TYPE_STRING: + *out = ACPI_TYPE_STRING; + break; + + case PROP_TYPE_UNKNOWN: + *out = ACPI_TYPE_ANY; + break; + + default: + return false; + } + + return true; +} + +static bool +acpi_type_to_prop(ACPI_OBJECT_TYPE acpitype, prop_type_t *out) +{ + switch (acpitype) { + case ACPI_TYPE_INTEGER: + *out = PROP_TYPE_NUMBER; + break; + + case ACPI_TYPE_BUFFER: + *out = PROP_TYPE_DATA; + break; + + case PROP_TYPE_STRING: + *out = PROP_TYPE_STRING; + break; + + default: + return false; + } + + return true; +} + +static int +acpi_device_get_property(device_t dev, devhandle_t call_handle, void *v) +{ + struct device_get_property_args *args = v; + ACPI_HANDLE hdl = devhandle_to_acpi(call_handle); + ACPI_OBJECT *propval; + ACPI_OBJECT_TYPE acpitype; + ACPI_STATUS rv; + ACPI_BUFFER buf; + int error = 0; + size_t copysize; + + /* + * No need to clamp size; ACPI sizes are unsigned (UINT32), + * and the upper layer has already clamped to fit in ssize_t. + */ + + if (! prop_type_to_acpi(args->reqtype, &acpitype)) { + return EFTYPE; + } + + again: + buf.Pointer = NULL; + buf.Length = ACPI_ALLOCATE_BUFFER; + + rv = acpi_dsd_property(hdl, args->prop, &buf, acpitype, &propval); + if (!ACPI_SUCCESS(rv)) { + if (rv == AE_TYPE) { + error = EFTYPE; + goto out; + } + + if (args->flags & DEVICE_GETPROP_RECURSE) { + /* Get the parent handle and try again. */ + rv = AcpiGetParent(hdl, &hdl); + if (ACPI_SUCCESS(rv)) { + if (buf.Pointer != NULL) { + ACPI_FREE(buf.Pointer); + } + goto again; + } + } + + error = ENOENT; + goto out; + } + + args->encoding = _BYTE_ORDER; + if (! acpi_type_to_prop(propval->Type, &args->type)) { + error = EFTYPE; + goto out; + } + + switch (propval->Type) { + case ACPI_TYPE_INTEGER: + args->propsize = sizeof(propval->Integer.Value); + if (args->buf != NULL) { + if (args->reqtype == PROP_TYPE_BOOL) { + *(bool *)args->buf = + propval->Integer.Value != 0; + goto out; + } + KASSERT(args->reqtype == PROP_TYPE_NUMBER); + if (args->buflen == sizeof(uint32_t)) { + if (propval->Integer.Value > UINT32_MAX) { + error = ERANGE; + goto out; + } + *(uint32_t *)args->buf = + (uint32_t)propval->Integer.Value; + } else if (args->buflen == sizeof(uint64_t)) { + *(uint64_t *)args->buf = propval->Integer.Value; + } else { + error = EFTYPE; /* wut? */ + } + } + break; + + case ACPI_TYPE_STRING: + /* +1 for trailing NUL */ + args->propsize = propval->String.Length + 1; + if (args->buf != NULL) { + KASSERT(args->reqtype == PROP_TYPE_STRING); + strlcpy(args->buf, propval->String.Pointer, + args->buflen); + } + break; + + case ACPI_TYPE_BUFFER: + copysize = args->propsize = propval->Buffer.Length; + if (args->buf != NULL) { + KASSERT(args->reqtype == PROP_TYPE_DATA); + if (args->buflen < copysize) { + copysize = args->buflen; + } + memcpy(args->buf, propval->Buffer.Pointer, copysize); + } + break; + + default: + error = EFTYPE; + goto out; + } + + out: + if (buf.Pointer != NULL) { + ACPI_FREE(buf.Pointer); + } + return error; +} +ACPI_DEVICE_CALL_REGISTER("device-get-property", acpi_device_get_property) + /* * Device Specific Method (_DSM) support */ Index: dev/ofw/ofw_subr.c =================================================================== RCS file: /cvsroot/src/sys/dev/ofw/ofw_subr.c,v retrieving revision 1.58 diff -u -p -r1.58 ofw_subr.c --- dev/ofw/ofw_subr.c 24 Apr 2021 23:36:57 -0000 1.58 +++ dev/ofw/ofw_subr.c 17 Aug 2021 01:10:45 -0000 @@ -133,6 +133,162 @@ of_device_enumerate_children(device_t de OF_DEVICE_CALL_REGISTER("device-enumerate-children", of_device_enumerate_children) +static bool +of_boolean_prop(int phandle, const char *prop, bool *valp) +{ + char buf[sizeof("false")] = { }; + int propsize; + + /* + * Alas, while this is the convention, apparently it is not + * universal, so we'll be liberal in what we accept. + */ +#if 0 + /* + * Naming convention is "propname?" is a boolean property + * with a "true" or "false" value. + */ + if (prop[strlen(prop) - 1] != '?') { + return false; + } +#endif + + propsize = OF_getprop(phandle, prop, buf, sizeof(buf)); + if (propsize < 4) { + return false; + } + + if (propsize >= 4 && memcmp(buf, "true", 4) == 0) { + *valp = true; + return true; + } + if (propsize >= 5 && memcmp(buf, "false", 5) == 0) { + *valp = false; + return true; + } + return false; +} + +static int +of_device_get_property(device_t dev, devhandle_t call_handle, void *v) +{ + struct device_get_property_args *args = v; + int phandle = devhandle_to_of(call_handle); + int propsize, rv = -1; + int buflen; + bool boolval = true; + prop_type_t proptype = PROP_TYPE_UNKNOWN; + + again: + propsize = OF_getproplen(phandle, args->prop); + if (propsize < 0) { + if (args->flags & DEVICE_GETPROP_RECURSE) { + /* Get the parent node and try again. */ + phandle = OF_parent(phandle); + if (phandle != 0) { + goto again; + } + } + return ENOENT; + } + + /* + * Check for the boolean property naming convention, and + * cache the value while we're at it. + */ + if (of_boolean_prop(phandle, args->prop, &boolval)) { + proptype = PROP_TYPE_BOOL; + } + + if (args->buf == NULL) { + goto done; + } + KASSERT(args->buflen != 0); + + /* Sizes must fit in int. */ + if (args->buflen > INT_MAX) { + args->buflen = INT_MAX; + } + + /* We may have to make size adjustments... */ + buflen = (int)args->buflen; + + switch (args->reqtype) { + case PROP_TYPE_NUMBER: + if (propsize == sizeof(uint32_t) && buflen == propsize) { + rv = of_getprop_uint32(phandle, args->prop, args->buf); + } else if (propsize == sizeof(uint64_t) && + buflen == propsize) { + rv = of_getprop_uint64(phandle, args->prop, args->buf); + } else if (propsize == sizeof(uint32_t) && + buflen == sizeof(uint64_t)) { + uint32_t val32; + rv = of_getprop_uint32(phandle, args->prop, &val32); + if (rv == 0) { + *(uint64_t *)args->buf = val32; + } + } else { + /* Looks like the wrong type. */ + return EFTYPE; + } + if (rv == 0) { + /* of_getprop_uintNN() returns 0, not prop length. */ + rv = propsize; + } + break; + + case PROP_TYPE_STRING: + /* + * If we're fetching as a string, we need to be careful + * with NUL-termination; some OpenFirmware impleementations + * are a little fast-and-loose with this. So, we first + * pre-zero the entire buffer, and then decrement the + * buffer length by 1, thus ensuring there will always + * be a NUL present. + */ + memset(args->buf, 0, buflen--); + if (__predict_false(buflen == 0)) { + /* Unlikely, but just in case... */ + rv = propsize; + break; + } + /* FALLTHROUGH */ + + case PROP_TYPE_DATA: + rv = OF_getprop(phandle, args->prop, args->buf, buflen); + break; + + case PROP_TYPE_BOOL: + /* + * If we noticed the boolean property naming convention, + * use the cached value from earlier. Otherwise, presence + * of the property -> true. + */ + if (proptype == PROP_TYPE_BOOL) { + *(bool *)args->buf = boolval; + } else { + *(bool *)args->buf = true; + } + rv = propsize; + break; + + default: + return EFTYPE; + } + + if (rv != propsize) { + /* Something has gone off the rails. */ + return EIO; + } + + done: + args->propsize = propsize; + args->encoding = _BIG_ENDIAN; + args->type = proptype; + return 0; +} +OF_DEVICE_CALL_REGISTER("device-get-property", of_device_get_property) + /* * int of_decode_int(p) * Index: kern/subr_device.c =================================================================== RCS file: /cvsroot/src/sys/kern/subr_device.c,v retrieving revision 1.8 diff -u -p -r1.8 subr_device.c --- kern/subr_device.c 7 Aug 2021 18:16:42 -0000 1.8 +++ kern/subr_device.c 17 Aug 2021 01:10:45 -0000 @@ -31,6 +31,7 @@ __KERNEL_RCSID(0, "$NetBSD: subr_device. #include #include +#include #include /* Root device. */ @@ -284,10 +285,9 @@ device_handle(device_t dev) return dev->dv_handle; } -int -device_call(device_t dev, const char *name, void *arg) +static int +_device_call(device_t dev, devhandle_t handle, const char *name, void *arg) { - devhandle_t handle = device_handle(dev); device_call_t call; devhandle_t call_handle; @@ -299,6 +299,12 @@ device_call(device_t dev, const char *na } int +device_call(device_t dev, const char *name, void *arg) +{ + return _device_call(dev, dev->dv_handle, name, arg); +} + +int device_enumerate_children(device_t dev, bool (*callback)(device_t, devhandle_t, void *), void *callback_arg) @@ -310,3 +316,682 @@ device_enumerate_children(device_t dev, return device_call(dev, "device-enumerate-children", &args); } + +static int +device_getprop_dict(device_t dev, struct device_get_property_args *args) +{ + prop_dictionary_t dict = dev->dv_properties; + prop_object_t propval; + size_t copysize; + + /* + * Return ENOENT before any other error so that we can rely + * on that error to tell us "property does not exist in this + * layer, so go check the platform device tree". + */ + propval = prop_dictionary_get(dict, args->prop); + if (propval == NULL) { + return ENOENT; + } + if (args->reqtype != PROP_TYPE_UNKNOWN && + args->reqtype != prop_object_type(propval)) { + return EFTYPE; + } + + args->encoding = _BYTE_ORDER; + args->type = prop_object_type(propval); + + switch (args->type) { + case PROP_TYPE_NUMBER: + args->propsize = sizeof(uintmax_t); + if (args->buf != NULL) { + if (args->buflen == sizeof(uint32_t)) { + if (!prop_number_uint32_value(propval, + args->buf)) { + return ERANGE; + } + } else if (args->buflen == sizeof(uint64_t)) { + if (!prop_number_uint64_value(propval, + args->buf)) { + return ERANGE; + } + } else { + return EFTYPE; /* wut? */ + } + } + break; + + case PROP_TYPE_STRING: + /* +1 for trailing NUL */ + args->propsize = prop_string_size(propval) + 1; + if (args->buf != NULL) { + strlcpy(args->buf, prop_string_value(propval), + args->buflen); + } + break; + + case PROP_TYPE_DATA: + copysize = args->propsize = prop_data_size(propval); + if (args->buf != NULL) { + if (args->buflen < copysize) { + copysize = args->buflen; + } + memcpy(args->buf, prop_data_value(propval), copysize); + } + break; + + case PROP_TYPE_BOOL: + args->propsize = sizeof(bool); + if (args->buf != NULL) { + *(bool *)args->buf = prop_bool_value(propval); + } + break; + + default: + return EFTYPE; + } + + return 0; +} + +static int +device_getprop_internal(device_t dev, devhandle_t handle, + struct device_get_property_args *args, int flags) +{ + int error; + + if (args->buf == NULL || args->buflen == 0) { + args->buf = NULL; + args->buflen = 0; + } else if (args->buflen > SSIZE_MAX) { + /* Sizes must fit in ssize_t. */ + args->buflen = SSIZE_MAX; + } + + args->flags = flags; + + /* Check the device's property dictionary first. */ + if (dev != NULL) { + error = device_getprop_dict(dev, args); + if (error != ENOENT) { + KASSERT(error != 0 || + (args->encoding == _BIG_ENDIAN || + args->encoding == _LITTLE_ENDIAN)); + return error; + } + } + + error = _device_call(dev, handle, "device-get-property", args); + KASSERT(error != 0 || + (args->encoding == _BIG_ENDIAN || + args->encoding == _LITTLE_ENDIAN)); + return error; +} + +static void * +device_getprop_alloc(device_t dev, devhandle_t handle, const char *prop, + int flags, prop_type_t reqtype, size_t *sizep) +{ + struct device_get_property_args args = { + .prop = prop, + .reqtype = reqtype, + }; + ssize_t propsize; + int error; + + error = device_getprop_internal(dev, handle, &args, flags); + if (error != 0 || args.propsize < 1) { + return NULL; + } + + args.prop = prop; + args.reqtype = reqtype; + args.buflen = propsize = args.propsize; + args.buf = kmem_alloc(propsize, KM_SLEEP); + error = device_getprop_internal(dev, handle, &args, flags); + if (error != 0 || args.propsize != propsize) { + kmem_free(args.buf, propsize); + return NULL; + } + + if (sizep != NULL) { + *sizep = propsize; + } + return args.buf; +} + +/* + * device_getprop_size: + * devhandle_getprop_size: + * + * Returns the size of the specified property, or -1 if the + * property does not exist. + */ +static ssize_t +_device_getprop_size(device_t dev, devhandle_t handle, const char *prop, + int flags) +{ + struct device_get_property_args args = { + .prop = prop, + .reqtype = PROP_TYPE_UNKNOWN, + }; + int error; + + error = device_getprop_internal(dev, handle, &args, flags); + if (error) { + return -1; + } + + return args.propsize; +} + +ssize_t +device_getprop_size(device_t dev, const char *prop, int flags) +{ + return _device_getprop_size(dev, dev->dv_handle, prop, flags); +} + +ssize_t +devhandle_getprop_size(devhandle_t handle, const char *prop, int flags) +{ + return _device_getprop_size(NULL, handle, prop, flags); +} + +/* + * device_hasprop: + * devhandle_hasprop: + * + * Returns true if the device has the specified property. + */ +bool +device_hasprop(device_t dev, const char *prop, int flags) +{ + return device_getprop_size(dev, prop, flags) >= 0; +} + +bool +devhandle_hasprop(devhandle_t handle, const char *prop, int flags) +{ + return devhandle_getprop_size(handle, prop, flags) >= 0; +} + +/* + * device_getprop_encoding: + * devhandle_getprop_encoding: + * + * Returns the byte order encoding of the specified propery. + * N.B. The encoding is determined by the property's backing + * store, not by the property itself. + * + * Returns a byte order indicator (_BIG_ENDIAN, _LITTLE_ENDIAN) + * or -1 if the property does not exist. + */ +static int +_device_getprop_encoding(device_t dev, devhandle_t handle, const char *prop, + int flags) +{ + struct device_get_property_args args = { + .prop = prop, + .reqtype = PROP_TYPE_UNKNOWN, + }; + int error; + + error = device_getprop_internal(dev, dev->dv_handle, &args, flags); + if (error) { + return -1; + } + + return args.encoding; +} + +int +device_getprop_encoding(device_t dev, const char *prop, int flags) +{ + return _device_getprop_encoding(dev, dev->dv_handle, prop, flags); +} + +int +devhandle_getprop_encoding(devhandle_t handle, const char *prop, int flags) +{ + return _device_getprop_encoding(NULL, handle, prop, flags); +} + +/* + * device_getprop_type: + * devhandle_getprop_type: + * + * Returns the type of the specified property, constrained by + * the capabilities of the property's backing store. If the + * type cannot be determined, PROP_TYPE_UNKNOWN is returned. + * Note that some types may be aliased (e.g. a boolean property + * may be returned as PROP_TYPE_NUMBER if the backing store + * supports a number type but not a boolean type). + */ +static prop_type_t +_device_getprop_type(device_t dev, devhandle_t handle, const char *prop, + int flags) +{ + struct device_get_property_args args = { + .prop = prop, + .reqtype = PROP_TYPE_UNKNOWN, + }; + int error; + + error = device_getprop_internal(dev, dev->dv_handle, &args, flags); + if (error) { + return PROP_TYPE_UNKNOWN; + } + + return args.type; +} + +prop_type_t +device_getprop_type(device_t dev, const char *prop, int flags) +{ + return _device_getprop_type(dev, dev->dv_handle, prop, flags); +} + +prop_type_t +devhandle_getprop_type(devhandle_t handle, const char *prop, int flags) +{ + return _device_getprop_type(NULL, handle, prop, flags); +} + +/* + * device_getprop_data: + * devhandle_getprop_data: + * + * Return the specified property as an octet stream into + * the provided buffer, copying at most 'buflen' bytes. + * Returns -1 on error or the actual property size on success. + */ +static ssize_t +_device_getprop_data(device_t dev, devhandle_t handle, const char *prop, + int flags, void *buf, size_t buflen) +{ + struct device_get_property_args args = { + .prop = prop, + .buf = buf, + .buflen = buflen, + .reqtype = PROP_TYPE_DATA, + }; + int error; + + if (buf == NULL || buflen == 0) { + return -1; + } + + error = device_getprop_internal(dev, handle, &args, flags); + if (error) { + return -1; + } + + return args.propsize; +} + +ssize_t +device_getprop_data(device_t dev, const char *prop, int flags, void *buf, + size_t buflen) +{ + return _device_getprop_data(dev, dev->dv_handle, prop, flags, buf, + buflen); +} + +ssize_t +devhandle_getprop_data(devhandle_t handle, const char *prop, int flags, + void *buf, size_t buflen) +{ + return _device_getprop_data(NULL, handle, prop, flags, buf, buflen); +} + +/* + * device_getprop_data_alloc: + * devhandle_getprop_data_alloc: + * + * A convenience wrapper around device_getprop_data(); + * Allocates a correctly-sized buffer for the property, + * fetches the property, and returns the buffer and size. + */ +void * +device_getprop_data_alloc(device_t dev, const char *prop, int flags, + size_t *sizep) +{ + return device_getprop_alloc(dev, dev->dv_handle, prop, flags, + PROP_TYPE_DATA, sizep); +} + +void * +devhandle_getprop_data_alloc(devhandle_t handle, const char *prop, + int flags, size_t *sizep) +{ + return device_getprop_alloc(NULL, handle, prop, flags, + PROP_TYPE_DATA, sizep); +} + +/* + * device_getprop_string: + * devhandle_getprop_string: + * + * Return the specified property as a NUL-terminated string + * into the provided buffer, copying at most 'buflen' bytes + * (including the terminating NUL). The returned string is + * always NUL-terminated. Returns -1 on error or the actual + * property size on success. + */ +static ssize_t +_device_getprop_string(device_t dev, devhandle_t handle, const char *prop, + int flags, char *buf, size_t buflen) +{ + struct device_get_property_args args = { + .prop = prop, + .buf = buf, + .buflen = buflen, + .reqtype = PROP_TYPE_STRING, + }; + int error; + + if (buf == NULL || buflen == 0) { + return -1; + } + + error = device_getprop_internal(dev, handle, &args, flags); + if (error) { + return -1; + } + + return args.propsize; +} + +ssize_t +device_getprop_string(device_t dev, const char *prop, int flags, char *buf, + size_t buflen) +{ + return _device_getprop_string(dev, dev->dv_handle, prop, flags, buf, + buflen); +} + +ssize_t +devhandle_getprop_string(devhandle_t handle, const char *prop, int flags, + char *buf, size_t buflen) +{ + return _device_getprop_string(NULL, handle, prop, flags, buf, buflen); +} + +/* + * device_getprop_string_alloc: + * devhandle_getprop_string_alloc: + * + * A convenience wrapper around device_getprop_string(); + * Allocates a correctly-sized buffer for the property, + * fetches the property, and returns the buffer and size. + */ +char * +device_getprop_string_alloc(device_t dev, const char *prop, int flags) +{ + return device_getprop_alloc(dev, dev->dv_handle, prop, flags, + PROP_TYPE_STRING, NULL); +} + +char * +devhandle_getprop_string_alloc(devhandle_t handle, const char *prop, int flags) +{ + return device_getprop_alloc(NULL, handle, prop, flags, + PROP_TYPE_STRING, NULL); +} + +/* + * device_getprop_bool: + * devhandle_getprop_bool: + * + * Get the specified property as a boolean value. Returns the value + * of the property, or false if the property does not exist. + */ +static bool +_device_getprop_bool(device_t dev, devhandle_t handle, const char *prop, + int flags) +{ + bool val; + struct device_get_property_args args = { + .prop = prop, + .buf = &val, + .buflen = sizeof(val), + .reqtype = PROP_TYPE_BOOL, + }; + int error; + + error = device_getprop_internal(dev, dev->dv_handle, &args, flags); + if (error) { + /* + * If the property exists but is not a boolean type + * (EFTYPE), we map this to 'true'; this is the same + * behavior that the traditional OpenBoot, OpenFirmware, + * and FDT interfaces have. + * + * If the property does not exist (ENOENT), or there + * is some other problem we translate this to 'false'. + */ + return error == EFTYPE ? true : false; + } + return val; +} + +bool +device_getprop_bool(device_t dev, const char *prop, int flags) +{ + return _device_getprop_bool(dev, dev->dv_handle, prop, flags); +} + +bool +devhandle_getprop_bool(devhandle_t handle, const char *prop, int flags) +{ + return _device_getprop_bool(NULL, handle, prop, flags); +} + +/* + * device_getprop_uint32: + * devhandle_getprop_uint32: + * + * Get the specified property as an unsigned 32-bit integer. + * Returns 0 upon success or -1 upon failure. + */ +static int +_device_getprop_uint32(device_t dev, devhandle_t handle, const char *prop, + int flags, uint32_t *valp) +{ + struct device_get_property_args args = { + .prop = prop, + .buf = valp, + .buflen = sizeof(*valp), + .reqtype = PROP_TYPE_NUMBER, + }; + int error; + + error = device_getprop_internal(dev, dev->dv_handle, &args, flags); + if (error) { + return -1; + } + + return 0; +} + +int +device_getprop_uint32(device_t dev, const char *prop, int flags, uint32_t *valp) +{ + return _device_getprop_uint32(dev, dev->dv_handle, prop, flags, valp); +} + +int +devhandle_getprop_uint32(devhandle_t handle, const char *prop, int flags, + uint32_t *valp) +{ + return _device_getprop_uint32(NULL, handle, prop, flags, valp); +} + +/* + * device_getprop_uint32_default: + * devhandle_getprop_uint32_default: + * + * Wrapper around device_getprop_uint32_default() that returns + * the specified default value if the property does not exist. + */ +uint32_t +device_getprop_uint32_default(device_t dev, const char *prop, int flags, + uint32_t dval) +{ + uint32_t val; + + return _device_getprop_uint32(dev, dev->dv_handle, prop, flags, + &val) == -1 + ? dval : val; +} + +uint32_t +devhandle_getprop_uint32_default(devhandle_t handle, const char *prop, + int flags, uint32_t dval) +{ + uint32_t val; + + return _device_getprop_uint32(NULL, handle, prop, flags, &val) == -1 + ? dval : val; +} + +/* + * device_getprop_uint64: + * devhandle_getprop_uint64: + * + * Get the specified property as an unsigned 64-bit integer. + * Returns 0 upon success or -1 upon failure. + */ +static int +_device_getprop_uint64(device_t dev, devhandle_t handle, const char *prop, + int flags, uint64_t *valp) +{ + struct device_get_property_args args = { + .prop = prop, + .buf = valp, + .buflen = sizeof(*valp), + .reqtype = PROP_TYPE_NUMBER, + }; + int error; + + error = device_getprop_internal(dev, handle, &args, flags); + if (error != 0) { + return -1; + } + + return 0; +} + +int +device_getprop_uint64(device_t dev, const char *prop, int flags, uint64_t *valp) +{ + return _device_getprop_uint64(dev, dev->dv_handle, prop, flags, valp); +} + +int +devhandle_getprop_uint64(devhandle_t handle, const char *prop, int flags, + uint64_t *valp) +{ + return _device_getprop_uint64(NULL, handle, prop, flags, valp); +} + +/* + * device_getprop_uint64_default: + * devhandle_getprop_uint64_default: + * + * Wrapper around device_getprop_uint64_default() that returns + * the specified default value if the property does not exist. + */ +uint64_t +device_getprop_uint64_default(device_t dev, const char *prop, int flags, + uint64_t dval) +{ + uint64_t val; + + return _device_getprop_uint64(dev, dev->dv_handle, prop, flags, + &val) == -1 + ? dval : val; +} + +uint64_t +devhandle_getprop_uint64_default(devhandle_t handle, const char *prop, + int flags, uint64_t dval) +{ + uint64_t val; + + return _device_getprop_uint64(NULL, handle, prop, flags, &val) == -1 + ? dval : val; +} + +/* + * device_setprop_data: + * + * Set the specified binary data property. + */ +int +device_setprop_data(device_t dev, const char *prop, const void *buf, size_t len) +{ + return prop_dictionary_set_data(dev->dv_properties, prop, buf, len) + ? 0 : -1; +} + +/* + * device_setprop_string: + * + * Set the specified string property. + */ +int +device_setprop_string(device_t dev, const char *prop, const char *str) +{ + return prop_dictionary_set_string(dev->dv_properties, prop, str) + ? 0 : -1; +} + +/* + * device_setprop_uint32: + * + * Set the specifed unsigned 32-bit integer property. + */ +int +device_setprop_uint32(device_t dev, const char *prop, uint32_t val) +{ + return prop_dictionary_set_uint32(dev->dv_properties, prop, val) + ? 0 : -1; +} + +/* + * device_setprop_uint64: + * + * Set the specified unsigned 64-bit integer property. + */ +int +device_setprop_uint64(device_t dev, const char *prop, uint64_t val) +{ + return prop_dictionary_set_uint64(dev->dv_properties, prop, val) + ? 0 : -1; +} + +/* + * device_setprop_bool: + * + * Set the specified boolean property. + */ +int +device_setprop_bool(device_t dev, const char *prop, bool val) +{ + return prop_dictionary_set_bool(dev->dv_properties, prop, val) + ? 0 : -1; +} + +/* + * device_delprop: + * + * Delete the spefified property. N.B. only deletes properties + * from the device's property dictionary; does not attempt to + * delete properties from the platform device tree. + */ +void +device_delprop(device_t dev, const char *prop) +{ + prop_dictionary_remove(dev->dv_properties, prop); +} Index: sys/device.h =================================================================== RCS file: /cvsroot/src/sys/sys/device.h,v retrieving revision 1.174 diff -u -p -r1.174 device.h --- sys/device.h 15 Aug 2021 22:08:01 -0000 1.174 +++ sys/device.h 17 Aug 2021 01:10:45 -0000 @@ -347,7 +347,11 @@ struct shutdown_state { bool initialized; deviter_t di; }; -#endif + +/* Flags passed to device_getprop_*() and friends. */ +#define DEVICE_GETPROP_RECURSE 0x01 /* look recursively up to root */ + +#endif /* _KERNEL || _KMEMUSER */ /* * Description of a locator, as part of interface attribute definitions. @@ -700,6 +704,61 @@ device_t device_find_by_driver_unit(cons int device_enumerate_children(device_t, bool (*)(device_t, devhandle_t, void *), void *); +bool device_hasprop(device_t, const char *, int); +ssize_t device_getprop_size(device_t, const char *, int); +int device_getprop_encoding(device_t, const char *, int); +prop_type_t device_getprop_type(device_t, const char *, int); + +ssize_t device_getprop_data(device_t, const char *, int, void *, + size_t); +ssize_t device_getprop_string(device_t, const char *, int, char *, + size_t); +bool device_getprop_bool(device_t, const char *, int); +int device_getprop_uint32(device_t, const char *, int, uint32_t *); +int device_getprop_uint64(device_t, const char *, int, uint64_t *); + +void * device_getprop_data_alloc(device_t, const char *, int, + size_t *); +char * device_getprop_string_alloc(device_t, const char *, int); + +uint32_t device_getprop_uint32_default(device_t, const char *, int, + uint32_t); +uint64_t device_getprop_uint64_default(device_t, const char *, int, + uint64_t); + +int device_setprop_data(device_t, const char *, const void *, + size_t); +int device_setprop_string(device_t, const char *, const char *); +int device_setprop_bool(device_t, const char *, bool); +int device_setprop_uint32(device_t, const char *, uint32_t); +int device_setprop_uint64(device_t, const char *, uint64_t); + +void device_delprop(device_t, const char *); + +bool devhandle_hasprop(devhandle_t, const char *, int); +ssize_t devhandle_getprop_size(devhandle_t, const char *, int); +int devhandle_getprop_encoding(devhandle_t, const char *, int); +prop_type_t devhandle_getprop_type(devhandle_t, const char *, int); + +ssize_t devhandle_getprop_data(devhandle_t, const char *, int, void *, + size_t); +ssize_t devhandle_getprop_string(devhandle_t, const char *, int, char *, + size_t); +bool devhandle_getprop_bool(devhandle_t, const char *, int); +int devhandle_getprop_uint32(devhandle_t, const char *, int, + uint32_t *); +int devhandle_getprop_uint64(devhandle_t, const char *, int, + uint64_t *); + +void * devhandle_getprop_data_alloc(devhandle_t, const char *, int, + size_t *); +char * devhandle_getprop_string_alloc(devhandle_t, const char *, int); + +uint32_t devhandle_getprop_uint32_default(devhandle_t, const char *, int, + uint32_t); +uint64_t devhandle_getprop_uint64_default(devhandle_t, const char *, int, + uint64_t); + int device_compatible_match(const char **, int, const struct device_compatible_entry *); int device_compatible_pmatch(const char **, int, @@ -798,6 +857,26 @@ device_t shutdown_next(struct shutdown_s * corresponding to the child device, as well as a user-supplied * argument. If the callback returns true, then enumeration * continues. If the callback returns false, enumeration is stopped. + * + * device-get-property + * + * Gets the specified propery (and its attributes) and copies it into + * the caller-provided buffer, up to the size specified by the caller. + * If the requested property type is PROP_TYPE_NUMBER, the value will + * be converted from the backing store's byte order to native byte order + * as needed. If no buffer is provided, only the property's attributes + * are returned. "dev" argument may be NULL. + * + * Errors: + * ENOENT The property does not exist. + * ERANGE The value stored in the property is out of + * range for the requested size. + * EFTYPE The property is a different type than what + * was requested. + * EINVAL The arguments provided to the call are + * invalid. + * EIO An input/output error to the backing + * store occurred. */ struct device_enumerate_children_args { @@ -805,6 +884,17 @@ struct device_enumerate_children_args { void * callback_arg; }; +struct device_get_property_args { + const char * prop; /* input */ + void * buf; /* input */ + size_t buflen; /* input */ + prop_type_t reqtype; /* input */ + int flags; /* input */ + ssize_t propsize; /* output */ + int encoding; /* output */ + prop_type_t type; /* output */ +}; + int device_call(device_t, const char *, void *); #endif /* _KERNEL */