Add exponential backoff to device monitor

This commit is contained in:
pixl 2023-05-01 21:51:08 -04:00
parent ffd5e054a1
commit 47e4ba2b44
No known key found for this signature in database
GPG Key ID: 1866C148CD593B6E
23 changed files with 211 additions and 100 deletions

View File

@ -18,9 +18,7 @@
#include <DeviceManager.h> #include <DeviceManager.h>
#include <backend/Error.h> #include <backend/Error.h>
#include <backend/hidpp10/Error.h>
#include <util/log.h> #include <util/log.h>
#include <ipcgull/function.h>
#include <thread> #include <thread>
#include <sstream> #include <sstream>
#include <utility> #include <utility>
@ -100,24 +98,29 @@ void DeviceManager::addDevice(std::string path) {
hidpp::Device device(path, hidpp::DefaultDevice, _self.lock(), hidpp::Device device(path, hidpp::DefaultDevice, _self.lock(),
config()->io_timeout.value_or(defaults::io_timeout)); config()->io_timeout.value_or(defaults::io_timeout));
isReceiver = device.version() == std::make_tuple(1, 0); 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) { } catch (hidpp10::Error& e) {
if (e.code() != hidpp10::Error::UnknownDevice) if (e.code() != hidpp10::Error::UnknownDevice)
throw; throw DeviceNotReady();
defaultExists = false;
} catch (hidpp::Device::InvalidDevice& e) { } catch (hidpp::Device::InvalidDevice& e) {
if (e.code() == hidpp::Device::InvalidDevice::VirtualNode) { if (e.code() == hidpp::Device::InvalidDevice::VirtualNode) {
logPrintf(DEBUG, "Ignoring virtual node on %s", logPrintf(DEBUG, "Ignoring virtual node on %s", path.c_str());
path.c_str()); } else if (e.code() == hidpp::Device::InvalidDevice::Asleep) {
return; /* May be a valid device, wait */
throw DeviceNotReady();
} }
defaultExists = false; return;
} catch (std::system_error& e) { } catch (std::system_error& e) {
logPrintf(WARN, "I/O error on %s: %s, skipping device.", logPrintf(WARN, "I/O error on %s: %s, skipping device.", path.c_str(), e.what());
path.c_str(), e.what());
return; return;
} catch (TimeoutError& e) { } catch (TimeoutError& e) {
logPrintf(WARN, "Device %s timed out.", path.c_str()); /* Ready and valid non-default devices should throw an UnknownDevice error */
defaultExists = false; throw DeviceNotReady();
} }
if (isReceiver) { if (isReceiver) {
@ -130,28 +133,30 @@ void DeviceManager::addDevice(std::string path) {
/* TODO: Can non-receivers only contain 1 device? /* TODO: Can non-receivers only contain 1 device?
* If the device exists, it is guaranteed to be an HID++ 2.0 device */ * If the device exists, it is guaranteed to be an HID++ 2.0 device */
if (defaultExists) { if (defaultExists) {
auto device = Device::make(path, hidpp::DefaultDevice, auto device = Device::make(path, hidpp::DefaultDevice, _self.lock());
_self.lock());
std::lock_guard<std::mutex> lock(_map_lock); std::lock_guard<std::mutex> lock(_map_lock);
_devices.emplace(path, device); _devices.emplace(path, device);
_ipc_devices->deviceAdded(device); _ipc_devices->deviceAdded(device);
} else { } else {
try { try {
auto device = Device::make(path, auto device = Device::make(path, hidpp::CordedDevice, _self.lock());
hidpp::CordedDevice, _self.lock());
std::lock_guard<std::mutex> lock(_map_lock); std::lock_guard<std::mutex> lock(_map_lock);
_devices.emplace(path, device); _devices.emplace(path, device);
_ipc_devices->deviceAdded(device); _ipc_devices->deviceAdded(device);
} catch (hidpp10::Error& e) { } catch (hidpp10::Error& e) {
if (e.code() != hidpp10::Error::UnknownDevice) if (e.code() != hidpp10::Error::UnknownDevice)
throw; throw DeviceNotReady();
else } catch (hidpp20::Error& e) {
logPrintf(WARN, "HID++ 1.0 error while trying to initialize %s: %s", if (e.code() != hidpp20::Error::UnknownDevice)
path.c_str(), e.what()); throw DeviceNotReady();
} catch (hidpp::Device::InvalidDevice& e) { // Ignore } catch (hidpp::Device::InvalidDevice& e) {
if (e.code() == hidpp::Device::InvalidDevice::Asleep)
throw DeviceNotReady();
} catch (std::system_error& e) { } catch (std::system_error& e) {
// This error should have been thrown previously // 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());
} catch (TimeoutError& e) {
throw DeviceNotReady();
} }
} }
} }

View File

@ -18,6 +18,12 @@
#include <backend/Error.h> #include <backend/Error.h>
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"; return "Device timed out";
} }

View File

@ -22,6 +22,11 @@
#include <stdexcept> #include <stdexcept>
namespace logid::backend { namespace logid::backend {
class DeviceNotReady : std::exception {
public:
[[nodiscard]] const char* what() const noexcept override;
};
class TimeoutError : public std::exception { class TimeoutError : public std::exception {
public: public:
TimeoutError() = default; TimeoutError() = default;

View File

@ -55,7 +55,7 @@ Device::Device(const std::string& path, DeviceIndex index,
duration<double, std::milli>(timeout))), duration<double, std::milli>(timeout))),
_raw_device(std::make_shared<raw::RawDevice>(path, monitor)), _raw_device(std::make_shared<raw::RawDevice>(path, monitor)),
_receiver(nullptr), _path(path), _index(index) { _receiver(nullptr), _path(path), _index(index) {
_init(); _setupReportsAndInit();
} }
Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index, Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index,
@ -64,7 +64,7 @@ Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index,
duration<double, std::milli>(timeout))), duration<double, std::milli>(timeout))),
_raw_device(std::move(raw_device)), _receiver(nullptr), _raw_device(std::move(raw_device)), _receiver(nullptr),
_path(_raw_device->rawPath()), _index(index) { _path(_raw_device->rawPath()), _index(index) {
_init(); _setupReportsAndInit();
} }
Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver, Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
@ -81,7 +81,7 @@ Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
_pid = event.pid; _pid = event.pid;
else else
_pid = receiver->getPairingInfo(_index).pid; _pid = receiver->getPairingInfo(_index).pid;
_init(); _setupReportsAndInit();
} }
Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver, Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
@ -92,7 +92,7 @@ Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
_receiver(receiver), _path(receiver->rawDevice()->rawPath()), _receiver(receiver), _path(receiver->rawDevice()->rawPath()),
_index(index) { _index(index) {
_pid = receiver->getPairingInfo(_index).pid; _pid = receiver->getPairingInfo(_index).pid;
_init(); _setupReportsAndInit();
} }
const std::string& Device::devicePath() const { const std::string& Device::devicePath() const {
@ -107,7 +107,8 @@ const std::tuple<uint8_t, uint8_t>& Device::version() const {
return _version; return _version;
} }
void Device::_init() {
void Device::_setupReportsAndInit() {
_event_handlers = std::make_shared<EventHandlerList<Device>>(); _event_handlers = std::make_shared<EventHandlerList<Device>>();
supported_reports = getSupportedReports(_raw_device->reportDescriptor()); supported_reports = getSupportedReports(_raw_device->reportDescriptor());
@ -132,11 +133,17 @@ void Device::_init() {
auto rsp = sendReport({ReportType::Short, _index, auto rsp = sendReport({ReportType::Short, _index,
hidpp20::FeatureID::ROOT, hidpp20::Root::Ping, hidpp20::FeatureID::ROOT, hidpp20::Root::Ping,
hidpp::softwareID}); hidpp::softwareID});
if (rsp.deviceIndex() != _index) { if (rsp.deviceIndex() != _index)
throw InvalidDevice(InvalidDevice::VirtualNode); throw InvalidDevice(InvalidDevice::VirtualNode);
}
} catch (hidpp10::Error& e) { } 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); this->handleEvent(_report);
}}); }});
_init();
}
void Device::_init() {
try { try {
hidpp20::Root root(this); hidpp20::Root root(this);
_version = root.getVersion(); _version = root.getVersion();
@ -162,6 +173,20 @@ void Device::_init() {
// HID++ 2.0 is not supported, assume HID++ 1.0 // HID++ 2.0 is not supported, assume HID++ 1.0
_version = std::make_tuple(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) { if (!_receiver) {
@ -230,9 +255,11 @@ Report Device::sendReport(const Report& report) {
if (std::holds_alternative<Report>(response)) { if (std::holds_alternative<Report>(response)) {
return std::get<Report>(response); return std::get<Report>(response);
} else if (std::holds_alternative<Report::Hidpp10Error>(response)) { } else if (std::holds_alternative<Report::Hidpp10Error>(response)) {
throw hidpp10::Error(std::get<Report::Hidpp10Error>(response).error_code); auto error = std::get<Report::Hidpp10Error>(response);
throw hidpp10::Error(error.error_code, error.device_index);
} else if (std::holds_alternative<Report::Hidpp20Error>(response)) { } else if (std::holds_alternative<Report::Hidpp20Error>(response)) {
throw hidpp20::Error(std::get<Report::Hidpp20Error>(response).error_code); auto error = std::get<Report::Hidpp20Error>(response);
throw hidpp20::Error(error.error_code, error.device_index);
} }
// Should not be reached // Should not be reached
@ -246,10 +273,10 @@ bool Device::responseReport(const Report& report) {
Report::Hidpp10Error hidpp10_error{}; Report::Hidpp10Error hidpp10_error{};
Report::Hidpp20Error hidpp20_error{}; Report::Hidpp20Error hidpp20_error{};
if (report.isError10(&hidpp10_error)) { if (report.isError10(hidpp10_error)) {
sub_id = hidpp10_error.sub_id; sub_id = hidpp10_error.sub_id;
response = hidpp10_error; response = hidpp10_error;
} else if (report.isError20(&hidpp20_error)) { } else if (report.isError20(hidpp20_error)) {
sub_id = hidpp20_error.feature_index; sub_id = hidpp20_error.feature_index;
response = hidpp20_error; response = hidpp20_error;
} else { } else {
@ -280,6 +307,27 @@ void Device::sendReportNoACK(const Report& report) {
_sendReport(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 { void Device::reportFixup(Report& report) const {
switch (report.type()) { switch (report.type()) {
case Report::Type::Short: case Report::Type::Short:

View File

@ -19,16 +19,16 @@
#ifndef LOGID_BACKEND_HIDPP_DEVICE_H #ifndef LOGID_BACKEND_HIDPP_DEVICE_H
#define LOGID_BACKEND_HIDPP_DEVICE_H #define LOGID_BACKEND_HIDPP_DEVICE_H
#include <backend/raw/RawDevice.h>
#include <backend/hidpp/Report.h>
#include <backend/hidpp/defs.h>
#include <backend/EventHandlerList.h>
#include <optional> #include <optional>
#include <variant> #include <variant>
#include <string> #include <string>
#include <memory> #include <memory>
#include <functional> #include <functional>
#include <map> #include <map>
#include <backend/raw/RawDevice.h>
#include <backend/hidpp/Report.h>
#include <backend/hidpp/defs.h>
#include <backend/EventHandlerList.h>
namespace logid::backend::hidpp10 { namespace logid::backend::hidpp10 {
// Need to define here for a constructor // Need to define here for a constructor
@ -100,6 +100,10 @@ namespace logid::backend::hidpp {
// Returns whether the report is a response // Returns whether the report is a response
virtual bool responseReport(const Report& report); virtual bool responseReport(const Report& report);
bool isStable20();
bool isStable10();
void _sendReport(Report report); void _sendReport(Report report);
void reportFixup(Report& report) const; void reportFixup(Report& report) const;
@ -110,6 +114,8 @@ namespace logid::backend::hidpp {
std::mutex _response_mutex; std::mutex _response_mutex;
std::condition_variable _response_cv; std::condition_variable _response_cv;
private: private:
void _setupReportsAndInit();
void _init(); void _init();
std::shared_ptr<raw::RawDevice> _raw_device; std::shared_ptr<raw::RawDevice> _raw_device;

View File

@ -265,31 +265,29 @@ void Report::setParams(const std::vector<uint8_t>& _params) {
_data[Offset::Parameters + i] = _params[i]; _data[Offset::Parameters + i] = _params[i];
} }
bool Report::isError10(Report::Hidpp10Error* error) const { bool Report::isError10(Report::Hidpp10Error& error) const {
assert(error != nullptr);
if (_data[Offset::Type] != Type::Short || if (_data[Offset::Type] != Type::Short ||
_data[Offset::SubID] != hidpp10::ErrorID) _data[Offset::SubID] != hidpp10::ErrorID)
return false; return false;
error->sub_id = _data[3]; error.device_index = deviceIndex();
error->address = _data[4]; error.sub_id = _data[3];
error->error_code = _data[5]; error.address = _data[4];
error.error_code = _data[5];
return true; return true;
} }
bool Report::isError20(Report::Hidpp20Error* error) const { bool Report::isError20(Report::Hidpp20Error& error) const {
assert(error != nullptr);
if (_data[Offset::Type] != Type::Long || if (_data[Offset::Type] != Type::Long ||
_data[Offset::Feature] != hidpp20::ErrorID) _data[Offset::Feature] != hidpp20::ErrorID)
return false; return false;
error->feature_index = _data[3]; error.device_index = deviceIndex();
error->function = (_data[4] >> 4) & 0x0f; error.feature_index = _data[3];
error->software_id = _data[4] & 0x0f; error.function = (_data[4] >> 4) & 0x0f;
error->error_code = _data[5]; error.software_id = _data[4] & 0x0f;
error.error_code = _data[5];
return true; return true;
} }

View File

@ -111,16 +111,18 @@ namespace logid::backend::hidpp {
void setParams(const std::vector<uint8_t>& _params); void setParams(const std::vector<uint8_t>& _params);
struct Hidpp10Error { struct Hidpp10Error {
hidpp::DeviceIndex device_index;
uint8_t sub_id, address, error_code; uint8_t sub_id, address, error_code;
}; };
bool isError10(Hidpp10Error* error) const; bool isError10(Hidpp10Error& error) const;
struct Hidpp20Error { struct Hidpp20Error {
hidpp::DeviceIndex device_index;
uint8_t feature_index, function, software_id, error_code; uint8_t feature_index, function, software_id, error_code;
}; };
bool isError20(Hidpp20Error* error) const; bool isError20(Hidpp20Error& error) const;
[[nodiscard]] const std::vector<uint8_t>& rawReport() const; [[nodiscard]] const std::vector<uint8_t>& rawReport() const;

View File

@ -69,10 +69,13 @@ hidpp::Report Device::sendReport(const hidpp::Report& report) {
auto response = response_slot.response.value(); auto response = response_slot.response.value();
response_slot.reset(); response_slot.reset();
if (std::holds_alternative<hidpp::Report>(response))
if (std::holds_alternative<hidpp::Report>(response)) {
return std::get<hidpp::Report>(response); return std::get<hidpp::Report>(response);
else // if(std::holds_alternative<Error::ErrorCode>(response)) } else { // if(std::holds_alternative<hidpp::Report::Hidpp10Error>(response))
throw Error(std::get<Error::ErrorCode>(response)); auto error = std::get<hidpp::Report::Hidpp10Error>(response);
throw Error(error.error_code, error.device_index);
}
} }
bool Device::responseReport(const hidpp::Report& report) { bool Device::responseReport(const hidpp::Report& report) {
@ -81,7 +84,7 @@ bool Device::responseReport(const hidpp::Report& report) {
bool is_error = false; bool is_error = false;
hidpp::Report::Hidpp10Error hidpp10_error{}; hidpp::Report::Hidpp10Error hidpp10_error{};
if (report.isError10(&hidpp10_error)) { if (report.isError10(hidpp10_error)) {
sub_id = hidpp10_error.sub_id; sub_id = hidpp10_error.sub_id;
is_error = true; is_error = true;
} else { } else {
@ -94,7 +97,7 @@ bool Device::responseReport(const hidpp::Report& report) {
return false; return false;
if (is_error) { if (is_error) {
response_slot.response = static_cast<Error::ErrorCode>(hidpp10_error.error_code); response_slot.response = hidpp10_error;
} else { } else {
response_slot.response = report; response_slot.response = report;
} }

View File

@ -51,7 +51,7 @@ namespace logid::backend::hidpp10 {
bool responseReport(const hidpp::Report& report) final; bool responseReport(const hidpp::Report& report) final;
private: private:
typedef std::variant<hidpp::Report, Error::ErrorCode> Response; typedef std::variant<hidpp::Report, hidpp::Report::Hidpp10Error> Response;
struct ResponseSlot { struct ResponseSlot {
std::optional<Response> response; std::optional<Response> response;
std::optional<uint8_t> sub_id; std::optional<uint8_t> sub_id;

View File

@ -19,9 +19,10 @@
#include <backend/hidpp10/Error.h> #include <backend/hidpp10/Error.h>
#include <cassert> #include <cassert>
using namespace logid::backend;
using namespace logid::backend::hidpp10; 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); assert(code != Success);
} }
@ -61,3 +62,7 @@ const char* Error::what() const noexcept {
uint8_t Error::code() const noexcept { uint8_t Error::code() const noexcept {
return _code; return _code;
} }
hidpp::DeviceIndex Error::deviceIndex() const noexcept {
return _index;
}

View File

@ -19,6 +19,7 @@
#ifndef LOGID_BACKEND_HIDPP10_ERROR_H #ifndef LOGID_BACKEND_HIDPP10_ERROR_H
#define LOGID_BACKEND_HIDPP10_ERROR_H #define LOGID_BACKEND_HIDPP10_ERROR_H
#include <backend/hidpp/defs.h>
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
@ -43,14 +44,17 @@ namespace logid::backend::hidpp10 {
WrongPINCode = 0x0C WrongPINCode = 0x0C
}; };
explicit Error(uint8_t code); Error(uint8_t code, hidpp::DeviceIndex index);
[[nodiscard]] const char* what() const noexcept override; [[nodiscard]] const char* what() const noexcept override;
[[nodiscard]] uint8_t code() const noexcept; [[nodiscard]] uint8_t code() const noexcept;
[[nodiscard]] hidpp::DeviceIndex deviceIndex() const noexcept;
private: private:
uint8_t _code; uint8_t _code;
hidpp::DeviceIndex _index;
}; };
} }

View File

@ -118,10 +118,12 @@ hidpp::Report Device::sendReport(const hidpp::Report& report) {
auto response = response_slot.response.value(); auto response = response_slot.response.value();
response_slot.reset(); response_slot.reset();
if (std::holds_alternative<hidpp::Report>(response)) if (std::holds_alternative<hidpp::Report>(response)) {
return std::get<hidpp::Report>(response); return std::get<hidpp::Report>(response);
else // if(std::holds_alternative<Error::ErrorCode>(response)) } else { // if(std::holds_alternative<Error::ErrorCode>(response))
throw Error(std::get<Error::ErrorCode>(response)); auto error = std::get<hidpp::Report::Hidpp20Error>(response);
throw Error(error.error_code, error.device_index);
}
} }
void Device::sendReportNoACK(const hidpp::Report& report) { void Device::sendReportNoACK(const hidpp::Report& report) {
@ -137,7 +139,7 @@ bool Device::responseReport(const hidpp::Report& report) {
bool is_error = false; bool is_error = false;
hidpp::Report::Hidpp20Error hidpp20_error{}; hidpp::Report::Hidpp20Error hidpp20_error{};
if (report.isError20(&hidpp20_error)) { if (report.isError20(hidpp20_error)) {
is_error = true; is_error = true;
sw_id = hidpp20_error.software_id; sw_id = hidpp20_error.software_id;
feature = hidpp20_error.feature_index; feature = hidpp20_error.feature_index;
@ -154,7 +156,7 @@ bool Device::responseReport(const hidpp::Report& report) {
} }
if (is_error) { if (is_error) {
response_slot.response = static_cast<Error::ErrorCode>(hidpp20_error.error_code); response_slot.response = hidpp20_error;
} else { } else {
response_slot.response = report; response_slot.response = report;
} }

View File

@ -56,7 +56,7 @@ namespace logid::backend::hidpp20 {
bool responseReport(const hidpp::Report& report) final; bool responseReport(const hidpp::Report& report) final;
private: private:
typedef std::variant<hidpp::Report, Error::ErrorCode> Response; typedef std::variant<hidpp::Report, hidpp::Report::Hidpp20Error> Response;
struct ResponseSlot { struct ResponseSlot {
std::optional<Response> response; std::optional<Response> response;
std::optional<uint8_t> feature; std::optional<uint8_t> feature;

View File

@ -19,9 +19,10 @@
#include <backend/hidpp20/Error.h> #include <backend/hidpp20/Error.h>
#include <cassert> #include <cassert>
using namespace logid::backend;
using namespace logid::backend::hidpp20; 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); assert(_code != NoError);
} }
@ -57,3 +58,7 @@ const char* Error::what() const noexcept {
uint8_t Error::code() const noexcept { uint8_t Error::code() const noexcept {
return _code; return _code;
} }
hidpp::DeviceIndex Error::deviceIndex() const noexcept {
return _index;
}

View File

@ -19,6 +19,7 @@
#ifndef LOGID_BACKEND_HIDPP20_ERROR_H #ifndef LOGID_BACKEND_HIDPP20_ERROR_H
#define LOGID_BACKEND_HIDPP20_ERROR_H #define LOGID_BACKEND_HIDPP20_ERROR_H
#include <backend/hidpp/defs.h>
#include <stdexcept> #include <stdexcept>
#include <cstdint> #include <cstdint>
@ -41,14 +42,17 @@ namespace logid::backend::hidpp20 {
UnknownDevice = 10 UnknownDevice = 10
}; };
explicit Error(uint8_t code); Error(uint8_t code, hidpp::DeviceIndex index);
[[nodiscard]] const char* what() const noexcept override; [[nodiscard]] const char* what() const noexcept override;
[[nodiscard]] uint8_t code() const noexcept; [[nodiscard]] uint8_t code() const noexcept;
[[nodiscard]] hidpp::DeviceIndex deviceIndex() const noexcept;
private: private:
uint8_t _code; uint8_t _code;
hidpp::DeviceIndex _index;
}; };
} }

View File

@ -40,8 +40,7 @@ namespace logid::backend::hidpp20 {
std::vector<uint8_t> callFunction(uint8_t function_id, std::vector<uint8_t> callFunction(uint8_t function_id,
std::vector<uint8_t>& params); std::vector<uint8_t>& params);
private: hidpp::Device* const _device;
hidpp::Device* _device;
uint8_t _index; uint8_t _index;
}; };
} }

View File

@ -55,8 +55,7 @@ namespace logid::backend::hidpp20 {
void callFunctionNoResponse(uint8_t function_id, std::vector<uint8_t>& params); void callFunctionNoResponse(uint8_t function_id, std::vector<uint8_t>& params);
private: Device* const _device;
Device* _device;
uint8_t _index; uint8_t _index;
}; };
} }

View File

@ -16,6 +16,7 @@
* *
*/ */
#include <backend/hidpp20/features/ChangeHost.h> #include <backend/hidpp20/features/ChangeHost.h>
#include <backend/hidpp20/Device.h>
#include <backend/hidpp20/Error.h> #include <backend/hidpp20/Error.h>
using namespace logid::backend::hidpp20; using namespace logid::backend::hidpp20;
@ -48,7 +49,7 @@ void ChangeHost::setHost(uint8_t host) {
getHostInfo(); getHostInfo();
if (host >= _host_count) if (host >= _host_count)
throw hidpp20::Error(hidpp20::Error::InvalidArgument); throw Error(hidpp20::Error::InvalidArgument, _device->deviceIndex());
std::vector<uint8_t> params = {host}; std::vector<uint8_t> params = {host};

View File

@ -17,6 +17,7 @@
*/ */
#include <backend/hidpp20/features/ReprogControls.h> #include <backend/hidpp20/features/ReprogControls.h>
#include <backend/hidpp20/Error.h> #include <backend/hidpp20/Error.h>
#include <backend/hidpp20/Device.h>
#include <cassert> #include <cassert>
using namespace logid::backend::hidpp20; using namespace logid::backend::hidpp20;
@ -106,7 +107,7 @@ ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid) {
auto it = _cids.find(cid); auto it = _cids.find(cid);
if (it == _cids.end()) if (it == _cids.end())
throw Error(Error::InvalidArgument); throw Error(Error::InvalidArgument, _device->deviceIndex());
else else
return it->second; return it->second;
} }

View File

@ -61,6 +61,15 @@ feature_info Root::getFeature(uint16_t feature_id) {
} }
} }
uint8_t Root::ping(uint8_t byte) {
std::vector<uint8_t> params(3);
params[2] = byte;
auto response = this->callFunction(Root::Function::Ping, params);
return response[2];
}
std::tuple<uint8_t, uint8_t> Root::getVersion() { std::tuple<uint8_t, uint8_t> Root::getVersion() {
std::vector<uint8_t> params(0); std::vector<uint8_t> params(0);
auto response = this->callFunction(Root::Function::Ping, params); auto response = this->callFunction(Root::Function::Ping, params);

View File

@ -39,6 +39,8 @@ namespace logid::backend::hidpp20 {
feature_info getFeature(uint16_t feature_id); feature_info getFeature(uint16_t feature_id);
uint8_t ping(uint8_t byte);
std::tuple<uint8_t, uint8_t> getVersion(); std::tuple<uint8_t, uint8_t> getVersion();
enum FeatureFlag : uint8_t { enum FeatureFlag : uint8_t {

View File

@ -20,6 +20,7 @@
#include <backend/raw/IOMonitor.h> #include <backend/raw/IOMonitor.h>
#include <backend/raw/RawDevice.h> #include <backend/raw/RawDevice.h>
#include <backend/hidpp/Device.h> #include <backend/hidpp/Device.h>
#include <backend/Error.h>
#include <util/task.h> #include <util/task.h>
#include <util/log.h> #include <util/log.h>
#include <system_error> #include <system_error>
@ -95,15 +96,7 @@ void DeviceMonitor::ready() {
std::string dev_node = udev_device_get_devnode(device); std::string dev_node = udev_device_get_devnode(device);
if (action == "add") if (action == "add")
spawn_task([this, dev_node]() { spawn_task([this, dev_node]() { _addHandler(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);
});
else if (action == "remove") else if (action == "remove")
spawn_task([this, dev_node]() { _removeHandler(dev_node); }); spawn_task([this, dev_node]() { _removeHandler(dev_node); });
@ -144,24 +137,37 @@ void DeviceMonitor::enumerate() {
std::string dev_node = udev_device_get_devnode(device); std::string dev_node = udev_device_get_devnode(device);
udev_device_unref(device); udev_device_unref(device);
_addHandler(dev_node); spawn_task([this, dev_node]() { _addHandler(dev_node); });
} }
udev_enumerate_unref(udev_enum); udev_enumerate_unref(udev_enum);
} }
void DeviceMonitor::_addHandler(const std::string& device) { void DeviceMonitor::_addHandler(const std::string& device) {
int tries;
for (tries = 0; tries < max_tries; ++tries) {
try { try {
auto supported_reports = backend::hidpp::getSupportedReports( auto supported_reports = backend::hidpp::getSupportedReports(
RawDevice::getReportDescriptor(device)); RawDevice::getReportDescriptor(device));
if (supported_reports) if (supported_reports)
this->addDevice(device); this->addDevice(device);
else else
logPrintf(DEBUG, "Unsupported device %s ignored", logPrintf(DEBUG, "Unsupported device %s ignored", device.c_str());
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) { } catch (std::exception& e) {
logPrintf(WARN, "Error adding device %s: %s", logPrintf(WARN, "Error adding device %s: %s", device.c_str(), e.what());
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);
} }
} }

View File

@ -33,7 +33,8 @@ struct udev_monitor;
namespace logid::backend::raw { namespace logid::backend::raw {
class IOMonitor; class IOMonitor;
static constexpr int ready_wait = 2000; static constexpr int max_tries = 5;
static constexpr int ready_backoff = 500;
class DeviceMonitor { class DeviceMonitor {
public: public: