\section{Platform and System Devices}


The discussion of the driver model thus far has focused on peripheral
expansion buses that tend to share many characteristics in their
handling of devices and drivers, despite having radically different
physical attributes. The model works well for them because it
consolidates often-replicated object management code present in each
of those bus drivers. This covers a vast majority of devices that the
kernel supports. 

However, the driver model must make exceptions for two classes of
devices: system-level devices, legacy I/O devices, and host
bridges. Legacy devices and host bridges are grouped into a common
category - 'platform devices', since they are an integral part of the
platform and the physical makeup of the system board. 



\subsection*{System Devices}

System-level devices are devices that are integral to the routine
operation of the system. This includes devices such as processors,
interrupt controllers, and system timers. System devices do not follow
normal read/write semantics. Because of this, they are not typically
regarded as I/O devices, and are not represented in any standard
way. 

They do have an internal representation, since the kernel does
communicate with them, and does expose a means to exert control over
some attributes of some system devices to users. They are also
relevant in topological representations. System power management
routines must suspend and resume system devices, as well as normal I/O
devices. And, it is useful to define affinities to instances of system
devices in systems where there are multiple instances of the same type
of system device.

These features can happen in architecture-specific code. But, the
driver model's generic representation of devices provides an
opportunity to consolidate, at least partially, the representations
into architecture-independent ones. 



\subsection*{Problems}

This representation raises a few exceptions in the driver model. For
one, there is no common controlling bus that system devices reside
on. Computers always have at least one system-level bus on which the
CPU and memory usually reside. This device is always present, and not
probed for like buses of other types. To accommodate for this, a
struct bus\_type object is allocated for the system bus and registered
on startup. This 'pseudo-bus' is intended to represent the controlling
bus of all system devices.

Secondly, system devices are dynamically discovered by bus probe
routines, like devices on other bus types are. There is no common way
to communicate with more than one type of system device, so this is
quite impossible. Devices are discovered via very specific operations
in driver-like code. 

The term 'driver-like' is used because system devices typically do not
have device drivers like most peripheral devices do. The purpose of
most device drivers is to implement device-specific support for a
programming interface designed for a certain class of devices. Most
system-level devices are in a class of their own, so there is no need
to register their existence, or abstract their specifics. But, there
are drivers that do initialization of system-level devices, and export
them to other parts of the kernel.

This makes the discovery of system devices dependent on the presence
of the device's driver. In other peripheral buses, device discovery
and driver registration are two mutually exclusive operations. This
does not completely break the model, but it causes system device
infrastructure to make obtuse calls to the driver model core during
registration. 

System devices are expected to register a device class object first,
then a device driver for the device type, setting its bus to be the
system pseudo-bus object, and its class to be the device class that
was just registered. Then, the devices should actually be registered,
with their bus set to the system bus object, and their driver set to
the driver just registered. This is not the most elegant solution, but
it allows system devices to have complete coverage within the driver
model. 


\subsection*{Representation}

System devices are described in the following way: 


\begin{table}
\begin{footnotesize}
\begin{tabularx}{\linewidth}{|X|X|X|} \hline
\textit{Name}	& \textit{Return Type} & \textit{Description} \\ \hline \hline

id	& u32	& Instance number of this system root.
\\\hline

dev	& struct device	& Generic device information for system root.
\\\hline

sysdev	& struct device	& Statically-allocated virtual system bridge
	for this system root. 
\\\hline

\end{tabularx} 
\end{footnotesize}
\caption{\footnotesize{struct subsystem Programming Interface.}}
\end{table}



\begin{table}
\begin{footnotesize}
\begin{tabularx}{\linewidth}{|X|X|X|} \hline
\textit{Name}	& \textit{Return Type} & \textit{Description} \\ \hline \hline

name	& char *	& Canonical name of system device.
\\\hline

id	& u32	& Enumerated instance of system device.
\\\hline

root	& struct sys\_root *	& System root device exists on.
\\\hline

dev	& struct device	& Generic device information.
\\\hline

\end{tabularx} \\
\end{footnotesize}
\caption{\footnotesize{struct subsystem Programming Interface.}}
\end{table}


struct sys\_root is designed to accommodate systems that are composed
of multiple system boards, though are regarded as having a contiguous
topology. NUMAQ systems are an example of a platform with this feature
that are composed of four system boards bridged together with a custom
controller to maintain cache coherency. 

To accurately represent the topology of the system, each system board
is represented by a struct sys\_root. On all systems, there is an
implicit sys\_root present. On NUMAQ systems, additional system roots
may be registered to represent the different system boards. Each of
these subordinate roots contain a struct device to represent a logical
bridge to the system bus on that board. 

When system devices are discovered, their sys\_root pointer may be set
to be the root under which they physically reside. They will be added
as children of the system 'bridge' of the root under which they
reside.  If their root is not set, they will be added under the
default root.  System devices are always added as members of the
system bus type. 

Both struct sys\_root and sys\_device contain 'id' fields, and struct
sys\_device contains a 'name' field. Device discovery routines can use
these fields to name enumerate the devices that are being registered
for easy identification purposes later, without having to reference
the embedded struct device.

\subsection*{Platform Devices}

Platform devices are the set of legacy I/O devices and host bridges to
peripheral buses. These sets of devices share the characteristic that
they are devices on the system board itself; they are not expansion
devices. They also share the characteristic with system devices that
they are not necessarily part of a common controlling bus. Like system
devices, their discovery is dependent on the presence of a driver for
them. Although, many modern firmwares are attempting to change this,
as will be described later.

To cope with platform devices, the driver model created a pseudo bus
to pose as the bus driver for platform devices. However, there is no
common parent for all platform devices. Legacy devices on modern
systems reside on an ISA bus, a subordinate of the PCI-ISA bridge on
the PCI bus. It is important to accurately describe the system
topology, so the platform device model allows for registrants to
encode this in the device's parent. 

The driver model has also created a structure very similar to struct
sys\_device to describe platform devices. 

\begin{table}
\begin{footnotesize}
\begin{tabularx}{\linewidth}{|X|X|X|} \hline
\textit{Name}	& \textit{Return Type} & \textit{Description} \\ \hline \hline

name	& char *	& Canonical name of platform device.
\\\hline

id	& u32		& Enumerated instance of platform device.
\\\hline

dev	& struct device	& Generic device information.
\\\hline

\end{tabularx} \\
\end{footnotesize}
\caption{\footnotesize{struct subsystem Programming Interface.}}
\end{table}


The 'name' and 'id' are intended to be set by the discoverer of the
devices to allow for easy identification and comparison, without
having to reference or parse the embedded device's fields. 



\subsection*{Discovery}

Platform device discovery has traditionally occurred when a driver
loaded and probed hard-coded I/O ports to test for existence. This can
cause problems, though, when a driver is running on a platform where
the ports to probe for existence are different. It doesn't even have
to be different architectures, only different revisions of the same
platform. Probing undefined I/O ports is dangerous and cause very
unpredictable behavior. It can also be a very slow process, and
significantly delay the boot process.

To cope with these problems, the drivers can be modified to use
different I/O ports on different platforms, but it often convolutes
the code. 


\subsection*{Firmware}

Many modern firmware implementations have made attempts to solve this
problem by defining tables to describe the devices that are attached
to the system board, since they are known when the firmware is built.
The kernel can read these tables to know what type of devices are
attached where, independent of any driver being loaded. 

This creates a need to do dynamic driver binding for these devices.
The platform bus is designed to handle this. Theoretically, a firmware
driver could parse the firmware tables, and add platform devices for
the devices that they describe. The drivers for these devices can be
registered with the platform bus, and bound to the devices registered
with it. 

However, in order to use this infrastructure, drivers for platform
devices must be modified to handle this. They must first register with
the platform bus, and not probe for devices when they are loaded.
Legacy probing mechanisms may still exist as a fall back, but they
must be separated from the core initialization routines of these
drivers.  It may also be beneficial to make these parts of the drivers
optional.

This feature also depends on the firmware drivers and device drivers
using the same name to represent a type of device. Each firmware
encodes a different way to describe each type of device, like a
different Plug N' Play ID for each. Instead of modifying the common
platform layer to know about every firmware driver's method of
encoding each device type, firmware drivers are expected to map their
local device descriptors into a common format. 

For example, instead of naming a Host-PCI bridge after its PnP ID,
"PNP0a03", it would name the device "pci", which is equally meaningful
to all code, as well as someone reading the code. The PCI bus driver
could then load and register a driver with name "pci". The platform
bus would use these two names in its 'match' method to match the
device with the driver, and call the PCI driver's probe method. 

Work in this area is largely experimental. Not much quantifiable
progress has been made, besides previous proof-of-concept
implementations. More work is expected to take place in this area in
the near future.
