-
Notifications
You must be signed in to change notification settings - Fork 0
Device Manager
Central registry for hardware devices, managing the device tree, driver binding, and lifecycle in JNode.
The DeviceManager (core/src/driver/org/jnode/driver/DeviceManager.java) is the heart of JNode's hardware discovery and management system. It maintains the complete device tree, binds devices to their drivers, handles device startup/shutdown ordering, and provides a query API for locating devices by ID or by the DeviceAPI they implement.
The device tree is hierarchical, rooted at the system bus. Each bus can have child buses or leaf devices attached. The Bus abstraction (Device.java, Bus.java) mirrors the physical topology: the PCI bus sits under the system bus, USB controllers sit under PCI, and individual devices (network cards, disk controllers, keyboards) sit under their respective bus controllers.
| Class | Location | Purpose |
|---|---|---|
DeviceManager |
org/jnode/driver/DeviceManager.java |
Interface defining the public API |
AbstractDeviceManager |
org/jnode/driver/AbstractDeviceManager.java |
Core implementation with device map, bus tree, listener dispatch |
DefaultDeviceManager |
org/jnode/driver/DefaultDeviceManager.java |
Concrete implementation; loads finders/mappers from plugin extension points |
Device |
org/jnode/driver/Device.java |
Software representation of a hardware device |
Bus |
org/jnode/driver/Bus.java |
A (potentially hierarchical) collection of devices |
Driver |
org/jnode/driver/Driver.java |
Abstract base class for device controllers |
DeviceToDriverMapper |
org/jnode/driver/DeviceToDriverMapper.java |
Interface for matching devices to drivers |
DeviceFinder |
org/jnode/driver/DeviceFinder.java |
Interface for hardware discovery |
DeviceAPI |
org/jnode/driver/DeviceAPI.java |
Marker interface for device capability interfaces |
The device tree is a directed acyclic graph rooted at the system bus (AbstractDeviceManager.getSystemBus()). Every Device and Bus has a reference to its parent bus:
// System bus is the root
Bus systemBus = deviceManager.getSystemBus();
// Each device knows its parent bus
Device dev = deviceManager.getDevice("eth0");
Bus parentBus = dev.getBus(); // e.g., the PCI busDeviceManager does not hold the tree structure directly in a single object — instead, each node (device or bus) holds a reference to its parent. The system bus is created in the AbstractDeviceManager constructor as a SystemBus (a package-private inner class extending Bus).
When a new Device is registered via register(Device):
flowchart TD
A[register device] --> B{ID already in map?}
B -->|yes| E[throw DeviceAlreadyRegisteredException]
B -->|no| C[set device manager reference]
C --> D[findDriver device]
D --> F{Driver found?}
F -->|no| G[device registered but not started]
F -->|yes| H[connect device to driver]
H --> I[start device]
I --> J[fire deviceRegistered event]
J --> K[notify DeviceManagerListener]
The findDriver method iterates over all registered DeviceToDriverMapper instances (sorted by match level, lowest first). Each mapper returns a Driver or null:
// AbstractDeviceManager.findDriver()
protected final Driver findDriver(Device device) {
synchronized (mappers) {
for (DeviceToDriverMapper mapper : mappers) {
final Driver drv = mapper.findDriver(device);
if (drv != null) {
return drv;
}
}
}
return null;
}Match levels (DeviceToDriverMapper):
-
MATCH_DEVICE_PREDEFINED = -1— Custom logic -
MATCH_DEVICE_REVISION = 0— Exact device + revision (best) -
MATCH_DEVICE = 1— Exact device -
MATCH_DEVCLASS = 2— Device class
After a driver is found, Device.setDriver(Driver) calls driver.connect(this), giving the driver a chance to initialize. Device startup follows (onStartDevice hook, then Driver.startDevice()).
Every device exposes one or more Device APIs — typed interfaces that applications use to interact with the hardware. All device APIs extend the marker interface DeviceAPI:
// The marker interface
public interface DeviceAPI { }
// Example: Serial port API
public interface SerialPortAPI extends DeviceAPI {
void send(byte b);
int receive();
void setSpeed(int baud);
}
// In the driver's afterConnect() or startDevice():
device.registerAPI(SerialPortAPI.class, new MySerialPortDriver(this));Clients look up APIs by class:
Device dev = deviceManager.getDevice("COM1");
SerialPortAPI serial = dev.getAPI(SerialPortAPI.class);
serial.setSpeed(9600);The Device.implementsAPI() and DeviceManager.getDevicesByAPI() methods support service discovery without coupling to specific device implementations:
// Find all devices that expose a network API
Collection<Device> netDevices = deviceManager.getDevicesByAPI(NetDeviceAPI.class);The getDevicesByAPI scan iterates all devices and checks each one:
public final Collection<Device> getDevicesByAPI(Class<? extends DeviceAPI> apiClass) {
final ArrayList<Device> result = new ArrayList<Device>();
for (Device dev : devices.values()) {
if (dev.implementsAPI(apiClass)) {
result.add(dev);
}
}
return result;
}API registration also propagates parent interfaces: if SerialPortAPI extends CharacterDeviceAPI which extends DeviceAPI, registering under SerialPortAPI automatically registers under both parent interfaces.
DefaultDeviceManager listens to two plugin extension points:
-
org.jnode.driver.finders— contributions areDeviceFinderimplementations that probe hardware -
org.jnode.driver.mappers— contributions areDeviceToDriverMapperimplementations that match devices to drivers
When a plugin extension is added, extensionAdded() calls findDevices() and findDeviceDrivers() to re-evaluate the device tree.
// DefaultDeviceManager.extensionAdded()
public final void extensionAdded(ExtensionPoint point, Extension extension) {
loadExtensions();
findDeviceDrivers();
}Mappers are sorted by match level so more specific mappers (e.g., exact PCI vendor/device ID) take precedence over generic ones.
| State | Transition |
|---|---|
| Registered | DeviceManager.register() |
| Driver connected |
findDriver() succeeds → device.setDriver()
|
| Started |
device.start() → Driver.startDevice()
|
| Stopped |
device.stop() → Driver.stopDevice()
|
| Unregistered |
DeviceManager.unregister() → device removed from map |
The DeviceManager also supports renaming devices with optional auto-numbering (rename(device, "eth", true) produces eth0, eth1, etc.).
Devices can be disabled at boot via the jnode.cmdline property:
// If cmdline contains "no<deviceId>", device start is blocked
if (cmdLine.indexOf("no" + device.getId()) >= 0) {
shouldStart = false;
}Two listener interfaces:
-
DeviceManagerListener— receivesdeviceRegistered,deviceUnregisterevents for all devices -
DeviceListener— receivesdeviceStarted,deviceStopevents
Both are fire-and-forget: listeners that take longer than 100ms to respond trigger an error log.
The system bus is a special Bus created without a parent:
this.systemBus = new SystemBus(); // in AbstractDeviceManager constructorChild buses (PCI, USB, ISA) attach to it. The system bus is the root of all hardware topology.
-
Multi-isolate API lookups —
implementsAPI()andgetAPI()use class-name matching (clazz.getName().equals(...)) rather than==comparison, to support multi-isolate scenarios where the same API interface may be loaded in different isolates. -
Device IDs can be renamed — A device's ID is mutable (
Device.setId()), controlled byDeviceManager.rename(). Do not rely on device ID strings being stable across hotplug events. -
Mapper match level ordering — The
MapperComparatorsorts mappers ascending by match level. AMATCH_DEVICE_REVISIONmapper (level 0) will be queried before aMATCH_DEVCLASSmapper (level 2). Drivers that claim broad compatibility must have higher (worse) match levels. -
Delayed driver binding — If
findDriver()returnsnull, the device is registered but not started.findDeviceDrivers()is called whenever a new mapper extension is registered, allowing late-bound drivers to connect and start previously registered devices. -
Plugin stop cascades to devices — When a plugin is stopped, its
PluginListenerautomatically callsdev.stop(true)on any device driven by that plugin's classloader. This couples plugin lifecycle to device lifecycle. -
Startup timing warnings — Device startups taking longer than 1 second log an info message; longer than 10 seconds (configurable
defaultStartTimeout) log an error. SlowstartDevice()implementations can cause boot hangs.
- Driver-Framework — Broader driver subsystem including bus drivers, resource management, and API hierarchies.
- Resource-Management — How drivers claim IRQ, I/O port, DMA, and memory resources.
- Interrupt-Handling — How hardware IRQs are routed to device driver handlers.
- Plugin-System — How DeviceFinder and DeviceToDriverMapper are registered via plugin descriptors.