Skip to content

Commit

Permalink
Start implementing udev monitoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Eeems committed Jul 1, 2023
1 parent e687472 commit c142944
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 16 deletions.
2 changes: 1 addition & 1 deletion applications/system-service/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ void Application::showSplashScreen(){
EPFrameBuffer::sendUpdate(textRect, EPFrameBuffer::Grayscale, EPFrameBuffer::PartialUpdate, true);
painter.end();
});
qDebug() << "Waitng for screen to finish...";
qDebug() << "Waiting for screen to finish...";
Oxide::Sentry::sentry_span(t, "wait", "Wait for screen finish updating", [](){
EPFrameBuffer::waitForLastUpdate();
});
Expand Down
46 changes: 34 additions & 12 deletions applications/system-service/powerapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define BATTERYAPI_H

#include <liboxide.h>
#include <liboxide/udev.h>

#include <QObject>
#include <QDebug>
Expand Down Expand Up @@ -46,25 +47,45 @@ class PowerAPI : public APIBase {
Oxide::Sentry::sentry_span(t, "update", "Update current state", [this]{
update();
});
Oxide::Sentry::sentry_span(t, "timer", "Setup timer", [this]{
timer = new QTimer(this);
timer->setSingleShot(false);
timer->setInterval(3 * 1000); // 3 seconds
timer->moveToThread(qApp->thread());
connect(timer, &QTimer::timeout, this, QOverload<>::of(&PowerAPI::update));
timer->start();
Oxide::Sentry::sentry_span(t, "monitor", "Setup monitor", [this]{
if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){
udev = new UDev(this);
udev->addMonitor("power_supply", NULL);
connect(udev, &UDev::event, this, QOverload<>::of(&PowerAPI::update));
udev->start();
}else{
timer = new QTimer(this);
timer->setSingleShot(false);
timer->setInterval(3 * 1000); // 3 seconds
timer->moveToThread(qApp->thread());
connect(timer, &QTimer::timeout, this, QOverload<>::of(&PowerAPI::update));
timer->start();
}
});
});
}
~PowerAPI(){
qDebug() << "Killing timer";
timer->stop();
delete timer;
if(timer != nullptr){
qDebug() << "Killing timer";
timer->stop();
delete timer;
}
if(udev != nullptr){
qDebug() << "Killing UDev monitor";
udev->stop();
delete udev;
}
}

void setEnabled(bool enabled) override {
if(enabled){
timer->start();
if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){
udev->start();
}else{
timer->start();
}
}else if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1){
udev->stop();
}else{
timer->stop();
}
Expand Down Expand Up @@ -152,7 +173,8 @@ class PowerAPI : public APIBase {
void chargerWarning();

private:
QTimer* timer;
QTimer* timer = nullptr;
UDev* udev = nullptr;
int m_state = Normal;
int m_batteryState = BatteryUnknown;
int m_batteryLevel = 0;
Expand Down
1 change: 1 addition & 0 deletions shared/liboxide/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* \brief Log a debug message if compiled with DEBUG mode, and debugging is enabled
* \param msg Debug message to log
*/
#define DEBUG
#ifdef DEBUG
#define O_DEBUG(msg) if(Oxide::debugEnabled()){ qDebug() << msg; }
#else
Expand Down
8 changes: 5 additions & 3 deletions shared/liboxide/liboxide.pro
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ SOURCES += \
settingsfile.cpp \
slothandler.cpp \
sysobject.cpp \
signalhandler.cpp
signalhandler.cpp \
udev.cpp

HEADERS += \
applications.h \
Expand All @@ -40,7 +41,8 @@ HEADERS += \
settingsfile.h \
slothandler.h \
sysobject.h \
signalhandler.h
signalhandler.h \
udev.h

PRECOMPILED_HEADER = \
liboxide_stable.h
Expand All @@ -59,7 +61,7 @@ DBUS_INTERFACES += \
../../interfaces/notificationapi.xml \
../../interfaces/notification.xml

LIBS += -lsystemd
LIBS += -lsystemd -ludev

liboxide_liboxide_h.target = include/liboxide/liboxide.h
liboxide_liboxide_h.commands = \
Expand Down
193 changes: 193 additions & 0 deletions shared/liboxide/udev.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
#include "udev.h"
#include "debug.h"

#include <fcntl.h>
#include <cerrno>

#include <QtConcurrent/QtConcurrentRun>
#include <QThread>

namespace Oxide {

UDev::UDev(QObject *parent) : QObject(), _thread(this){
qRegisterMetaType<Device>("UDev::Device");
udevLib = udev_new();
connect(parent, &QObject::destroyed, this, &QObject::deleteLater);
connect(&_thread, &QThread::started, this, &UDev::run);
connect(&_thread, &QThread::finished, [this]{
running = false;
});
moveToThread(&_thread);
}

UDev::~UDev(){
if(udevLib != nullptr){
udev_unref(udevLib);
udevLib = nullptr;
}
}

void UDev::start(){
if(running){
return;
}
wait();
qDebug() << "UDev::Starting...";
running = true;
_thread.start();
qDebug() << "UDev::Started";
}

void UDev::stop(){
qDebug() << "UDev::Stopping...";
if(running){
running = false;
wait();
}
qDebug() << "UDev::Stopped";
}

bool UDev::isRunning(){ return running || _thread.isRunning(); }

void UDev::wait(){
qDebug() << "UDev::Waiting...";
while(isRunning()){
thread()->yieldCurrentThread();
}
qDebug() << "UDev::Waited";
}

void UDev::addMonitor(QString subsystem, QString deviceType){
QStringList* list;
if(monitors.contains(subsystem)){
list = monitors[subsystem];
}else{
list = new QStringList();
monitors.insert(subsystem, list);
}
if(!list->contains(deviceType)){
list->append(deviceType);
}
}
void UDev::removeMonitor(QString subsystem, QString deviceType){
if(monitors.contains(subsystem)){
monitors[subsystem]->removeAll(deviceType);
}
}

QList<UDev::Device> UDev::getDeviceList(const QString& subsystem){
QList<Device> deviceList;
struct udev_enumerate* udevEnumeration = udev_enumerate_new(udevLib);
if(udevEnumeration == nullptr){
return deviceList;
}
udev_enumerate_add_match_subsystem(udevEnumeration, subsystem.toUtf8().constData());
udev_enumerate_scan_devices(udevEnumeration);
struct udev_list_entry* udevDeviceList = udev_enumerate_get_list_entry(udevEnumeration);
if(udevDeviceList != nullptr){
struct udev_list_entry* entry = nullptr;
udev_list_entry_foreach(entry, udevDeviceList){
if(entry == nullptr){
continue;
}
const char* path = udev_list_entry_get_name(entry);
if(path == nullptr){
continue;
}
Device device;
struct udev_device* udevDevice = udev_device_new_from_syspath(udevLib, path);
device.action = getActionType(udevDevice);
device.path = path;
device.subsystem = subsystem;
device.deviceType = QString(udev_device_get_devtype(udevDevice));
udev_device_unref(udevDevice);
deviceList.append(device);
}
}
udev_enumerate_unref(udevEnumeration);
return deviceList;
}

UDev::ActionType UDev::getActionType(udev_device* udevDevice){
if(udevDevice == nullptr){
return Unknown;
}
return getActionType(QString(udev_device_get_action(udevDevice)).trimmed().toUpper());
}

UDev::ActionType UDev::getActionType(const QString& actionType){
if(actionType == "ADD"){
return Add;
}
if(actionType == "REMOVE"){
return Remove;
}
if(actionType == "Change"){
return Change;
}
if(actionType == "OFFLINE"){
return Offline;
}
if(actionType == "ONLINE"){
return Online;
}
return Unknown;
}

void UDev::run(){
O_DEBUG("UDev::Monitor starting");
udev_monitor* mon = udev_monitor_new_from_netlink(udevLib, "udev");
if(!mon){
O_WARNING("UDev::Monitor Unable to listen to UDev: Failed to create netlink monitor");
O_DEBUG(strerror(errno))
return;
}
for(QString subsystem : monitors.keys()){
for(QString deviceType : *monitors[subsystem]){
O_DEBUG("UDev::Monitor " << subsystem << deviceType);
int err = udev_monitor_filter_add_match_subsystem_devtype(
mon,
subsystem.toUtf8().constData(),
deviceType.isNull() || deviceType.isEmpty() ? deviceType.toUtf8().constData() : NULL
);
if(err < 0){
O_WARNING("UDev::Monitor Unable to add filter: " << strerror(err));
}
}
}
int err = udev_monitor_enable_receiving(mon);
if(err < 0){
O_WARNING("UDev::Monitor Unable to listen to UDev:" << strerror(err));
udev_monitor_unref(mon);
return;
}
while(running){
udev_device* dev = udev_monitor_receive_device(mon);
if(dev != nullptr){
Device device;
device.action = getActionType(dev);
device.path = QString(udev_device_get_devnode(dev));
device.subsystem = QString(udev_device_get_subsystem(dev));
device.deviceType = QString(udev_device_get_devtype(dev));
udev_device_unref(dev);
O_DEBUG("UDev::Monitor UDev event" << device);
emit event(device);
}else if(errno && errno != EAGAIN){
O_WARNING("UDev::Monitor error checking event:" << strerror(errno));
}
auto timestamp = QDateTime::currentMSecsSinceEpoch();
QThread::yieldCurrentThread();
if(QDateTime::currentMSecsSinceEpoch() - timestamp < 30){
QThread::msleep(30);
}
}
O_DEBUG("UDev::Monitor stopping");
udev_monitor_unref(mon);
}
QDebug operator<<(QDebug debug, const UDev::Device& device){
QDebugStateSaver saver(debug);
Q_UNUSED(saver)
debug.nospace() << device.debugString().c_str();
return debug.maybeSpace();
}
}
83 changes: 83 additions & 0 deletions shared/liboxide/udev.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*!
* \addtogroup Oxide
* @{
* \file
*/
#pragma once

#include "liboxide_global.h"

#include <libudev.h>

#include <QObject>

using namespace std;

namespace Oxide {
class UDev : public QObject {
Q_OBJECT

public:
static void ensureMaxThreads();
explicit UDev(QObject *parent = nullptr);
~UDev();

enum ActionType {
Add,
Remove,
Change,
Online,
Offline,
Unknown
};
struct Device {
QString subsystem;
QString deviceType;
QString path;
ActionType action = Unknown;
QString actionString() const {
switch(action){
case Add:
return "ADD";
case Remove:
return "REMOVE";
case Change:
return "CHANGE";
case Online:
return "ONLINE";
case Offline:
return "OFFLINE";
case Unknown:
default:
return "UNKNOWN";
}
}
string debugString() const {
return QString("<Device %1/%2 %3>").arg(subsystem, deviceType, actionString()).toStdString();
}
};
void start();
void stop();
bool isRunning();
void wait();
void addMonitor(QString subsystem, QString deviceType);
void removeMonitor(QString subsystem, QString deviceType);
QList<Device> getDeviceList(const QString& subsystem);
ActionType getActionType(udev_device* udevDevice);
ActionType getActionType(const QString& actionType);

signals:
void event(Device device);

private:
struct udev* udevLib = nullptr;
bool running = false;
QMap<QString, QStringList*> monitors;
QThread _thread;

protected:
void run();
};
QDebug operator<<(QDebug debug, const UDev::Device& device);
}
Q_DECLARE_METATYPE(Oxide::UDev::Device)

0 comments on commit c142944

Please sign in to comment.