This is a question that I have been asking myself for a while. I've tried finding good reads about this, but I cannot seem to find a solution that's suitable for how I think things should be.
I believe that for portability and maintenance reasons, drivers should preferably not be dependent on each other. However, sometimes one driver may require functionality provided by an other driver. An I²C bus for example may have a timeout that depends on the Timer driver.
The way I have been doing this until now is by simply #include'ing the drivers in the other drivers, but this is not a desirable solution. I feel like there should be a better way of doing this.
I'm thinking of adding another layer, a sort of abstraction between the main application and all drivers. However, this feels like it's just moving the problem somewhere else and not solving it.
I've used Function pointers, but this, too, makes maintenance a nuissance.
Are there any good sources or ideas about driver interdependency and how to neatly solve a problem like this?
On big controllers, Cortex M3/4 and the like, it is totally fine to have countless layers. For example the SD-Card interface of the LPC1822 consists of an "sdif" driver, handling the basic communication and pin toggling of the card interface. On top of that there is the "sdmmc" driver, providing more sophisticated functions. Over top this one could be the FAT system (using the real time clock), and on and on....
In contrary, on a tiny 8-bit controller it is maybe better to have no layers at all. Those 3 registers you have to set for an i2c communication are manageable. Don't write hundred lines of code to do something trivial. In that case it is totally fine to include the timer directly in the I2C routines. If you want your program to be more understandable for your colleagues, use your time to write good comments and documentation instead of encapsulating all and everything in functions and abstraction layers.
When you are ressource constrained and your program isn't that big anyway, don't burden yourself with too much overhead only to get consistent layers. Layers is something for big complicated software. In embedded computing you are sometimes better off keeping your sideways dependencies instead of writing huge libs that don't fit into the flash space.