Implement receiver HID++ connect/disconnect events

Many changes were made here but that was the biggest one.

There's currently a bug where std::system_error: Broken pipe is thrown
after launching the daemon with a receiver connector.

A workaround for this bug is to simply shake the mouse while starting
the daemon. I will investigate this soon.
This commit is contained in:
pixl 2020-06-21 05:33:33 -04:00
parent b05e525bbc
commit e40da5f0c0
No known key found for this signature in database
GPG Key ID: 1866C148CD593B6E
24 changed files with 689 additions and 181 deletions

View File

@ -27,7 +27,9 @@ add_executable(logid
backend/hidpp20/Device.cpp backend/hidpp20/Device.cpp
backend/hidpp20/Error.cpp backend/hidpp20/Error.cpp
backend/hidpp20/Feature.cpp backend/hidpp20/Feature.cpp
backend/hidpp20/EssentialFeature.cpp
backend/hidpp20/features/Root.cpp backend/hidpp20/features/Root.cpp
backend/hidpp20/features/DeviceName.cpp
backend/dj/Report.cpp backend/dj/Report.cpp
util/mutex_queue.h) util/mutex_queue.h)

View File

@ -2,6 +2,7 @@
#include "Device.h" #include "Device.h"
using namespace logid; using namespace logid;
using namespace logid::backend;
Device::Device(std::string path, backend::hidpp::DeviceIndex index) : Device::Device(std::string path, backend::hidpp::DeviceIndex index) :
_hidpp20 (path, index), _path (path), _index (index) _hidpp20 (path, index), _path (path), _index (index)
@ -9,6 +10,13 @@ Device::Device(std::string path, backend::hidpp::DeviceIndex index) :
log_printf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index); log_printf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index);
} }
Device::Device(const std::shared_ptr<backend::raw::RawDevice>& raw_device,
hidpp::DeviceIndex index) : _hidpp20(raw_device, index), _path
(raw_device->hidrawPath()), _index (index)
{
log_printf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index);
}
void Device::sleep() void Device::sleep()
{ {
log_printf(INFO, "%s:%d fell asleep.", _path.c_str(), _index); log_printf(INFO, "%s:%d fell asleep.", _path.c_str(), _index);

View File

@ -14,6 +14,8 @@ namespace logid
{ {
public: public:
Device(std::string path, backend::hidpp::DeviceIndex index); Device(std::string path, backend::hidpp::DeviceIndex index);
Device(const std::shared_ptr<backend::raw::RawDevice>& raw_device,
backend::hidpp::DeviceIndex index);
void wakeup(); void wakeup();
void sleep(); void sleep();
private: private:

View File

@ -33,6 +33,7 @@ void DeviceManager::addDevice(std::string path)
if(isReceiver) { if(isReceiver) {
log_printf(INFO, "Detected receiver at %s", path.c_str()); log_printf(INFO, "Detected receiver at %s", path.c_str());
auto receiver = std::make_shared<Receiver>(path); auto receiver = std::make_shared<Receiver>(path);
receiver->run();
_receivers.emplace(path, receiver); _receivers.emplace(path, receiver);
} else { } else {
/* TODO: Error check? /* TODO: Error check?
@ -48,6 +49,9 @@ void DeviceManager::addDevice(std::string path)
} catch(hidpp10::Error &e) { } catch(hidpp10::Error &e) {
if(e.code() != hidpp10::Error::UnknownDevice) if(e.code() != hidpp10::Error::UnknownDevice)
throw; throw;
else
log_printf(WARN, "HID++ 1.0 error while trying to initialize"
" %s: %s", path.c_str(), e.what());
} catch(hidpp::Device::InvalidDevice &e) { // Ignore } catch(hidpp::Device::InvalidDevice &e) { // Ignore
} catch(std::system_error &e) { } catch(std::system_error &e) {
// This error should have been thrown previously // This error should have been thrown previously

View File

@ -1,9 +1,50 @@
#include <cassert>
#include "Receiver.h" #include "Receiver.h"
#include "util.h" #include "util.h"
#include "backend/hidpp10/Error.h"
#include "backend/hidpp20/Error.h"
using namespace logid; using namespace logid;
using namespace logid::backend;
Receiver::Receiver(std::string path) : _path (path) Receiver::Receiver(std::string path) : dj::ReceiverMonitor(path)
{ {
log_printf(DEBUG, "logid::Receiver created on %s", path.c_str()); log_printf(DEBUG, "logid::Receiver created on %s", path.c_str());
} }
void Receiver::addDevice(hidpp::DeviceConnectionEvent event)
{
try {
if(!event.linkEstablished)
return; // Device is probably asleep, wait until it wakes up
hidpp::Device hidpp_device(receiver(), event);
auto version = hidpp_device.version();
if(std::get<0>(version) < 2) {
log_printf(INFO, "Unsupported HID++ 1.0 device on %s:%d connected.",
_path.c_str(), event.index);
return;
}
std::shared_ptr<Device> device = std::make_shared<Device>(
receiver()->rawDevice(), event.index);
assert(_devices.find(event.index) == _devices.end());
_devices.emplace(event.index, device);
} catch(hidpp10::Error &e) {
log_printf(ERROR, "Caught HID++ 1.0 error while trying to initialize "
"%s:%d: %s", _path.c_str(), event.index, e.what());
} catch(hidpp20::Error &e) {
log_printf(ERROR, "Caught HID++ 2.0 error while trying to initialize "
"%s:%d: %s", _path.c_str(), event.index, e.what());
}
}
void Receiver::removeDevice(hidpp::DeviceIndex index)
{
_devices.erase(index);
}

View File

@ -2,14 +2,21 @@
#define LOGID_RECEIVER_H #define LOGID_RECEIVER_H
#include <string> #include <string>
#include "backend/dj/ReceiverMonitor.h"
#include "Device.h"
namespace logid namespace logid
{ {
class Receiver class Receiver : public backend::dj::ReceiverMonitor
{ {
public: public:
Receiver(std::string path); Receiver(std::string path);
protected:
virtual void addDevice(backend::hidpp::DeviceConnectionEvent event);
virtual void removeDevice(backend::hidpp::DeviceIndex index);
private: private:
std::map<backend::hidpp::DeviceIndex, std::shared_ptr<Device>> _devices;
std::string _path; std::string _path;
}; };
} }

View File

@ -32,47 +32,17 @@ Receiver::Receiver(std::string path) :
{ {
if(!supportsDjReports(raw_device->reportDescriptor())) if(!supportsDjReports(raw_device->reportDescriptor()))
throw InvalidReceiver(InvalidReceiver::NoDJReports); throw InvalidReceiver(InvalidReceiver::NoDJReports);
// Pass all HID++ events on DefaultDevice to handleHidppEvent
RawEventHandler hidppRawEventHandler;
hidppRawEventHandler.condition = [this](std::vector<uint8_t>& report)->bool
{
return (report[hidpp::Offset::Type] == hidpp::Report::Type::Short ||
report[hidpp::Offset::Type] == hidpp::Report::Type::Long) &&
(report[hidpp::Offset::DeviceIndex] == hidpp::DefaultDevice);
};
hidppRawEventHandler.callback = [this](std::vector<uint8_t>& report)->void
{
hidpp::Report _report(report);
this->handleHidppEvent(_report);
};
// Pass all DJ events with device index to handleHidppEvent
RawEventHandler djRawEventHandler;
djRawEventHandler.condition = [this](std::vector<uint8_t>& report)->bool
{
return (report[Offset::Type] == Report::Type::Short ||
report[Offset::Type] == Report::Type::Long);
};
djRawEventHandler.callback = [this](std::vector<uint8_t>& report)->void
{
Report _report(report);
this->handleDjEvent(_report);
};
raw_device->addEventHandler("RECV_HIDPP", hidppRawEventHandler);
raw_device->addEventHandler("RECV_DJ", djRawEventHandler);
} }
void Receiver::enumerate() void Receiver::enumerateDj()
{ {
sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{}); sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{});
} }
Receiver::notification_flags Receiver::getHidppNotifications() Receiver::notification_flags Receiver::getHidppNotifications()
{ {
auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {}); auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {},
hidpp::ReportType::Short);
notification_flags flags{}; notification_flags flags{};
flags.device_battery_status = response[0] & (1 << 4); flags.device_battery_status = response[0] & (1 << 4);
@ -89,17 +59,28 @@ void Receiver::enableHidppNotifications(notification_flags flags)
if(flags.device_battery_status) if(flags.device_battery_status)
request[0] |= (1 << 4); request[0] |= (1 << 4);
if(flags.receiver_wireless_notifications) if(flags.receiver_wireless_notifications)
request[1] |= (1 << 0); request[1] |= 1;
if(flags.receiver_software_present) if(flags.receiver_software_present)
request[1] |= (1 << 3); request[1] |= (1 << 3);
_hidpp10_device.setRegister(EnableHidppNotifications, request); _hidpp10_device.setRegister(EnableHidppNotifications, request,
hidpp::ReportType::Short);
}
void Receiver::enumerateHidpp()
{
/* This isn't in the documentation but this is how solaar does it
* All I know is that when (p0 & 2), devices are enumerated
*/
_hidpp10_device.setRegister(ConnectionState, {2},
hidpp::ReportType::Short);
} }
///TODO: Investigate usage ///TODO: Investigate usage
uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index) uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index)
{ {
auto response = _hidpp10_device.setRegister(ConnectionState, {index}); auto response = _hidpp10_device.getRegister(ConnectionState, {index},
hidpp::ReportType::Short);
return response[0]; return response[0];
} }
@ -113,7 +94,8 @@ void Receiver::startPairing(uint8_t timeout)
request[1] = hidpp::DefaultDevice; request[1] = hidpp::DefaultDevice;
request[2] = timeout; request[2] = timeout;
_hidpp10_device.setRegister(DevicePairing, request); _hidpp10_device.setRegister(DevicePairing, request,
hidpp::ReportType::Short);
} }
void Receiver::stopPairing() void Receiver::stopPairing()
@ -124,7 +106,8 @@ void Receiver::stopPairing()
request[0] = 2; request[0] = 2;
request[1] = hidpp::DefaultDevice; request[1] = hidpp::DefaultDevice;
_hidpp10_device.setRegister(DevicePairing, request); _hidpp10_device.setRegister(DevicePairing, request,
hidpp::ReportType::Short);
} }
void Receiver::disconnect(hidpp::DeviceIndex index) void Receiver::disconnect(hidpp::DeviceIndex index)
@ -135,12 +118,14 @@ void Receiver::disconnect(hidpp::DeviceIndex index)
request[0] = 3; request[0] = 3;
request[1] = index; request[1] = index;
_hidpp10_device.setRegister(DevicePairing, request); _hidpp10_device.setRegister(DevicePairing, request,
hidpp::ReportType::Short);
} }
std::map<hidpp::DeviceIndex, uint8_t> Receiver::getDeviceActivity() std::map<hidpp::DeviceIndex, uint8_t> Receiver::getDeviceActivity()
{ {
auto response = _hidpp10_device.getRegister(DeviceActivity, {}); auto response = _hidpp10_device.getRegister(DeviceActivity, {},
hidpp::ReportType::Long);
std::map<hidpp::DeviceIndex, uint8_t> device_activity; std::map<hidpp::DeviceIndex, uint8_t> device_activity;
for(uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++) for(uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++)
@ -156,7 +141,8 @@ struct Receiver::PairingInfo
request[0] = index; request[0] = index;
request[0] += 0x19; request[0] += 0x19;
auto response = _hidpp10_device.getRegister(PairingInfo, request); auto response = _hidpp10_device.getRegister(PairingInfo, request,
hidpp::ReportType::Long);
struct PairingInfo info{}; struct PairingInfo info{};
info.destinationId = response[0]; info.destinationId = response[0];
@ -175,7 +161,8 @@ struct Receiver::ExtendedPairingInfo
request[0] = index; request[0] = index;
request[0] += 0x29; request[0] += 0x29;
auto response = _hidpp10_device.getRegister(PairingInfo, request); auto response = _hidpp10_device.getRegister(PairingInfo, request,
hidpp::ReportType::Long);
ExtendedPairingInfo info{}; ExtendedPairingInfo info{};
@ -197,7 +184,8 @@ std::string Receiver::getDeviceName(hidpp::DeviceIndex index)
request[0] = index; request[0] = index;
request[0] += 0x39; request[0] += 0x39;
auto response = _hidpp10_device.getRegister(PairingInfo, request); auto response = _hidpp10_device.getRegister(PairingInfo, request,
hidpp::ReportType::Long);
uint8_t size = response[0]; uint8_t size = response[0];
assert(size <= 14); assert(size <= 14);
@ -215,12 +203,12 @@ hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(hidpp::Report& report)
return report.deviceIndex(); return report.deviceIndex();
} }
Receiver::DeviceConnectionEvent Receiver::deviceConnectionEvent( hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(
hidpp::Report &report) hidpp::Report &report)
{ {
assert(report.subId() == DeviceConnection); assert(report.subId() == DeviceConnection);
DeviceConnectionEvent event{}; hidpp::DeviceConnectionEvent event{};
event.index = report.deviceIndex(); event.index = report.deviceIndex();
event.unifying = ((report.paramBegin()[0] & 0b111) == 0x04); event.unifying = ((report.paramBegin()[0] & 0b111) == 0x04);
@ -240,23 +228,54 @@ Receiver::DeviceConnectionEvent Receiver::deviceConnectionEvent(
void Receiver::handleDjEvent(Report& report) void Receiver::handleDjEvent(Report& report)
{ {
if(report.feature() == DeviceConnection || for(auto& handler : dj_event_handlers)
report.feature() == DeviceDisconnection || if(handler.second->condition(report))
report.feature() == ConnectionStatus) handler.second->callback(report);
{
printf("%s DJ IN: ", raw_device->hidrawPath().c_str());
for(auto &i: report.rawData())
printf("%02x ", i);
printf("\n");
}
} }
void Receiver::handleHidppEvent(hidpp::Report &report) void Receiver::handleHidppEvent(hidpp::Report &report)
{ {
printf("%s HID++ IN: ", raw_device->hidrawPath().c_str()); for(auto& handler : hidpp_event_handlers)
for(auto &i: report.rawReport()) if(handler.second->condition(report))
printf("%02x ", i); handler.second->callback(report);
printf("\n"); }
void Receiver::addDjEventHandler(const std::string& nickname,
const std::shared_ptr<EventHandler>& handler)
{
auto it = dj_event_handlers.find(nickname);
assert(it == dj_event_handlers.end());
dj_event_handlers.emplace(nickname, handler);
}
void Receiver::removeDjEventHandler(const std::string &nickname)
{
dj_event_handlers.erase(nickname);
}
const std::map<std::string, std::shared_ptr<EventHandler>>&
Receiver::djEventHandlers()
{
return dj_event_handlers;
}
void Receiver::addHidppEventHandler(const std::string& nickname,
const std::shared_ptr<hidpp::EventHandler>& handler)
{
auto it = hidpp_event_handlers.find(nickname);
assert(it == hidpp_event_handlers.end());
hidpp_event_handlers.emplace(nickname, handler);
}
void Receiver::removeHidppEventHandler(const std::string &nickname)
{
hidpp_event_handlers.erase(nickname);
}
const std::map<std::string, std::shared_ptr<hidpp::EventHandler>>&
Receiver::hidppEventHandlers()
{
return hidpp_event_handlers;
} }
void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function, void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function,
@ -276,10 +295,56 @@ void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function,
void Receiver::listen() void Receiver::listen()
{ {
if(!raw_device->isListening())
std::thread{[=]() { raw_device->listen(); }}.detach(); std::thread{[=]() { raw_device->listen(); }}.detach();
if(raw_device->eventHandlers().find("RECV_HIDPP") ==
raw_device->eventHandlers().end()) {
// Pass all HID++ events on DefaultDevice to handleHidppEvent
std::shared_ptr<RawEventHandler> hidppRawEventHandler =
std::make_shared<RawEventHandler>();
hidppRawEventHandler->condition = [](std::vector<uint8_t>& report)->bool
{
return (report[hidpp::Offset::Type] == hidpp::Report::Type::Short ||
report[hidpp::Offset::Type] == hidpp::Report::Type::Long);
};
hidppRawEventHandler->callback = [this](std::vector<uint8_t>& report)->void
{
hidpp::Report _report(report);
this->handleHidppEvent(_report);
};
raw_device->addEventHandler("RECV_HIDPP", hidppRawEventHandler);
}
if(raw_device->eventHandlers().find("RECV_DJ") ==
raw_device->eventHandlers().end()) {
// Pass all DJ events with device index to handleHidppEvent
std::shared_ptr<RawEventHandler> djRawEventHandler =
std::make_shared<RawEventHandler>();
djRawEventHandler->condition = [](std::vector<uint8_t>& report)->bool
{
return (report[Offset::Type] == Report::Type::Short ||
report[Offset::Type] == Report::Type::Long);
};
djRawEventHandler->callback = [this](std::vector<uint8_t>& report)->void
{
Report _report(report);
this->handleDjEvent(_report);
};
raw_device->addEventHandler("RECV_DJ", djRawEventHandler);
}
} }
void Receiver::stopListening() void Receiver::stopListening()
{ {
raw_device->removeEventHandler("RECV_HIDPP");
raw_device->removeEventHandler("RECV_DJ");
if(raw_device->eventHandlers().empty())
raw_device->stopListener(); raw_device->stopListener();
} }
std::shared_ptr<raw::RawDevice> Receiver::rawDevice() const
{
return raw_device;
}

View File

@ -11,6 +11,12 @@ namespace logid {
namespace backend { namespace backend {
namespace dj namespace dj
{ {
struct EventHandler
{
std::function<bool(Report&)> condition;
std::function<void(Report&)> callback;
};
class InvalidReceiver : public std::exception class InvalidReceiver : public std::exception
{ {
public: public:
@ -43,7 +49,7 @@ namespace dj
GetPairedDevices = 0x81 GetPairedDevices = 0x81
}; };
void enumerate(); void enumerateDj();
/* The following functions deal with HID++ 1.0 features. /* The following functions deal with HID++ 1.0 features.
* While these are not technically DJ functions, it is redundant * While these are not technically DJ functions, it is redundant
@ -77,7 +83,7 @@ namespace dj
notification_flags getHidppNotifications(); notification_flags getHidppNotifications();
void enableHidppNotifications(notification_flags flags); void enableHidppNotifications(notification_flags flags);
///TODO: Understand output of this function void enumerateHidpp();
uint8_t getConnectionState(hidpp::DeviceIndex index); uint8_t getConnectionState(hidpp::DeviceIndex index);
void startPairing(uint8_t timeout = 0); void startPairing(uint8_t timeout = 0);
@ -107,35 +113,57 @@ namespace dj
std::string getDeviceName(hidpp::DeviceIndex index); std::string getDeviceName(hidpp::DeviceIndex index);
static hidpp::DeviceIndex deviceDisconnectionEvent(
hidpp::Report& report);
static hidpp::DeviceConnectionEvent deviceConnectionEvent(
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);
const std::map<std::string, std::shared_ptr<EventHandler>>&
djEventHandlers();
void addHidppEventHandler(const std::string& nickname,
const std::shared_ptr<hidpp::EventHandler>& handler);
void removeHidppEventHandler(const std::string& nickname);
const std::map<std::string, std::shared_ptr<hidpp::EventHandler>>&
hidppEventHandlers();
std::shared_ptr<raw::RawDevice> rawDevice() const;
private:
void sendDjRequest(hidpp::DeviceIndex index, uint8_t function,
const std::vector<uint8_t>&& params);
void handleDjEvent(dj::Report& report);
void handleHidppEvent(hidpp::Report& report);
std::map<std::string, std::shared_ptr<EventHandler>>
dj_event_handlers;
std::map<std::string, std::shared_ptr<hidpp::EventHandler>>
hidpp_event_handlers;
std::shared_ptr<raw::RawDevice> raw_device;
hidpp10::Device _hidpp10_device;
};
}
namespace hidpp
{
struct DeviceConnectionEvent struct DeviceConnectionEvent
{ {
hidpp::DeviceIndex index; hidpp::DeviceIndex index;
uint16_t pid; uint16_t pid;
DeviceType::DeviceType deviceType; dj::DeviceType::DeviceType deviceType;
bool unifying; bool unifying;
bool softwarePresent; bool softwarePresent;
bool encrypted; bool encrypted;
bool linkEstablished; bool linkEstablished;
bool withPayload; bool withPayload;
}; };
static hidpp::DeviceIndex deviceDisconnectionEvent(
hidpp::Report& report);
static DeviceConnectionEvent deviceConnectionEvent(
hidpp::Report& report);
void handleDjEvent(dj::Report& report);
void handleHidppEvent(hidpp::Report& report);
void listen();
void stopListening();
private:
void sendDjRequest(hidpp::DeviceIndex index, uint8_t function,
const std::vector<uint8_t>&& params);
std::shared_ptr<raw::RawDevice> raw_device;
hidpp10::Device _hidpp10_device;
};
}}} }}}
#endif //LOGID_BACKEND_DJ_RECEIVER_H #endif //LOGID_BACKEND_DJ_RECEIVER_H

View File

@ -1,30 +1,70 @@
#include "ReceiverMonitor.h" #include "ReceiverMonitor.h"
#include <utility> #include <utility>
#include <cassert>
using namespace logid::backend::dj; using namespace logid::backend::dj;
ReceiverMonitor::ReceiverMonitor(std::string path) : _reciever (std::move(path)) ReceiverMonitor::ReceiverMonitor(std::string path) : _receiver (
std::make_shared<Receiver>(std::move(path)))
{ {
assert(_receiver->hidppEventHandlers().find("RECVMON") ==
_receiver->hidppEventHandlers().end());
assert(_receiver->djEventHandlers().find("RECVMON") ==
_receiver->djEventHandlers().end());
Receiver::notification_flags notification_flags{ Receiver::notification_flags notification_flags{
true, true,
true, true,
true}; true};
_reciever.enableHidppNotifications(notification_flags); _receiver->enableHidppNotifications(notification_flags);
} }
void ReceiverMonitor::run() void ReceiverMonitor::run()
{ {
_reciever.listen(); _receiver->listen();
if(_receiver->hidppEventHandlers().find("RECVMON") ==
_receiver->hidppEventHandlers().end())
{
std::shared_ptr<hidpp::EventHandler> eventHandler =
std::make_shared<hidpp::EventHandler>();
eventHandler->condition = [](hidpp::Report &report) -> bool {
return (report.subId() == Receiver::DeviceConnection ||
report.subId() == Receiver::DeviceDisconnection);
};
eventHandler->callback = [this](hidpp::Report &report) -> void {
/* Running in a new thread prevents deadlocks since the
* receiver may be enumerating.
*/
std::thread{[this](hidpp::Report report) {
if (report.subId() == Receiver::DeviceConnection)
this->addDevice(this->_receiver->deviceConnectionEvent(
report));
else if (report.subId() == Receiver::DeviceDisconnection)
this->removeDevice(this->_receiver->
deviceDisconnectionEvent(report));
}, report}.detach();
};
_receiver->addHidppEventHandler("RECVMON", eventHandler);
}
enumerate(); enumerate();
} }
void ReceiverMonitor::stop() void ReceiverMonitor::stop()
{ {
_reciever.stopListening(); _receiver->stopListening();
} }
void ReceiverMonitor::enumerate() void ReceiverMonitor::enumerate()
{ {
_reciever.enumerate(); _receiver->enumerateHidpp();
}
std::shared_ptr<Receiver> ReceiverMonitor::receiver() const
{
return _receiver;
} }

View File

@ -21,7 +21,7 @@ namespace dj
void stop(); void stop();
protected: protected:
virtual void addDevice(hidpp::DeviceIndex index, uint16_t pid) = 0; virtual void addDevice(hidpp::DeviceConnectionEvent event) = 0;
virtual void removeDevice(hidpp::DeviceIndex index) = 0; virtual void removeDevice(hidpp::DeviceIndex index) = 0;
// Internal methods for derived class // Internal methods for derived class
@ -29,8 +29,10 @@ namespace dj
void _stopPairing(); void _stopPairing();
void _unpair(); void _unpair();
std::shared_ptr<Receiver> receiver() const;
private: private:
Receiver _reciever; std::shared_ptr<Receiver> _receiver;
}; };
}}} }}}

View File

@ -1,9 +1,12 @@
#include <assert.h> #include <cassert>
#include <utility>
#include "Device.h" #include "Device.h"
#include "Report.h" #include "Report.h"
#include "../hidpp20/features/Root.h" #include "../hidpp20/features/Root.h"
#include "../hidpp20/features/DeviceName.h"
#include "../hidpp20/Error.h" #include "../hidpp20/Error.h"
#include "../hidpp10/Error.h" #include "../hidpp10/Error.h"
#include "../dj/Receiver.h"
using namespace logid::backend; using namespace logid::backend;
using namespace logid::backend::hidpp; using namespace logid::backend::hidpp;
@ -16,6 +19,10 @@ const char* Device::InvalidDevice::what() const noexcept
return "Invalid HID++ device"; return "Invalid HID++ device";
case InvalidRawDevice: case InvalidRawDevice:
return "Invalid raw device"; return "Invalid raw device";
case Asleep:
return "Device asleep";
default:
return "Invalid device";
} }
} }
@ -26,36 +33,41 @@ Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept
/// TODO: Initialize a single RawDevice for each path. /// TODO: Initialize a single RawDevice for each path.
Device::Device(const std::string& path, DeviceIndex index): Device::Device(const std::string& path, DeviceIndex index):
raw_device (std::make_shared<raw::RawDevice>(path)), path (path), _raw_device (std::make_shared<raw::RawDevice>(path)), _receiver (nullptr),
_index (index) _path (path), _index (index)
{ {
_init(); _init();
} }
Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index) : Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index) :
raw_device (raw_device), _index (index) _raw_device (std::move(raw_device)), _receiver (nullptr),
_path (_raw_device->hidrawPath()), _index (index)
{ {
_init(); _init();
} }
Device::Device(std::shared_ptr<dj::Receiver> receiver,
hidpp::DeviceConnectionEvent event) :
_raw_device (receiver->rawDevice()), _index (event.index)
{
// Device will throw an error soon, just do it now
if(!event.linkEstablished)
throw InvalidDevice(InvalidDevice::Asleep);
_pid = event.pid;
_init();
}
void Device::_init() void Device::_init()
{ {
supported_reports = getSupportedReports(raw_device->reportDescriptor()); supported_reports = getSupportedReports(_raw_device->reportDescriptor());
if(!supported_reports) if(!supported_reports)
throw InvalidDevice(InvalidDevice::NoHIDPPReport); throw InvalidDevice(InvalidDevice::NoHIDPPReport);
try try {
{ hidpp20::EssentialRoot root(this);
Report versionRequest(Report::Type::Short, _index, _version = root.getVersion();
hidpp20::FeatureID::ROOT,hidpp20::Root::Ping, } catch(hidpp10::Error &e) {
LOGID_HIDPP_SOFTWARE_ID);
auto versionResponse = sendReport(versionRequest);
auto versionResponse_params = versionResponse.paramBegin();
_version = std::make_tuple(versionResponse_params[0], versionResponse_params[1]);
}
catch(hidpp10::Error &e)
{
// Valid HID++ 1.0 devices should send an InvalidSubID error // Valid HID++ 1.0 devices should send an InvalidSubID error
if(e.code() != hidpp10::Error::InvalidSubID) if(e.code() != hidpp10::Error::InvalidSubID)
throw; throw;
@ -64,29 +76,30 @@ void Device::_init()
_version = std::make_tuple(1, 0); _version = std::make_tuple(1, 0);
} }
// Pass all HID++ events with device index to this device. if(!_receiver) {
RawEventHandler rawEventHandler; _pid = _raw_device->productId();
rawEventHandler.condition = [this](std::vector<uint8_t>& report)->bool if(std::get<0>(_version) >= 2) {
{ try {
return (report[Offset::Type] == Report::Type::Short || hidpp20::EssentialDeviceName deviceName(this);
report[Offset::Type] == Report::Type::Long) && _name = deviceName.getName();
(report[Offset::DeviceIndex] == this->_index); } catch(hidpp20::UnsupportedFeature &e) {
}; _name = _raw_device->name();
rawEventHandler.callback = [this](std::vector<uint8_t>& report)->void }
{ } else {
Report _report(report); _name = _raw_device->name();
this->handleEvent(_report); }
}; } else {
_name = _receiver->getDeviceName(_index);
raw_device->addEventHandler("DEV_" + std::to_string(_index), rawEventHandler); }
} }
Device::~Device() Device::~Device()
{ {
raw_device->removeEventHandler("DEV_" + std::to_string(_index)); _raw_device->removeEventHandler("DEV_" + std::to_string(_index));
} }
void Device::addEventHandler(const std::string& nickname, const std::shared_ptr<EventHandler>& handler) void Device::addEventHandler(const std::string& nickname,
const std::shared_ptr<EventHandler>& handler)
{ {
auto it = event_handlers.find(nickname); auto it = event_handlers.find(nickname);
assert(it == event_handlers.end()); assert(it == event_handlers.end());
@ -119,7 +132,7 @@ Report Device::sendReport(Report& report)
assert(supported_reports & HIDPP_REPORT_LONG_SUPPORTED); assert(supported_reports & HIDPP_REPORT_LONG_SUPPORTED);
} }
auto raw_response = raw_device->sendReport(report.rawReport()); auto raw_response = _raw_device->sendReport(report.rawReport());
Report response(raw_response); Report response(raw_response);
@ -136,5 +149,30 @@ Report Device::sendReport(Report& report)
void Device::listen() void Device::listen()
{ {
raw_device->listen(); if(!_raw_device->isListening())
std::thread{[=]() { _raw_device->listen(); }}.detach();
// Pass all HID++ events with device index to this device.
std::shared_ptr<RawEventHandler> rawEventHandler;
rawEventHandler->condition = [this](std::vector<uint8_t>& report)->bool
{
return (report[Offset::Type] == Report::Type::Short ||
report[Offset::Type] == Report::Type::Long) &&
(report[Offset::DeviceIndex] == this->_index);
};
rawEventHandler->callback = [this](std::vector<uint8_t>& report)->void
{
Report _report(report);
this->handleEvent(_report);
};
_raw_device->addEventHandler("DEV_" + std::to_string(_index), rawEventHandler);
}
void Device::stopListening()
{
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
if(!_raw_device->eventHandlers().empty())
_raw_device->stopListener();
} }

View File

@ -11,8 +11,14 @@
namespace logid { namespace logid {
namespace backend { namespace backend {
namespace dj
{
// Need to define here for a constructor
class Receiver;
}
namespace hidpp namespace hidpp
{ {
struct DeviceConnectionEvent;
struct EventHandler struct EventHandler
{ {
std::function<bool(Report&)> condition; std::function<bool(Report&)> condition;
@ -27,7 +33,8 @@ namespace hidpp
enum Reason enum Reason
{ {
NoHIDPPReport, NoHIDPPReport,
InvalidRawDevice InvalidRawDevice,
Asleep
}; };
InvalidDevice(Reason reason) : _reason (reason) {} InvalidDevice(Reason reason) : _reason (reason) {}
virtual const char *what() const noexcept; virtual const char *what() const noexcept;
@ -36,18 +43,21 @@ namespace hidpp
Reason _reason; Reason _reason;
}; };
Device(const std::string& path, DeviceIndex index); explicit Device(const std::string& path, DeviceIndex index);
Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index); explicit Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index);
explicit Device(std::shared_ptr<dj::Receiver> receiver,
hidpp::DeviceConnectionEvent event);
~Device(); ~Device();
std::string devicePath() const { return path; } std::string devicePath() const { return _path; }
DeviceIndex deviceIndex() const { return _index; } DeviceIndex deviceIndex() const { return _index; }
std::tuple<uint8_t, uint8_t> version() const { return _version; } std::tuple<uint8_t, uint8_t> version() const { return _version; }
void listen(); // Runs asynchronously void listen(); // Runs asynchronously
void stopListening(); void stopListening();
void addEventHandler(const std::string& nickname, const std::shared_ptr<EventHandler>& handler); void addEventHandler(const std::string& nickname,
const std::shared_ptr<EventHandler>& handler);
void removeEventHandler(const std::string& nickname); void removeEventHandler(const std::string& nickname);
Report sendReport(Report& report); Report sendReport(Report& report);
@ -56,12 +66,15 @@ namespace hidpp
private: private:
void _init(); void _init();
std::shared_ptr<raw::RawDevice> raw_device; std::shared_ptr<raw::RawDevice> _raw_device;
std::string path; std::shared_ptr<dj::Receiver> _receiver;
std::string _path;
DeviceIndex _index; DeviceIndex _index;
uint8_t supported_reports; uint8_t supported_reports;
std::tuple<uint8_t, uint8_t> _version; std::tuple<uint8_t, uint8_t> _version;
uint16_t _pid;
std::string _name;
std::map<std::string, std::shared_ptr<EventHandler>> event_handlers; std::map<std::string, std::shared_ptr<EventHandler>> event_handlers;
}; };

View File

@ -180,6 +180,16 @@ void Report::setType(Report::Type type)
_data[Offset::Type] = type; _data[Offset::Type] = type;
} }
hidpp::DeviceIndex Report::deviceIndex()
{
return static_cast<hidpp::DeviceIndex>(_data[Offset::DeviceIndex]);
}
void Report::setDeviceIndex(hidpp::DeviceIndex index)
{
_data[Offset::DeviceIndex] = index;
}
uint8_t Report::feature() const uint8_t Report::feature() const
{ {
return _data[Offset::Feature]; return _data[Offset::Feature];

View File

@ -60,6 +60,9 @@ namespace logid::backend::hidpp
Report::Type type() const; Report::Type type() const;
void setType(Report::Type type); void setType(Report::Type type);
logid::backend::hidpp::DeviceIndex deviceIndex();
void setDeviceIndex(hidpp::DeviceIndex index);
uint8_t feature() const; uint8_t feature() const;
void setFeature(uint8_t feature); void setFeature(uint8_t feature);
@ -96,11 +99,6 @@ namespace logid::backend::hidpp
bool isError20(hidpp20_error* error); bool isError20(hidpp20_error* error);
logid::backend::hidpp::DeviceIndex deviceIndex()
{
return static_cast<DeviceIndex>(_data[Offset::DeviceIndex]);
}
std::vector<uint8_t> rawReport () const { return _data; } std::vector<uint8_t> rawReport () const { return _data; }
private: private:
static constexpr std::size_t HeaderLength = 4; static constexpr std::size_t HeaderLength = 4;

View File

@ -19,13 +19,10 @@ Device::Device(std::shared_ptr<raw::RawDevice> raw_dev,
} }
std::vector<uint8_t> Device::getRegister(uint8_t address, std::vector<uint8_t> Device::getRegister(uint8_t address,
const std::vector<uint8_t>& params) const std::vector<uint8_t>& params, hidpp::Report::Type type)
{ {
assert(params.size() <= hidpp::LongParamLength); assert(params.size() <= hidpp::LongParamLength);
hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ?
hidpp::Report::Type::Short : hidpp::Report::Type::Long;
uint8_t sub_id = type == hidpp::Report::Type::Short ? uint8_t sub_id = type == hidpp::Report::Type::Short ?
GetRegisterShort : GetRegisterLong; GetRegisterShort : GetRegisterLong;
@ -33,13 +30,11 @@ std::vector<uint8_t> Device::getRegister(uint8_t address,
} }
std::vector<uint8_t> Device::setRegister(uint8_t address, std::vector<uint8_t> Device::setRegister(uint8_t address,
const std::vector<uint8_t>& params) const std::vector<uint8_t>& params,
hidpp::Report::Type type)
{ {
assert(params.size() <= hidpp::LongParamLength); assert(params.size() <= hidpp::LongParamLength);
hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ?
hidpp::Report::Type::Short : hidpp::Report::Type::Long;
uint8_t sub_id = type == hidpp::Report::Type::Short ? uint8_t sub_id = type == hidpp::Report::Type::Short ?
SetRegisterShort : SetRegisterLong; SetRegisterShort : SetRegisterLong;

View File

@ -15,10 +15,10 @@ namespace hidpp10
hidpp::DeviceIndex index); hidpp::DeviceIndex index);
std::vector<uint8_t> getRegister(uint8_t address, std::vector<uint8_t> getRegister(uint8_t address,
const std::vector<uint8_t>& params); const std::vector<uint8_t>& params, hidpp::Report::Type type);
std::vector<uint8_t> setRegister(uint8_t address, std::vector<uint8_t> setRegister(uint8_t address,
const std::vector<uint8_t>& params); const std::vector<uint8_t>& params, hidpp::Report::Type type);
private: private:
std::vector<uint8_t> accessRegister(uint8_t sub_id, std::vector<uint8_t> accessRegister(uint8_t sub_id,
uint8_t address, const std::vector<uint8_t>& params); uint8_t address, const std::vector<uint8_t>& params);

View File

@ -0,0 +1,44 @@
#include <cassert>
#include "EssentialFeature.h"
#include "feature_defs.h"
#include "features/Root.h"
using namespace logid::backend::hidpp20;
std::vector<uint8_t> EssentialFeature::callFunction(uint8_t function_id,
std::vector<uint8_t>& params)
{
hidpp::Report::Type type;
assert(params.size() <= hidpp::LongParamLength);
if(params.size() <= hidpp::ShortParamLength)
type = hidpp::Report::Type::Short;
else if(params.size() <= hidpp::LongParamLength)
type = hidpp::Report::Type::Long;
hidpp::Report request(type, _device->deviceIndex(), _index, function_id,
LOGID_HIDPP_SOFTWARE_ID);
std::copy(params.begin(), params.end(), request.paramBegin());
auto response = _device->sendReport(request);
return std::vector<uint8_t>(response.paramBegin(), response.paramEnd());
}
EssentialFeature::EssentialFeature(hidpp::Device* dev, uint16_t _id) :
_device (dev)
{
_index = hidpp20::FeatureID::ROOT;
if(_id)
{
std::vector<uint8_t> getFunc_req(2);
getFunc_req[0] = (_id >> 8) & 0xff;
getFunc_req[1] = _id & 0xff;
auto getFunc_resp = this->callFunction(Root::GetFeature, getFunc_req);
_index = getFunc_resp[0];
// 0 if not found
if(!_index)
throw UnsupportedFeature(_id);
}
}

View File

@ -0,0 +1,32 @@
#ifndef LOGID_HIDPP20_ESSENTIAL_FEATURE_H
#define LOGID_HIDPP20_ESSENTIAL_FEATURE_H
// WARNING: UNSAFE
/* This class is only meant to provide essential HID++ 2.0 features to the
* hidpp::Device class. No version checks are provided here
*/
#include "Device.h"
namespace logid {
namespace backend {
namespace hidpp20
{
class EssentialFeature
{
public:
static const uint16_t ID;
virtual uint16_t getID() = 0;
protected:
EssentialFeature(hidpp::Device* dev, uint16_t _id);
std::vector<uint8_t> callFunction(uint8_t function_id,
std::vector<uint8_t>& params);
private:
hidpp::Device* _device;
uint8_t _index;
};
}}}
#endif //LOGID_HIDPP20_ESSENTIAL_FEATURE_H

View File

@ -0,0 +1,68 @@
#include <cmath>
#include "DeviceName.h"
using namespace logid::backend;
using namespace logid::backend::hidpp20;
DeviceName::DeviceName(Device* dev) : Feature(dev, ID)
{
}
uint8_t DeviceName::getNameLength()
{
std::vector<uint8_t> params(0);
auto response = this->callFunction(Function::GetLength, params);
return response[0];
}
std::string _getName(uint8_t length,
const std::function<std::vector<uint8_t>(std::vector<uint8_t>)>& fcall)
{
uint8_t function_calls = length/hidpp::LongParamLength;
if(length % hidpp::LongParamLength)
function_calls++;
std::vector<uint8_t> params(1);
std::string name;
for(uint8_t i = 0; i < function_calls; i++) {
params[0] = i*hidpp::LongParamLength;
auto name_section = fcall(params);
for(std::size_t j = 0; j < hidpp::LongParamLength; j++) {
if(params[0] + j >= length)
return name;
name += name_section[j];
}
}
return name;
}
std::string DeviceName::getName()
{
return _getName(getNameLength(), [this]
(std::vector<uint8_t> params)->std::vector<uint8_t> {
return this->callFunction(Function::GetDeviceName, params);
});
}
EssentialDeviceName::EssentialDeviceName(hidpp::Device* dev) :
EssentialFeature(dev, ID)
{
}
uint8_t EssentialDeviceName::getNameLength()
{
std::vector<uint8_t> params(0);
auto response = this->callFunction(DeviceName::Function::GetLength, params);
return response[0];
}
std::string EssentialDeviceName::getName()
{
return _getName(getNameLength(), [this]
(std::vector<uint8_t> params)->std::vector<uint8_t> {
return this->callFunction(DeviceName::Function::GetDeviceName, params);
});
}

View File

@ -0,0 +1,43 @@
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H
#define LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H
#include "../Feature.h"
#include "../feature_defs.h"
#include "../EssentialFeature.h"
namespace logid {
namespace backend {
namespace hidpp20
{
class DeviceName : public Feature
{
public:
static const uint16_t ID = FeatureID::DEVICE_NAME;
virtual uint16_t getID() { return ID; }
enum Function : uint8_t
{
GetLength = 0,
GetDeviceName = 1
};
DeviceName(Device* device);
uint8_t getNameLength();
std::string getName();
};
class EssentialDeviceName : public EssentialFeature
{
public:
static const uint16_t ID = FeatureID::DEVICE_NAME;
virtual uint16_t getID() { return ID; }
EssentialDeviceName(hidpp::Device* device);
uint8_t getNameLength();
std::string getName();
};
}}}
#endif //LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H

View File

@ -6,27 +6,36 @@ Root::Root(Device* dev) : Feature(dev, ID)
{ {
} }
feature_info Root::getFeature(uint16_t feature_id) std::vector<uint8_t> _genGetFeatureParams(uint16_t feature_id)
{ {
feature_info info{};
std::vector<uint8_t> params(2); std::vector<uint8_t> params(2);
params[0] = feature_id & 0xff; params[0] = feature_id & 0xff;
params[1] = (feature_id >> 8) & 0xff; params[1] = (feature_id >> 8) & 0xff;
return params;
}
auto response = this->callFunction(Function::GetFeature, params); feature_info _genGetFeatureInfo(uint16_t feature_id, std::vector<uint8_t> response)
{
feature_info info{};
info.feature_id = response[0]; info.feature_id = response[0];
if(!info.feature_id) if(!info.feature_id)
throw UnsupportedFeature(feature_id); throw UnsupportedFeature(feature_id);
info.hidden = response[1] & FeatureFlag::Hidden; info.hidden = response[1] & Root::FeatureFlag::Hidden;
info.obsolete = response[1] & FeatureFlag::Obsolete; info.obsolete = response[1] & Root::FeatureFlag::Obsolete;
info.internal = response[1] & FeatureFlag::Internal; info.internal = response[1] & Root::FeatureFlag::Internal;
return info; return info;
} }
feature_info Root::getFeature(uint16_t feature_id)
{
auto params = _genGetFeatureParams(feature_id);
auto response = this->callFunction(Function::GetFeature, params);
return _genGetFeatureInfo(feature_id, response);
}
std::tuple<uint8_t, uint8_t> Root::getVersion() std::tuple<uint8_t, uint8_t> Root::getVersion()
{ {
std::vector<uint8_t> params(0); std::vector<uint8_t> params(0);
@ -34,3 +43,22 @@ std::tuple<uint8_t, uint8_t> Root::getVersion()
return std::make_tuple(response[0], response[1]); return std::make_tuple(response[0], response[1]);
} }
EssentialRoot::EssentialRoot(hidpp::Device* dev) : EssentialFeature(dev, ID)
{
}
feature_info EssentialRoot::getFeature(uint16_t feature_id)
{
auto params = _genGetFeatureParams(feature_id);
auto response = this->callFunction(Root::Function::GetFeature, params);
return _genGetFeatureInfo(feature_id, response);
}
std::tuple<uint8_t, uint8_t> EssentialRoot::getVersion()
{
std::vector<uint8_t> params(0);
auto response = this->callFunction(Root::Function::Ping, params);
return std::make_tuple(response[0], response[1]);
}

View File

@ -2,6 +2,7 @@
#define LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H #define LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H
#include "../Feature.h" #include "../Feature.h"
#include "../EssentialFeature.h"
#include "../feature_defs.h" #include "../feature_defs.h"
namespace logid { namespace logid {
@ -24,7 +25,7 @@ namespace hidpp20
feature_info getFeature (uint16_t feature_id); feature_info getFeature (uint16_t feature_id);
std::tuple<uint8_t, uint8_t> getVersion(); std::tuple<uint8_t, uint8_t> getVersion();
private:
enum FeatureFlag : uint8_t enum FeatureFlag : uint8_t
{ {
Obsolete = 1<<7, Obsolete = 1<<7,
@ -32,6 +33,18 @@ namespace hidpp20
Internal = 1<<5 Internal = 1<<5
}; };
}; };
class EssentialRoot : public EssentialFeature
{
public:
static const uint16_t ID = FeatureID::ROOT;
virtual uint16_t getID() { return ID; }
EssentialRoot(hidpp::Device* device);
feature_info getFeature (uint16_t feature_id);
std::tuple<uint8_t, uint8_t> getVersion();
};
}}} }}}
#endif //LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H #endif //LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H

View File

@ -24,8 +24,8 @@ using namespace std::chrono;
bool RawDevice::supportedReportID(uint8_t id) bool RawDevice::supportedReportID(uint8_t id)
{ {
return (hidpp::ReportType::Short == id) || (hidpp::ReportType::Long == id) || return (hidpp::ReportType::Short == id) || (hidpp::ReportType::Long == id)
(dj::ReportType::Short == id) || (dj::ReportType::Long == id); || (dj::ReportType::Short == id) || (dj::ReportType::Long == id);
} }
RawDevice::RawDevice(std::string path) : path (path), continue_listen (false) RawDevice::RawDevice(std::string path) : path (path), continue_listen (false)
@ -34,14 +34,16 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false)
fd = ::open(path.c_str(), O_RDWR); fd = ::open(path.c_str(), O_RDWR);
if (fd == -1) if (fd == -1)
throw std::system_error(errno, std::system_category(), "RawDevice open failed"); throw std::system_error(errno, std::system_category(),
"RawDevice open failed");
hidraw_devinfo devinfo{}; hidraw_devinfo devinfo{};
if (-1 == ::ioctl(fd, HIDIOCGRAWINFO, &devinfo)) if (-1 == ::ioctl(fd, HIDIOCGRAWINFO, &devinfo))
{ {
int err = errno; int err = errno;
::close(fd); ::close(fd);
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWINFO failed"); throw std::system_error(err, std::system_category(),
"RawDevice HIDIOCGRAWINFO failed");
} }
vid = devinfo.vendor; vid = devinfo.vendor;
pid = devinfo.product; pid = devinfo.product;
@ -51,22 +53,25 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false)
{ {
int err = errno; int err = errno;
::close(fd); ::close(fd);
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWNAME failed"); throw std::system_error(err, std::system_category(),
"RawDevice HIDIOCGRAWNAME failed");
} }
name.assign(name_buf, ret - 1); _name.assign(name_buf, ret - 1);
hidraw_report_descriptor _rdesc{}; hidraw_report_descriptor _rdesc{};
if (-1 == ::ioctl(fd, HIDIOCGRDESCSIZE, &_rdesc.size)) if (-1 == ::ioctl(fd, HIDIOCGRDESCSIZE, &_rdesc.size))
{ {
int err = errno; int err = errno;
::close(fd); ::close(fd);
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESCSIZE failed"); throw std::system_error(err, std::system_category(),
"RawDevice HIDIOCGRDESCSIZE failed");
} }
if (-1 == ::ioctl(fd, HIDIOCGRDESC, &_rdesc)) if (-1 == ::ioctl(fd, HIDIOCGRDESC, &_rdesc))
{ {
int err = errno; int err = errno;
::close(fd); ::close(fd);
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESC failed"); throw std::system_error(err, std::system_category(),
"RawDevice HIDIOCGRDESC failed");
} }
rdesc = std::vector<uint8_t>(_rdesc.value, _rdesc.value + _rdesc.size); rdesc = std::vector<uint8_t>(_rdesc.value, _rdesc.value + _rdesc.size);
@ -74,7 +79,8 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false)
{ {
int err = errno; int err = errno;
close(fd); close(fd);
throw std::system_error(err, std::system_category(), "RawDevice pipe open failed"); throw std::system_error(err, std::system_category(),
"RawDevice pipe open failed");
} }
continue_listen = false; continue_listen = false;
@ -92,8 +98,6 @@ RawDevice::~RawDevice()
std::vector<uint8_t> RawDevice::sendReport(const std::vector<uint8_t>& report) std::vector<uint8_t> RawDevice::sendReport(const std::vector<uint8_t>& report)
{ {
assert(supportedReportID(report[0]));
/* If the listener will stop, handle I/O manually. /* If the listener will stop, handle I/O manually.
* Otherwise, push to queue and wait for result. */ * Otherwise, push to queue and wait for result. */
if(continue_listen) if(continue_listen)
@ -189,11 +193,6 @@ std::vector<uint8_t> RawDevice::_respondToReport
int RawDevice::_sendReport(const std::vector<uint8_t>& report) int RawDevice::_sendReport(const std::vector<uint8_t>& report)
{ {
std::lock_guard<std::mutex> lock(dev_io);
int ret = ::write(fd, report.data(), report.size());
if(ret == -1)
throw std::system_error(errno, std::system_category(), "_sendReport write failed");
if(logid::global_verbosity == LogLevel::RAWREPORT) { if(logid::global_verbosity == LogLevel::RAWREPORT) {
printf("[RAWREPORT] %s OUT: ", path.c_str()); printf("[RAWREPORT] %s OUT: ", path.c_str());
for(auto &i : report) for(auto &i : report)
@ -201,6 +200,14 @@ int RawDevice::_sendReport(const std::vector<uint8_t>& report)
printf("\n"); printf("\n");
} }
assert(supportedReportID(report[0]));
std::lock_guard<std::mutex> lock(dev_io);
int ret = ::write(fd, report.data(), report.size());
if(ret == -1)
throw std::system_error(errno, std::system_category(),
"_sendReport write failed");
return ret; return ret;
} }
@ -225,13 +232,15 @@ int RawDevice::_readReport(std::vector<uint8_t>& report, std::size_t maxDataLeng
} while(ret == -1 && errno == EINTR); } while(ret == -1 && errno == EINTR);
if(ret == -1) if(ret == -1)
throw std::system_error(errno, std::system_category(), "_readReport select failed"); throw std::system_error(errno, std::system_category(),
"_readReport select failed");
if(FD_ISSET(fd, &fds)) if(FD_ISSET(fd, &fds))
{ {
ret = read(fd, report.data(), report.size()); ret = read(fd, report.data(), report.size());
if(ret == -1) if(ret == -1)
throw std::system_error(errno, std::system_category(), "_readReport read failed"); throw std::system_error(errno, std::system_category(),
"_readReport read failed");
report.resize(ret); report.resize(ret);
} }
@ -240,7 +249,8 @@ int RawDevice::_readReport(std::vector<uint8_t>& report, std::size_t maxDataLeng
char c; char c;
ret = read(dev_pipe[0], &c, sizeof(char)); ret = read(dev_pipe[0], &c, sizeof(char));
if(ret == -1) if(ret == -1)
throw std::system_error(errno, std::system_category(), "_readReport read pipe failed"); throw std::system_error(errno, std::system_category(),
"_readReport read pipe failed");
} }
if(0 == ret) if(0 == ret)
@ -260,7 +270,8 @@ void RawDevice::interruptRead()
{ {
char c = 0; char c = 0;
if(-1 == write(dev_pipe[1], &c, sizeof(char))) if(-1 == write(dev_pipe[1], &c, sizeof(char)))
throw std::system_error(errno, std::system_category(), "interruptRead write pipe failed"); throw std::system_error(errno, std::system_category(),
"interruptRead write pipe failed");
// Ensure I/O has halted // Ensure I/O has halted
std::lock_guard<std::mutex> lock(dev_io); std::lock_guard<std::mutex> lock(dev_io);
@ -302,7 +313,8 @@ void RawDevice::stopListener()
interruptRead(); interruptRead();
} }
void RawDevice::addEventHandler(const std::string& nickname, RawEventHandler& handler) void RawDevice::addEventHandler(const std::string& nickname,
const std::shared_ptr<RawEventHandler>& handler)
{ {
auto it = event_handlers.find(nickname); auto it = event_handlers.find(nickname);
assert(it == event_handlers.end()); assert(it == event_handlers.end());
@ -314,11 +326,17 @@ void RawDevice::removeEventHandler(const std::string &nickname)
event_handlers.erase(nickname); event_handlers.erase(nickname);
} }
const std::map<std::string, std::shared_ptr<RawEventHandler>>&
RawDevice::eventHandlers()
{
return event_handlers;
}
void RawDevice::handleEvent(std::vector<uint8_t> &report) void RawDevice::handleEvent(std::vector<uint8_t> &report)
{ {
for(auto& handler : event_handlers) for(auto& handler : event_handlers)
if(handler.second.condition(report)) if(handler.second->condition(report))
handler.second.callback(report); handler.second->callback(report);
} }
bool RawDevice::isListening() bool RawDevice::isListening()
@ -328,5 +346,5 @@ bool RawDevice::isListening()
if(ret) if(ret)
listening.unlock(); listening.unlock();
return ret; return !ret;
} }

View File

@ -23,9 +23,14 @@ namespace raw
public: public:
static bool supportedReportID(uint8_t id); static bool supportedReportID(uint8_t id);
RawDevice(std::string path); explicit RawDevice(std::string path);
~RawDevice(); ~RawDevice();
std::string hidrawPath() const { return path; } std::string hidrawPath() const { return path; }
std::string name() const { return _name; }
uint16_t vendorId() const { return vid; }
uint16_t productId() const { return pid; }
std::vector<uint8_t> reportDescriptor() const { return rdesc; } std::vector<uint8_t> reportDescriptor() const { return rdesc; }
std::vector<uint8_t> sendReport(const std::vector<uint8_t>& report); std::vector<uint8_t> sendReport(const std::vector<uint8_t>& report);
@ -36,8 +41,11 @@ namespace raw
void stopListener(); void stopListener();
bool isListening(); bool isListening();
void addEventHandler(const std::string& nickname, RawEventHandler& handler); void addEventHandler(const std::string& nickname,
const std::shared_ptr<backend::RawEventHandler>& handler);
void removeEventHandler(const std::string& nickname); void removeEventHandler(const std::string& nickname);
const std::map<std::string, std::shared_ptr<backend::RawEventHandler>>&
eventHandlers();
private: private:
std::mutex dev_io, listening; std::mutex dev_io, listening;
@ -46,12 +54,13 @@ namespace raw
int dev_pipe[2]; int dev_pipe[2];
uint16_t vid; uint16_t vid;
uint16_t pid; uint16_t pid;
std::string name; std::string _name;
std::vector<uint8_t> rdesc; std::vector<uint8_t> rdesc;
std::atomic<bool> continue_listen; std::atomic<bool> continue_listen;
std::map<std::string, backend::RawEventHandler> event_handlers; std::map<std::string, std::shared_ptr<backend::RawEventHandler>>
event_handlers;
void handleEvent(std::vector<uint8_t>& report); void handleEvent(std::vector<uint8_t>& report);
/* These will only be used internally and processed with a queue */ /* These will only be used internally and processed with a queue */