Implement dj::Receiver class
Again, many things were done here.
This commit is contained in:
21
src/logid/backend/dj/Error.cpp
Normal file
21
src/logid/backend/dj/Error.cpp
Normal 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();
|
||||
}
|
||||
}
|
30
src/logid/backend/dj/Error.h
Normal file
30
src/logid/backend/dj/Error.h
Normal 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
|
255
src/logid/backend/dj/Receiver.cpp
Normal file
255
src/logid/backend/dj/Receiver.cpp
Normal 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();
|
||||
}
|
125
src/logid/backend/dj/Receiver.h
Normal file
125
src/logid/backend/dj/Receiver.h
Normal 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
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -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
|
Reference in New Issue
Block a user