easy modularization of drivers
One of the most prominent changes is the migration from the flat and ad-hoc system to a device tree layout.
At the top level resides the _"root"_ device which is the parent to hang all other devices on. For each architecture, there is typically a single child of "root" which has such things as _host-to-PCI bridges_, etc. attached to it. For x86, this "root" device is the _"nexus"_ device. For Alpha, various different models of Alpha have different top-level devices corresponding to the different hardware chipsets, including _lca_, _apecs_, _cia_ and _tsunami_.
A device in the Newbus context represents a single hardware entity in the system. For instance each PCI device is represented by a Newbus device. Any device in the system can have children; a device which has children is often called a _"bus"_. Examples of common busses in the system are ISA and PCI, which manage lists of devices attached to ISA and PCI busses respectively.
Often, a connection between different kinds of bus is represented by a _"bridge"_ device, which normally has one child for the attached bus. An example of this is a _PCI-to-PCI bridge_ which is represented by a device _[.filename]#pcibN#_ on the parent PCI bus and has a child _[.filename]#pciN#_ for the attached bus. This layout simplifies the implementation of the PCI bus tree, allowing common code to be used for both top-level and bridged busses.
Each device in the Newbus architecture asks its parent to map its resources. The parent then asks its own parent until the nexus is reached. So, basically the nexus is the only part of the Newbus system which knows about all resources.
An ISA device might want to map its IO port at `0x230`, so it asks its parent, in this case the ISA bus. The ISA bus hands it over to the PCI-to-ISA bridge which in its turn asks the PCI bus, which reaches the host-to-PCI bridge and finally the nexus. The beauty of this transition upwards is that there is room to translate the requests. For example, the `0x230` IO port request might become memory-mapped at `0xb0000230` on a MIPS box by the PCI bridge.
Resource allocation can be controlled at any place in the device tree. For instance on many Alpha platforms, ISA interrupts are managed separately from PCI interrupts and resource allocations for ISA interrupts are managed by the Alpha's ISA bus device. On IA-32, ISA and PCI interrupts are both managed by the top-level nexus device. For both ports, memory and port address space is managed by a single entity - nexus for IA-32 and the relevant chipset driver on Alpha (e.g., CIA or tsunami).
In order to normalize access to memory and port mapped resources, Newbus integrates the `bus_space` APIs from NetBSD. These provide a single API to replace inb/outb and direct memory reads/writes. The advantage of this is that a single driver can easily use either memory-mapped registers or port-mapped registers (some hardware supports both).
This support is integrated into the resource allocation mechanism. When a resource is allocated, a driver can retrieve the associated `bus_space_tag_t` and `bus_space_handle_t` from the resource.
Newbus also allows for definitions of interface methods in files dedicated to this purpose. These are the [.filename]#.m# files that are found under the [.filename]#src/sys# hierarchy.
The core of the Newbus system is an extensible "object-based programming" model. Each device in the system has a table of methods which it supports. The system and other devices uses those methods to control the device and request services. The different methods supported by a device are defined by a number of "interfaces". An "interface" is simply a group of related methods which can be implemented by a device.
In the Newbus system, the methods for a device are provided by the various device drivers in the system. When a device is attached to a driver during _auto-configuration_, it uses the method table declared by the driver. A device can later _detach_ from its driver and _re-attach_ to a new driver with a new method table. This allows dynamic replacement of drivers which can be useful for driver development.
The interfaces are described by an interface definition language similar to the language used to define vnode operations for file systems. The interface would be stored in a methods file (which would normally be named [.filename]#foo_if.m#).
Newbus Methods
# Foo subsystem/driver (a comment...)
METHOD int doit {
device_t dev;
# DEFAULT is the method that will be used, if a method was not
# provided via: DEVMETHOD()