Implement dj::Receiver class

Again, many things were done here.
This commit is contained in:
pixl 2020-06-19 03:58:00 -04:00
parent 47db60fad8
commit b41649b0de
No known key found for this signature in database
GPG Key ID: 1866C148CD593B6E
18 changed files with 819 additions and 26 deletions

View File

@ -15,9 +15,12 @@ add_executable(logid
backend/Error.cpp backend/Error.cpp
backend/raw/DeviceMonitor.cpp backend/raw/DeviceMonitor.cpp
backend/raw/RawDevice.cpp backend/raw/RawDevice.cpp
backend/dj/Receiver.cpp
backend/dj/Error.cpp
backend/hidpp/Device.cpp backend/hidpp/Device.cpp
backend/hidpp/Report.cpp backend/hidpp/Report.cpp
backend/hidpp10/Error.cpp backend/hidpp10/Error.cpp
backend/hidpp10/Device.cpp
backend/hidpp20/Device.cpp backend/hidpp20/Device.cpp
backend/hidpp20/Error.cpp backend/hidpp20/Error.cpp
backend/hidpp20/Feature.cpp backend/hidpp20/Feature.cpp

View File

@ -4,6 +4,7 @@
#include "DeviceMonitor.h" #include "DeviceMonitor.h"
#include "util.h" #include "util.h"
#include "backend/hidpp10/Error.h" #include "backend/hidpp10/Error.h"
#include "backend/dj/Receiver.h"
#define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded" #define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded"
@ -103,12 +104,26 @@ void DeviceMonitor::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceI
void DeviceMonitor::addDevice(std::string path) void DeviceMonitor::addDevice(std::string path)
{ {
try { try {
auto device = std::make_shared<backend::hidpp::Device>(path, hidpp::DeviceIndex::WirelessDevice1); std::tuple<uint8_t, uint8_t> version;
log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); try
{
hidpp::Device device(path, hidpp::DefaultDevice);
auto version = device->version(); log_printf(DEBUG, "Detected HID++ device at %s", path.c_str());
log_printf(DEBUG, "HID++ version: %d.%d", std::get<0>(version), std::get<1>(version));
version = device.version();
log_printf(DEBUG, "HID++ version: %d.%d", std::get<0>(version), std::get<1>(version));
} catch(std::system_error &e) { }
if(version == std::make_tuple(1, 0))
{
// This is a receiver
dj::Receiver receiver(path);
receiver.enumerate();
receiver.listen();
while(true) {}
}
/*
auto eventHandler = std::make_shared<backend::hidpp::EventHandler>(); auto eventHandler = std::make_shared<backend::hidpp::EventHandler>();
eventHandler->condition = [device](backend::hidpp::Report& report)->bool eventHandler->condition = [device](backend::hidpp::Report& report)->bool
{ {
@ -125,9 +140,9 @@ void DeviceMonitor::addDevice(std::string path)
device->addEventHandler("MONITOR_ALL", eventHandler); device->addEventHandler("MONITOR_ALL", eventHandler);
devices.push_back(device); devices.push_back(device);*/
std::thread([device]() { device->listen(); }).detach(); //std::thread([device]() { device->listen(); }).detach();
} }
catch(hidpp10::Error &e) catch(hidpp10::Error &e)
{ {
@ -139,10 +154,11 @@ void DeviceMonitor::addDevice(std::string path)
{ {
log_printf(DEBUG, "Detected device at %s but %s", path.c_str(), e.what()); log_printf(DEBUG, "Detected device at %s but %s", path.c_str(), e.what());
} }
/*
catch(std::system_error &e) catch(std::system_error &e)
{ {
log_printf(WARN, "Failed to open %s: %s", path.c_str(), e.what()); log_printf(WARN, "Failed to open %s: %s", path.c_str(), e.what());
} } */
} }
void DeviceMonitor::removeDevice(std::string path) void DeviceMonitor::removeDevice(std::string path)

View File

@ -0,0 +1,21 @@
#include "Error.h"
using namespace logid::backend::dj;
Error::Error(uint8_t code) : _code (code)
{
}
const char* Error::what() const noexcept
{
switch(_code)
{
case Unknown:
return "Unknown";
case KeepAliveTimeout:
return "Keep-alive timeout";
default:
return std::string("Reserved error code " + std::to_string(_code))
.c_str();
}
}

View File

@ -0,0 +1,30 @@
#ifndef LOGID_HIDPP_BACKEND_DJ_ERROR_H
#define LOGID_HIDPP_BACKEND_DJ_ERROR_H
#include <cstdint>
#include <stdexcept>
namespace logid {
namespace backend {
namespace dj
{
class Error : public std::exception
{
public:
enum ErrorCode : uint8_t
{
Unknown = 0x00,
KeepAliveTimeout = 0x01
};
Error(uint8_t code);
virtual const char* what() const noexcept;
uint8_t code() const noexcept;
private:
uint8_t _code;
};
}}}
#endif //LOGID_HIDPP_BACKEND_DJ_ERROR_H

View File

@ -0,0 +1,255 @@
#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
{
return "Invalid receiver";
}
InvalidReceiver::Reason InvalidReceiver::code() const noexcept
{
return _reason;
}
Receiver::Receiver(std::string path) :
raw_device (std::make_shared<raw::RawDevice>(path)),
_hidpp10_device (raw_device, hidpp::DefaultDevice)
{
if(!supportsDjReports(raw_device->reportDescriptor()))
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()
{
sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{});
}
Receiver::notification_flags Receiver::getHidppNotifications()
{
auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {});
notification_flags flags{};
flags.device_battery_status = response[0] & (1 << 4);
flags.receiver_wireless_notifications = response[1] & (1 << 0);
flags.receiver_software_present = response[1] & (1 << 3);
}
void Receiver::enableHidppNotifications(notification_flags flags)
{
std::vector<uint8_t> request(3);
if(flags.device_battery_status)
request[0] |= (1 << 4);
if(flags.receiver_wireless_notifications)
request[1] |= (1 << 0);
if(flags.receiver_software_present)
request[1] |= (1 << 3);
_hidpp10_device.setRegister(EnableHidppNotifications, request);
}
///TODO: Investigate usage
uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index)
{
auto response = _hidpp10_device.setRegister(ConnectionState, {index});
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);
}
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);
}
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);
}
std::map<hidpp::DeviceIndex, uint8_t> Receiver::getDeviceActivity()
{
auto response = _hidpp10_device.getRegister(DeviceActivity, {});
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;
}
Receiver::pairing_info Receiver::getPairingInfo(hidpp::DeviceIndex index)
{
std::vector<uint8_t> request(1);
request[0] = index;
request[0] += 0x19;
auto response = _hidpp10_device.getRegister(PairingInfo, request);
pairing_info info{};
info.destination_id = response[0];
info.report_interval = response[1];
info.pid = response[2];
info.pid |= (response[3] << 8);
info.device_type = response[6];
return info;
}
Receiver::extended_pairing_info
Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index)
{
std::vector<uint8_t> request(1);
request[0] = index;
request[0] += 0x29;
auto response = _hidpp10_device.getRegister(PairingInfo, request);
extended_pairing_info info{};
info.serial_number = 0;
for(uint8_t i = 0; i < 4; i++)
info.serial_number |= (response[i] << 8*i);
for(uint8_t i = 0; i < 4; i++)
info.report_types[i] = response[i + 4];
info.power_switch_location = 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);
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::deviceConnectionEvent(hidpp::Report &report)
{
assert(report.subId() == DeviceConnection);
return report.deviceIndex();
}
hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(hidpp::Report& report)
{
assert(report.subId() == DeviceDisconnection);
return report.deviceIndex();
}
void Receiver::handleDjEvent(Report& report)
{
if(report.feature() == DeviceConnection ||
report.feature() == DeviceDisconnection ||
report.feature() == ConnectionStatus)
{
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)
{
printf("%s HID++ IN: ", raw_device->hidrawPath().c_str());
for(auto &i: report.rawReport())
printf("%02x ", i);
printf("\n");
}
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()
{
std::thread{[=]() { raw_device->listen(); }}.detach();
}

View File

@ -0,0 +1,125 @@
#ifndef LOGID_BACKEND_DJ_RECEIVER_H
#define LOGID_BACKEND_DJ_RECEIVER_H
#include <cstdint>
#include "../raw/RawDevice.h"
#include "Report.h"
#include "../hidpp/Report.h"
#include "../hidpp10/Device.h"
namespace logid {
namespace backend {
namespace dj
{
class InvalidReceiver : public std::exception
{
public:
enum Reason
{
NoDJReports
};
explicit InvalidReceiver(Reason reason);
virtual const char* what() const noexcept;
Reason code() const noexcept;
private:
Reason _reason;
};
class Receiver
{
public:
explicit Receiver(std::string path);
enum DjEvents : uint8_t
{
DeviceDisconnection = 0x40,
DeviceConnection = 0x41,
ConnectionStatus = 0x42
};
enum DjCommands : uint8_t
{
SwitchAndKeepAlive = 0x80,
GetPairedDevices = 0x81
};
void enumerate();
/* The following functions deal with HID++ 1.0 features.
* While these are not technically DJ functions, it is redundant
* to have a separate hidpp10::Receiver class for these functions.
*/
enum HidppEvents : uint8_t
{
// These events are identical to their DJ counterparts
// DeviceDisconnection = 0x40,
// DeviceConnection = 0x41,
LockingChange = 0x4a
};
enum HidppRegisters : uint8_t
{
EnableHidppNotifications = 0x00,
ConnectionState = 0x02,
DevicePairing = 0xb2,
DeviceActivity = 0xb3,
PairingInfo = 0xb5
};
struct notification_flags
{
bool device_battery_status;
bool receiver_wireless_notifications;
bool receiver_software_present;
};
notification_flags getHidppNotifications();
void enableHidppNotifications(notification_flags flags);
///TODO: Understand output of this function
uint8_t getConnectionState(hidpp::DeviceIndex index);
void startPairing(uint8_t timeout = 0);
void stopPairing();
void disconnect(hidpp::DeviceIndex index);
std::map<hidpp::DeviceIndex, uint8_t> getDeviceActivity();
struct pairing_info
{
uint8_t destination_id;
uint8_t report_interval;
uint16_t pid;
uint8_t device_type; ///TODO: Create enum for DeviceType
};
struct extended_pairing_info
{
uint32_t serial_number;
uint8_t report_types[4];
uint8_t power_switch_location; ///TODO: Create enum
};
pairing_info getPairingInfo(hidpp::DeviceIndex index);
extended_pairing_info getExtendedPairingInfo(hidpp::DeviceIndex index);
std::string getDeviceName(hidpp::DeviceIndex index);
hidpp::DeviceIndex deviceConnectionEvent(hidpp::Report& report);
hidpp::DeviceIndex deviceDisconnectionEvent(hidpp::Report& report);
void handleDjEvent(dj::Report& report);
void handleHidppEvent(hidpp::Report& report);
void listen();
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

View File

@ -1,11 +1,12 @@
#include <array> #include <array>
#include <algorithm> #include <algorithm>
#include <cassert>
#include "Report.h" #include "Report.h"
using namespace logid::backend::dj; using namespace logid::backend::dj;
using namespace logid::backend; using namespace logid::backend;
static const std::array<uint8_t, 35> DJReportDesc = { static const std::array<uint8_t, 34> DJReportDesc = {
0xA1, 0x01, // Collection (Application) 0xA1, 0x01, // Collection (Application)
0x85, 0x20, // Report ID (32) 0x85, 0x20, // Report ID (32)
0x95, 0x0E, // Report Count (14) 0x95, 0x0E, // Report Count (14)
@ -25,8 +26,68 @@ static const std::array<uint8_t, 35> DJReportDesc = {
0xC0 // End Collection 0xC0 // End Collection
}; };
bool dj::supportsDjReports(std::vector<uint8_t>& rdesc) bool dj::supportsDjReports(std::vector<uint8_t>&& rdesc)
{ {
auto it = std::search(rdesc.begin(), rdesc.end(), DJReportDesc.begin(), DJReportDesc.end()); auto it = std::search(rdesc.begin(), rdesc.end(), DJReportDesc.begin(), DJReportDesc.end());
return it != rdesc.end(); return it != rdesc.end();
} }
Report::Report(std::vector<uint8_t>& data) : _data (data)
{
switch(data[Offset::Type])
{
case ReportType::Short:
_data.resize(HeaderLength+ShortParamLength);
break;
case ReportType::Long:
_data.resize(HeaderLength+LongParamLength);
break;
default:
assert(false);
}
}
Report::Report(Report::Type type, hidpp::DeviceIndex index, uint8_t feature)
{
switch(type)
{
case ReportType::Short:
_data.resize(HeaderLength+ShortParamLength);
break;
case ReportType::Long:
_data.resize(HeaderLength+LongParamLength);
break;
default:
assert(false);
}
_data[Offset::Type] = type;
_data[Offset::DeviceIndex] = index;
_data[Offset::Feature] = feature;
}
Report::Type Report::type() const
{
return static_cast<Type>(_data[Offset::Type]);
}
hidpp::DeviceIndex Report::index() const
{
return static_cast<hidpp::DeviceIndex>(_data[Offset::DeviceIndex]);
}
uint8_t Report::feature() const
{
return _data[Offset::Feature];
}
std::vector<uint8_t>::iterator Report::paramBegin()
{
return _data.begin() + Offset::Parameters;
}
std::vector<uint8_t> Report::rawData() const
{
return _data;
}

View File

@ -3,17 +3,35 @@
#include <cstdint> #include <cstdint>
#include "../raw/RawDevice.h" #include "../raw/RawDevice.h"
#include "defs.h"
#include "../hidpp/defs.h"
namespace logid::backend::dj namespace logid::backend::dj
{ {
bool supportsDjReports(std::vector<uint8_t>& rawDevice); namespace Offset
{
static constexpr uint8_t Type = 0;
static constexpr uint8_t DeviceIndex = 1;
static constexpr uint8_t Feature = 2;
static constexpr uint8_t Parameters = 3;
}
bool supportsDjReports(std::vector<uint8_t>&& rdesc);
class Report class Report
{ {
enum Type: uint8_t public:
{ typedef ReportType::ReportType Type;
Short = 0x20, // Short DJ reports use 12 byte parameters
Long = 0x21 // Long DJ reports use 29 byte parameters explicit Report(std::vector<uint8_t>& data);
}; Report(Type type, hidpp::DeviceIndex index, uint8_t feature);
Type type() const;
hidpp::DeviceIndex index() const;
uint8_t feature() const;
std::vector<uint8_t>::iterator paramBegin();
std::vector<uint8_t> rawData() const;
private:
std::vector<uint8_t> _data;
}; };
} }

View File

@ -15,6 +15,12 @@ namespace dj
Long = 0x21 Long = 0x21
}; };
} }
static constexpr uint8_t ErrorFeature = 0x7f;
static constexpr std::size_t HeaderLength = 3;
static constexpr std::size_t ShortParamLength = 12;
static constexpr std::size_t LongParamLength = 29;
}}} }}}
#endif //LOGID_BACKEND_DJ_DEFS_H #endif //LOGID_BACKEND_DJ_DEFS_H

View File

@ -26,7 +26,19 @@ 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), index (index) raw_device (std::make_shared<raw::RawDevice>(path)), path (path),
_index (index)
{
_init();
}
Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index) :
raw_device (raw_device), _index (index)
{
_init();
}
void Device::_init()
{ {
supported_reports = getSupportedReports(raw_device->reportDescriptor()); supported_reports = getSupportedReports(raw_device->reportDescriptor());
if(!supported_reports) if(!supported_reports)
@ -34,8 +46,9 @@ Device::Device(const std::string& path, DeviceIndex index):
try try
{ {
Report versionRequest(Report::Type::Short, index, hidpp20::FeatureID::ROOT, Report versionRequest(Report::Type::Short, _index,
hidpp20::Root::Ping, LOGID_HIDPP_SOFTWARE_ID); hidpp20::FeatureID::ROOT,hidpp20::Root::Ping,
LOGID_HIDPP_SOFTWARE_ID);
auto versionResponse = sendReport(versionRequest); auto versionResponse = sendReport(versionRequest);
auto versionResponse_params = versionResponse.paramBegin(); auto versionResponse_params = versionResponse.paramBegin();
@ -53,10 +66,11 @@ Device::Device(const std::string& path, DeviceIndex index):
// Pass all HID++ events with device index to this device. // Pass all HID++ events with device index to this device.
RawEventHandler rawEventHandler; RawEventHandler rawEventHandler;
rawEventHandler.condition = [index](std::vector<uint8_t>& report)->bool rawEventHandler.condition = [this](std::vector<uint8_t>& report)->bool
{ {
return (report[Offset::Type] == Report::Type::Short || return (report[Offset::Type] == Report::Type::Short ||
report[Offset::Type] == Report::Type::Long) && (report[Offset::DeviceIndex] == index); report[Offset::Type] == Report::Type::Long) &&
(report[Offset::DeviceIndex] == this->_index);
}; };
rawEventHandler.callback = [this](std::vector<uint8_t>& report)->void rawEventHandler.callback = [this](std::vector<uint8_t>& report)->void
{ {
@ -64,7 +78,15 @@ Device::Device(const std::string& path, DeviceIndex index):
this->handleEvent(_report); this->handleEvent(_report);
}; };
raw_device->addEventHandler("DEV_" + std::to_string(index), rawEventHandler); raw_device->addEventHandler("DEV_" + std::to_string(_index), rawEventHandler);
}
Device::~Device()
{
raw_device->removeEventHandler("DEV_" + std::to_string(_index));
///TODO: tmp
raw_device->stopListener();
raw_device.reset();
} }
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)

View File

@ -37,9 +37,11 @@ namespace hidpp
}; };
Device(const std::string& path, DeviceIndex index); Device(const std::string& path, DeviceIndex index);
Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index);
~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
@ -52,9 +54,11 @@ namespace hidpp
void handleEvent(Report& report); void handleEvent(Report& report);
private: private:
void _init();
std::shared_ptr<raw::RawDevice> raw_device; std::shared_ptr<raw::RawDevice> raw_device;
std::string path; 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;

View File

@ -95,6 +95,27 @@ const char *Report::InvalidReportLength::what() const noexcept
return "Invalid report length"; return "Invalid report length";
} }
Report::Report(Report::Type type, DeviceIndex device_index,
uint8_t sub_id, uint8_t address)
{
switch(type)
{
case Type::Short:
_data.resize(HeaderLength + ShortParamLength);
break;
case Type::Long:
_data.resize(HeaderLength + LongParamLength);
break;
default:
throw InvalidReportID();
}
_data[Offset::Type] = type;
_data[Offset::DeviceIndex] = device_index;
_data[Offset::SubID] = sub_id;
_data[Offset::Address] = address;
}
Report::Report(Report::Type type, DeviceIndex device_index, Report::Report(Report::Type type, DeviceIndex device_index,
uint8_t feature_index, uint8_t function, uint8_t sw_id) uint8_t feature_index, uint8_t function, uint8_t sw_id)
{ {
@ -123,6 +144,7 @@ Report::Report(const std::vector<uint8_t>& data)
{ {
_data = data; _data = data;
// Truncating data is entirely valid here.
switch(_data[Offset::Type]) switch(_data[Offset::Type])
{ {
case Type::Short: case Type::Short:
@ -136,6 +158,11 @@ Report::Report(const std::vector<uint8_t>& data)
} }
} }
Report::Type Report::type() const
{
return static_cast<Report::Type>(_data[Offset::Type]);
}
void Report::setType(Report::Type type) void Report::setType(Report::Type type)
{ {
switch(type) switch(type)
@ -153,6 +180,58 @@ void Report::setType(Report::Type type)
_data[Offset::Type] = type; _data[Offset::Type] = type;
} }
uint8_t Report::feature() const
{
return _data[Offset::Feature];
}
void Report::setFeature(uint8_t feature)
{
_data[Offset::Parameters] = feature;
}
uint8_t Report::subId() const
{
return _data[Offset::SubID];
}
void Report::setSubId(uint8_t sub_id)
{
_data[Offset::SubID] = sub_id;
}
uint8_t Report::function() const
{
return (_data[Offset::Function] >> 4) & 0x0f;
}
void Report::setFunction(uint8_t function)
{
_data[Offset::Function] &= 0x0f;
_data[Offset::Function] |= (function << 4) & 0x0f;
}
uint8_t Report::swId() const
{
return _data[Offset::Function] & 0x0f;
}
void Report::setSwId(uint8_t sub_id)
{
_data[Offset::Function] &= 0x0f;
_data[Offset::Function] |= sub_id & 0x0f;
}
uint8_t Report::address() const
{
return _data[Offset::Address];
}
void Report::setAddress(uint8_t address)
{
_data[Offset::Address] = address;
}
void Report::setParams(const std::vector<uint8_t>& _params) void Report::setParams(const std::vector<uint8_t>& _params)
{ {
assert(_params.size() <= _data.size()-HeaderLength); assert(_params.size() <= _data.size()-HeaderLength);

View File

@ -20,6 +20,7 @@ namespace logid::backend::hidpp
static constexpr uint8_t DeviceIndex = 1; static constexpr uint8_t DeviceIndex = 1;
static constexpr uint8_t SubID = 2; static constexpr uint8_t SubID = 2;
static constexpr uint8_t Feature = 2; static constexpr uint8_t Feature = 2;
static constexpr uint8_t Address = 3;
static constexpr uint8_t Function = 3; static constexpr uint8_t Function = 3;
static constexpr uint8_t Parameters = 4; static constexpr uint8_t Parameters = 4;
} }
@ -47,16 +48,37 @@ namespace logid::backend::hidpp
static constexpr uint8_t swIdMask = 0x0f; static constexpr uint8_t swIdMask = 0x0f;
static constexpr uint8_t functionMask = 0x0f; static constexpr uint8_t functionMask = 0x0f;
Report(Report::Type type, DeviceIndex device_index,
uint8_t sub_id,
uint8_t address);
Report(Report::Type type, DeviceIndex device_index, Report(Report::Type type, DeviceIndex device_index,
uint8_t feature_index, uint8_t feature_index,
uint8_t function, uint8_t function,
uint8_t sw_id); uint8_t sw_id);
explicit Report(const std::vector<uint8_t>& data); explicit Report(const std::vector<uint8_t>& data);
Report::Type type() const { return static_cast<Report::Type>(_data[Offset::Type]); }; Report::Type type() const;
void setType(Report::Type type); void setType(Report::Type type);
std::vector<uint8_t>::iterator paramBegin() { return _data.begin() + Offset::Parameters; } uint8_t feature() const;
void setFeature(uint8_t feature);
uint8_t subId() const;
void setSubId(uint8_t sub_id);
uint8_t function() const;
void setFunction(uint8_t function);
uint8_t swId() const;
void setSwId(uint8_t sw_id);
uint8_t address() const;
void setAddress(uint8_t address);
std::vector<uint8_t>::iterator paramBegin()
{
return _data.begin() + Offset::Parameters;
}
std::vector<uint8_t>::iterator paramEnd() { return _data.end(); } std::vector<uint8_t>::iterator paramEnd() { return _data.end(); }
void setParams(const std::vector<uint8_t>& _params); void setParams(const std::vector<uint8_t>& _params);

View File

@ -0,0 +1,62 @@
#include <cassert>
#include <utility>
#include "Device.h"
#include "defs.h"
using namespace logid::backend;
using namespace logid::backend::hidpp10;
Device::Device(const std::string &path, hidpp::DeviceIndex index) :
hidpp::Device(path, index)
{
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)
{
assert(version() == std::make_tuple(1, 0));
}
std::vector<uint8_t> Device::getRegister(uint8_t address,
const std::vector<uint8_t>& params)
{
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 ?
GetRegisterShort : GetRegisterLong;
return accessRegister(sub_id, address, params);
}
std::vector<uint8_t> Device::setRegister(uint8_t address,
const std::vector<uint8_t>& params)
{
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 ?
SetRegisterShort : SetRegisterLong;
return accessRegister(sub_id, address, params);
}
std::vector<uint8_t> Device::accessRegister(uint8_t sub_id, uint8_t address, const std::vector<uint8_t> &params)
{
hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ?
hidpp::Report::Type::Short : hidpp::Report::Type::Long;
hidpp::Report request(type, deviceIndex(), sub_id, address);
std::copy(params.begin(), params.end(), request.paramBegin());
auto response = sendReport(request);
return std::vector<uint8_t>(response.paramBegin(), response.paramEnd());
}

View File

@ -0,0 +1,28 @@
#ifndef LOGID_BACKEND_HIDPP10_DEVICE_H
#define LOGID_BACKEND_HIDPP10_DEVICE_H
#include "../hidpp/Device.h"
namespace logid {
namespace backend {
namespace hidpp10
{
class Device : public hidpp::Device
{
public:
Device(const std::string& path, hidpp::DeviceIndex index);
Device(std::shared_ptr<raw::RawDevice> raw_dev,
hidpp::DeviceIndex index);
std::vector<uint8_t> getRegister(uint8_t address,
const std::vector<uint8_t>& params);
std::vector<uint8_t> setRegister(uint8_t address,
const std::vector<uint8_t>& params);
private:
std::vector<uint8_t> accessRegister(uint8_t sub_id,
uint8_t address, const std::vector<uint8_t>& params);
};
}}}
#endif //LOGID_BACKEND_HIDPP10_DEVICE_H

View File

@ -0,0 +1,17 @@
#ifndef LOGID_BACKEND_HIDPP10_DEFS_H
#define LOGID_BACKEND_HIDPP10_DEFS_H
namespace logid {
namespace backend {
namespace hidpp10
{
enum SubID: uint8_t
{
SetRegisterShort = 0x80,
GetRegisterShort = 0x81,
SetRegisterLong = 0x82,
GetRegisterLong = 0x83
};
}}}
#endif //LOGID_BACKEND_HIDPP10_DEFS_H

View File

@ -75,6 +75,8 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false)
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;
} }
RawDevice::~RawDevice() RawDevice::~RawDevice()
@ -106,6 +108,27 @@ std::vector<uint8_t> RawDevice::sendReport(const std::vector<uint8_t>& report)
return _respondToReport(report); return _respondToReport(report);
} }
// DJ commands are not systematically acknowledged, do not expect a result.
void RawDevice::sendReportNoResponse(const std::vector<uint8_t>& report)
{
assert(supportedReportID(report[0]));
/* If the listener will stop, handle I/O manually.
* Otherwise, push to queue and wait for result. */
if(continue_listen)
{
std::packaged_task<std::vector<uint8_t>()> task([this, report]() {
this->_sendReport(report);
return std::vector<uint8_t>();
});
auto f = task.get_future();
write_queue.push(&task);
f.get();
}
else
_sendReport(report);
}
std::vector<uint8_t> RawDevice::_respondToReport std::vector<uint8_t> RawDevice::_respondToReport
(const std::vector<uint8_t>& request) (const std::vector<uint8_t>& request)
{ {

View File

@ -29,6 +29,7 @@ namespace raw
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);
void sendReportNoResponse(const std::vector<uint8_t>& report);
void interruptRead(); void interruptRead();
void listen(); void listen();