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 QStringLiteral("processor"),
38 QStringLiteral("cpu"),
39 QStringLiteral("sound"),
40 QStringLiteral("tty"),
41 QStringLiteral("dvb"),
42 QStringLiteral("net"),
43 QStringLiteral("usb"),
44 QStringLiteral("input"),
45 };
46 m_client = new UdevQt::Client(subsystems);
47}
48
49UDevManager::Private::~Private()
50{
51 delete m_client;
52}
53
54bool UDevManager::Private::isOfInterest(const QString &udi, const UdevQt::Device &device)
55{
56 if (m_devicesOfInterest.contains(udi)) {
57 return true;
58 }
59
60 bool isOfInterest = checkOfInterest(device);
61 if (isOfInterest) {
62 m_devicesOfInterest.append(udi);
63 }
64
65 return isOfInterest;
66}
67
68bool UDevManager::Private::checkOfInterest(const UdevQt::Device &device)
69{
70 const QString subsystem = device.subsystem();
71#ifdef UDEV_DETAILED_OUTPUT
72 qDebug() << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<";
73 qDebug() << "Path:" << device.sysfsPath();
74 qDebug() << "Properties:" << device.deviceProperties();
75
76 const QStringList properties = device.deviceProperties();
77 for (const QString &key : properties) {
78 qDebug() << "\t" << key << ":" << device.deviceProperty(key).toString();
79 }
80 qDebug() << "Driver:" << device.driver();
81 qDebug() << "Subsystem:" << subsystem;
82 qDebug() << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>";
83#endif
84
85 if (subsystem == QLatin1String("cpu")) {
86 // Linux ACPI reports processor slots, rather than processors.
87 // Empty slots will not have a system device associated with them.
88 return QFile::exists(device.sysfsPath() + QStringLiteral("/sysdev")) //
89 || QFile::exists(device.sysfsPath() + QStringLiteral("/cpufreq")) //
90 || QFile::exists(device.sysfsPath() + QStringLiteral("/topology/core_id"));
91 }
92 if (subsystem == QLatin1String("sound") && device.deviceProperty(QStringLiteral("SOUND_FORM_FACTOR")).toString() != QStringLiteral("internal")) {
93 return true;
94 }
95
96 if (subsystem == QLatin1String("tty")) {
97 QString path = device.deviceProperty(QStringLiteral("DEVPATH")).toString();
98
99 int lastSlash = path.length() - path.lastIndexOf(QLatin1String("/")) - 1;
100 QByteArray lastElement = path.right(lastSlash).toLatin1();
101
102 if (lastElement.startsWith("tty") && !path.startsWith(QStringLiteral("/devices/virtual"))) {
103 return true;
104 }
105 }
106
107 if (subsystem == QLatin1String("input")) {
108 /* clang-format off */
109 if (device.deviceProperty(QStringLiteral("ID_INPUT_MOUSE")).toInt() == 1
110 || device.deviceProperty(QStringLiteral("ID_INPUT_TOUCHPAD")).toInt() == 1
111 || device.deviceProperty(QStringLiteral("ID_INPUT_TABLET")).toInt() == 1
112 || device.deviceProperty(QStringLiteral("ID_INPUT_JOYSTICK")).toInt() == 1
113 || device.deviceProperty(QStringLiteral("ID_INPUT_TOUCHSCREEN")).toInt() == 1) { /* clang-format on */
114 return true;
115 }
116 }
117
118 /* clang-format off */
119 return subsystem == QLatin1String("dvb")
120 || subsystem == QLatin1String("net")
121 || (!device.deviceProperty(QStringLiteral("ID_MEDIA_PLAYER")).toString().isEmpty()
122 && device.parent().deviceProperty(QStringLiteral("ID_MEDIA_PLAYER")).toString().isEmpty()) // media-player-info recognized devices
123 || (device.deviceProperty(QStringLiteral("ID_GPHOTO2")).toInt() == 1
124 && device.parent().deviceProperty(QStringLiteral("ID_GPHOTO2")).toInt() != 1); // GPhoto2 cameras
125 /* clang-format on */
126}
127
128UDevManager::UDevManager(QObject *parent)
129 : Solid::Ifaces::DeviceManager(parent)
130 , d(new Private)
131{
132 connect(d->m_client, SIGNAL(deviceAdded(UdevQt::Device)), this, SLOT(slotDeviceAdded(UdevQt::Device)));
133 connect(d->m_client, SIGNAL(deviceRemoved(UdevQt::Device)), this, SLOT(slotDeviceRemoved(UdevQt::Device)));
134
135 // clang-format off
136 d->m_supportedInterfaces << Solid::DeviceInterface::GenericInterface
137 << Solid::DeviceInterface::Processor
138 << Solid::DeviceInterface::Camera
139 << Solid::DeviceInterface::PortableMediaPlayer
140 << Solid::DeviceInterface::Block;
141 // clang-format on
142}
143
144UDevManager::~UDevManager()
145{
146 delete d;
147}
148
149QString UDevManager::udiPrefix() const
150{
151 return QString::fromLatin1(UDEV_UDI_PREFIX);
152}
153
154QSet<Solid::DeviceInterface::Type> UDevManager::supportedInterfaces() const
155{
156 return d->m_supportedInterfaces;
157}
158
159QStringList UDevManager::allDevices()
160{
161 QStringList res;
162 const UdevQt::DeviceList deviceList = d->m_client->allDevices();
163 for (const UdevQt::Device &device : deviceList) {
164 if (d->isOfInterest(udiPrefix() + device.sysfsPath(), device)) {
165 res << udiPrefix() + device.sysfsPath();
166 }
167 }
168 return res;
169}
170
171QStringList UDevManager::devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type)
172{
173 QStringList result;
174
175 if (!parentUdi.isEmpty()) {
176 const UdevQt::DeviceList deviceList = d->m_client->allDevices();
177 for (const UdevQt::Device &dev : deviceList) {
178 UDevDevice device(dev);
179 if (device.queryDeviceInterface(type) && d->isOfInterest(udiPrefix() + dev.sysfsPath(), dev) && device.parentUdi() == parentUdi) {
180 result << udiPrefix() + dev.sysfsPath();
181 }
182 }
183 return result;
184 }
185
186 if (type == DeviceInterface::Unknown) {
187 return allDevices();
188 }
189
190 UdevQt::DeviceList deviceList;
191
192 // Already limit the number of devices we query and have to create wrapper items for here
193 if (type == Solid::DeviceInterface::Processor) {
194 deviceList = (d->m_client->devicesBySubsystem(QStringLiteral("processor")) //
195 + d->m_client->devicesBySubsystem(QStringLiteral("cpu")));
196 } else if (type == Solid::DeviceInterface::Camera) {
197 deviceList = d->m_client->devicesBySubsystemsAndProperties({QStringLiteral("usb")}, //
198 {{QStringLiteral("ID_GPHOTO2"), QStringLiteral("*")}}); // match any
199 } else if (type == Solid::DeviceInterface::PortableMediaPlayer) {
200 deviceList = d->m_client->devicesBySubsystemsAndProperties({QStringLiteral("usb")}, //
201 {{QStringLiteral("ID_MEDIA_PLAYER"), QStringLiteral("*")}}); // match any
202 } else {
203 deviceList = d->m_client->allDevices();
204 }
205
206 for (const UdevQt::Device &dev : std::as_const(deviceList)) {
207 UDevDevice device(dev);
208 if (device.queryDeviceInterface(type) //
209 && d->isOfInterest(udiPrefix() + dev.sysfsPath(), dev)) {
210 result << udiPrefix() + dev.sysfsPath();
211 }
212 }
213
214 return result;
215}
216
217QObject *UDevManager::createDevice(const QString &udi_)
218{
219 if (udi_ == udiPrefix()) {
220 RootDevice *const device = new RootDevice(QStringLiteral(UDEV_UDI_PREFIX));
221 device->setProduct(tr("Devices"));
222 device->setDescription(tr("Devices declared in your system"));
223 device->setIcon(QStringLiteral("computer"));
224
225 return device;
226 }
227
228 const QString udi = udi_.right(udi_.size() - udiPrefix().size());
229 UdevQt::Device device = d->m_client->deviceBySysfsPath(udi);
230
231 if (d->isOfInterest(udi_, device) || QFile::exists(udi)) {
232 return new UDevDevice(device);
233 }
234
235 return nullptr;
236}
237
238void UDevManager::slotDeviceAdded(const UdevQt::Device &device)
239{
240 if (d->isOfInterest(udiPrefix() + device.sysfsPath(), device)) {
241 Q_EMIT deviceAdded(udiPrefix() + device.sysfsPath());
242 }
243}
244
245void UDevManager::slotDeviceRemoved(const UdevQt::Device &device)
246{
247 if (d->isOfInterest(udiPrefix() + device.sysfsPath(), device)) {
248 Q_EMIT deviceRemoved(udiPrefix() + device.sysfsPath());
249 d->m_devicesOfInterest.removeAll(udiPrefix() + device.sysfsPath());
250 }
251}
252
253#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.
char * toString(const EngineQuery &query)
QString path(const QString &relativePath)
KGuiItem properties()
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)
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 6 2024 12:07:11 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.