As an embedded developer, I often write drivers for hardware (though this question really applies to any shared resource). The "standard" interface I have come up with looks like the following. As an example, I will showing an interface for a "Voltage Probe."
class IVoltageProbe
{
public:
virtual void enable() = 0;
virtual void disable() = 0;
virtual units::Volts read() = 0;
};
The standard "enable" function allows the user to place the driver into a state such that it is ready to to perform its intended function. The "disable" function places the driver (and associated hardware) into its lowest-power, uninitialized, etc. state.
This has been working well across my code until now.
I needed to implement two different Voltage Probes to be used from independent threads. In this case, they are implemented as using ADC channels. However, even though each probe is a separate channel, they must both be part of the same ADC hardware block. In addition, there is no way to independently "read" from on channel without affecting the configuration of the other.
Thus, for example, if I provide a simple implementation of "enable" and "disable" that actually enables and disables the underlying ADC hardware, this will affect the state of the other voltage probe. The single ADC is a shared hardware resource.
One possible solution I thought of to this problem involves a "proxy" driver which
- Keeps track of all the upper-level voltage probes and their enabled/disabled states
- Enables the underlying hardware if any of the probes is enabled
- Disables the underlying hardware if all the probes are disabled
- Provides some form of mutual exclusion (e.g., just use a mutex)
This solution ends up making what once was simple code into something much more complex.
What are some other solutions to this type of problem? Do I need to consider redesigning my abstract interface?