183 lines
5.4 KiB
C++
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 */
|