mirror of
https://github.com/PixlOne/logiops.git
synced 2025-07-14 05:12:34 +08:00
Remove DJ support requirement from receivers
Allows Logi Bolt receivers to be detected by logid but not yet used.
This commit is contained in:
parent
d13d1feb4b
commit
fc96bb7b40
@ -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)
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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
|
@ -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());
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
203
src/logid/backend/hidpp10/Receiver.cpp
Normal file
203
src/logid/backend/hidpp10/Receiver.cpp
Normal 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;
|
||||
}
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user