On a previous project I spent a lot of time trying to figure out a 'generic device driver'. That is actually a decently hard space to crack. As each device has randomly different ways of talking. Some are serial/local bus. Some are memory space. Some have a combination. Some systems broadcast data, some you have to query it, some you have to query then wait for the next broadcast. Some systems mildly follow the spec, some strictly follow it, some strictly follow their own flavor of the spec or an outdated one, some have their totally made up spec that they may or may not give you. Even from the same company you can have two models that on the outside if you use their stuff looks the same but under the hood is sorta different enough that your driver is garbage. I basically ended up here https://xkcd.com/927/
I just ended up there because I ended up with yet another 'standard' that wraps the wildly different 'APIs' that devices present to the OS.
I set it up so the application layer could just say 'I want to change/get some datapoint'. But the underside of that the 'datapoint' object had to know quite a bit about what to feed into the system to bind up things correctly to do that data transfer. Last time I looked home assistant had something similar to what I was coming up with. I just could not get it to not have a leaky abstraction. The problem was one system might have a set of 16 bit address spaces. While the next one would be 8 bit, the next one would be 78 bit. Then properly decoding those things was also leaky. As many of these systems will do things like bit masking/shifting to pull data out partiular fields. Plus some systems are just simple memory read/writes and others are bind out an RS232 port and fill out this packet just right (dont forget the correct baud rates), oh and there are 6 other things packed into the return packet. The real painful ones were the vendors who got tricky with a DRM handshake before each call and a simple decode system.