Ignore hidpp responses to kernel driver

Also fixes a bug where unconfigured devices would error.
This commit is contained in:
pixl 2022-01-28 18:28:58 -05:00
parent 7862c85d71
commit f25a1e4657
No known key found for this signature in database
GPG Key ID: 1866C148CD593B6E
15 changed files with 141 additions and 27 deletions

View File

@ -296,7 +296,12 @@ config::Device& Device::_getConfig(
auto& devices = manager->config()->devices;
if(!devices.has_value())
devices = decltype(config::Config::devices)();
auto& device = devices.value()[name];
if(!devices.value().count(name)) {
devices.value().emplace(name, config::Device());
}
auto& device = devices.value().at(name);
if(std::holds_alternative<config::Profile>(device)) {
config::Device d;
d.profiles["default"] = std::get<config::Profile>(device);
@ -306,7 +311,7 @@ config::Device& Device::_getConfig(
auto& conf = std::get<config::Device>(device);
if(conf.profiles.empty()) {
conf.profiles["default"] = std::get<config::Profile>(device);
conf.profiles["default"] = {};
conf.default_profile = "default";
}

View File

@ -112,7 +112,13 @@ void DeviceManager::addDevice(std::string path)
} catch(hidpp10::Error &e) {
if(e.code() != hidpp10::Error::UnknownDevice)
throw;
} catch(hidpp::Device::InvalidDevice &e) { // Ignore
} catch(hidpp::Device::InvalidDevice &e) {
if(e.code() == hidpp::Device::InvalidDevice::VirtualNode) {
logPrintf(DEBUG, "Ignoring virtual node on %s",
path.c_str());
return;
}
defaultExists = false;
} catch(std::system_error &e) {
logPrintf(WARN, "I/O error on %s: %s, skipping device.",

View File

@ -47,6 +47,61 @@ std::shared_ptr<Action> _makeAction(Device* device,
return std::make_shared<typename action_type<T>::type>(device, action);
}
template <typename T>
std::shared_ptr<Action> _makeAction(
Device *device, const std::string &name,
std::optional<T>& config)
{
if(name == "pizza.pixl.LogiOps.Action.ChangeDPI") {
config = config::ChangeDPI();
return Action::makeAction(device, config.value());
} else if(name == "pizza.pixl.LogiOps.Action.ChangeHost") {
config = config::ChangeHost();
return Action::makeAction(device, config.value());
} else if(name == "pizza.pixl.LogiOps.Action.CycleDPI") {
config = config::CycleDPI();
return Action::makeAction(device, config.value());
} else if(name == "pizza.pixl.LogiOps.Action.Keypress") {
config = config::KeypressAction();
return Action::makeAction(device, config.value());
} else if(name == "pizza.pixl.LogiOps.Action.None") {
config = config::NoAction();
return Action::makeAction(device, config.value());
} else if(name == "pizza.pixl.LogiOps.Action.ToggleHiresScroll") {
config = config::ToggleHiresScroll();
return Action::makeAction(device, config.value());
} else if(name == "pizza.pixl.LogiOps.Action.ToggleSmartShift") {
config = config::ToggleHiresScroll();
return Action::makeAction(device, config.value());
} else if(name == "pizza.pixl.LogiOps.Action.Default") {
return nullptr;
}
throw InvalidAction();
}
std::shared_ptr<Action> Action::makeAction(
Device *device, const std::string &name,
std::optional<config::BasicAction> &config)
{
return _makeAction(device, name, config);
}
std::shared_ptr<Action> Action::makeAction(
Device *device, const std::string &name,
std::optional<config::Action> &config)
{
try {
return _makeAction(device, name, config);
} catch(actions::InvalidAction& e) {
if(name == "pizza.pixl.LogiOps.Action.Gesture") {
config = config::GestureAction();
return makeAction(device, config.value());
}
throw;
}
}
std::shared_ptr<Action> Action::makeAction(Device *device,
config::BasicAction& action)
{

View File

@ -46,6 +46,14 @@ namespace actions {
class Action
{
public:
static std::shared_ptr<Action> makeAction(
Device* device, const std::string& name,
std::optional<config::BasicAction>& config);
static std::shared_ptr<Action> makeAction(
Device* device, const std::string& name,
std::optional<config::Action>& config);
static std::shared_ptr<Action> makeAction(Device* device,
config::BasicAction& action);

View File

@ -22,8 +22,3 @@ const char *logid::backend::TimeoutError::what() const noexcept
{
return "Device timed out";
}
const char *logid::backend::InvalidDevice::what() const noexcept
{
return "Device has become invalidated";
}

View File

@ -30,12 +30,6 @@ public:
const char* what() const noexcept override;
};
class InvalidDevice : public std::exception
{
public:
InvalidDevice() = default;
const char* what() const noexcept override;
};
}}
#endif //LOGID_BACKEND_ERROR_H

View File

@ -41,6 +41,8 @@ const char* Device::InvalidDevice::what() const noexcept
return "Invalid raw device";
case Asleep:
return "Device asleep";
case VirtualNode:
return "Virtual device";
default:
return "Invalid device";
}
@ -122,6 +124,35 @@ void Device::_init()
if(!supported_reports)
throw InvalidDevice(InvalidDevice::NoHIDPPReport);
/* hid_logitech_dj creates virtual /dev/hidraw nodes for receiver
* devices. We should ignore these devices.
*/
if(_index == hidpp::DefaultDevice) {
_raw_handler = _raw_device->addEventHandler({
[index=this->_index](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->handleEvent(_report);
} });
try {
auto rsp = sendReport({ReportType::Short, _index, 0, 0,
LOGID_HIDPP_SOFTWARE_ID});
if(rsp.deviceIndex() != _index) {
throw InvalidDevice(InvalidDevice::VirtualNode);
}
} catch(hidpp10::Error& e) {
// Ignore
} catch(std::exception& e) {
_raw_device->removeEventHandler(_raw_handler);
throw;
}
_raw_device->removeEventHandler(_raw_handler);
}
_raw_handler = _raw_device->addEventHandler({
[index=this->_index](const std::vector<uint8_t>& report)->bool {
return (report[Offset::Type] == Report::Type::Short ||
@ -227,14 +258,16 @@ Report Device::sendReport(const Report &report)
if(!valid)
throw TimeoutError();
std::lock_guard<std::mutex> sl(_slot_lock);
{
Report::Hidpp10Error error{};
if(report.isError10(&error))
if(_report_slot.value().isError10(&error))
throw hidpp10::Error(error.error_code);
}
{
Report::Hidpp20Error error{};
if(report.isError20(&error))
if(_report_slot.value().isError20(&error))
throw hidpp20::Error(error.error_code);
}
return _report_slot.value();
@ -247,6 +280,11 @@ bool Device::responseReport(const Report &report)
return false;
}
// Basic check to see if the report is a response
if( (report.swId() != LOGID_HIDPP_SOFTWARE_ID)
&& report.subId() < 0x80)
return false;
std::lock_guard lock(_slot_lock);
_report_slot = report;
_resp_cv.notify_all();

View File

@ -53,7 +53,8 @@ namespace hidpp
{
NoHIDPPReport,
InvalidRawDevice,
Asleep
Asleep,
VirtualNode
};
InvalidDevice(Reason reason) : _reason (reason) {}
virtual const char* what() const noexcept;

View File

@ -246,10 +246,10 @@ uint8_t Report::swId() const
return _data[Offset::Function] & 0x0f;
}
void Report::setSwId(uint8_t sub_id)
void Report::setSwId(uint8_t sw_id)
{
_data[Offset::Function] &= 0xf0;
_data[Offset::Function] |= sub_id & 0x0f;
_data[Offset::Function] |= sw_id & 0x0f;
}
uint8_t Report::address() const

View File

@ -19,7 +19,7 @@
#ifndef LOGID_BACKEND_HIDPP_DEFS_H
#define LOGID_BACKEND_HIDPP_DEFS_H
#define LOGID_HIDPP_SOFTWARE_ID 1
#define LOGID_HIDPP_SOFTWARE_ID 2
#include <cstdint>

View File

@ -169,12 +169,16 @@ hidpp::Report Device::sendReport(const hidpp::Report& report)
bool Device::responseReport(const hidpp::Report& report)
{
std::lock_guard<std::mutex> lock(_response_lock);
uint8_t sw_id = report.swId();
uint8_t sw_id;
bool is_error = false;
hidpp::Report::Hidpp20Error hidpp20_error {};
if(report.isError20(&hidpp20_error))
if(report.isError20(&hidpp20_error)) {
is_error = true;
sw_id = hidpp20_error.software_id;
} else {
sw_id = report.swId();
}
auto response_slot = _responses.find(sw_id);
if(response_slot == _responses.end())

View File

@ -74,6 +74,10 @@ uint8_t EssentialDeviceName::getNameLength()
std::vector<uint8_t> params(0);
auto response = this->callFunction(DeviceName::Function::GetLength, params);
if(response[0] == 1)
return 1;
return response[0];
}

View File

@ -92,5 +92,8 @@ std::tuple<uint8_t, uint8_t> EssentialRoot::getVersion()
std::vector<uint8_t> params(0);
auto response = this->callFunction(Root::Function::Ping, params);
if(response[0] == 0x11)
return std::make_tuple(1, 0);
return std::make_tuple(response[0], response[1]);
}

View File

@ -155,8 +155,10 @@ const std::vector<uint8_t>& RawDevice::reportDescriptor() const
void RawDevice::sendReport(const std::vector<uint8_t>& report)
{
if(!_valid)
throw InvalidDevice();
if(!_valid) {
// We could throw an error here, but this will likely be closed soon.
return;
}
if(logid::global_loglevel <= LogLevel::RAWREPORT) {
printf("[RAWREPORT] %s OUT: ", _path.c_str());

View File

@ -382,11 +382,10 @@ namespace logid::config {
struct config_io<std::optional<T>> {
static std::optional<T> get(const libconfig::Setting& parent,
const std::string& name) {
try {
if(parent.exists(name))
return config_io<T>::get(parent.lookup(name));
} catch(libconfig::SettingException& e) {
else
return {};
}
}
static void set(libconfig::Setting& parent,