You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

183 lines
5.8 KiB

/*
* Copyright 2019-2020 PixlOne
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "DeviceMonitor.h"
#include "../../util/task.h"
#include "../../util/log.h"
#include "RawDevice.h"
#include "../hidpp/Device.h"
#include <thread>
#include <system_error>
extern "C"
{
#include <unistd.h>
#include <libudev.h>
}
using namespace logid::backend::raw;
DeviceMonitor::DeviceMonitor()
{
if(-1 == pipe(_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()
{
this->stop();
udev_unref(_udev_context);
for(int i : _pipe)
close(i);
}
void DeviceMonitor::run()
{
int ret;
std::lock_guard<std::mutex> 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);
_run_monitor = true;
while (_run_monitor) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(_pipe[0], &fds);
FD_SET(fd, &fds);
if (-1 == select (std::max (_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")
task::spawn([this, name=devnode]() {
auto supported_reports = backend::hidpp::getSupportedReports(
RawDevice::getReportDescriptor(name));
if(supported_reports)
this->addDevice(name);
else
logPrintf(DEBUG, "Unsupported device %s ignored",
name.c_str());
}, [name=devnode](std::exception& e){
logPrintf(WARN, "Error adding device %s: %s",
name.c_str(), e.what());
});
else if (action == "remove")
task::spawn([this, name=devnode]() {
this->removeDevice(name);
}, [name=devnode](std::exception& e){
logPrintf(WARN, "Error removing device %s: %s",
name.c_str(), e.what());
});
udev_device_unref (device);
}
if (FD_ISSET(_pipe[0], &fds)) {
char c;
if (-1 == read(_pipe[0], &c, sizeof (char)))
throw std::system_error (errno, std::system_category(),
"read pipe");
break;
}
}
}
void DeviceMonitor::stop()
{
_run_monitor = false;
std::lock_guard<std::mutex> lock(_running);
}
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);
task::spawn([this, name=devnode]() {
auto supported_reports = backend::hidpp::getSupportedReports(
RawDevice::getReportDescriptor(name));
if(supported_reports)
this->addDevice(name);
else
logPrintf(DEBUG, "Unsupported device %s ignored",
name.c_str());
}, [name=devnode](std::exception& e){
logPrintf(WARN, "Error adding device %s: %s",
name.c_str(), e.what());
});
}
udev_enumerate_unref(udev_enum);
}