Subject: More Device Properties
To: None <tech-kern@netbsd.org>
From: None <eeh@netbsd.org>
List: tech-kern
Date: 02/16/2001 23:51:47
Another version with many enhancements:



				Device Properties


1	Summary

I propose adding properties and accessors to device nodes.  There are a number
of cases where drivers need to pass information to other drivers or acquire
information that is not available to them and accessible only through machine
dependent code.  To solve these sorts of problems I propose adding a property
framework to allow arbitrary data to be associated with particular device nodes.

Examples of data that are currently problematical but could easily be passed
as properties are device locators, which require and arbitrary bus-specific
structure to pass betwen the parent bus node and the child device node, and
MAC addresses for ethernet devices, which on some machines are available by a
simple firmware call, but on others require the driver to dig bits out of an
EPROM.

The most useful aspects of device properties occur when the parent driver is
able to add properties which a child device uses during its attach routine.
The current infrastructure makes this impossible since the child device
structure is not available to the parent until after it has finished
attaching.


2	Synopsis

2.0	Public Data Structures

The device structure will be modified as follows:

struct device {
	enum	devclass dv_class;	/* this device's classification */
	TAILQ_ENTRY(device) dv_list;	/* entry on list of all devices */
	struct	cfdata *dv_cfdata;	/* config data that found us */
	int	dv_unit;		/* device unit number */
	int	dv_flags;		/* misc. flags; see below */
	char	dv_xname[16];		/* external name (name + unit) */
	struct	device *dv_parent;	/* pointer to parent device */
	CIRCLEQ_HEAD(dv_children, device) dv_child;	/* circular list of children */
	CIRCLEQ_ENTRY(device) dv_sibling;	/* circular list sibling device */
	SLIST_HEAD(devpropslist, devprop) dv_props;
	void	*dv_private;
};


2.1	Flags

PROP_WAIT		-- Specify that the call can sleep.
PROP_INHERIT		-- Inherit properties from parent.
PROP_STRING		-- Property is a string.
PROP_CONST		-- Property is constant do not allocate storage
			   for the data.

2.2	Macros

DEVICE_PRIVATE(dev)	-- Access the private softc data of dev.


2.3	Functions

   struct device *config_create(struct device *parent, int flags);
   struct device *config_found_smd(struct device *dev, void *aux, 
			cfprint_t print, cfmatch_t submatch, 
			struct device *child);
   struct device *dev_config_found(struct device *parent, 
			struct device *child, cfprint_t print, 
			cfmatch_t submatch);
   struct cfdata *dev_config_search(cfmatch_t fn, struct device *parent, 
			void *aux, struct device *child);
   struct device *dev_config_attach(struct device *parent, struct cfdata *cf, 
			void *aux, cfprint_t print, struct device *child);


   int dev_setprop(struct device *dev, const char *name, void *val, 
			size_t len, int flags);
   int dev_copyprops(struct device *source, struct device *dest, 
			int flags);
   size_t dev_getprop(struct device *dev, const char *name, void *val, 
			size_t len, int flags);
   int dev_delprop(struct device *dev, const char *name, int flags);

   size_t md_getprop(struct device *dev, const char *name, void *val, 
			size_t len, int flags);


3	Description

A property is a tuple that consists of a pointer to a device node (struct
device *), string, and an arbitrary amount of data.  This tuple is established
by dev_setprop(), retrieved by dev_getprop(), and destroyed by dev_delprop().  In
addition, there is a machine dependent hook, md_getprop() that is called if
attempting to retrieve a property fails.  md_getprop() can then use any
arbitrary method to generate property data if it so desires, or cause a failure
to be reported.

This functionality will be implemented in two phases.  The first phase will
add these functions, but retain the existing behavior for compatilbility.
config_create(9) will generate a dummy device node that the parent can use to
attach properties to.  config_attach*(9) functions will consume the dummy
device node then create the real device node and migrate properties to it.

The second phase will separate the `struct device' from the device's softc.
config_create(9) will create the true device node, which config_found*(9) will
attach to the device tree.  If the device probes successfully,
config_attach(9) will allocate a separate device softc structure and link it
to `struct device'.  If devices are detached, the `struct device' will remain
with all its properties but the softc will be destroyed.  This change will
cause breakage in practically all existing device drivers, so it will be
relegated to some future date.


3.0	Changed Data Structures

Several fields will be added to `struct device' and some will be relocated
to compact the size of the structure on 64-bit platforms.

A circular list of child devices will be added so device tree traversal can be
done both up-tree and down-tree.  The head of the list is the `dv_child'
field, and the `dv_sibling' field will be used for nodes on the list.

A singly linked list of properties will hang from the `dv_props' field.

A pointer to the device private data will be contained in `dv_private'.


3.1	Changed Functionality

A new device type `DV_NONE' will be added to signify that a device node
is as yet un-probed.

Drivers that expect properties during probe should:

	o Define a softc that does not contain a `struct device *'

	o Specify a negative size in the `ca_devsize' field of
	  the `cfattach' structure they provide.

By doing that, the `aux' parameter will point to a `struct device *', with all
all locators attached as properties in addition to any properties the parent
driver has attached. In addition, any `aux' structure provided by the parent
can be accessible from the device pointer through the use of the
`DEVICE_PRIVATE(dev)' macro.

config_rootsearch() will be modified to provide devices with locators as 
properties to new style devices as dev_config_search() below.


3.2	New Functionality

DEV_PRIVATE(dev)

Retrieves a pointer to the device's private data.


struct device *config_create(struct device *parent, int flags);

config_create() will trigger the creation of an un-configured device node and
attach it to the tree under `parent'.  This allows the parent driver to attach
properties to the device node before calling config_found*() so they are
available to the child device during the probe and attach steps.  If PROP_WAIT
is not specified then config_create(9) will not sleep to resolve resource
shortages.  Returns a pointer to a `struct device' on success, and NULL on
error.


struct device *config_found_smd(struct device *parent, void *aux, cfprint_t print, 
			cfmatch_t submatch, struct device *child);

In addition to config_found(9), and config_found_sm(9), config_found_smd(9)
can be used to supply device properties to child devices during probe and 
attach.  The `child' parameter is passed down to config_search(9) and
config_attach(9).


struct device *dev_config_found(struct device *dev, struct device *child,
		 	cfprint_t print, cfmatch_t submatch);

dev_config_found(9) is intended as an interface for bus drivers that no longer
need to supply an `aux'.  The `child' parameter will initally be a dummy
container but later will be the real child device node.  


struct cfdata *dev_config_search(cfmatch_t fn, struct device *parent, void *aux, 
			struct device *child);

dev_config_search(9) takes an additional parameter `child' which is a pointer
to a device which provides a set of properties for devices to use during
probe.  

If the device being probed specifies a negative value in the `ca_devsize'
field of the `cfattach' structure, a `child' device is created if it does not
exist.  Locator data are attached to the `child' as properties, and the
`aux' parameter is placed in its device private data field.  It is then
passed to the probe routine as the `aux' parameter.  The child driver should
cast the `aux' parameter to a `struct device *' and use it to query for
properties.  If it needs access to the aux data, use of `DEVICE_PRIVATE()'
on it will return the `aux' provided by the parent.

If the device provides a positive value in `ca_devsize' field of the 
`cfattach' structure, the probe routine is called with the `aux' passed
in by the parent in the `aux' field and the `child' device with locator
properties is not available from the *_probe() routine.


struct device *dev_config_attach(struct device *parent, struct cfdata *cf, 
			void *aux, cfprint_t print, struct device *child)

If a non-NULL `child' is passed in to dev_config_attach(9), and the device
being probed specifies a negative value in the `ca_devsize' field of the
`cfattach' structure, a softc of that size will be allocated and a pointer
to it placed in the device private field of the `child' device.

If a non-NULL `child' is passed in to dev_config_attach(9), and the device
being probed specifies a positive value in the `ca_devsize' field of the
`cfattach' structure, a new device node is allocated of the appropriate size,
all properties attached to it are moved to the newly created device node for
the device being attached.


int dev_setprop(struct device *dev, const char *name, void *val, size_t len, 
			int flags);

Create a property `name' and attach it to `dev' with a `len' byte value 
copied from location `val'.  If PROP_WAIT is not specified then dev_setprop(9)
will not sleep for resource shortage.  If PROP_CONST is specified, no storage
is allocated for the value, and when the property is queried it will copy
`len' bytes from the location specified by `val', so that data cannot be
freed or the kernel may panic.  If PROP_STRING is specified then the property
is marked as being an ASCIIZ string.  Returns 0 on success or an error value.


int dev_copyprops(struct device *source, struct device *dest, int flags);

Copy all properties associated with `source' device to `dest' device
structure.  It does not traverse the device tree or make any use of
md_getprop().  If PROP_WAIT is not specified then dev_copyprops(9) will not
sleep for resource shortage.  Returns 0 on success or an error value.


size_t dev_getprop(struct device *dev, const char *name, void *val, 
			size_t len, int flags);

Retrieve a property called `name' associated with `dev'.  If the property is
not found dev_getprop(9) will call md_getprop(9).  If the flag PROP_INHERIT is
set, and there is no property with that name associated with this device node,
it will try to retrieve the property from any parent device nodes.  Returns -1
if the property cannot be found, otherwise it returns the length of the value
data and if `val' is not NULL it copies up to `len' bytes of the property data
to the location pointed to by `val'.


int dev_delprop(struct device *dev, const char *name, int flags);

Remove a property from a device node.  If a NULL is supplied for the name,
dev_delprop(9) will remove all properties from a device node.  It returns 0 on
success or an error value.


size_t md_getprop(struct device *dev, const char *name, void *val, 
			size_t len, int flags);

If defined an a machine dependent header file, this function is called when
ever an attempt is made to retrieve a property from a device node but the
property is not found.  It allows machine dependent code to look up properties
from other locations.  It should be implemented to behave the same way as
dev_getprop(9) does.  It does not need to traverse parent device nodes.


Appendix 1

Providing md_setprop() and md_delprop() would be possible but make the
framework much more complicated.  If permanent storage of properties is
desired it should use some machine dependent method since non-volatile
storage is not necessarily available on all architectures.


Appendix 2

The config(8) application will eventually be extended to take typed 
values for locators and for properties that can be specified in
config files.