Multiple things have been done in this commit; the base of the new backend has effectively been created. This branch currently has many vital parts commented out. Therefore, this branch is currently only intended for debugging.master
parent
526ffec61a
commit
ec4ae56bc4
20 changed files with 798 additions and 79 deletions
@ -0,0 +1,126 @@ |
||||
#include <thread> |
||||
#include <sstream> |
||||
|
||||
#include "DeviceMonitor.h" |
||||
#include "util.h" |
||||
|
||||
#define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded" |
||||
|
||||
using namespace logid; |
||||
using namespace logid::backend; |
||||
|
||||
/*
|
||||
void stopAndDeleteConnectedDevice (ConnectedDevice &connected_device) |
||||
{ |
||||
if(!connected_device.device->waiting_for_receiver) |
||||
log_printf(INFO, "%s (Device %d on %s) disconnected", connected_device.device->name.c_str(), |
||||
connected_device.device->index, connected_device.device->path.c_str()); |
||||
connected_device.device->stop(); |
||||
connected_device.associatedThread.join(); |
||||
delete(connected_device.device); |
||||
} |
||||
|
||||
DeviceMonitor::~DeviceMonitor() |
||||
{ |
||||
this->devices_mutex.lock(); |
||||
for (auto it = this->devices.begin(); it != this->devices.end(); it++) { |
||||
for (auto jt = it->second.begin(); jt != it->second.end(); jt++) { |
||||
stopAndDeleteConnectedDevice(jt->second); |
||||
} |
||||
} |
||||
this->devices_mutex.unlock(); |
||||
} |
||||
|
||||
Device* DeviceMonitor::insertNewDevice(const std::string &path, HIDPP::DeviceIndex index) |
||||
{ |
||||
auto device = new Device(path, index); |
||||
device->init(); |
||||
|
||||
this->devices_mutex.lock(); |
||||
log_printf(INFO, "%s detected: device %d on %s", device->name.c_str(), index, path.c_str()); |
||||
auto path_bucket = this->devices.emplace(path, std::map<HIDPP::DeviceIndex, ConnectedDevice>()).first; |
||||
path_bucket->second.emplace(index, ConnectedDevice{ |
||||
device, |
||||
std::thread([device]() { |
||||
device->start(); |
||||
}) |
||||
}); |
||||
this->devices_mutex.unlock(); |
||||
|
||||
return device; |
||||
} |
||||
|
||||
Device* DeviceMonitor::insertNewReceiverDevice(const std::string &path, HIDPP::DeviceIndex index) |
||||
{ |
||||
auto *device = new Device(path, index); |
||||
this->devices_mutex.lock(); |
||||
auto path_bucket = this->devices.emplace(path, std::map<HIDPP::DeviceIndex, ConnectedDevice>()).first; |
||||
path_bucket->second.emplace(index, ConnectedDevice{ |
||||
device, |
||||
std::thread([device]() { |
||||
device->waitForReceiver(); |
||||
}) |
||||
}); |
||||
this->devices_mutex.unlock(); |
||||
|
||||
return device; |
||||
} |
||||
|
||||
void DeviceMonitor::stopAndDeleteAllDevicesIn (const std::string &path) |
||||
{ |
||||
this->devices_mutex.lock(); |
||||
auto path_bucket = this->devices.find(path); |
||||
if (path_bucket != this->devices.end()) |
||||
{ |
||||
for (auto& index_bucket : path_bucket->second) { |
||||
stopAndDeleteConnectedDevice(index_bucket.second); |
||||
} |
||||
this->devices.erase(path_bucket); |
||||
} |
||||
this->devices_mutex.unlock(); |
||||
} |
||||
|
||||
void DeviceMonitor::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index) |
||||
{ |
||||
this->devices_mutex.lock(); |
||||
auto path_bucket = this->devices.find(path); |
||||
if (path_bucket != this->devices.end()) |
||||
{ |
||||
auto index_bucket = path_bucket->second.find(index); |
||||
if (index_bucket != path_bucket->second.end()) |
||||
{ |
||||
stopAndDeleteConnectedDevice(index_bucket->second); |
||||
path_bucket->second.erase(index_bucket); |
||||
} |
||||
} |
||||
this->devices_mutex.unlock(); |
||||
|
||||
log_printf(WARN, "Attempted to disconnect not previously connected device %d on %s", index, path.c_str()); |
||||
} |
||||
*/ |
||||
|
||||
void DeviceMonitor::addDevice(std::string path) |
||||
{ |
||||
try { |
||||
backend::hidpp::Device device(path, hidpp::DeviceIndex::DefaultDevice); |
||||
|
||||
log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); |
||||
} |
||||
catch(backend::hidpp::Device::InvalidDevice &e) |
||||
{ |
||||
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) |
||||
{ |
||||
log_printf(DEBUG, "Device %s disconnected", path.c_str()); |
||||
/*
|
||||
ipc_server->removeReceiver(path); |
||||
this->stopAndDeleteAllDevicesIn(std::string(path)); |
||||
*/ |
||||
} |
@ -0,0 +1,32 @@ |
||||
#include <array> |
||||
#include <algorithm> |
||||
#include "Report.h" |
||||
|
||||
using namespace logid::backend::dj; |
||||
using namespace logid::backend; |
||||
|
||||
static const std::array<uint8_t, 35> 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
|
||||
}; |
||||
|
||||
bool dj::supportsDjReports(std::vector<uint8_t>& rdesc) |
||||
{ |
||||
auto it = std::search(rdesc.begin(), rdesc.end(), DJReportDesc.begin(), DJReportDesc.end()); |
||||
return it != rdesc.end(); |
||||
} |
@ -0,0 +1,20 @@ |
||||
#ifndef LOGID_BACKEND_DJ_REPORT_H |
||||
#define LOGID_BACKEND_DJ_REPORT_H |
||||
|
||||
#include <cstdint> |
||||
#include "../raw/RawDevice.h" |
||||
|
||||
namespace logid::backend::dj |
||||
{ |
||||
bool supportsDjReports(std::vector<uint8_t>& rawDevice); |
||||
class Report |
||||
{ |
||||
enum Type: uint8_t |
||||
{ |
||||
Short = 0x20, // Short DJ reports use 12 byte parameters
|
||||
Long = 0x21 // Long DJ reports use 29 byte parameters
|
||||
}; |
||||
}; |
||||
} |
||||
|
||||
#endif //LOGID_BACKEND_DJ_REPORT_H
|
@ -0,0 +1,30 @@ |
||||
#include "Device.h" |
||||
#include "Report.h" |
||||
|
||||
using namespace logid::backend; |
||||
using namespace logid::backend::hidpp; |
||||
|
||||
const char* Device::InvalidDevice::what() const noexcept |
||||
{ |
||||
switch(_reason) |
||||
{ |
||||
case NoHIDPPReport: |
||||
return "Invalid HID++ device"; |
||||
case InvalidRawDevice: |
||||
return "Invalid raw device"; |
||||
} |
||||
} |
||||
|
||||
Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept |
||||
{ |
||||
return _reason; |
||||
} |
||||
|
||||
/// TODO: Initialize a single RawDevice for each path.
|
||||
Device::Device(std::string path, DeviceIndex index): |
||||
raw_device (std::make_shared<raw::RawDevice>(path)), path (path), index (index) |
||||
{ |
||||
supported_reports = getSupportedReports(raw_device->reportDescriptor()); |
||||
if(!supported_reports) |
||||
throw InvalidDevice(InvalidDevice::NoHIDPPReport); |
||||
} |
@ -0,0 +1,51 @@ |
||||
#ifndef LOGID_HIDPP_DEVICE_H |
||||
#define LOGID_HIDPP_DEVICE_H |
||||
|
||||
#include <string> |
||||
#include <memory> |
||||
#include "../raw/RawDevice.h" |
||||
|
||||
namespace logid::backend::hidpp |
||||
{ |
||||
enum DeviceIndex: uint8_t |
||||
{ |
||||
DefaultDevice = 0, |
||||
WirelessDevice1 = 1, |
||||
WirelessDevice2 = 2, |
||||
WirelessDevice3 = 3, |
||||
WirelessDevice4 = 4, |
||||
WirelessDevice5 = 5, |
||||
WirelessDevice6 = 6, |
||||
CordedDevice = 0xff |
||||
}; |
||||
|
||||
class Device |
||||
{ |
||||
public: |
||||
class InvalidDevice : std::exception |
||||
{ |
||||
public: |
||||
enum Reason |
||||
{ |
||||
NoHIDPPReport, |
||||
InvalidRawDevice |
||||
}; |
||||
InvalidDevice(Reason reason) : _reason (reason) {} |
||||
virtual const char *what() const noexcept; |
||||
virtual Reason code() const noexcept; |
||||
private: |
||||
Reason _reason; |
||||
|
||||
}; |
||||
Device(std::string path, DeviceIndex index); |
||||
std::string devicePath() const { return path; } |
||||
DeviceIndex deviceIndex() const { return index; } |
||||
private: |
||||
std::shared_ptr<logid::backend::raw::RawDevice> raw_device; |
||||
std::string path; |
||||
DeviceIndex index; |
||||
uint8_t supported_reports; |
||||
}; |
||||
} |
||||
|
||||
#endif //LOGID_HIDPP_DEVICE_H
|
@ -0,0 +1,83 @@ |
||||
#include <array> |
||||
#include <algorithm> |
||||
#include "Report.h" |
||||
|
||||
using namespace logid::backend::hidpp; |
||||
using namespace logid::backend; |
||||
|
||||
/* Report descriptors were sourced from cvuchener/hidpp */ |
||||
static const std::array<uint8_t, 22> ShortReportDesc = { |
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x10, // Report ID (16)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x06, // Report Count (6)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x01, // Usage (0001 - Vendor)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x01, // Usage (0001 - Vendor)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
}; |
||||
|
||||
static const std::array<uint8_t, 22> LongReportDesc = { |
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x11, // Report ID (17)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x13, // Report Count (19)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x02, // Usage (0002 - Vendor)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x02, // Usage (0002 - Vendor)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
}; |
||||
|
||||
/* Alternative versions from the G602 */ |
||||
static const std::array<uint8_t, 22> ShortReportDesc2 = { |
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x10, // Report ID (16)
|
||||
0x95, 0x06, // Report Count (6)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x01, // Usage (0001 - Vendor)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x01, // Usage (0001 - Vendor)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
}; |
||||
|
||||
static const std::array<uint8_t, 22> LongReportDesc2 = { |
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x11, // Report ID (17)
|
||||
0x95, 0x13, // Report Count (19)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x02, // Usage (0002 - Vendor)
|
||||
0x81, 0x00, // Input (Data, Array, Absolute)
|
||||
0x09, 0x02, // Usage (0002 - Vendor)
|
||||
0x91, 0x00, // Output (Data, Array, Absolute)
|
||||
0xC0 // End Collection
|
||||
}; |
||||
|
||||
uint8_t hidpp::getSupportedReports(std::vector<uint8_t>&& rdesc) |
||||
{ |
||||
uint8_t ret = 0; |
||||
|
||||
auto it = std::search(rdesc.begin(), rdesc.end(), ShortReportDesc.begin(), ShortReportDesc.end()); |
||||
if(it == rdesc.end()) |
||||
it = std::search(rdesc.begin(), rdesc.end(), ShortReportDesc2.begin(), ShortReportDesc2.end()); |
||||
if(it != rdesc.end()) |
||||
ret |= HIDPP_REPORT_SHORT_SUPPORTED; |
||||
|
||||
it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc.begin(), LongReportDesc2.end()); |
||||
if(it == rdesc.end()) |
||||
it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc2.begin(), LongReportDesc2.end()); |
||||
if(it != rdesc.end()) |
||||
ret |= HIDPP_REPORT_LONG_SUPPORTED; |
||||
|
||||
return ret; |
||||
} |
@ -0,0 +1,57 @@ |
||||
#ifndef LOGID_BACKEND_HIDPP_REPORT_H |
||||
#define LOGID_BACKEND_HIDPP_REPORT_H |
||||
|
||||
#include <cstdint> |
||||
#include "../raw/RawDevice.h" |
||||
#include "Device.h" |
||||
|
||||
#define LOGID_HIDPP_SW_ID 0x0f |
||||
|
||||
/* Some devices only support a subset of these reports */ |
||||
#define HIDPP_REPORT_SHORT_SUPPORTED 1U |
||||
#define HIDPP_REPORT_LONG_SUPPORTED 1U<<1U |
||||
/* Very long reports exist, however they have not been encountered so far */ |
||||
|
||||
namespace logid::backend::hidpp |
||||
{ |
||||
uint8_t getSupportedReports(std::vector<uint8_t>&& rdesc); |
||||
class Report |
||||
{ |
||||
public: |
||||
enum Type: uint8_t |
||||
{ |
||||
Short = 0x10, |
||||
Long = 0x11 |
||||
}; |
||||
|
||||
class InvalidReportID: std::exception |
||||
{ |
||||
InvalidReportID(); |
||||
virtual const char* what() const noexcept; |
||||
}; |
||||
|
||||
class InvalidReportLength: std::exception |
||||
{ |
||||
InvalidReportLength(); |
||||
virtual const char* what() const noexcept; |
||||
}; |
||||
|
||||
static constexpr std::size_t MaxDataLength = 32; |
||||
|
||||
Report(uint8_t report_id, const uint8_t* data, std::size_t length); |
||||
Report(std::vector<uint8_t> data); |
||||
|
||||
Type type() const; |
||||
void setType(Report::Type type); |
||||
|
||||
logid::backend::hidpp::DeviceIndex deviceIndex(); |
||||
|
||||
std::vector<uint8_t> rawReport () const { return _data; } |
||||
|
||||
private: |
||||
static constexpr std::size_t HeaderLength = 4; |
||||
std::vector<uint8_t> _data; |
||||
}; |
||||
} |
||||
|
||||
#endif //LOGID_BACKEND_HIDPP_REPORT_H
|
@ -0,0 +1,133 @@ |
||||
#include "DeviceMonitor.h" |
||||
|
||||
#include <thread> |
||||
#include <system_error> |
||||
|
||||
extern "C" |
||||
{ |
||||
#include <unistd.h> |
||||
#include <libudev.h> |
||||
} |
||||
|
||||
using namespace logid::backend::raw; |
||||
|
||||
DeviceMonitor::DeviceMonitor() |
||||
{ |
||||
if(-1 == pipe(monitor_pipe)) |
||||
throw std::system_error(errno, std::system_category(), "pipe creation failed"); |
||||
|
||||
udev_context = udev_new(); |
||||
if(!udev_context) |
||||
throw std::runtime_error("udev_new failed"); |
||||
} |
||||
|
||||
DeviceMonitor::~DeviceMonitor() |
||||
{ |
||||
bool is_running = running.try_lock(); |
||||
if(is_running) |
||||
running.unlock(); |
||||
else |
||||
this->stop(); |
||||
|
||||
udev_unref(udev_context); |
||||
|
||||
for(int i : monitor_pipe) |
||||
close(i); |
||||
} |
||||
|
||||
void DeviceMonitor::run() |
||||
{ |
||||
int ret; |
||||
const std::lock_guard<std::mutex> run_lock(running); |
||||
|
||||
struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev_context, "udev"); |
||||
if(!monitor) |
||||
throw std::runtime_error("udev_monitor_new_from_netlink failed"); |
||||
|
||||
ret = udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw", nullptr); |
||||
if (0 != ret) |
||||
throw std::system_error (-ret, std::system_category (), |
||||
"udev_monitor_filter_add_match_subsystem_devtype"); |
||||
|
||||
ret = udev_monitor_enable_receiving(monitor); |
||||
if(0 != ret) |
||||
throw std::system_error(-ret, std::system_category(), |
||||
"udev_moniotr_enable_receiving"); |
||||
|
||||
this->enumerate(); |
||||
|
||||
int fd = udev_monitor_get_fd(monitor); |
||||
while (true) { |
||||
fd_set fds; |
||||
FD_ZERO(&fds); |
||||
FD_SET(monitor_pipe[0], &fds); |
||||
FD_SET(fd, &fds); |
||||
if (-1 == select (std::max (monitor_pipe[0], fd)+1, &fds, nullptr, nullptr, nullptr)) { |
||||
if (errno == EINTR) |
||||
continue; |
||||
throw std::system_error (errno, std::system_category(), "udev_monitor select"); |
||||
} |
||||
if (FD_ISSET(fd, &fds)) { |
||||
struct udev_device *device = udev_monitor_receive_device(monitor); |
||||
std::string action = udev_device_get_action(device); |
||||
std::string devnode = udev_device_get_devnode(device); |
||||
if (action == "add") |
||||
std::thread([this](const std::string name) { |
||||
this->addDevice(name); |
||||
}, devnode).detach(); |
||||
else if (action == "remove") |
||||
std::thread([this](const std::string name) { |
||||
this->removeDevice(name); |
||||
}, devnode).detach(); |
||||
udev_device_unref (device); |
||||
} |
||||
if (FD_ISSET(monitor_pipe[0], &fds)) { |
||||
char c; |
||||
if (-1 == read(monitor_pipe[0], &c, sizeof (char))) |
||||
throw std::system_error (errno, std::system_category (), |
||||
"read pipe"); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void DeviceMonitor::stop() |
||||
{ |
||||
|
||||
} |
||||
|
||||
void DeviceMonitor::enumerate() |
||||
{ |
||||
int ret; |
||||
struct udev_enumerate* udev_enum = udev_enumerate_new(udev_context); |
||||
ret = udev_enumerate_add_match_subsystem(udev_enum, "hidraw"); |
||||
if(0 != ret) |
||||
throw std::system_error(-ret, std::system_category(), |
||||
"udev_enumerate_add_match_subsystem"); |
||||
|
||||
ret = udev_enumerate_scan_devices(udev_enum); |
||||
if(0 != ret) |
||||
throw std::system_error(-ret, std::system_category(), |
||||
"udev_enumerate_scan_devices"); |
||||
|
||||
struct udev_list_entry* udev_enum_entry; |
||||
udev_list_entry_foreach(udev_enum_entry, |
||||
udev_enumerate_get_list_entry(udev_enum)) |
||||
{ |
||||
const char* name = udev_list_entry_get_name(udev_enum_entry); |
||||
|
||||
struct udev_device* device = udev_device_new_from_syspath(udev_context, |
||||
name); |
||||
if(!device) |
||||
throw std::runtime_error("udev_device_new_from_syspath failed"); |
||||
|
||||
std::string devnode = udev_device_get_devnode(device); |
||||
udev_device_unref(device); |
||||
|
||||
std::thread([this](const std::string name) { |
||||
this->addDevice(name); |
||||
}, devnode).detach(); |
||||
} |
||||
|
||||
udev_enumerate_unref(udev_enum); |
||||
} |
@ -0,0 +1,32 @@ |
||||
#ifndef LOGID_BACKEND_RAW_DEVICEMONITOR_H |
||||
#define LOGID_BACKEND_RAW_DEVICEMONITOR_H |
||||
|
||||
#include <string> |
||||
#include <mutex> |
||||
|
||||
extern "C" |
||||
{ |
||||
#include <libudev.h> |
||||
} |
||||
|
||||
namespace logid::backend::raw |
||||
{ |
||||
class DeviceMonitor |
||||
{ |
||||
public: |
||||
void enumerate(); |
||||
void run(); |
||||
void stop(); |
||||
protected: |
||||
DeviceMonitor(); |
||||
~DeviceMonitor(); |
||||
virtual void addDevice(std::string device) = 0; |
||||
virtual void removeDevice(std::string device) = 0; |
||||
private: |
||||
struct udev* udev_context; |
||||
int monitor_pipe[2]; |
||||
std::mutex running; |
||||
}; |
||||
} |
||||
|
||||
#endif //LOGID_BACKEND_RAW_DEVICEMONITOR_H
|
@ -0,0 +1,147 @@ |
||||
#include "RawDevice.h" |
||||
|
||||
#include <string> |
||||
#include <system_error> |
||||
#include <utility> |
||||
|
||||
|
||||
extern "C" |
||||
{ |
||||
#include <unistd.h> |
||||
#include <fcntl.h> |
||||
#include <sys/ioctl.h> |
||||
#include <linux/hidraw.h> |
||||
} |
||||
|
||||
using namespace logid::backend::raw; |
||||
using namespace std::chrono; |
||||
|
||||
RawDevice::RawDevice(std::string path) : path (path) |
||||
{ |
||||
int ret; |
||||
|
||||
fd = ::open(path.c_str(), O_RDWR); |
||||
if (fd == -1) |
||||
throw std::system_error(errno, std::system_category(), "RawDevice open failed"); |
||||
|
||||
hidraw_devinfo devinfo{}; |
||||
if (-1 == ::ioctl(fd, HIDIOCGRAWINFO, &devinfo)) |
||||
{ |
||||
int err = errno; |
||||
::close(fd); |
||||
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWINFO failed"); |
||||
} |
||||
vid = devinfo.vendor; |
||||
pid = devinfo.product; |
||||
|
||||
char name_buf[256]; |
||||
if (-1 == (ret = ::ioctl(fd, HIDIOCGRAWNAME(sizeof(name_buf)), name_buf))) |
||||
{ |
||||
int err = errno; |
||||
::close(fd); |
||||
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWNAME failed"); |
||||
} |
||||
name.assign(name_buf, ret - 1); |
||||
|
||||
hidraw_report_descriptor _rdesc{}; |
||||
if (-1 == ::ioctl(fd, HIDIOCGRDESCSIZE, &_rdesc.size)) |
||||
{ |
||||
int err = errno; |
||||
::close(fd); |
||||
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESCSIZE failed"); |
||||
} |
||||
if (-1 == ::ioctl(fd, HIDIOCGRDESC, &_rdesc)) |
||||
{ |
||||
int err = errno; |
||||
::close(fd); |
||||
throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESC failed"); |
||||
} |
||||
rdesc = std::vector<uint8_t>(_rdesc.value, _rdesc.value + _rdesc.size); |
||||
|
||||
if (-1 == ::pipe(dev_pipe)) |
||||
{ |
||||
int err = errno; |
||||
close(fd); |
||||
throw std::system_error(err, std::system_category(), "RawDevice pipe open failed"); |
||||
} |
||||
} |
||||
|
||||
RawDevice::~RawDevice() |
||||
{ |
||||
if(fd != -1) |
||||
{ |
||||
::close(fd); |
||||
::close(dev_pipe[0]); |
||||
::close(dev_pipe[1]); |
||||
} |
||||
} |
||||
|
||||
void RawDevice::sendReport(std::vector<uint8_t> report) |
||||
{ |
||||
_sendReport(std::move(report)); |
||||
} |
||||
|
||||
std::vector<uint8_t> RawDevice::readReport(std::size_t maxDataLength) |
||||
{ |
||||
return _readReport(maxDataLength); |
||||
} |
||||
|
||||
void RawDevice::_sendReport(std::vector<uint8_t> report) |
||||
{ |
||||
std::lock_guard<std::mutex> lock(dev_io); |
||||
int ret = ::write(fd, report.data(), report.size()); |
||||
if(ret == -1) |
||||
throw std::system_error(errno, std::system_category(), "_sendReport write failed"); |
||||
} |
||||
|
||||
std::vector<uint8_t> RawDevice::_readReport(std::size_t maxDataLength) |
||||
{ |
||||
std::lock_guard<std::mutex> lock(dev_io); |
||||
int ret; |
||||
std::vector<uint8_t> report(maxDataLength); |
||||
|
||||
timeval timeout = { duration_cast<milliseconds>(HIDPP_IO_TIMEOUT).count(), |
||||
duration_cast<microseconds>(HIDPP_IO_TIMEOUT).count() }; |
||||
|
||||
fd_set fds; |
||||
do { |
||||
FD_ZERO(&fds); |
||||
FD_SET(fd, &fds); |
||||
FD_SET(dev_pipe[0], &fds); |
||||
|
||||
ret = select(std::max(fd, dev_pipe[0]) + 1, |
||||
&fds, nullptr, nullptr, |
||||
(HIDPP_IO_TIMEOUT.count() > 0 ? nullptr : &timeout)); |
||||
} while(ret == -1 && errno == EINTR); |
||||
|
||||
if(ret == -1) |
||||
throw std::system_error(errno, std::system_category(), "_readReport select failed"); |
||||
|
||||
if(FD_ISSET(fd, &fds)) |
||||
{ |
||||
ret = read(fd, report.data(), report.size()); |
||||
if(ret == -1) |
||||
throw std::system_error(errno, std::system_category(), "_readReport read failed"); |
||||
report.resize(ret); |
||||
} |
||||
|
||||
if(FD_ISSET(dev_pipe[0], &fds)) |
||||
{ |
||||
char c; |
||||
ret = read(dev_pipe[0], &c, sizeof(char)); |
||||
if(ret == -1) |
||||
throw std::system_error(errno, std::system_category(), "_readReport read pipe failed"); |
||||
} |
||||
|
||||
return report; |
||||
} |
||||
|
||||
void RawDevice::interruptRead() |
||||
{ |
||||
char c = 0; |
||||
if(-1 == write(dev_pipe[1], &c, sizeof(char))) |
||||
throw std::system_error(errno, std::system_category(), "interruptRead write pipe failed"); |
||||
|
||||
// Ensure I/O has halted
|
||||
std::lock_guard<std::mutex> lock(dev_io); |
||||
} |
@ -0,0 +1,41 @@ |
||||
#ifndef LOGID_BACKEND_RAWDEVICE_H |
||||
#define LOGID_BACKEND_RAWDEVICE_H |
||||
|
||||
#include <string> |
||||
#include <vector> |
||||
#include <mutex> |
||||
|
||||
#define HIDPP_IO_TIMEOUT std::chrono::seconds(2) |
||||
|
||||
namespace logid::backend::raw |
||||
{ |
||||
class RawDevice |
||||
{ |
||||
public: |
||||
RawDevice(std::string path); |
||||
~RawDevice(); |
||||
std::string hidrawPath() const { return path; } |
||||
std::vector<uint8_t> reportDescriptor() const { return rdesc; } |
||||
|
||||
/// TODO: Process reports in a queue.
|
||||
void sendReport(std::vector<uint8_t> report); |
||||
std::vector<uint8_t> readReport(std::size_t maxDataLength); |
||||
|
||||
void interruptRead(); |
||||
private: |
||||
std::mutex dev_io; |
||||
std::string path; |
||||
int fd; |
||||
int dev_pipe[2]; |
||||
uint16_t vid; |
||||
uint16_t pid; |
||||
std::string name; |
||||
std::vector<uint8_t> rdesc; |
||||
|
||||
/* These will only be used internally and processed with a queue */ |
||||
void _sendReport(std::vector<uint8_t> report); |
||||
std::vector<uint8_t> _readReport(std::size_t maxDataLength); |
||||
}; |
||||
} |
||||
|
||||
#endif //LOGID_BACKEND_RAWDEVICE_H
|
Loading…
Reference in new issue