Subject: Yet more Device Properties
To: None <tech-kern@netbsd.org>
From: None <eeh@netbsd.org>
List: tech-kern
Date: 02/21/2001 18:25:23
New version.  Names changed to protect the innocent:



				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 rearranged to look like this:

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 */
	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

DEV_PRIVATE(dev)			-- Access the private softc data of dev.
DEV_CFA_DECL(size, match, attach)	-- Declare cfattach structure


2.3	Transitional Functions

   struct device *config_found_sad(struct device *dev, void *aux, 
			cfprint_t print, cfmatch_t submatch, 
			struct device *child);
   struct cfdata *config_search_ad(cfmatch_t fn, struct device *parent, 
			void *aux, struct device *child);
   struct device *config_attach_ad(struct device *parent, struct cfdata *cf, 
			void *aux, cfprint_t print, struct device *child);


2.4	New Functions

   struct device *dev_config_create(struct device *parent, int flags);
   struct device *dev_config_found(struct device *parent, struct device *child, 
			cfmatch_t submatch, cfprint_t print);
   struct cfdata *dev_config_search(struct device *parent, struct device *child,
			cfmatch_t fn);
   struct device *dev_config_attach(struct device *parent, struct device *child, 
			struct cfdata *cf, cfprint_t print);

   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 dev_mdgetprop(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, dev_mdgetprop() that is called if
attempting to retrieve a property fails.  dev_mdgetprop() 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.
dev_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.
dev_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'.  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 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.

The latter is handled by CFA_DECL().

By doing that, the `aux' parameter passed in to the device's probe function
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 config_search_ad() below.


3.2	New Functionality


DEV_PRIVATE(dev)

Retrieves a pointer to the device's private data.


DEV_CFA_DECL(size, match, attach)

Declare a `struct cfattach' for a new-style device driver.  The `size' is the
size of the softc, and the `match' and `attach' parameters should be pointers
to the match and attach functions for that device.


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

dev_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 dev_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_sad(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_sad(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 cfdata *config_search_ad(cfmatch_t fn, struct device *parent, 
			void *aux, struct device *child);

config_search_ad(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 *config_attach_ad(struct device *parent, struct cfdata *cf, 
			void *aux, cfprint_t print, struct device *child)

If a non-NULL `child' is passed in to config_attach_ad(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 config_attach_ad(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.


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) 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 device *dev_config_attach(struct device *parent, struct device *child, 
			struct cfdata *cf, cfprint_t print);

dev_config_attach(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.


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 a NUL terminated ASCII 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
dev_mdgetprop().  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 dev_mdgetprop(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 dev_mdgetprop(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 dev_mdsetprop() and dev_mddelprop() 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.


Appendix 3

A set of protocols need to be developed for the use of properties for
attaching device and bus nodes.  Required global properties should be defined,
as well as bus-specific properties.