mirror of
https://github.com/PixlOne/logiops.git
synced 2025-07-16 14:22:35 +08:00
Use epoll for I/O multiplexing
This commit is contained in:
parent
dbe24f9350
commit
7862c85d71
@ -41,6 +41,7 @@ add_executable(logid
|
||||
backend/Error.cpp
|
||||
backend/raw/DeviceMonitor.cpp
|
||||
backend/raw/RawDevice.cpp
|
||||
backend/raw/IOMonitor.cpp
|
||||
backend/dj/Receiver.cpp
|
||||
backend/dj/ReceiverMonitor.cpp
|
||||
backend/dj/Error.cpp
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include "util/log.h"
|
||||
#include "features/DPI.h"
|
||||
#include "Device.h"
|
||||
@ -97,7 +98,7 @@ std::shared_ptr<Device> Device::make(
|
||||
|
||||
Device::Device(std::string path, backend::hidpp::DeviceIndex index,
|
||||
std::shared_ptr<DeviceManager> manager) :
|
||||
_hidpp20 (path, index,
|
||||
_hidpp20 (path, index, manager,
|
||||
manager->config()->io_timeout.value_or(defaults::io_timeout)),
|
||||
_path (std::move(path)), _index (index),
|
||||
_config (_getConfig(manager, _hidpp20.name())),
|
||||
@ -113,8 +114,9 @@ Device::Device(std::string path, backend::hidpp::DeviceIndex index,
|
||||
Device::Device(std::shared_ptr<backend::raw::RawDevice> raw_device,
|
||||
hidpp::DeviceIndex index,
|
||||
std::shared_ptr<DeviceManager> manager) :
|
||||
_hidpp20(raw_device, index),
|
||||
_path (raw_device->hidrawPath()), _index (index),
|
||||
_hidpp20(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())),
|
||||
_receiver (nullptr),
|
||||
_manager (manager),
|
||||
@ -127,7 +129,9 @@ Device::Device(std::shared_ptr<backend::raw::RawDevice> raw_device,
|
||||
|
||||
Device::Device(Receiver* receiver, hidpp::DeviceIndex index,
|
||||
std::shared_ptr<DeviceManager> manager) :
|
||||
_hidpp20 (receiver->rawReceiver(), index),
|
||||
_hidpp20 (receiver->rawReceiver(), index,
|
||||
manager->config()->io_timeout.value_or(
|
||||
defaults::io_timeout)),
|
||||
_path (receiver->path()), _index (index),
|
||||
_config (_getConfig(manager, _hidpp20.name())),
|
||||
_receiver (receiver),
|
||||
@ -162,8 +166,6 @@ void Device::_init()
|
||||
feature.second->configure();
|
||||
feature.second->listen();
|
||||
}
|
||||
|
||||
_hidpp20.listen();
|
||||
}
|
||||
|
||||
std::string Device::name()
|
||||
|
@ -55,6 +55,7 @@ DeviceManager::DeviceManager(std::shared_ptr<Configuration> config,
|
||||
_device_node->add_server(_server);
|
||||
_receiver_node->add_server(_server);
|
||||
_root_node->add_server(_server);
|
||||
ready();
|
||||
}
|
||||
|
||||
std::shared_ptr<DeviceManager> DeviceManager::make(
|
||||
@ -94,8 +95,7 @@ void DeviceManager::addDevice(std::string path)
|
||||
|
||||
// Check if device is ignored before continuing
|
||||
{
|
||||
raw::RawDevice raw_dev(
|
||||
path,config()->io_timeout.value_or(defaults::io_timeout));
|
||||
raw::RawDevice raw_dev(path, _self.lock());
|
||||
if(config()->ignore.has_value() &&
|
||||
config()->ignore.value().contains(raw_dev.productId())) {
|
||||
logPrintf(DEBUG, "%s: Device 0x%04x ignored.",
|
||||
@ -106,7 +106,7 @@ void DeviceManager::addDevice(std::string path)
|
||||
|
||||
try {
|
||||
hidpp::Device device(
|
||||
path, hidpp::DefaultDevice,
|
||||
path, hidpp::DefaultDevice, _self.lock(),
|
||||
config()->io_timeout.value_or(defaults::io_timeout));
|
||||
isReceiver = device.version() == std::make_tuple(1, 0);
|
||||
} catch(hidpp10::Error &e) {
|
||||
@ -126,7 +126,6 @@ void DeviceManager::addDevice(std::string path)
|
||||
if(isReceiver) {
|
||||
logPrintf(INFO, "Detected receiver at %s", path.c_str());
|
||||
auto receiver = Receiver::make(path, _self.lock());
|
||||
receiver->run();
|
||||
std::lock_guard<std::mutex> lock(_map_lock);
|
||||
_receivers.emplace(path, receiver);
|
||||
_ipc_receivers->receiverAdded(receiver);
|
||||
|
@ -64,13 +64,14 @@ std::shared_ptr<Receiver> Receiver::make(
|
||||
|
||||
Receiver::Receiver(const std::string& path,
|
||||
const std::shared_ptr<DeviceManager>& manager) :
|
||||
dj::ReceiverMonitor(path,
|
||||
dj::ReceiverMonitor(path, manager,
|
||||
manager->config()->io_timeout.value_or(
|
||||
defaults::io_timeout)),
|
||||
_path (path), _manager (manager), _nickname (manager),
|
||||
_ipc_node (manager->receiversNode()->make_child(_nickname)),
|
||||
_ipc_interface (_ipc_node->make_interface<ReceiverIPC>(this))
|
||||
{
|
||||
ready();
|
||||
}
|
||||
|
||||
const Receiver::DeviceList& Receiver::devices() const {
|
||||
@ -118,7 +119,9 @@ void Receiver::addDevice(hidpp::DeviceConnectionEvent event)
|
||||
if(!event.linkEstablished)
|
||||
return;
|
||||
|
||||
hidpp::Device hidpp_device(receiver(), event);
|
||||
hidpp::Device hidpp_device(
|
||||
receiver(), event,
|
||||
manager->config()->io_timeout.value_or(defaults::io_timeout));
|
||||
|
||||
auto version = hidpp_device.version();
|
||||
|
||||
|
@ -22,3 +22,8 @@ const char *logid::backend::TimeoutError::what() const noexcept
|
||||
{
|
||||
return "Device timed out";
|
||||
}
|
||||
|
||||
const char *logid::backend::InvalidDevice::what() const noexcept
|
||||
{
|
||||
return "Device has become invalidated";
|
||||
}
|
||||
|
@ -29,6 +29,13 @@ public:
|
||||
TimeoutError() = default;
|
||||
const char* what() const noexcept override;
|
||||
};
|
||||
|
||||
class InvalidDevice : public std::exception
|
||||
{
|
||||
public:
|
||||
InvalidDevice() = default;
|
||||
const char* what() const noexcept override;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LOGID_BACKEND_ERROR_H
|
@ -43,13 +43,44 @@ InvalidReceiver::Reason InvalidReceiver::code() const noexcept
|
||||
return _reason;
|
||||
}
|
||||
|
||||
Receiver::Receiver(std::string path, double io_timeout) :
|
||||
_raw_device (std::make_shared<raw::RawDevice>(
|
||||
std::move(path), io_timeout)),
|
||||
_hidpp10_device (_raw_device, hidpp::DefaultDevice)
|
||||
Receiver::Receiver(std::string path,
|
||||
const std::shared_ptr<raw::DeviceMonitor>& monitor,
|
||||
double timeout) :
|
||||
_raw_device (std::make_shared<raw::RawDevice>(std::move(path), monitor)),
|
||||
_hidpp10_device (_raw_device, hidpp::DefaultDevice, timeout)
|
||||
{
|
||||
if(!supportsDjReports(_raw_device->reportDescriptor()))
|
||||
throw InvalidReceiver(InvalidReceiver::NoDJReports);
|
||||
|
||||
// Pass all HID++ events on DefaultDevice to handleHidppEvent
|
||||
_raw_hidpp_handler = _raw_device->addEventHandler({
|
||||
[](const std::vector<uint8_t>& report)->bool {
|
||||
return (report[hidpp::Offset::Type] == hidpp::Report::Type::Short ||
|
||||
report[hidpp::Offset::Type] == hidpp::Report::Type::Long);
|
||||
},
|
||||
[this](const std::vector<uint8_t>& report)->void {
|
||||
hidpp::Report _report(report);
|
||||
this->_handleHidppEvent(_report);
|
||||
}
|
||||
});
|
||||
|
||||
// Pass all DJ events with device index to handleDjEvent
|
||||
_raw_dj_handler = _raw_device->addEventHandler({
|
||||
[](const std::vector<uint8_t>& report)->bool {
|
||||
return (report[Offset::Type] == Report::Type::Short ||
|
||||
report[Offset::Type] == Report::Type::Long);
|
||||
},
|
||||
[this](const std::vector<uint8_t>& report)->void {
|
||||
Report _report(report);
|
||||
this->_handleDjEvent(_report);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Receiver::~Receiver()
|
||||
{
|
||||
_raw_device->removeEventHandler(_raw_dj_handler);
|
||||
_raw_device->removeEventHandler(_raw_hidpp_handler);
|
||||
}
|
||||
|
||||
void Receiver::enumerateDj()
|
||||
@ -312,7 +343,7 @@ void Receiver::_sendDjRequest(hidpp::DeviceIndex index, uint8_t function,
|
||||
|
||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
||||
|
||||
_raw_device->sendReportNoResponse(request.rawData());
|
||||
_raw_device->sendReport(request.rawData());
|
||||
}
|
||||
|
||||
Receiver::ConnectionStatusEvent Receiver::connectionStatusEvent(Report& report)
|
||||
@ -324,57 +355,6 @@ Receiver::ConnectionStatusEvent Receiver::connectionStatusEvent(Report& report)
|
||||
return event;
|
||||
}
|
||||
|
||||
void Receiver::listen()
|
||||
{
|
||||
if(!_raw_device->isListening())
|
||||
_raw_device->listenAsync();
|
||||
|
||||
if(_raw_device->eventHandlers().find("RECV_HIDPP") ==
|
||||
_raw_device->eventHandlers().end()) {
|
||||
// Pass all HID++ events on DefaultDevice to handleHidppEvent
|
||||
std::shared_ptr<raw::RawEventHandler> hidpp_handler =
|
||||
std::make_shared<raw::RawEventHandler>();
|
||||
hidpp_handler->condition = [](std::vector<uint8_t>& report)->bool
|
||||
{
|
||||
return (report[hidpp::Offset::Type] == hidpp::Report::Type::Short ||
|
||||
report[hidpp::Offset::Type] == hidpp::Report::Type::Long);
|
||||
};
|
||||
hidpp_handler->callback = [this](std::vector<uint8_t>& report)
|
||||
->void {
|
||||
hidpp::Report _report(report);
|
||||
this->_handleHidppEvent(_report);
|
||||
};
|
||||
_raw_device->addEventHandler("RECV_HIDPP", hidpp_handler);
|
||||
}
|
||||
|
||||
if(_raw_device->eventHandlers().find("RECV_DJ") ==
|
||||
_raw_device->eventHandlers().end()) {
|
||||
// Pass all DJ events with device index to handleDjEvent
|
||||
std::shared_ptr<raw::RawEventHandler> dj_handler =
|
||||
std::make_shared<raw::RawEventHandler>();
|
||||
dj_handler->condition = [](std::vector<uint8_t>& report)->bool
|
||||
{
|
||||
return (report[Offset::Type] == Report::Type::Short ||
|
||||
report[Offset::Type] == Report::Type::Long);
|
||||
};
|
||||
dj_handler->callback = [this](std::vector<uint8_t>& report)->void
|
||||
{
|
||||
Report _report(report);
|
||||
this->_handleDjEvent(_report);
|
||||
};
|
||||
_raw_device->addEventHandler("RECV_DJ", dj_handler);
|
||||
}
|
||||
}
|
||||
|
||||
void Receiver::stopListening()
|
||||
{
|
||||
_raw_device->removeEventHandler("RECV_HIDPP");
|
||||
_raw_device->removeEventHandler("RECV_DJ");
|
||||
|
||||
if(_raw_device->eventHandlers().empty())
|
||||
_raw_device->stopListener();
|
||||
}
|
||||
|
||||
std::shared_ptr<raw::RawDevice> Receiver::rawDevice() const
|
||||
{
|
||||
return _raw_device;
|
||||
|
@ -52,7 +52,10 @@ namespace dj
|
||||
class Receiver final
|
||||
{
|
||||
public:
|
||||
Receiver(std::string path, double io_timeout);
|
||||
Receiver(std::string path,
|
||||
const std::shared_ptr<raw::DeviceMonitor>& monitor,
|
||||
double timeout);
|
||||
~Receiver();
|
||||
|
||||
enum DjEvents : uint8_t
|
||||
{
|
||||
@ -161,9 +164,6 @@ namespace dj
|
||||
static hidpp::DeviceConnectionEvent deviceConnectionEvent(
|
||||
const hidpp::Report& report);
|
||||
|
||||
void listen();
|
||||
void stopListening();
|
||||
|
||||
void addDjEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<EventHandler>& handler);
|
||||
void removeDjEventHandler(const std::string& nickname);
|
||||
@ -184,6 +184,9 @@ namespace dj
|
||||
void _handleDjEvent(dj::Report& report);
|
||||
void _handleHidppEvent(hidpp::Report& report);
|
||||
|
||||
raw::RawDevice::EvHandlerId _raw_hidpp_handler;
|
||||
raw::RawDevice::EvHandlerId _raw_dj_handler;
|
||||
|
||||
std::map<std::string, std::shared_ptr<EventHandler>>
|
||||
_dj_event_handlers;
|
||||
std::map<std::string, std::shared_ptr<hidpp::EventHandler>>
|
||||
|
@ -25,8 +25,11 @@
|
||||
|
||||
using namespace logid::backend::dj;
|
||||
|
||||
ReceiverMonitor::ReceiverMonitor(std::string path, double io_timeout) :
|
||||
_receiver (std::make_shared<Receiver>(std::move(path), io_timeout))
|
||||
ReceiverMonitor::ReceiverMonitor(std::string path,
|
||||
std::shared_ptr<raw::DeviceMonitor> monitor,
|
||||
double timeout):
|
||||
_receiver (std::make_shared<Receiver>(
|
||||
std::move(path), std::move(monitor), timeout))
|
||||
{
|
||||
assert(_receiver->hidppEventHandlers().find("RECVMON") ==
|
||||
_receiver->hidppEventHandlers().end());
|
||||
@ -42,13 +45,11 @@ ReceiverMonitor::ReceiverMonitor(std::string path, double io_timeout) :
|
||||
|
||||
ReceiverMonitor::~ReceiverMonitor()
|
||||
{
|
||||
this->stop();
|
||||
_receiver->removeHidppEventHandler("RECVMON");
|
||||
}
|
||||
|
||||
void ReceiverMonitor::run()
|
||||
void ReceiverMonitor::ready()
|
||||
{
|
||||
_receiver->listen();
|
||||
|
||||
if(_receiver->hidppEventHandlers().find("RECVMON") ==
|
||||
_receiver->hidppEventHandlers().end()) {
|
||||
std::shared_ptr<hidpp::EventHandler> event_handler =
|
||||
@ -62,8 +63,8 @@ void ReceiverMonitor::run()
|
||||
/* Running in a new thread prevents deadlocks since the
|
||||
* receiver may be enumerating.
|
||||
*/
|
||||
std::async([this, report,
|
||||
path=this->_receiver->rawDevice()->hidrawPath()]() {
|
||||
spawn_task([this, report,
|
||||
path= this->_receiver->rawDevice()->rawPath()]() {
|
||||
if (report.subId() == Receiver::DeviceConnection) {
|
||||
try {
|
||||
this->addDevice(this->_receiver->deviceConnectionEvent
|
||||
@ -93,13 +94,6 @@ void ReceiverMonitor::run()
|
||||
enumerate();
|
||||
}
|
||||
|
||||
void ReceiverMonitor::stop()
|
||||
{
|
||||
_receiver->removeHidppEventHandler("RECVMON");
|
||||
|
||||
_receiver->stopListening();
|
||||
}
|
||||
|
||||
void ReceiverMonitor::enumerate()
|
||||
{
|
||||
_receiver->enumerateHidpp();
|
||||
@ -107,36 +101,34 @@ void ReceiverMonitor::enumerate()
|
||||
|
||||
void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index)
|
||||
{
|
||||
std::string nickname = "WAIT_DEV_" + std::to_string(index);
|
||||
auto handler = std::make_shared<raw::RawEventHandler>();
|
||||
handler->condition = [index](std::vector<uint8_t>& report)->bool {
|
||||
auto handler_id = std::make_shared<raw::RawDevice::EvHandlerId>();
|
||||
|
||||
*handler_id = _receiver->rawDevice()->addEventHandler({
|
||||
[index](const std::vector<uint8_t>& report)->bool {
|
||||
return report[Offset::DeviceIndex] == index;
|
||||
};
|
||||
|
||||
handler->callback = [this, index, nickname](std::vector<uint8_t>& report) {
|
||||
(void)report; // Suppress unused warning
|
||||
|
||||
},
|
||||
[this, index, handler_id](
|
||||
[[maybe_unused]] const std::vector<uint8_t>& report) {
|
||||
hidpp::DeviceConnectionEvent event{};
|
||||
event.withPayload = false;
|
||||
event.linkEstablished = true;
|
||||
event.index = index;
|
||||
event.fromTimeoutCheck = true;
|
||||
|
||||
spawn_task(
|
||||
[this, event, nickname]() {
|
||||
spawn_task([this, event, handler_id]() {
|
||||
assert(handler_id);
|
||||
try {
|
||||
_receiver->rawDevice()->removeEventHandler(nickname);
|
||||
this->addDevice(event);
|
||||
_receiver->rawDevice()->removeEventHandler(*handler_id);
|
||||
addDevice(event);
|
||||
} catch(std::exception& e) {
|
||||
logPrintf(ERROR, "Failed to add device %d to receiver "
|
||||
"on %s: %s", event.index,
|
||||
_receiver->rawDevice()->hidrawPath().c_str(),
|
||||
_receiver->rawDevice()->rawPath().c_str(),
|
||||
e.what());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
_receiver->rawDevice()->addEventHandler(nickname, handler);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<Receiver> ReceiverMonitor::receiver() const
|
||||
|
@ -33,14 +33,13 @@ namespace dj
|
||||
{
|
||||
public:
|
||||
ReceiverMonitor(std::string path,
|
||||
double io_timeout);
|
||||
std::shared_ptr<raw::DeviceMonitor> monitor,
|
||||
double timeout);
|
||||
virtual ~ReceiverMonitor();
|
||||
|
||||
void enumerate();
|
||||
void run();
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
void ready();
|
||||
virtual void addDevice(hidpp::DeviceConnectionEvent event) = 0;
|
||||
virtual void removeDevice(hidpp::DeviceIndex index) = 0;
|
||||
|
||||
@ -52,7 +51,7 @@ namespace dj
|
||||
|
||||
void _unpair();
|
||||
|
||||
std::shared_ptr<Receiver> receiver() const;
|
||||
[[nodiscard]] std::shared_ptr<Receiver> receiver() const;
|
||||
private:
|
||||
std::shared_ptr<Receiver> _receiver;
|
||||
};
|
||||
|
@ -66,7 +66,7 @@ static const std::array<uint8_t, 39> DJReportDesc2 = {
|
||||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
bool dj::supportsDjReports(std::vector<uint8_t>&& rdesc)
|
||||
bool dj::supportsDjReports(const std::vector<uint8_t>& rdesc)
|
||||
{
|
||||
auto it = std::search(rdesc.begin(), rdesc.end(),
|
||||
DJReportDesc.begin(), DJReportDesc.end());
|
||||
@ -76,7 +76,7 @@ bool dj::supportsDjReports(std::vector<uint8_t>&& rdesc)
|
||||
return it != rdesc.end();
|
||||
}
|
||||
|
||||
Report::Report(std::vector<uint8_t>& data) : _data (data)
|
||||
Report::Report(const std::vector<uint8_t>& data) : _data (data)
|
||||
{
|
||||
switch(data[Offset::Type]) {
|
||||
case ReportType::Short:
|
||||
|
@ -24,9 +24,7 @@
|
||||
#include "defs.h"
|
||||
#include "../hidpp/defs.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
namespace dj
|
||||
namespace logid::backend::dj
|
||||
{
|
||||
namespace Offset
|
||||
{
|
||||
@ -36,13 +34,13 @@ namespace dj
|
||||
static constexpr uint8_t Parameters = 3;
|
||||
}
|
||||
|
||||
bool supportsDjReports(std::vector<uint8_t>&& rdesc);
|
||||
bool supportsDjReports(const std::vector<uint8_t>& rdesc);
|
||||
class Report
|
||||
{
|
||||
public:
|
||||
typedef ReportType::ReportType Type;
|
||||
|
||||
explicit Report(std::vector<uint8_t>& data);
|
||||
explicit Report(const std::vector<uint8_t>& data);
|
||||
Report(Type type, hidpp::DeviceIndex index, uint8_t feature);
|
||||
|
||||
Type type() const;
|
||||
@ -53,6 +51,6 @@ namespace dj
|
||||
private:
|
||||
std::vector<uint8_t> _data;
|
||||
};
|
||||
}}}
|
||||
}
|
||||
|
||||
#endif //LOGID_BACKEND_DJ_REPORT_H
|
||||
|
@ -24,11 +24,14 @@
|
||||
#include "../hidpp20/features/DeviceName.h"
|
||||
#include "../hidpp20/Error.h"
|
||||
#include "../hidpp10/Error.h"
|
||||
#include "../Error.h"
|
||||
#include "../dj/Receiver.h"
|
||||
|
||||
using namespace logid::backend;
|
||||
using namespace logid::backend::hidpp;
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
const char* Device::InvalidDevice::what() const noexcept
|
||||
{
|
||||
switch(_reason) {
|
||||
@ -49,24 +52,31 @@ Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept
|
||||
}
|
||||
|
||||
Device::Device(const std::string& path, DeviceIndex index,
|
||||
double io_timeout):
|
||||
_raw_device (std::make_shared<raw::RawDevice>(path, io_timeout)),
|
||||
std::shared_ptr<raw::DeviceMonitor> monitor, double timeout):
|
||||
io_timeout (duration_cast<milliseconds>(
|
||||
duration<double, std::milli>(timeout))),
|
||||
_raw_device (std::make_shared<raw::RawDevice>(path, std::move(monitor))),
|
||||
_receiver (nullptr), _path (path), _index (index)
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index) :
|
||||
Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index,
|
||||
double timeout) :
|
||||
io_timeout (duration_cast<milliseconds>(
|
||||
duration<double, std::milli>(timeout))),
|
||||
_raw_device (std::move(raw_device)), _receiver (nullptr),
|
||||
_path (_raw_device->hidrawPath()), _index (index)
|
||||
_path (_raw_device->rawPath()), _index (index)
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
hidpp::DeviceConnectionEvent event) :
|
||||
hidpp::DeviceConnectionEvent event, double timeout) :
|
||||
io_timeout (duration_cast<milliseconds>(
|
||||
duration<double, std::milli>(timeout))),
|
||||
_raw_device (receiver->rawDevice()), _receiver (receiver),
|
||||
_path (receiver->rawDevice()->hidrawPath()), _index (event.index)
|
||||
_path (receiver->rawDevice()->rawPath()), _index (event.index)
|
||||
{
|
||||
// Device will throw an error soon, just do it now
|
||||
if(!event.linkEstablished)
|
||||
@ -80,8 +90,11 @@ Device::Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
DeviceIndex index) : _raw_device (receiver->rawDevice()),
|
||||
_receiver (receiver), _path (receiver->rawDevice()->hidrawPath()),
|
||||
DeviceIndex index, double timeout) :
|
||||
io_timeout (duration_cast<milliseconds>(
|
||||
duration<double, std::milli>(timeout))),
|
||||
_raw_device (receiver->rawDevice()),
|
||||
_receiver (receiver), _path (receiver->rawDevice()->rawPath()),
|
||||
_index (index)
|
||||
{
|
||||
_pid = receiver->getPairingInfo(_index).pid;
|
||||
@ -105,11 +118,21 @@ std::tuple<uint8_t, uint8_t> Device::version() const
|
||||
|
||||
void Device::_init()
|
||||
{
|
||||
_listening = false;
|
||||
_supported_reports = getSupportedReports(_raw_device->reportDescriptor());
|
||||
if(!_supported_reports)
|
||||
supported_reports = getSupportedReports(_raw_device->reportDescriptor());
|
||||
if(!supported_reports)
|
||||
throw InvalidDevice(InvalidDevice::NoHIDPPReport);
|
||||
|
||||
_raw_handler = _raw_device->addEventHandler({
|
||||
[index=this->_index](const std::vector<uint8_t>& report)->bool {
|
||||
return (report[Offset::Type] == Report::Type::Short ||
|
||||
report[Offset::Type] == Report::Type::Long) &&
|
||||
(report[Offset::DeviceIndex] == index);
|
||||
}, [this](const std::vector<uint8_t>& report)->void {
|
||||
Report _report(report);
|
||||
this->handleEvent(_report);
|
||||
} });
|
||||
|
||||
try {
|
||||
try {
|
||||
hidpp20::EssentialRoot root(this);
|
||||
_version = root.getVersion();
|
||||
@ -146,12 +169,15 @@ void Device::_init()
|
||||
_name = _receiver->getDeviceName(_index);
|
||||
}
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
_raw_device->removeEventHandler(_raw_handler);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
if(_listening)
|
||||
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
|
||||
_raw_device->removeEventHandler(_raw_handler);
|
||||
}
|
||||
|
||||
void Device::addEventHandler(const std::string& nickname,
|
||||
@ -175,53 +201,76 @@ const std::map<std::string, std::shared_ptr<EventHandler>>&
|
||||
|
||||
void Device::handleEvent(Report& report)
|
||||
{
|
||||
if(responseReport(report))
|
||||
return;
|
||||
|
||||
for(auto& handler : _event_handlers)
|
||||
if(handler.second->condition(report))
|
||||
handler.second->callback(report);
|
||||
}
|
||||
|
||||
Report Device::sendReport(Report& report)
|
||||
Report Device::sendReport(const Report &report)
|
||||
{
|
||||
switch(report.type())
|
||||
std::lock_guard<std::mutex> lock(_send_lock);
|
||||
{
|
||||
case Report::Type::Short:
|
||||
if(!(_supported_reports & HIDPP_REPORT_SHORT_SUPPORTED))
|
||||
report.setType(Report::Type::Long);
|
||||
break;
|
||||
case Report::Type::Long:
|
||||
/* Report can be truncated, but that isn't a good idea. */
|
||||
assert(_supported_reports & HIDPP_REPORT_LONG_SUPPORTED);
|
||||
std::lock_guard<std::mutex> sl(_slot_lock);
|
||||
_report_slot = {};
|
||||
}
|
||||
sendReportNoResponse(report);
|
||||
std::unique_lock<std::mutex> wait(_resp_wait_lock);
|
||||
bool valid = _resp_cv.wait_for(
|
||||
wait, io_timeout, [this](){
|
||||
std::lock_guard<std::mutex> sl(_slot_lock);
|
||||
return _report_slot.has_value();
|
||||
});
|
||||
|
||||
auto raw_response = _raw_device->sendReport(report.rawReport());
|
||||
if(!valid)
|
||||
throw TimeoutError();
|
||||
|
||||
Report response(raw_response);
|
||||
|
||||
Report::Hidpp10Error hidpp10_error{};
|
||||
if(response.isError10(&hidpp10_error))
|
||||
throw hidpp10::Error(hidpp10_error.error_code);
|
||||
|
||||
Report::Hidpp20Error hidpp20_error{};
|
||||
if(response.isError20(&hidpp20_error))
|
||||
throw hidpp20::Error(hidpp20_error.error_code);
|
||||
|
||||
return response;
|
||||
{
|
||||
Report::Hidpp10Error error{};
|
||||
if(report.isError10(&error))
|
||||
throw hidpp10::Error(error.error_code);
|
||||
}
|
||||
{
|
||||
Report::Hidpp20Error error{};
|
||||
if(report.isError20(&error))
|
||||
throw hidpp20::Error(error.error_code);
|
||||
}
|
||||
return _report_slot.value();
|
||||
}
|
||||
|
||||
void Device::sendReportNoResponse(Report &report)
|
||||
bool Device::responseReport(const Report &report)
|
||||
{
|
||||
if(_send_lock.try_lock()) {
|
||||
_send_lock.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard lock(_slot_lock);
|
||||
_report_slot = report;
|
||||
_resp_cv.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Device::sendReportNoResponse(Report report)
|
||||
{
|
||||
reportFixup(report);
|
||||
_raw_device->sendReport(report.rawReport());
|
||||
}
|
||||
|
||||
void Device::reportFixup(Report& report)
|
||||
{
|
||||
switch(report.type())
|
||||
{
|
||||
case Report::Type::Short:
|
||||
if(!(_supported_reports & HIDPP_REPORT_SHORT_SUPPORTED))
|
||||
if(!(supported_reports & HIDPP_REPORT_SHORT_SUPPORTED))
|
||||
report.setType(Report::Type::Long);
|
||||
break;
|
||||
case Report::Type::Long:
|
||||
/* Report can be truncated, but that isn't a good idea. */
|
||||
assert(_supported_reports & HIDPP_REPORT_LONG_SUPPORTED);
|
||||
assert(supported_reports & HIDPP_REPORT_LONG_SUPPORTED);
|
||||
}
|
||||
|
||||
_raw_device->sendReportNoResponse(report.rawReport());
|
||||
}
|
||||
|
||||
std::string Device::name() const
|
||||
@ -233,36 +282,3 @@ uint16_t Device::pid() const
|
||||
{
|
||||
return _pid;
|
||||
}
|
||||
|
||||
void Device::listen()
|
||||
{
|
||||
if(!_raw_device->isListening())
|
||||
_raw_device->listenAsync();
|
||||
|
||||
// Pass all HID++ events with device index to this device.
|
||||
auto handler = std::make_shared<raw::RawEventHandler>();
|
||||
handler->condition = [index=this->_index](std::vector<uint8_t>& report)
|
||||
->bool {
|
||||
return (report[Offset::Type] == Report::Type::Short ||
|
||||
report[Offset::Type] == Report::Type::Long) &&
|
||||
(report[Offset::DeviceIndex] == index);
|
||||
};
|
||||
handler->callback = [this](std::vector<uint8_t>& report)->void {
|
||||
Report _report(report);
|
||||
this->handleEvent(_report);
|
||||
};
|
||||
|
||||
_raw_device->addEventHandler("DEV_" + std::to_string(_index), handler);
|
||||
_listening = true;
|
||||
}
|
||||
|
||||
void Device::stopListening()
|
||||
{
|
||||
if(_listening)
|
||||
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
|
||||
|
||||
_listening = false;
|
||||
|
||||
if(!_raw_device->eventHandlers().empty())
|
||||
_raw_device->stopListener();
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
#ifndef LOGID_BACKEND_HIDPP_DEVICE_H
|
||||
#define LOGID_BACKEND_HIDPP_DEVICE_H
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
@ -62,14 +63,14 @@ namespace hidpp
|
||||
};
|
||||
|
||||
Device(const std::string& path, DeviceIndex index,
|
||||
double io_timeout);
|
||||
Device(std::shared_ptr<raw::RawDevice> raw_device,
|
||||
DeviceIndex index);
|
||||
std::shared_ptr<raw::DeviceMonitor> monitor, double timeout);
|
||||
Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index,
|
||||
double timeout);
|
||||
Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
hidpp::DeviceConnectionEvent event);
|
||||
hidpp::DeviceConnectionEvent event, double timeout);
|
||||
Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
DeviceIndex index);
|
||||
~Device();
|
||||
DeviceIndex index, double timeout);
|
||||
virtual ~Device();
|
||||
|
||||
std::string devicePath() const;
|
||||
DeviceIndex deviceIndex() const;
|
||||
@ -78,33 +79,42 @@ namespace hidpp
|
||||
std::string name() const;
|
||||
uint16_t pid() const;
|
||||
|
||||
void listen(); // Runs asynchronously
|
||||
void stopListening();
|
||||
|
||||
void addEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<EventHandler>& handler);
|
||||
void removeEventHandler(const std::string& nickname);
|
||||
const std::map<std::string, std::shared_ptr<EventHandler>>&
|
||||
eventHandlers();
|
||||
|
||||
Report sendReport(Report& report);
|
||||
void sendReportNoResponse(Report& report);
|
||||
virtual Report sendReport(const Report& report);
|
||||
void sendReportNoResponse(Report report);
|
||||
|
||||
void handleEvent(Report& report);
|
||||
protected:
|
||||
// Returns whether the report is a response
|
||||
virtual bool responseReport(const Report& report);
|
||||
|
||||
void reportFixup(Report& report);
|
||||
|
||||
const std::chrono::milliseconds io_timeout;
|
||||
uint8_t supported_reports;
|
||||
private:
|
||||
void _init();
|
||||
|
||||
std::shared_ptr<raw::RawDevice> _raw_device;
|
||||
raw::RawDevice::EvHandlerId _raw_handler;
|
||||
std::shared_ptr<dj::Receiver> _receiver;
|
||||
std::string _path;
|
||||
DeviceIndex _index;
|
||||
uint8_t _supported_reports;
|
||||
|
||||
std::tuple<uint8_t, uint8_t> _version;
|
||||
uint16_t _pid;
|
||||
std::string _name;
|
||||
|
||||
std::atomic<bool> _listening;
|
||||
std::mutex _send_lock;
|
||||
std::mutex _resp_wait_lock;
|
||||
std::condition_variable _resp_cv;
|
||||
std::mutex _slot_lock;
|
||||
std::optional<Report> _report_slot;
|
||||
|
||||
std::map<std::string, std::shared_ptr<EventHandler>> _event_handlers;
|
||||
};
|
||||
|
@ -84,7 +84,7 @@ static const std::array<uint8_t, 22> LongReportDesc2 = {
|
||||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
uint8_t hidpp::getSupportedReports(std::vector<uint8_t>&& rdesc)
|
||||
uint8_t hidpp::getSupportedReports(const std::vector<uint8_t>& rdesc)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
|
||||
@ -248,7 +248,7 @@ uint8_t Report::swId() const
|
||||
|
||||
void Report::setSwId(uint8_t sub_id)
|
||||
{
|
||||
_data[Offset::Function] &= 0x0f;
|
||||
_data[Offset::Function] &= 0xf0;
|
||||
_data[Offset::Function] |= sub_id & 0x0f;
|
||||
}
|
||||
|
||||
@ -290,7 +290,7 @@ void Report::setParams(const std::vector<uint8_t>& _params)
|
||||
_data[Offset::Parameters + i] = _params[i];
|
||||
}
|
||||
|
||||
bool Report::isError10(Report::Hidpp10Error *error)
|
||||
bool Report::isError10(Report::Hidpp10Error *error) const
|
||||
{
|
||||
assert(error != nullptr);
|
||||
|
||||
@ -305,7 +305,7 @@ bool Report::isError10(Report::Hidpp10Error *error)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Report::isError20(Report::Hidpp20Error* error)
|
||||
bool Report::isError20(Report::Hidpp20Error* error) const
|
||||
{
|
||||
assert(error != nullptr);
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace logid {
|
||||
namespace backend {
|
||||
namespace hidpp
|
||||
{
|
||||
uint8_t getSupportedReports(std::vector<uint8_t>&& rdesc);
|
||||
uint8_t getSupportedReports(const std::vector<uint8_t>& rdesc);
|
||||
|
||||
namespace Offset
|
||||
{
|
||||
@ -78,8 +78,8 @@ namespace hidpp
|
||||
Report::Type type() const;
|
||||
void setType(Report::Type type);
|
||||
|
||||
logid::backend::hidpp::DeviceIndex deviceIndex() const;
|
||||
void setDeviceIndex(hidpp::DeviceIndex index);
|
||||
DeviceIndex deviceIndex() const;
|
||||
void setDeviceIndex(DeviceIndex index);
|
||||
|
||||
uint8_t feature() const;
|
||||
void setFeature(uint8_t feature);
|
||||
@ -106,15 +106,15 @@ namespace hidpp
|
||||
{
|
||||
uint8_t sub_id, address, error_code;
|
||||
};
|
||||
bool isError10(Hidpp10Error* error);
|
||||
bool isError10(Hidpp10Error* error) const;
|
||||
|
||||
struct Hidpp20Error
|
||||
{
|
||||
uint8_t feature_index, function, software_id, error_code;
|
||||
};
|
||||
bool isError20(Hidpp20Error* error);
|
||||
bool isError20(Hidpp20Error* error) const;
|
||||
|
||||
std::vector<uint8_t> rawReport () const { return _data; }
|
||||
std::vector<uint8_t> rawReport() const { return _data; }
|
||||
|
||||
static constexpr std::size_t HeaderLength = 4;
|
||||
private:
|
||||
|
@ -19,7 +19,7 @@
|
||||
#ifndef LOGID_BACKEND_HIDPP_DEFS_H
|
||||
#define LOGID_BACKEND_HIDPP_DEFS_H
|
||||
|
||||
#define LOGID_HIDPP_SOFTWARE_ID 0
|
||||
#define LOGID_HIDPP_SOFTWARE_ID 1
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
@ -20,29 +20,107 @@
|
||||
#include <utility>
|
||||
#include "Device.h"
|
||||
#include "defs.h"
|
||||
#include "../Error.h"
|
||||
|
||||
using namespace logid::backend;
|
||||
using namespace logid::backend::hidpp10;
|
||||
|
||||
Device::Device(const std::string &path, hidpp::DeviceIndex index,
|
||||
double io_timeout) :
|
||||
hidpp::Device(path, index, io_timeout)
|
||||
Device::Device(const std::string &path,
|
||||
hidpp::DeviceIndex index,
|
||||
std::shared_ptr<raw::DeviceMonitor> monitor, double timeout) :
|
||||
hidpp::Device(path, index, std::move(monitor), timeout)
|
||||
{
|
||||
assert(version() == std::make_tuple(1, 0));
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<raw::RawDevice> raw_dev,
|
||||
hidpp::DeviceIndex index) : hidpp::Device(std::move(raw_dev), index)
|
||||
hidpp::DeviceIndex index,
|
||||
double timeout) : hidpp::Device(std::move(raw_dev), index, timeout)
|
||||
{
|
||||
assert(version() == std::make_tuple(1, 0));
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<dj::Receiver> receiver, hidpp::DeviceIndex index)
|
||||
: hidpp::Device(receiver, index)
|
||||
Device::Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
hidpp::DeviceIndex index,
|
||||
double timeout)
|
||||
: hidpp::Device(std::move(receiver), index, timeout)
|
||||
{
|
||||
assert(version() == std::make_tuple(1, 0));
|
||||
}
|
||||
|
||||
hidpp::Report Device::sendReport(const hidpp::Report& report)
|
||||
{
|
||||
decltype(_responses)::iterator response_slot;
|
||||
while(true) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
response_slot = _responses.find(report.subId());
|
||||
if(response_slot == _responses.end()) {
|
||||
response_slot = _responses.emplace(
|
||||
report.subId(), std::optional<Response>()).first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(_response_wait_lock);
|
||||
_response_cv.wait(lock, [this, sub_id=report.subId()](){
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
return _responses.find(sub_id) != _responses.end();
|
||||
});
|
||||
}
|
||||
|
||||
sendReportNoResponse(report);
|
||||
std::unique_lock<std::mutex> wait(_response_wait_lock);
|
||||
bool valid = _response_cv.wait_for(wait, io_timeout,
|
||||
[this, &response_slot]() {
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
return response_slot->second.has_value();
|
||||
});
|
||||
|
||||
if(!valid) {
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
_responses.erase(response_slot);
|
||||
throw TimeoutError();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
assert(response_slot->second.has_value());
|
||||
auto response = response_slot->second.value();
|
||||
_responses.erase(response_slot);
|
||||
if(std::holds_alternative<hidpp::Report>(response))
|
||||
return std::get<hidpp::Report>(response);
|
||||
else // if(std::holds_alternative<Error::ErrorCode>(response))
|
||||
throw Error(std::get<Error::ErrorCode>(response));
|
||||
}
|
||||
|
||||
bool Device::responseReport(const hidpp::Report& report)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
uint8_t sub_id;
|
||||
|
||||
bool is_error = false;
|
||||
hidpp::Report::Hidpp10Error hidpp10_error {};
|
||||
if(report.isError10(&hidpp10_error)) {
|
||||
sub_id = hidpp10_error.sub_id;
|
||||
is_error = true;
|
||||
} else {
|
||||
sub_id = report.subId();
|
||||
}
|
||||
|
||||
auto response_slot = _responses.find(sub_id);
|
||||
if(response_slot == _responses.end())
|
||||
return false;
|
||||
|
||||
if(is_error) {
|
||||
response_slot->second = static_cast<Error::ErrorCode>(
|
||||
hidpp10_error.error_code);
|
||||
} else {
|
||||
response_slot->second = report;
|
||||
}
|
||||
|
||||
_response_cv.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Device::getRegister(uint8_t address,
|
||||
const std::vector<uint8_t>& params, hidpp::Report::Type type)
|
||||
{
|
||||
@ -76,7 +154,7 @@ std::vector<uint8_t> Device::accessRegister(uint8_t sub_id, uint8_t address,
|
||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
||||
|
||||
auto response = sendReport(request);
|
||||
return std::vector<uint8_t>(response.paramBegin(), response.paramEnd());
|
||||
return {response.paramBegin(), response.paramEnd()};
|
||||
}
|
||||
|
||||
|
||||
|
@ -19,7 +19,11 @@
|
||||
#ifndef LOGID_BACKEND_HIDPP10_DEVICE_H
|
||||
#define LOGID_BACKEND_HIDPP10_DEVICE_H
|
||||
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#include "../hidpp/Device.h"
|
||||
#include "Error.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
@ -29,18 +33,29 @@ namespace hidpp10
|
||||
{
|
||||
public:
|
||||
Device(const std::string& path, hidpp::DeviceIndex index,
|
||||
double io_timeout);
|
||||
std::shared_ptr<raw::DeviceMonitor> monitor, double timeout);
|
||||
Device(std::shared_ptr<raw::RawDevice> raw_dev,
|
||||
hidpp::DeviceIndex index);
|
||||
hidpp::DeviceIndex index, double timeout);
|
||||
Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
hidpp::DeviceIndex index);
|
||||
hidpp::DeviceIndex index, double timeout);
|
||||
|
||||
hidpp::Report sendReport(const hidpp::Report& report) final;
|
||||
|
||||
std::vector<uint8_t> getRegister(uint8_t address,
|
||||
const std::vector<uint8_t>& params, hidpp::Report::Type type);
|
||||
|
||||
std::vector<uint8_t> setRegister(uint8_t address,
|
||||
const std::vector<uint8_t>& params, hidpp::Report::Type type);
|
||||
protected:
|
||||
bool responseReport(const hidpp::Report& report) final;
|
||||
private:
|
||||
std::mutex _response_lock;
|
||||
std::mutex _response_wait_lock;
|
||||
std::condition_variable _response_cv;
|
||||
|
||||
typedef std::variant<hidpp::Report, Error::ErrorCode> Response;
|
||||
std::map<uint8_t, std::optional<Response>> _responses;
|
||||
|
||||
std::vector<uint8_t> accessRegister(uint8_t sub_id,
|
||||
uint8_t address, const std::vector<uint8_t>& params);
|
||||
};
|
||||
|
@ -20,26 +20,44 @@
|
||||
|
||||
#include "Device.h"
|
||||
#include "../hidpp/defs.h"
|
||||
#include "../Error.h"
|
||||
#include "../dj/Receiver.h"
|
||||
|
||||
using namespace logid::backend;
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
Device::Device(std::string path, hidpp::DeviceIndex index, double io_timeout) :
|
||||
hidpp::Device(path, index, io_timeout)
|
||||
Device::Device(const std::string& path, hidpp::DeviceIndex index,
|
||||
std::shared_ptr<raw::DeviceMonitor> monitor, double timeout) :
|
||||
hidpp::Device(path, index,
|
||||
std::move(monitor), timeout)
|
||||
{
|
||||
assert(std::get<0>(version()) >= 2);
|
||||
// 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)
|
||||
: hidpp::Device(raw_device, index)
|
||||
hidpp::DeviceIndex index, double timeout) :
|
||||
hidpp::Device(std::move(raw_device), index, timeout)
|
||||
{
|
||||
assert(std::get<0>(version()) >= 2);
|
||||
if(std::get<0>(version()) < 2)
|
||||
throw std::runtime_error("Invalid HID++ version");
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<dj::Receiver> receiver, hidpp::DeviceIndex index)
|
||||
: hidpp::Device(receiver, index)
|
||||
Device::Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
hidpp::DeviceConnectionEvent event, double timeout) :
|
||||
hidpp::Device(std::move(receiver), event, timeout)
|
||||
{
|
||||
assert(std::get<0>(version()) >= 2);
|
||||
if(std::get<0>(version()) < 2)
|
||||
throw std::runtime_error("Invalid HID++ version");
|
||||
}
|
||||
|
||||
Device::Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
hidpp::DeviceIndex index, double timeout)
|
||||
: hidpp::Device(std::move(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,
|
||||
@ -82,3 +100,93 @@ void Device::callFunctionNoResponse(uint8_t feature_index, uint8_t function,
|
||||
|
||||
this->sendReportNoResponse(request);
|
||||
}
|
||||
|
||||
hidpp::Report Device::sendReport(const hidpp::Report& report)
|
||||
{
|
||||
decltype(_responses)::iterator response_slot;
|
||||
|
||||
while(true) {
|
||||
{
|
||||
std::lock_guard lock(_response_lock);
|
||||
if(_responses.empty()) {
|
||||
response_slot = _responses.emplace(
|
||||
2, std::optional<Response>()).first;
|
||||
break;
|
||||
} else if(_responses.size() < response_slots) {
|
||||
uint8_t i = 0;
|
||||
for(auto& x : _responses) {
|
||||
if(x.first != i + 1) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
i = x.first;
|
||||
}
|
||||
assert(_responses.count(i) == 0);
|
||||
|
||||
response_slot = _responses.emplace(
|
||||
i, std::optional<Response>()).first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(_response_wait_lock);
|
||||
_response_cv.wait(lock, [this, sub_id=report.subId()](){
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
return _responses.size() < response_slots;
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
hidpp::Report mod_report {report};
|
||||
mod_report.setSwId(response_slot->first);
|
||||
sendReportNoResponse(std::move(mod_report));
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> wait(_response_wait_lock);
|
||||
bool valid = _response_cv.wait_for(wait, io_timeout,
|
||||
[this, &response_slot]() {
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
return response_slot->second.has_value();
|
||||
});
|
||||
|
||||
if(!valid) {
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
_responses.erase(response_slot);
|
||||
throw TimeoutError();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
assert(response_slot->second.has_value());
|
||||
auto response = response_slot->second.value();
|
||||
_responses.erase(response_slot);
|
||||
if(std::holds_alternative<hidpp::Report>(response))
|
||||
return std::get<hidpp::Report>(response);
|
||||
else // if(std::holds_alternative<Error::ErrorCode>(response))
|
||||
throw Error(std::get<Error::ErrorCode>(response));
|
||||
}
|
||||
|
||||
bool Device::responseReport(const hidpp::Report& report)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_response_lock);
|
||||
uint8_t sw_id = report.swId();
|
||||
|
||||
bool is_error = false;
|
||||
hidpp::Report::Hidpp20Error hidpp20_error {};
|
||||
if(report.isError20(&hidpp20_error))
|
||||
is_error = true;
|
||||
|
||||
auto response_slot = _responses.find(sw_id);
|
||||
if(response_slot == _responses.end())
|
||||
return false;
|
||||
|
||||
if(is_error) {
|
||||
response_slot->second = static_cast<Error::ErrorCode>(
|
||||
hidpp20_error.error_code);
|
||||
} else {
|
||||
response_slot->second = report;
|
||||
}
|
||||
|
||||
_response_cv.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
@ -19,8 +19,12 @@
|
||||
#ifndef LOGID_BACKEND_HIDPP20_DEVICE_H
|
||||
#define LOGID_BACKEND_HIDPP20_DEVICE_H
|
||||
|
||||
#include "../hidpp/Device.h"
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#include "../hidpp/Device.h"
|
||||
#include "Error.h"
|
||||
|
||||
namespace logid {
|
||||
namespace backend {
|
||||
@ -28,11 +32,14 @@ namespace hidpp20 {
|
||||
class Device : public hidpp::Device
|
||||
{
|
||||
public:
|
||||
Device(std::string path, hidpp::DeviceIndex index,
|
||||
double io_timeout);
|
||||
Device(std::shared_ptr<raw::RawDevice> raw_device, hidpp::DeviceIndex index);
|
||||
Device(std::shared_ptr<dj::Receiver> receiver, hidpp::DeviceIndex
|
||||
index);
|
||||
Device(const std::string& path, hidpp::DeviceIndex index,
|
||||
std::shared_ptr<raw::DeviceMonitor> monitor, double timeout);
|
||||
Device(std::shared_ptr<raw::RawDevice> raw_device,
|
||||
hidpp::DeviceIndex index, double timeout);
|
||||
Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
hidpp::DeviceConnectionEvent event, double timeout);
|
||||
Device(std::shared_ptr<dj::Receiver> receiver,
|
||||
hidpp::DeviceIndex index, double timeout);
|
||||
|
||||
std::vector<uint8_t> callFunction(uint8_t feature_index,
|
||||
uint8_t function,
|
||||
@ -41,6 +48,18 @@ namespace hidpp20 {
|
||||
void callFunctionNoResponse(uint8_t feature_index,
|
||||
uint8_t function,
|
||||
std::vector<uint8_t>& params);
|
||||
|
||||
hidpp::Report sendReport(const hidpp::Report& report) final;
|
||||
protected:
|
||||
bool responseReport(const hidpp::Report& report) final;
|
||||
private:
|
||||
std::mutex _response_lock;
|
||||
std::mutex _response_wait_lock;
|
||||
std::condition_variable _response_cv;
|
||||
|
||||
static constexpr int response_slots = 14;
|
||||
typedef std::variant<hidpp::Report, Error::ErrorCode> Response;
|
||||
std::map<uint8_t, std::optional<Response>> _responses;
|
||||
};
|
||||
}}}
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include "RawDevice.h"
|
||||
#include "../hidpp/Device.h"
|
||||
|
||||
#include <thread>
|
||||
#include <system_error>
|
||||
|
||||
extern "C"
|
||||
@ -34,117 +33,85 @@ extern "C"
|
||||
using namespace logid;
|
||||
using namespace logid::backend::raw;
|
||||
|
||||
DeviceMonitor::DeviceMonitor()
|
||||
DeviceMonitor::DeviceMonitor() : _io_monitor (std::make_shared<IOMonitor>()),
|
||||
_ready (false)
|
||||
{
|
||||
if(-1 == pipe(_pipe))
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"pipe creation failed");
|
||||
|
||||
int ret;
|
||||
_udev_context = udev_new();
|
||||
if(!_udev_context)
|
||||
throw std::runtime_error("udev_new failed");
|
||||
|
||||
_udev_monitor = udev_monitor_new_from_netlink(_udev_context,
|
||||
"udev");
|
||||
if(!_udev_monitor) {
|
||||
if(_udev_context)
|
||||
udev_unref(_udev_context);
|
||||
throw std::runtime_error("udev_monitor_new_from_netlink failed");
|
||||
}
|
||||
|
||||
ret = udev_monitor_filter_add_match_subsystem_devtype(
|
||||
_udev_monitor, "hidraw", nullptr);
|
||||
if(0 != ret) {
|
||||
if(_udev_monitor)
|
||||
udev_monitor_unref(_udev_monitor);
|
||||
if(_udev_context)
|
||||
udev_unref(_udev_context);
|
||||
throw std::system_error(
|
||||
-ret, std::system_category(),
|
||||
"udev_monitor_filter_add_match_subsystem_devtype");
|
||||
}
|
||||
|
||||
ret = udev_monitor_enable_receiving(_udev_monitor);
|
||||
if(0 != ret) {
|
||||
if(_udev_monitor)
|
||||
udev_monitor_unref(_udev_monitor);
|
||||
if(_udev_context)
|
||||
udev_unref(_udev_context);
|
||||
throw std::system_error(-ret, std::system_category(),
|
||||
"udev_monitor_enable_receiving");
|
||||
}
|
||||
|
||||
_fd = udev_monitor_get_fd(_udev_monitor);
|
||||
}
|
||||
|
||||
DeviceMonitor::~DeviceMonitor()
|
||||
{
|
||||
this->stop();
|
||||
if(_ready)
|
||||
_io_monitor->remove(_fd);
|
||||
|
||||
if(_udev_monitor)
|
||||
udev_monitor_unref(_udev_monitor);
|
||||
if(_udev_context)
|
||||
udev_unref(_udev_context);
|
||||
|
||||
for(int i : _pipe)
|
||||
close(i);
|
||||
}
|
||||
|
||||
void DeviceMonitor::run()
|
||||
void DeviceMonitor::ready()
|
||||
{
|
||||
int ret;
|
||||
std::lock_guard<std::mutex> lock(_running);
|
||||
if(_ready)
|
||||
return;
|
||||
_ready = true;
|
||||
|
||||
struct udev_monitor* monitor = udev_monitor_new_from_netlink(_udev_context,
|
||||
"udev");
|
||||
if(!monitor)
|
||||
throw std::runtime_error("udev_monitor_new_from_netlink failed");
|
||||
|
||||
ret = udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw",
|
||||
nullptr);
|
||||
if (0 != ret)
|
||||
throw std::system_error (-ret, std::system_category(),
|
||||
"udev_monitor_filter_add_match_subsystem_devtype");
|
||||
|
||||
ret = udev_monitor_enable_receiving(monitor);
|
||||
if(0 != ret)
|
||||
throw std::system_error(-ret, std::system_category(),
|
||||
"udev_moniotr_enable_receiving");
|
||||
|
||||
this->enumerate();
|
||||
|
||||
int fd = udev_monitor_get_fd(monitor);
|
||||
|
||||
_run_monitor = true;
|
||||
while (_run_monitor) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(_pipe[0], &fds);
|
||||
FD_SET(fd, &fds);
|
||||
|
||||
if (-1 == select (std::max (_pipe[0], fd)+1, &fds, nullptr,
|
||||
nullptr, nullptr)) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
throw std::system_error (errno, std::system_category(),
|
||||
"udev_monitor select");
|
||||
}
|
||||
|
||||
if (FD_ISSET(fd, &fds)) {
|
||||
struct udev_device *device = udev_monitor_receive_device(monitor);
|
||||
_io_monitor->add(_fd, {
|
||||
[this]() {
|
||||
struct udev_device *device = udev_monitor_receive_device(
|
||||
_udev_monitor);
|
||||
std::string action = udev_device_get_action(device);
|
||||
std::string devnode = udev_device_get_devnode(device);
|
||||
|
||||
if (action == "add")
|
||||
spawn_task(
|
||||
[this, name=devnode]() {
|
||||
try {
|
||||
// Wait for device to initialise
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
auto supported_reports = backend::hidpp::getSupportedReports(
|
||||
RawDevice::getReportDescriptor(name));
|
||||
if(supported_reports)
|
||||
this->addDevice(name);
|
||||
else
|
||||
logPrintf(DEBUG, "Unsupported device %s ignored",
|
||||
name.c_str());
|
||||
} catch(std::exception& e) {
|
||||
logPrintf(WARN, "Error adding device %s: %s",
|
||||
name.c_str(), e.what());
|
||||
}
|
||||
});
|
||||
spawn_task([this, devnode]() { _addHandler(devnode); } );
|
||||
else if (action == "remove")
|
||||
spawn_task(
|
||||
[this, name=devnode]() {
|
||||
try {
|
||||
this->removeDevice(name);
|
||||
} catch(std::exception& e) {
|
||||
logPrintf(WARN, "Error removing device %s: %s",
|
||||
name.c_str(), e.what());
|
||||
spawn_task([this, devnode]() { _removeHandler(devnode); } );
|
||||
|
||||
udev_device_unref(device);
|
||||
},
|
||||
[]() {
|
||||
throw std::runtime_error("udev hangup");
|
||||
},
|
||||
[]() {
|
||||
throw std::runtime_error("udev error");
|
||||
}
|
||||
});
|
||||
|
||||
udev_device_unref (device);
|
||||
}
|
||||
if (FD_ISSET(_pipe[0], &fds)) {
|
||||
char c;
|
||||
if (-1 == read(_pipe[0], &c, sizeof (char)))
|
||||
throw std::system_error (errno, std::system_category(),
|
||||
"read pipe");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceMonitor::stop()
|
||||
{
|
||||
_run_monitor = false;
|
||||
std::lock_guard<std::mutex> lock(_running);
|
||||
}
|
||||
|
||||
void DeviceMonitor::enumerate()
|
||||
@ -174,22 +141,39 @@ void DeviceMonitor::enumerate()
|
||||
std::string devnode = udev_device_get_devnode(device);
|
||||
udev_device_unref(device);
|
||||
|
||||
spawn_task(
|
||||
[this, name=devnode]() {
|
||||
try {
|
||||
auto supported_reports = backend::hidpp::getSupportedReports(
|
||||
RawDevice::getReportDescriptor(name));
|
||||
if(supported_reports)
|
||||
this->addDevice(name);
|
||||
else
|
||||
logPrintf(DEBUG, "Unsupported device %s ignored",
|
||||
name.c_str());
|
||||
} catch(std::exception& e) {
|
||||
logPrintf(WARN, "Error adding device %s: %s",
|
||||
name.c_str(), e.what());
|
||||
}
|
||||
});
|
||||
spawn_task([this, devnode]() { _addHandler(devnode); } );
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceMonitor::_removeHandler(const std::string& device)
|
||||
{
|
||||
try {
|
||||
this->removeDevice(device);
|
||||
} catch(std::exception& e) {
|
||||
logPrintf(WARN, "Error removing device %s: %s",
|
||||
device.c_str(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<IOMonitor> DeviceMonitor::ioMonitor() const
|
||||
{
|
||||
return _io_monitor;
|
||||
}
|
||||
|
@ -23,28 +23,40 @@
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include "IOMonitor.h"
|
||||
|
||||
struct udev;
|
||||
extern "C"
|
||||
{
|
||||
struct udev;
|
||||
struct udev_monitor;
|
||||
}
|
||||
|
||||
namespace logid::backend::raw
|
||||
{
|
||||
class DeviceMonitor
|
||||
{
|
||||
public:
|
||||
virtual ~DeviceMonitor();
|
||||
|
||||
void enumerate();
|
||||
void run();
|
||||
void stop();
|
||||
[[nodiscard]] std::shared_ptr<IOMonitor> ioMonitor() const;
|
||||
|
||||
protected:
|
||||
DeviceMonitor();
|
||||
virtual ~DeviceMonitor();
|
||||
// This should be run once the derived class is ready
|
||||
void ready();
|
||||
virtual void addDevice(std::string device) = 0;
|
||||
virtual void removeDevice(std::string device) = 0;
|
||||
private:
|
||||
void _addHandler(const std::string& device);
|
||||
void _removeHandler(const std::string& device);
|
||||
|
||||
std::shared_ptr<IOMonitor> _io_monitor;
|
||||
|
||||
struct udev* _udev_context;
|
||||
int _pipe[2];
|
||||
std::atomic<bool> _run_monitor;
|
||||
std::mutex _running;
|
||||
struct udev_monitor* _udev_monitor;
|
||||
int _fd;
|
||||
bool _ready;
|
||||
};
|
||||
}
|
||||
|
||||
|
200
src/logid/backend/raw/IOMonitor.cpp
Normal file
200
src/logid/backend/raw/IOMonitor.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <cassert>
|
||||
#include "IOMonitor.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
}
|
||||
|
||||
using namespace logid::backend::raw;
|
||||
|
||||
IOHandler::IOHandler(std::function<void()> r,
|
||||
std::function<void()> hup,
|
||||
std::function<void()> err) :
|
||||
read (std::move(r)),
|
||||
hangup (std::move(hup)),
|
||||
error (std::move(err))
|
||||
{
|
||||
}
|
||||
|
||||
IOMonitor::IOMonitor() : _epoll_fd (epoll_create1(0)),
|
||||
_event_fd(eventfd(0, EFD_NONBLOCK))
|
||||
{
|
||||
if(_epoll_fd < 0) {
|
||||
if(_event_fd >= 0)
|
||||
close(_event_fd);
|
||||
throw std::runtime_error("failed to create epoll fd");
|
||||
}
|
||||
|
||||
if(_event_fd < 0) {
|
||||
if(_epoll_fd >= 0)
|
||||
close(_epoll_fd);
|
||||
throw std::runtime_error("failed to create event fd");
|
||||
}
|
||||
|
||||
struct epoll_event event{};
|
||||
event.events = EPOLLIN;
|
||||
event.data.fd = _event_fd;
|
||||
|
||||
if(::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, _event_fd, &event)) {
|
||||
throw std::system_error(errno, std::generic_category());
|
||||
}
|
||||
|
||||
_fds.emplace(std::piecewise_construct,std::forward_as_tuple(_event_fd),
|
||||
std::forward_as_tuple([]() { },[]() {
|
||||
throw std::runtime_error("eventfd hangup");
|
||||
},[]() {
|
||||
throw std::runtime_error("eventfd error");
|
||||
}));
|
||||
|
||||
std::thread([this](){
|
||||
_listen();
|
||||
}).detach();
|
||||
}
|
||||
|
||||
IOMonitor::~IOMonitor() noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> ctl_lock(_ctl_lock);
|
||||
_stop();
|
||||
|
||||
if(_event_fd >= 0)
|
||||
close(_event_fd);
|
||||
|
||||
if(_epoll_fd >= 0)
|
||||
close(_epoll_fd);
|
||||
}
|
||||
|
||||
void IOMonitor::_listen()
|
||||
{
|
||||
std::unique_lock<std::mutex> run_lock(_run_lock, std::try_to_lock);
|
||||
if(!run_lock.owns_lock())
|
||||
throw std::runtime_error("IOMonitor already listening");
|
||||
std::vector<struct epoll_event> events;
|
||||
|
||||
_is_running = true;
|
||||
|
||||
while(_is_running) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_interrupt_lock);
|
||||
_interrupt_cv.wait(lock, [this](){
|
||||
return !(bool)_interrupting;
|
||||
});
|
||||
|
||||
if(!_is_running)
|
||||
break;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> io_lock(_io_lock);
|
||||
if(events.size() != _fds.size())
|
||||
events.resize(_fds.size());
|
||||
int eventc = ::epoll_wait(_epoll_fd, events.data(), events.size(), -1);
|
||||
for(int i = 0; i < eventc; ++i) {
|
||||
const auto& handler = _fds.at(events[i].data.fd);
|
||||
if(events[i].events & EPOLLIN)
|
||||
handler.read();
|
||||
if(events[i].events & EPOLLHUP)
|
||||
handler.hangup();
|
||||
if(events[i].events & EPOLLERR)
|
||||
handler.error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IOMonitor::_stop() noexcept
|
||||
{
|
||||
_interrupt();
|
||||
_is_running = false;
|
||||
_continue();
|
||||
std::lock_guard<std::mutex> run_lock(_run_lock);
|
||||
}
|
||||
|
||||
bool IOMonitor::_running() const
|
||||
{
|
||||
std::unique_lock<std::mutex> run_lock(_run_lock, std::try_to_lock);
|
||||
return !run_lock.owns_lock();
|
||||
}
|
||||
|
||||
void IOMonitor::add(int fd, IOHandler handler)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_ctl_lock);
|
||||
_interrupt();
|
||||
|
||||
struct epoll_event event{};
|
||||
event.events = EPOLLIN | EPOLLHUP | EPOLLERR;
|
||||
event.data.fd = fd;
|
||||
|
||||
// TODO: EPOLL_CTL_MOD
|
||||
if(_fds.contains(fd)) {
|
||||
_continue();
|
||||
throw std::runtime_error("duplicate io fd");
|
||||
}
|
||||
|
||||
if(::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event)) {
|
||||
_continue();
|
||||
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);
|
||||
|
||||
::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;
|
||||
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;
|
||||
ssize_t ret = ::read(_event_fd, &counter, sizeof(counter));
|
||||
|
||||
assert(ret != -1);
|
||||
|
||||
if(counter == 1) {
|
||||
_interrupting = false;
|
||||
_interrupt_cv.notify_all();
|
||||
}
|
||||
}
|
69
src/logid/backend/raw/IOMonitor.h
Normal file
69
src/logid/backend/raw/IOMonitor.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_BACKEND_RAW_IOMONITOR_H
|
||||
#define LOGID_BACKEND_RAW_IOMONITOR_H
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace logid::backend::raw {
|
||||
struct IOHandler {
|
||||
std::function<void()> read;
|
||||
std::function<void()> hangup;
|
||||
std::function<void()> error;
|
||||
|
||||
IOHandler(std::function<void()> r,
|
||||
std::function<void()> hup,
|
||||
std::function<void()> err);
|
||||
};
|
||||
|
||||
class IOMonitor
|
||||
{
|
||||
public:
|
||||
IOMonitor();
|
||||
~IOMonitor() noexcept;
|
||||
|
||||
void add(int fd, IOHandler handler);
|
||||
void remove(int fd) noexcept;
|
||||
private:
|
||||
void _listen(); // This is a blocking call
|
||||
void _stop() noexcept;
|
||||
|
||||
bool _running() const;
|
||||
|
||||
void _interrupt() noexcept;
|
||||
void _continue() noexcept;
|
||||
|
||||
std::map<int, IOHandler> _fds;
|
||||
std::mutex _io_lock, _ctl_lock;
|
||||
mutable std::mutex _run_lock;
|
||||
std::atomic_bool _is_running;
|
||||
|
||||
std::atomic_bool _interrupting;
|
||||
std::mutex _interrupt_lock;
|
||||
std::condition_variable _interrupt_cv;
|
||||
|
||||
const int _epoll_fd;
|
||||
const int _event_fd;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //LOGID_BACKEND_RAW_IOMONITOR_H
|
@ -17,19 +17,15 @@
|
||||
*/
|
||||
|
||||
#include "RawDevice.h"
|
||||
#include "DeviceMonitor.h"
|
||||
#include "IOMonitor.h"
|
||||
#include "../Error.h"
|
||||
#include "../hidpp/defs.h"
|
||||
#include "../dj/defs.h"
|
||||
#include "../../util/log.h"
|
||||
#include "../hidpp/Report.h"
|
||||
#include "../../util/thread.h"
|
||||
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
#define MAX_DATA_LENGTH 32
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <cassert>
|
||||
@ -43,101 +39,88 @@ using namespace logid::backend::raw;
|
||||
using namespace logid::backend;
|
||||
using namespace std::chrono;
|
||||
|
||||
bool RawDevice::supportedReport(uint8_t id, uint8_t length)
|
||||
int get_fd(const std::string& path)
|
||||
{
|
||||
switch(id) {
|
||||
case hidpp::ReportType::Short:
|
||||
return length == (hidpp::ShortParamLength +
|
||||
hidpp::Report::HeaderLength);
|
||||
case hidpp::ReportType::Long:
|
||||
return length == (hidpp::LongParamLength +
|
||||
hidpp::Report::HeaderLength);
|
||||
case dj::ReportType::Short:
|
||||
return length == (dj::ShortParamLength + dj::HeaderLength);
|
||||
case dj::ReportType::Long:
|
||||
return length == (dj::LongParamLength + dj::HeaderLength);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RawDevice::RawDevice(std::string path, double io_timeout) :
|
||||
_path (std::move(path)),
|
||||
_continue_listen (false), _continue_respond (false),
|
||||
_io_timeout (duration_cast<milliseconds>(
|
||||
duration<double, std::milli>(io_timeout)))
|
||||
{
|
||||
int ret;
|
||||
|
||||
_fd = ::open(_path.c_str(), O_RDWR);
|
||||
if (_fd == -1)
|
||||
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
||||
if(fd == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"RawDevice open failed");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
RawDevice::dev_info get_devinfo(int fd)
|
||||
{
|
||||
hidraw_devinfo devinfo{};
|
||||
if (-1 == ::ioctl(_fd, HIDIOCGRAWINFO, &devinfo)) {
|
||||
if (-1 == ::ioctl(fd, HIDIOCGRAWINFO, &devinfo)) {
|
||||
int err = errno;
|
||||
::close(_fd);
|
||||
::close(fd);
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice HIDIOCGRAWINFO failed");
|
||||
}
|
||||
_vid = devinfo.vendor;
|
||||
_pid = devinfo.product;
|
||||
|
||||
return {devinfo.vendor, devinfo.product};
|
||||
}
|
||||
|
||||
std::string get_name(int fd)
|
||||
{
|
||||
ssize_t len;
|
||||
char name_buf[256];
|
||||
if (-1 == (ret = ::ioctl(_fd, HIDIOCGRAWNAME(sizeof(name_buf)), name_buf)
|
||||
)) {
|
||||
if (-1 == (len = ::ioctl(fd, HIDIOCGRAWNAME(sizeof(name_buf)), name_buf))) {
|
||||
int err = errno;
|
||||
::close(_fd);
|
||||
::close(fd);
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice HIDIOCGRAWNAME failed");
|
||||
}
|
||||
_name.assign(name_buf, ret - 1);
|
||||
|
||||
_rdesc = getReportDescriptor(_fd);
|
||||
|
||||
if (-1 == ::pipe(_pipe)) {
|
||||
int err = errno;
|
||||
close(_fd);
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice pipe open failed");
|
||||
}
|
||||
|
||||
_continue_listen = false;
|
||||
return {name_buf, static_cast<size_t>(len)};
|
||||
}
|
||||
|
||||
RawDevice::~RawDevice()
|
||||
RawDevice::RawDevice(std::string path,
|
||||
std::shared_ptr<DeviceMonitor> monitor) :
|
||||
_valid (true),
|
||||
_path (std::move(path)),
|
||||
_fd (get_fd(_path)),
|
||||
_devinfo (get_devinfo(_fd)),
|
||||
_name (get_name(_fd)),
|
||||
_rdesc (getReportDescriptor(_fd)),
|
||||
_io_monitor (monitor->ioMonitor())
|
||||
{
|
||||
if(_fd != -1)
|
||||
{
|
||||
::close(_fd);
|
||||
::close(_pipe[0]);
|
||||
::close(_pipe[1]);
|
||||
}
|
||||
_io_monitor->add(_fd, {
|
||||
[this]() { _readReports(); },
|
||||
[this]() { _valid = false; },
|
||||
[this]() { _valid = false; }
|
||||
});
|
||||
}
|
||||
std::string RawDevice::hidrawPath() const
|
||||
|
||||
RawDevice::~RawDevice() noexcept
|
||||
{
|
||||
_io_monitor->remove(_fd);
|
||||
::close(_fd);
|
||||
}
|
||||
|
||||
const std::string& RawDevice::rawPath() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
std::string RawDevice::name() const
|
||||
const std::string& RawDevice::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
uint16_t RawDevice::vendorId() const
|
||||
int16_t RawDevice::vendorId() const
|
||||
{
|
||||
return _vid;
|
||||
return _devinfo.vid;
|
||||
}
|
||||
|
||||
uint16_t RawDevice::productId() const
|
||||
int16_t RawDevice::productId() const
|
||||
{
|
||||
return _pid;
|
||||
return _devinfo.pid;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::getReportDescriptor(std::string path)
|
||||
{
|
||||
int fd = ::open(path.c_str(), O_RDWR);
|
||||
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"open failed");
|
||||
@ -165,140 +148,16 @@ std::vector<uint8_t> RawDevice::getReportDescriptor(int fd)
|
||||
return std::vector<uint8_t>(rdesc.value, rdesc.value + rdesc.size);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::reportDescriptor() const
|
||||
const std::vector<uint8_t>& RawDevice::reportDescriptor() const
|
||||
{
|
||||
return _rdesc;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::sendReport(const std::vector<uint8_t>& report)
|
||||
void RawDevice::sendReport(const std::vector<uint8_t>& report)
|
||||
{
|
||||
/* If the listener will stop, handle I/O manually.
|
||||
* Otherwise, push to queue and wait for result. */
|
||||
if(_continue_listen) {
|
||||
std::mutex send_report;
|
||||
std::unique_lock<std::mutex> lock(send_report);
|
||||
std::condition_variable cv;
|
||||
bool top_of_queue = false;
|
||||
auto task = std::make_shared<std::packaged_task<std::vector<uint8_t>()>>
|
||||
( [this, report, &cv, &top_of_queue] () {
|
||||
top_of_queue = true;
|
||||
cv.notify_all();
|
||||
return this->_respondToReport(report);
|
||||
});
|
||||
auto f = task->get_future();
|
||||
_io_queue.push(task);
|
||||
interruptRead(false); // Alert listener to prioritise
|
||||
cv.wait(lock, [&top_of_queue]{ return top_of_queue; });
|
||||
auto status = f.wait_for(_io_timeout);
|
||||
if(status == std::future_status::timeout) {
|
||||
_continue_respond = false;
|
||||
interruptRead();
|
||||
return f.get(); // Expecting an error, but it could work
|
||||
}
|
||||
return f.get();
|
||||
}
|
||||
else {
|
||||
std::exception_ptr _exception;
|
||||
auto response = std::async(std::launch::deferred,
|
||||
[this, report]()->std::vector<uint8_t> {
|
||||
return _respondToReport(report);
|
||||
});
|
||||
auto status = response.wait_for(_io_timeout);
|
||||
if(status == std::future_status::timeout) {
|
||||
interruptRead();
|
||||
if(response.valid())
|
||||
response.wait();
|
||||
throw TimeoutError();
|
||||
}
|
||||
return response.get();
|
||||
}
|
||||
}
|
||||
if(!_valid)
|
||||
throw InvalidDevice();
|
||||
|
||||
// DJ commands are not systematically acknowledged, do not expect a result.
|
||||
void RawDevice::sendReportNoResponse(const std::vector<uint8_t>& report)
|
||||
{
|
||||
/* If the listener will stop, handle I/O manually.
|
||||
* Otherwise, push to queue and wait for result. */
|
||||
if(_continue_listen) {
|
||||
auto task = std::make_shared<std::packaged_task<std::vector<uint8_t>()>>
|
||||
([this, report]() {
|
||||
this->_sendReport(report);
|
||||
return std::vector<uint8_t>();
|
||||
});
|
||||
auto f = task->get_future();
|
||||
_io_queue.push(task);
|
||||
f.get();
|
||||
}
|
||||
else
|
||||
_sendReport(report);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::_respondToReport
|
||||
(const std::vector<uint8_t>& request)
|
||||
{
|
||||
_sendReport(request);
|
||||
_continue_respond = true;
|
||||
|
||||
auto start_point = std::chrono::steady_clock::now();
|
||||
|
||||
while(_continue_respond) {
|
||||
std::vector<uint8_t> response;
|
||||
auto current_point = std::chrono::steady_clock::now();
|
||||
auto timeout = _io_timeout - std::chrono::duration_cast
|
||||
<std::chrono::milliseconds>(current_point - start_point);
|
||||
if(timeout.count() <= 0)
|
||||
throw TimeoutError();
|
||||
_readReport(response, MAX_DATA_LENGTH, timeout);
|
||||
|
||||
if(!_continue_respond)
|
||||
throw TimeoutError();
|
||||
|
||||
// All reports have the device index at byte 2
|
||||
if(response[1] != request[1]) {
|
||||
if(_continue_listen)
|
||||
this->_handleEvent(response);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(hidpp::ReportType::Short == request[0] ||
|
||||
hidpp::ReportType::Long == request[0]) {
|
||||
if(hidpp::ReportType::Short != response[0] &&
|
||||
hidpp::ReportType::Long != response[0]) {
|
||||
if(_continue_listen)
|
||||
this->_handleEvent(response);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Error; leave to device to handle
|
||||
if(response[2] == 0x8f || response[2] == 0xff)
|
||||
return response;
|
||||
|
||||
bool others_match = true;
|
||||
for(int i = 2; i < 4; i++)
|
||||
if(response[i] != request[i])
|
||||
others_match = false;
|
||||
|
||||
if(others_match)
|
||||
return response;
|
||||
} else if(dj::ReportType::Short == request[0] ||
|
||||
dj::ReportType::Long == request[0]) {
|
||||
//Error; leave to device ot handle
|
||||
if(0x7f == response[2])
|
||||
return response;
|
||||
else if(response[2] == request[2])
|
||||
return response;
|
||||
}
|
||||
|
||||
if(_continue_listen)
|
||||
this->_handleEvent(response);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int RawDevice::_sendReport(const std::vector<uint8_t>& report)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_dev_io);
|
||||
if(logid::global_loglevel <= LogLevel::RAWREPORT) {
|
||||
printf("[RAWREPORT] %s OUT: ", _path.c_str());
|
||||
for(auto &i : report)
|
||||
@ -306,76 +165,32 @@ int RawDevice::_sendReport(const std::vector<uint8_t>& report)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
assert(supportedReport(report[0], report.size()));
|
||||
|
||||
int ret = ::write(_fd, report.data(), report.size());
|
||||
if(ret == -1) {
|
||||
///TODO: This seems like a hacky solution
|
||||
// Try again before failing
|
||||
ret = ::write(_fd, report.data(), report.size());
|
||||
if(ret == -1)
|
||||
if(write(_fd, report.data(), report.size()) == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"_sendReport write failed");
|
||||
}
|
||||
|
||||
return ret;
|
||||
"sendReport write failed");
|
||||
}
|
||||
|
||||
int RawDevice::_readReport(std::vector<uint8_t> &report,
|
||||
std::size_t maxDataLength)
|
||||
RawDevice::EvHandlerId RawDevice::addEventHandler(RawEventHandler handler)
|
||||
{
|
||||
return _readReport(report, maxDataLength, _io_timeout);
|
||||
std::unique_lock<std::mutex> lock(_event_handler_lock);
|
||||
_event_handlers.emplace_front(std::move(handler));
|
||||
return _event_handlers.cbegin();
|
||||
}
|
||||
|
||||
int RawDevice::_readReport(std::vector<uint8_t> &report,
|
||||
std::size_t maxDataLength, std::chrono::milliseconds timeout)
|
||||
void RawDevice::removeEventHandler(RawDevice::EvHandlerId id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_dev_io);
|
||||
int ret;
|
||||
report.resize(maxDataLength);
|
||||
std::unique_lock<std::mutex> lock(_event_handler_lock);
|
||||
_event_handlers.erase(id);
|
||||
}
|
||||
|
||||
timeval timeout_tv{};
|
||||
timeout_tv.tv_sec = duration_cast<seconds>(_io_timeout)
|
||||
.count();
|
||||
timeout_tv.tv_usec = duration_cast<microseconds>(
|
||||
_io_timeout).count() %
|
||||
duration_cast<microseconds>(seconds(1)).count();
|
||||
void RawDevice::_readReports()
|
||||
{
|
||||
uint8_t buf[max_data_length];
|
||||
ssize_t len;
|
||||
|
||||
auto timeout_ms = duration_cast<milliseconds>(timeout).count();
|
||||
|
||||
fd_set fds;
|
||||
do {
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(_fd, &fds);
|
||||
FD_SET(_pipe[0], &fds);
|
||||
|
||||
ret = select(std::max(_fd, _pipe[0]) + 1,
|
||||
&fds, nullptr, nullptr,
|
||||
(timeout_ms > 0 ? nullptr : &timeout_tv));
|
||||
} while(ret == -1 && errno == EINTR);
|
||||
|
||||
if(ret == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"_readReport select failed");
|
||||
|
||||
if(FD_ISSET(_fd, &fds)) {
|
||||
ret = read(_fd, report.data(), report.size());
|
||||
if(ret == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"_readReport read failed");
|
||||
report.resize(ret);
|
||||
}
|
||||
|
||||
if(FD_ISSET(_pipe[0], &fds)) {
|
||||
char c;
|
||||
ret = read(_pipe[0], &c, sizeof(char));
|
||||
if(ret == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"_readReport read pipe failed");
|
||||
}
|
||||
|
||||
if(0 == ret)
|
||||
throw backend::TimeoutError();
|
||||
while(-1 != (len = ::read(_fd, buf, max_data_length))) {
|
||||
assert(len <= max_data_length);
|
||||
std::vector<uint8_t> report(buf, buf + len);
|
||||
|
||||
if(logid::global_loglevel <= LogLevel::RAWREPORT) {
|
||||
printf("[RAWREPORT] %s IN: ", _path.c_str());
|
||||
@ -384,102 +199,14 @@ int RawDevice::_readReport(std::vector<uint8_t> &report,
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RawDevice::interruptRead(bool wait_for_halt)
|
||||
{
|
||||
char c = 0;
|
||||
if(-1 == write(_pipe[1], &c, sizeof(char)))
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"interruptRead write pipe failed");
|
||||
|
||||
// Ensure I/O has halted
|
||||
if(wait_for_halt)
|
||||
std::lock_guard<std::mutex> lock(_dev_io);
|
||||
}
|
||||
|
||||
void RawDevice::listen()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_listening);
|
||||
_continue_listen = true;
|
||||
_listen_condition.notify_all();
|
||||
while(_continue_listen) {
|
||||
while(!_io_queue.empty()) {
|
||||
auto task = _io_queue.front();
|
||||
(*task)();
|
||||
_io_queue.pop();
|
||||
_handleEvent(report);
|
||||
}
|
||||
std::vector<uint8_t> report;
|
||||
_readReport(report, MAX_DATA_LENGTH);
|
||||
|
||||
this->_handleEvent(report);
|
||||
}
|
||||
|
||||
// Listener is stopped, handle I/O queue
|
||||
while(!_io_queue.empty()) {
|
||||
auto task = _io_queue.front();
|
||||
(*task)();
|
||||
_io_queue.pop();
|
||||
}
|
||||
|
||||
_continue_listen = false;
|
||||
}
|
||||
|
||||
void RawDevice::listenAsync()
|
||||
{
|
||||
std::mutex listen_check;
|
||||
std::unique_lock<std::mutex> check_lock(listen_check);
|
||||
thread::spawn({[this]() { listen(); }});
|
||||
|
||||
// Block until RawDevice is listening
|
||||
_listen_condition.wait(check_lock, [this](){
|
||||
return (bool)_continue_listen;
|
||||
});
|
||||
}
|
||||
|
||||
void RawDevice::stopListener()
|
||||
{
|
||||
_continue_listen = false;
|
||||
interruptRead();
|
||||
}
|
||||
|
||||
void RawDevice::addEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<raw::RawEventHandler>& handler)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_event_handler_lock);
|
||||
assert(_event_handlers.find(nickname) == _event_handlers.end());
|
||||
assert(handler);
|
||||
_event_handlers.emplace(nickname, handler);
|
||||
}
|
||||
|
||||
void RawDevice::removeEventHandler(const std::string &nickname)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_event_handler_lock);
|
||||
_event_handlers.erase(nickname);
|
||||
}
|
||||
|
||||
const std::map<std::string, std::shared_ptr<raw::RawEventHandler>>&
|
||||
RawDevice::eventHandlers()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_event_handler_lock);
|
||||
return _event_handlers;
|
||||
}
|
||||
|
||||
void RawDevice::_handleEvent(std::vector<uint8_t> &report)
|
||||
void RawDevice::_handleEvent(const std::vector<uint8_t>& report)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_event_handler_lock);
|
||||
for(auto& handler : _event_handlers)
|
||||
if(handler.second->condition(report))
|
||||
handler.second->callback(report);
|
||||
}
|
||||
|
||||
bool RawDevice::isListening()
|
||||
{
|
||||
bool ret = _listening.try_lock();
|
||||
|
||||
if(ret)
|
||||
_listening.unlock();
|
||||
|
||||
return !ret;
|
||||
if(handler.condition(report))
|
||||
handler.callback(report);
|
||||
}
|
||||
|
@ -26,77 +26,62 @@
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <set>
|
||||
#include <list>
|
||||
|
||||
#include "defs.h"
|
||||
#include "../../util/mutex_queue.h"
|
||||
|
||||
namespace logid::backend::raw
|
||||
{
|
||||
class DeviceMonitor;
|
||||
class IOMonitor;
|
||||
|
||||
class RawDevice
|
||||
{
|
||||
public:
|
||||
static bool supportedReport(uint8_t id, uint8_t length);
|
||||
static constexpr int max_data_length = 32;
|
||||
typedef std::list<RawEventHandler>::const_iterator EvHandlerId;
|
||||
|
||||
explicit RawDevice(std::string path,
|
||||
double io_timeout);
|
||||
~RawDevice();
|
||||
std::string hidrawPath() const;
|
||||
struct dev_info {
|
||||
int16_t vid;
|
||||
int16_t pid;
|
||||
};
|
||||
|
||||
std::string name() const;
|
||||
uint16_t vendorId() const;
|
||||
uint16_t productId() const;
|
||||
RawDevice(std::string path,
|
||||
std::shared_ptr<DeviceMonitor> monitor);
|
||||
~RawDevice() noexcept;
|
||||
|
||||
[[nodiscard]] const std::string& rawPath() const;
|
||||
|
||||
[[nodiscard]] const std::string& name() const;
|
||||
[[nodiscard]] int16_t vendorId() const;
|
||||
[[nodiscard]] int16_t productId() const;
|
||||
|
||||
static std::vector<uint8_t> getReportDescriptor(std::string path);
|
||||
static std::vector<uint8_t> getReportDescriptor(int fd);
|
||||
std::vector<uint8_t> reportDescriptor() const;
|
||||
[[nodiscard]] const std::vector<uint8_t>& reportDescriptor() const;
|
||||
|
||||
std::vector<uint8_t> sendReport(const std::vector<uint8_t>& report);
|
||||
void sendReportNoResponse(const std::vector<uint8_t>& report);
|
||||
void interruptRead(bool wait_for_halt=true);
|
||||
void sendReport(const std::vector<uint8_t>& report);
|
||||
|
||||
void listen();
|
||||
void listenAsync();
|
||||
void stopListener();
|
||||
bool isListening();
|
||||
|
||||
void addEventHandler(const std::string& nickname,
|
||||
const std::shared_ptr<RawEventHandler>& handler);
|
||||
void removeEventHandler(const std::string& nickname);
|
||||
const std::map<std::string, std::shared_ptr<RawEventHandler>>&
|
||||
eventHandlers();
|
||||
EvHandlerId addEventHandler(RawEventHandler handler);
|
||||
void removeEventHandler(EvHandlerId id);
|
||||
|
||||
private:
|
||||
std::mutex _dev_io, _listening;
|
||||
std::string _path;
|
||||
int _fd;
|
||||
int _pipe[2];
|
||||
uint16_t _vid;
|
||||
uint16_t _pid;
|
||||
std::string _name;
|
||||
std::vector<uint8_t> _rdesc;
|
||||
void _readReports();
|
||||
|
||||
std::atomic<bool> _continue_listen;
|
||||
std::atomic<bool> _continue_respond;
|
||||
std::condition_variable _listen_condition;
|
||||
std::atomic_bool _valid;
|
||||
|
||||
const std::chrono::milliseconds _io_timeout;
|
||||
const std::string _path;
|
||||
const int _fd;
|
||||
const dev_info _devinfo;
|
||||
const std::string _name;
|
||||
const std::vector<uint8_t> _rdesc;
|
||||
|
||||
std::map<std::string, std::shared_ptr<RawEventHandler>>
|
||||
_event_handlers;
|
||||
std::shared_ptr<IOMonitor> _io_monitor;
|
||||
|
||||
std::list<RawEventHandler> _event_handlers;
|
||||
std::mutex _event_handler_lock;
|
||||
void _handleEvent(std::vector<uint8_t>& report);
|
||||
|
||||
/* These will only be used internally and processed with a queue */
|
||||
int _sendReport(const std::vector<uint8_t>& report);
|
||||
int _readReport(std::vector<uint8_t>& report, std::size_t maxDataLength);
|
||||
int _readReport(std::vector<uint8_t>& report, std::size_t maxDataLength,
|
||||
std::chrono::milliseconds timeout);
|
||||
|
||||
std::vector<uint8_t> _respondToReport(const std::vector<uint8_t>&
|
||||
request);
|
||||
|
||||
mutex_queue<std::shared_ptr<std::packaged_task<std::vector<uint8_t>()>>>
|
||||
_io_queue;
|
||||
void _handleEvent(const std::vector<uint8_t>& report);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,14 @@ namespace raw
|
||||
{
|
||||
struct RawEventHandler
|
||||
{
|
||||
std::function<bool(std::vector<uint8_t>& )> condition;
|
||||
std::function<void(std::vector<uint8_t>& )> callback;
|
||||
std::function<bool(const std::vector<uint8_t>&)> condition;
|
||||
std::function<void(const std::vector<uint8_t>&)> callback;
|
||||
|
||||
RawEventHandler(std::function<bool(const std::vector<uint8_t>&)> cond,
|
||||
std::function<void(const std::vector<uint8_t>&)> call) :
|
||||
condition (std::move(cond)), callback (std::move(call))
|
||||
{
|
||||
}
|
||||
};
|
||||
}}}
|
||||
|
||||
|
@ -57,7 +57,7 @@ void logid::reload()
|
||||
{
|
||||
log_printf(INFO, "Reloading logid...");
|
||||
finder_reloading.lock();
|
||||
finder->stop();
|
||||
finder->_stop();
|
||||
Configuration* old_config = global_config;
|
||||
global_config = new Configuration(config_file.c_str());
|
||||
delete(old_config);
|
||||
@ -157,19 +157,6 @@ int main(int argc, char** argv)
|
||||
std::shared_ptr<Configuration> config;
|
||||
std::shared_ptr<InputDevice> virtual_input;
|
||||
|
||||
auto server = ipcgull::make_server("pizza.pixl.LogiOps",
|
||||
"/pizza/pixl/LogiOps",
|
||||
ipcgull::IPCGULL_USER);
|
||||
|
||||
std::thread( [server]() {
|
||||
try {
|
||||
server->start();
|
||||
} catch(ipcgull::connection_failed& e) {
|
||||
logPrintf(ERROR, "Lost IPC connection, terminating.");
|
||||
std::terminate();
|
||||
}
|
||||
} ).detach();
|
||||
|
||||
// Read config
|
||||
try {
|
||||
config = std::make_shared<Configuration>(options.config_file);
|
||||
@ -178,6 +165,10 @@ int main(int argc, char** argv)
|
||||
config = std::make_shared<Configuration>();
|
||||
}
|
||||
|
||||
auto server = ipcgull::make_server("pizza.pixl.LogiOps",
|
||||
"/pizza/pixl/LogiOps",
|
||||
ipcgull::IPCGULL_USER);
|
||||
|
||||
//Create a virtual input device
|
||||
try {
|
||||
virtual_input = std::make_unique<InputDevice>(LOGID_VIRTUAL_INPUT_NAME);
|
||||
@ -186,12 +177,16 @@ int main(int argc, char** argv)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Scan devices, create listeners, handlers, etc.
|
||||
// Device manager runs on its own I/O thread asynchronously
|
||||
auto device_manager = DeviceManager::make(config, virtual_input, server);
|
||||
|
||||
device_manager->run();
|
||||
device_manager->enumerate();
|
||||
|
||||
server->stop_sync();
|
||||
try {
|
||||
server->start();
|
||||
} catch(ipcgull::connection_failed& e) {
|
||||
logPrintf(ERROR, "Lost IPC connection, terminating.");
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user