feat: update

This commit is contained in:
anjingyu 2025-03-03 15:24:57 +08:00
parent 73bafa3e86
commit 9de53f0f5b
6 changed files with 404 additions and 0 deletions

63
serialport/CMakeLists.txt Normal file
View File

@ -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 ()

View File

View File

@ -0,0 +1,3 @@
#if defined(OS_LINUX) || defined(OS_ANDROID)
#endif /* OS_LINUX || OS_ANDROID */

View File

@ -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 */

View File

@ -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 */

View File