diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 89d2422..4ce4d1f 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -15,9 +15,12 @@ add_executable(logid backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp + backend/dj/Receiver.cpp + backend/dj/Error.cpp backend/hidpp/Device.cpp backend/hidpp/Report.cpp backend/hidpp10/Error.cpp + backend/hidpp10/Device.cpp backend/hidpp20/Device.cpp backend/hidpp20/Error.cpp backend/hidpp20/Feature.cpp diff --git a/src/logid/DeviceMonitor.cpp b/src/logid/DeviceMonitor.cpp index 90b4cdd..a24aec4 100644 --- a/src/logid/DeviceMonitor.cpp +++ b/src/logid/DeviceMonitor.cpp @@ -4,6 +4,7 @@ #include "DeviceMonitor.h" #include "util.h" #include "backend/hidpp10/Error.h" +#include "backend/dj/Receiver.h" #define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded" @@ -103,12 +104,26 @@ void DeviceMonitor::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceI void DeviceMonitor::addDevice(std::string path) { try { - auto device = std::make_shared(path, hidpp::DeviceIndex::WirelessDevice1); - log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); + std::tuple version; + try + { + hidpp::Device device(path, hidpp::DefaultDevice); - auto version = device->version(); - log_printf(DEBUG, "HID++ version: %d.%d", std::get<0>(version), std::get<1>(version)); + log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); + version = device.version(); + log_printf(DEBUG, "HID++ version: %d.%d", std::get<0>(version), std::get<1>(version)); + } catch(std::system_error &e) { } + + if(version == std::make_tuple(1, 0)) + { + // This is a receiver + dj::Receiver receiver(path); + receiver.enumerate(); + receiver.listen(); + while(true) {} + } + /* auto eventHandler = std::make_shared(); eventHandler->condition = [device](backend::hidpp::Report& report)->bool { @@ -125,9 +140,9 @@ void DeviceMonitor::addDevice(std::string path) device->addEventHandler("MONITOR_ALL", eventHandler); - devices.push_back(device); + devices.push_back(device);*/ - std::thread([device]() { device->listen(); }).detach(); + //std::thread([device]() { device->listen(); }).detach(); } catch(hidpp10::Error &e) { @@ -139,10 +154,11 @@ void DeviceMonitor::addDevice(std::string path) { log_printf(DEBUG, "Detected device at %s but %s", path.c_str(), e.what()); } + /* catch(std::system_error &e) { log_printf(WARN, "Failed to open %s: %s", path.c_str(), e.what()); - } + } */ } void DeviceMonitor::removeDevice(std::string path) diff --git a/src/logid/backend/dj/Error.cpp b/src/logid/backend/dj/Error.cpp new file mode 100644 index 0000000..d3a4dbc --- /dev/null +++ b/src/logid/backend/dj/Error.cpp @@ -0,0 +1,21 @@ +#include "Error.h" + +using namespace logid::backend::dj; + +Error::Error(uint8_t code) : _code (code) +{ +} + +const char* Error::what() const noexcept +{ + switch(_code) + { + case Unknown: + return "Unknown"; + case KeepAliveTimeout: + return "Keep-alive timeout"; + default: + return std::string("Reserved error code " + std::to_string(_code)) + .c_str(); + } +} \ No newline at end of file diff --git a/src/logid/backend/dj/Error.h b/src/logid/backend/dj/Error.h new file mode 100644 index 0000000..f462377 --- /dev/null +++ b/src/logid/backend/dj/Error.h @@ -0,0 +1,30 @@ +#ifndef LOGID_HIDPP_BACKEND_DJ_ERROR_H +#define LOGID_HIDPP_BACKEND_DJ_ERROR_H + +#include +#include + +namespace logid { +namespace backend { +namespace dj +{ + class Error : public std::exception + { + public: + enum ErrorCode : uint8_t + { + Unknown = 0x00, + KeepAliveTimeout = 0x01 + }; + + Error(uint8_t code); + + virtual const char* what() const noexcept; + uint8_t code() const noexcept; + + private: + uint8_t _code; + }; +}}} + +#endif //LOGID_HIDPP_BACKEND_DJ_ERROR_H diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp new file mode 100644 index 0000000..7d5aef4 --- /dev/null +++ b/src/logid/backend/dj/Receiver.cpp @@ -0,0 +1,255 @@ +#include +#include "Report.h" +#include "Receiver.h" +#include "Error.h" + +using namespace logid::backend::dj; +using namespace logid::backend; + +InvalidReceiver::InvalidReceiver(Reason reason) : _reason (reason) +{ +} + +const char* InvalidReceiver::what() const noexcept +{ + return "Invalid receiver"; +} + +InvalidReceiver::Reason InvalidReceiver::code() const noexcept +{ + return _reason; +} + +Receiver::Receiver(std::string path) : + raw_device (std::make_shared(path)), + _hidpp10_device (raw_device, hidpp::DefaultDevice) +{ + if(!supportsDjReports(raw_device->reportDescriptor())) + throw InvalidReceiver(InvalidReceiver::NoDJReports); + + + // Pass all HID++ events on DefaultDevice to handleHidppEvent + RawEventHandler hidppRawEventHandler; + hidppRawEventHandler.condition = [this](std::vector& report)->bool + { + return (report[hidpp::Offset::Type] == hidpp::Report::Type::Short || + report[hidpp::Offset::Type] == hidpp::Report::Type::Long) && + (report[hidpp::Offset::DeviceIndex] == hidpp::DefaultDevice); + }; + hidppRawEventHandler.callback = [this](std::vector& report)->void + { + hidpp::Report _report(report); + this->handleHidppEvent(_report); + }; + + // Pass all DJ events with device index to handleHidppEvent + RawEventHandler djRawEventHandler; + djRawEventHandler.condition = [this](std::vector& report)->bool + { + return (report[Offset::Type] == Report::Type::Short || + report[Offset::Type] == Report::Type::Long); + }; + djRawEventHandler.callback = [this](std::vector& report)->void + { + Report _report(report); + this->handleDjEvent(_report); + }; + + raw_device->addEventHandler("RECV_HIDPP", hidppRawEventHandler); + raw_device->addEventHandler("RECV_DJ", djRawEventHandler); +} + +void Receiver::enumerate() +{ + sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{}); +} + +Receiver::notification_flags Receiver::getHidppNotifications() +{ + auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {}); + + notification_flags flags{}; + + flags.device_battery_status = response[0] & (1 << 4); + flags.receiver_wireless_notifications = response[1] & (1 << 0); + flags.receiver_software_present = response[1] & (1 << 3); +} + +void Receiver::enableHidppNotifications(notification_flags flags) +{ + std::vector request(3); + + if(flags.device_battery_status) + request[0] |= (1 << 4); + if(flags.receiver_wireless_notifications) + request[1] |= (1 << 0); + if(flags.receiver_software_present) + request[1] |= (1 << 3); + + _hidpp10_device.setRegister(EnableHidppNotifications, request); +} + +///TODO: Investigate usage +uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index) +{ + auto response = _hidpp10_device.setRegister(ConnectionState, {index}); + + return response[0]; +} + +void Receiver::startPairing(uint8_t timeout) +{ + ///TODO: Device number == Device index? + std::vector request(3); + + request[0] = 1; + request[1] = hidpp::DefaultDevice; + request[2] = timeout; + + _hidpp10_device.setRegister(DevicePairing, request); +} + +void Receiver::stopPairing() +{ + ///TODO: Device number == Device index? + std::vector request(3); + + request[0] = 2; + request[1] = hidpp::DefaultDevice; + + _hidpp10_device.setRegister(DevicePairing, request); +} + +void Receiver::disconnect(hidpp::DeviceIndex index) +{ + ///TODO: Device number == Device index? + std::vector request(3); + + request[0] = 3; + request[1] = index; + + _hidpp10_device.setRegister(DevicePairing, request); +} + +std::map Receiver::getDeviceActivity() +{ + auto response = _hidpp10_device.getRegister(DeviceActivity, {}); + + std::map device_activity; + for(uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++) + device_activity[static_cast(i)] = response[i]; + + return device_activity; +} + +Receiver::pairing_info Receiver::getPairingInfo(hidpp::DeviceIndex index) +{ + std::vector request(1); + request[0] = index; + request[0] += 0x19; + + auto response = _hidpp10_device.getRegister(PairingInfo, request); + + pairing_info info{}; + info.destination_id = response[0]; + info.report_interval = response[1]; + info.pid = response[2]; + info.pid |= (response[3] << 8); + info.device_type = response[6]; + + return info; +} + +Receiver::extended_pairing_info + Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index) +{ + std::vector request(1); + request[0] = index; + request[0] += 0x29; + + auto response = _hidpp10_device.getRegister(PairingInfo, request); + + extended_pairing_info info{}; + + info.serial_number = 0; + for(uint8_t i = 0; i < 4; i++) + info.serial_number |= (response[i] << 8*i); + + for(uint8_t i = 0; i < 4; i++) + info.report_types[i] = response[i + 4]; + + info.power_switch_location = response[8] & 0xf; + + return info; +} + +std::string Receiver::getDeviceName(hidpp::DeviceIndex index) +{ + std::vector request(1); + request[0] = index; + request[0] += 0x39; + + auto response = _hidpp10_device.getRegister(PairingInfo, request); + + uint8_t size = response[0]; + assert(size <= 14); + + std::string name(size, ' '); + for(std::size_t i = 0; i < size; i++) + name[i] = response[i + 1]; + + return name; +} + +hidpp::DeviceIndex Receiver::deviceConnectionEvent(hidpp::Report &report) +{ + assert(report.subId() == DeviceConnection); + return report.deviceIndex(); +} + +hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(hidpp::Report& report) +{ + assert(report.subId() == DeviceDisconnection); + return report.deviceIndex(); +} + +void Receiver::handleDjEvent(Report& report) +{ + if(report.feature() == DeviceConnection || + report.feature() == DeviceDisconnection || + report.feature() == ConnectionStatus) + { + printf("%s DJ IN: ", raw_device->hidrawPath().c_str()); + for(auto &i: report.rawData()) + printf("%02x ", i); + printf("\n"); + } +} + +void Receiver::handleHidppEvent(hidpp::Report &report) +{ + printf("%s HID++ IN: ", raw_device->hidrawPath().c_str()); + for(auto &i: report.rawReport()) + printf("%02x ", i); + printf("\n"); +} + +void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function, + const std::vector&& params) +{ + assert(params.size() <= LongParamLength); + + Report::Type type = params.size() <= ShortParamLength ? + ReportType::Short : ReportType::Long; + + Report request(type, index, function); + + std::copy(params.begin(), params.end(), request.paramBegin()); + + raw_device->sendReportNoResponse(request.rawData()); +} + +void Receiver::listen() +{ + std::thread{[=]() { raw_device->listen(); }}.detach(); +} \ No newline at end of file diff --git a/src/logid/backend/dj/Receiver.h b/src/logid/backend/dj/Receiver.h new file mode 100644 index 0000000..24ffa19 --- /dev/null +++ b/src/logid/backend/dj/Receiver.h @@ -0,0 +1,125 @@ +#ifndef LOGID_BACKEND_DJ_RECEIVER_H +#define LOGID_BACKEND_DJ_RECEIVER_H + +#include +#include "../raw/RawDevice.h" +#include "Report.h" +#include "../hidpp/Report.h" +#include "../hidpp10/Device.h" + +namespace logid { +namespace backend { +namespace dj +{ + class InvalidReceiver : public std::exception + { + public: + enum Reason + { + NoDJReports + }; + explicit InvalidReceiver(Reason reason); + virtual const char* what() const noexcept; + Reason code() const noexcept; + private: + Reason _reason; + }; + + class Receiver + { + public: + explicit Receiver(std::string path); + + enum DjEvents : uint8_t + { + DeviceDisconnection = 0x40, + DeviceConnection = 0x41, + ConnectionStatus = 0x42 + }; + + enum DjCommands : uint8_t + { + SwitchAndKeepAlive = 0x80, + GetPairedDevices = 0x81 + }; + + void enumerate(); + + /* The following functions deal with HID++ 1.0 features. + * While these are not technically DJ functions, it is redundant + * to have a separate hidpp10::Receiver class for these functions. + */ + + enum HidppEvents : uint8_t + { + // These events are identical to their DJ counterparts + // DeviceDisconnection = 0x40, + // DeviceConnection = 0x41, + LockingChange = 0x4a + }; + + enum HidppRegisters : uint8_t + { + EnableHidppNotifications = 0x00, + ConnectionState = 0x02, + DevicePairing = 0xb2, + DeviceActivity = 0xb3, + PairingInfo = 0xb5 + }; + + struct notification_flags + { + bool device_battery_status; + bool receiver_wireless_notifications; + bool receiver_software_present; + }; + + notification_flags getHidppNotifications(); + void enableHidppNotifications(notification_flags flags); + + ///TODO: Understand output of this function + uint8_t getConnectionState(hidpp::DeviceIndex index); + + void startPairing(uint8_t timeout = 0); + void stopPairing(); + void disconnect(hidpp::DeviceIndex index); + + std::map getDeviceActivity(); + + struct pairing_info + { + uint8_t destination_id; + uint8_t report_interval; + uint16_t pid; + uint8_t device_type; ///TODO: Create enum for DeviceType + }; + + struct extended_pairing_info + { + uint32_t serial_number; + uint8_t report_types[4]; + uint8_t power_switch_location; ///TODO: Create enum + }; + + pairing_info getPairingInfo(hidpp::DeviceIndex index); + extended_pairing_info getExtendedPairingInfo(hidpp::DeviceIndex index); + + std::string getDeviceName(hidpp::DeviceIndex index); + + hidpp::DeviceIndex deviceConnectionEvent(hidpp::Report& report); + hidpp::DeviceIndex deviceDisconnectionEvent(hidpp::Report& report); + + void handleDjEvent(dj::Report& report); + void handleHidppEvent(hidpp::Report& report); + + void listen(); + private: + void sendDjRequest(hidpp::DeviceIndex index, uint8_t function, + const std::vector&& params); + + std::shared_ptr raw_device; + hidpp10::Device _hidpp10_device; + }; +}}} + +#endif //LOGID_BACKEND_DJ_RECEIVER_H \ No newline at end of file diff --git a/src/logid/backend/dj/Report.cpp b/src/logid/backend/dj/Report.cpp index b8cccb3..0dff2fd 100644 --- a/src/logid/backend/dj/Report.cpp +++ b/src/logid/backend/dj/Report.cpp @@ -1,11 +1,12 @@ #include #include +#include #include "Report.h" using namespace logid::backend::dj; using namespace logid::backend; -static const std::array DJReportDesc = { +static const std::array DJReportDesc = { 0xA1, 0x01, // Collection (Application) 0x85, 0x20, // Report ID (32) 0x95, 0x0E, // Report Count (14) @@ -25,8 +26,68 @@ static const std::array DJReportDesc = { 0xC0 // End Collection }; -bool dj::supportsDjReports(std::vector& rdesc) +bool dj::supportsDjReports(std::vector&& rdesc) { auto it = std::search(rdesc.begin(), rdesc.end(), DJReportDesc.begin(), DJReportDesc.end()); return it != rdesc.end(); -} \ No newline at end of file +} + +Report::Report(std::vector& 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(_data[Offset::Type]); +} + +hidpp::DeviceIndex Report::index() const +{ + return static_cast(_data[Offset::DeviceIndex]); +} + +uint8_t Report::feature() const +{ + return _data[Offset::Feature]; +} + +std::vector::iterator Report::paramBegin() +{ + return _data.begin() + Offset::Parameters; +} + +std::vector Report::rawData() const +{ + return _data; +} diff --git a/src/logid/backend/dj/Report.h b/src/logid/backend/dj/Report.h index c4078f4..6f01bb7 100644 --- a/src/logid/backend/dj/Report.h +++ b/src/logid/backend/dj/Report.h @@ -3,17 +3,35 @@ #include #include "../raw/RawDevice.h" +#include "defs.h" +#include "../hidpp/defs.h" namespace logid::backend::dj { - bool supportsDjReports(std::vector& rawDevice); + namespace Offset + { + static constexpr uint8_t Type = 0; + static constexpr uint8_t DeviceIndex = 1; + static constexpr uint8_t Feature = 2; + static constexpr uint8_t Parameters = 3; + } + + bool supportsDjReports(std::vector&& rdesc); class Report { - enum Type: uint8_t - { - Short = 0x20, // Short DJ reports use 12 byte parameters - Long = 0x21 // Long DJ reports use 29 byte parameters - }; + public: + typedef ReportType::ReportType Type; + + explicit Report(std::vector& data); + Report(Type type, hidpp::DeviceIndex index, uint8_t feature); + + Type type() const; + hidpp::DeviceIndex index() const; + uint8_t feature() const; + std::vector::iterator paramBegin(); + std::vector rawData() const; + private: + std::vector _data; }; } diff --git a/src/logid/backend/dj/defs.h b/src/logid/backend/dj/defs.h index 3f373c2..5c0124e 100644 --- a/src/logid/backend/dj/defs.h +++ b/src/logid/backend/dj/defs.h @@ -15,6 +15,12 @@ namespace dj Long = 0x21 }; } + + static constexpr uint8_t ErrorFeature = 0x7f; + + static constexpr std::size_t HeaderLength = 3; + static constexpr std::size_t ShortParamLength = 12; + static constexpr std::size_t LongParamLength = 29; }}} #endif //LOGID_BACKEND_DJ_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 6134475..4994aef 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -26,7 +26,19 @@ Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept /// TODO: Initialize a single RawDevice for each path. Device::Device(const std::string& path, DeviceIndex index): - raw_device (std::make_shared(path)), path (path), index (index) + raw_device (std::make_shared(path)), path (path), + _index (index) +{ + _init(); +} + +Device::Device(std::shared_ptr raw_device, DeviceIndex index) : + raw_device (raw_device), _index (index) +{ + _init(); +} + +void Device::_init() { supported_reports = getSupportedReports(raw_device->reportDescriptor()); if(!supported_reports) @@ -34,8 +46,9 @@ Device::Device(const std::string& path, DeviceIndex index): try { - Report versionRequest(Report::Type::Short, index, hidpp20::FeatureID::ROOT, - hidpp20::Root::Ping, LOGID_HIDPP_SOFTWARE_ID); + Report versionRequest(Report::Type::Short, _index, + hidpp20::FeatureID::ROOT,hidpp20::Root::Ping, + LOGID_HIDPP_SOFTWARE_ID); auto versionResponse = sendReport(versionRequest); auto versionResponse_params = versionResponse.paramBegin(); @@ -53,10 +66,11 @@ Device::Device(const std::string& path, DeviceIndex index): // Pass all HID++ events with device index to this device. RawEventHandler rawEventHandler; - rawEventHandler.condition = [index](std::vector& report)->bool + rawEventHandler.condition = [this](std::vector& report)->bool { return (report[Offset::Type] == Report::Type::Short || - report[Offset::Type] == Report::Type::Long) && (report[Offset::DeviceIndex] == index); + report[Offset::Type] == Report::Type::Long) && + (report[Offset::DeviceIndex] == this->_index); }; rawEventHandler.callback = [this](std::vector& report)->void { @@ -64,7 +78,15 @@ Device::Device(const std::string& path, DeviceIndex index): this->handleEvent(_report); }; - raw_device->addEventHandler("DEV_" + std::to_string(index), rawEventHandler); + raw_device->addEventHandler("DEV_" + std::to_string(_index), rawEventHandler); +} + +Device::~Device() +{ + raw_device->removeEventHandler("DEV_" + std::to_string(_index)); + ///TODO: tmp + raw_device->stopListener(); + raw_device.reset(); } void Device::addEventHandler(const std::string& nickname, const std::shared_ptr& handler) diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 0e91b76..fc5215d 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -37,9 +37,11 @@ namespace hidpp }; Device(const std::string& path, DeviceIndex index); + Device(std::shared_ptr raw_device, DeviceIndex index); + ~Device(); std::string devicePath() const { return path; } - DeviceIndex deviceIndex() const { return index; } + DeviceIndex deviceIndex() const { return _index; } std::tuple version() const { return _version; } void listen(); // Runs asynchronously @@ -52,9 +54,11 @@ namespace hidpp void handleEvent(Report& report); private: + void _init(); + std::shared_ptr raw_device; std::string path; - DeviceIndex index; + DeviceIndex _index; uint8_t supported_reports; std::tuple _version; diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index 99e32ea..ac8b173 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -95,6 +95,27 @@ const char *Report::InvalidReportLength::what() const noexcept return "Invalid report length"; } +Report::Report(Report::Type type, DeviceIndex device_index, + uint8_t sub_id, uint8_t address) +{ + switch(type) + { + case Type::Short: + _data.resize(HeaderLength + ShortParamLength); + break; + case Type::Long: + _data.resize(HeaderLength + LongParamLength); + break; + default: + throw InvalidReportID(); + } + + _data[Offset::Type] = type; + _data[Offset::DeviceIndex] = device_index; + _data[Offset::SubID] = sub_id; + _data[Offset::Address] = address; +} + Report::Report(Report::Type type, DeviceIndex device_index, uint8_t feature_index, uint8_t function, uint8_t sw_id) { @@ -123,6 +144,7 @@ Report::Report(const std::vector& data) { _data = data; + // Truncating data is entirely valid here. switch(_data[Offset::Type]) { case Type::Short: @@ -136,6 +158,11 @@ Report::Report(const std::vector& data) } } +Report::Type Report::type() const +{ + return static_cast(_data[Offset::Type]); +} + void Report::setType(Report::Type type) { switch(type) @@ -153,6 +180,58 @@ void Report::setType(Report::Type type) _data[Offset::Type] = type; } +uint8_t Report::feature() const +{ + return _data[Offset::Feature]; +} + +void Report::setFeature(uint8_t feature) +{ + _data[Offset::Parameters] = feature; +} + +uint8_t Report::subId() const +{ + return _data[Offset::SubID]; +} + +void Report::setSubId(uint8_t sub_id) +{ + _data[Offset::SubID] = sub_id; +} + +uint8_t Report::function() const +{ + return (_data[Offset::Function] >> 4) & 0x0f; +} + +void Report::setFunction(uint8_t function) +{ + _data[Offset::Function] &= 0x0f; + _data[Offset::Function] |= (function << 4) & 0x0f; +} + +uint8_t Report::swId() const +{ + return _data[Offset::Function] & 0x0f; +} + +void Report::setSwId(uint8_t sub_id) +{ + _data[Offset::Function] &= 0x0f; + _data[Offset::Function] |= sub_id & 0x0f; +} + +uint8_t Report::address() const +{ + return _data[Offset::Address]; +} + +void Report::setAddress(uint8_t address) +{ + _data[Offset::Address] = address; +} + void Report::setParams(const std::vector& _params) { assert(_params.size() <= _data.size()-HeaderLength); diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index 929f783..8945cf1 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -20,6 +20,7 @@ namespace logid::backend::hidpp static constexpr uint8_t DeviceIndex = 1; static constexpr uint8_t SubID = 2; static constexpr uint8_t Feature = 2; + static constexpr uint8_t Address = 3; static constexpr uint8_t Function = 3; static constexpr uint8_t Parameters = 4; } @@ -47,16 +48,37 @@ namespace logid::backend::hidpp static constexpr uint8_t swIdMask = 0x0f; static constexpr uint8_t functionMask = 0x0f; + Report(Report::Type type, DeviceIndex device_index, + uint8_t sub_id, + uint8_t address); Report(Report::Type type, DeviceIndex device_index, uint8_t feature_index, uint8_t function, uint8_t sw_id); explicit Report(const std::vector& data); - Report::Type type() const { return static_cast(_data[Offset::Type]); }; + Report::Type type() const; void setType(Report::Type type); - std::vector::iterator paramBegin() { return _data.begin() + Offset::Parameters; } + uint8_t feature() const; + void setFeature(uint8_t feature); + + uint8_t subId() const; + void setSubId(uint8_t sub_id); + + uint8_t function() const; + void setFunction(uint8_t function); + + uint8_t swId() const; + void setSwId(uint8_t sw_id); + + uint8_t address() const; + void setAddress(uint8_t address); + + std::vector::iterator paramBegin() + { + return _data.begin() + Offset::Parameters; + } std::vector::iterator paramEnd() { return _data.end(); } void setParams(const std::vector& _params); diff --git a/src/logid/backend/hidpp10/Device.cpp b/src/logid/backend/hidpp10/Device.cpp new file mode 100644 index 0000000..b69e579 --- /dev/null +++ b/src/logid/backend/hidpp10/Device.cpp @@ -0,0 +1,62 @@ +#include +#include +#include "Device.h" +#include "defs.h" + +using namespace logid::backend; +using namespace logid::backend::hidpp10; + +Device::Device(const std::string &path, hidpp::DeviceIndex index) : + hidpp::Device(path, index) +{ + assert(version() == std::make_tuple(1, 0)); +} + +Device::Device(std::shared_ptr raw_dev, + hidpp::DeviceIndex index) : hidpp::Device(std::move(raw_dev), index) +{ + assert(version() == std::make_tuple(1, 0)); +} + +std::vector Device::getRegister(uint8_t address, + const std::vector& params) +{ + assert(params.size() <= hidpp::LongParamLength); + + hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? + hidpp::Report::Type::Short : hidpp::Report::Type::Long; + + uint8_t sub_id = type == hidpp::Report::Type::Short ? + GetRegisterShort : GetRegisterLong; + + return accessRegister(sub_id, address, params); +} + +std::vector Device::setRegister(uint8_t address, + const std::vector& params) +{ + assert(params.size() <= hidpp::LongParamLength); + + hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? + hidpp::Report::Type::Short : hidpp::Report::Type::Long; + + uint8_t sub_id = type == hidpp::Report::Type::Short ? + SetRegisterShort : SetRegisterLong; + + return accessRegister(sub_id, address, params); +} + +std::vector Device::accessRegister(uint8_t sub_id, uint8_t address, const std::vector ¶ms) +{ + hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? + hidpp::Report::Type::Short : hidpp::Report::Type::Long; + + hidpp::Report request(type, deviceIndex(), sub_id, address); + std::copy(params.begin(), params.end(), request.paramBegin()); + + auto response = sendReport(request); + return std::vector(response.paramBegin(), response.paramEnd()); +} + + + diff --git a/src/logid/backend/hidpp10/Device.h b/src/logid/backend/hidpp10/Device.h new file mode 100644 index 0000000..76da044 --- /dev/null +++ b/src/logid/backend/hidpp10/Device.h @@ -0,0 +1,28 @@ +#ifndef LOGID_BACKEND_HIDPP10_DEVICE_H +#define LOGID_BACKEND_HIDPP10_DEVICE_H + +#include "../hidpp/Device.h" + +namespace logid { +namespace backend { +namespace hidpp10 +{ + class Device : public hidpp::Device + { + public: + Device(const std::string& path, hidpp::DeviceIndex index); + Device(std::shared_ptr raw_dev, + hidpp::DeviceIndex index); + + std::vector getRegister(uint8_t address, + const std::vector& params); + + std::vector setRegister(uint8_t address, + const std::vector& params); + private: + std::vector accessRegister(uint8_t sub_id, + uint8_t address, const std::vector& params); + }; +}}} + +#endif //LOGID_BACKEND_HIDPP10_DEVICE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp10/defs.h b/src/logid/backend/hidpp10/defs.h new file mode 100644 index 0000000..1d080f1 --- /dev/null +++ b/src/logid/backend/hidpp10/defs.h @@ -0,0 +1,17 @@ +#ifndef LOGID_BACKEND_HIDPP10_DEFS_H +#define LOGID_BACKEND_HIDPP10_DEFS_H + +namespace logid { +namespace backend { +namespace hidpp10 +{ + enum SubID: uint8_t + { + SetRegisterShort = 0x80, + GetRegisterShort = 0x81, + SetRegisterLong = 0x82, + GetRegisterLong = 0x83 + }; +}}} + +#endif //LOGID_BACKEND_HIDPP10_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index eeabeb5..d7b9b60 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -75,6 +75,8 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false) close(fd); throw std::system_error(err, std::system_category(), "RawDevice pipe open failed"); } + + continue_listen = false; } RawDevice::~RawDevice() @@ -106,6 +108,27 @@ std::vector RawDevice::sendReport(const std::vector& report) return _respondToReport(report); } +// DJ commands are not systematically acknowledged, do not expect a result. +void RawDevice::sendReportNoResponse(const std::vector& report) +{ + assert(supportedReportID(report[0])); + + /* If the listener will stop, handle I/O manually. + * Otherwise, push to queue and wait for result. */ + if(continue_listen) + { + std::packaged_task()> task([this, report]() { + this->_sendReport(report); + return std::vector(); + }); + auto f = task.get_future(); + write_queue.push(&task); + f.get(); + } + else + _sendReport(report); +} + std::vector RawDevice::_respondToReport (const std::vector& request) { diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index 8e78003..1eebeb0 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -29,6 +29,7 @@ namespace raw std::vector reportDescriptor() const { return rdesc; } std::vector sendReport(const std::vector& report); + void sendReportNoResponse(const std::vector& report); void interruptRead(); void listen();