Remove DJ support requirement from receivers

Allows Logi Bolt receivers to be detected by logid but not yet used.
This commit is contained in:
pixl 2023-04-29 16:43:30 -04:00
parent d13d1feb4b
commit fc96bb7b40
No known key found for this signature in database
GPG Key ID: 1866C148CD593B6E
21 changed files with 313 additions and 854 deletions

View File

@ -42,9 +42,8 @@ add_executable(logid
backend/raw/DeviceMonitor.cpp
backend/raw/RawDevice.cpp
backend/raw/IOMonitor.cpp
backend/dj/Receiver.cpp
backend/dj/ReceiverMonitor.cpp
backend/dj/Error.cpp
backend/hidpp10/Receiver.cpp
backend/hidpp10/ReceiverMonitor.cpp
backend/hidpp/Device.cpp
backend/hidpp/Report.cpp
backend/hidpp10/Error.cpp
@ -64,7 +63,6 @@ add_executable(logid
backend/hidpp20/features/ChangeHost.cpp
backend/hidpp20/features/WirelessDeviceStatus.cpp
backend/hidpp20/features/ThumbWheel.cpp
backend/dj/Report.cpp
util/task.cpp
util/ExceptionHandler.cpp)

View File

@ -18,6 +18,7 @@
#include <DeviceManager.h>
#include <backend/Error.h>
#include <backend/hidpp10/Error.h>
#include <util/log.h>
#include <ipcgull/function.h>
#include <thread>
@ -96,9 +97,8 @@ void DeviceManager::addDevice(std::string path) {
}
try {
hidpp::Device device(
path, hidpp::DefaultDevice, _self.lock(),
config()->io_timeout.value_or(defaults::io_timeout));
hidpp::Device device(path, hidpp::DefaultDevice, _self.lock(),
config()->io_timeout.value_or(defaults::io_timeout));
isReceiver = device.version() == std::make_tuple(1, 0);
} catch (hidpp10::Error& e) {
if (e.code() != hidpp10::Error::UnknownDevice)
@ -146,14 +146,12 @@ void DeviceManager::addDevice(std::string path) {
if (e.code() != hidpp10::Error::UnknownDevice)
throw;
else
logPrintf(WARN,
"HID++ 1.0 error while trying to initialize %s:"
"%s", path.c_str(), e.what());
logPrintf(WARN, "HID++ 1.0 error while trying to initialize %s: %s",
path.c_str(), e.what());
} catch (hidpp::Device::InvalidDevice& e) { // Ignore
} catch (std::system_error& e) {
// This error should have been thrown previously
logPrintf(WARN, "I/O error on %s: %s", path.c_str(),
e.what());
logPrintf(WARN, "I/O error on %s: %s", path.c_str(), e.what());
}
}
}

View File

@ -19,6 +19,7 @@
#include <Receiver.h>
#include <DeviceManager.h>
#include <backend/Error.h>
#include <backend/hidpp10/Error.h>
#include <util/log.h>
#include <ipc_defs.h>
@ -61,8 +62,8 @@ std::shared_ptr<Receiver> Receiver::make(
Receiver::Receiver(const std::string& path,
const std::shared_ptr<DeviceManager>& manager) :
dj::ReceiverMonitor(path, manager,
manager->config()->io_timeout.value_or(
hidpp10::ReceiverMonitor(path, manager,
manager->config()->io_timeout.value_or(
defaults::io_timeout)),
_path(path), _manager(manager), _nickname(manager),
_ipc_node(manager->receiversNode()->make_child(_nickname)),
@ -162,7 +163,7 @@ const std::string& Receiver::path() const {
return _path;
}
std::shared_ptr<dj::Receiver> Receiver::rawReceiver() {
std::shared_ptr<hidpp10::Receiver> Receiver::rawReceiver() {
return receiver();
}

View File

@ -21,7 +21,7 @@
#include <string>
#include <Device.h>
#include <backend/dj/ReceiverMonitor.h>
#include <backend/hidpp10/ReceiverMonitor.h>
namespace logid {
class ReceiverNickname {
@ -41,7 +41,7 @@ namespace logid {
const std::weak_ptr<DeviceManager> _manager;
};
class Receiver : public backend::dj::ReceiverMonitor,
class Receiver : public backend::hidpp10::ReceiverMonitor,
public ipcgull::object {
public:
typedef std::map<backend::hidpp::DeviceIndex, std::shared_ptr<Device>>
@ -55,7 +55,7 @@ namespace logid {
[[nodiscard]] const std::string& path() const;
std::shared_ptr<backend::dj::Receiver> rawReceiver();
std::shared_ptr<backend::hidpp10::Receiver> rawReceiver();
[[nodiscard]] const DeviceList& devices() const;

View File

@ -1,39 +0,0 @@
/*
* Copyright 2019-2023 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <backend/dj/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 "Reserved";
}
}
uint8_t Error::code() const noexcept {
return _code;
}

View File

@ -1,44 +0,0 @@
/*
* Copyright 2019-2023 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LOGID_BACKEND_DJ_ERROR_H
#define LOGID_BACKEND_DJ_ERROR_H
#include <cstdint>
#include <stdexcept>
namespace logid::backend::dj {
class Error : public std::exception {
public:
enum ErrorCode : uint8_t {
Unknown = 0x00,
KeepAliveTimeout = 0x01
};
explicit Error(uint8_t code);
[[nodiscard]] const char* what() const noexcept override;
[[nodiscard]] uint8_t code() const noexcept;
private:
uint8_t _code;
};
}
#endif //LOGID_BACKEND_DJ_ERROR_H

View File

@ -1,336 +0,0 @@
/*
* Copyright 2019-2023 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <backend/dj/Receiver.h>
#include <cassert>
#include <backend/dj/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,
const std::shared_ptr<raw::DeviceMonitor>& monitor,
double timeout) :
_raw_device(std::make_shared<raw::RawDevice>(std::move(path), monitor)),
_hidpp10_device(_raw_device, hidpp::DefaultDevice, timeout) {
if (!supportsDjReports(_raw_device->reportDescriptor()))
throw InvalidReceiver(InvalidReceiver::NoDJReports);
// Pass all HID++ events on DefaultDevice to handleHidppEvent
_raw_hidpp_handler = _raw_device->addEventHandler(
{
[](const std::vector<uint8_t>& report) -> bool {
return (report[hidpp::Offset::Type] ==
hidpp::Report::Type::Short ||
report[hidpp::Offset::Type] ==
hidpp::Report::Type::Long);
},
[this](const std::vector<uint8_t>& report) -> void {
hidpp::Report _report(report);
this->_handleHidppEvent(_report);
}
});
// Pass all DJ events with device index to handleDjEvent
_raw_dj_handler = _raw_device->addEventHandler(
{
[](const std::vector<uint8_t>& report) -> bool {
return (report[Offset::Type] ==
Report::Type::Short ||
report[Offset::Type] ==
Report::Type::Long);
},
[this](const std::vector<uint8_t>& report) -> void {
Report _report(report);
this->_handleDjEvent(_report);
}
});
}
Receiver::~Receiver() {
_raw_device->removeEventHandler(_raw_dj_handler);
_raw_device->removeEventHandler(_raw_hidpp_handler);
}
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] += 0x1f;
auto response = _hidpp10_device.getRegister(PairingInfo, request,
hidpp::ReportType::Long);
struct PairingInfo info{};
info.destinationId = response[1];
info.reportInterval = response[2];
info.pid = response[4];
info.pid |= (response[3] << 8);
info.deviceType = static_cast<DeviceType::DeviceType>(response[7]);
return info;
}
struct Receiver::ExtendedPairingInfo
Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index) {
std::vector<uint8_t> request(1);
request[0] = index;
request[0] += 0x2f;
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 + 1] << 8 * i);
for (uint8_t i = 0; i < 4; i++)
info.reportTypes[i] = response[i + 5];
uint8_t psl = response[8] & 0xf;
if (psl > 0xc)
info.powerSwitchLocation = PowerSwitchLocation::Reserved;
else
info.powerSwitchLocation = static_cast<PowerSwitchLocation>(psl);
return info;
}
std::string Receiver::getDeviceName(hidpp::DeviceIndex index) {
std::vector<uint8_t> request(1);
request[0] = index;
request[0] += 0x3f;
auto response = _hidpp10_device.getRegister(PairingInfo, request,
hidpp::ReportType::Long);
uint8_t size = response[1];
assert(size <= 14);
std::string name(size, ' ');
for (std::size_t i = 0; i < size; i++)
name[i] = (char) (response[i + 2]);
return name;
}
hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(const hidpp::Report&
report) {
assert(report.subId() == DeviceDisconnection);
return report.deviceIndex();
}
hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(const
hidpp::Report& report) {
assert(report.subId() == DeviceConnection);
hidpp::DeviceConnectionEvent event{};
event.index = report.deviceIndex();
event.unifying = ((report.address() & 0b111) == 0x04);
event.deviceType = static_cast<DeviceType::DeviceType>(
report.paramBegin()[0] & 0x0f);
event.softwarePresent = report.paramBegin()[0] & (1 << 4);
event.encrypted = report.paramBegin()[0] & (1 << 5);
event.linkEstablished = !(report.paramBegin()[0] & (1 << 6));
event.withPayload = report.paramBegin()[0] & (1 << 7);
event.fromTimeoutCheck = false;
event.pid = (report.paramBegin()[2] << 8);
event.pid |= report.paramBegin()[1];
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) {
assert(_dj_event_handlers.find(nickname) == _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) {
assert(_hidpp_event_handlers.find(nickname) == _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->sendReport(request.rawData());
}
Receiver::ConnectionStatusEvent Receiver::connectionStatusEvent(Report& report) {
assert(report.feature() == ConnectionStatus);
ConnectionStatusEvent event{};
event.index = report.index();
event.linkLost = report.paramBegin()[0];
return event;
}
std::shared_ptr<raw::RawDevice> Receiver::rawDevice() const {
return _raw_device;
}

View File

@ -1,127 +0,0 @@
/*
* Copyright 2019-2023 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <backend/dj/Report.h>
#include <algorithm>
#include <array>
#include <cassert>
using namespace logid::backend::dj;
using namespace logid::backend;
static const std::array<uint8_t, 34> DJReportDesc = {
0xA1, 0x01, // Collection (Application)
0x85, 0x20, // Report ID (32)
0x95, 0x0E, // Report Count (14)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0x41, // Usage (0x41)
0x81, 0x00, // Input (Data, Array, Absolute)
0x09, 0x41, // Usage (0x41)
0x91, 0x00, // Output (Data, Array, Absolute)
0x85, 0x21, // Report ID (33)
0x95, 0x1F, // Report Count (31)
0x09, 0x42, // Usage (0x42)
0x81, 0x00, // Input (Data, Array, Absolute)
0x09, 0x42, // Usage (0x42)
0x91, 0x00, // Output (Data, Array, Absolute)
0xC0 // End Collection
};
static const std::array<uint8_t, 39> DJReportDesc2 = {
0xA1, 0x01, // Collection (Application)
0x85, 0x20, // Report ID (32)
0x75, 0x08, // Report Size (8)
0x95, 0x0E, // Report Count (14)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0x41, // Usage (0x41)
0x81, 0x00, // Input (Data, Array, Absolute)
0x09, 0x41, // Usage (0x41)
0x91, 0x00, // Output (Data, Array, Absolute)
0x85, 0x21, // Report ID (33)
0x95, 0x1F, // Report Count (31)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0x42, // Usage (0x42)
0x81, 0x00, // Input (Data, Array, Absolute)
0x09, 0x42, // Usage (0x42)
0x91, 0x00, // Output (Data, Array, Absolute)
0xC0 // End Collection
};
bool dj::supportsDjReports(const std::vector<uint8_t>& report_desc) {
auto it = std::search(report_desc.begin(), report_desc.end(),
DJReportDesc.begin(), DJReportDesc.end());
if (it == report_desc.end())
it = std::search(report_desc.begin(), report_desc.end(),
DJReportDesc2.begin(), DJReportDesc2.end());
return it != report_desc.end();
}
Report::Report(const 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;
}
const std::vector<uint8_t>& Report::rawData() const {
return _data;
}

View File

@ -1,60 +0,0 @@
/*
* Copyright 2019-2023 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LOGID_BACKEND_DJ_REPORT_H
#define LOGID_BACKEND_DJ_REPORT_H
#include <cstdint>
#include <vector>
#include <backend/dj/defs.h>
#include <backend/hidpp/defs.h>
namespace logid::backend::dj {
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(const std::vector<uint8_t>& report_desc);
class Report {
public:
typedef ReportType::ReportType Type;
explicit Report(const std::vector<uint8_t>& data);
Report(Type type, hidpp::DeviceIndex index, uint8_t feature);
[[nodiscard]] Type type() const;
[[nodiscard]] hidpp::DeviceIndex index() const;
[[nodiscard]] uint8_t feature() const;
std::vector<uint8_t>::iterator paramBegin();
[[nodiscard]] const std::vector<uint8_t>& rawData() const;
private:
std::vector<uint8_t> _data;
};
}
#endif //LOGID_BACKEND_DJ_REPORT_H

View File

@ -1,53 +0,0 @@
/*
* Copyright 2019-2023 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LOGID_BACKEND_DJ_DEFS_H
#define LOGID_BACKEND_DJ_DEFS_H
#include <cstdint>
namespace logid::backend::dj {
namespace ReportType {
enum ReportType : uint8_t {
Short = 0x20,
Long = 0x21
};
}
namespace DeviceType {
enum DeviceType : uint8_t {
Unknown = 0x00,
Keyboard = 0x01,
Mouse = 0x02,
Numpad = 0x03,
Presenter = 0x04,
/* 0x05-0x07 is reserved */
Trackball = 0x08,
Touchpad = 0x09
};
}
[[maybe_unused]]
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

View File

@ -20,8 +20,7 @@
#include <backend/hidpp20/features/Root.h>
#include <backend/hidpp20/features/DeviceName.h>
#include <backend/hidpp20/Feature.h>
#include <backend/hidpp10/Error.h>
#include <backend/dj/Receiver.h>
#include <backend/hidpp10/Receiver.h>
#include <backend/Error.h>
#include <cassert>
#include <utility>
@ -68,7 +67,7 @@ Device::Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index,
_init();
}
Device::Device(const std::shared_ptr<dj::Receiver>& receiver,
Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceConnectionEvent event, double timeout) :
io_timeout(duration_cast<milliseconds>(
duration<double, std::milli>(timeout))),
@ -85,7 +84,7 @@ Device::Device(const std::shared_ptr<dj::Receiver>& receiver,
_init();
}
Device::Device(const std::shared_ptr<dj::Receiver>& receiver,
Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
DeviceIndex index, double timeout) :
io_timeout(duration_cast<milliseconds>(
duration<double, std::milli>(timeout))),
@ -130,8 +129,7 @@ void Device::_init() {
}});
try {
auto rsp = sendReport(
{ReportType::Short, _index,
auto rsp = sendReport({ReportType::Short, _index,
hidpp20::FeatureID::ROOT, hidpp20::Root::Ping,
hidpp::softwareID});
if (rsp.deviceIndex() != _index) {
@ -253,7 +251,7 @@ Report Device::sendReport(const Report& report) {
if (std::holds_alternative<Report>(response)) {
return std::get<Report>(response);
} else if(std::holds_alternative<Report::Hidpp10Error>(response)) {
throw hidpp20::Error(std::get<Report::Hidpp10Error>(response).error_code);
throw hidpp10::Error(std::get<Report::Hidpp10Error>(response).error_code);
} else if(std::holds_alternative<Report::Hidpp20Error>(response)) {
throw hidpp20::Error(std::get<Report::Hidpp20Error>(response).error_code);
}
@ -289,6 +287,10 @@ bool Device::responseReport(const Report& report) {
}
const std::shared_ptr<raw::RawDevice>& Device::rawDevice() const {
return _raw_device;
}
void Device::_sendReport(Report report) {
reportFixup(report);
_raw_device->sendReport(report.rawReport());

View File

@ -29,7 +29,7 @@
#include <backend/hidpp/Report.h>
#include <backend/hidpp/defs.h>
namespace logid::backend::dj {
namespace logid::backend::hidpp10 {
// Need to define here for a constructor
class Receiver;
}
@ -70,10 +70,10 @@ namespace logid::backend::hidpp {
Device(std::shared_ptr<raw::RawDevice> raw_device, DeviceIndex index,
double timeout);
Device(const std::shared_ptr<dj::Receiver>& receiver,
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceConnectionEvent event, double timeout);
Device(const std::shared_ptr<dj::Receiver>& receiver,
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
DeviceIndex index, double timeout);
virtual ~Device();
@ -98,6 +98,8 @@ namespace logid::backend::hidpp {
void handleEvent(Report& report);
[[nodiscard]] const std::shared_ptr<raw::RawDevice>& rawDevice() const;
protected:
// Returns whether the report is a response
virtual bool responseReport(const Report& report);
@ -116,7 +118,7 @@ namespace logid::backend::hidpp {
std::shared_ptr<raw::RawDevice> _raw_device;
raw::RawDevice::EvHandlerId _raw_handler;
std::shared_ptr<dj::Receiver> _receiver;
std::shared_ptr<hidpp10::Receiver> _receiver;
std::string _path;
DeviceIndex _index;

View File

@ -35,7 +35,7 @@ Device::Device(std::shared_ptr<raw::RawDevice> raw_dev, hidpp::DeviceIndex index
assert(version() == std::make_tuple(1, 0));
}
Device::Device(const std::shared_ptr<dj::Receiver>& receiver,
Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceIndex index,
double timeout)
: hidpp::Device(receiver, index, timeout) {

View File

@ -34,7 +34,7 @@ namespace logid::backend::hidpp10 {
Device(std::shared_ptr<raw::RawDevice> raw_dev,
hidpp::DeviceIndex index, double timeout);
Device(const std::shared_ptr<dj::Receiver>& receiver,
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceIndex index, double timeout);
hidpp::Report sendReport(const hidpp::Report& report) final;

View File

@ -0,0 +1,203 @@
/*
* Copyright 2019-2023 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <backend/hidpp10/Receiver.h>
#include <backend/hidpp10/Error.h>
#include <cassert>
using namespace logid::backend::hidpp10;
using namespace logid::backend;
const char* InvalidReceiver::what() const noexcept {
return "Not a receiver";
}
Receiver::Receiver(const std::string& path, const std::shared_ptr<raw::DeviceMonitor>& monitor,
double timeout) : Device(path, hidpp::DefaultDevice, monitor, timeout) {
// Check if the device is a receiver
try {
getNotificationFlags();
} catch(hidpp10::Error& e) {
if (e.code() == Error::InvalidAddress)
throw InvalidReceiver();
}
}
Receiver::NotificationFlags Receiver::getNotificationFlags() {
auto response = 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::setNotifications(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);
setRegister(EnableHidppNotifications, request, hidpp::ReportType::Short);
}
void Receiver::enumerate() {
setRegister(ConnectionState, {2}, hidpp::ReportType::Short);
}
///TODO: Investigate usage
uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index) {
auto response = 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;
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;
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;
setRegister(DevicePairing, request, hidpp::ReportType::Short);
}
std::map<hidpp::DeviceIndex, uint8_t> Receiver::getDeviceActivity() {
auto response = 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] += 0x1f;
auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long);
struct PairingInfo info{};
info.destinationId = response[1];
info.reportInterval = response[2];
info.pid = response[4];
info.pid |= (response[3] << 8);
info.deviceType = static_cast<hidpp::DeviceType>(response[7]);
return info;
}
struct Receiver::ExtendedPairingInfo
Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index) {
std::vector<uint8_t> request(1);
request[0] = index;
request[0] += 0x2f;
auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long);
ExtendedPairingInfo info{};
info.serialNumber = 0;
for (uint8_t i = 0; i < 4; i++)
info.serialNumber |= (response[i + 1] << 8 * i);
for (uint8_t i = 0; i < 4; i++)
info.reportTypes[i] = response[i + 5];
uint8_t psl = response[8] & 0xf;
if (psl > 0xc)
info.powerSwitchLocation = PowerSwitchLocation::Reserved;
else
info.powerSwitchLocation = static_cast<PowerSwitchLocation>(psl);
return info;
}
std::string Receiver::getDeviceName(hidpp::DeviceIndex index) {
std::vector<uint8_t> request(1);
request[0] = index;
request[0] += 0x3f;
auto response = getRegister(PairingInfo, request, hidpp::ReportType::Long);
uint8_t size = response[1];
assert(size <= 14);
std::string name(size, ' ');
for (std::size_t i = 0; i < size; i++)
name[i] = (char) (response[i + 2]);
return name;
}
hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(const hidpp::Report&
report) {
assert(report.subId() == DeviceDisconnection);
return report.deviceIndex();
}
hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(const hidpp::Report& report) {
assert(report.subId() == DeviceConnection);
hidpp::DeviceConnectionEvent event{};
event.index = report.deviceIndex();
event.unifying = ((report.address() & 0b111) == 0x04);
event.deviceType = static_cast<hidpp::DeviceType>(report.paramBegin()[0] & 0x0f);
event.softwarePresent = report.paramBegin()[0] & (1 << 4);
event.encrypted = report.paramBegin()[0] & (1 << 5);
event.linkEstablished = !(report.paramBegin()[0] & (1 << 6));
event.withPayload = report.paramBegin()[0] & (1 << 7);
event.fromTimeoutCheck = false;
event.pid = (report.paramBegin()[2] << 8);
event.pid |= report.paramBegin()[1];
return event;
}

View File

@ -20,75 +20,59 @@
#define LOGID_BACKEND_DJ_RECEIVER_H
#include <cstdint>
#include <backend/raw/RawDevice.h>
#include <backend/dj/Report.h>
#include <backend/hidpp/Report.h>
#include <backend/hidpp10/Device.h>
namespace logid::backend::dj {
struct EventHandler {
std::function<bool(const Report&)> condition;
std::function<void(const Report&)> callback;
namespace logid::backend::hidpp {
enum DeviceType : uint8_t {
DeviceUnknown = 0x00,
DeviceKeyboard = 0x01,
DeviceMouse = 0x02,
DeviceNumpad = 0x03,
DevicePresenter = 0x04,
/* 0x05-0x07 is reserved */
DeviceTrackball = 0x08,
DeviceTouchpad = 0x09
};
struct DeviceConnectionEvent {
DeviceIndex index;
uint16_t pid{};
DeviceType deviceType = DeviceUnknown;
bool unifying{};
bool softwarePresent{};
bool encrypted{};
bool linkEstablished{};
bool withPayload{};
bool fromTimeoutCheck = false; // Fake field
};
}
namespace logid::backend::hidpp10 {
class InvalidReceiver : public std::exception {
public:
enum Reason {
NoDJReports
};
explicit InvalidReceiver(Reason reason);
[[nodiscard]] const char* what() const noexcept override;
[[nodiscard]] Reason code() const noexcept;
private:
Reason _reason;
};
class Receiver final {
class Receiver : public Device {
public:
Receiver(std::string path,
Receiver(const std::string& path,
const std::shared_ptr<raw::DeviceMonitor>& monitor,
double timeout);
~Receiver();
enum DjEvents : uint8_t {
DeviceDisconnection = 0x40,
DeviceConnection = 0x41,
ConnectionStatus = 0x42
};
enum DjCommands : uint8_t {
/* Kernel driver should handle this */
SwitchAndKeepAlive [[maybe_unused]] = 0x80,
GetPairedDevices = 0x81
};
void enumerateDj();
struct ConnectionStatusEvent {
hidpp::DeviceIndex index;
bool linkLost;
};
ConnectionStatusEvent connectionStatusEvent(dj::Report& report);
/* 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 {
enum Events : uint8_t {
// These events are identical to their DJ counterparts
// DeviceDisconnection = 0x40,
// DeviceConnection = 0x41,
DeviceDisconnection = 0x40,
DeviceConnection = 0x41,
LockingChange = 0x4a
};
enum HidppRegisters : uint8_t {
enum Registers : uint8_t {
EnableHidppNotifications = 0x00,
ConnectionState = 0x02,
DevicePairing = 0xb2,
@ -102,11 +86,11 @@ namespace logid::backend::dj {
bool receiverSoftwarePresent;
};
NotificationFlags getHidppNotifications();
NotificationFlags getNotificationFlags();
void enableHidppNotifications(NotificationFlags flags);
void setNotifications(NotificationFlags flags);
void enumerateHidpp();
void enumerate();
uint8_t getConnectionState(hidpp::DeviceIndex index);
@ -122,7 +106,7 @@ namespace logid::backend::dj {
uint8_t destinationId;
uint8_t reportInterval;
uint16_t pid;
DeviceType::DeviceType deviceType;
hidpp::DeviceType deviceType;
};
enum class PowerSwitchLocation : uint8_t {
@ -149,67 +133,13 @@ namespace logid::backend::dj {
struct PairingInfo getPairingInfo(hidpp::DeviceIndex index);
struct ExtendedPairingInfo getExtendedPairingInfo(hidpp::DeviceIndex
index);
struct ExtendedPairingInfo getExtendedPairingInfo(hidpp::DeviceIndex index);
std::string getDeviceName(hidpp::DeviceIndex index);
static hidpp::DeviceIndex deviceDisconnectionEvent(
const hidpp::Report& report);
static hidpp::DeviceIndex deviceDisconnectionEvent(const hidpp::Report& report);
static hidpp::DeviceConnectionEvent deviceConnectionEvent(
const hidpp::Report& report);
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();
[[nodiscard]] 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);
raw::RawDevice::EvHandlerId _raw_hidpp_handler;
raw::RawDevice::EvHandlerId _raw_dj_handler;
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 logid::backend::hidpp {
struct DeviceConnectionEvent {
hidpp::DeviceIndex index;
uint16_t pid{};
dj::DeviceType::DeviceType deviceType;
bool unifying{};
bool softwarePresent{};
bool encrypted{};
bool linkEstablished{};
bool withPayload{};
bool fromTimeoutCheck = false; // Fake field
static hidpp::DeviceConnectionEvent deviceConnectionEvent(const hidpp::Report& report);
};
}

View File

@ -16,43 +16,36 @@
*
*/
#include <backend/dj/ReceiverMonitor.h>
#include <utility>
#include <backend/hidpp10/ReceiverMonitor.h>
#include <cassert>
#include <util/task.h>
#include <util/log.h>
using namespace logid::backend::dj;
using namespace logid::backend::hidpp10;
using namespace logid::backend::hidpp;
ReceiverMonitor::ReceiverMonitor(std::string path,
const std::shared_ptr<raw::DeviceMonitor>& monitor,
double timeout) :
_receiver(std::make_shared<Receiver>(
std::move(path), monitor, timeout)) {
assert(!_receiver->hidppEventHandlers().contains(ev_handler_name));
assert(!_receiver->djEventHandlers().contains(ev_handler_name));
ReceiverMonitor::ReceiverMonitor(const std::string& path,
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout)
: _receiver(std::make_shared<Receiver>(path, monitor, timeout)) {
Receiver::NotificationFlags notification_flags{
true,
true,
true};
_receiver->enableHidppNotifications(notification_flags);
Receiver::NotificationFlags notification_flags{true, true, true};
_receiver->setNotifications(notification_flags);
}
ReceiverMonitor::~ReceiverMonitor() {
_receiver->removeHidppEventHandler(ev_handler_name);
if (ev_handler.has_value())
_receiver->removeEventHandler(ev_handler.value());
}
void ReceiverMonitor::ready() {
if (!_receiver->hidppEventHandlers().contains(ev_handler_name)) {
std::shared_ptr<hidpp::EventHandler> event_handler =
std::make_shared<hidpp::EventHandler>();
event_handler->condition = [](hidpp::Report& report) -> bool {
if (!ev_handler.has_value()) {
hidpp::EventHandler event_handler;
event_handler.condition = [](hidpp::Report& report) -> bool {
return (report.subId() == Receiver::DeviceConnection ||
report.subId() == Receiver::DeviceDisconnection);
};
event_handler->callback = [this](hidpp::Report& report) -> void {
event_handler.callback = [this](hidpp::Report& report) -> void {
/* Running in a new thread prevents deadlocks since the
* receiver may be enumerating.
*/
@ -80,14 +73,14 @@ void ReceiverMonitor::ready() {
});
};
_receiver->addHidppEventHandler(ev_handler_name, event_handler);
ev_handler = _receiver->addEventHandler(event_handler);
}
enumerate();
}
void ReceiverMonitor::enumerate() {
_receiver->enumerateHidpp();
_receiver->enumerate();
}
void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) {
@ -96,8 +89,7 @@ void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) {
*handler_id = _receiver->rawDevice()->addEventHandler(
{
[index](const std::vector<uint8_t>& report) -> bool {
return report[Offset::DeviceIndex] ==
index;
return report[Offset::DeviceIndex] == index;
},
[this, index, handler_id](
[[maybe_unused]] const std::vector<uint8_t>& report) {
@ -107,25 +99,17 @@ void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) {
event.index = index;
event.fromTimeoutCheck = true;
spawn_task(
[this, event, handler_id]() {
assert(handler_id);
try {
_receiver->rawDevice()->removeEventHandler(
*handler_id);
addDevice(
event);
} catch (
std::exception& e) {
logPrintf(
ERROR,
"Failed to add device %d to receiver "
"on %s: %s",
event.index,
_receiver->rawDevice()->rawPath().c_str(),
e.what());
}
});
spawn_task([this, event, handler_id]() {
assert(handler_id);
try {
_receiver->rawDevice()->removeEventHandler(*handler_id);
addDevice(event);
} catch (std::exception& e) {
logPrintf(ERROR, "Failed to add device %d to receiver on %s: %s",
event.index, _receiver->rawDevice()->rawPath().c_str(),
e.what());
}
});
}
});
}

View File

@ -19,16 +19,16 @@
#ifndef LOGID_BACKEND_DJ_RECEIVERMONITOR_H
#define LOGID_BACKEND_DJ_RECEIVERMONITOR_H
#include <backend/hidpp10/Receiver.h>
#include <backend/hidpp/defs.h>
#include <cstdint>
#include <string>
#include <backend/dj/Receiver.h>
#include <backend/hidpp/defs.h>
namespace logid::backend::dj {
namespace logid::backend::hidpp10 {
// This class will run on the RawDevice thread,
class ReceiverMonitor {
public:
ReceiverMonitor(std::string path,
ReceiverMonitor(const std::string& path,
const std::shared_ptr<raw::DeviceMonitor>& monitor,
double timeout);
@ -55,9 +55,9 @@ namespace logid::backend::dj {
[[nodiscard]] std::shared_ptr<Receiver> receiver() const;
private:
static constexpr const char* ev_handler_name = "receiver_monitor";
std::shared_ptr<Receiver> _receiver;
std::optional<hidpp::Device::EvHandlerId> ev_handler;
};
}

View File

@ -19,7 +19,7 @@
#include <cassert>
#include <backend/hidpp20/Device.h>
#include <backend/Error.h>
#include <backend/dj/Receiver.h>
#include <backend/hidpp10/Receiver.h>
using namespace logid::backend;
using namespace logid::backend::hidpp20;
@ -39,14 +39,14 @@ Device::Device(std::shared_ptr<raw::RawDevice> raw_device,
throw std::runtime_error("Invalid HID++ version");
}
Device::Device(const std::shared_ptr<dj::Receiver>& receiver,
Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceConnectionEvent event, double timeout) :
hidpp::Device(receiver, event, timeout) {
if (std::get<0>(version()) < 2)
throw std::runtime_error("Invalid HID++ version");
}
Device::Device(const std::shared_ptr<dj::Receiver>& receiver,
Device::Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceIndex index, double timeout)
: hidpp::Device(receiver, index, timeout) {
if (std::get<0>(version()) < 2)

View File

@ -34,10 +34,10 @@ namespace logid::backend::hidpp20 {
Device(std::shared_ptr<raw::RawDevice> raw_device,
hidpp::DeviceIndex index, double timeout);
Device(const std::shared_ptr<dj::Receiver>& receiver,
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceConnectionEvent event, double timeout);
Device(const std::shared_ptr<dj::Receiver>& receiver,
Device(const std::shared_ptr<hidpp10::Receiver>& receiver,
hidpp::DeviceIndex index, double timeout);
std::vector<uint8_t> callFunction(uint8_t feature_index,

View File

@ -19,7 +19,7 @@
#include <Device.h>
#include <algorithm>
#include <cmath>
#include "ipc_defs.h"
#include <ipc_defs.h>
using namespace logid::features;
using namespace logid::backend;