Macos – Mac – Virtual Serial Port


I need to create a Cocoa app that will create a virtual serial port available to other apps, meaning registered in the IO Kit Registry.

Gist of the app:

  • Create a virtual serial port (listed in /dev and registered with the IOKit Registry)
  • Initiate a tcp connection out to another computer
  • Proxy everything received on the virtual serial port out to the
    network and vice versa.

This app will be used by third party apps that talk to serial ports on the computer, allowing for the particular serial device to be located across the network. The Cocoa and network part is no problem, I've written several apps that talk over the network. My hangup is the serial port.

I've done the test with socat/netcat/minicom to verify that it all works to proxy pty/tty traffic over the network but the tty I use doesn't show up as usable by random applications because it's not registered in the IO Kit Registry.

While I can use a pty/tty master/slave for the communication, I need this slave tty to show up to Mac applications. What would be very handy is a way to register a tty in the IO Kit Registry.

Do I really need to create a custom IOKit kext driver that gets registered at Cocoa app runtime? If so, I have a big learning curve ahead of me. Where should I start reading? Or, can I use IOKit to create a virtual serial port and register it as a usable serial port for applications without having to load any kernel extensions?

Thank you for any help you can provide,

Best Solution

First of all, have you checked if you can borrow a solution from this app? It's not obvious from the website if they've managed to get their virtual serial ports fully integrated into the system.

If there is a way to do it from user space, I'm not aware of it. The user-space IOKit API generally doesn't let you create class instances, let alone new device driver classes. Maybe you can somehow otherwise persuade the Cocoa libraries to find it despite not being registered in the kernel.

I don't know if you could get away with creating a "dummy" serial port in the kernel and then move your tty into its place in /dev from your userspace daemon. Maybe that's an option.

In case you do have to do it all in the kernel:

The virtual driver itself shouldn't be too much work, at least, though it will require some time to get up to speed with kernel dev. Unfortunately, the documentation is pretty thin for serial port drivers - the key is subclassing the IOSerialDriverSync abstract class. Just about the only description I've seen is in Ole Henry Halvorsen's OSX and iOS Kernel Programming book. It also has a fragment of an example for the reading & writing operations. (disclosure: I was one of the tech reviewers for this book; I don't receive any incentives for recommending it - in this case it's literally the only documentation I know of) You can find the source for a complete serial port driver in Apple's USBCDC driver, AppleUSBCDCDMM is the class that actually represents the serial port node.

It's relatively straightforward to open a so-called "kernel control" socket in the kernel, the individual APIs are documented here; from user space you use the normal BSD socket send/recv APIs. (this is also described in the aforementioned book) Your daemon can then connect to that, and all you'd need to do is push the data between the socket and the virtual serial port device. You'll need to handle disconnect events and such correctly of course.

Still, I think this is achievable as a first kernel project for an experienced C programmer (with some C++).

I hope that helps!