feat: update
This commit is contained in:
parent
73bafa3e86
commit
9de53f0f5b
|
@ -0,0 +1,63 @@
|
||||||
|
cmake_minimum_required (VERSION 3.15)
|
||||||
|
|
||||||
|
project (serialport)
|
||||||
|
|
||||||
|
set (ADMAKE_DISABLE_ADDRESS_SANITIZER ON)
|
||||||
|
|
||||||
|
include (CMakeListsPub)
|
||||||
|
|
||||||
|
set (TARGET_NAME ${CMAKE_PROJECT_NAME})
|
||||||
|
|
||||||
|
file (GLOB_RECURSE SRC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
|
||||||
|
|
||||||
|
add_library (${TARGET_NAME}_OBJ OBJECT ${SRC})
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
|
||||||
|
elseif (ANDROID)
|
||||||
|
add_compile_options (-ffunction-sections -fdata-sections -fvisibility=hidden -Wl,--gc-sections)
|
||||||
|
elseif (UNIX)
|
||||||
|
|
||||||
|
elseif (WIN32)
|
||||||
|
# Disable the lib export by DLL
|
||||||
|
set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
target_include_directories (${TARGET_NAME}_OBJ
|
||||||
|
PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/source")
|
||||||
|
|
||||||
|
add_library (${TARGET_NAME} STATIC $<TARGET_OBJECTS:${TARGET_NAME}_OBJ>)
|
||||||
|
add_library (${TARGET_NAME}_SO SHARED $<TARGET_OBJECTS:${TARGET_NAME}_OBJ>)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
set_target_properties (${TARGET_NAME}_SO PROPERTIES
|
||||||
|
OUTPUT_NAME "serialportdll")
|
||||||
|
target_link_libraries (${TARGET_NAME}_SO PUBLIC user32)
|
||||||
|
elseif (APPLE)
|
||||||
|
set (DEP_FRAMEWOKS Foundation)
|
||||||
|
|
||||||
|
macro (add_osx_framework target fwname)
|
||||||
|
find_library (FRAMEWORK_${fwname}
|
||||||
|
NAMES ${fwname}
|
||||||
|
PATHS ${CMAKE_OSX_SYSROOT}/System/Library
|
||||||
|
PATH_SUFFIXES Frameworks
|
||||||
|
NO_DEFAULT_PATH)
|
||||||
|
if (${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND)
|
||||||
|
message (ERROR ": Framework ${fwname} not found")
|
||||||
|
else ()
|
||||||
|
message (STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}")
|
||||||
|
target_link_libraries (${target} "-framework ${fwname}")
|
||||||
|
endif ()
|
||||||
|
endmacro ()
|
||||||
|
|
||||||
|
foreach (framework ${DEP_FRAMEWOKS})
|
||||||
|
add_osx_framework (${TARGET_NAME}_SO ${framework})
|
||||||
|
endforeach ()
|
||||||
|
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ADMAKE_BUILD_TEST)
|
||||||
|
endif ()
|
|
@ -0,0 +1,3 @@
|
||||||
|
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||||
|
|
||||||
|
#endif /* OS_LINUX || OS_ANDROID */
|
|
@ -0,0 +1,182 @@
|
||||||
|
#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 */
|
|
@ -0,0 +1,156 @@
|
||||||
|
#if defined(OS_WINDOWS)
|
||||||
|
|
||||||
|
// CreateWindowEx GetMessage DispatchMessage RegisterClassEx UnregisterClass
|
||||||
|
// DefWindowProc SetWindowLong GetWindowLong
|
||||||
|
#include <dbt.h>
|
||||||
|
#include <tchar.h> /**< _T */
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <memory> /**< std::shared_ptr */
|
||||||
|
#include <thread> /**< std::thread */
|
||||||
|
|
||||||
|
#define CLASS_NAME _T("SerialPortHotPlugMonitorWnd")
|
||||||
|
|
||||||
|
class SerialPortHotPlugDelegate {
|
||||||
|
public:
|
||||||
|
virtual void OnConnected() = 0;
|
||||||
|
virtual void OnDisconnected() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SerialPortHotPlugMonitor {
|
||||||
|
SerialPortHotPlugMonitor(std::shared_ptr<SerialPortHotPlugDelegate> delegate)
|
||||||
|
: delegate(delegate_) {
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
~SerialPortHotPlugMonitor() {
|
||||||
|
if (thread_) {
|
||||||
|
if (thread_.joinable()) {
|
||||||
|
thread_.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnregisterClass(CLASS_NAME, GetModuleHandle(NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void operator()() {
|
||||||
|
// create hidden window to receive device change messages
|
||||||
|
if (nullptr == CreateWindowEx(0, CLASS_NAME, 0, 0, 0, 0, 0, 0,
|
||||||
|
0 /*HWND_MESSAGE*/, 0,
|
||||||
|
GetModuleHandle(nullptr), this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// message loop
|
||||||
|
MSG msg;
|
||||||
|
while (GetMessage(&msg, nullptr, 0, 0)) {
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool Init() {
|
||||||
|
// https://docs.microsoft.com/zh-cn/windows/win32/devio/registering-for-device-notification
|
||||||
|
WNDCLASSEX wnd_cls{0};
|
||||||
|
|
||||||
|
HINSTANCE instance = GetModuleHandle(nullptr);
|
||||||
|
|
||||||
|
if (nullptr == instance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wnd_cls.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
wnd_cls.lpszClassName = CLASS_NAME;
|
||||||
|
wnd_cls.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProc);
|
||||||
|
wnd_cls.hInstance = instance;
|
||||||
|
|
||||||
|
if (!RegisterClassEx(&wnd_cls)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_ = std::thread(&SerialPortHotPlugMonitor::operator(), this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam,
|
||||||
|
LPARAM lParam) {
|
||||||
|
switch (message) {
|
||||||
|
case WM_CREATE:
|
||||||
|
SetWindowLongPtr(hWnd, GWLP_USERDATA,
|
||||||
|
(LONG_PTR)((CREATESTRUCT *)lParam)->lpCreateParams);
|
||||||
|
break;
|
||||||
|
case WM_DEVICECHANGE: {
|
||||||
|
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
|
||||||
|
|
||||||
|
if (lpdb && DBT_DEVTYP_PORT == lpdb->dbch_devicetype) {
|
||||||
|
SerialPortHotPlugMonitor *monitor =
|
||||||
|
(SerialPortHotPlugMonitor *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
||||||
|
if (base && base->delegate) {
|
||||||
|
PDEV_BROADCAST_PORT devinfo = (PDEV_BROADCAST_PORT)lpdb;
|
||||||
|
|
||||||
|
if (DBT_DEVICEARRIVAL == wParam) {
|
||||||
|
#ifdef UNICODE
|
||||||
|
char port_name[256];
|
||||||
|
#ifdef CSERIALPORT_USE_UTF8
|
||||||
|
monirot->delegate_->OnConnected(
|
||||||
|
WCharToUTF8(port_name, 256, devinfo->dbcp_name), 1);
|
||||||
|
#else
|
||||||
|
monitor->delegate_->OnConnected(
|
||||||
|
WCharToNativeMB(port_name, 256, devinfo->dbcp_name), 1);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifdef CSERIALPORT_USE_UTF8
|
||||||
|
char portNameUTF8[256];
|
||||||
|
wchar_t portNameWChar[256];
|
||||||
|
// ANSI to WChar
|
||||||
|
NativeMBToWChar(portNameWChar, 256, devinfo->dbcp_name);
|
||||||
|
// WChar to UTF8
|
||||||
|
monitor->delegate_->OnConnected(
|
||||||
|
WCharToUTF8(portNameUTF8, 256, portNameWChar), 1);
|
||||||
|
#else
|
||||||
|
monitor->delegate_->OnConnected(devinfo->dbcp_name, 1);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
} else if (DBT_DEVICEREMOVECOMPLETE == wParam) {
|
||||||
|
#ifdef UNICODE
|
||||||
|
char portName[256];
|
||||||
|
#ifdef CSERIALPORT_USE_UTF8
|
||||||
|
monitor->delegate_->OnConnected(
|
||||||
|
WCharToUTF8(portName, 256, devinfo->dbcp_name), 0);
|
||||||
|
#else
|
||||||
|
monitor->delegate_->OnConnected(
|
||||||
|
WCharToNativeMB(portName, 256, devinfo->dbcp_name), 0);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifdef CSERIALPORT_USE_UTF8
|
||||||
|
char portNameUTF8[256];
|
||||||
|
wchar_t portNameWChar[256];
|
||||||
|
// ANSI to WChar
|
||||||
|
NativeMBToWChar(portNameWChar, 256, pDevInf->dbcp_name);
|
||||||
|
// WChar to UTF8
|
||||||
|
monitor->delegate_->OnConnected(
|
||||||
|
WCharToUTF8(portNameUTF8, 256, portNameWChar), 0);
|
||||||
|
#else
|
||||||
|
monitor->delegate_->OnConnected(devinfo->dbcp_name, 0);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send all other messages on to the default windows handler
|
||||||
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::thread thread_;
|
||||||
|
std::shared_ptr<SerialPortHotPlugDelegate> delegate_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* OS_WINDOWS */
|
Loading…
Reference in New Issue