diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index e78a3bf..1faee33 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -114,35 +114,8 @@ void Device::_setupReportsAndInit() { /* hid_logitech_dj creates virtual /dev/hidraw nodes for receiver * devices. We should ignore these devices. */ - if (_index == hidpp::DefaultDevice) { - _raw_handler = _raw_device->addEventHandler( - {[](const std::vector& report) -> bool { - return (report[Offset::Type] == Report::Type::Short || - report[Offset::Type] == Report::Type::Long); - }, - [self_weak = _self](const std::vector& report) -> void { - Report _report(report); - if(auto self = self_weak.lock()) - self->handleEvent(_report); - }}); - - try { - auto rsp = sendReport({ReportType::Short, _index, - hidpp20::FeatureID::ROOT, hidpp20::Root::Ping, - hidpp::softwareID}); - if (rsp.deviceIndex() != _index) - throw InvalidDevice(InvalidDevice::VirtualNode); - } catch (hidpp10::Error& e) { - if (e.deviceIndex() != _index) - throw InvalidDevice(InvalidDevice::VirtualNode); - } catch (hidpp20::Error& e) { - /* This shouldn't happen, the device must not be ready */ - if (e.deviceIndex() != _index) - throw InvalidDevice(InvalidDevice::VirtualNode); - else - throw DeviceNotReady(); - } - } + if (_raw_device->isSubDevice()) + throw InvalidDevice(InvalidDevice::VirtualNode); _raw_handler = _raw_device->addEventHandler( {[index = _index]( @@ -228,6 +201,7 @@ Report Device::sendReport(const Report& report) { /* Must complete transaction before next send */ std::lock_guard send_lock(_send_mutex); _sent_sub_id = report.subId(); + _sent_address = report.address(); std::unique_lock lock(_response_mutex); _sendReport(report); @@ -244,6 +218,7 @@ Report Device::sendReport(const Report& report) { Response response = _response.value(); _response.reset(); _sent_sub_id.reset(); + _sent_address.reset(); if (std::holds_alternative(response)) { return std::get(response); @@ -263,20 +238,23 @@ bool Device::responseReport(const Report& report) { std::lock_guard lock(_response_mutex); Response response = report; uint8_t sub_id; + uint8_t address; Report::Hidpp10Error hidpp10_error{}; Report::Hidpp20Error hidpp20_error{}; if (report.isError10(hidpp10_error)) { sub_id = hidpp10_error.sub_id; + address = hidpp10_error.address; response = hidpp10_error; } else if (report.isError20(hidpp20_error)) { sub_id = hidpp20_error.feature_index; - response = hidpp20_error; + address = (hidpp20_error.function << 4) | (hidpp20_error.software_id & 0x0f); } else { sub_id = report.subId(); + address = report.address(); } - if (sub_id == _sent_sub_id) { + if (sub_id == _sent_sub_id && address == _sent_address) { _response = response; _response_cv.notify_all(); return true; diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 838b9d6..38997c0 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -158,6 +158,7 @@ namespace logid::backend::hidpp { std::optional _response; std::optional _sent_sub_id{}; + std::optional _sent_address{}; std::shared_ptr> _event_handlers; diff --git a/src/logid/backend/hidpp/defs.h b/src/logid/backend/hidpp/defs.h index 253f3c0..da15c1c 100644 --- a/src/logid/backend/hidpp/defs.h +++ b/src/logid/backend/hidpp/defs.h @@ -42,7 +42,7 @@ namespace logid::backend::hidpp { static constexpr uint8_t softwareID = 2; /* For sending reports with no response, use a different SW ID */ - static constexpr uint8_t noAckSoftwareID = 1; + static constexpr uint8_t noAckSoftwareID = 3; static constexpr std::size_t ShortParamLength = 3; static constexpr std::size_t LongParamLength = 16; diff --git a/src/logid/backend/hidpp10/Device.cpp b/src/logid/backend/hidpp10/Device.cpp index f2972c9..f661107 100644 --- a/src/logid/backend/hidpp10/Device.cpp +++ b/src/logid/backend/hidpp10/Device.cpp @@ -24,6 +24,23 @@ using namespace logid::backend; using namespace logid::backend::hidpp10; +hidpp::Report setupRegReport(hidpp::DeviceIndex index, + uint8_t sub_id, uint8_t address, + const std::vector& params) { + hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? + hidpp::Report::Type::Short : hidpp::Report::Type::Long; + + if (sub_id == SetRegisterLong) { + // When setting a long register, the report must be long. + type = hidpp::Report::Type::Long; + } + + hidpp::Report request(type, index, sub_id, address); + std::copy(params.begin(), params.end(), request.paramBegin()); + + return request; +} + Device::Device(const std::string& path, hidpp::DeviceIndex index, const std::shared_ptr& monitor, double timeout) : hidpp::Device(path, index, monitor, timeout) { @@ -124,22 +141,24 @@ std::vector Device::setRegister(uint8_t address, return accessRegister(sub_id, address, params); } +void Device::setRegisterNoResponse(uint8_t address, + const std::vector& params, + hidpp::Report::Type type) { + assert(params.size() <= hidpp::LongParamLength); + + uint8_t sub_id = type == hidpp::Report::Type::Short ? + SetRegisterShort : SetRegisterLong; + + return accessRegisterNoResponse(sub_id, address, params); +} + std::vector Device::accessRegister(uint8_t sub_id, uint8_t address, const std::vector& params) { - hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? - hidpp::Report::Type::Short : hidpp::Report::Type::Long; - - if (sub_id == SetRegisterLong) { - // When setting a long register, the report must be long. - type = hidpp::Report::Type::Long; - } - - hidpp::Report request(type, deviceIndex(), sub_id, address); - std::copy(params.begin(), params.end(), request.paramBegin()); - - auto response = sendReport(request); + auto response = sendReport(setupRegReport(deviceIndex(), sub_id, address, params)); return {response.paramBegin(), response.paramEnd()}; } - - +void Device::accessRegisterNoResponse(uint8_t sub_id, uint8_t address, + const std::vector& params) { + sendReportNoACK(setupRegReport(deviceIndex(), sub_id, address, params)); +} diff --git a/src/logid/backend/hidpp10/Device.h b/src/logid/backend/hidpp10/Device.h index 5ae3fd6..0876d54 100644 --- a/src/logid/backend/hidpp10/Device.h +++ b/src/logid/backend/hidpp10/Device.h @@ -39,6 +39,9 @@ namespace logid::backend::hidpp10 { const std::vector& params, hidpp::Report::Type type); + void setRegisterNoResponse(uint8_t address, const std::vector& params, + hidpp::Report::Type type); + protected: Device(const std::string& path, hidpp::DeviceIndex index, const std::shared_ptr& monitor, double timeout); @@ -53,18 +56,24 @@ namespace logid::backend::hidpp10 { private: typedef std::variant Response; + struct ResponseSlot { std::optional response; std::optional sub_id; + void reset(); }; + std::array _responses; std::vector accessRegister( uint8_t sub_id, uint8_t address, const std::vector& params); + void accessRegisterNoResponse( + uint8_t sub_id, uint8_t address, const std::vector& params); + protected: - template + template static std::shared_ptr makeDerived(Args... args) { auto device = hidpp::Device::makeDerived(std::forward(args)...); @@ -73,8 +82,9 @@ namespace logid::backend::hidpp10 { return device; } + public: - template + template static std::shared_ptr make(Args... args) { return makeDerived(std::forward(args)...); } diff --git a/src/logid/backend/hidpp10/Receiver.cpp b/src/logid/backend/hidpp10/Receiver.cpp index 65ee05c..9cefc76 100644 --- a/src/logid/backend/hidpp10/Receiver.cpp +++ b/src/logid/backend/hidpp10/Receiver.cpp @@ -68,7 +68,7 @@ void Receiver::setNotifications(NotificationFlags flags) { } void Receiver::enumerate() { - setRegister(ConnectionState, {2}, hidpp::ReportType::Short); + setRegisterNoResponse(ConnectionState, {2}, hidpp::ReportType::Short); } ///TODO: Investigate usage diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 11a886a..54e4c9a 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -24,6 +24,7 @@ #include #include #include +#include extern "C" { @@ -32,12 +33,17 @@ extern "C" #include #include #include +#include } using namespace logid::backend::raw; using namespace logid::backend; using namespace std::chrono; +static constexpr int max_write_tries = 8; + +static const std::regex virtual_path_regex(R"~((.*\/)(.*:)([0-9]+))~"); + int get_fd(const std::string& path) { int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK); if (fd == -1) @@ -56,7 +62,37 @@ RawDevice::dev_info get_dev_info(int fd) { "RawDevice HIDIOCGRAWINFO failed"); } - return {dev_info.vendor, dev_info.product}; + RawDevice::BusType type = RawDevice::OtherBus; + + switch (dev_info.bustype) { + case BUS_USB: + type = RawDevice::USB; + break; + case BUS_BLUETOOTH: + type = RawDevice::Bluetooth; + break; + default:; + } + + + return { + .vid = dev_info.vendor, + .pid = dev_info.product, + .bus_type = type + }; +} + +std::string get_phys(int fd) { + ssize_t len; + char buf[256]; + if (-1 == (len = ::ioctl(fd, HIDIOCGRAWPHYS(sizeof(buf)), buf))) { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRAWPHYS failed"); + } + + return {buf, static_cast(len) - 1}; } std::string get_name(int fd) { @@ -68,7 +104,7 @@ std::string get_name(int fd) { throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWNAME failed"); } - return {name_buf, static_cast(len)}; + return {name_buf, static_cast(len) - 1}; } RawDevice::RawDevice(std::string path, const std::shared_ptr& monitor) : @@ -76,6 +112,12 @@ RawDevice::RawDevice(std::string path, const std::shared_ptr& mon _dev_info(get_dev_info(_fd)), _name(get_name(_fd)), _report_desc(getReportDescriptor(_fd)), _io_monitor(monitor->ioMonitor()), _event_handlers(std::make_shared>()) { + + if (busType() == USB) { + auto phys = get_phys(_fd); + _sub_device = std::regex_match(phys, virtual_path_regex); + } + _io_monitor->add(_fd, { [this]() { _readReports(); }, [this]() { _valid = false; }, @@ -105,6 +147,14 @@ int16_t RawDevice::productId() const { return _dev_info.pid; } +RawDevice::BusType RawDevice::busType() const { + return _dev_info.bus_type; +} + +bool RawDevice::isSubDevice() const { + return _sub_device; +} + std::vector RawDevice::getReportDescriptor(const std::string& path) { int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK); if (fd == -1) @@ -150,9 +200,13 @@ void RawDevice::sendReport(const std::vector& report) { printf("\n"); } - if (write(_fd, report.data(), report.size()) == -1) - throw std::system_error(errno, std::system_category(), - "sendReport write failed"); + + for (int i = 0; i < max_write_tries && write(_fd, report.data(), report.size()) == -1; ++i) { + auto err = errno; + if (err != EPIPE) + throw std::system_error(err, std::system_category(), + "sendReport write failed"); + } } EventHandlerLock RawDevice::addEventHandler(RawEventHandler handler) { diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index 222cd12..f1669fb 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -39,9 +39,16 @@ namespace logid::backend::raw { static constexpr int max_data_length = 32; typedef RawEventHandler EventHandler; + enum BusType { + USB, + Bluetooth, + OtherBus + }; + struct dev_info { int16_t vid; int16_t pid; + BusType bus_type; }; RawDevice(std::string path, const std::shared_ptr& monitor); @@ -57,6 +64,10 @@ namespace logid::backend::raw { [[nodiscard]] int16_t productId() const; + [[nodiscard]] BusType busType() const; + + [[nodiscard]] bool isSubDevice() const; + static std::vector getReportDescriptor(const std::string& path); static std::vector getReportDescriptor(int fd); @@ -80,6 +91,8 @@ namespace logid::backend::raw { std::shared_ptr _io_monitor; + bool _sub_device = false; + std::shared_ptr> _event_handlers; void _handleEvent(const std::vector& report);