diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index 697c53a..2328b03 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -18,9 +18,7 @@ #include #include -#include #include -#include #include #include #include @@ -100,24 +98,29 @@ void DeviceManager::addDevice(std::string path) { 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 (hidpp20::Error& e) { + if (e.code() != hidpp20::Error::UnknownDevice) + throw DeviceNotReady(); + defaultExists = false; } catch (hidpp10::Error& e) { if (e.code() != hidpp10::Error::UnknownDevice) - throw; + throw DeviceNotReady(); + defaultExists = false; } catch (hidpp::Device::InvalidDevice& e) { if (e.code() == hidpp::Device::InvalidDevice::VirtualNode) { - logPrintf(DEBUG, "Ignoring virtual node on %s", - path.c_str()); - return; + logPrintf(DEBUG, "Ignoring virtual node on %s", path.c_str()); + } else if (e.code() == hidpp::Device::InvalidDevice::Asleep) { + /* May be a valid device, wait */ + throw DeviceNotReady(); } - defaultExists = false; + return; } catch (std::system_error& e) { - logPrintf(WARN, "I/O error on %s: %s, skipping device.", - path.c_str(), e.what()); + logPrintf(WARN, "I/O error on %s: %s, skipping device.", path.c_str(), e.what()); return; } catch (TimeoutError& e) { - logPrintf(WARN, "Device %s timed out.", path.c_str()); - defaultExists = false; + /* Ready and valid non-default devices should throw an UnknownDevice error */ + throw DeviceNotReady(); } if (isReceiver) { @@ -130,28 +133,30 @@ void DeviceManager::addDevice(std::string path) { /* TODO: Can non-receivers only contain 1 device? * If the device exists, it is guaranteed to be an HID++ 2.0 device */ if (defaultExists) { - auto device = Device::make(path, hidpp::DefaultDevice, - _self.lock()); + auto device = Device::make(path, hidpp::DefaultDevice, _self.lock()); std::lock_guard lock(_map_lock); _devices.emplace(path, device); _ipc_devices->deviceAdded(device); } else { try { - auto device = Device::make(path, - hidpp::CordedDevice, _self.lock()); + auto device = Device::make(path, hidpp::CordedDevice, _self.lock()); std::lock_guard lock(_map_lock); _devices.emplace(path, device); _ipc_devices->deviceAdded(device); } catch (hidpp10::Error& e) { 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()); - } catch (hidpp::Device::InvalidDevice& e) { // Ignore + throw DeviceNotReady(); + } catch (hidpp20::Error& e) { + if (e.code() != hidpp20::Error::UnknownDevice) + throw DeviceNotReady(); + } catch (hidpp::Device::InvalidDevice& e) { + if (e.code() == hidpp::Device::InvalidDevice::Asleep) + throw DeviceNotReady(); } 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()); + } catch (TimeoutError& e) { + throw DeviceNotReady(); } } } diff --git a/src/logid/backend/Error.cpp b/src/logid/backend/Error.cpp index 8cb841b..6623e30 100644 --- a/src/logid/backend/Error.cpp +++ b/src/logid/backend/Error.cpp @@ -18,6 +18,12 @@ #include -const char* logid::backend::TimeoutError::what() const noexcept { +using namespace logid::backend; + +const char* DeviceNotReady::what() const noexcept { + return "device not ready"; +} + +const char* TimeoutError::what() const noexcept { return "Device timed out"; } diff --git a/src/logid/backend/Error.h b/src/logid/backend/Error.h index f6e9b56..26b309d 100644 --- a/src/logid/backend/Error.h +++ b/src/logid/backend/Error.h @@ -22,6 +22,11 @@ #include namespace logid::backend { + class DeviceNotReady : std::exception { + public: + [[nodiscard]] const char* what() const noexcept override; + }; + class TimeoutError : public std::exception { public: TimeoutError() = default; diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index b501350..4eba5f4 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -55,7 +55,7 @@ Device::Device(const std::string& path, DeviceIndex index, duration(timeout))), _raw_device(std::make_shared(path, monitor)), _receiver(nullptr), _path(path), _index(index) { - _init(); + _setupReportsAndInit(); } Device::Device(std::shared_ptr raw_device, DeviceIndex index, @@ -64,7 +64,7 @@ Device::Device(std::shared_ptr raw_device, DeviceIndex index, duration(timeout))), _raw_device(std::move(raw_device)), _receiver(nullptr), _path(_raw_device->rawPath()), _index(index) { - _init(); + _setupReportsAndInit(); } Device::Device(const std::shared_ptr& receiver, @@ -81,7 +81,7 @@ Device::Device(const std::shared_ptr& receiver, _pid = event.pid; else _pid = receiver->getPairingInfo(_index).pid; - _init(); + _setupReportsAndInit(); } Device::Device(const std::shared_ptr& receiver, @@ -92,7 +92,7 @@ Device::Device(const std::shared_ptr& receiver, _receiver(receiver), _path(receiver->rawDevice()->rawPath()), _index(index) { _pid = receiver->getPairingInfo(_index).pid; - _init(); + _setupReportsAndInit(); } const std::string& Device::devicePath() const { @@ -107,7 +107,8 @@ const std::tuple& Device::version() const { return _version; } -void Device::_init() { + +void Device::_setupReportsAndInit() { _event_handlers = std::make_shared>(); supported_reports = getSupportedReports(_raw_device->reportDescriptor()); @@ -132,11 +133,17 @@ void Device::_init() { auto rsp = sendReport({ReportType::Short, _index, hidpp20::FeatureID::ROOT, hidpp20::Root::Ping, hidpp::softwareID}); - if (rsp.deviceIndex() != _index) { + if (rsp.deviceIndex() != _index) throw InvalidDevice(InvalidDevice::VirtualNode); - } } catch (hidpp10::Error& e) { - // Ignore + 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(); } } @@ -152,6 +159,10 @@ void Device::_init() { this->handleEvent(_report); }}); + _init(); +} + +void Device::_init() { try { hidpp20::Root root(this); _version = root.getVersion(); @@ -162,6 +173,20 @@ void Device::_init() { // HID++ 2.0 is not supported, assume HID++ 1.0 _version = std::make_tuple(1, 0); + } catch (hidpp20::Error& e) { + /* Should never happen, device not ready? */ + throw DeviceNotReady(); + } + + /* Do a stability test before going further */ + if (std::get<0>(_version) >= 2) { + if (!isStable20()) { + throw DeviceNotReady(); + } + } else { + if (!isStable10()) { + throw DeviceNotReady(); + } } if (!_receiver) { @@ -230,9 +255,11 @@ Report Device::sendReport(const Report& report) { if (std::holds_alternative(response)) { return std::get(response); } else if (std::holds_alternative(response)) { - throw hidpp10::Error(std::get(response).error_code); + auto error = std::get(response); + throw hidpp10::Error(error.error_code, error.device_index); } else if (std::holds_alternative(response)) { - throw hidpp20::Error(std::get(response).error_code); + auto error = std::get(response); + throw hidpp20::Error(error.error_code, error.device_index); } // Should not be reached @@ -246,10 +273,10 @@ bool Device::responseReport(const Report& report) { Report::Hidpp10Error hidpp10_error{}; Report::Hidpp20Error hidpp20_error{}; - if (report.isError10(&hidpp10_error)) { + if (report.isError10(hidpp10_error)) { sub_id = hidpp10_error.sub_id; response = hidpp10_error; - } else if (report.isError20(&hidpp20_error)) { + } else if (report.isError20(hidpp20_error)) { sub_id = hidpp20_error.feature_index; response = hidpp20_error; } else { @@ -280,6 +307,27 @@ void Device::sendReportNoACK(const Report& report) { _sendReport(report); } +bool Device::isStable10() { + return true; +} + +bool Device::isStable20() { + static constexpr std::string ping_seq = "hello"; + + hidpp20::Root root(this); + + try { + for (auto c: ping_seq) { + if (root.ping(c) != c) + return false; + } + } catch (std::exception& e) { + return false; + } + + return true; +} + void Device::reportFixup(Report& report) const { switch (report.type()) { case Report::Type::Short: diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 48f1d33..de0de92 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -19,16 +19,16 @@ #ifndef LOGID_BACKEND_HIDPP_DEVICE_H #define LOGID_BACKEND_HIDPP_DEVICE_H +#include +#include +#include +#include #include #include #include #include #include #include -#include -#include -#include -#include namespace logid::backend::hidpp10 { // Need to define here for a constructor @@ -100,6 +100,10 @@ namespace logid::backend::hidpp { // Returns whether the report is a response virtual bool responseReport(const Report& report); + bool isStable20(); + + bool isStable10(); + void _sendReport(Report report); void reportFixup(Report& report) const; @@ -110,6 +114,8 @@ namespace logid::backend::hidpp { std::mutex _response_mutex; std::condition_variable _response_cv; private: + void _setupReportsAndInit(); + void _init(); std::shared_ptr _raw_device; diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index 68797e5..9fdb3c1 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -265,31 +265,29 @@ void Report::setParams(const std::vector& _params) { _data[Offset::Parameters + i] = _params[i]; } -bool Report::isError10(Report::Hidpp10Error* error) const { - assert(error != nullptr); - +bool Report::isError10(Report::Hidpp10Error& error) const { if (_data[Offset::Type] != Type::Short || _data[Offset::SubID] != hidpp10::ErrorID) return false; - error->sub_id = _data[3]; - error->address = _data[4]; - error->error_code = _data[5]; + error.device_index = deviceIndex(); + error.sub_id = _data[3]; + error.address = _data[4]; + error.error_code = _data[5]; return true; } -bool Report::isError20(Report::Hidpp20Error* error) const { - assert(error != nullptr); - +bool Report::isError20(Report::Hidpp20Error& error) const { if (_data[Offset::Type] != Type::Long || _data[Offset::Feature] != hidpp20::ErrorID) return false; - error->feature_index = _data[3]; - error->function = (_data[4] >> 4) & 0x0f; - error->software_id = _data[4] & 0x0f; - error->error_code = _data[5]; + error.device_index = deviceIndex(); + error.feature_index = _data[3]; + error.function = (_data[4] >> 4) & 0x0f; + error.software_id = _data[4] & 0x0f; + error.error_code = _data[5]; return true; } diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index 2c8e587..305e928 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -111,16 +111,18 @@ namespace logid::backend::hidpp { void setParams(const std::vector& _params); struct Hidpp10Error { + hidpp::DeviceIndex device_index; uint8_t sub_id, address, error_code; }; - bool isError10(Hidpp10Error* error) const; + bool isError10(Hidpp10Error& error) const; struct Hidpp20Error { + hidpp::DeviceIndex device_index; uint8_t feature_index, function, software_id, error_code; }; - bool isError20(Hidpp20Error* error) const; + bool isError20(Hidpp20Error& error) const; [[nodiscard]] const std::vector& rawReport() const; diff --git a/src/logid/backend/hidpp10/Device.cpp b/src/logid/backend/hidpp10/Device.cpp index d9f357f..8d40a8b 100644 --- a/src/logid/backend/hidpp10/Device.cpp +++ b/src/logid/backend/hidpp10/Device.cpp @@ -58,9 +58,9 @@ hidpp::Report Device::sendReport(const hidpp::Report& report) { _sendReport(report); bool valid = _response_cv.wait_for(lock, io_timeout, - [&response_slot]() { - return response_slot.response.has_value(); - }); + [&response_slot]() { + return response_slot.response.has_value(); + }); if (!valid) { response_slot.reset(); @@ -69,10 +69,13 @@ hidpp::Report Device::sendReport(const hidpp::Report& report) { auto response = response_slot.response.value(); response_slot.reset(); - if (std::holds_alternative(response)) + + if (std::holds_alternative(response)) { return std::get(response); - else // if(std::holds_alternative(response)) - throw Error(std::get(response)); + } else { // if(std::holds_alternative(response)) + auto error = std::get(response); + throw Error(error.error_code, error.device_index); + } } bool Device::responseReport(const hidpp::Report& report) { @@ -81,7 +84,7 @@ bool Device::responseReport(const hidpp::Report& report) { bool is_error = false; hidpp::Report::Hidpp10Error hidpp10_error{}; - if (report.isError10(&hidpp10_error)) { + if (report.isError10(hidpp10_error)) { sub_id = hidpp10_error.sub_id; is_error = true; } else { @@ -94,7 +97,7 @@ bool Device::responseReport(const hidpp::Report& report) { return false; if (is_error) { - response_slot.response = static_cast(hidpp10_error.error_code); + response_slot.response = hidpp10_error; } else { response_slot.response = report; } diff --git a/src/logid/backend/hidpp10/Device.h b/src/logid/backend/hidpp10/Device.h index 8301f8b..9e46c27 100644 --- a/src/logid/backend/hidpp10/Device.h +++ b/src/logid/backend/hidpp10/Device.h @@ -51,7 +51,7 @@ namespace logid::backend::hidpp10 { bool responseReport(const hidpp::Report& report) final; private: - typedef std::variant Response; + typedef std::variant Response; struct ResponseSlot { std::optional response; std::optional sub_id; diff --git a/src/logid/backend/hidpp10/Error.cpp b/src/logid/backend/hidpp10/Error.cpp index ed3aa3d..fd145e2 100644 --- a/src/logid/backend/hidpp10/Error.cpp +++ b/src/logid/backend/hidpp10/Error.cpp @@ -19,9 +19,10 @@ #include #include +using namespace logid::backend; using namespace logid::backend::hidpp10; -Error::Error(uint8_t code) : _code(code) { +Error::Error(uint8_t code, hidpp::DeviceIndex index) : _code(code), _index(index) { assert(code != Success); } @@ -60,4 +61,8 @@ const char* Error::what() const noexcept { uint8_t Error::code() const noexcept { return _code; -} \ No newline at end of file +} + +hidpp::DeviceIndex Error::deviceIndex() const noexcept { + return _index; +} diff --git a/src/logid/backend/hidpp10/Error.h b/src/logid/backend/hidpp10/Error.h index facbb8f..d172061 100644 --- a/src/logid/backend/hidpp10/Error.h +++ b/src/logid/backend/hidpp10/Error.h @@ -19,6 +19,7 @@ #ifndef LOGID_BACKEND_HIDPP10_ERROR_H #define LOGID_BACKEND_HIDPP10_ERROR_H +#include #include #include @@ -43,14 +44,17 @@ namespace logid::backend::hidpp10 { WrongPINCode = 0x0C }; - explicit Error(uint8_t code); + Error(uint8_t code, hidpp::DeviceIndex index); [[nodiscard]] const char* what() const noexcept override; [[nodiscard]] uint8_t code() const noexcept; + [[nodiscard]] hidpp::DeviceIndex deviceIndex() const noexcept; + private: uint8_t _code; + hidpp::DeviceIndex _index; }; } diff --git a/src/logid/backend/hidpp20/Device.cpp b/src/logid/backend/hidpp20/Device.cpp index 4d8bc0a..ebb847b 100644 --- a/src/logid/backend/hidpp20/Device.cpp +++ b/src/logid/backend/hidpp20/Device.cpp @@ -118,10 +118,12 @@ hidpp::Report Device::sendReport(const hidpp::Report& report) { auto response = response_slot.response.value(); response_slot.reset(); - if (std::holds_alternative(response)) + if (std::holds_alternative(response)) { return std::get(response); - else // if(std::holds_alternative(response)) - throw Error(std::get(response)); + } else { // if(std::holds_alternative(response)) + auto error = std::get(response); + throw Error(error.error_code, error.device_index); + } } void Device::sendReportNoACK(const hidpp::Report& report) { @@ -137,7 +139,7 @@ bool Device::responseReport(const hidpp::Report& report) { bool is_error = false; hidpp::Report::Hidpp20Error hidpp20_error{}; - if (report.isError20(&hidpp20_error)) { + if (report.isError20(hidpp20_error)) { is_error = true; sw_id = hidpp20_error.software_id; feature = hidpp20_error.feature_index; @@ -154,7 +156,7 @@ bool Device::responseReport(const hidpp::Report& report) { } if (is_error) { - response_slot.response = static_cast(hidpp20_error.error_code); + response_slot.response = hidpp20_error; } else { response_slot.response = report; } diff --git a/src/logid/backend/hidpp20/Device.h b/src/logid/backend/hidpp20/Device.h index 3ce82f9..cc2fa73 100644 --- a/src/logid/backend/hidpp20/Device.h +++ b/src/logid/backend/hidpp20/Device.h @@ -56,7 +56,7 @@ namespace logid::backend::hidpp20 { bool responseReport(const hidpp::Report& report) final; private: - typedef std::variant Response; + typedef std::variant Response; struct ResponseSlot { std::optional response; std::optional feature; diff --git a/src/logid/backend/hidpp20/Error.cpp b/src/logid/backend/hidpp20/Error.cpp index 355a888..4eb0772 100644 --- a/src/logid/backend/hidpp20/Error.cpp +++ b/src/logid/backend/hidpp20/Error.cpp @@ -19,9 +19,10 @@ #include #include +using namespace logid::backend; using namespace logid::backend::hidpp20; -Error::Error(uint8_t code) : _code(code) { +Error::Error(uint8_t code, hidpp::DeviceIndex index) : _code(code), _index (index) { assert(_code != NoError); } @@ -56,4 +57,8 @@ const char* Error::what() const noexcept { uint8_t Error::code() const noexcept { return _code; +} + +hidpp::DeviceIndex Error::deviceIndex() const noexcept { + return _index; } \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Error.h b/src/logid/backend/hidpp20/Error.h index e2cfa31..94e48eb 100644 --- a/src/logid/backend/hidpp20/Error.h +++ b/src/logid/backend/hidpp20/Error.h @@ -19,6 +19,7 @@ #ifndef LOGID_BACKEND_HIDPP20_ERROR_H #define LOGID_BACKEND_HIDPP20_ERROR_H +#include #include #include @@ -41,14 +42,17 @@ namespace logid::backend::hidpp20 { UnknownDevice = 10 }; - explicit Error(uint8_t code); + Error(uint8_t code, hidpp::DeviceIndex index); [[nodiscard]] const char* what() const noexcept override; [[nodiscard]] uint8_t code() const noexcept; + [[nodiscard]] hidpp::DeviceIndex deviceIndex() const noexcept; + private: uint8_t _code; + hidpp::DeviceIndex _index; }; } diff --git a/src/logid/backend/hidpp20/EssentialFeature.h b/src/logid/backend/hidpp20/EssentialFeature.h index 3247d78..7e3f995 100644 --- a/src/logid/backend/hidpp20/EssentialFeature.h +++ b/src/logid/backend/hidpp20/EssentialFeature.h @@ -40,8 +40,7 @@ namespace logid::backend::hidpp20 { std::vector callFunction(uint8_t function_id, std::vector& params); - private: - hidpp::Device* _device; + hidpp::Device* const _device; uint8_t _index; }; } diff --git a/src/logid/backend/hidpp20/Feature.h b/src/logid/backend/hidpp20/Feature.h index 8aff48d..1e269e2 100644 --- a/src/logid/backend/hidpp20/Feature.h +++ b/src/logid/backend/hidpp20/Feature.h @@ -55,8 +55,7 @@ namespace logid::backend::hidpp20 { void callFunctionNoResponse(uint8_t function_id, std::vector& params); - private: - Device* _device; + Device* const _device; uint8_t _index; }; } diff --git a/src/logid/backend/hidpp20/features/ChangeHost.cpp b/src/logid/backend/hidpp20/features/ChangeHost.cpp index bcf9fe9..f22d7a3 100644 --- a/src/logid/backend/hidpp20/features/ChangeHost.cpp +++ b/src/logid/backend/hidpp20/features/ChangeHost.cpp @@ -16,6 +16,7 @@ * */ #include +#include #include using namespace logid::backend::hidpp20; @@ -48,7 +49,7 @@ void ChangeHost::setHost(uint8_t host) { getHostInfo(); if (host >= _host_count) - throw hidpp20::Error(hidpp20::Error::InvalidArgument); + throw Error(hidpp20::Error::InvalidArgument, _device->deviceIndex()); std::vector params = {host}; diff --git a/src/logid/backend/hidpp20/features/ReprogControls.cpp b/src/logid/backend/hidpp20/features/ReprogControls.cpp index dd3a7f9..6858a75 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.cpp +++ b/src/logid/backend/hidpp20/features/ReprogControls.cpp @@ -17,6 +17,7 @@ */ #include #include +#include #include using namespace logid::backend::hidpp20; @@ -106,7 +107,7 @@ ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid) { auto it = _cids.find(cid); if (it == _cids.end()) - throw Error(Error::InvalidArgument); + throw Error(Error::InvalidArgument, _device->deviceIndex()); else return it->second; } diff --git a/src/logid/backend/hidpp20/features/Root.cpp b/src/logid/backend/hidpp20/features/Root.cpp index 68e0267..0cc3e36 100644 --- a/src/logid/backend/hidpp20/features/Root.cpp +++ b/src/logid/backend/hidpp20/features/Root.cpp @@ -61,6 +61,15 @@ feature_info Root::getFeature(uint16_t feature_id) { } } +uint8_t Root::ping(uint8_t byte) { + std::vector params(3); + params[2] = byte; + + auto response = this->callFunction(Root::Function::Ping, params); + + return response[2]; +} + std::tuple Root::getVersion() { std::vector params(0); auto response = this->callFunction(Root::Function::Ping, params); diff --git a/src/logid/backend/hidpp20/features/Root.h b/src/logid/backend/hidpp20/features/Root.h index a1112ed..89017c4 100644 --- a/src/logid/backend/hidpp20/features/Root.h +++ b/src/logid/backend/hidpp20/features/Root.h @@ -39,6 +39,8 @@ namespace logid::backend::hidpp20 { feature_info getFeature(uint16_t feature_id); + uint8_t ping(uint8_t byte); + std::tuple getVersion(); enum FeatureFlag : uint8_t { diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp index 7e77fab..71c744e 100644 --- a/src/logid/backend/raw/DeviceMonitor.cpp +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -95,15 +96,7 @@ void DeviceMonitor::ready() { std::string dev_node = udev_device_get_devnode(device); if (action == "add") - spawn_task([this, dev_node]() { - /* Device was just connected, may not be ready yet. - * Sleep for 2 seconds to ensure device is ready before attempting to add. - * This is a bit of a hack and this time was determined through a bit of - * experimentation. - */ - std::this_thread::sleep_for(std::chrono::milliseconds(ready_wait)); - _addHandler(dev_node); - }); + spawn_task([this, dev_node]() { _addHandler(dev_node); }); else if (action == "remove") spawn_task([this, dev_node]() { _removeHandler(dev_node); }); @@ -144,24 +137,37 @@ void DeviceMonitor::enumerate() { std::string dev_node = udev_device_get_devnode(device); udev_device_unref(device); - _addHandler(dev_node); + spawn_task([this, dev_node]() { _addHandler(dev_node); }); } udev_enumerate_unref(udev_enum); } void DeviceMonitor::_addHandler(const std::string& device) { - try { - auto supported_reports = backend::hidpp::getSupportedReports( - RawDevice::getReportDescriptor(device)); - if (supported_reports) - this->addDevice(device); - else - logPrintf(DEBUG, "Unsupported device %s ignored", - device.c_str()); - } catch (std::exception& e) { - logPrintf(WARN, "Error adding device %s: %s", - device.c_str(), e.what()); + int tries; + for (tries = 0; tries < max_tries; ++tries) { + try { + auto supported_reports = backend::hidpp::getSupportedReports( + RawDevice::getReportDescriptor(device)); + if (supported_reports) + this->addDevice(device); + else + logPrintf(DEBUG, "Unsupported device %s ignored", device.c_str()); + break; + } catch (backend::DeviceNotReady& e) { + /* Do exponential backoff for 2^tries * backoff ms. */ + std::chrono::milliseconds timeout((1 << tries) * ready_backoff); + logPrintf(DEBUG, "Failed to add device %s on try %d, backing off for %dms", + device.c_str(), tries + 1, timeout.count()); + std::this_thread::sleep_for(timeout); + } catch (std::exception& e) { + logPrintf(WARN, "Error adding device %s: %s", device.c_str(), e.what()); + break; + } + } + if (tries == max_tries) { + logPrintf(WARN, "Failed to add device %s after %d tries. Treating as failure.", + device.c_str(), max_tries); } } diff --git a/src/logid/backend/raw/DeviceMonitor.h b/src/logid/backend/raw/DeviceMonitor.h index 5ef710b..ac3061e 100644 --- a/src/logid/backend/raw/DeviceMonitor.h +++ b/src/logid/backend/raw/DeviceMonitor.h @@ -33,7 +33,8 @@ struct udev_monitor; namespace logid::backend::raw { class IOMonitor; - static constexpr int ready_wait = 2000; + static constexpr int max_tries = 5; + static constexpr int ready_backoff = 500; class DeviceMonitor { public: