Solid

udevmanager.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Rafael Fernández López <ereslibre@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "udevmanager.h"
8
9#include "../shared/rootdevice.h"
10#include "udev.h"
11#include "udevdevice.h"
12
13#include <QDebug>
14#include <QFile>
15#include <QSet>
16
17using namespace Solid::Backends::UDev;
18using namespace Solid::Backends::Shared;
19
20class UDevManager::Private
21{
22public:
23 Private();
24 ~Private();
25
26 bool isOfInterest(const QString &udi, const UdevQt::Device &device);
27 bool checkOfInterest(const UdevQt::Device &device);
28
29 UdevQt::Client *m_client;
30 QStringList m_devicesOfInterest;
31 QSet<Solid::DeviceInterface::Type> m_supportedInterfaces;
32};
33
34UDevManager::Private::Private()
35{
36 QStringList subsystems;
37 subsystems << "processor";
38 subsystems << "cpu";
39 subsystems << "sound";
40 subsystems << "tty";
41 subsystems << "dvb";
42 subsystems << "net";
43 subsystems << "usb";
44 subsystems << "input";
45 m_client = new UdevQt::Client(subsystems);
46}
47
48UDevManager::Private::~Private()
49{
50 delete m_client;
51}
52
53bool UDevManager::Private::isOfInterest(const QString &udi, const UdevQt::Device &device)
54{
55 if (m_devicesOfInterest.contains(udi)) {
56 return true;
57 }
58
59 bool isOfInterest = checkOfInterest(device);
60 if (isOfInterest) {
61 m_devicesOfInterest.append(udi);
62 }
63
64 return isOfInterest;
65}
66
67bool UDevManager::Private::checkOfInterest(const UdevQt::Device &device)
68{
69 const QString subsystem = device.subsystem();
70#ifdef UDEV_DETAILED_OUTPUT
71 qDebug() << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<";
72 qDebug() << "Path:" << device.sysfsPath();
73 qDebug() << "Properties:" << device.deviceProperties();
74
75 const QStringList properties = device.deviceProperties();
76 for (const QString &key : properties) {
77 qDebug() << "\t" << key << ":" << device.deviceProperty(key).toString();
78 }
79 qDebug() << "Driver:" << device.driver();
80 qDebug() << "Subsystem:" << subsystem;
81 qDebug() << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>";
82#endif
83
84 if (subsystem == QLatin1String("cpu")) {
85 // Linux ACPI reports processor slots, rather than processors.
86 // Empty slots will not have a system device associated with them.
87 return QFile::exists(device.sysfsPath() + "/sysdev") //
88 || QFile::exists(device.sysfsPath() + "/cpufreq") //
89 || QFile::exists(device.sysfsPath() + "/topology/core_id");
90 }
91 if (subsystem == QLatin1String("sound") && device.deviceProperty("SOUND_FORM_FACTOR").toString() != "internal") {
92 return true;
93 }
94
95 if (subsystem == QLatin1String("tty")) {
96 QString path = device.deviceProperty("DEVPATH").toString();
97
98 int lastSlash = path.length() - path.lastIndexOf(QLatin1String("/")) - 1;
99 QByteArray lastElement = path.right(lastSlash).toLatin1();
100
101 if (lastElement.startsWith("tty") && !path.startsWith("/devices/virtual")) {
102 return true;
103 }
104 }
105
106 if (subsystem == QLatin1String("input")) {
107 /* clang-format off */
108 if (device.deviceProperty("ID_INPUT_MOUSE").toInt() == 1
109 || device.deviceProperty("ID_INPUT_TOUCHPAD").toInt() == 1
110 || device.deviceProperty("ID_INPUT_TABLET").toInt() == 1
111 || device.deviceProperty("ID_INPUT_JOYSTICK").toInt() == 1
112 || device.deviceProperty("ID_INPUT_TOUCHSCREEN").toInt() == 1) { /* clang-format on */
113 return true;
114 }
115 }
116
117 /* clang-format off */
118 return subsystem == QLatin1String("dvb")
119 || subsystem == QLatin1String("net")
120 || (!device.deviceProperty("ID_MEDIA_PLAYER").toString().isEmpty()
121 && device.parent().deviceProperty("ID_MEDIA_PLAYER").toString().isEmpty()) // media-player-info recognized devices
122 || (device.deviceProperty("ID_GPHOTO2").toInt() == 1
123 && device.parent().deviceProperty("ID_GPHOTO2").toInt() != 1); // GPhoto2 cameras
124 /* clang-format on */
125}
126
127UDevManager::UDevManager(QObject *parent)
128 : Solid::Ifaces::DeviceManager(parent)
129 , d(new Private)
130{
131 connect(d->m_client, SIGNAL(deviceAdded(UdevQt::Device)), this, SLOT(slotDeviceAdded(UdevQt::Device)));
132 connect(d->m_client, SIGNAL(deviceRemoved(UdevQt::Device)), this, SLOT(slotDeviceRemoved(UdevQt::Device)));
133
134 // clang-format off
135 d->m_supportedInterfaces << Solid::DeviceInterface::GenericInterface
136 << Solid::DeviceInterface::Processor
137 << Solid::DeviceInterface::Camera
138 << Solid::DeviceInterface::PortableMediaPlayer
139 << Solid::DeviceInterface::Block;
140 // clang-format on
141}
142
143UDevManager::~UDevManager()
144{
145 delete d;
146}
147
148QString UDevManager::udiPrefix() const
149{
150 return QString::fromLatin1(UDEV_UDI_PREFIX);
151}
152
153QSet<Solid::DeviceInterface::Type> UDevManager::supportedInterfaces() const
154{
155 return d->m_supportedInterfaces;
156}
157
158QStringList UDevManager::allDevices()
159{
160 QStringList res;
161 const UdevQt::DeviceList deviceList = d->m_client->allDevices();
162 for (const UdevQt::Device &device : deviceList) {
163 if (d->isOfInterest(udiPrefix() + device.sysfsPath(), device)) {
164 res << udiPrefix() + device.sysfsPath();
165 }
166 }
167 return res;
168}
169
170QStringList UDevManager::devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type)
171{
172 QStringList result;
173
174 if (!parentUdi.isEmpty()) {
175 const UdevQt::DeviceList deviceList = d->m_client->allDevices();
176 for (const UdevQt::Device &dev : deviceList) {
177 UDevDevice device(dev);
178 if (device.queryDeviceInterface(type) && d->isOfInterest(udiPrefix() + dev.sysfsPath(), dev) && device.parentUdi() == parentUdi) {
179 result << udiPrefix() + dev.sysfsPath();
180 }
181 }
182 return result;
183 }
184
185 if (type == DeviceInterface::Unknown) {
186 return allDevices();
187 }
188
189 UdevQt::DeviceList deviceList;
190
191 // Already limit the number of devices we query and have to create wrapper items for here
192 if (type == Solid::DeviceInterface::Processor) {
193 deviceList = (d->m_client->devicesBySubsystem(QStringLiteral("processor")) //
194 + d->m_client->devicesBySubsystem(QStringLiteral("cpu")));
195 } else if (type == Solid::DeviceInterface::Camera) {
196 deviceList = d->m_client->devicesBySubsystemsAndProperties({QStringLiteral("usb")}, //
197 {{QStringLiteral("ID_GPHOTO2"), QStringLiteral("*")}}); // match any
198 } else if (type == Solid::DeviceInterface::PortableMediaPlayer) {
199 deviceList = d->m_client->devicesBySubsystemsAndProperties({QStringLiteral("usb")}, //
200 {{QStringLiteral("ID_MEDIA_PLAYER"), QStringLiteral("*")}}); // match any
201 } else {
202 deviceList = d->m_client->allDevices();
203 }
204
205 for (const UdevQt::Device &dev : std::as_const(deviceList)) {
206 UDevDevice device(dev);
207 if (device.queryDeviceInterface(type) //
208 && d->isOfInterest(udiPrefix() + dev.sysfsPath(), dev)) {
209 result << udiPrefix() + dev.sysfsPath();
210 }
211 }
212
213 return result;
214}
215
216QObject *UDevManager::createDevice(const QString &udi_)
217{
218 if (udi_ == udiPrefix()) {
219 RootDevice *const device = new RootDevice(UDEV_UDI_PREFIX);
220 device->setProduct(tr("Devices"));
221 device->setDescription(tr("Devices declared in your system"));
222 device->setIcon("computer");
223
224 return device;
225 }
226
227 const QString udi = udi_.right(udi_.size() - udiPrefix().size());
228 UdevQt::Device device = d->m_client->deviceBySysfsPath(udi);
229
230 if (d->isOfInterest(udi_, device) || QFile::exists(udi)) {
231 return new UDevDevice(device);
232 }
233
234 return nullptr;
235}
236
237void UDevManager::slotDeviceAdded(const UdevQt::Device &device)
238{
239 if (d->isOfInterest(udiPrefix() + device.sysfsPath(), device)) {
240 Q_EMIT deviceAdded(udiPrefix() + device.sysfsPath());
241 }
242}
243
244void UDevManager::slotDeviceRemoved(const UdevQt::Device &device)
245{
246 if (d->isOfInterest(udiPrefix() + device.sysfsPath(), device)) {
247 Q_EMIT deviceRemoved(udiPrefix() + device.sysfsPath());
248 d->m_devicesOfInterest.removeAll(udiPrefix() + device.sysfsPath());
249 }
250}
251
252#include "moc_udevmanager.cpp"
Type
This enum type defines the type of device interface that a Device can have.
void deviceAdded(const QString &udi)
This signal is emitted when a new device appears in the system.
void deviceRemoved(const QString &udi)
This signal is emitted when a device disappears from the system.
DeviceManager(QObject *parent=nullptr)
Constructs a DeviceManager.
QString path(const QString &relativePath)
KGuiItem properties()
The single responsibility of this class is to create arguments valid for logind Inhibit call.
Definition fakebattery.h:16
bool startsWith(QByteArrayView bv) const const
bool exists() const const
qsizetype removeAll(const AT &t)
Q_EMITQ_EMIT
QObject * parent() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype length() const const
QString right(qsizetype n) const const
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
int toInt(bool *ok) const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:12 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.