Over the last year or so, I’ve been playing around with ideas for a generic 
device properties API, and have been slowly adding some infrastructure to the 
kernel to enable it.  Enough of the infrastructure is now in place (or in the 
pipeline) and I think my design is now fully-formed enough in my head to float 
it here.

First, the problem statement.  Right now, there are two distinct domains in 
which device properties live: in the device’s properties dictionary (accessible 
using device_properties() and then using the proplib(3) APIs to manipulate 
them) and in the platform’s device tree (e.g. ACPI, OpenFirmware, FDT, Sun 
OpenBoot, etc.). There is code scattered all around to reflect device tree 
properties into the device_t’s dictionary, usually in device_register() 
functions provide by MD code.  There is no common API for accessing the 
properties provided by the platform device tree.  This, unfortunately, results 
in a bunch of duplicated-then-tweaked code, odd-ball hacks to attempt to push 
additional information from MD code to MI drivers, and other such 
unpleasantness.

I would like to change that.

The API I’m proposing unifies these two domains.  By default, getting a 
property will fall through to the “lower layer” (the device tree), unless a 
property has been explicitly set by software, in which case its value is 
interposed.  There is a separate set of functions that allow software to go 
directly to the device tree, if needed, using the device’s devhandle directly.

The interface to the device tree’s properties (called the “backing store” in 
the code) is done using the devhandle / device_call() infrastructure I added 
earlier.  Device tree back-ends register a set of methods that provide the 
property access interface, and those methods are looked up based on the 
device’s devhandle implementation (ACPI and FDT/OFW handles can co-exist in the 
same system, for example, and the code supports this).  The backing store is 
consulted if a requested property is not found in the device’s properties 
dictionary.  Clients should use the correctly-typed API calls to interact with 
properties, as the dictionary and some backing stores can / do have typing 
systems (e.g. ACPI), although some (notably the OpenBoot / OpenFirmware / FDT 
types) do not.  This places some limitations on how clients can interact with 
properties, but if all the clients follow the typing rules, then things should 
mostly work out OK.

I’ve modeled the API loosely on the classical OpenBoot / OpenFirmware client 
API, mainly because it allows for some fairly convenient calling patters that 
reduce boilerplate code in drivers.  There isn’t a lot of error reporting 
specificity (-1 is the “an error occurred” return value) mainly because there 
is very little difference in what the client would do depending on what kind of 
error occurred, and it’s always possible to disambiguate after the fact by 
querying other things about the property (e.g. if it exists, what its type is, 
etc.) for things that really care.  One principle I stuck to was that 
properties are always returned into caller-provided buffers… this is slightly 
different than the prom_getprop() function on the sparc port (which has the 
option to allocate the buffer the caller does not provide one) and is also 
different than what the FDT code does (which, if you’re using the low-level FDT 
APIs directly, returns pointers into the static FDT blob).

Anyway, the API:

==> int device_getproplen(device_t, const char *prop);
Returns the size of the specified property, or -1 if the property does not 
exist.  Properties may have a size of 0.  For strings and binary data, this is 
very straight-forward.  For integers and booleans, the size is backing-store 
dependent, but does not necessarily represent the range of the value (e.g. in 
ACPI, integers are always stored as 64-bit values, even if the largest value 
that property will ever have is e.g. 16).

==> bool device_hasprop(device_t dev, const char *prop);
This is a wrapper around device_getproplen().  It returns true if 
device_getproplen() is >= 0.

==>.int device_getpropencoding(device_t dev, const char *prop);
Returns the byte order encoding of the property as defined by the backing store 
from which the property was fetched (_BIG_ENDIAN or _LITTLE_ENDIAN), or -1 if 
the property does not exist.

==> prop_type_t device_getproptype(device_t dev, const char *prop);
Returns the type of the property: PROP_TYPE_DATA, PROP_TYPE_STRING, 
PROP_TYPE_NUMBER, PROP_TYPE_BOOL, or PROP_TYPE_UNKNOWN.  The accuracy of this 
return value is limited by the capability of the backing store from which the 
property was fetched.  The device’s property dictionary has full type 
information for each property.  ACPI has some (ACPI lacks a native BOOL type).  
OpenBoot / OpenFirmware / FDT always return UNKNOWN because those backing 
stores have no typing information.

==> int device_getprop(device_t dev, const char *prop, void *buf, int buflen); 
Fetch the specified binary data property into buf, copying at most buflen 
bytes.  Returns the actual size of the property in the backing store or -1 if 
the property does not exist or if the property is not binary data.

==> int device_getprop_string(device_t dev, const char *prop, void *buf, int 
buflen);
Fetch the specified property as a string info buf, copying at most buflen 
bytes, **including the terminating NUL**.  If the backing store has typing 
information, ensures the property is a string.  Returns the actual size of the 
property in the backing store **including the terminating NUL** or -1 if the 
property does not exist or is not a string.

==> bool device_getprop_bool(device_t dev, const char *prop);
Fetch the specified property as a bool.  The behavior of this function is 
somewhat complicated by backing store capabilities.  If the property exists and 
a BOOL type, the value of the property is returned directly.  If the property 
is a NUMBER type, returns true if the number in the backing store is non-zero, 
and false if zero.  Otherwise, returns true if the property exists and false 
otherwise.

==> int device_getprop_uint32(device_t, const char *, uint32_t *);
==> int device_getprop_uint64(device_t, const char *, uint64_t *);
Fetch the specified property as a {32,64}-bit unsigned integer.  If the backing 
store supports it, typing will be enforced.  The value will also be 
range-checked to ensure it fits in the requested type.  For the 
OpenFirmware-style backing stores, a request for a 32-bit integer will fail if 
the prop size is not exactly 4 bytes; a request for a 64-bit integer will be 
allowed for 4 byte or 8 byte properties.  Values returned by these functions 
are transcoded into the host byte order as needed.

More complex arrangements of numbers and other data objects are extremely 
difficult to represent in a generic way, especially with untyped backing-stores 
like OpenFirmware / FDT, so I’m not trying with those (I’m looking at you, 
of_getprop_uint32_array()).  I think a better approach for those is to provide 
domain-specific interfaces to the property bindings for those platform device 
trees.

Here are the corresponding set prop operations… they set values only in the 
device’s property dictionary, and can override values from the device tree if 
needed.

int             device_setprop(device_t, const char *, const void *, int);
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);

This deletes a property from the device’s property dictionary, possibly 
exposing a property from the device tree that was previously in its shadow.

int             device_delprop(device_t, const char *);

These are corresponding functions that go directly to the device tree backing 
store.

int             devhandle_getproplen(devhandle_t handle, const char *prop);
bool          devhandle_hasprop(devhandle_t handle, const char *prop);
int             devhandle_getpropencoding(devhandle_t handle, const char *prop);
prop_type_t devhandle_getproptype(devhandle_t handle, const char *prop);

int             devhandle_getprop(devhandle_t handle, const char *prop, void *, 
int); 
int             devhandle_getprop_string(devhandle_t handle, const char * prop, 
void *, int);
bool          devhandle_getprop_bool(devhandle_t handle, const char *prop);
int             devhandle_getprop_uint32(devhandle_t handle, const char *prop, 
uint32_t *);
int             devhandle_getprop_uint64(devhandle_t handle, const char *prop, 
uint64_t *);

No set prop functions are provided for the device tree layer at this time.

Thoughts / feedback?  Thx.

-- thorpej

Reply via email to