Implement raw DeviceMonitor
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.
This commit is contained in:
parent
526ffec61a
commit
ec4ae56bc4
|
@ -11,66 +11,29 @@ find_package(PkgConfig REQUIRED)
|
|||
add_executable(logid
|
||||
logid.cpp
|
||||
util.cpp
|
||||
util.h
|
||||
Configuration.cpp
|
||||
Configuration.h
|
||||
Actions.cpp
|
||||
Actions.h
|
||||
Device.cpp
|
||||
Device.h
|
||||
DeviceFinder.cpp
|
||||
DeviceFinder.h
|
||||
EvdevDevice.cpp
|
||||
EvdevDevice.h)
|
||||
DeviceMonitor.cpp
|
||||
backend/raw/DeviceMonitor.cpp
|
||||
backend/raw/RawDevice.cpp
|
||||
backend/hidpp/Device.cpp
|
||||
backend/hidpp/Report.cpp
|
||||
backend/dj/Report.cpp)
|
||||
|
||||
set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
pkg_check_modules(PC_EVDEV libevdev)
|
||||
pkg_check_modules(PC_EVDEV libevdev REQUIRED)
|
||||
pkg_check_modules(SYSTEMD "systemd")
|
||||
pkg_check_modules(LIBCONFIG libconfig)
|
||||
|
||||
find_path(HIDPP_INCLUDE_DIR hidpp)
|
||||
find_library(HIDPP_LIBRARY libhidpp.so)
|
||||
pkg_check_modules(LIBCONFIG libconfig REQUIRED)
|
||||
pkg_check_modules(LIBUDEV libudev REQUIRED)
|
||||
|
||||
find_path(EVDEV_INCLUDE_DIR libevdev/libevdev.h
|
||||
HINTS ${PC_EVDEV_INCLUDE_DIRS} ${PC_EVDEV_INCLUDEDIR})
|
||||
find_library(EVDEV_LIBRARY
|
||||
NAMES evdev libevdev)
|
||||
|
||||
if((NOT HIDPP_INCLUDE_DIR) OR (NOT EXISTS ${HIDPP_INCLUDE_DIR}) OR (NOT HIDPP_LIBRARY) OR FORCE_BUILD_HIDPP)
|
||||
message("Could not find libhidpp include dir, getting submodule")
|
||||
include_directories(${HIDPP_INCLUDE_DIR} ${EVDEV_INCLUDE_DIR} ${DBUSCXX_INCLUDE_DIR} ${LIBUDEV_INCLUDE_DIRECTORIES})
|
||||
|
||||
execute_process(COMMAND git submodule update --init -- hidpp
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
set(DEFAULT_HID_BACKEND "linux")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
set(DEFAULT_HID_BACKEND "windows")
|
||||
else()
|
||||
message(WARNING "System is not supported")
|
||||
endif()
|
||||
|
||||
set(HID_BACKEND "${DEFAULT_HID_BACKEND}" CACHE STRING "Backend used for accessing HID devices")
|
||||
set_property(CACHE HID_BACKEND PROPERTY STRINGS linux windows)
|
||||
|
||||
if("${HID_BACKEND}" STREQUAL "linux")
|
||||
pkg_check_modules(LIBUDEV libudev REQUIRED)
|
||||
elseif("${HID_BACKEND}" STREQUAL "windows")
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
add_definitions(-D_WIN32_WINNT=0x0600) # Use vista or later
|
||||
else()
|
||||
message(FATAL_ERROR "HID_BACKEND is invalid.")
|
||||
endif()
|
||||
|
||||
add_subdirectory(hidpp/src/libhidpp)
|
||||
|
||||
set(HIDPP_INCLUDE_DIR "hidpp/src/libhidpp/")
|
||||
set(HIDPP_LIBRARY hidpp)
|
||||
else()
|
||||
set(HIDPP_INCLUDE_DIR ${HIDPP_INCLUDE_DIR}/hidpp)
|
||||
endif()
|
||||
|
||||
include_directories(${HIDPP_INCLUDE_DIR} ${EVDEV_INCLUDE_DIR})
|
||||
target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++
|
||||
${DBUSCXX_LIBRARIES} ${LIBUDEV_LIBRARIES})
|
||||
|
||||
target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ ${HIDPP_LIBRARY})
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define LOGID_DEVICE_H
|
||||
|
||||
#include "Actions.h"
|
||||
#include "DeviceFinder.h"
|
||||
#include "DeviceMonitor.h"
|
||||
#include "Configuration.h"
|
||||
|
||||
#include <map>
|
||||
|
|
126
src/logid/DeviceMonitor.cpp
Normal file
126
src/logid/DeviceMonitor.cpp
Normal file
|
@ -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));
|
||||
*/
|
||||
}
|
|
@ -1,16 +1,12 @@
|
|||
#ifndef LOGID_DEVICEFINDER_H
|
||||
#define LOGID_DEVICEFINDER_H
|
||||
#ifndef LOGID_DEVICEMONITOR_H
|
||||
#define LOGID_DEVICEMONITOR_H
|
||||
|
||||
#include <hid/DeviceMonitor.h>
|
||||
#include <hidpp/SimpleDispatcher.h>
|
||||
#include <hidpp10/Device.h>
|
||||
#include <hidpp10/IReceiver.h>
|
||||
#include <hidpp20/IReprogControls.h>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include "Device.h"
|
||||
#include "backend/raw/DeviceMonitor.h"
|
||||
#include "backend/hidpp/Device.h"
|
||||
|
||||
#define MAX_CONNECTION_TRIES 10
|
||||
#define TIME_BETWEEN_CONNECTION_TRIES 500ms
|
||||
|
@ -25,24 +21,26 @@ namespace logid
|
|||
std::thread associatedThread;
|
||||
};
|
||||
|
||||
class DeviceFinder : public HID::DeviceMonitor
|
||||
class DeviceMonitor : public backend::raw::DeviceMonitor
|
||||
{
|
||||
public:
|
||||
~DeviceFinder();
|
||||
~DeviceMonitor();
|
||||
|
||||
/*
|
||||
Device* insertNewDevice (const std::string &path, HIDPP::DeviceIndex index);
|
||||
Device* insertNewReceiverDevice (const std::string &path, HIDPP::DeviceIndex index);
|
||||
void stopAndDeleteAllDevicesIn (const std::string &path);
|
||||
void stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index);
|
||||
*/
|
||||
protected:
|
||||
void addDevice(const char* path);
|
||||
void removeDevice(const char* path);
|
||||
void addDevice(std::string path) override;
|
||||
void removeDevice(std::string path) override;
|
||||
private:
|
||||
std::mutex devices_mutex;
|
||||
std::map<std::string, std::map<HIDPP::DeviceIndex, ConnectedDevice>> devices;
|
||||
std::map<std::string, std::map<backend::hidpp::DeviceIndex, ConnectedDevice>> devices;
|
||||
};
|
||||
|
||||
extern DeviceFinder* finder;
|
||||
extern DeviceMonitor* finder;
|
||||
}
|
||||
|
||||
#endif //LOGID_DEVICEFINDER_H
|
32
src/logid/backend/dj/Report.cpp
Normal file
32
src/logid/backend/dj/Report.cpp
Normal file
|
@ -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();
|
||||
}
|
20
src/logid/backend/dj/Report.h
Normal file
20
src/logid/backend/dj/Report.h
Normal file
|
@ -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
|
30
src/logid/backend/hidpp/Device.cpp
Normal file
30
src/logid/backend/hidpp/Device.cpp
Normal file
|
@ -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);
|
||||
}
|
51
src/logid/backend/hidpp/Device.h
Normal file
51
src/logid/backend/hidpp/Device.h
Normal file
|
@ -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
src/logid/backend/hidpp/Error.cpp
Normal file
0
src/logid/backend/hidpp/Error.cpp
Normal file
0
src/logid/backend/hidpp/Error.h
Normal file
0
src/logid/backend/hidpp/Error.h
Normal file
83
src/logid/backend/hidpp/Report.cpp
Normal file
83
src/logid/backend/hidpp/Report.cpp
Normal file
|
@ -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;
|
||||
}
|
57
src/logid/backend/hidpp/Report.h
Normal file
57
src/logid/backend/hidpp/Report.h
Normal file
|
@ -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
|
133
src/logid/backend/raw/DeviceMonitor.cpp
Normal file
133
src/logid/backend/raw/DeviceMonitor.cpp
Normal file
|
@ -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);
|
||||
}
|
32
src/logid/backend/raw/DeviceMonitor.h
Normal file
32
src/logid/backend/raw/DeviceMonitor.h
Normal file
|
@ -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
|
147
src/logid/backend/raw/RawDevice.cpp
Normal file
147
src/logid/backend/raw/RawDevice.cpp
Normal file
|
@ -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);
|
||||
}
|
41
src/logid/backend/raw/RawDevice.h
Normal file
41
src/logid/backend/raw/RawDevice.h
Normal file
|
@ -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
|
|
@ -4,12 +4,7 @@
|
|||
#include <mutex>
|
||||
|
||||
#include "util.h"
|
||||
#include "Device.h"
|
||||
#include "Actions.h"
|
||||
#include "Configuration.h"
|
||||
#include "EvdevDevice.h"
|
||||
#include "DeviceFinder.h"
|
||||
#include "IPCServer.h"
|
||||
#include "DeviceMonitor.h"
|
||||
#include "logid.h"
|
||||
|
||||
#define evdev_name "logid"
|
||||
|
@ -25,9 +20,8 @@ using namespace logid;
|
|||
std::string config_file = DEFAULT_CONFIG_FILE;
|
||||
|
||||
LogLevel logid::global_verbosity = INFO;
|
||||
Configuration* logid::global_config;
|
||||
EvdevDevice* logid::global_evdev;
|
||||
DeviceFinder* logid::finder;
|
||||
// Configuration* logid::global_config;
|
||||
DeviceMonitor* logid::finder;
|
||||
|
||||
bool logid::kill_logid = false;
|
||||
std::mutex logid::finder_reloading;
|
||||
|
@ -41,6 +35,7 @@ enum class Option
|
|||
Version
|
||||
};
|
||||
|
||||
/*
|
||||
void logid::reload()
|
||||
{
|
||||
log_printf(INFO, "Reloading logid...");
|
||||
|
@ -50,9 +45,10 @@ void logid::reload()
|
|||
global_config = new Configuration(config_file.c_str());
|
||||
delete(old_config);
|
||||
delete(finder);
|
||||
finder = new DeviceFinder();
|
||||
finder = new DeviceMonitor();
|
||||
finder_reloading.unlock();
|
||||
}
|
||||
*/
|
||||
|
||||
void read_cli_options(int argc, char** argv)
|
||||
{
|
||||
|
@ -153,10 +149,12 @@ int main(int argc, char** argv)
|
|||
{
|
||||
read_cli_options(argc, argv);
|
||||
|
||||
/*
|
||||
// Read config
|
||||
try { global_config = new Configuration(config_file.c_str()); }
|
||||
catch (std::exception &e) { global_config = new Configuration(); }
|
||||
|
||||
|
||||
//Create an evdev device called 'logid'
|
||||
try { global_evdev = new EvdevDevice(evdev_name); }
|
||||
catch(std::system_error& e)
|
||||
|
@ -165,8 +163,10 @@ int main(int argc, char** argv)
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// Scan devices, create listeners, handlers, etc.
|
||||
finder = new DeviceFinder();
|
||||
finder = new DeviceMonitor();
|
||||
|
||||
while(!kill_logid)
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
namespace logid
|
||||
{
|
||||
void reload();
|
||||
// void reload();
|
||||
|
||||
extern bool kill_logid;
|
||||
extern std::mutex finder_reloading;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <cstdarg>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
|
@ -34,6 +35,7 @@ const char* logid::level_prefix(LogLevel level)
|
|||
return "DEBUG";
|
||||
}
|
||||
|
||||
/*
|
||||
Direction logid::getDirection(int x, int y)
|
||||
{
|
||||
if(x == 0 && y == 0) return Direction::None;
|
||||
|
@ -111,6 +113,8 @@ Action logid::stringToAction(std::string s)
|
|||
throw std::invalid_argument(original_str + " is an invalid action.");
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
LogLevel logid::stringToLogLevel(std::string s)
|
||||
{
|
||||
std::string original_str = s;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef LOGID_UTIL_H
|
||||
#define LOGID_UTIL_H
|
||||
|
||||
#include "Actions.h"
|
||||
//#include "Actions.h"
|
||||
|
||||
namespace logid
|
||||
{
|
||||
|
@ -19,10 +19,12 @@ namespace logid
|
|||
|
||||
const char* level_prefix(LogLevel level);
|
||||
|
||||
/*
|
||||
Direction getDirection(int x, int y);
|
||||
Direction stringToDirection(std::string s);
|
||||
GestureMode stringToGestureMode(std::string s);
|
||||
Action stringToAction(std::string s);
|
||||
*/
|
||||
LogLevel stringToLogLevel(std::string s);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user