Require HID++ devices to be made as shared_ptrs

Binds event handlers and tasks to lifetime of object. Ensures no race
conditions occur during destruction of HID++ devices.
This commit is contained in:
pixl 2023-05-02 14:30:47 -04:00
parent a468861f7d
commit a578493dba
No known key found for this signature in database
GPG Key ID: 1866C148CD593B6E
13 changed files with 157 additions and 79 deletions

View File

@ -93,10 +93,10 @@ std::shared_ptr<Device> Device::make(
Device::Device(std::string path, backend::hidpp::DeviceIndex index,
const std::shared_ptr<DeviceManager>& manager) :
_hidpp20(path, index, manager,
manager->config()->io_timeout.value_or(defaults::io_timeout)),
_hidpp20(hidpp20::Device::make(path, index, manager,
manager->config()->io_timeout.value_or(defaults::io_timeout))),
_path(std::move(path)), _index(index),
_config(_getConfig(manager, _hidpp20.name())),
_config(_getConfig(manager, _hidpp20->name())),
_receiver(nullptr),
_manager(manager),
_nickname(manager),
@ -107,10 +107,11 @@ Device::Device(std::string path, backend::hidpp::DeviceIndex index,
Device::Device(std::shared_ptr<backend::raw::RawDevice> raw_device,
hidpp::DeviceIndex index, const std::shared_ptr<DeviceManager>& manager) :
_hidpp20(std::move(raw_device), index,
manager->config()->io_timeout.value_or(defaults::io_timeout)),
_hidpp20(hidpp20::Device::make(
std::move(raw_device), index,
manager->config()->io_timeout.value_or(defaults::io_timeout))),
_path(raw_device->rawPath()), _index(index),
_config(_getConfig(manager, _hidpp20.name())),
_config(_getConfig(manager, _hidpp20->name())),
_receiver(nullptr),
_manager(manager),
_nickname(manager),
@ -121,11 +122,11 @@ Device::Device(std::shared_ptr<backend::raw::RawDevice> raw_device,
Device::Device(Receiver* receiver, hidpp::DeviceIndex index,
const std::shared_ptr<DeviceManager>& manager) :
_hidpp20(receiver->rawReceiver(), index,
manager->config()->io_timeout.value_or(
defaults::io_timeout)),
_hidpp20(hidpp20::Device::make(
receiver->rawReceiver(), index,
manager->config()->io_timeout.value_or(defaults::io_timeout))),
_path(receiver->path()), _index(index),
_config(_getConfig(manager, _hidpp20.name())),
_config(_getConfig(manager, _hidpp20->name())),
_receiver(receiver),
_manager(manager),
_nickname(manager),
@ -159,11 +160,11 @@ void Device::_init() {
}
std::string Device::name() {
return _hidpp20.name();
return _hidpp20->name();
}
uint16_t Device::pid() {
return _hidpp20.pid();
return _hidpp20->pid();
}
void Device::sleep() {
@ -223,15 +224,15 @@ const config::Profile& Device::activeProfile() const {
}
hidpp20::Device& Device::hidpp20() {
return _hidpp20;
return *_hidpp20;
}
void Device::_makeResetMechanism() {
try {
hidpp20::Reset reset(&_hidpp20);
hidpp20::Reset reset(_hidpp20.get());
_reset_mechanism = std::make_unique<std::function<void()>>(
[dev = &this->_hidpp20] {
hidpp20::Reset reset(dev);
[dev = _hidpp20] {
hidpp20::Reset reset(dev.get());
reset.reset(reset.getProfile());
});
} catch (hidpp20::UnsupportedFeature& e) {

View File

@ -134,7 +134,7 @@ namespace logid {
}
}
backend::hidpp20::Device _hidpp20;
std::shared_ptr<backend::hidpp20::Device> _hidpp20;
std::string _path;
backend::hidpp::DeviceIndex _index;
std::map<std::string, std::shared_ptr<features::DeviceFeature>>

View File

@ -95,9 +95,10 @@ void DeviceManager::addDevice(std::string path) {
}
try {
hidpp::Device device(path, hidpp::DefaultDevice, _self.lock(),
config()->io_timeout.value_or(defaults::io_timeout));
isReceiver = device.version() == std::make_tuple(1, 0);
auto device = hidpp::Device::make(
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();

View File

@ -111,10 +111,10 @@ void Receiver::addDevice(hidpp::DeviceConnectionEvent event) {
if (!event.linkEstablished)
return;
hidpp::Device hidpp_device(receiver(), event,
manager->config()->io_timeout.value_or(defaults::io_timeout));
auto hidpp_device = hidpp::Device::make(
receiver(), event, manager->config()->io_timeout.value_or(defaults::io_timeout));
auto version = hidpp_device.version();
auto version = hidpp_device->version();
if (std::get<0>(version) < 2) {
logPrintf(INFO, "Unsupported HID++ 1.0 device on %s:%d connected.",
@ -122,6 +122,8 @@ void Receiver::addDevice(hidpp::DeviceConnectionEvent event) {
return;
}
hidpp_device.reset();
auto device = Device::make(this, event.index, manager);
std::lock_guard<std::mutex> manager_lock(manager->mutex());
_devices.emplace(event.index, device);

View File

@ -55,7 +55,6 @@ Device::Device(const std::string& path, DeviceIndex index,
duration<double, std::milli>(timeout))),
_raw_device(std::make_shared<raw::RawDevice>(path, monitor)),
_receiver(nullptr), _path(path), _index(index) {
_setupReportsAndInit();
}
Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index,
@ -64,7 +63,6 @@ Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index,
duration<double, std::milli>(timeout))),
_raw_device(std::move(raw_device)), _receiver(nullptr),
_path(_raw_device->rawPath()), _index(index) {
_setupReportsAndInit();
}
Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
@ -81,7 +79,6 @@ Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
_pid = event.pid;
else
_pid = receiver->getPairingInfo(_index).pid;
_setupReportsAndInit();
}
Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
@ -92,7 +89,6 @@ Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
_receiver(receiver), _path(receiver->rawDevice()->rawPath()),
_index(index) {
_pid = receiver->getPairingInfo(_index).pid;
_setupReportsAndInit();
}
const std::string& Device::devicePath() const {
@ -124,9 +120,10 @@ void Device::_setupReportsAndInit() {
return (report[Offset::Type] == Report::Type::Short ||
report[Offset::Type] == Report::Type::Long);
},
[this](const std::vector<uint8_t>& report) -> void {
[self_weak = _self](const std::vector<uint8_t>& report) -> void {
Report _report(report);
this->handleEvent(_report);
if(auto self = self_weak.lock())
self->handleEvent(_report);
}});
try {
@ -154,9 +151,10 @@ void Device::_setupReportsAndInit() {
report[Offset::Type] == Report::Type::Long) &&
(report[Offset::DeviceIndex] == index);
},
[this](const std::vector<uint8_t>& report) -> void {
[self_weak = _self](const std::vector<uint8_t>& report) -> void {
Report _report(report);
this->handleEvent(_report);
if(auto self = self_weak.lock())
self->handleEvent(_report);
}});
_init();

View File

@ -38,7 +38,24 @@ namespace logid::backend::hidpp10 {
namespace logid::backend::hidpp {
struct DeviceConnectionEvent;
namespace {
template <typename T>
class DeviceWrapper : public T {
friend class Device;
public:
template <typename... Args>
explicit DeviceWrapper(Args... args) : T(std::forward<Args>(args)...) { }
template <typename... Args>
static std::shared_ptr<T> make(Args... args) {
return std::make_shared<DeviceWrapper>(std::forward<Args>(args)...);
}
};
}
class Device {
template <typename T>
friend class DeviceWrapper;
public:
struct EventHandler {
std::function<bool(Report&)> condition;
@ -64,18 +81,6 @@ namespace logid::backend::hidpp {
Reason _reason;
};
Device(const std::string& path, DeviceIndex index,
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index,
double timeout);
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceConnectionEvent event, double timeout);
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
DeviceIndex index, double timeout);
[[nodiscard]] const std::string& devicePath() const;
[[nodiscard]] DeviceIndex deviceIndex() const;
@ -96,7 +101,23 @@ namespace logid::backend::hidpp {
[[nodiscard]] const std::shared_ptr<raw::RawDevice>& rawDevice() const;
Device(const Device&) = delete;
Device(Device&&) = delete;
virtual ~Device() = default;
protected:
Device(const std::string& path, DeviceIndex index,
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index,
double timeout);
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceConnectionEvent event, double timeout);
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
DeviceIndex index, double timeout);
// Returns whether the report is a response
virtual bool responseReport(const Report& report);
@ -108,6 +129,11 @@ namespace logid::backend::hidpp {
void reportFixup(Report& report) const;
template <typename T>
[[nodiscard]] std::weak_ptr<T> self() const {
return std::dynamic_pointer_cast<T>(_self);
}
const std::chrono::milliseconds io_timeout;
uint8_t supported_reports{};
@ -136,6 +162,23 @@ namespace logid::backend::hidpp {
std::optional<uint8_t> _sent_sub_id{};
std::shared_ptr<EventHandlerList<Device>> _event_handlers;
std::weak_ptr<Device> _self;
protected:
template <typename T, typename... Args>
static std::shared_ptr<T> makeDerived(Args... args) {
auto device = DeviceWrapper<T>::make(std::forward<Args>(args)...);
device->_self = device;
device->_setupReportsAndInit();
return device;
}
public:
template <typename... Args>
static std::shared_ptr<Device> make(Args... args) {
return makeDerived<Device>(std::forward<Args>(args)...);
}
};
typedef Device::EventHandler EventHandler;

View File

@ -27,19 +27,16 @@ using namespace logid::backend::hidpp10;
Device::Device(const std::string& path, hidpp::DeviceIndex index,
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout) :
hidpp::Device(path, index, monitor, timeout) {
assert(version() == std::make_tuple(1, 0));
}
Device::Device(std::shared_ptr<raw::RawDevice> raw_dev, hidpp::DeviceIndex index,
double timeout) : hidpp::Device(std::move(raw_dev), index, timeout) {
assert(version() == std::make_tuple(1, 0));
}
Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceIndex index,
double timeout)
: hidpp::Device(receiver, index, timeout) {
assert(version() == std::make_tuple(1, 0));
}
void Device::ResponseSlot::reset() {

View File

@ -28,14 +28,6 @@
namespace logid::backend::hidpp10 {
class Device : public hidpp::Device {
public:
Device(const std::string& path, hidpp::DeviceIndex index,
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
Device(std::shared_ptr<raw::RawDevice> raw_dev,
hidpp::DeviceIndex index, double timeout);
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceIndex index, double timeout);
hidpp::Report sendReport(const hidpp::Report& report) final;
@ -48,6 +40,15 @@ namespace logid::backend::hidpp10 {
hidpp::Report::Type type);
protected:
Device(const std::string& path, hidpp::DeviceIndex index,
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
Device(std::shared_ptr<raw::RawDevice> raw_dev,
hidpp::DeviceIndex index, double timeout);
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceIndex index, double timeout);
bool responseReport(const hidpp::Report& report) final;
private:
@ -61,6 +62,22 @@ namespace logid::backend::hidpp10 {
std::vector<uint8_t> accessRegister(
uint8_t sub_id, uint8_t address, const std::vector<uint8_t>& params);
protected:
template <typename T, typename... Args>
static std::shared_ptr<T> makeDerived(Args... args) {
auto device = hidpp::Device::makeDerived<T>(std::forward<Args>(args)...);
if (std::get<0>(device->version()) != 1)
throw std::invalid_argument("not a hid++ 1.0 device");
return device;
}
public:
template <typename... Args>
static std::shared_ptr<Device> make(Args... args) {
return makeDerived<Device>(std::forward<Args>(args)...);
}
};
}

View File

@ -28,6 +28,9 @@ const char* InvalidReceiver::what() const noexcept {
Receiver::Receiver(const std::string& path, const std::shared_ptr<raw::DeviceMonitor>& monitor,
double timeout) : Device(path, hidpp::DefaultDevice, monitor, timeout) {
}
void Receiver::_receiverCheck() {
// Check if the device is a receiver
try {
getNotificationFlags();

View File

@ -89,9 +89,6 @@ namespace logid::backend::hidpp10 {
class Receiver : public Device {
public:
Receiver(const std::string& path,
const std::shared_ptr<raw::DeviceMonitor>& monitor,
double timeout);
/* The following functions deal with HID++ 1.0 features.
* While these are not technically DJ functions, it is redundant
@ -199,8 +196,25 @@ namespace logid::backend::hidpp10 {
static std::string passkeyEvent(const hidpp::Report& report);
protected:
Receiver(const std::string& path,
const std::shared_ptr<raw::DeviceMonitor>& monitor,
double timeout);
private:
void _receiverCheck();
bool _is_bolt = false;
public:
template <typename... Args>
static std::shared_ptr<Receiver> make(Args... args) {
auto receiver = makeDerived<Receiver>(std::forward<Args>(args)...);
receiver->_receiverCheck();
return receiver;
}
};
}

View File

@ -25,7 +25,7 @@ using namespace logid::backend::hidpp;
ReceiverMonitor::ReceiverMonitor(const std::string& path,
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout)
: _receiver(std::make_shared<Receiver>(path, monitor, timeout)) {
: _receiver(Receiver::make(path, monitor, timeout)) {
Receiver::NotificationFlags notification_flags{true, true, true};
_receiver->setNotifications(notification_flags);

View File

@ -27,30 +27,21 @@ using namespace logid::backend::hidpp20;
Device::Device(const std::string& path, hidpp::DeviceIndex index,
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout) :
hidpp::Device(path, index, monitor, timeout) {
// TODO: Fix version check
if (std::get<0>(version()) < 2)
throw std::runtime_error("Invalid HID++ version");
}
Device::Device(std::shared_ptr<raw::RawDevice> raw_device,
hidpp::DeviceIndex index, double timeout) :
hidpp::Device(std::move(raw_device), index, timeout) {
if (std::get<0>(version()) < 2)
throw std::runtime_error("Invalid HID++ version");
}
Device::Device(const std::shared_ptr<hidpp10::Receiver>& 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<hidpp10::Receiver>& receiver,
hidpp::DeviceIndex index, double timeout)
: hidpp::Device(receiver, index, timeout) {
if (std::get<0>(version()) < 2)
throw std::runtime_error("Invalid HID++ version");
}
std::vector<uint8_t> Device::callFunction(uint8_t feature_index,

View File

@ -28,18 +28,6 @@
namespace logid::backend::hidpp20 {
class Device : public hidpp::Device {
public:
Device(const std::string& path, hidpp::DeviceIndex index,
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
Device(std::shared_ptr<raw::RawDevice> raw_device,
hidpp::DeviceIndex index, double timeout);
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceConnectionEvent event, double timeout);
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceIndex index, double timeout);
std::vector<uint8_t> callFunction(uint8_t feature_index,
uint8_t function,
std::vector<uint8_t>& params);
@ -53,6 +41,18 @@ namespace logid::backend::hidpp20 {
void sendReportNoACK(const hidpp::Report& report) final;
protected:
Device(const std::string& path, hidpp::DeviceIndex index,
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
Device(std::shared_ptr<raw::RawDevice> raw_device,
hidpp::DeviceIndex index, double timeout);
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceConnectionEvent event, double timeout);
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceIndex index, double timeout);
bool responseReport(const hidpp::Report& report) final;
private:
@ -65,6 +65,17 @@ namespace logid::backend::hidpp20 {
/* Multiplex responses on lower nibble of SubID, ignore upper nibble for space */
std::array<ResponseSlot, 16> _responses;
public:
template <typename... Args>
static std::shared_ptr<Device> make(Args... args) {
auto device = makeDerived<Device>(std::forward<Args>(args)...);
if (std::get<0>(device->version()) < 2)
throw std::invalid_argument("not a hid++ 2.0 device");
return device;
}
};
}