mirror of
https://github.com/PixlOne/logiops.git
synced 2025-07-13 21:02:43 +08:00
Merge pull request #380 from PixlOne/fix-wakeup-memleak
This commit is contained in:
commit
5767aac362
@ -184,7 +184,6 @@ void Device::sleep() {
|
||||
|
||||
void Device::wakeup() {
|
||||
std::lock_guard<std::mutex> lock(_state_lock);
|
||||
logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index);
|
||||
|
||||
reconfigure();
|
||||
|
||||
@ -192,6 +191,8 @@ void Device::wakeup() {
|
||||
_awake = true;
|
||||
_ipc_interface->notifyStatus();
|
||||
}
|
||||
|
||||
logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index);
|
||||
}
|
||||
|
||||
void Device::reconfigure() {
|
||||
|
@ -24,21 +24,64 @@
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <list>
|
||||
#include <atomic>
|
||||
|
||||
template <class T>
|
||||
class EventHandlerLock;
|
||||
|
||||
template <class T>
|
||||
struct EventHandlerList {
|
||||
typedef std::list<typename T::EventHandler> list_t;
|
||||
typedef typename list_t::const_iterator iterator_t;
|
||||
class EventHandlerList {
|
||||
public:
|
||||
typedef std::list<std::pair<typename T::EventHandler, std::atomic_bool>> list_t;
|
||||
typedef typename list_t::iterator iterator_t;
|
||||
private:
|
||||
list_t list;
|
||||
std::shared_mutex mutex;
|
||||
std::shared_mutex add_mutex;
|
||||
|
||||
std::list<typename T::EventHandler> list;
|
||||
mutable std::shared_mutex mutex;
|
||||
void cleanup() {
|
||||
std::unique_lock lock(mutex, std::try_to_lock);
|
||||
if (lock.owns_lock()) {
|
||||
std::list<iterator_t> to_remove;
|
||||
for (auto it = list.begin(); it != list.end(); ++it) {
|
||||
if (!it->second)
|
||||
to_remove.push_back(it);
|
||||
}
|
||||
|
||||
for(auto& it : to_remove)
|
||||
list.erase(it);
|
||||
}
|
||||
}
|
||||
public:
|
||||
iterator_t add(typename T::EventHandler handler) {
|
||||
std::unique_lock add_lock(add_mutex);
|
||||
list.emplace_front(std::move(handler), true);
|
||||
return list.begin();
|
||||
}
|
||||
|
||||
void remove(iterator_t iterator) {
|
||||
std::unique_lock lock(mutex);
|
||||
std::unique_lock lock(mutex, std::try_to_lock);
|
||||
if (lock.owns_lock()) {
|
||||
std::unique_lock add_lock(add_mutex);
|
||||
list.erase(iterator);
|
||||
} else {
|
||||
iterator->second = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Arg>
|
||||
void run_all(Arg arg) {
|
||||
cleanup();
|
||||
std::shared_lock lock(mutex);
|
||||
std::shared_lock add_lock(add_mutex);
|
||||
for (auto& handler : list) {
|
||||
add_lock.unlock();
|
||||
if (handler.second) {
|
||||
if (handler.first.condition(arg))
|
||||
handler.first.callback(arg);
|
||||
}
|
||||
add_lock.lock();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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<uint8_t>& report) -> bool {
|
||||
return (report[Offset::Type] == Report::Type::Short ||
|
||||
report[Offset::Type] == Report::Type::Long);
|
||||
},
|
||||
[self_weak = _self](const std::vector<uint8_t>& 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)
|
||||
if (_raw_device->isSubDevice())
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
_raw_handler = _raw_device->addEventHandler(
|
||||
{[index = _index](
|
||||
@ -214,25 +187,21 @@ void Device::_init() {
|
||||
}
|
||||
|
||||
EventHandlerLock<Device> Device::addEventHandler(EventHandler handler) {
|
||||
std::unique_lock lock(_event_handlers->mutex);
|
||||
_event_handlers->list.emplace_front(std::move(handler));
|
||||
return {_event_handlers, _event_handlers->list.cbegin()};
|
||||
return {_event_handlers, _event_handlers->add(std::move(handler))};
|
||||
}
|
||||
|
||||
void Device::handleEvent(Report& report) {
|
||||
if (responseReport(report))
|
||||
return;
|
||||
|
||||
std::shared_lock lock(_event_handlers->mutex);
|
||||
for (auto& handler: _event_handlers->list)
|
||||
if (handler.condition(report))
|
||||
handler.callback(report);
|
||||
_event_handlers->run_all(report);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -249,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<Report>(response)) {
|
||||
return std::get<Report>(response);
|
||||
@ -268,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;
|
||||
|
@ -158,6 +158,7 @@ namespace logid::backend::hidpp {
|
||||
|
||||
std::optional<Response> _response;
|
||||
std::optional<uint8_t> _sent_sub_id{};
|
||||
std::optional<uint8_t> _sent_address{};
|
||||
|
||||
std::shared_ptr<EventHandlerList<Device>> _event_handlers;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<uint8_t>& 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<raw::DeviceMonitor>& monitor, double timeout) :
|
||||
hidpp::Device(path, index, monitor, timeout) {
|
||||
@ -124,22 +141,24 @@ std::vector<uint8_t> Device::setRegister(uint8_t address,
|
||||
return accessRegister(sub_id, address, params);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Device::accessRegister(uint8_t sub_id, uint8_t address,
|
||||
const std::vector<uint8_t>& params) {
|
||||
hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ?
|
||||
hidpp::Report::Type::Short : hidpp::Report::Type::Long;
|
||||
void Device::setRegisterNoResponse(uint8_t address,
|
||||
const std::vector<uint8_t>& params,
|
||||
hidpp::Report::Type type) {
|
||||
assert(params.size() <= hidpp::LongParamLength);
|
||||
|
||||
if (sub_id == SetRegisterLong) {
|
||||
// When setting a long register, the report must be long.
|
||||
type = hidpp::Report::Type::Long;
|
||||
uint8_t sub_id = type == hidpp::Report::Type::Short ?
|
||||
SetRegisterShort : SetRegisterLong;
|
||||
|
||||
return accessRegisterNoResponse(sub_id, address, params);
|
||||
}
|
||||
|
||||
hidpp::Report request(type, deviceIndex(), sub_id, address);
|
||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
||||
|
||||
auto response = sendReport(request);
|
||||
std::vector<uint8_t> Device::accessRegister(uint8_t sub_id, uint8_t address,
|
||||
const std::vector<uint8_t>& params) {
|
||||
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<uint8_t>& params) {
|
||||
sendReportNoACK(setupRegReport(deviceIndex(), sub_id, address, params));
|
||||
}
|
||||
|
@ -39,6 +39,9 @@ namespace logid::backend::hidpp10 {
|
||||
const std::vector<uint8_t>& params,
|
||||
hidpp::Report::Type type);
|
||||
|
||||
void setRegisterNoResponse(uint8_t address, const std::vector<uint8_t>& params,
|
||||
hidpp::Report::Type type);
|
||||
|
||||
protected:
|
||||
Device(const std::string& path, hidpp::DeviceIndex index,
|
||||
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
|
||||
@ -53,16 +56,22 @@ namespace logid::backend::hidpp10 {
|
||||
|
||||
private:
|
||||
typedef std::variant<hidpp::Report, hidpp::Report::Hidpp10Error> Response;
|
||||
|
||||
struct ResponseSlot {
|
||||
std::optional<Response> response;
|
||||
std::optional<uint8_t> sub_id;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
std::array<ResponseSlot, SubIDCount> _responses;
|
||||
|
||||
std::vector<uint8_t> accessRegister(
|
||||
uint8_t sub_id, uint8_t address, const std::vector<uint8_t>& params);
|
||||
|
||||
void accessRegisterNoResponse(
|
||||
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) {
|
||||
@ -73,6 +82,7 @@ namespace logid::backend::hidpp10 {
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
static std::shared_ptr<Device> make(Args... args) {
|
||||
|
@ -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
|
||||
|
@ -158,13 +158,17 @@ void ReceiverMonitor::enumerate() {
|
||||
}
|
||||
|
||||
void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) {
|
||||
auto handler_id = std::make_shared<EventHandlerLock<raw::RawDevice>>();
|
||||
|
||||
*handler_id = _receiver->rawDevice()->addEventHandler(
|
||||
const std::lock_guard lock(_wait_mutex);
|
||||
if (!_waiters.count(index)) {
|
||||
_waiters.emplace(index, _receiver->rawDevice()->addEventHandler(
|
||||
{[index](const std::vector<uint8_t>& report) -> bool {
|
||||
return report[Offset::DeviceIndex] == index;
|
||||
/* Connection events should be handled by connect_ev_handler */
|
||||
auto sub_id = report[Offset::SubID];
|
||||
return report[Offset::DeviceIndex] == index &&
|
||||
sub_id != Receiver::DeviceConnection &&
|
||||
sub_id != Receiver::DeviceDisconnection;
|
||||
},
|
||||
[self_weak = _self, index, handler_id](
|
||||
[self_weak = _self, index](
|
||||
[[maybe_unused]] const std::vector<uint8_t>& report) {
|
||||
hidpp::DeviceConnectionEvent event{};
|
||||
event.withPayload = false;
|
||||
@ -172,14 +176,13 @@ void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) {
|
||||
event.index = index;
|
||||
event.fromTimeoutCheck = true;
|
||||
|
||||
run_task([self_weak, event, handler_id]() {
|
||||
*handler_id = {};
|
||||
if (auto self = self_weak.lock()) {
|
||||
run_task([self_weak, event]() {
|
||||
if (auto self = self_weak.lock())
|
||||
self->_addHandler(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Receiver> ReceiverMonitor::receiver() const {
|
||||
@ -217,6 +220,8 @@ void ReceiverMonitor::_addHandler(const hidpp::DeviceConnectionEvent& event, int
|
||||
auto device_path = _receiver->devicePath();
|
||||
try {
|
||||
addDevice(event);
|
||||
const std::lock_guard lock(_wait_mutex);
|
||||
_waiters.erase(event.index);
|
||||
} catch (DeviceNotReady& e) {
|
||||
if (tries == max_tries) {
|
||||
logPrintf(WARN, "Failed to add device %s:%d after %d tries."
|
||||
|
@ -101,6 +101,9 @@ namespace logid::backend::hidpp10 {
|
||||
|
||||
std::weak_ptr<ReceiverMonitor> _self;
|
||||
|
||||
std::mutex _wait_mutex;
|
||||
std::map<hidpp::DeviceIndex, EventHandlerLock<raw::RawDevice>> _waiters;
|
||||
|
||||
public:
|
||||
template<typename T, typename... Args>
|
||||
static std::shared_ptr<T> make(Args... args) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 PixlOne
|
||||
* 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
|
||||
@ -17,6 +17,7 @@
|
||||
*/
|
||||
#include <backend/raw/IOMonitor.h>
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -35,6 +36,55 @@ IOHandler::IOHandler(std::function<void()> r,
|
||||
error(std::move(err)) {
|
||||
}
|
||||
|
||||
class IOMonitor::io_lock {
|
||||
std::optional<std::unique_lock<std::mutex>> _lock;
|
||||
IOMonitor* _io_monitor;
|
||||
const uint64_t counter = 1;
|
||||
|
||||
public:
|
||||
explicit io_lock(IOMonitor* io_monitor) : _io_monitor(io_monitor) {
|
||||
_io_monitor->_interrupting = true;
|
||||
[[maybe_unused]] ssize_t ret = ::write(_io_monitor->_event_fd, &counter, sizeof(counter));
|
||||
assert(ret == sizeof(counter));
|
||||
_lock.emplace(_io_monitor->_run_mutex);
|
||||
}
|
||||
|
||||
io_lock(const io_lock&) = delete;
|
||||
|
||||
io_lock& operator=(const io_lock&) = delete;
|
||||
|
||||
io_lock(io_lock&& o) noexcept: _lock(std::move(o._lock)), _io_monitor(o._io_monitor) {
|
||||
o._lock.reset();
|
||||
o._io_monitor = nullptr;
|
||||
}
|
||||
|
||||
io_lock& operator=(io_lock&& o) noexcept {
|
||||
if (this != &o) {
|
||||
_lock = std::move(o._lock);
|
||||
_io_monitor = o._io_monitor;
|
||||
o._lock.reset();
|
||||
o._io_monitor = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~io_lock() noexcept {
|
||||
if (_lock && _io_monitor) {
|
||||
uint64_t buf{};
|
||||
[[maybe_unused]] const ssize_t ret = ::read(
|
||||
_io_monitor->_event_fd, &buf, sizeof(counter));
|
||||
|
||||
assert(ret != -1);
|
||||
|
||||
if (buf == counter) {
|
||||
_io_monitor->_interrupting = false;
|
||||
_io_monitor->_interrupt_cv.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IOMonitor::IOMonitor() : _epoll_fd(epoll_create1(0)),
|
||||
_event_fd(eventfd(0, EFD_NONBLOCK)) {
|
||||
if (_epoll_fd < 0) {
|
||||
@ -69,7 +119,6 @@ IOMonitor::IOMonitor() : _epoll_fd(epoll_create1(0)),
|
||||
}
|
||||
|
||||
IOMonitor::~IOMonitor() noexcept {
|
||||
std::lock_guard<std::mutex> ctl_lock(_ctl_lock);
|
||||
_stop();
|
||||
|
||||
if (_event_fd >= 0)
|
||||
@ -80,23 +129,21 @@ IOMonitor::~IOMonitor() noexcept {
|
||||
}
|
||||
|
||||
void IOMonitor::_listen() {
|
||||
std::lock_guard<std::mutex> run_lock(_run_lock);
|
||||
std::unique_lock lock(_run_mutex);
|
||||
std::vector<struct epoll_event> events;
|
||||
|
||||
_is_running = true;
|
||||
|
||||
while (_is_running) {
|
||||
if (_interrupting) {
|
||||
std::unique_lock<std::mutex> lock(_interrupt_mutex);
|
||||
_interrupt_cv.wait(lock, [this]() {
|
||||
return !(bool) _interrupting;
|
||||
return !_interrupting;
|
||||
});
|
||||
|
||||
if (!_is_running)
|
||||
break;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> io_lock(_io_lock);
|
||||
if (events.size() != _fds.size())
|
||||
events.resize(_fds.size());
|
||||
int ev_count = ::epoll_wait(_epoll_fd, events.data(), (int) events.size(), -1);
|
||||
@ -113,76 +160,32 @@ void IOMonitor::_listen() {
|
||||
}
|
||||
|
||||
void IOMonitor::_stop() noexcept {
|
||||
_interrupt();
|
||||
{
|
||||
[[maybe_unused]] const io_lock lock(this);
|
||||
_is_running = false;
|
||||
_continue();
|
||||
}
|
||||
_io_thread->join();
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
bool IOMonitor::_running() const {
|
||||
std::unique_lock<std::mutex> run_lock(_run_lock, std::try_to_lock);
|
||||
return !run_lock.owns_lock() || _is_running;
|
||||
}
|
||||
|
||||
void IOMonitor::add(int fd, IOHandler handler) {
|
||||
std::lock_guard<std::mutex> lock(_ctl_lock);
|
||||
_interrupt();
|
||||
[[maybe_unused]] const io_lock lock(this);
|
||||
|
||||
struct epoll_event event{};
|
||||
event.events = EPOLLIN | EPOLLHUP | EPOLLERR;
|
||||
event.data.fd = fd;
|
||||
|
||||
// TODO: EPOLL_CTL_MOD
|
||||
if (_fds.contains(fd)) {
|
||||
_continue();
|
||||
if (_fds.contains(fd))
|
||||
throw std::runtime_error("duplicate io fd");
|
||||
}
|
||||
|
||||
if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event)) {
|
||||
_continue();
|
||||
if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event))
|
||||
throw std::system_error(errno, std::generic_category());
|
||||
}
|
||||
_fds.emplace(fd, std::move(handler));
|
||||
|
||||
_continue();
|
||||
}
|
||||
|
||||
void IOMonitor::remove(int fd) noexcept {
|
||||
std::lock_guard<std::mutex> lock(_ctl_lock);
|
||||
_interrupt();
|
||||
std::lock_guard<std::mutex> io_lock(_io_lock);
|
||||
[[maybe_unused]] const io_lock lock(this);
|
||||
|
||||
::epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
|
||||
_fds.erase(fd);
|
||||
|
||||
_continue();
|
||||
}
|
||||
|
||||
void IOMonitor::_interrupt() noexcept {
|
||||
std::unique_lock<std::mutex> run_lock(_run_lock, std::try_to_lock);
|
||||
|
||||
_interrupting = true;
|
||||
|
||||
uint64_t counter = 1;
|
||||
[[maybe_unused]] ssize_t ret = ::write(_event_fd, &counter, sizeof(counter));
|
||||
assert(ret == sizeof(counter));
|
||||
|
||||
// Wait for the IO monitor to _stop
|
||||
std::lock_guard<std::mutex> io_lock(_io_lock);
|
||||
|
||||
}
|
||||
|
||||
void IOMonitor::_continue() noexcept {
|
||||
std::unique_lock<std::mutex> run_lock(_run_lock, std::try_to_lock);
|
||||
|
||||
uint64_t counter;
|
||||
[[maybe_unused]] ssize_t ret = ::read(_event_fd, &counter, sizeof(counter));
|
||||
|
||||
assert(ret != -1);
|
||||
|
||||
if (counter == 1) {
|
||||
_interrupting = false;
|
||||
_interrupt_cv.notify_all();
|
||||
}
|
||||
}
|
@ -40,6 +40,14 @@ namespace logid::backend::raw {
|
||||
public:
|
||||
IOMonitor();
|
||||
|
||||
IOMonitor(IOMonitor&&) = delete;
|
||||
|
||||
IOMonitor(const IOMonitor&) = delete;
|
||||
|
||||
IOMonitor& operator=(IOMonitor&&) = delete;
|
||||
|
||||
IOMonitor& operator=(const IOMonitor&) = delete;
|
||||
|
||||
~IOMonitor() noexcept;
|
||||
|
||||
void add(int fd, IOHandler handler);
|
||||
@ -50,26 +58,19 @@ namespace logid::backend::raw {
|
||||
void _listen(); // This is a blocking call
|
||||
void _stop() noexcept;
|
||||
|
||||
[[maybe_unused]]
|
||||
[[nodiscard]] bool _running() const;
|
||||
|
||||
void _interrupt() noexcept;
|
||||
|
||||
void _continue() noexcept;
|
||||
|
||||
std::unique_ptr<std::thread> _io_thread;
|
||||
|
||||
std::map<int, IOHandler> _fds;
|
||||
std::mutex _io_lock, _ctl_lock;
|
||||
mutable std::mutex _run_lock;
|
||||
mutable std::mutex _run_mutex;
|
||||
std::atomic_bool _is_running;
|
||||
|
||||
std::atomic_bool _interrupting;
|
||||
std::mutex _interrupt_mutex;
|
||||
std::condition_variable _interrupt_cv;
|
||||
|
||||
const int _epoll_fd;
|
||||
const int _event_fd;
|
||||
|
||||
class io_lock;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include <regex>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -32,12 +33,17 @@ extern "C"
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/hidraw.h>
|
||||
#include <linux/input.h>
|
||||
}
|
||||
|
||||
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<size_t>(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<size_t>(len)};
|
||||
return {name_buf, static_cast<size_t>(len) - 1};
|
||||
}
|
||||
|
||||
RawDevice::RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& monitor) :
|
||||
@ -76,6 +112,12 @@ RawDevice::RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& mon
|
||||
_dev_info(get_dev_info(_fd)), _name(get_name(_fd)),
|
||||
_report_desc(getReportDescriptor(_fd)), _io_monitor(monitor->ioMonitor()),
|
||||
_event_handlers(std::make_shared<EventHandlerList<RawDevice>>()) {
|
||||
|
||||
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<uint8_t> RawDevice::getReportDescriptor(const std::string& path) {
|
||||
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1)
|
||||
@ -150,15 +200,17 @@ void RawDevice::sendReport(const std::vector<uint8_t>& report) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (write(_fd, report.data(), report.size()) == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
|
||||
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> RawDevice::addEventHandler(RawEventHandler handler) {
|
||||
std::unique_lock<std::shared_mutex> lock(_event_handlers->mutex);
|
||||
_event_handlers->list.emplace_front(std::move(handler));
|
||||
return {_event_handlers, _event_handlers->list.cbegin()};
|
||||
return {_event_handlers, _event_handlers->add(std::forward<RawEventHandler>(handler))};
|
||||
}
|
||||
|
||||
void RawDevice::_readReports() {
|
||||
@ -181,8 +233,5 @@ void RawDevice::_readReports() {
|
||||
}
|
||||
|
||||
void RawDevice::_handleEvent(const std::vector<uint8_t>& report) {
|
||||
std::shared_lock<std::shared_mutex> lock(_event_handlers->mutex);
|
||||
for (auto& handler : _event_handlers->list)
|
||||
if (handler.condition(report))
|
||||
handler.callback(report);
|
||||
_event_handlers->run_all(report);
|
||||
}
|
||||
|
@ -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<DeviceMonitor>& 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<uint8_t> getReportDescriptor(const std::string& path);
|
||||
|
||||
static std::vector<uint8_t> getReportDescriptor(int fd);
|
||||
@ -80,6 +91,8 @@ namespace logid::backend::raw {
|
||||
|
||||
std::shared_ptr<IOMonitor> _io_monitor;
|
||||
|
||||
bool _sub_device = false;
|
||||
|
||||
std::shared_ptr<EventHandlerList<RawDevice>> _event_handlers;
|
||||
|
||||
void _handleEvent(const std::vector<uint8_t>& report);
|
||||
|
@ -63,7 +63,7 @@ RemapButton::RemapButton(Device* dev) : DeviceFeature(dev),
|
||||
if ((action->reprogFlags() & hidpp20::ReprogControls::RawXYDiverted) &&
|
||||
(!_reprog_controls->supportsRawXY() ||
|
||||
!(info.additionalFlags & hidpp20::ReprogControls::RawXY)))
|
||||
logPrintf(WARN, "%s: Cannot divert raw XY movements for CID 0x%02x",
|
||||
logPrintf(WARN, "%s: 'Cannot divert raw XY movements for CID 0x%02x",
|
||||
_device->name().c_str(), info.controlID);
|
||||
|
||||
report.flags |= action->reprogFlags();
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <util/task.h>
|
||||
#include <queue>
|
||||
#include <optional>
|
||||
#include <cassert>
|
||||
|
||||
using namespace logid;
|
||||
using namespace std::chrono;
|
||||
@ -35,18 +36,38 @@ static std::priority_queue<task, std::vector<task>, task_less> tasks {};
|
||||
static std::mutex task_mutex {};
|
||||
static std::condition_variable task_cv {};
|
||||
static std::atomic_bool workers_init = false;
|
||||
static std::atomic_bool workers_run = false;
|
||||
|
||||
[[noreturn]] static void worker() {
|
||||
void stop_workers() {
|
||||
std::unique_lock lock(task_mutex);
|
||||
while (true) {
|
||||
task_cv.wait(lock, []() { return !tasks.empty(); });
|
||||
if (workers_init) {
|
||||
workers_run = false;
|
||||
lock.unlock();
|
||||
task_cv.notify_all();
|
||||
|
||||
/* Wait for all workers to end */
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
void worker() {
|
||||
std::unique_lock lock(task_mutex);
|
||||
while (workers_run) {
|
||||
task_cv.wait(lock, []() { return !tasks.empty() || !workers_run; });
|
||||
|
||||
if (!workers_run)
|
||||
break;
|
||||
|
||||
/* top task is in the future, wait */
|
||||
if (tasks.top().time >= system_clock::now()) {
|
||||
auto wait = tasks.top().time - system_clock::now();
|
||||
task_cv.wait_for(lock, wait, []() {
|
||||
return !tasks.empty() && (tasks.top().time < system_clock::now());
|
||||
return (!tasks.empty() && (tasks.top().time < system_clock::now())) ||
|
||||
!workers_run;
|
||||
});
|
||||
|
||||
if (!workers_run)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!tasks.empty()) {
|
||||
@ -67,11 +88,15 @@ static std::atomic_bool workers_init = false;
|
||||
|
||||
void logid::init_workers(int worker_count) {
|
||||
std::lock_guard lock(task_mutex);
|
||||
assert(!workers_init);
|
||||
|
||||
for (int i = 0; i < worker_count; ++i)
|
||||
std::thread(&worker).detach();
|
||||
|
||||
workers_init = true;
|
||||
workers_run = true;
|
||||
|
||||
atexit(&stop_workers);
|
||||
}
|
||||
|
||||
void logid::run_task(std::function<void()> function) {
|
||||
|
Loading…
Reference in New Issue
Block a user