The documentation about drivers ( more so for the “new” style ) is a bit lacking, or not-so-didatic in some parts.
One suggestion would to work a bit in it, then starting converting the “old style” drivers to it. In that process we could even fix some bugs or improve things a little, That could pave the way to reimplement parts of it in C++, as suggested , for a “NG - New Generation” style of drivers.
Is there some conditions in that the old style drivers cannot be reimplemented in the new style ? Like, functions not supported or the like ?
The main “pain point” is that currently the device_manager needs to be modified for each new type of driver we add. There is a piece of hardcoded C++ code which decides which parts of the hardware to probe when, for example, the OS is looking for an audio device. I have explained it in a previous post.
So, we should improve on that. We can do it while adjusting all the existing “new style” drivers at the same time, rather than going with “let’s rewrite everything from scratch” and end up with 3 concurrent APIs, I think? But I always approach things in the “I think we can fix it” way, maybe sometimes I’m wrong about that
I expect that all drivers can migrate to new driver API V2. After redesign, basic concepts like device tree and attributes will be the same, so it should be not hard to adapt code.
Maybe adjust / extend the “new driver” subsystem, in a way that it doesn´t incapacitate the existing drivers ? Or designing, on paper, a new system from scratch, until it is complete enough to judge if it would work ? If a system can be designed in a way that seem it would work much better / correctly, then it could be implemented and with enough documentation t make easier to convert the existing drivers ?
The “old style” seems clearer/easier to understand, but maybe it is due to having more documentation, both from here and from BeOS. The “new style” seems a bit vague, maybe due to missing some more in-depth article describing it, from the base to a finished simple example.
I made initial functional device manager v2 implementation enough to boot to desktop on TinyEMU.
Some design considerations:
C++ API is used, but only with pure virtual functions and no virtual destructors to allow potential use of C or Rust programming languages.
Unlike separate device and driver nodes in device manager v1, only one node is used both for device and driver. Device node expose 2 interfaces: bus interface set by bus manager when registering node and driver interface provided by dynamically found and attached device driver module.
Drivers may provide multiple interfaces identified by interface name string. It allows to export multiple different interfaces or different versions of the same interface for backward compatibility.
Device node supports only single driver. For multi-driver nodes such as root node, new child node is automatically created. Parent node have control over creation of child node and it can set bus driver interface.
“Device” is renamed to “DevFsNode” to avoid confusion.
Device manager is moved to separate add-on for now (can be moved back later).
Device node tree:
Node tree:
Node("Devices Root"): no driver
Node("Root subnode"): bus_managers/fdt/driver/v1
Node("Root"): no driver
Node("cpus"): no driver
Node("cpu@0"): no driver
Node("interrupt-controller"): no driver
Node("memory@80000000"): no driver
Node("htif"): no driver
Node("soc"): no driver
Node("clint@2000000"): no driver
Node("plic@40100000"): interrupt_controllers/plic/driver/v1
Node("serial@10000000"): no driver
Node("virtio@40010000"): busses/virtio/virtio_mmio/driver/v1
Node("Virtio MMIO"): no driver
Node("virtio@40011000"): busses/virtio/virtio_mmio/driver/v1
Node("Virtio MMIO"): no driver
Node("virtio@40012000"): busses/virtio/virtio_mmio/driver/v1
Node("Virtio MMIO"): drivers/disk/virtual/virtio_block/driver/v1
Node("virtio@40013000"): busses/virtio/virtio_mmio/driver/v1
Node("Virtio MMIO"): drivers/input/virtio_input/driver/v1
Node("virtio@40014000"): busses/virtio/virtio_mmio/driver/v1
Node("Virtio MMIO"): drivers/input/virtio_input/driver/v1
Node("framebuffer@41000000"): no driver
Node("chosen"): no driver
This mostly looks like the old device-manager API, only in C++ and with a rather different implementation behind it, no?
I’m not sure that solves most of the problems discussed earlier up thread, though it does deal with some of the issues with boilerplate and the like. Ultimately I’m not sure about using C++, even just C++ with a standard vtable setup, for the APIs (though certainly for the implementations.)
Well, it solves issues mentioned in top post except power management (power management need proper test cases first). Please ignore hard-coded driver lookup table (DriverRoster::Lookup), driver roster is not implemented yet. Driver roster is supposed to read compatible hardware information from driver add-ons resources or BFS attributes.
Does it? It looks like it solves some part of the “inflexible abstractions” but I’m not so sure how much. Your example “null” driver just ‘attaches’ itself to the first thing ‘Probe’ is called on. Why not just have a facility to register a device node without having called ‘Probe’ at all?
That’s just one example, I didn’t look through all your commits in serious detail, but I think I saw other items from the original posts which weren’t resolved, or at least weren’t fully resolved.
C++ vtables are equivalent to C structures-of-function-pointers, as you pointed out, but this equivalence is very fragile and easily broken. Maintaining C++ ABI is already tricky in userland, and there we don’t worry about C interoperability. Maintaining it in the kernel and worrying about C interoperability at the same time sounds like a lot of work for not much particular gain.
Probe is just an entry point of driver (device manager attempts to call it to attempt to attach potential driver to device node). How it can to load itself and register something without an entry point?
Or you mean use devfs API directly for registering devfs nodes? Who and why will load add-on in this case?
It is fully equivalent to C in terms of ABI maintenance. As long you add new methods to the end of interface, you are fine. No FBC padding is needed because implementation inheritance is not used.
C interoperability is only theoretical for now, probably nobody will actually care about it because all Haiku drivers are written in C++. And FreeBSD drivers compat layer is written in C++.
Global module initialization is the real “entry point”, isn’t it?
Except for the ones that are currently written in C, though we could admittedly rename the files.
Nonetheless, knowing what modifications break C++ ABI is annoying to deal with. In C structures, it’s very obvious what changes break ABI and which don’t.
Using module entry point will only complicate things. You will need to do the same with addition to register probe handler in entry point. Maybe you can skip node probe in simple drivers like /dev/null, but in actual hardware drivers you will need probe function for each device node.
Nonetheless, knowing what modifications break C++ ABI is annoying to deal with.
I repeat: it is the same as in C. If you modify virtual function or add new virtual function not at the end of interface, you will break ABI. Current C based device manager is very fragile to extension because it use structure inheritance. You can’t add new methods to base interfaces because device interfaces extend it (see bus_manager, pci_device_info etc.).
It is much more annoying to write C to C++ wrappers in every driver.
Why make most drivers more complicated only to make “hello world” type drivers a bit shorter?
Also attaching drivers to some bus (root, generic etc.) is needed for driver automatic loading, unless some separate kernel add-on auto-loading mechanism will be invented.
It may be reasonable to make devfs nodes to registered directly with devfs public interface and not manage devfs nodes with device manager.
First, it will mean something similar to dual C and C++ interface vtable definitions. It will be need to write each interface twice and maintain coherence. Second even worse, it will actually need to write interfaces three (!) times: C interface, C to C++ wrapper (for C++ implementations of C interfaces), C++ to C wrapper (for calling C interfaces from C++ code). For what!? C++ phobia? In C++ OS with strong C++ ABI maintenance traditions? Are you kidding?
I wasn’t advocating that. Making such “hello world” drivers shorter shouldn’t make any other drivers longer. Perhaps my proposal isn’t clear?
Why would you need to write this at all? The other two, maybe, but why would this one be needed in my proposed setup?
I don’t know if a C++ wrapper is really necessary anyway. Just a few #ifdef __cplusplus to add short-form versions of functions. But we already have this in some places, e.g. inline functions with default arguments instead of macros or whatever.
Updated previous post. Managing devfs nodes by device manager is useful anyway for introspection purposes (see which driver and which device node published devfs nodes). It is already available with current device manager v1.
Again, why bother with C if kernel code and drivers are mostly C++? I really don’t understand. There are no practical reasons in near perspective. I know nobody who want to write Haiku drivers in pure C.
For now I think it is perfectly fine to define interfaces in C++ only and add C/Rust ABI-compatible definitions if someone really need it and actually will write new drivers.