Use epoll for I/O multiplexing

This commit is contained in:
pixl 2022-01-28 15:48:00 -05:00
parent dbe24f9350
commit 7862c85d71
No known key found for this signature in database
GPG Key ID: 1866C148CD593B6E
29 changed files with 1056 additions and 843 deletions

View File

@ -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

View File

@ -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()

View File

@ -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);

View File

@ -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();

View File

@ -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";
}

View File

@ -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

View File

@ -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;

View File

@ -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>>

View File

@ -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

View File

@ -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;
};

View File

@ -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:

View File

@ -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

View File

@ -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)
{
std::lock_guard<std::mutex> lock(_send_lock);
{
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();
});
if(!valid)
throw TimeoutError();
{
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();
}
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);
}
auto raw_response = _raw_device->sendReport(report.rawReport());
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;
}
void Device::sendReportNoResponse(Report &report)
{
switch(report.type())
{
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);
}
_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();
}

View File

@ -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;
};

View File

@ -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);

View File

@ -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,13 +106,13 @@ 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; }

View File

@ -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>

View File

@ -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()};
}

View File

@ -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);
};

View File

@ -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;
}

View File

@ -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;
};
}}}

View File

@ -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");
}
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;
}

View File

@ -23,28 +23,40 @@
#include <mutex>
#include <atomic>
#include <memory>
#include "IOMonitor.h"
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;
};
}

View 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();
}
}

View 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

View File

@ -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");
return {name_buf, static_cast<size_t>(len)};
}
_continue_listen = false;
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())
{
_io_monitor->add(_fd, {
[this]() { _readReports(); },
[this]() { _valid = false; },
[this]() { _valid = false; }
});
}
RawDevice::~RawDevice()
{
if(_fd != -1)
RawDevice::~RawDevice() noexcept
{
_io_monitor->remove(_fd);
::close(_fd);
::close(_pipe[0]);
::close(_pipe[1]);
}
}
std::string RawDevice::hidrawPath() const
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");
"sendReport write failed");
}
return ret;
}
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);
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();
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);
std::unique_lock<std::mutex> lock(_event_handler_lock);
_event_handlers.erase(id);
}
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");
}
void RawDevice::_readReports()
{
uint8_t buf[max_data_length];
ssize_t len;
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;
_handleEvent(report);
}
}
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();
}
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);
}

View File

@ -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);
};
}

View File

@ -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))
{
}
};
}}}

View File

@ -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;
}