Device Autoconfiguration Process
The NetBSD device architecture is a tree consisting of children
(devices and buses) descended from parents (buses). The "family tree"
of devices is (partially) defined in kernel configuration files named files.*, where each child (device
or bus) is specified as being attached to a specific parent (bus). The
syntax of this attachment is discussed in the kernel configuration section, and described in detail in the NetBSD man page on config.
The root of the tree is the "root" bus; all other buses and
devices are descendants of this. In particular, for ARM-based systems,
the bus "mainbus" is attached to the root in the file files.arm, and
this is the bus to which the cpu is attached (in files.arm) as well as
all system buses (in the processor-specific files.vx115).
The above defines the basic relationship between devices and buses.
However, the actual instances of a specific device or bus on a platform
are defined in the main platform configuration file - here, the file
VX115_VEP. In this file, instances of buses and devices are specified
with the bus/device base name followed by a digit - for example,
mainbus0. There may be multiple instances of the same device,
differentiated by their numerical suffixes - for example, vx115_com0
and vx115_com1. In addition, the specification of a device instance can
provide a device locator, which is a list of parameters specific to
that instance - for example, the base address of the device instance,
IRQ, etc. The parameters that make up the locator, along with
default values, are part of the parent (bus) specification in the
files.* files. These parameters are used during the
autoconfiguration process, described below.
The tree structure of devices is built by the kernel configuration
process, in which the NetBSD config utility is run on the platform
configuration files to create source files with data structures
reflecting the specified device relationships. The tree structure
created by the kernel configuration process is used during kernel
initialization in a process called autoconfiguration. This involves the
sequential initialization of devices in a depth-first search of the
tree of devices, as follows.
- Each bus and device defines two methods, match( ) and attach( ),
and registers them with the autoconfiguration system using the
CFATTACH_DECL macro.
- During kernel initialization, each parent (bus) instance first
probes its child instances by calling their match( ) functions,
supplying the information (such as the device address and other
parameters) that was specified in the child device's instance
specification. The match( ) function uses the supplied parameters to
determine if the information corresponds to that child, and if so,
returns a non-zero value.
- When a child instance has returned that it's a match, the parent
then calls the device's attach( ) method, in which the child instance
initializes itself. If the child is itself a parent (bus), it will
probe its children as part of its attach( ) method.
Each parent uses the NetBSD config_search method and its own
search method to probe its children. config_search provides the parent
search method with a cfdata struct which contains the device
instance-specific locator parameters defined in the device instance
specification. This data is passed to the match( ) and attach( )
functions. The cfdata structure associated with a device has a very
general structure, with a loc field that's an array containing the
bus-specific locator parameters with array indices that are defines
generated by the config utility from the parameter names in the bus
specification. For convenience, the parameters are usually copied into
a more "structured" struct before being passed to match( ) and attach( ).
A Few More Details
The parent device search routine uses the NetBSD kernel function
config_match function to look up and call its children's match( ) functions. The
config_match( ) function calls the match( ) function for a device
as follows.
- calls config_cfattach_lookup( ) with device name as argument
- calls config_cfdriver_lookup( )
- runs through allcfdrivers list
- returns cfdriver with matching name
- returns config_cfattach_lookup_cd( ) with driver as arg
- runs through cfdriver's cfattachlist field to find name match
- returns cfattach which matches name
- call the cfattach struct's field cf_match = match function of child
The cfdata structures passed to parents (buses) during the
autoconfiguration process are generated in the file ioconf.c during the
configuration process from files.vx115 and VX115_VEP. Each bus
structure (parent) defines cfdata locator fields in files.vx115; the
format of the declaration of a bus and its locator fields is
device foobus {[field1 = default1], ... [fieldn = defaultn]}
- field1 through fieldn are locator
names; the configuration process generates defines from the locator names with names FOOBUSCF_FIELD1, ...,
FOOBUSCF_FIELDN, which are used as the array offsets to the corresponding information in the cfdata strcut's loc array
- defaults become defined values FOOBUSCF_FIELD1_DEFAULT, ... , FOOBUSCF_FIELDn_DEFAULT
For example, the files.vx115 file defines its internal APB with the text
device vx115_apb { [addr=-1], [size=0], [intr=-1], [index=0] }: bus_space_generic
This bus thus defines 4 locators, which result in the generation of the
defines VX115_APBCF_ADDR, VX115_APBCF_SIZE, VX115_APBCF_INTR, and
VX115_APBCF_INDEX for the locator positions in the cfdata struct's loc
array, and the default-value defines VX115_APBCF_ADDR_DEFAULT = -1,
VX115_APBCF_SIZE_DEFAULT = 0, VX115_APBCF_INTR_DEFAULT = -1, and
VX115_APBCF_INDEX_DEFAULT = 0
Device instances and their locator values for cfdata loc fields are
specified in VX115_VEP. The format of the device instance
specification is defined in the NetBSD man page ****.
The declarations for the timer device attached to the Vx115's APB
is
vx115_clk0 at vx115_apb? addr 0x700C5000 size 0x68 intr 9
Any field not specified gets default value in cfdata loc field, so in
this case index = 0. To make it easier to work with the locator values
outside of the rather unstructured cfdata loc array, it's typical to
define a struct for the bus that mirrors the cfdata loc fields. For the
Vx115's buses we define in vx115_var.h the struct type
struct vx115_attach_args
{
bus_space_tag_t sa_iot; /* bus tag */
bus_addr_t sa_addr; /* device base address */
bus_size_t sa_size; /* device io space size */
int sa_intr; /* interrupt number */
int sa_index; /* device index */
};
This is initialized when the bus's devices are being probed from
the corresponding device's cfdata struct, and then passed to the
device's match( ) and attach( ) functions; note the use of the locator
defines as offsets in the loc array of the cfdata struct.
int vx115_apb_search(struct device * parent, struct cfdata * cf, void *aux)
{
struct vx115_softc *sc = (struct vx115_softc *) parent;
struct vx115_attach_args aa;
DPRINTF("vx115_apb_search\n");
aa.sa_iot = sc->sc_iot;
aa.sa_addr = cf->cf_loc[VX115_APBCF_ADDR];
aa.sa_size = cf->cf_loc[VX115_APBCF_SIZE];
aa.sa_index = cf->cf_loc[VX115_APBCF_INDEX];
aa.sa_intr = cf->cf_loc[VX115_APBCF_INTR];
if (config_match(parent, cf, &aa))
config_attach(parent, cf, &aa, vx115_apb_print);
return 0;
}
Comments/questions: jsevy@cs.drexel.edu