Your process description seems to suggest that P1 needs data read by P3 (c1), P2 needs data read by P3 (c2), and P3 needs data read by P1 and P2 (a2 and b1).
So, is this what needs to happen (where * can occur anytime after 2):
- P1 <- read a1
- P3 <- read c1
- P3 <- read c2
- P2 <- read b1 and b2
*. P1 <- read a2
Or do P1, P2, and P3 use a cache of the last value read in the other process?
If the latter, you just need one mutual exclusion semaphore to control access to the device. If the former, you can have a fourth process that handles reading all the data and synchronize on when a valid set is available for all the use and when the data set has been read by all parties.
The fourth process doesn't have to know ahead of time what the data to be read is, it can simply implement a message queue where P1, P2, and P3 send it the parameters for data to be read. It then incrementes a counting semaphore each time it completes reads from each process. After sending their read message, everyone waits until the "data ready" semaphore reaches 3, then knows a valid set of data is available. After doing a read, they in turn increment a "data read" semaphore and wait for it to reach 3. When it does, they then wait on the "data ready" semaphore to go to 0 (the fourth process handles this after everyone has signaled their read, setting the "data ready" semaphore back to 0 then the "read data" semaphore back to 0. and the whole process repeats itself. Note, you'd never have a case where a process could miss the transition back to zero (deadlock) since each process needs to signal that their read is complete before the wait for "data ready" to go to zero by the time the third process gets around to doing it's read.
So, bottom line, you should be able to implement this with either 1 semaphore or 2 depending on what your requirements are for a concurrent set of data.
EDIT - Reviewing this, the "data ready" semaphore doesn't need to be a counting one since the thread with sole ownership of the input device can simply keep an internal count and just signal once when a complete data set is ready.