mirror of
https://github.com/PixlOne/logiops.git
synced 2025-07-13 12:52:42 +08:00
Fix broken pipes and improve receiver handling
Retry before failing on broken pipe error (fixes Nano receiver), and test for virtual devices without I/O.
This commit is contained in:
parent
be5ee9f793
commit
5e32120b2c
@ -114,35 +114,8 @@ void Device::_setupReportsAndInit() {
|
||||
/* 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(
|
||||
{[](const std::vector<uint8_t>& report) -> bool {
|
||||
return (report[Offset::Type] == Report::Type::Short ||
|
||||
report[Offset::Type] == Report::Type::Long);
|
||||
},
|
||||
[self_weak = _self](const std::vector<uint8_t>& report) -> void {
|
||||
Report _report(report);
|
||||
if(auto self = self_weak.lock())
|
||||
self->handleEvent(_report);
|
||||
}});
|
||||
|
||||
try {
|
||||
auto rsp = sendReport({ReportType::Short, _index,
|
||||
hidpp20::FeatureID::ROOT, hidpp20::Root::Ping,
|
||||
hidpp::softwareID});
|
||||
if (rsp.deviceIndex() != _index)
|
||||
throw InvalidDevice(InvalidDevice::VirtualNode);
|
||||
} catch (hidpp10::Error& e) {
|
||||
if (e.deviceIndex() != _index)
|
||||
throw InvalidDevice(InvalidDevice::VirtualNode);
|
||||
} catch (hidpp20::Error& e) {
|
||||
/* This shouldn't happen, the device must not be ready */
|
||||
if (e.deviceIndex() != _index)
|
||||
throw InvalidDevice(InvalidDevice::VirtualNode);
|
||||
else
|
||||
throw DeviceNotReady();
|
||||
}
|
||||
}
|
||||
if (_raw_device->isSubDevice())
|
||||
throw InvalidDevice(InvalidDevice::VirtualNode);
|
||||
|
||||
_raw_handler = _raw_device->addEventHandler(
|
||||
{[index = _index](
|
||||
@ -228,6 +201,7 @@ Report Device::sendReport(const Report& report) {
|
||||
/* Must complete transaction before next send */
|
||||
std::lock_guard send_lock(_send_mutex);
|
||||
_sent_sub_id = report.subId();
|
||||
_sent_address = report.address();
|
||||
std::unique_lock lock(_response_mutex);
|
||||
_sendReport(report);
|
||||
|
||||
@ -244,6 +218,7 @@ Report Device::sendReport(const Report& report) {
|
||||
Response response = _response.value();
|
||||
_response.reset();
|
||||
_sent_sub_id.reset();
|
||||
_sent_address.reset();
|
||||
|
||||
if (std::holds_alternative<Report>(response)) {
|
||||
return std::get<Report>(response);
|
||||
@ -263,20 +238,23 @@ bool Device::responseReport(const Report& report) {
|
||||
std::lock_guard lock(_response_mutex);
|
||||
Response response = report;
|
||||
uint8_t sub_id;
|
||||
uint8_t address;
|
||||
|
||||
Report::Hidpp10Error hidpp10_error{};
|
||||
Report::Hidpp20Error hidpp20_error{};
|
||||
if (report.isError10(hidpp10_error)) {
|
||||
sub_id = hidpp10_error.sub_id;
|
||||
address = hidpp10_error.address;
|
||||
response = hidpp10_error;
|
||||
} else if (report.isError20(hidpp20_error)) {
|
||||
sub_id = hidpp20_error.feature_index;
|
||||
response = hidpp20_error;
|
||||
address = (hidpp20_error.function << 4) | (hidpp20_error.software_id & 0x0f);
|
||||
} else {
|
||||
sub_id = report.subId();
|
||||
address = report.address();
|
||||
}
|
||||
|
||||
if (sub_id == _sent_sub_id) {
|
||||
if (sub_id == _sent_sub_id && address == _sent_address) {
|
||||
_response = response;
|
||||
_response_cv.notify_all();
|
||||
return true;
|
||||
|
@ -158,6 +158,7 @@ namespace logid::backend::hidpp {
|
||||
|
||||
std::optional<Response> _response;
|
||||
std::optional<uint8_t> _sent_sub_id{};
|
||||
std::optional<uint8_t> _sent_address{};
|
||||
|
||||
std::shared_ptr<EventHandlerList<Device>> _event_handlers;
|
||||
|
||||
|
@ -42,7 +42,7 @@ namespace logid::backend::hidpp {
|
||||
|
||||
static constexpr uint8_t softwareID = 2;
|
||||
/* For sending reports with no response, use a different SW ID */
|
||||
static constexpr uint8_t noAckSoftwareID = 1;
|
||||
static constexpr uint8_t noAckSoftwareID = 3;
|
||||
|
||||
static constexpr std::size_t ShortParamLength = 3;
|
||||
static constexpr std::size_t LongParamLength = 16;
|
||||
|
@ -24,6 +24,23 @@
|
||||
using namespace logid::backend;
|
||||
using namespace logid::backend::hidpp10;
|
||||
|
||||
hidpp::Report setupRegReport(hidpp::DeviceIndex index,
|
||||
uint8_t sub_id, uint8_t address,
|
||||
const std::vector<uint8_t>& params) {
|
||||
hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ?
|
||||
hidpp::Report::Type::Short : hidpp::Report::Type::Long;
|
||||
|
||||
if (sub_id == SetRegisterLong) {
|
||||
// When setting a long register, the report must be long.
|
||||
type = hidpp::Report::Type::Long;
|
||||
}
|
||||
|
||||
hidpp::Report request(type, index, sub_id, address);
|
||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
Device::Device(const std::string& path, hidpp::DeviceIndex index,
|
||||
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout) :
|
||||
hidpp::Device(path, index, monitor, timeout) {
|
||||
@ -124,22 +141,24 @@ std::vector<uint8_t> Device::setRegister(uint8_t address,
|
||||
return accessRegister(sub_id, address, params);
|
||||
}
|
||||
|
||||
void Device::setRegisterNoResponse(uint8_t address,
|
||||
const std::vector<uint8_t>& params,
|
||||
hidpp::Report::Type type) {
|
||||
assert(params.size() <= hidpp::LongParamLength);
|
||||
|
||||
uint8_t sub_id = type == hidpp::Report::Type::Short ?
|
||||
SetRegisterShort : SetRegisterLong;
|
||||
|
||||
return accessRegisterNoResponse(sub_id, address, params);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Device::accessRegister(uint8_t sub_id, uint8_t address,
|
||||
const std::vector<uint8_t>& params) {
|
||||
hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ?
|
||||
hidpp::Report::Type::Short : hidpp::Report::Type::Long;
|
||||
|
||||
if (sub_id == SetRegisterLong) {
|
||||
// When setting a long register, the report must be long.
|
||||
type = hidpp::Report::Type::Long;
|
||||
}
|
||||
|
||||
hidpp::Report request(type, deviceIndex(), sub_id, address);
|
||||
std::copy(params.begin(), params.end(), request.paramBegin());
|
||||
|
||||
auto response = sendReport(request);
|
||||
auto response = sendReport(setupRegReport(deviceIndex(), sub_id, address, params));
|
||||
return {response.paramBegin(), response.paramEnd()};
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Device::accessRegisterNoResponse(uint8_t sub_id, uint8_t address,
|
||||
const std::vector<uint8_t>& params) {
|
||||
sendReportNoACK(setupRegReport(deviceIndex(), sub_id, address, params));
|
||||
}
|
||||
|
@ -39,6 +39,9 @@ namespace logid::backend::hidpp10 {
|
||||
const std::vector<uint8_t>& params,
|
||||
hidpp::Report::Type type);
|
||||
|
||||
void setRegisterNoResponse(uint8_t address, const std::vector<uint8_t>& params,
|
||||
hidpp::Report::Type type);
|
||||
|
||||
protected:
|
||||
Device(const std::string& path, hidpp::DeviceIndex index,
|
||||
const std::shared_ptr<raw::DeviceMonitor>& monitor, double timeout);
|
||||
@ -53,18 +56,24 @@ namespace logid::backend::hidpp10 {
|
||||
|
||||
private:
|
||||
typedef std::variant<hidpp::Report, hidpp::Report::Hidpp10Error> Response;
|
||||
|
||||
struct ResponseSlot {
|
||||
std::optional<Response> response;
|
||||
std::optional<uint8_t> sub_id;
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
std::array<ResponseSlot, SubIDCount> _responses;
|
||||
|
||||
std::vector<uint8_t> accessRegister(
|
||||
uint8_t sub_id, uint8_t address, const std::vector<uint8_t>& params);
|
||||
|
||||
void accessRegisterNoResponse(
|
||||
uint8_t sub_id, uint8_t address, const std::vector<uint8_t>& params);
|
||||
|
||||
protected:
|
||||
template <typename T, typename... Args>
|
||||
template<typename T, typename... Args>
|
||||
static std::shared_ptr<T> makeDerived(Args... args) {
|
||||
auto device = hidpp::Device::makeDerived<T>(std::forward<Args>(args)...);
|
||||
|
||||
@ -73,8 +82,9 @@ namespace logid::backend::hidpp10 {
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
template<typename... Args>
|
||||
static std::shared_ptr<Device> make(Args... args) {
|
||||
return makeDerived<Device>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ void Receiver::setNotifications(NotificationFlags flags) {
|
||||
}
|
||||
|
||||
void Receiver::enumerate() {
|
||||
setRegister(ConnectionState, {2}, hidpp::ReportType::Short);
|
||||
setRegisterNoResponse(ConnectionState, {2}, hidpp::ReportType::Short);
|
||||
}
|
||||
|
||||
///TODO: Investigate usage
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include <regex>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -32,12 +33,17 @@ extern "C"
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/hidraw.h>
|
||||
#include <linux/input.h>
|
||||
}
|
||||
|
||||
using namespace logid::backend::raw;
|
||||
using namespace logid::backend;
|
||||
using namespace std::chrono;
|
||||
|
||||
static constexpr int max_write_tries = 8;
|
||||
|
||||
static const std::regex virtual_path_regex(R"~((.*\/)(.*:)([0-9]+))~");
|
||||
|
||||
int get_fd(const std::string& path) {
|
||||
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1)
|
||||
@ -56,7 +62,37 @@ RawDevice::dev_info get_dev_info(int fd) {
|
||||
"RawDevice HIDIOCGRAWINFO failed");
|
||||
}
|
||||
|
||||
return {dev_info.vendor, dev_info.product};
|
||||
RawDevice::BusType type = RawDevice::OtherBus;
|
||||
|
||||
switch (dev_info.bustype) {
|
||||
case BUS_USB:
|
||||
type = RawDevice::USB;
|
||||
break;
|
||||
case BUS_BLUETOOTH:
|
||||
type = RawDevice::Bluetooth;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
.vid = dev_info.vendor,
|
||||
.pid = dev_info.product,
|
||||
.bus_type = type
|
||||
};
|
||||
}
|
||||
|
||||
std::string get_phys(int fd) {
|
||||
ssize_t len;
|
||||
char buf[256];
|
||||
if (-1 == (len = ::ioctl(fd, HIDIOCGRAWPHYS(sizeof(buf)), buf))) {
|
||||
int err = errno;
|
||||
::close(fd);
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice HIDIOCGRAWPHYS failed");
|
||||
}
|
||||
|
||||
return {buf, static_cast<size_t>(len) - 1};
|
||||
}
|
||||
|
||||
std::string get_name(int fd) {
|
||||
@ -68,7 +104,7 @@ std::string get_name(int fd) {
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"RawDevice HIDIOCGRAWNAME failed");
|
||||
}
|
||||
return {name_buf, static_cast<size_t>(len)};
|
||||
return {name_buf, static_cast<size_t>(len) - 1};
|
||||
}
|
||||
|
||||
RawDevice::RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& monitor) :
|
||||
@ -76,6 +112,12 @@ RawDevice::RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& mon
|
||||
_dev_info(get_dev_info(_fd)), _name(get_name(_fd)),
|
||||
_report_desc(getReportDescriptor(_fd)), _io_monitor(monitor->ioMonitor()),
|
||||
_event_handlers(std::make_shared<EventHandlerList<RawDevice>>()) {
|
||||
|
||||
if (busType() == USB) {
|
||||
auto phys = get_phys(_fd);
|
||||
_sub_device = std::regex_match(phys, virtual_path_regex);
|
||||
}
|
||||
|
||||
_io_monitor->add(_fd, {
|
||||
[this]() { _readReports(); },
|
||||
[this]() { _valid = false; },
|
||||
@ -105,6 +147,14 @@ int16_t RawDevice::productId() const {
|
||||
return _dev_info.pid;
|
||||
}
|
||||
|
||||
RawDevice::BusType RawDevice::busType() const {
|
||||
return _dev_info.bus_type;
|
||||
}
|
||||
|
||||
bool RawDevice::isSubDevice() const {
|
||||
return _sub_device;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> RawDevice::getReportDescriptor(const std::string& path) {
|
||||
int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1)
|
||||
@ -150,9 +200,13 @@ void RawDevice::sendReport(const std::vector<uint8_t>& report) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (write(_fd, report.data(), report.size()) == -1)
|
||||
throw std::system_error(errno, std::system_category(),
|
||||
"sendReport write failed");
|
||||
|
||||
for (int i = 0; i < max_write_tries && write(_fd, report.data(), report.size()) == -1; ++i) {
|
||||
auto err = errno;
|
||||
if (err != EPIPE)
|
||||
throw std::system_error(err, std::system_category(),
|
||||
"sendReport write failed");
|
||||
}
|
||||
}
|
||||
|
||||
EventHandlerLock<RawDevice> RawDevice::addEventHandler(RawEventHandler handler) {
|
||||
|
@ -39,9 +39,16 @@ namespace logid::backend::raw {
|
||||
static constexpr int max_data_length = 32;
|
||||
typedef RawEventHandler EventHandler;
|
||||
|
||||
enum BusType {
|
||||
USB,
|
||||
Bluetooth,
|
||||
OtherBus
|
||||
};
|
||||
|
||||
struct dev_info {
|
||||
int16_t vid;
|
||||
int16_t pid;
|
||||
BusType bus_type;
|
||||
};
|
||||
|
||||
RawDevice(std::string path, const std::shared_ptr<DeviceMonitor>& monitor);
|
||||
@ -57,6 +64,10 @@ namespace logid::backend::raw {
|
||||
|
||||
[[nodiscard]] int16_t productId() const;
|
||||
|
||||
[[nodiscard]] BusType busType() const;
|
||||
|
||||
[[nodiscard]] bool isSubDevice() const;
|
||||
|
||||
static std::vector<uint8_t> getReportDescriptor(const std::string& path);
|
||||
|
||||
static std::vector<uint8_t> getReportDescriptor(int fd);
|
||||
@ -80,6 +91,8 @@ namespace logid::backend::raw {
|
||||
|
||||
std::shared_ptr<IOMonitor> _io_monitor;
|
||||
|
||||
bool _sub_device = false;
|
||||
|
||||
std::shared_ptr<EventHandlerList<RawDevice>> _event_handlers;
|
||||
|
||||
void _handleEvent(const std::vector<uint8_t>& report);
|
||||
|
Loading…
Reference in New Issue
Block a user