toys/serialport/source/platform/mac.cpp

183 lines
5.4 KiB
C++

#if defined(OS_MACOS)
// macOS
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <memory> /**< std::shared_ptr */
#include <thread> /**< std::thread */
class SerialPortHotPlugDelegate {
public:
virtual void OnConnected() = 0;
virtual void OnDisconnected() = 0;
};
class SerialPortHotPlugMonitor {
SerialPortHotPlugMonitor(std::shared_ptr<SerialPortHotPlugDelegate> delegate)
: notification_port_(nullptr), serial_port_add_iter_(0),
serial_port_remove_iter_(0), delegate_(delegate) {
Init();
}
~SerialPortHotPlugMonitor() {
if (thread_) {
if (thread_.joinable()) {
thread_.join();
}
}
if (notification_port_ != nullptr) {
IONotificationPortDestroy(notification_port_);
}
if (serial_port_add_iter_) {
IOObjectRelease(serial_port_add_iter_);
}
if (serial_port_remove_iter_) {
IOObjectRelease(serial_port_remove_iter_);
}
}
int connectHotPlugEvent(itas109::CSerialPortHotPlugListener *event) {
if (event) {
p_listener = event;
return 0;
} else {
return -1;
}
return 0;
}
int disconnectHotPlugEvent() {
p_listener = NULL;
return 0;
}
private:
inline void operator()() {
// create notificaiton
notification_port_ = IONotificationPortCreate(kIOMasterPortDefault);
if (!notification_port_) {
return;
}
// create run loop
CFRunLoopSourceRef runLoopSource =
IONotificationPortGetRunLoopSource(notification_port_);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
kCFRunLoopDefaultMode);
kern_return_t result;
// match add serial
CFMutableDictionaryRef matching_add_dict =
IOServiceMatching(kIOSerialBSDServiceValue);
if (!matching_add_dict) {
IONotificationPortDestroy(notification_port_);
return;
}
// register serial add event
result = IOServiceAddMatchingNotification(
notification_port_, kIOPublishNotification, matching_add_dict,
SerialPortAdd, this, &serial_port_add_iter_);
if (result != KERN_SUCCESS) {
// fprintf(stderr, "Failed to add publish notification: %d.\n", result);
CFRelease(matching_add_dict);
IONotificationPortDestroy(notification_port_);
return;
}
// deal current device
SerialPortAdd(this, serial_port_add_iter_);
// clear dirct
// CFRelease(matchingAddDict); // TODO
// match remove serial
CFMutableDictionaryRef matching_remove_dict =
IOServiceMatching(kIOSerialBSDServiceValue);
if (!matching_remove_dict) {
// fprintf(stderr, "Failed to create matching add dictionary.\n");
IONotificationPortDestroy(notification_port_);
return;
}
// register serial remove event
result = IOServiceAddMatchingNotification(
notification_port_, kIOTerminatedNotification, matching_remove_dict,
SerialPortRemove, this, &serial_port_remove_iter_);
if (result != KERN_SUCCESS) {
// fprintf(stderr, "Failed to add terminated notification: %d.\n",
// result);
CFRelease(matching_remove_dict);
IONotificationPortDestroy(notification_port_);
return;
}
// deal current device
SerialPortRemove(this, serial_port_remove_iter_);
// clear dict
// CFRelease(matchingRemoveDict); // TODO
// message loop (could not delete)
CFRunLoopRun();
}
private:
bool Init() {
thread_ = std::thread(&SerialPortHotPlugMonitor::operator(), this);
return true;
}
private:
static void SerialPortAdd(void *refcon, io_iterator_t iterator) {
SerialPortHotPlugMonitor *monitor = reinterpret_cast<SerialPortHotPlugMonitor *>()refcon);
if (monitor) {
io_object_t serial_port;
while ((serial_port = IOIteratorNext(iterator))) {
char port_name[256];
CFStringRef cf_string = (CFStringRef)IORegistryEntryCreateCFProperty(
serial_port, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
if (cf_string != nullptr) {
CFStringGetCString(cf_string, port_name, sizeof(port_name),
kCFStringEncodingUTF8);
CFRelease(cf_string);
if (monitor->delegate_) {
monitor->delegate_->OnConnected(port_name, 1);
}
}
IOObjectRelease(serial_port);
}
}
}
static void SerialPortRemove(void *refcon, io_iterator_t iterator) {
SerialPortHotPlugMonitor *monitor = reinterpret_cast<SerialPortHotPlugMonitor *>()refcon);
if (monitor) {
io_object_t serial_port;
while ((serial_port = IOIteratorNext(iterator))) {
char port_name[256];
CFStringRef cf_string = (CFStringRef)IORegistryEntryCreateCFProperty(
serial_port, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
if (cf_string != nullptr) {
CFStringGetCString(cf_string, port_name, sizeof(port_name),
kCFStringEncodingUTF8);
CFRelease(cf_string);
if (monitor->delegate_) {
monitor->delegate_->OnDisconnected(port_name, 0);
}
}
IOObjectRelease(serial_port);
}
}
}
private:
IONotificationPortRef notification_port_;
io_iterator_t serial_port_add_iter_;
io_iterator_t serial_port_remove_iter_;
std::thread thread_;
std::shared_ptr<SerialPortHotPlugDelegate> delegate_;
};
#endif /* OS_MACOS */