diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index e283dcd..4dc86f2 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -42,9 +42,8 @@ add_executable(logid backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp backend/raw/IOMonitor.cpp - backend/dj/Receiver.cpp - backend/dj/ReceiverMonitor.cpp - backend/dj/Error.cpp + backend/hidpp10/Receiver.cpp + backend/hidpp10/ReceiverMonitor.cpp backend/hidpp/Device.cpp backend/hidpp/Report.cpp backend/hidpp10/Error.cpp @@ -64,7 +63,6 @@ add_executable(logid backend/hidpp20/features/ChangeHost.cpp backend/hidpp20/features/WirelessDeviceStatus.cpp backend/hidpp20/features/ThumbWheel.cpp - backend/dj/Report.cpp util/task.cpp util/ExceptionHandler.cpp) diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index 55aa932..697c53a 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -96,9 +97,8 @@ void DeviceManager::addDevice(std::string path) { } try { - hidpp::Device device( - path, hidpp::DefaultDevice, _self.lock(), - config()->io_timeout.value_or(defaults::io_timeout)); + hidpp::Device device(path, hidpp::DefaultDevice, _self.lock(), + config()->io_timeout.value_or(defaults::io_timeout)); isReceiver = device.version() == std::make_tuple(1, 0); } catch (hidpp10::Error& e) { if (e.code() != hidpp10::Error::UnknownDevice) @@ -146,14 +146,12 @@ void DeviceManager::addDevice(std::string path) { if (e.code() != hidpp10::Error::UnknownDevice) throw; else - logPrintf(WARN, - "HID++ 1.0 error while trying to initialize %s:" - "%s", path.c_str(), e.what()); + logPrintf(WARN, "HID++ 1.0 error while trying to initialize %s: %s", + path.c_str(), e.what()); } catch (hidpp::Device::InvalidDevice& e) { // Ignore } catch (std::system_error& e) { // This error should have been thrown previously - logPrintf(WARN, "I/O error on %s: %s", path.c_str(), - e.what()); + logPrintf(WARN, "I/O error on %s: %s", path.c_str(), e.what()); } } } diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index 854b69f..909f972 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -61,8 +62,8 @@ std::shared_ptr Receiver::make( Receiver::Receiver(const std::string& path, const std::shared_ptr& manager) : - dj::ReceiverMonitor(path, manager, - manager->config()->io_timeout.value_or( + hidpp10::ReceiverMonitor(path, manager, + manager->config()->io_timeout.value_or( defaults::io_timeout)), _path(path), _manager(manager), _nickname(manager), _ipc_node(manager->receiversNode()->make_child(_nickname)), @@ -162,7 +163,7 @@ const std::string& Receiver::path() const { return _path; } -std::shared_ptr Receiver::rawReceiver() { +std::shared_ptr Receiver::rawReceiver() { return receiver(); } diff --git a/src/logid/Receiver.h b/src/logid/Receiver.h index 8cb3de9..14d8a96 100644 --- a/src/logid/Receiver.h +++ b/src/logid/Receiver.h @@ -21,7 +21,7 @@ #include #include -#include +#include namespace logid { class ReceiverNickname { @@ -41,7 +41,7 @@ namespace logid { const std::weak_ptr _manager; }; - class Receiver : public backend::dj::ReceiverMonitor, + class Receiver : public backend::hidpp10::ReceiverMonitor, public ipcgull::object { public: typedef std::map> @@ -55,7 +55,7 @@ namespace logid { [[nodiscard]] const std::string& path() const; - std::shared_ptr rawReceiver(); + std::shared_ptr rawReceiver(); [[nodiscard]] const DeviceList& devices() const; diff --git a/src/logid/backend/dj/Error.cpp b/src/logid/backend/dj/Error.cpp deleted file mode 100644 index 8ee6d4e..0000000 --- a/src/logid/backend/dj/Error.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#include - -using namespace logid::backend::dj; - -Error::Error(uint8_t code) : _code(code) { -} - -const char* Error::what() const noexcept { - switch (_code) { - case Unknown: - return "Unknown"; - case KeepAliveTimeout: - return "Keep-alive timeout"; - default: - return "Reserved"; - } -} - -uint8_t Error::code() const noexcept { - return _code; -} \ No newline at end of file diff --git a/src/logid/backend/dj/Error.h b/src/logid/backend/dj/Error.h deleted file mode 100644 index 92425e0..0000000 --- a/src/logid/backend/dj/Error.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#ifndef LOGID_BACKEND_DJ_ERROR_H -#define LOGID_BACKEND_DJ_ERROR_H - -#include -#include - -namespace logid::backend::dj { - class Error : public std::exception { - public: - enum ErrorCode : uint8_t { - Unknown = 0x00, - KeepAliveTimeout = 0x01 - }; - - explicit Error(uint8_t code); - - [[nodiscard]] const char* what() const noexcept override; - - [[nodiscard]] uint8_t code() const noexcept; - - private: - uint8_t _code; - }; -} - -#endif //LOGID_BACKEND_DJ_ERROR_H \ No newline at end of file diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp deleted file mode 100644 index b51efe9..0000000 --- a/src/logid/backend/dj/Receiver.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -using namespace logid::backend::dj; -using namespace logid::backend; - -InvalidReceiver::InvalidReceiver(Reason reason) : _reason(reason) { -} - -const char* InvalidReceiver::what() const noexcept { - switch (_reason) { - case NoDJReports: - return "No DJ reports"; - default: - return "Invalid receiver"; - } -} - -InvalidReceiver::Reason InvalidReceiver::code() const noexcept { - return _reason; -} - -Receiver::Receiver(std::string path, - const std::shared_ptr& monitor, - double timeout) : - _raw_device(std::make_shared(std::move(path), monitor)), - _hidpp10_device(_raw_device, hidpp::DefaultDevice, timeout) { - if (!supportsDjReports(_raw_device->reportDescriptor())) - throw InvalidReceiver(InvalidReceiver::NoDJReports); - - // Pass all HID++ events on DefaultDevice to handleHidppEvent - _raw_hidpp_handler = _raw_device->addEventHandler( - { - [](const std::vector& report) -> bool { - return (report[hidpp::Offset::Type] == - hidpp::Report::Type::Short || - report[hidpp::Offset::Type] == - hidpp::Report::Type::Long); - }, - [this](const std::vector& report) -> void { - hidpp::Report _report(report); - this->_handleHidppEvent(_report); - } - }); - - // Pass all DJ events with device index to handleDjEvent - _raw_dj_handler = _raw_device->addEventHandler( - { - [](const std::vector& report) -> bool { - return (report[Offset::Type] == - Report::Type::Short || - report[Offset::Type] == - Report::Type::Long); - }, - [this](const std::vector& report) -> void { - Report _report(report); - this->_handleDjEvent(_report); - } - }); -} - -Receiver::~Receiver() { - _raw_device->removeEventHandler(_raw_dj_handler); - _raw_device->removeEventHandler(_raw_hidpp_handler); -} - -void Receiver::enumerateDj() { - _sendDjRequest(hidpp::DefaultDevice, GetPairedDevices, {}); -} - -Receiver::NotificationFlags Receiver::getHidppNotifications() { - auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {}, - hidpp::ReportType::Short); - - NotificationFlags flags{}; - flags.deviceBatteryStatus = response[0] & (1 << 4); - flags.receiverWirelessNotifications = response[1] & (1 << 0); - flags.receiverSoftwarePresent = response[1] & (1 << 3); - - return flags; -} - -void Receiver::enableHidppNotifications(NotificationFlags flags) { - std::vector request(3); - - if (flags.deviceBatteryStatus) - request[0] |= (1 << 4); - if (flags.receiverWirelessNotifications) - request[1] |= 1; - if (flags.receiverSoftwarePresent) - request[1] |= (1 << 3); - - _hidpp10_device.setRegister(EnableHidppNotifications, request, - hidpp::ReportType::Short); -} - -void Receiver::enumerateHidpp() { - /* This isn't in the documentation but this is how solaar does it - * All I know is that when (p0 & 2), devices are enumerated - */ - _hidpp10_device.setRegister(ConnectionState, {2}, - hidpp::ReportType::Short); -} - -///TODO: Investigate usage -uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index) { - auto response = _hidpp10_device.getRegister(ConnectionState, {index}, - hidpp::ReportType::Short); - - return response[0]; -} - -void Receiver::startPairing(uint8_t timeout) { - ///TODO: Device number == Device index? - std::vector request(3); - - request[0] = 1; - request[1] = hidpp::DefaultDevice; - request[2] = timeout; - - _hidpp10_device.setRegister(DevicePairing, request, - hidpp::ReportType::Short); -} - -void Receiver::stopPairing() { - ///TODO: Device number == Device index? - std::vector request(3); - - request[0] = 2; - request[1] = hidpp::DefaultDevice; - - _hidpp10_device.setRegister(DevicePairing, request, - hidpp::ReportType::Short); -} - -void Receiver::disconnect(hidpp::DeviceIndex index) { - ///TODO: Device number == Device index? - std::vector request(3); - - request[0] = 3; - request[1] = index; - - _hidpp10_device.setRegister(DevicePairing, request, - hidpp::ReportType::Short); -} - -std::map Receiver::getDeviceActivity() { - auto response = _hidpp10_device.getRegister(DeviceActivity, {}, - hidpp::ReportType::Long); - - std::map device_activity; - for (uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++) - device_activity[static_cast(i)] = response[i]; - - return device_activity; -} - -struct Receiver::PairingInfo -Receiver::getPairingInfo(hidpp::DeviceIndex index) { - std::vector request(1); - request[0] = index; - request[0] += 0x1f; - - auto response = _hidpp10_device.getRegister(PairingInfo, request, - hidpp::ReportType::Long); - - struct PairingInfo info{}; - info.destinationId = response[1]; - info.reportInterval = response[2]; - info.pid = response[4]; - info.pid |= (response[3] << 8); - info.deviceType = static_cast(response[7]); - - return info; -} - -struct Receiver::ExtendedPairingInfo -Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index) { - std::vector request(1); - request[0] = index; - request[0] += 0x2f; - - auto response = _hidpp10_device.getRegister(PairingInfo, request, - hidpp::ReportType::Long); - - ExtendedPairingInfo info{}; - - info.serialNumber = 0; - for (uint8_t i = 0; i < 4; i++) - info.serialNumber |= (response[i + 1] << 8 * i); - - for (uint8_t i = 0; i < 4; i++) - info.reportTypes[i] = response[i + 5]; - - uint8_t psl = response[8] & 0xf; - if (psl > 0xc) - info.powerSwitchLocation = PowerSwitchLocation::Reserved; - else - info.powerSwitchLocation = static_cast(psl); - - return info; -} - -std::string Receiver::getDeviceName(hidpp::DeviceIndex index) { - std::vector request(1); - request[0] = index; - request[0] += 0x3f; - - auto response = _hidpp10_device.getRegister(PairingInfo, request, - hidpp::ReportType::Long); - - uint8_t size = response[1]; - assert(size <= 14); - - std::string name(size, ' '); - for (std::size_t i = 0; i < size; i++) - name[i] = (char) (response[i + 2]); - - return name; -} - -hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(const hidpp::Report& -report) { - assert(report.subId() == DeviceDisconnection); - return report.deviceIndex(); -} - -hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(const - hidpp::Report& report) { - assert(report.subId() == DeviceConnection); - - hidpp::DeviceConnectionEvent event{}; - - event.index = report.deviceIndex(); - event.unifying = ((report.address() & 0b111) == 0x04); - - event.deviceType = static_cast( - report.paramBegin()[0] & 0x0f); - event.softwarePresent = report.paramBegin()[0] & (1 << 4); - event.encrypted = report.paramBegin()[0] & (1 << 5); - event.linkEstablished = !(report.paramBegin()[0] & (1 << 6)); - event.withPayload = report.paramBegin()[0] & (1 << 7); - event.fromTimeoutCheck = false; - - event.pid = (report.paramBegin()[2] << 8); - event.pid |= report.paramBegin()[1]; - - return event; -} - -void Receiver::_handleDjEvent(Report& report) { - for (auto& handler: _dj_event_handlers) - if (handler.second->condition(report)) - handler.second->callback(report); -} - -void Receiver::_handleHidppEvent(hidpp::Report& report) { - for (auto& handler: _hidpp_event_handlers) - if (handler.second->condition(report)) - handler.second->callback(report); -} - -void Receiver::addDjEventHandler(const std::string& nickname, - const std::shared_ptr& handler) { - assert(_dj_event_handlers.find(nickname) == _dj_event_handlers.end()); - _dj_event_handlers.emplace(nickname, handler); -} - -void Receiver::removeDjEventHandler(const std::string& nickname) { - _dj_event_handlers.erase(nickname); -} - -const std::map>& -Receiver::djEventHandlers() { - return _dj_event_handlers; -} - -void Receiver::addHidppEventHandler(const std::string& nickname, - const std::shared_ptr& handler) { - assert(_hidpp_event_handlers.find(nickname) == _hidpp_event_handlers.end()); - _hidpp_event_handlers.emplace(nickname, handler); -} - -void Receiver::removeHidppEventHandler(const std::string& nickname) { - _hidpp_event_handlers.erase(nickname); -} - -const std::map>& -Receiver::hidppEventHandlers() { - return _hidpp_event_handlers; -} - -void Receiver::_sendDjRequest(hidpp::DeviceIndex index, uint8_t function, - const std::vector&& params) { - assert(params.size() <= LongParamLength); - - Report::Type type = params.size() <= ShortParamLength ? - ReportType::Short : ReportType::Long; - - Report request(type, index, function); - - std::copy(params.begin(), params.end(), request.paramBegin()); - - _raw_device->sendReport(request.rawData()); -} - -Receiver::ConnectionStatusEvent Receiver::connectionStatusEvent(Report& report) { - assert(report.feature() == ConnectionStatus); - ConnectionStatusEvent event{}; - event.index = report.index(); - event.linkLost = report.paramBegin()[0]; - return event; -} - -std::shared_ptr Receiver::rawDevice() const { - return _raw_device; -} \ No newline at end of file diff --git a/src/logid/backend/dj/Report.cpp b/src/logid/backend/dj/Report.cpp deleted file mode 100644 index 8be886a..0000000 --- a/src/logid/backend/dj/Report.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -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 -}; - -static const std::array DJReportDesc2 = { - 0xA1, 0x01, // Collection (Application) - 0x85, 0x20, // Report ID (32) - 0x75, 0x08, // Report Size (8) - 0x95, 0x0E, // Report Count (14) - 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) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x00, // Logical Maximum (255) - 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(const std::vector& report_desc) { - auto it = std::search(report_desc.begin(), report_desc.end(), - DJReportDesc.begin(), DJReportDesc.end()); - if (it == report_desc.end()) - it = std::search(report_desc.begin(), report_desc.end(), - DJReportDesc2.begin(), DJReportDesc2.end()); - return it != report_desc.end(); -} - -Report::Report(const std::vector& data) : _data(data) { - switch (data[Offset::Type]) { - case ReportType::Short: - _data.resize(HeaderLength + ShortParamLength); - break; - case ReportType::Long: - _data.resize(HeaderLength + LongParamLength); - break; - default: - assert(false); - } -} - -Report::Report(Report::Type type, hidpp::DeviceIndex index, uint8_t feature) { - switch (type) { - case ReportType::Short: - _data.resize(HeaderLength + ShortParamLength); - break; - case ReportType::Long: - _data.resize(HeaderLength + LongParamLength); - break; - default: - assert(false); - } - - _data[Offset::Type] = type; - _data[Offset::DeviceIndex] = index; - _data[Offset::Feature] = feature; -} - - -Report::Type Report::type() const { - return static_cast(_data[Offset::Type]); -} - -hidpp::DeviceIndex Report::index() const { - return static_cast(_data[Offset::DeviceIndex]); -} - -uint8_t Report::feature() const { - return _data[Offset::Feature]; -} - -std::vector::iterator Report::paramBegin() { - return _data.begin() + Offset::Parameters; -} - -const std::vector& Report::rawData() const { - return _data; -} diff --git a/src/logid/backend/dj/Report.h b/src/logid/backend/dj/Report.h deleted file mode 100644 index 8ddae8b..0000000 --- a/src/logid/backend/dj/Report.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#ifndef LOGID_BACKEND_DJ_REPORT_H -#define LOGID_BACKEND_DJ_REPORT_H - -#include -#include -#include -#include - -namespace logid::backend::dj { - namespace Offset { - static constexpr uint8_t Type = 0; - static constexpr uint8_t DeviceIndex = 1; - static constexpr uint8_t Feature = 2; - static constexpr uint8_t Parameters = 3; - } - - bool supportsDjReports(const std::vector& report_desc); - - class Report { - public: - typedef ReportType::ReportType Type; - - explicit Report(const std::vector& data); - - Report(Type type, hidpp::DeviceIndex index, uint8_t feature); - - [[nodiscard]] Type type() const; - - [[nodiscard]] hidpp::DeviceIndex index() const; - - [[nodiscard]] uint8_t feature() const; - - std::vector::iterator paramBegin(); - - [[nodiscard]] const std::vector& rawData() const; - - private: - std::vector _data; - }; -} - -#endif //LOGID_BACKEND_DJ_REPORT_H diff --git a/src/logid/backend/dj/defs.h b/src/logid/backend/dj/defs.h deleted file mode 100644 index 9d8d5a5..0000000 --- a/src/logid/backend/dj/defs.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2019-2023 PixlOne - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#ifndef LOGID_BACKEND_DJ_DEFS_H -#define LOGID_BACKEND_DJ_DEFS_H - -#include - -namespace logid::backend::dj { - namespace ReportType { - enum ReportType : uint8_t { - Short = 0x20, - Long = 0x21 - }; - } - - namespace DeviceType { - enum DeviceType : uint8_t { - Unknown = 0x00, - Keyboard = 0x01, - Mouse = 0x02, - Numpad = 0x03, - Presenter = 0x04, - /* 0x05-0x07 is reserved */ - Trackball = 0x08, - Touchpad = 0x09 - }; - } - - [[maybe_unused]] - static constexpr uint8_t ErrorFeature = 0x7f; - - static constexpr std::size_t HeaderLength = 3; - static constexpr std::size_t ShortParamLength = 12; - static constexpr std::size_t LongParamLength = 29; -} - -#endif //LOGID_BACKEND_DJ_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 31c4d47..d90e3ae 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -20,8 +20,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -68,7 +67,7 @@ Device::Device(std::shared_ptr raw_device, DeviceIndex index, _init(); } -Device::Device(const std::shared_ptr& receiver, +Device::Device(const std::shared_ptr& receiver, hidpp::DeviceConnectionEvent event, double timeout) : io_timeout(duration_cast( duration(timeout))), @@ -85,7 +84,7 @@ Device::Device(const std::shared_ptr& receiver, _init(); } -Device::Device(const std::shared_ptr& receiver, +Device::Device(const std::shared_ptr& receiver, DeviceIndex index, double timeout) : io_timeout(duration_cast( duration(timeout))), @@ -130,8 +129,7 @@ void Device::_init() { }}); try { - auto rsp = sendReport( - {ReportType::Short, _index, + auto rsp = sendReport({ReportType::Short, _index, hidpp20::FeatureID::ROOT, hidpp20::Root::Ping, hidpp::softwareID}); if (rsp.deviceIndex() != _index) { @@ -253,7 +251,7 @@ Report Device::sendReport(const Report& report) { if (std::holds_alternative(response)) { return std::get(response); } else if(std::holds_alternative(response)) { - throw hidpp20::Error(std::get(response).error_code); + throw hidpp10::Error(std::get(response).error_code); } else if(std::holds_alternative(response)) { throw hidpp20::Error(std::get(response).error_code); } @@ -289,6 +287,10 @@ bool Device::responseReport(const Report& report) { } +const std::shared_ptr& Device::rawDevice() const { + return _raw_device; +} + void Device::_sendReport(Report report) { reportFixup(report); _raw_device->sendReport(report.rawReport()); diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 9c286f3..766b1b2 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -29,7 +29,7 @@ #include #include -namespace logid::backend::dj { +namespace logid::backend::hidpp10 { // Need to define here for a constructor class Receiver; } @@ -70,10 +70,10 @@ namespace logid::backend::hidpp { Device(std::shared_ptr raw_device, DeviceIndex index, double timeout); - Device(const std::shared_ptr& receiver, + Device(const std::shared_ptr& receiver, hidpp::DeviceConnectionEvent event, double timeout); - Device(const std::shared_ptr& receiver, + Device(const std::shared_ptr& receiver, DeviceIndex index, double timeout); virtual ~Device(); @@ -98,6 +98,8 @@ namespace logid::backend::hidpp { void handleEvent(Report& report); + [[nodiscard]] const std::shared_ptr& rawDevice() const; + protected: // Returns whether the report is a response virtual bool responseReport(const Report& report); @@ -116,7 +118,7 @@ namespace logid::backend::hidpp { std::shared_ptr _raw_device; raw::RawDevice::EvHandlerId _raw_handler; - std::shared_ptr _receiver; + std::shared_ptr _receiver; std::string _path; DeviceIndex _index; diff --git a/src/logid/backend/hidpp10/Device.cpp b/src/logid/backend/hidpp10/Device.cpp index ae643cd..9aca401 100644 --- a/src/logid/backend/hidpp10/Device.cpp +++ b/src/logid/backend/hidpp10/Device.cpp @@ -35,7 +35,7 @@ Device::Device(std::shared_ptr raw_dev, hidpp::DeviceIndex index assert(version() == std::make_tuple(1, 0)); } -Device::Device(const std::shared_ptr& receiver, +Device::Device(const std::shared_ptr& receiver, hidpp::DeviceIndex index, double timeout) : hidpp::Device(receiver, index, timeout) { diff --git a/src/logid/backend/hidpp10/Device.h b/src/logid/backend/hidpp10/Device.h index a63222f..8301f8b 100644 --- a/src/logid/backend/hidpp10/Device.h +++ b/src/logid/backend/hidpp10/Device.h @@ -34,7 +34,7 @@ namespace logid::backend::hidpp10 { Device(std::shared_ptr raw_dev, hidpp::DeviceIndex index, double timeout); - Device(const std::shared_ptr& receiver, + Device(const std::shared_ptr& receiver, hidpp::DeviceIndex index, double timeout); hidpp::Report sendReport(const hidpp::Report& report) final; diff --git a/src/logid/backend/hidpp10/Receiver.cpp b/src/logid/backend/hidpp10/Receiver.cpp new file mode 100644 index 0000000..094af5b --- /dev/null +++ b/src/logid/backend/hidpp10/Receiver.cpp @@ -0,0 +1,203 @@ +/* + * Copyright 2019-2023 PixlOne + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +using namespace logid::backend::hidpp10; +using namespace logid::backend; + +const char* InvalidReceiver::what() const noexcept { + return "Not a receiver"; +} + +Receiver::Receiver(const std::string& path, const std::shared_ptr& monitor, + double timeout) : Device(path, hidpp::DefaultDevice, monitor, timeout) { + // Check if the device is a receiver + try { + getNotificationFlags(); + } catch(hidpp10::Error& e) { + if (e.code() == Error::InvalidAddress) + throw InvalidReceiver(); + } +} + +Receiver::NotificationFlags Receiver::getNotificationFlags() { + auto response = getRegister(EnableHidppNotifications, {}, hidpp::ReportType::Short); + + NotificationFlags flags{}; + flags.deviceBatteryStatus = response[0] & (1 << 4); + flags.receiverWirelessNotifications = response[1] & (1 << 0); + flags.receiverSoftwarePresent = response[1] & (1 << 3); + + return flags; +} + +void Receiver::setNotifications(NotificationFlags flags) { + std::vector request(3); + + if (flags.deviceBatteryStatus) + request[0] |= (1 << 4); + if (flags.receiverWirelessNotifications) + request[1] |= 1; + if (flags.receiverSoftwarePresent) + request[1] |= (1 << 3); + + setRegister(EnableHidppNotifications, request, hidpp::ReportType::Short); +} + +void Receiver::enumerate() { + setRegister(ConnectionState, {2}, hidpp::ReportType::Short); +} + +///TODO: Investigate usage +uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index) { + auto response = getRegister(ConnectionState, {index}, hidpp::ReportType::Short); + + return response[0]; +} + +void Receiver::startPairing(uint8_t timeout) { + ///TODO: Device number == Device index? + std::vector request(3); + + request[0] = 1; + request[1] = hidpp::DefaultDevice; + request[2] = timeout; + + setRegister(DevicePairing, request, hidpp::ReportType::Short); +} + +void Receiver::stopPairing() { + ///TODO: Device number == Device index? + std::vector request(3); + + request[0] = 2; + request[1] = hidpp::DefaultDevice; + + setRegister(DevicePairing, request, hidpp::ReportType::Short); +} + +void Receiver::disconnect(hidpp::DeviceIndex index) { + ///TODO: Device number == Device index? + std::vector request(3); + + request[0] = 3; + request[1] = index; + + setRegister(DevicePairing, request, hidpp::ReportType::Short); +} + +std::map Receiver::getDeviceActivity() { + auto response = getRegister(DeviceActivity, {}, hidpp::ReportType::Long); + + std::map device_activity; + for (uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++) + device_activity[static_cast(i)] = response[i]; + + return device_activity; +} + +struct Receiver::PairingInfo +Receiver::getPairingInfo(hidpp::DeviceIndex index) { + std::vector request(1); + request[0] = index; + request[0] += 0x1f; + + auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long); + + struct PairingInfo info{}; + info.destinationId = response[1]; + info.reportInterval = response[2]; + info.pid = response[4]; + info.pid |= (response[3] << 8); + info.deviceType = static_cast(response[7]); + + return info; +} + +struct Receiver::ExtendedPairingInfo +Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index) { + std::vector request(1); + request[0] = index; + request[0] += 0x2f; + + auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long); + + ExtendedPairingInfo info{}; + + info.serialNumber = 0; + for (uint8_t i = 0; i < 4; i++) + info.serialNumber |= (response[i + 1] << 8 * i); + + for (uint8_t i = 0; i < 4; i++) + info.reportTypes[i] = response[i + 5]; + + uint8_t psl = response[8] & 0xf; + if (psl > 0xc) + info.powerSwitchLocation = PowerSwitchLocation::Reserved; + else + info.powerSwitchLocation = static_cast(psl); + + return info; +} + +std::string Receiver::getDeviceName(hidpp::DeviceIndex index) { + std::vector request(1); + request[0] = index; + request[0] += 0x3f; + + auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long); + + uint8_t size = response[1]; + assert(size <= 14); + + std::string name(size, ' '); + for (std::size_t i = 0; i < size; i++) + name[i] = (char) (response[i + 2]); + + return name; +} + +hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(const hidpp::Report& +report) { + assert(report.subId() == DeviceDisconnection); + return report.deviceIndex(); +} + +hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(const hidpp::Report& report) { + assert(report.subId() == DeviceConnection); + + hidpp::DeviceConnectionEvent event{}; + + event.index = report.deviceIndex(); + event.unifying = ((report.address() & 0b111) == 0x04); + + event.deviceType = static_cast(report.paramBegin()[0] & 0x0f); + event.softwarePresent = report.paramBegin()[0] & (1 << 4); + event.encrypted = report.paramBegin()[0] & (1 << 5); + event.linkEstablished = !(report.paramBegin()[0] & (1 << 6)); + event.withPayload = report.paramBegin()[0] & (1 << 7); + event.fromTimeoutCheck = false; + + event.pid = (report.paramBegin()[2] << 8); + event.pid |= report.paramBegin()[1]; + + return event; +} diff --git a/src/logid/backend/dj/Receiver.h b/src/logid/backend/hidpp10/Receiver.h similarity index 53% rename from src/logid/backend/dj/Receiver.h rename to src/logid/backend/hidpp10/Receiver.h index d21c6c1..f376620 100644 --- a/src/logid/backend/dj/Receiver.h +++ b/src/logid/backend/hidpp10/Receiver.h @@ -20,75 +20,59 @@ #define LOGID_BACKEND_DJ_RECEIVER_H #include -#include -#include -#include #include -namespace logid::backend::dj { - struct EventHandler { - std::function condition; - std::function callback; +namespace logid::backend::hidpp { + enum DeviceType : uint8_t { + DeviceUnknown = 0x00, + DeviceKeyboard = 0x01, + DeviceMouse = 0x02, + DeviceNumpad = 0x03, + DevicePresenter = 0x04, + /* 0x05-0x07 is reserved */ + DeviceTrackball = 0x08, + DeviceTouchpad = 0x09 }; + struct DeviceConnectionEvent { + DeviceIndex index; + uint16_t pid{}; + DeviceType deviceType = DeviceUnknown; + bool unifying{}; + bool softwarePresent{}; + bool encrypted{}; + bool linkEstablished{}; + bool withPayload{}; + bool fromTimeoutCheck = false; // Fake field + }; +} + +namespace logid::backend::hidpp10 { + class InvalidReceiver : public std::exception { public: - enum Reason { - NoDJReports - }; - - explicit InvalidReceiver(Reason reason); - [[nodiscard]] const char* what() const noexcept override; - - [[nodiscard]] Reason code() const noexcept; - - private: - Reason _reason; }; - class Receiver final { + class Receiver : public Device { public: - Receiver(std::string path, + Receiver(const std::string& path, const std::shared_ptr& monitor, double timeout); - ~Receiver(); - - enum DjEvents : uint8_t { - DeviceDisconnection = 0x40, - DeviceConnection = 0x41, - ConnectionStatus = 0x42 - }; - - enum DjCommands : uint8_t { - /* Kernel driver should handle this */ - SwitchAndKeepAlive [[maybe_unused]] = 0x80, - GetPairedDevices = 0x81 - }; - - void enumerateDj(); - - struct ConnectionStatusEvent { - hidpp::DeviceIndex index; - bool linkLost; - }; - - ConnectionStatusEvent connectionStatusEvent(dj::Report& report); - /* The following functions deal with HID++ 1.0 features. * While these are not technically DJ functions, it is redundant * to have a separate hidpp10::Receiver class for these functions. */ - enum HidppEvents : uint8_t { + enum Events : uint8_t { // These events are identical to their DJ counterparts - // DeviceDisconnection = 0x40, - // DeviceConnection = 0x41, + DeviceDisconnection = 0x40, + DeviceConnection = 0x41, LockingChange = 0x4a }; - enum HidppRegisters : uint8_t { + enum Registers : uint8_t { EnableHidppNotifications = 0x00, ConnectionState = 0x02, DevicePairing = 0xb2, @@ -102,11 +86,11 @@ namespace logid::backend::dj { bool receiverSoftwarePresent; }; - NotificationFlags getHidppNotifications(); + NotificationFlags getNotificationFlags(); - void enableHidppNotifications(NotificationFlags flags); + void setNotifications(NotificationFlags flags); - void enumerateHidpp(); + void enumerate(); uint8_t getConnectionState(hidpp::DeviceIndex index); @@ -122,7 +106,7 @@ namespace logid::backend::dj { uint8_t destinationId; uint8_t reportInterval; uint16_t pid; - DeviceType::DeviceType deviceType; + hidpp::DeviceType deviceType; }; enum class PowerSwitchLocation : uint8_t { @@ -149,67 +133,13 @@ namespace logid::backend::dj { struct PairingInfo getPairingInfo(hidpp::DeviceIndex index); - struct ExtendedPairingInfo getExtendedPairingInfo(hidpp::DeviceIndex - index); + struct ExtendedPairingInfo getExtendedPairingInfo(hidpp::DeviceIndex index); std::string getDeviceName(hidpp::DeviceIndex index); - static hidpp::DeviceIndex deviceDisconnectionEvent( - const hidpp::Report& report); + static hidpp::DeviceIndex deviceDisconnectionEvent(const hidpp::Report& report); - static hidpp::DeviceConnectionEvent deviceConnectionEvent( - const hidpp::Report& report); - - void addDjEventHandler(const std::string& nickname, - const std::shared_ptr& handler); - - void removeDjEventHandler(const std::string& nickname); - - const std::map>& - djEventHandlers(); - - void addHidppEventHandler(const std::string& nickname, - const std::shared_ptr& handler); - - void removeHidppEventHandler(const std::string& nickname); - - const std::map>& - hidppEventHandlers(); - - [[nodiscard]] std::shared_ptr rawDevice() const; - - private: - void _sendDjRequest(hidpp::DeviceIndex index, uint8_t function, - const std::vector&& params); - - void _handleDjEvent(dj::Report& report); - - void _handleHidppEvent(hidpp::Report& report); - - raw::RawDevice::EvHandlerId _raw_hidpp_handler; - raw::RawDevice::EvHandlerId _raw_dj_handler; - - std::map> - _dj_event_handlers; - std::map> - _hidpp_event_handlers; - - std::shared_ptr _raw_device; - hidpp10::Device _hidpp10_device; - }; -} - -namespace logid::backend::hidpp { - struct DeviceConnectionEvent { - hidpp::DeviceIndex index; - uint16_t pid{}; - dj::DeviceType::DeviceType deviceType; - bool unifying{}; - bool softwarePresent{}; - bool encrypted{}; - bool linkEstablished{}; - bool withPayload{}; - bool fromTimeoutCheck = false; // Fake field + static hidpp::DeviceConnectionEvent deviceConnectionEvent(const hidpp::Report& report); }; } diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/hidpp10/ReceiverMonitor.cpp similarity index 58% rename from src/logid/backend/dj/ReceiverMonitor.cpp rename to src/logid/backend/hidpp10/ReceiverMonitor.cpp index 8570cfa..6401ccc 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/hidpp10/ReceiverMonitor.cpp @@ -16,43 +16,36 @@ * */ -#include -#include +#include #include #include #include -using namespace logid::backend::dj; +using namespace logid::backend::hidpp10; +using namespace logid::backend::hidpp; -ReceiverMonitor::ReceiverMonitor(std::string path, - const std::shared_ptr& monitor, - double timeout) : - _receiver(std::make_shared( - std::move(path), monitor, timeout)) { - assert(!_receiver->hidppEventHandlers().contains(ev_handler_name)); - assert(!_receiver->djEventHandlers().contains(ev_handler_name)); +ReceiverMonitor::ReceiverMonitor(const std::string& path, + const std::shared_ptr& monitor, double timeout) + : _receiver(std::make_shared(path, monitor, timeout)) { - Receiver::NotificationFlags notification_flags{ - true, - true, - true}; - _receiver->enableHidppNotifications(notification_flags); + Receiver::NotificationFlags notification_flags{true, true, true}; + _receiver->setNotifications(notification_flags); } ReceiverMonitor::~ReceiverMonitor() { - _receiver->removeHidppEventHandler(ev_handler_name); + if (ev_handler.has_value()) + _receiver->removeEventHandler(ev_handler.value()); } void ReceiverMonitor::ready() { - if (!_receiver->hidppEventHandlers().contains(ev_handler_name)) { - std::shared_ptr event_handler = - std::make_shared(); - event_handler->condition = [](hidpp::Report& report) -> bool { + if (!ev_handler.has_value()) { + hidpp::EventHandler event_handler; + event_handler.condition = [](hidpp::Report& report) -> bool { return (report.subId() == Receiver::DeviceConnection || report.subId() == Receiver::DeviceDisconnection); }; - event_handler->callback = [this](hidpp::Report& report) -> void { + event_handler.callback = [this](hidpp::Report& report) -> void { /* Running in a new thread prevents deadlocks since the * receiver may be enumerating. */ @@ -80,14 +73,14 @@ void ReceiverMonitor::ready() { }); }; - _receiver->addHidppEventHandler(ev_handler_name, event_handler); + ev_handler = _receiver->addEventHandler(event_handler); } enumerate(); } void ReceiverMonitor::enumerate() { - _receiver->enumerateHidpp(); + _receiver->enumerate(); } void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) { @@ -96,8 +89,7 @@ void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) { *handler_id = _receiver->rawDevice()->addEventHandler( { [index](const std::vector& report) -> bool { - return report[Offset::DeviceIndex] == - index; + return report[Offset::DeviceIndex] == index; }, [this, index, handler_id]( [[maybe_unused]] const std::vector& report) { @@ -107,25 +99,17 @@ void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) { event.index = index; event.fromTimeoutCheck = true; - spawn_task( - [this, event, handler_id]() { - assert(handler_id); - try { - _receiver->rawDevice()->removeEventHandler( - *handler_id); - addDevice( - event); - } catch ( - std::exception& e) { - logPrintf( - ERROR, - "Failed to add device %d to receiver " - "on %s: %s", - event.index, - _receiver->rawDevice()->rawPath().c_str(), - e.what()); - } - }); + spawn_task([this, event, handler_id]() { + assert(handler_id); + try { + _receiver->rawDevice()->removeEventHandler(*handler_id); + addDevice(event); + } catch (std::exception& e) { + logPrintf(ERROR, "Failed to add device %d to receiver on %s: %s", + event.index, _receiver->rawDevice()->rawPath().c_str(), + e.what()); + } + }); } }); } diff --git a/src/logid/backend/dj/ReceiverMonitor.h b/src/logid/backend/hidpp10/ReceiverMonitor.h similarity index 90% rename from src/logid/backend/dj/ReceiverMonitor.h rename to src/logid/backend/hidpp10/ReceiverMonitor.h index 59ce595..c147e80 100644 --- a/src/logid/backend/dj/ReceiverMonitor.h +++ b/src/logid/backend/hidpp10/ReceiverMonitor.h @@ -19,16 +19,16 @@ #ifndef LOGID_BACKEND_DJ_RECEIVERMONITOR_H #define LOGID_BACKEND_DJ_RECEIVERMONITOR_H +#include +#include #include #include -#include -#include -namespace logid::backend::dj { +namespace logid::backend::hidpp10 { // This class will run on the RawDevice thread, class ReceiverMonitor { public: - ReceiverMonitor(std::string path, + ReceiverMonitor(const std::string& path, const std::shared_ptr& monitor, double timeout); @@ -55,9 +55,9 @@ namespace logid::backend::dj { [[nodiscard]] std::shared_ptr receiver() const; private: - static constexpr const char* ev_handler_name = "receiver_monitor"; - std::shared_ptr _receiver; + + std::optional ev_handler; }; } diff --git a/src/logid/backend/hidpp20/Device.cpp b/src/logid/backend/hidpp20/Device.cpp index bcf5aeb..4d8bc0a 100644 --- a/src/logid/backend/hidpp20/Device.cpp +++ b/src/logid/backend/hidpp20/Device.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include using namespace logid::backend; using namespace logid::backend::hidpp20; @@ -39,14 +39,14 @@ Device::Device(std::shared_ptr raw_device, throw std::runtime_error("Invalid HID++ version"); } -Device::Device(const std::shared_ptr& receiver, +Device::Device(const std::shared_ptr& receiver, hidpp::DeviceConnectionEvent event, double timeout) : hidpp::Device(receiver, event, timeout) { if (std::get<0>(version()) < 2) throw std::runtime_error("Invalid HID++ version"); } -Device::Device(const std::shared_ptr& receiver, +Device::Device(const std::shared_ptr& receiver, hidpp::DeviceIndex index, double timeout) : hidpp::Device(receiver, index, timeout) { if (std::get<0>(version()) < 2) diff --git a/src/logid/backend/hidpp20/Device.h b/src/logid/backend/hidpp20/Device.h index cb9f55f..3ce82f9 100644 --- a/src/logid/backend/hidpp20/Device.h +++ b/src/logid/backend/hidpp20/Device.h @@ -34,10 +34,10 @@ namespace logid::backend::hidpp20 { Device(std::shared_ptr raw_device, hidpp::DeviceIndex index, double timeout); - Device(const std::shared_ptr& receiver, + Device(const std::shared_ptr& receiver, hidpp::DeviceConnectionEvent event, double timeout); - Device(const std::shared_ptr& receiver, + Device(const std::shared_ptr& receiver, hidpp::DeviceIndex index, double timeout); std::vector callFunction(uint8_t feature_index, diff --git a/src/logid/features/DPI.cpp b/src/logid/features/DPI.cpp index a4b39d1..fd6c970 100644 --- a/src/logid/features/DPI.cpp +++ b/src/logid/features/DPI.cpp @@ -19,7 +19,7 @@ #include #include #include -#include "ipc_defs.h" +#include using namespace logid::features; using namespace logid::backend;