Dunno if this helps - but here are two approaches I took for my CM11A and my CM19A.
The CM11A code is threaded up the wazoo - but I have only one thread that is allowed to communicate with the device. It's class exposes a read() method that, when called by other threads, grabs a mutex on a ring buffer and returns whatever data is in the buffer.
I don't need a write() yet, but if/when - it'll be an asynchronous responder. It'll accept and queue the write, and either send them in order or something more sophisticated. And we'll see what kind of "bytes written" response I decide to send back to the caller.
The class then has a private read() method that spawns a thread that does nothing but spin on the COM port, waiting for data, and then sticking it in the ring buffer, moving the pointers.
I did it more as an exercise to see if I remembered some pthread() stuff and C++.
I'm not sure it's an elegant solution.
But it works for me.
--
As for the CM19A - I simply dedicate a process, deftly called "./cm19a" that owns the device. Other processes then use standard IPC stuff to make their read/write requests. In my case, I use TCP sockets (specifically SmartSockets).
I dunno if this is elegant either, but in 10 years of doing embedded device development, and 20+ years of large scale system creation, I tend to fall into a pattern of "separation of concerns" and asynch messaging between them. It just scales so much mo' betta for me.
I have once process that is responsible for the CM19A interface. A separate process that sends X10 commands to that CM19A process. Another process that takes CM19A receptions and does "heyu" things. etc.
--
Yeah - you're right. Sooner or later, you need some uber-app, or process in the center that keeps track of everything. That's the job of my app called "homeserver" and it runs on the Linux server in the basement. It receives all of the socket transmissions and just keeps global state.
I'm not happy with that and have started on replacing that with an Enterprise Service Bus (currently Mule).
With an ESB - your not worried about threads or devices, they become endpoints on the bus. And the bus handles routing of the events.
Not sure if I helped or made it worse.