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
18 changed files with 819 additions and 26 deletions

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 <algorithm>
#include <cassert>
#include "Report.h"
using namespace logid::backend::dj;
using namespace logid::backend;
static const std::array<uint8_t, 35> DJReportDesc = {
static const std::array<uint8_t, 34> DJReportDesc = {
0xA1, 0x01, // Collection (Application)
0x85, 0x20, // Report ID (32)
0x95, 0x0E, // Report Count (14)
@@ -25,8 +26,68 @@ static const std::array<uint8_t, 35> DJReportDesc = {
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());
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 "../raw/RawDevice.h"
#include "defs.h"
#include "../hidpp/defs.h"
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
{
enum Type: uint8_t
{
Short = 0x20, // Short DJ reports use 12 byte parameters
Long = 0x21 // Long DJ reports use 29 byte parameters
};
public:
typedef ReportType::ReportType Type;
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
};
}
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