One of the recent threads on drivers made me thinking. It would be nice to have common, reusable driver set for hobby OSes, but so far all such attempts failed. I was thinking about why, and how could we avoid their mistakes. This is what I got this far:

- BSD, Linux etc.: OpenSource true, but they are tied to their kernels way too much- CDI, UDI: they are way too complex trying to solve everything and their structure not ideal for a microkernel- Minix: as it has a separated message queue interface, this is the best candidate so far, but not suitable for monolithic kernels- FUSE: is a good working example on how such a source compatible interface can be done

So what if we wouldn't want to solve all the issues (like UDI) instead we focus on the smallest common denominator and aim for K.I.S.S.? Could it be viable and would there be enough interest in such a project?

What I have in mind, is to define a source compatible interface in each and every driver. The spec would not care about how to name or organise your device files (or if you have a /dev file system at all), it would not care how you call those functions (directly from the FS layer or from a message dispatcher), and it would not care about how you detect the devices, those would be up to the kernel entirely.

Instead what it would do, is to provide the following functions in every compatible driver:- init: checks if the device really exists and sets up internal data structures. Called on boot or when the device is attached- open: called with a device index when the corresponding device file opened (if there's no /dev then it's the kernel's responsibility to call this hook with the appropriate device index before any reads or writes)- read: called with a device index and buffer address, size. The driver is supposed to read size bytes into the buffer from the device indexed by the argument.- write: similar to read, but for writes- ioctl: set up or query device characteristics. Same arguments as read/write, the command buffer would be device type specific. For queries, the response would be placed in the command buffer.- seek: set position for the given device index if applicable. Character devices (like uart) would simply do nothing in this function- irq: called with the device index when irq is fired- reset: reset the device to it's defaults.- close: called when the corresponding device file closed. Could be an empty function (if there's no /dev. then the kernel should call this with the appropriate device index when reads or writes are done)- fini: called when the device is removed or when the system is shutting down

The header would provide ANSI C function prototypes for these functions, gathered together in a struct typedef with a C++ wrapper (and maybe a C++ class template if there's interest in that). The struct typedefs for the ioctl commands would be defined here too. I'm planning to have common structs here, so not like ath5k_setmtu_struct_t and such, but instead command with two 64 bit arguments, command with a buffer and 3 32 bit arguments etc. For example all serial drivers would be expected to use the same, single command struct for setting baud, data bits, parity, stop bits etc. This header would define the error codes too (negative return values, like device timed out, buffer not big enough etc.)

On a typical UNIX-like system, the device index would be the device minor number. Other kernels must face the fact that there can be more devices of the same kind, handled by the same driver, so they must index those somehow. This index would be passed to every function as a first argument, and there it would select the proper device context from the internal data.

The spec would expect a compliant kernel to provide "register_irq", "malloc" and "free", but those could be macros.

Integration in a monolithic kernel: the FS layer should call driver functions directly when needed. The provided malloc should be a macro and should allocate kernel heap memory.

Integration in a microkernel: there's a need to create a dispatcher main() function to receive a message and call one of these functions accordingly (there should be no common wrapper, like fuse_main() for example, because it's up to every kernel how it implements messaging). The malloc could be a standard libc malloc implementation.

We could call this spec OSDev Drivers, or ODD in short (because it's odd to specify only the smallest subset in each and every driver, but if you think about it, helps portability a lot).

Any thoughts? Would you be interested in such a minimalistic approach?bzt

Do you consider the fact that a lot of folks have patchy understanding and handle of things like:

memory management, especially with page translation

exclusive/atomic accesses, race conditions, deadlocks

multi-processor system issues

interrupts

device specifics

format and protocol specifics

C

assembly

error handling

design (especially, well structured and testable)

debugging

testing

tools

Implementing the building blocks isn't easy. Putting a system together using those blocks isn't easy either (besides, it may be not enough fun for everyone). One needs to do that while really understanding what they're doing, how the components work and interact with one another, etc.

Or do you think those are non-issues? (If they weren't, well, everyone would have proper drivers and minimally functional OSes (and not just some crappy hello-world-like boot loaders) in a matter of months) What if they can't understand your code or satisfy its requirements because, well, they just aren't up to the task yet or they're doing something inherently incompatible with your code for one reason or another?

IOW, what's your idea as to why there is a problem and what it is? Are you sure you aren't trying to help with the wrong thing?

One question that comes to mind is how to handle synchronous / asynchronous drivers? In contrast to synchronous drivers that just return from the entry point, asynchronous drivers probably want to complete operations by invoking a callback. Would that be out of scope?

In addition to that, there needs to be some way to access hardware (e.g., to map MMIO regions from PCI BARs and/or legacy HW).

_________________managarm: A microkernel-based OS that is capable of running a Wayland desktop

Thanks for the answers! I think there's a misunderstanding on the goal of this project, I'll try to explain below.

alexfru wrote:

Do you consider the fact that a lot of folks have patchy understanding and handle of things like

No. I don't care what folks can't do. I'm interested in helping those who can. Developers with patchy understanding of those things wouldn't be able to write their OS, and no interface can help with that. They must learn and practice more, that's the only way. But beginners are not ODD's target audiance. It's aimed for advanced developers, who have implemented a lot already in their kernels, and now want to increase the number of supported devices.

alexfru wrote:

Implementing the building blocks isn't easy. Putting a system together using those blocks isn't easy either

No it is not. But easier than to get and read all hw specs and implement everything on your own. ODD does not provide ready-to-use building blocks btw, that's not the goal. It's just a recommendation on driver source interfaces. You definitely need to do some integration work in your kernel with that, but that's okay as ODD doesn't want to tell you how you should write your kernel or if you want async or sync calls or microkernel or monolithic kernel etc.

alexfru wrote:

One needs to do that while really understanding what they're doing, how the components work and interact with one another, etc.

That's why I'm aiming at a minimalistic approach. ODD does not care about the components nor how they interact, except for the lowest layer of the driver, but only at source compatibility level.

alexfru wrote:

What if they can't understand your code or satisfy its requirements because, well, they just aren't up to the task yet or they're doing something inherently incompatible with your code for one reason or another?

Then they won't be able to understand the hw specs and write their own drivers either. Simple as that. Again, ODD is not a complex code, just some definitions in a single C header. It's just an interface that encapsulates the lowest layer of the driver. By following it's recommendations and using it's typedefs, hobby OSes could exchange driver sources with minimal integration effort (but not with zero effort).

alexfru wrote:

IOW, what's your idea as to why there is a problem and what it is? Are you sure you aren't trying to help with the wrong thing?

Positive. I'm not trying to design a complex API, or binary compatible drivers or anything like that. I'm just suggesting a minimalistic source driver interface with some kernel hooks to make porting device drivers among hobby OSes easier.

Korona wrote:

One question that comes to mind is how to handle synchronous / asynchronous drivers?

Out of the scope. This spec is a lower layer than that. This only provides somewhat standardized way to read/write from a device, but it does not care when and how you call those. That's a higher abstraction level which I deliberatly don't want to address. The point is to create a simple interface for the drivers so that you can integrate them into your kernel more easily.

Korona wrote:

In addition to that, there needs to be some way to access hardware (e.g., to map MMIO regions from PCI BARs and/or legacy HW).

That's correct.

songziming wrote:

ODD spec is only good for big and complex devices, like graphics adaptor, network card, sound card, etc.

Exactly. Let's say you already have a networking stack but only one network card driver. With ODD, you can increase the number of supported network cards easily (but you have to have a networking stack, as ODD does not want to solve everything). Another example, you have implemented a GUI with shiny screen resolution chooser, but you have only one video card driver. With ODD, you can add more (but you have to have the GUI and screen resolution chooser as ODD does not provide those).

songziming wrote:

That also means we have to define a set of kernel API, just like ACPICA does.

Definitely not. ACPICA tries to solve all aspects and create fully functional driver ecosystem and therefore is very complex. ODD just tries to somewhat standardize driver interfaces to ease portability. Fundamentally different goals. But it is true that it requires some hooks from the kernel, but I definitely want to keep that as small as possible (4-5 functions tops).

I hope this makes it clear what ODD is and isn't. To sum it up, it's a C/C++ header file to provide a standardized driver API and interface to make porting drivers among hobby OSes easier. Nothing more, nothing less.

I hope this makes it clear what ODD is and isn't. To sum it up, it's a C/C++ header file to provide a standardized driver API and interface to make porting drivers among hobby OSes easier. Nothing more, nothing less.

My point is that you've pretty much chosen the old (I'm not saying necessarily/universally bad) paradigm of everything being a file with a small hole for extensions to it and exceptions from it (ioctl, AKA Pandora's box ). It's been around for a long time and has become a practical standard.

If the intended audience, as you say, is knowledgeable and experienced, couldn't they pull off something similar, write the same kind of familiar (UNIXish?) API?

Or is there an actual intent to supply the API with implementations? Are you gonna write them? If not, who? Whom and how are you going to convince to code their stuff to your API?

As you currently suggest it, there are several problems with your proposed interface

First, the suggested interface really only works for a kernel implementation that "pulls" information out of its device drivers. For example, suppose you would have a network card driver adhering to this interface. Then you would need to have something (probably a thread) continously calling read and waiting for its results to inject packets into the rest of the network stack. This might be far from ideal for systems that are designed with the idea in mind that network card drivers "push" received packets up through the rest of the network stack. You claim this is out of scope, but this actually determines on the lowest level how a driver is structured.

Second, while the open/read/write/ioctl/seek/close interface works decent (although it definitely has its problems) for cramming direct device access through file systems, it is in my opinion a lot less suitable for an internal kernel device interface. The main issue is that for quite a number of devices, there will be a significant device-agnostic stack on top of it interacting with the device. For example, for a network interface, there is typically a complete tcp/ip stack handling the traffic of all the individual interfaces, which therefore would need to talk with individual network card drivers. To make this possible, you will need to specify a lot more specifically how a network card is to expose its functionality over this interface. Similar problems also come up with things like sound cards, serial ports, keyboards and displays.

Third, you are certainly going to need more than just "register_irq", "malloc" and "free" for the operating system interface. At a minimum, most drivers will need the ability to request access to: port ranges (port io), specific memory regions (memory mapped io). Beyond that, for things like dma, they will need some way to deal with physical adresses and physically contiguous buffers.

My point is that you've pretty much chosen the old (I'm not saying necessarily/universally bad) paradigm of everything being a file

I have not. Where are the files in my proposed interface? You are biased by the names, but again, could be "attach, startop, endop, getdata, senddata, setpos, command (and deattach instead of fini)." As I've said, I've only used the names you are familiar with to help understanding of the purpose of each function. There's a good reason why I wrote the first argument is a device index (and not a file descriptor nor a FILE struct pointer), because there are no files involved at all.

davidv1992 wrote:

This might be far from ideal for systems that are designed with the idea in mind that network card drivers "push" received packets up through the rest of the network stack.

There's no "pull" or "push" defined here. You are lost in the abstractions. There's only a function to get the data (the received packet) from the device, and a function to send data (packet to be sent) to the device that's all. ODD does not tell you when to call those and what to do with the data (that's a higher abstraction layer), you can push that received data up to your network stack if you like. Since you have mentioned the network stack, if I wrote that ODD driver is only responsible for communicating the ISO/OSI physical layer 1 with the device, but does not care how that data is used by layer 2 and above, would it make now sense to you?Another example: a storage driver would allow you to read and write a sector, but it won't parse a partitioning table for you as that's a higher abstraction layer.

davidv1992 wrote:

device access through file systems

No files or file systems at all. Just drivers and device indeces. For example you have a machine with two ne2k cards. Then it is up to your kernel how to access the driver; ODD only expects that the ne2k driver should consistently choose the same card for the index 0 and 1. Likewise, if you have 4 serial ports, then it is up to your kernel how you call the uart driver; ODD only expects that index 0-3 should consistently select the same port (which can be "COM1:", "/dev/ttyS1", used by a special CommOpen(1) API whatever, ODD doesn't care).

Okay, let's forget it. It seems the concept of a source-compatible, low level interface is beyond your comprehention, you are confused by similar names and lost in abstraction layers. That's a pity. (No offense meant, the answers made it clear people on this forum are not ready yet to collaborate on such a project.)

Okay, let's forget it. It seems the concept of a source-compatible, low level interface is beyond your comprehention, you are confused by similar names and lost in abstraction layers. That's a pity. (No offense meant, the answers made it clear people on this forum are not ready yet to collaborate on such a project.)

If you can't respond to pretty straight forward and obvious criticism about your ideas without getting huffy, maybe it's you who isn't ready to collaborate with others. If you brought something to the table beyond an idea people might offer something beyond critique of the idea in return.

Personally, I doubt such a simple interface would encapsulate meaningful functionality for anything but the simplest devices, so I don't see the point of it since it won't actually make the task of porting drivers any easier.

The original post describes the standard driver interfaces found in commercial systems, although they are not source-compatible on their own, which is what is being sold.

The goal seems to be to write a framework (each OS will implement and maintain it in different ways) which provides a common interface so that a driver can be written under its specification, and be used anywhere that framework runs.

The cost of this framework has to be less than the cost it takes to write separate drivers for each of the participating systems. I doubt that that condition will be satisfied.

But, there are these limited to smaller subsystems:Windows has storport port driver, to whose API specification the storage vendors write their miniport drivers. Windows also has a NDIS. Interestingly, Linux can run some NDIS drivers through a wrapper. I concede that they belong to a single OS (namely, Windows) but they are still (specialized) interfaces meant to reduce the effort spent by developers (vendors) writing the corresponding drivers.

An important point to note here is that, even though this is a single OS, NDIS and storport are two /different/ frameworks, each catering to devices possessing different properties.

bzt wrote:

There's a good reason why I wrote the first argument is a device index (and not a file descriptor nor a FILE struct pointer), because there are no files involved at all.

A distinction without a difference. The index/fd/FILE all identify the device and help in locating/managing the device's state.

bzt wrote:

Okay, let's forget it. It seems the concept of a source-compatible, low level interface is beyond your comprehention, you are confused by similar names and lost in abstraction layers. That's a pity. (No offense meant, the answers made it clear people on this forum are not ready yet to collaborate on such a project.)

Please don't lose hope on account of us pitiable folks. Take two OS (I suggest Linux and one of the BSDs), remove the built-in support for a device (for e.g. a usb host controller) in both, and build and publish the interfaces and the common driver.

One thing that might make the effort more approachable is separating what the drivers provide from what driver demand. Both can be specified individually, possibly in two different documents. For example, in microkernels it could be reasonable for drivers to demand access to (part of) a standard C library. Hence, stuff like malloc() does not need to be specified in this case. Instead, it would be possible to first focus on what drivers provide, for example, the read()/write()/ioctl() interfaces.

_________________managarm: A microkernel-based OS that is capable of running a Wayland desktop

Instead what it would do, is to provide the following functions in every compatible driver: open ... read ... write

What if a driver interface looks completly different? For example my kernel doesn't have general read/write functions at all. There is only an open function that returns function pointers to the driver's read/write routines. How would this work with your idea?

Instead what it would do, is to provide the following functions in every compatible driver: open ... read ... write

What if a driver interface looks completly different? For example my kernel doesn't have general read/write functions at all. There is only an open function that returns function pointers to the driver's read/write routines. How would this work with your idea?

Who is online

Users browsing this forum: No registered users and 6 guests

You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum