logiops/src/logid/backend/dj/Receiver.cpp

349 lines
9.6 KiB
C++
Raw Normal View History

#include <cassert>
#include "Report.h"
#include "Receiver.h"
#include "Error.h"
using namespace logid::backend::dj;
using namespace logid::backend;
InvalidReceiver::InvalidReceiver(Reason reason) : _reason (reason)
{
}
const char* InvalidReceiver::what() const noexcept
{
switch(_reason) {
case NoDJReports:
return "No DJ reports";
default:
return "Invalid receiver";
}
}
InvalidReceiver::Reason InvalidReceiver::code() const noexcept
{
return _reason;
}
Receiver::Receiver(std::string path) :
_raw_device (std::make_shared<raw::RawDevice>(std::move(path))),
_hidpp10_device (_raw_device, hidpp::DefaultDevice)
{
if(!supportsDjReports(_raw_device->reportDescriptor()))
throw InvalidReceiver(InvalidReceiver::NoDJReports);
}
void Receiver::enumerateDj()
{
sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{});
}
Receiver::NotificationFlags Receiver::getHidppNotifications()
{
auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {},
hidpp::ReportType::Short);
NotificationFlags flags{};
flags.deviceBatteryStatus = response[0] & (1 << 4);
flags.receiverWirelessNotifications = response[1] & (1 << 0);
flags.receiverSoftwarePresent = response[1] & (1 << 3);
return flags;
}
void Receiver::enableHidppNotifications(NotificationFlags flags)
{
std::vector<uint8_t> request(3);
if(flags.deviceBatteryStatus)
request[0] |= (1 << 4);
if(flags.receiverWirelessNotifications)
request[1] |= 1;
if(flags.receiverSoftwarePresent)
request[1] |= (1 << 3);
_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
uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index)
{
auto response = _hidpp10_device.getRegister(ConnectionState, {index},
hidpp::ReportType::Short);
return response[0];
}
void Receiver::startPairing(uint8_t timeout)
{
///TODO: Device number == Device index?
std::vector<uint8_t> request(3);
request[0] = 1;
request[1] = hidpp::DefaultDevice;
request[2] = timeout;
_hidpp10_device.setRegister(DevicePairing, request,
hidpp::ReportType::Short);
}
void Receiver::stopPairing()
{
///TODO: Device number == Device index?
std::vector<uint8_t> request(3);
request[0] = 2;
request[1] = hidpp::DefaultDevice;
_hidpp10_device.setRegister(DevicePairing, request,
hidpp::ReportType::Short);
}
void Receiver::disconnect(hidpp::DeviceIndex index)
{
///TODO: Device number == Device index?
std::vector<uint8_t> request(3);
request[0] = 3;
request[1] = index;
_hidpp10_device.setRegister(DevicePairing, request,
hidpp::ReportType::Short);
}
std::map<hidpp::DeviceIndex, uint8_t> Receiver::getDeviceActivity()
{
auto response = _hidpp10_device.getRegister(DeviceActivity, {},
hidpp::ReportType::Long);
std::map<hidpp::DeviceIndex, uint8_t> device_activity;
for(uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++)
device_activity[static_cast<hidpp::DeviceIndex>(i)] = response[i];
return device_activity;
}
struct Receiver::PairingInfo
Receiver::getPairingInfo(hidpp::DeviceIndex index)
{
std::vector<uint8_t> request(1);
request[0] = index;
request[0] += 0x19;
auto response = _hidpp10_device.getRegister(PairingInfo, request,
hidpp::ReportType::Long);
struct PairingInfo info{};
info.destinationId = response[0];
info.reportInterval = response[1];
info.pid = response[2];
info.pid |= (response[3] << 8);
info.deviceType = static_cast<DeviceType::DeviceType>(response[6]);
return info;
}
struct Receiver::ExtendedPairingInfo
Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index)
{
std::vector<uint8_t> request(1);
request[0] = index;
request[0] += 0x29;
auto response = _hidpp10_device.getRegister(PairingInfo, request,
hidpp::ReportType::Long);
ExtendedPairingInfo info{};
info.serialNumber = 0;
for(uint8_t i = 0; i < 4; i++)
info.serialNumber |= (response[i] << 8*i);
for(uint8_t i = 0; i < 4; i++)
info.reportTypes[i] = response[i + 4];
info.powerSwitchLocation = response[8] & 0xf;
return info;
}
std::string Receiver::getDeviceName(hidpp::DeviceIndex index)
{
std::vector<uint8_t> request(1);
request[0] = index;
request[0] += 0x39;
auto response = _hidpp10_device.getRegister(PairingInfo, request,
hidpp::ReportType::Long);
uint8_t size = response[0];
assert(size <= 14);
std::string name(size, ' ');
for(std::size_t i = 0; i < size; i++)
name[i] = response[i + 1];
return name;
}
hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(hidpp::Report& report)
{
assert(report.subId() == DeviceDisconnection);
return report.deviceIndex();
}
hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(
hidpp::Report &report)
{
assert(report.subId() == DeviceConnection);
hidpp::DeviceConnectionEvent event{};
event.index = report.deviceIndex();
event.unifying = ((report.paramBegin()[0] & 0b111) == 0x04);
event.deviceType = static_cast<DeviceType::DeviceType>(
report.paramBegin()[1] & 0x0f);
event.softwarePresent = report.paramBegin()[1] & (1<<4);
event.encrypted = report.paramBegin()[1] & (1<<5);
event.linkEstablished = report.paramBegin()[1] & (1<<6);
event.withPayload = report.paramBegin()[1] & (1<<7);
event.pid = report.paramBegin()[3];
event.pid |= (report.paramBegin()[2] << 8);
return event;
}
void Receiver::handleDjEvent(Report& report)
{
for(auto& handler : _dj_event_handlers)
if(handler.second->condition(report))
handler.second->callback(report);
}
void Receiver::handleHidppEvent(hidpp::Report &report)
{
for(auto& handler : _hidpp_event_handlers)
if(handler.second->condition(report))
handler.second->callback(report);
}
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,
const std::vector<uint8_t>&& params)
{
assert(params.size() <= LongParamLength);
Report::Type type = params.size() <= ShortParamLength ?
ReportType::Short : ReportType::Long;
Report request(type, index, function);
std::copy(params.begin(), params.end(), request.paramBegin());
_raw_device->sendReportNoResponse(request.rawData());
}
void Receiver::listen()
{
if(!_raw_device->isListening())
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<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;
}