|
dxd - dynax driver framework 3.0.2
cross platform open source driver development framework
|
| Layer | Kernel | User-mode counterpart | shared |
|---|---|---|---|
| 0 platform | dxd_platform.h, sync, memory, dxd::clock per platform directory | dx_platform.h, dx_sync.h, dx::clock | parallel classes of one shape (kernel: no STL, no exceptions) |
| 1 driver model | dxd::driver, dxd::device, their clients dxd::driver::client, dxd::device::client | dx::driver, dx::device; the client end is dx::proxy::device | IOCTL selectors and payloads |
| 2 device kinds | dxd::usb::stream::device<super_device_t, desc_t>, PCI, FireWire, virtual | dx::usb::stream::device, dx::virtuel::stream::device, ... | dx::stream::desc, dx::circular, dx::stream::cursor, the ring rules |
| 3 audio, optional | PortCls, AudioDriverKit | CoreAudio server plugin, service/ASIO | - |
| 4 product | desc, composition, entry | compositions | the desc |
A non-audio driver uses layers 0 to 2.
Three roles, the same on every platform:
| Platform | Driver | Device | Client |
|---|---|---|---|
| KMDF | kmdf::driver | kmdf::device | dxd::device::client; the WDFFILEOBJECT is only the line |
| WDM | platform::driver with the IRP table | platform::device | dxd::device::client |
| PortCls | portcls::adapter, derived from platform::driver | portcls::adapter::device | dxd::device::client |
| DriverKit | platform::driver: a service with a device container, its driver::client creates and removes devices | platform::device: one service per device | IOUserClient on the device or driver service |
| AudioDriverKit | audiodriverkit::driver (IOUserAudioDriver) | audiodriverkit::device | as DriverKit |
| IOKit | platform::driver: each device shell owns one, as DriverKit's hardware dext | platform::device: one IOService shell per device | IOUserClient shell on the device service |
Every implementation has a dxd::driver (decided).** dxd::device holds its driver by reference. The OS dictates a driver object under WDM, PortCls and KMDF (DRIVER_OBJECT, WDFDRIVER), under AudioDriverKit (IOUserAudioDriver, the provider of every IOUserAudioDevice) and under Linux (pci_driver, usb_driver); under DriverKit a virtual device requires one, since the dext creates its devices itself. The one combination without an OS reason, hardware under DriverKit without audio - the OS matches the device service directly on IOUSBHostDevice/IOPCIDevice - gets its driver all the same, so the contract is one. Open: the form there. A device shell takes its driver from its provider, the driver shell (hosting()), and a hardware provider is none; either the device shell owns a driver of its own and deletes it after its device, or one dxd::platform::driver per dext process stands for the DRIVER_OBJECT.
The driver's own access point** (dxd::driver::client) exists under DriverKit alone, because macOS has no software device API and the driver creates virtual devices itself. On Windows every device arrives through PnP (a software device by SwDeviceCreate from the service, hardware from its bus) and the driver's client_create() stays {} (decided): a control device would stand outside PnP and keep the driver in memory; a driver-wide access point without a device would be a root-enumerated devnode ROOT\<URID>. User mode reaches version and log through the device handle on every platform - there is no second channel for the same IOCTLs (decided).
One base (decided).** Every platform's driver derives from the platform-free dxd::driver:
The base holds nothing OS-specific: the shape of the factory create(), the life-cycle sequence, log. GUID, registry Parameters and device interface belong to the Windows branches, Info.plist matters to DriverKit; Linux fits in, its bus core calling probe() per device as PnP calls create(). PortCls derives from the WDM branch (decided): the context on the DRIVER_OBJECT, the registry Parameters, the unload hook, the client dispatch (Create/Cleanup/Close/DeviceControl, complete(), removed) and the surprise removal and remove sequence are general WDM in dxd::platform::driver; the adapter replaces registration (PcInitializeAdapterDriver), device creation and start (PcAddAdapterDevice, miniports), the forwarding of foreign IRPs (PcDispatchIrp) and the offset of the device context.
KMDF stands beside WDM (decided).** KMDF brings what a common WDM base would offer (WdfDriverOpenParametersRegistryKey, WdfDeviceCreateDeviceInterface, WdfDeviceQueryProperty, WDFIOTARGET, WdfUsbTargetDevice) and derives from dxd::driver/dxd::device directly. The WDM helpers (wdm::registry, wdm::irp, wdm::unicode) stay free tools. A device kind's OS half therefore exists per driver model, and all of them are made (decided):
| Device kind | WDM (dxd::platform, PortCls) | KMDF | DriverKit |
|---|---|---|---|
| USB | URBs to wdm.bus | WDFUSBDEVICE/WDFUSBINTERFACE/WDFUSBPIPE | USBDriverKit: IOUSBHostDevice/IOUSBHostInterface/IOUSBHostPipe |
| PCI | CmResourceTypeMemory/CmResourceTypeInterrupt from the start IRP, IoConnectInterruptEx, IoGetDmaAdapter | dxd_kmdf_pci_device.h | PCIDriverKit: IOPCIDevice, IOInterruptDispatchSource, IODMACommand |
| FireWire | IRB to wdm.bus (dxd_firewire_platform_device.h) | IRB through the WDFIOTARGET (dxd_kmdf_firewire_platform_device.h), both on dxd_firewire_platform.h | a client of dxd::firewire::controller - a unit a child service or the controller's IOCTLs (open) |
The platform-free part of each kind (usb::device, pci::device, firewire::device: descriptors, configuration, register layout) is extracted from the Windows headers. FireWire's is dxd::firewire::device (kernel/dxd_firewire_device.h): a unit's transactions over its platform's transport, firewire::platform::device - the row above, and IOKit's on IOFireWireUnit. A transport offers read(), write() and lock() - a transaction split into packets, across a bus reset, timeout its bound where the transport takes one -, busreset(), the address spaces address and phy, and host_property().
Layering (decided, user mode the model):** contract (dxd::device) -> OS platform or driver model -> OS half of the device kind (usb::platform::device) -> device kind, platform-free (usb::device) -> streaming/product.
Names (decided).** The cross-platform base carries the plainest names, dxd::driver, dxd::device, with the clients nested as in user mode; bases are super, template parameters super_<what>_t, never "base". A platform namespace level exists only where a platform-free class of the same name derives from it (dxd::platform::device under dxd::device, dx::usb::platform::device under dx::usb::device); an OS half without one is the name itself (dxd::pci::device) until its platform-free class is extracted. An OS's platform branch is dxd::platform in dxd_platform_device.h; the search paths separate Windows, DriverKit, IOKit and Linux. The forms taking over the call line carry their framework as namespace and prefix: dxd::kmdf, dxd::portcls (adapter), dxd::audiodriverkit; a plain WDM or DriverKit product takes dxd::platform directly. Headers (decided):** a bare name or _platform is unique only where the search paths separate the platforms; KMDF and PortCls share kernel/wdm with the WDM branch and carry their prefix (dxd_kmdf_*.h, dxd_portcls_*.h, likewise dxd_audiodriverkit_*.h), the OS half of a device kind is dxd_<kind>_platform_device.h (WDM, DriverKit, IOKit) beside its platform-free class, dxd_<kind>_device.h without one, or dxd_kmdf_<kind>_device.h; a header all Windows forms share that differs from macOS/Linux is _platform, like dxd_sync_platform.h. portcls is namespace and part of file names only; in prose it is PortCls.
IOKit is a platform with its own search path (decided).** Kext and dext are separate builds against different SDKs, never meet in one translation unit, and live apart at runtime; IOKit follows the header rule without a prefix (iokit/dxd_platform_device.h, iokit/dxd_<kind>_platform_device.h). IOKit carries real dxd namespaces - erasing them by #define dxd goes, and so do the global token macros (#define device ...); what must stay unique is the OSMetaClass name the kernel keeps flat across all kexts, so every OSObject class is itself named DX_CAT(DX_PRODUCT_ID,_dxd_<name>) with the planned name its alias. To check: whether kmutil loads kexts whose classes live in namespaces.
Power is a driver's option (decided):** dxd::power<super_driver_t> binds the OS's power management to its devices' sleep()/wake() (dxd::device::powered()), composed over the driver's form - class driver: public dxd::power<dxd::platform::driver>, dxd::power<dxd::portcls::adapter>; one specialization per form (dxd_power.h per platform, dxd_<form>_power.h). A driver without it leaves the device in the working state as far as it knows; a driver of hardware composes it, as a device loses its state in a system sleep whether told or not.
| Windows PortCls | DriverKit AudioDriverKit | |
|---|---|---|
| driver form | portcls::adapter (WDM, PcInitializeAdapterDriver) | audiodriverkit::driver (IOUserAudioDriver), one service per dext |
| per device | PortCls subdevices on the FDO (PcRegisterSubdevice) | one IOUserAudioDevice per audiodriverkit::device, attached by AddObject() |
| per pin | WaveRT stream | IOUserAudioStream |
| per tick to the OS | WaveRT advance() and notification events | UpdateCurrentZeroTimestamp() |
| data | WaveRT buffer, cyclic | HAL IO buffer, IO operation handler |
| Building block | Windows | DriverKit | Reason |
|---|---|---|---|
| entry | DriverEntry | Info.plist personality + iig Start | the OS's entry model |
| creating a software device | through the OS (SwDeviceCreate -> PnP) | through the driver (IOCTL -> IOService::Create) | macOS has no software device API; the same API outward |
| driver foundation | KMDF or plain WDM; PortCls on WDM | IOService, AudioDriverKit on it | PortCls owns the WDM dispatch; KMDF brings everything itself |
| memory to the client | the kernel maps | the client maps by type | a dext cannot map into its client |
| signal to the client | KEVENT by handle | OSAction completion | the OS's event model |
| clock | KTIMER thread | IOTimerDispatchSource | hidden in the platform's dxd::clock |
| audio per tick | WaveRT position + notification | zero timestamp | the audio API's model |
| driver access point (driver::client) | none, client_create() stays {} | user client type ioctl::driver::client on the driver service | see Driver model (layer 1) |
Each step builds on its own - macOS locally, Windows on the build server - before the next begins.