Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
So, I'm trying to make the drivers for my kernel abstract and easy to write (e.g.: the driver author shouldn't have to worry about many quirks of the kernel itself, just the hardware it supports). To that end, my drivers follow a convention like this:
The driver has a function called init. I don't have a deinit/shutdown function yet, but once I actually can shutdown the system I'll be adding that in.
Each driver init function is dependent on the driver; I want to somehow standardize this, but I don't know what I should standardize and leave alone (e.g.: should I require a memory allocation/deallocation routine, a set of port IO routines, etc.). For my NVMe implementation, for example, it accepts an array of 6 u64 elements for the BARs of the NVMe controller, a memory allocation and deallocation routine, the interrupt line of the PCIe device, and an interrupt registration routine that the driver can call to let the kernel know that the driver wants to listen to that interrupt. Interrupt listeners work similar to windows hooks, e.g.: there can be multiple interrupt listeners set for a single interrupt, and each is called in sequence. Interrupt listeners must execute as fast as possible because the interrupt handler does not return an EOI until all listeners have been notified.
The drivers are directly written in rust and are linked directly into the kernel, though I'm considering decoupling this completely. Making them C-compatible will require me to be able to execute ELF code, which will take me a while to actually figure out, but the drivers will be like DLLs of sorts. I was also thinking about making drivers web assembly modules, but I'm not very knowledgeable on writing WASM in rust (pretty much all the guides assume a web environment).
Right now this driver scheme sounds over-complicated since all the drivers are linked directly into the kernel. However, my main questions are:
1) What should I standardize in (say) a DriverInitializationInfo struct?
2) Do you guys have any suggestions on how I can improve this design?
Initialization and uninitialization of the driver module (which only happens once, when the driver is loaded or unloaded) should be kept separate from adding and removing devices. For every kind of bus (PCI, USB, etc), there should be a standardized interface that the driver uses to communicate with the bus. For PCI devices, this probably just means being able to read and write from the device's configuration space, while for others such as USB, it would get more complicated. Once a device has been added, the system should have a way to notify the driver of events such as device removal.
You also need some place to store information about which driver to invoke for a specific device (as identified by VID/PID, class code etc.).
Pointers to other kernel functions that are called by the driver, such as memory allocation, synchronization and whatnot, do not belong in the bus interface and should be provided on module initialization.