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 "udevdevice.h"
8
9#include "cpuinfo.h"
10#include "udevblock.h"
11#include "udevcamera.h"
12#include "udevgenericinterface.h"
13#include "udevportablemediaplayer.h"
14#include "udevprocessor.h"
15
16#include <sys/socket.h>
17#include <sys/types.h>
18#include <linux/if_arp.h>
19
20using namespace Solid::Backends::UDev;
21
22namespace
23{
24// Helper function for three-tier property fallback with hwdb support
25QString getUdevPropertyWithFallback(const UdevQt::Device &device, const QString &basePropertyName)
26{
27 // Try *_FROM_DATABASE first (from hwdb, most readable)
28 QString value = device.deviceProperty(name: basePropertyName + QStringLiteral("_FROM_DATABASE")).toString();
29 if (value.isEmpty()) {
30 // Fall back to *_ENC (hex-encoded, needs decoding and trimming)
31 value = device.decodedDeviceProperty(name: basePropertyName + QStringLiteral("_ENC")).trimmed();
32 if (value.isEmpty()) {
33 // Last resort: raw property (may have underscores)
34 value = device.deviceProperty(name: basePropertyName).toString().replace(before: QLatin1Char('_'), after: QLatin1Char(' '));
35 }
36 }
37 return value;
38}
39}
40
41UDevDevice::UDevDevice(const UdevQt::Device device)
42 : Solid::Ifaces::Device()
43 , m_device(device)
44{
45}
46
47UDevDevice::~UDevDevice()
48{
49}
50
51QString UDevDevice::udi() const
52{
53 return devicePath();
54}
55
56QString UDevDevice::parentUdi() const
57{
58 return QStringLiteral(UDEV_UDI_PREFIX);
59}
60
61QString UDevDevice::vendor() const
62{
63 QString vendor = m_device.sysfsProperty(QStringLiteral("manufacturer")).toString();
64 if (vendor.isEmpty()) {
65 if (queryDeviceInterface(type: Solid::DeviceInterface::Processor)) {
66 // sysfs doesn't have anything useful here
67 vendor = extractCpuVendor(processorNumber: deviceNumber());
68 }
69
70 if (vendor.isEmpty()) {
71 vendor = getUdevPropertyWithFallback(device: m_device, QStringLiteral("ID_VENDOR"));
72 }
73 }
74 return vendor;
75}
76
77QString UDevDevice::product() const
78{
79 QString product = m_device.sysfsProperty(QStringLiteral("product")).toString();
80 if (product.isEmpty()) {
81 if (queryDeviceInterface(type: Solid::DeviceInterface::Processor)) {
82 // sysfs doesn't have anything useful here
83 product = extractCpuModel(processorNumber: deviceNumber());
84 }
85
86 if (product.isEmpty()) {
87 product = getUdevPropertyWithFallback(device: m_device, QStringLiteral("ID_MODEL"));
88 }
89 }
90 return product;
91}
92
93QString UDevDevice::displayName() const
94{
95 QString vendorName = vendor();
96 QString productName = product();
97
98 // If both vendor and product are available, check for redundancy
99 if (!vendorName.isEmpty() && !productName.isEmpty()) {
100 // If product already contains vendor, return product alone to avoid duplication
101 if (productName.toLower().contains(s: vendorName.toLower())) {
102 return productName;
103 }
104 // Otherwise combine them
105 return vendorName + QLatin1String(" ") + productName;
106 }
107
108 // Fallback: return whichever is available
109 if (!productName.isEmpty()) {
110 return productName;
111 }
112
113 if (!vendorName.isEmpty()) {
114 return vendorName;
115 }
116
117 // Ultimate fallback: use description
118 return description();
119}
120
121QString UDevDevice::icon() const
122{
123 if (parentUdi().isEmpty()) {
124 return QLatin1String("computer");
125 }
126
127 if (queryDeviceInterface(type: Solid::DeviceInterface::Processor)) {
128 return QLatin1String("cpu");
129 } else if (queryDeviceInterface(type: Solid::DeviceInterface::PortableMediaPlayer)) {
130 // TODO: check out special cases like iPod
131 return QLatin1String("multimedia-player");
132 } else if (queryDeviceInterface(type: Solid::DeviceInterface::Camera)) {
133 return QLatin1String("camera-photo");
134 }
135
136 return QString();
137}
138
139QStringList UDevDevice::emblems() const
140{
141 return QStringList();
142}
143
144QString UDevDevice::description() const
145{
146 if (parentUdi().isEmpty()) {
147 return tr(s: "Computer");
148 }
149
150 if (queryDeviceInterface(type: Solid::DeviceInterface::Processor)) {
151 return tr(s: "Processor");
152 } else if (queryDeviceInterface(type: Solid::DeviceInterface::PortableMediaPlayer)) {
153 /*
154 * HACK: As Media player is very generic return the device product instead
155 * until we can return the Name.
156 */
157 const PortableMediaPlayer *player = new PortableMediaPlayer(const_cast<UDevDevice *>(this));
158 if (player->supportedProtocols().contains(QStringLiteral("mtp"))) {
159 return product();
160 } else {
161 // TODO: check out special cases like iPod
162 return tr(s: "Portable Media Player");
163 }
164 } else if (queryDeviceInterface(type: Solid::DeviceInterface::Camera)) {
165 return tr(s: "Camera");
166 }
167
168 return QString();
169}
170
171bool UDevDevice::queryDeviceInterface(const Solid::DeviceInterface::Type &type) const
172{
173 switch (type) {
174 case Solid::DeviceInterface::GenericInterface:
175 return true;
176
177 case Solid::DeviceInterface::Processor:
178 return m_device.subsystem() == QLatin1String("cpu");
179
180 case Solid::DeviceInterface::Camera:
181 return m_device.subsystem() == QLatin1String("usb") && property(QStringLiteral("ID_GPHOTO2")).isValid();
182
183 case Solid::DeviceInterface::PortableMediaPlayer:
184 return m_device.subsystem() == QLatin1String("usb") && property(QStringLiteral("ID_MEDIA_PLAYER")).isValid();
185
186 case Solid::DeviceInterface::Block:
187 return !property(QStringLiteral("MAJOR")).toString().isEmpty();
188
189 default:
190 return false;
191 }
192}
193
194QObject *UDevDevice::createDeviceInterface(const Solid::DeviceInterface::Type &type)
195{
196 if (!queryDeviceInterface(type)) {
197 return nullptr;
198 }
199
200 switch (type) {
201 case Solid::DeviceInterface::GenericInterface:
202 return new GenericInterface(this);
203
204 case Solid::DeviceInterface::Processor:
205 return new Processor(this);
206
207 case Solid::DeviceInterface::Camera:
208 return new Camera(this);
209
210 case Solid::DeviceInterface::PortableMediaPlayer:
211 return new PortableMediaPlayer(this);
212
213 case Solid::DeviceInterface::Block:
214 return new Block(this);
215
216 default:
217 qFatal(msg: "Shouldn't happen");
218 return nullptr;
219 }
220}
221
222QString UDevDevice::device() const
223{
224 return devicePath();
225}
226
227QVariant UDevDevice::property(const QString &key) const
228{
229 const QVariant res = m_device.deviceProperty(name: key);
230 if (res.isValid()) {
231 return res;
232 }
233 return m_device.sysfsProperty(name: key);
234}
235
236QMap<QString, QVariant> UDevDevice::allProperties() const
237{
238 QMap<QString, QVariant> res;
239 const QStringList properties = m_device.deviceProperties();
240 for (const QString &prop : properties) {
241 res[prop] = property(key: prop);
242 }
243 return res;
244}
245
246bool UDevDevice::propertyExists(const QString &key) const
247{
248 return m_device.deviceProperties().contains(str: key);
249}
250
251QString UDevDevice::systemAttribute(const char *attribute) const
252{
253 return m_device.sysfsProperty(name: QString::fromLatin1(ba: attribute)).toString();
254}
255
256QString UDevDevice::deviceName() const
257{
258 return m_device.sysfsPath();
259}
260
261int UDevDevice::deviceNumber() const
262{
263 return m_device.sysfsNumber();
264}
265
266QString UDevDevice::devicePath() const
267{
268 return QStringLiteral(UDEV_UDI_PREFIX) + deviceName();
269}
270
271UdevQt::Device UDevDevice::udevDevice()
272{
273 return m_device;
274}
275
276#include "moc_udevdevice.cpp"
277

source code of solid/src/solid/devices/backends/udev/udevdevice.cpp