From ec4ae56bc4efe43706e31e93fa5e13d561ebecb9 Mon Sep 17 00:00:00 2001 From: pixl Date: Tue, 16 Jun 2020 19:53:38 -0400 Subject: [PATCH] Implement raw DeviceMonitor Multiple things have been done in this commit; the base of the new backend has effectively been created. This branch currently has many vital parts commented out. Therefore, this branch is currently only intended for debugging. --- src/logid/CMakeLists.txt | 63 ++------ src/logid/Device.h | 2 +- src/logid/DeviceMonitor.cpp | 126 +++++++++++++++ src/logid/{DeviceFinder.h => DeviceMonitor.h} | 26 ++-- src/logid/backend/dj/Report.cpp | 32 ++++ src/logid/backend/dj/Report.h | 20 +++ src/logid/backend/hidpp/Device.cpp | 30 ++++ src/logid/backend/hidpp/Device.h | 51 ++++++ src/logid/backend/hidpp/Error.cpp | 0 src/logid/backend/hidpp/Error.h | 0 src/logid/backend/hidpp/Report.cpp | 83 ++++++++++ src/logid/backend/hidpp/Report.h | 57 +++++++ src/logid/backend/raw/DeviceMonitor.cpp | 133 ++++++++++++++++ src/logid/backend/raw/DeviceMonitor.h | 32 ++++ src/logid/backend/raw/RawDevice.cpp | 147 ++++++++++++++++++ src/logid/backend/raw/RawDevice.h | 41 +++++ src/logid/logid.cpp | 22 +-- src/logid/logid.h | 2 +- src/logid/util.cpp | 4 + src/logid/util.h | 4 +- 20 files changed, 797 insertions(+), 78 deletions(-) create mode 100644 src/logid/DeviceMonitor.cpp rename src/logid/{DeviceFinder.h => DeviceMonitor.h} (54%) create mode 100644 src/logid/backend/dj/Report.cpp create mode 100644 src/logid/backend/dj/Report.h create mode 100644 src/logid/backend/hidpp/Device.cpp create mode 100644 src/logid/backend/hidpp/Device.h create mode 100644 src/logid/backend/hidpp/Error.cpp create mode 100644 src/logid/backend/hidpp/Error.h create mode 100644 src/logid/backend/hidpp/Report.cpp create mode 100644 src/logid/backend/hidpp/Report.h create mode 100644 src/logid/backend/raw/DeviceMonitor.cpp create mode 100644 src/logid/backend/raw/DeviceMonitor.h create mode 100644 src/logid/backend/raw/RawDevice.cpp create mode 100644 src/logid/backend/raw/RawDevice.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index c924dca..1bafd4a 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -11,66 +11,29 @@ find_package(PkgConfig REQUIRED) add_executable(logid logid.cpp util.cpp - util.h - Configuration.cpp - Configuration.h - Actions.cpp - Actions.h - Device.cpp - Device.h - DeviceFinder.cpp - DeviceFinder.h - EvdevDevice.cpp - EvdevDevice.h) + DeviceMonitor.cpp + backend/raw/DeviceMonitor.cpp + backend/raw/RawDevice.cpp + backend/hidpp/Device.cpp + backend/hidpp/Report.cpp + backend/dj/Report.cpp) + set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -pkg_check_modules(PC_EVDEV libevdev) +pkg_check_modules(PC_EVDEV libevdev REQUIRED) pkg_check_modules(SYSTEMD "systemd") -pkg_check_modules(LIBCONFIG libconfig) - -find_path(HIDPP_INCLUDE_DIR hidpp) -find_library(HIDPP_LIBRARY libhidpp.so) +pkg_check_modules(LIBCONFIG libconfig REQUIRED) +pkg_check_modules(LIBUDEV libudev REQUIRED) find_path(EVDEV_INCLUDE_DIR libevdev/libevdev.h HINTS ${PC_EVDEV_INCLUDE_DIRS} ${PC_EVDEV_INCLUDEDIR}) find_library(EVDEV_LIBRARY NAMES evdev libevdev) -if((NOT HIDPP_INCLUDE_DIR) OR (NOT EXISTS ${HIDPP_INCLUDE_DIR}) OR (NOT HIDPP_LIBRARY) OR FORCE_BUILD_HIDPP) - message("Could not find libhidpp include dir, getting submodule") +include_directories(${HIDPP_INCLUDE_DIR} ${EVDEV_INCLUDE_DIR} ${DBUSCXX_INCLUDE_DIR} ${LIBUDEV_INCLUDE_DIRECTORIES}) - execute_process(COMMAND git submodule update --init -- hidpp - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - - if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") - set(DEFAULT_HID_BACKEND "linux") - elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - set(DEFAULT_HID_BACKEND "windows") - else() - message(WARNING "System is not supported") - endif() - - set(HID_BACKEND "${DEFAULT_HID_BACKEND}" CACHE STRING "Backend used for accessing HID devices") - set_property(CACHE HID_BACKEND PROPERTY STRINGS linux windows) - - if("${HID_BACKEND}" STREQUAL "linux") - pkg_check_modules(LIBUDEV libudev REQUIRED) - elseif("${HID_BACKEND}" STREQUAL "windows") - add_definitions(-DUNICODE -D_UNICODE) - add_definitions(-D_WIN32_WINNT=0x0600) # Use vista or later - else() - message(FATAL_ERROR "HID_BACKEND is invalid.") - endif() - - add_subdirectory(hidpp/src/libhidpp) - - set(HIDPP_INCLUDE_DIR "hidpp/src/libhidpp/") - set(HIDPP_LIBRARY hidpp) -else() - set(HIDPP_INCLUDE_DIR ${HIDPP_INCLUDE_DIR}/hidpp) -endif() - -include_directories(${HIDPP_INCLUDE_DIR} ${EVDEV_INCLUDE_DIR}) +target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ + ${DBUSCXX_LIBRARIES} ${LIBUDEV_LIBRARIES}) target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ ${HIDPP_LIBRARY}) diff --git a/src/logid/Device.h b/src/logid/Device.h index 7fba0f7..4a3ca6c 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -2,7 +2,7 @@ #define LOGID_DEVICE_H #include "Actions.h" -#include "DeviceFinder.h" +#include "DeviceMonitor.h" #include "Configuration.h" #include diff --git a/src/logid/DeviceMonitor.cpp b/src/logid/DeviceMonitor.cpp new file mode 100644 index 0000000..790df62 --- /dev/null +++ b/src/logid/DeviceMonitor.cpp @@ -0,0 +1,126 @@ +#include +#include + +#include "DeviceMonitor.h" +#include "util.h" + +#define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded" + +using namespace logid; +using namespace logid::backend; + +/* +void stopAndDeleteConnectedDevice (ConnectedDevice &connected_device) +{ + if(!connected_device.device->waiting_for_receiver) + log_printf(INFO, "%s (Device %d on %s) disconnected", connected_device.device->name.c_str(), + connected_device.device->index, connected_device.device->path.c_str()); + connected_device.device->stop(); + connected_device.associatedThread.join(); + delete(connected_device.device); +} + +DeviceMonitor::~DeviceMonitor() +{ + this->devices_mutex.lock(); + for (auto it = this->devices.begin(); it != this->devices.end(); it++) { + for (auto jt = it->second.begin(); jt != it->second.end(); jt++) { + stopAndDeleteConnectedDevice(jt->second); + } + } + this->devices_mutex.unlock(); +} + +Device* DeviceMonitor::insertNewDevice(const std::string &path, HIDPP::DeviceIndex index) +{ + auto device = new Device(path, index); + device->init(); + + this->devices_mutex.lock(); + log_printf(INFO, "%s detected: device %d on %s", device->name.c_str(), index, path.c_str()); + auto path_bucket = this->devices.emplace(path, std::map()).first; + path_bucket->second.emplace(index, ConnectedDevice{ + device, + std::thread([device]() { + device->start(); + }) + }); + this->devices_mutex.unlock(); + + return device; +} + +Device* DeviceMonitor::insertNewReceiverDevice(const std::string &path, HIDPP::DeviceIndex index) +{ + auto *device = new Device(path, index); + this->devices_mutex.lock(); + auto path_bucket = this->devices.emplace(path, std::map()).first; + path_bucket->second.emplace(index, ConnectedDevice{ + device, + std::thread([device]() { + device->waitForReceiver(); + }) + }); + this->devices_mutex.unlock(); + + return device; +} + +void DeviceMonitor::stopAndDeleteAllDevicesIn (const std::string &path) +{ + this->devices_mutex.lock(); + auto path_bucket = this->devices.find(path); + if (path_bucket != this->devices.end()) + { + for (auto& index_bucket : path_bucket->second) { + stopAndDeleteConnectedDevice(index_bucket.second); + } + this->devices.erase(path_bucket); + } + this->devices_mutex.unlock(); +} + +void DeviceMonitor::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index) +{ + this->devices_mutex.lock(); + auto path_bucket = this->devices.find(path); + if (path_bucket != this->devices.end()) + { + auto index_bucket = path_bucket->second.find(index); + if (index_bucket != path_bucket->second.end()) + { + stopAndDeleteConnectedDevice(index_bucket->second); + path_bucket->second.erase(index_bucket); + } + } + this->devices_mutex.unlock(); + + log_printf(WARN, "Attempted to disconnect not previously connected device %d on %s", index, path.c_str()); +} +*/ + +void DeviceMonitor::addDevice(std::string path) +{ + try { + backend::hidpp::Device device(path, hidpp::DeviceIndex::DefaultDevice); + + log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); + } + catch(backend::hidpp::Device::InvalidDevice &e) + { + log_printf(DEBUG, "Detected device at %s but %s", path.c_str(), e.what()); + } + catch(std::system_error &e) + { + log_printf(WARN, "Failed to open %s: %s", path.c_str(), e.what()); + } +} + +void DeviceMonitor::removeDevice(std::string path) +{ + log_printf(DEBUG, "Device %s disconnected", path.c_str()); + /* + ipc_server->removeReceiver(path); + this->stopAndDeleteAllDevicesIn(std::string(path)); + */ +} diff --git a/src/logid/DeviceFinder.h b/src/logid/DeviceMonitor.h similarity index 54% rename from src/logid/DeviceFinder.h rename to src/logid/DeviceMonitor.h index 687b33d..c288f0c 100644 --- a/src/logid/DeviceFinder.h +++ b/src/logid/DeviceMonitor.h @@ -1,16 +1,12 @@ -#ifndef LOGID_DEVICEFINDER_H -#define LOGID_DEVICEFINDER_H +#ifndef LOGID_DEVICEMONITOR_H +#define LOGID_DEVICEMONITOR_H -#include -#include -#include -#include -#include #include #include #include -#include "Device.h" +#include "backend/raw/DeviceMonitor.h" +#include "backend/hidpp/Device.h" #define MAX_CONNECTION_TRIES 10 #define TIME_BETWEEN_CONNECTION_TRIES 500ms @@ -25,24 +21,26 @@ namespace logid std::thread associatedThread; }; - class DeviceFinder : public HID::DeviceMonitor + class DeviceMonitor : public backend::raw::DeviceMonitor { public: - ~DeviceFinder(); + ~DeviceMonitor(); + /* Device* insertNewDevice (const std::string &path, HIDPP::DeviceIndex index); Device* insertNewReceiverDevice (const std::string &path, HIDPP::DeviceIndex index); void stopAndDeleteAllDevicesIn (const std::string &path); void stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index); + */ protected: - void addDevice(const char* path); - void removeDevice(const char* path); + void addDevice(std::string path) override; + void removeDevice(std::string path) override; private: std::mutex devices_mutex; - std::map> devices; + std::map> devices; }; - extern DeviceFinder* finder; + extern DeviceMonitor* finder; } #endif //LOGID_DEVICEFINDER_H \ No newline at end of file diff --git a/src/logid/backend/dj/Report.cpp b/src/logid/backend/dj/Report.cpp new file mode 100644 index 0000000..b8cccb3 --- /dev/null +++ b/src/logid/backend/dj/Report.cpp @@ -0,0 +1,32 @@ +#include +#include +#include "Report.h" + +using namespace logid::backend::dj; +using namespace logid::backend; + +static const std::array DJReportDesc = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x20, // Report ID (32) + 0x95, 0x0E, // Report Count (14) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x41, // Usage (0x41) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x41, // Usage (0x41) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0x85, 0x21, // Report ID (33) + 0x95, 0x1F, // Report Count (31) + 0x09, 0x42, // Usage (0x42) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x42, // Usage (0x42) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + +bool dj::supportsDjReports(std::vector& rdesc) +{ + auto it = std::search(rdesc.begin(), rdesc.end(), DJReportDesc.begin(), DJReportDesc.end()); + return it != rdesc.end(); +} \ No newline at end of file diff --git a/src/logid/backend/dj/Report.h b/src/logid/backend/dj/Report.h new file mode 100644 index 0000000..c4078f4 --- /dev/null +++ b/src/logid/backend/dj/Report.h @@ -0,0 +1,20 @@ +#ifndef LOGID_BACKEND_DJ_REPORT_H +#define LOGID_BACKEND_DJ_REPORT_H + +#include +#include "../raw/RawDevice.h" + +namespace logid::backend::dj +{ + bool supportsDjReports(std::vector& rawDevice); + class Report + { + enum Type: uint8_t + { + Short = 0x20, // Short DJ reports use 12 byte parameters + Long = 0x21 // Long DJ reports use 29 byte parameters + }; + }; +} + +#endif //LOGID_BACKEND_DJ_REPORT_H diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp new file mode 100644 index 0000000..9b517bb --- /dev/null +++ b/src/logid/backend/hidpp/Device.cpp @@ -0,0 +1,30 @@ +#include "Device.h" +#include "Report.h" + +using namespace logid::backend; +using namespace logid::backend::hidpp; + +const char* Device::InvalidDevice::what() const noexcept +{ + switch(_reason) + { + case NoHIDPPReport: + return "Invalid HID++ device"; + case InvalidRawDevice: + return "Invalid raw device"; + } +} + +Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept +{ + return _reason; +} + +/// TODO: Initialize a single RawDevice for each path. +Device::Device(std::string path, DeviceIndex index): + raw_device (std::make_shared(path)), path (path), index (index) +{ + supported_reports = getSupportedReports(raw_device->reportDescriptor()); + if(!supported_reports) + throw InvalidDevice(InvalidDevice::NoHIDPPReport); +} \ No newline at end of file diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h new file mode 100644 index 0000000..70e0956 --- /dev/null +++ b/src/logid/backend/hidpp/Device.h @@ -0,0 +1,51 @@ +#ifndef LOGID_HIDPP_DEVICE_H +#define LOGID_HIDPP_DEVICE_H + +#include +#include +#include "../raw/RawDevice.h" + +namespace logid::backend::hidpp +{ + enum DeviceIndex: uint8_t + { + DefaultDevice = 0, + WirelessDevice1 = 1, + WirelessDevice2 = 2, + WirelessDevice3 = 3, + WirelessDevice4 = 4, + WirelessDevice5 = 5, + WirelessDevice6 = 6, + CordedDevice = 0xff + }; + + class Device + { + public: + class InvalidDevice : std::exception + { + public: + enum Reason + { + NoHIDPPReport, + InvalidRawDevice + }; + InvalidDevice(Reason reason) : _reason (reason) {} + virtual const char *what() const noexcept; + virtual Reason code() const noexcept; + private: + Reason _reason; + + }; + Device(std::string path, DeviceIndex index); + std::string devicePath() const { return path; } + DeviceIndex deviceIndex() const { return index; } + private: + std::shared_ptr raw_device; + std::string path; + DeviceIndex index; + uint8_t supported_reports; + }; +} + +#endif //LOGID_HIDPP_DEVICE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp/Error.cpp b/src/logid/backend/hidpp/Error.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/logid/backend/hidpp/Error.h b/src/logid/backend/hidpp/Error.h new file mode 100644 index 0000000..e69de29 diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp new file mode 100644 index 0000000..c9b27b4 --- /dev/null +++ b/src/logid/backend/hidpp/Report.cpp @@ -0,0 +1,83 @@ +#include +#include +#include "Report.h" + +using namespace logid::backend::hidpp; +using namespace logid::backend; + +/* Report descriptors were sourced from cvuchener/hidpp */ +static const std::array ShortReportDesc = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x10, // Report ID (16) + 0x75, 0x08, // Report Size (8) + 0x95, 0x06, // Report Count (6) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x01, // Usage (0001 - Vendor) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x01, // Usage (0001 - Vendor) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + +static const std::array LongReportDesc = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x11, // Report ID (17) + 0x75, 0x08, // Report Size (8) + 0x95, 0x13, // Report Count (19) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x02, // Usage (0002 - Vendor) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x02, // Usage (0002 - Vendor) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + +/* Alternative versions from the G602 */ +static const std::array ShortReportDesc2 = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x10, // Report ID (16) + 0x95, 0x06, // Report Count (6) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x01, // Usage (0001 - Vendor) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x01, // Usage (0001 - Vendor) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + +static const std::array LongReportDesc2 = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x11, // Report ID (17) + 0x95, 0x13, // Report Count (19) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x02, // Usage (0002 - Vendor) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x02, // Usage (0002 - Vendor) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + +uint8_t hidpp::getSupportedReports(std::vector&& rdesc) +{ + uint8_t ret = 0; + + auto it = std::search(rdesc.begin(), rdesc.end(), ShortReportDesc.begin(), ShortReportDesc.end()); + if(it == rdesc.end()) + it = std::search(rdesc.begin(), rdesc.end(), ShortReportDesc2.begin(), ShortReportDesc2.end()); + if(it != rdesc.end()) + ret |= HIDPP_REPORT_SHORT_SUPPORTED; + + it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc.begin(), LongReportDesc2.end()); + if(it == rdesc.end()) + it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc2.begin(), LongReportDesc2.end()); + if(it != rdesc.end()) + ret |= HIDPP_REPORT_LONG_SUPPORTED; + + return ret; +} \ No newline at end of file diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h new file mode 100644 index 0000000..d69c4b6 --- /dev/null +++ b/src/logid/backend/hidpp/Report.h @@ -0,0 +1,57 @@ +#ifndef LOGID_BACKEND_HIDPP_REPORT_H +#define LOGID_BACKEND_HIDPP_REPORT_H + +#include +#include "../raw/RawDevice.h" +#include "Device.h" + +#define LOGID_HIDPP_SW_ID 0x0f + +/* Some devices only support a subset of these reports */ +#define HIDPP_REPORT_SHORT_SUPPORTED 1U +#define HIDPP_REPORT_LONG_SUPPORTED 1U<<1U +/* Very long reports exist, however they have not been encountered so far */ + +namespace logid::backend::hidpp +{ + uint8_t getSupportedReports(std::vector&& rdesc); + class Report + { + public: + enum Type: uint8_t + { + Short = 0x10, + Long = 0x11 + }; + + class InvalidReportID: std::exception + { + InvalidReportID(); + virtual const char* what() const noexcept; + }; + + class InvalidReportLength: std::exception + { + InvalidReportLength(); + virtual const char* what() const noexcept; + }; + + static constexpr std::size_t MaxDataLength = 32; + + Report(uint8_t report_id, const uint8_t* data, std::size_t length); + Report(std::vector data); + + Type type() const; + void setType(Report::Type type); + + logid::backend::hidpp::DeviceIndex deviceIndex(); + + std::vector rawReport () const { return _data; } + + private: + static constexpr std::size_t HeaderLength = 4; + std::vector _data; + }; +} + +#endif //LOGID_BACKEND_HIDPP_REPORT_H \ No newline at end of file diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp new file mode 100644 index 0000000..69eac98 --- /dev/null +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -0,0 +1,133 @@ +#include "DeviceMonitor.h" + +#include +#include + +extern "C" +{ +#include +#include +} + +using namespace logid::backend::raw; + +DeviceMonitor::DeviceMonitor() +{ + if(-1 == pipe(monitor_pipe)) + throw std::system_error(errno, std::system_category(), "pipe creation failed"); + + udev_context = udev_new(); + if(!udev_context) + throw std::runtime_error("udev_new failed"); +} + +DeviceMonitor::~DeviceMonitor() +{ + bool is_running = running.try_lock(); + if(is_running) + running.unlock(); + else + this->stop(); + + udev_unref(udev_context); + + for(int i : monitor_pipe) + close(i); +} + +void DeviceMonitor::run() +{ + int ret; + const std::lock_guard run_lock(running); + + struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev_context, "udev"); + if(!monitor) + throw std::runtime_error("udev_monitor_new_from_netlink failed"); + + ret = udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw", nullptr); + if (0 != ret) + throw std::system_error (-ret, std::system_category (), + "udev_monitor_filter_add_match_subsystem_devtype"); + + ret = udev_monitor_enable_receiving(monitor); + if(0 != ret) + throw std::system_error(-ret, std::system_category(), + "udev_moniotr_enable_receiving"); + + this->enumerate(); + + int fd = udev_monitor_get_fd(monitor); + while (true) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(monitor_pipe[0], &fds); + FD_SET(fd, &fds); + if (-1 == select (std::max (monitor_pipe[0], fd)+1, &fds, nullptr, nullptr, nullptr)) { + if (errno == EINTR) + continue; + throw std::system_error (errno, std::system_category(), "udev_monitor select"); + } + if (FD_ISSET(fd, &fds)) { + struct udev_device *device = udev_monitor_receive_device(monitor); + std::string action = udev_device_get_action(device); + std::string devnode = udev_device_get_devnode(device); + if (action == "add") + std::thread([this](const std::string name) { + this->addDevice(name); + }, devnode).detach(); + else if (action == "remove") + std::thread([this](const std::string name) { + this->removeDevice(name); + }, devnode).detach(); + udev_device_unref (device); + } + if (FD_ISSET(monitor_pipe[0], &fds)) { + char c; + if (-1 == read(monitor_pipe[0], &c, sizeof (char))) + throw std::system_error (errno, std::system_category (), + "read pipe"); + break; + } + } +} + +void DeviceMonitor::stop() +{ + +} + +void DeviceMonitor::enumerate() +{ + int ret; + struct udev_enumerate* udev_enum = udev_enumerate_new(udev_context); + ret = udev_enumerate_add_match_subsystem(udev_enum, "hidraw"); + if(0 != ret) + throw std::system_error(-ret, std::system_category(), + "udev_enumerate_add_match_subsystem"); + + ret = udev_enumerate_scan_devices(udev_enum); + if(0 != ret) + throw std::system_error(-ret, std::system_category(), + "udev_enumerate_scan_devices"); + + struct udev_list_entry* udev_enum_entry; + udev_list_entry_foreach(udev_enum_entry, + udev_enumerate_get_list_entry(udev_enum)) + { + const char* name = udev_list_entry_get_name(udev_enum_entry); + + struct udev_device* device = udev_device_new_from_syspath(udev_context, + name); + if(!device) + throw std::runtime_error("udev_device_new_from_syspath failed"); + + std::string devnode = udev_device_get_devnode(device); + udev_device_unref(device); + + std::thread([this](const std::string name) { + this->addDevice(name); + }, devnode).detach(); + } + + udev_enumerate_unref(udev_enum); +} \ No newline at end of file diff --git a/src/logid/backend/raw/DeviceMonitor.h b/src/logid/backend/raw/DeviceMonitor.h new file mode 100644 index 0000000..af2a0c6 --- /dev/null +++ b/src/logid/backend/raw/DeviceMonitor.h @@ -0,0 +1,32 @@ +#ifndef LOGID_BACKEND_RAW_DEVICEMONITOR_H +#define LOGID_BACKEND_RAW_DEVICEMONITOR_H + +#include +#include + +extern "C" +{ +#include +} + +namespace logid::backend::raw +{ + class DeviceMonitor + { + public: + void enumerate(); + void run(); + void stop(); + protected: + DeviceMonitor(); + ~DeviceMonitor(); + virtual void addDevice(std::string device) = 0; + virtual void removeDevice(std::string device) = 0; + private: + struct udev* udev_context; + int monitor_pipe[2]; + std::mutex running; + }; +} + +#endif //LOGID_BACKEND_RAW_DEVICEMONITOR_H \ No newline at end of file diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp new file mode 100644 index 0000000..22513ae --- /dev/null +++ b/src/logid/backend/raw/RawDevice.cpp @@ -0,0 +1,147 @@ +#include "RawDevice.h" + +#include +#include +#include + + +extern "C" +{ +#include +#include +#include +#include +} + +using namespace logid::backend::raw; +using namespace std::chrono; + +RawDevice::RawDevice(std::string path) : path (path) +{ + int ret; + + fd = ::open(path.c_str(), O_RDWR); + if (fd == -1) + throw std::system_error(errno, std::system_category(), "RawDevice open failed"); + + hidraw_devinfo devinfo{}; + if (-1 == ::ioctl(fd, HIDIOCGRAWINFO, &devinfo)) + { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWINFO failed"); + } + vid = devinfo.vendor; + pid = devinfo.product; + + char name_buf[256]; + if (-1 == (ret = ::ioctl(fd, HIDIOCGRAWNAME(sizeof(name_buf)), name_buf))) + { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWNAME failed"); + } + name.assign(name_buf, ret - 1); + + hidraw_report_descriptor _rdesc{}; + if (-1 == ::ioctl(fd, HIDIOCGRDESCSIZE, &_rdesc.size)) + { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESCSIZE failed"); + } + if (-1 == ::ioctl(fd, HIDIOCGRDESC, &_rdesc)) + { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESC failed"); + } + rdesc = std::vector(_rdesc.value, _rdesc.value + _rdesc.size); + + if (-1 == ::pipe(dev_pipe)) + { + int err = errno; + close(fd); + throw std::system_error(err, std::system_category(), "RawDevice pipe open failed"); + } +} + +RawDevice::~RawDevice() +{ + if(fd != -1) + { + ::close(fd); + ::close(dev_pipe[0]); + ::close(dev_pipe[1]); + } +} + +void RawDevice::sendReport(std::vector report) +{ + _sendReport(std::move(report)); +} + +std::vector RawDevice::readReport(std::size_t maxDataLength) +{ + return _readReport(maxDataLength); +} + +void RawDevice::_sendReport(std::vector report) +{ + std::lock_guard lock(dev_io); + int ret = ::write(fd, report.data(), report.size()); + if(ret == -1) + throw std::system_error(errno, std::system_category(), "_sendReport write failed"); +} + +std::vector RawDevice::_readReport(std::size_t maxDataLength) +{ + std::lock_guard lock(dev_io); + int ret; + std::vector report(maxDataLength); + + timeval timeout = { duration_cast(HIDPP_IO_TIMEOUT).count(), + duration_cast(HIDPP_IO_TIMEOUT).count() }; + + fd_set fds; + do { + FD_ZERO(&fds); + FD_SET(fd, &fds); + FD_SET(dev_pipe[0], &fds); + + ret = select(std::max(fd, dev_pipe[0]) + 1, + &fds, nullptr, nullptr, + (HIDPP_IO_TIMEOUT.count() > 0 ? nullptr : &timeout)); + } while(ret == -1 && errno == EINTR); + + if(ret == -1) + throw std::system_error(errno, std::system_category(), "_readReport select failed"); + + if(FD_ISSET(fd, &fds)) + { + ret = read(fd, report.data(), report.size()); + if(ret == -1) + throw std::system_error(errno, std::system_category(), "_readReport read failed"); + report.resize(ret); + } + + if(FD_ISSET(dev_pipe[0], &fds)) + { + char c; + ret = read(dev_pipe[0], &c, sizeof(char)); + if(ret == -1) + throw std::system_error(errno, std::system_category(), "_readReport read pipe failed"); + } + + return report; +} + +void RawDevice::interruptRead() +{ + char c = 0; + if(-1 == write(dev_pipe[1], &c, sizeof(char))) + throw std::system_error(errno, std::system_category(), "interruptRead write pipe failed"); + + // Ensure I/O has halted + std::lock_guard lock(dev_io); +} \ No newline at end of file diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h new file mode 100644 index 0000000..c6c88fc --- /dev/null +++ b/src/logid/backend/raw/RawDevice.h @@ -0,0 +1,41 @@ +#ifndef LOGID_BACKEND_RAWDEVICE_H +#define LOGID_BACKEND_RAWDEVICE_H + +#include +#include +#include + +#define HIDPP_IO_TIMEOUT std::chrono::seconds(2) + +namespace logid::backend::raw +{ + class RawDevice + { + public: + RawDevice(std::string path); + ~RawDevice(); + std::string hidrawPath() const { return path; } + std::vector reportDescriptor() const { return rdesc; } + + /// TODO: Process reports in a queue. + void sendReport(std::vector report); + std::vector readReport(std::size_t maxDataLength); + + void interruptRead(); + private: + std::mutex dev_io; + std::string path; + int fd; + int dev_pipe[2]; + uint16_t vid; + uint16_t pid; + std::string name; + std::vector rdesc; + + /* These will only be used internally and processed with a queue */ + void _sendReport(std::vector report); + std::vector _readReport(std::size_t maxDataLength); + }; +} + +#endif //LOGID_BACKEND_RAWDEVICE_H \ No newline at end of file diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 891574b..562341f 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -4,12 +4,7 @@ #include #include "util.h" -#include "Device.h" -#include "Actions.h" -#include "Configuration.h" -#include "EvdevDevice.h" -#include "DeviceFinder.h" -#include "IPCServer.h" +#include "DeviceMonitor.h" #include "logid.h" #define evdev_name "logid" @@ -25,9 +20,8 @@ using namespace logid; std::string config_file = DEFAULT_CONFIG_FILE; LogLevel logid::global_verbosity = INFO; -Configuration* logid::global_config; -EvdevDevice* logid::global_evdev; -DeviceFinder* logid::finder; +// Configuration* logid::global_config; +DeviceMonitor* logid::finder; bool logid::kill_logid = false; std::mutex logid::finder_reloading; @@ -41,6 +35,7 @@ enum class Option Version }; +/* void logid::reload() { log_printf(INFO, "Reloading logid..."); @@ -50,9 +45,10 @@ void logid::reload() global_config = new Configuration(config_file.c_str()); delete(old_config); delete(finder); - finder = new DeviceFinder(); + finder = new DeviceMonitor(); finder_reloading.unlock(); } + */ void read_cli_options(int argc, char** argv) { @@ -153,10 +149,12 @@ int main(int argc, char** argv) { read_cli_options(argc, argv); + /* // Read config try { global_config = new Configuration(config_file.c_str()); } catch (std::exception &e) { global_config = new Configuration(); } + //Create an evdev device called 'logid' try { global_evdev = new EvdevDevice(evdev_name); } catch(std::system_error& e) @@ -165,8 +163,10 @@ int main(int argc, char** argv) return EXIT_FAILURE; } + */ + // Scan devices, create listeners, handlers, etc. - finder = new DeviceFinder(); + finder = new DeviceMonitor(); while(!kill_logid) { diff --git a/src/logid/logid.h b/src/logid/logid.h index a5dc790..7498e5c 100644 --- a/src/logid/logid.h +++ b/src/logid/logid.h @@ -5,7 +5,7 @@ namespace logid { - void reload(); + // void reload(); extern bool kill_logid; extern std::mutex finder_reloading; diff --git a/src/logid/util.cpp b/src/logid/util.cpp index 32d9aca..7b0218d 100644 --- a/src/logid/util.cpp +++ b/src/logid/util.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "util.h" @@ -34,6 +35,7 @@ const char* logid::level_prefix(LogLevel level) return "DEBUG"; } +/* Direction logid::getDirection(int x, int y) { if(x == 0 && y == 0) return Direction::None; @@ -111,6 +113,8 @@ Action logid::stringToAction(std::string s) throw std::invalid_argument(original_str + " is an invalid action."); } + */ + LogLevel logid::stringToLogLevel(std::string s) { std::string original_str = s; diff --git a/src/logid/util.h b/src/logid/util.h index 7f41cb7..09bea48 100644 --- a/src/logid/util.h +++ b/src/logid/util.h @@ -1,7 +1,7 @@ #ifndef LOGID_UTIL_H #define LOGID_UTIL_H -#include "Actions.h" +//#include "Actions.h" namespace logid { @@ -19,10 +19,12 @@ namespace logid const char* level_prefix(LogLevel level); + /* Direction getDirection(int x, int y); Direction stringToDirection(std::string s); GestureMode stringToGestureMode(std::string s); Action stringToAction(std::string s); + */ LogLevel stringToLogLevel(std::string s); }