1 /*
2     SPDX-FileCopyrightText: 2013 Patrick von Reth <vonreth@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 "windevice.h"
8 #include <solid/deviceinterface.h>
9 
10 #include "winbattery.h"
11 #include "winblock.h"
12 #include "wingenericinterface.h"
13 #include "winopticaldisc.h"
14 #include "winopticaldrive.h"
15 #include "winprocessor.h"
16 #include "winstorageaccess.h"
17 #include "winstoragedrive.h"
18 #include "winstoragevolume.h"
19 
20 #include <batclass.h>
21 
22 #if defined(__MINGW32__) && !defined(IOCTL_STORAGE_QUERY_PROPERTY)
23 #include <winioctl_backport.h>
24 #endif
25 
26 using namespace Solid::Backends::Win;
27 
WinDevice(const QString & udi)28 WinDevice::WinDevice(const QString &udi)
29     : Device()
30     , m_udi(udi)
31     , m_type(Solid::DeviceInterface::Unknown)
32 {
33     /*
34      /org/kde/solid/win/volume/disk #%1, partition #%2
35      /org/kde/solid/win/storage.cdrom/disk #%0
36     */
37     QStringList data = udi.split("/");
38     QString parentName = data[6].split(",")[0].trimmed();
39     QString type = data[5];
40 
41     if (type == "storage") {
42         m_type = Solid::DeviceInterface::StorageDrive;
43     } else if (type == "volume") {
44         m_type = Solid::DeviceInterface::StorageVolume;
45     } else if (type == "storage.cdrom") {
46         m_type = Solid::DeviceInterface::OpticalDrive;
47     } else if (type == "volume.cdrom") {
48         m_type = Solid::DeviceInterface::OpticalDisc;
49     } else if (type == "cpu") {
50         m_type = Solid::DeviceInterface::Processor;
51     } else if (type == "power.battery") {
52         m_type = Solid::DeviceInterface::Battery;
53     } else if (type == "volume.virtual") {
54         m_type = Solid::DeviceInterface::StorageAccess;
55     }
56 
57     switch (m_type) {
58     case Solid::DeviceInterface::StorageVolume: {
59         m_parentUdi = QLatin1String("/org/kde/solid/win/storage/") + parentName;
60         break;
61     }
62     case Solid::DeviceInterface::OpticalDisc: {
63         m_parentUdi = QLatin1String("/org/kde/solid/win/storage.cdrom/") + parentName;
64         break;
65     }
66     case Solid::DeviceInterface::StorageAccess: {
67         m_parentUdi = WinBlock::udiFromDriveLetter(WinBlock::resolveVirtualDrive(udi).mid(0, 2));
68         if (m_parentUdi.isEmpty()) {
69             m_parentUdi = QLatin1String("/org/kde/solid/win/") + type;
70         }
71         break;
72     }
73     default:
74         m_parentUdi = QLatin1String("/org/kde/solid/win/") + type;
75     }
76 
77     switch (m_type) {
78     case Solid::DeviceInterface::Processor:
79         initCpuDevice();
80         break;
81     case Solid::DeviceInterface::Battery:
82         initBatteryDevice();
83         break;
84     default:
85         if (queryDeviceInterface(Solid::DeviceInterface::StorageAccess) || queryDeviceInterface(Solid::DeviceInterface::StorageDrive)) {
86             initStorageDevice();
87         } else {
88             qWarning() << "Unknown device" << udi;
89         }
90     }
91 }
92 
initStorageDevice()93 void WinDevice::initStorageDevice()
94 {
95     QString dev;
96     switch (m_type) {
97     case Solid::DeviceInterface::StorageAccess:
98         dev = WinBlock::driveLetterFromUdi(udi());
99         m_product = QString("Virtual drive %1").arg(dev);
100         m_description = QString("%1 (%2)").arg(dev, WinBlock::resolveVirtualDrive(udi()));
101         return;
102     case Solid::DeviceInterface::OpticalDrive:
103         dev = WinBlock::driveLetterFromUdi(udi());
104         break;
105     case Solid::DeviceInterface::StorageDrive:
106         dev = QString("PhysicalDrive%1").arg(WinBlock(this).deviceMajor());
107         break;
108     default:
109         dev = WinBlock::driveLetterFromUdi(udi());
110         const QString label = WinStorageVolume(this).label();
111         if (!label.isEmpty()) {
112             m_description = QStringLiteral("%1 (%2)").arg(dev, label);
113         } else {
114             m_description = dev;
115         }
116     }
117 
118     STORAGE_PROPERTY_QUERY query;
119     ZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
120     query.PropertyId = StorageDeviceProperty;
121     query.QueryType = PropertyStandardQuery;
122 
123     char buff[1024];
124     WinDeviceManager::getDeviceInfo<char, STORAGE_PROPERTY_QUERY>(dev, IOCTL_STORAGE_QUERY_PROPERTY, buff, 1024, &query);
125     STORAGE_DEVICE_DESCRIPTOR *info = ((STORAGE_DEVICE_DESCRIPTOR *)buff);
126     if (info->VendorIdOffset != 0) {
127         m_vendor = QString((char *)buff + info->VendorIdOffset).trimmed();
128         if (info->ProductIdOffset != 0) {
129             m_product = QString((char *)buff + info->ProductIdOffset).trimmed();
130         }
131     } else if (info->ProductIdOffset != 0) { // fallback doesn't work for all devices
132         QStringList tmp = QString((char *)buff + info->ProductIdOffset).trimmed().split(" ");
133         m_vendor = tmp.takeFirst();
134         m_product = tmp.join(" ");
135     }
136 }
137 
initBatteryDevice()138 void WinDevice::initBatteryDevice()
139 {
140     WinBattery::Battery battery = WinBattery::batteryInfoFromUdi(m_udi);
141     BATTERY_QUERY_INFORMATION query;
142     ZeroMemory(&query, sizeof(query));
143     query.BatteryTag = battery.second;
144 
145     DWORD size = 1024;
146     wchar_t buff[1024];
147 
148     query.InformationLevel = BatteryDeviceName;
149     WinDeviceManager::getDeviceInfo<wchar_t, BATTERY_QUERY_INFORMATION>(battery.first, IOCTL_BATTERY_QUERY_INFORMATION, buff, size, &query);
150     m_product = QString::fromWCharArray(buff);
151 
152     query.InformationLevel = BatteryManufactureName;
153     WinDeviceManager::getDeviceInfo<wchar_t, BATTERY_QUERY_INFORMATION>(battery.first, IOCTL_BATTERY_QUERY_INFORMATION, buff, size, &query);
154     m_vendor = QString::fromWCharArray(buff);
155 
156     switch (WinBattery(this).technology()) {
157     case Solid::Battery::LithiumIon:
158         m_description = tr("Lithium Ion", "battery technology");
159         break;
160     case Solid::Battery::LeadAcid:
161         m_description = tr("Lead Acid", "battery technology");
162         break;
163     case Solid::Battery::NickelCadmium:
164         m_description = tr("Nickel Cadmium", "battery technology");
165         break;
166     case Solid::Battery::NickelMetalHydride:
167         m_description = tr("Nickel Metal Hydride", "battery technology");
168         break;
169     default:
170         m_description = tr("Unknown", "battery technology");
171     }
172 }
173 
initCpuDevice()174 void WinDevice::initCpuDevice()
175 {
176     WinProcessor cpu(this);
177     WinProcessor::ProcessorInfo info = WinProcessor::updateCache()[cpu.number()];
178     m_vendor = info.vendor;
179     m_product = info.produuct;
180     m_description = info.name;
181 }
182 
udi() const183 QString WinDevice::udi() const
184 {
185     return m_udi;
186 }
187 
parentUdi() const188 QString WinDevice::parentUdi() const
189 {
190     return m_parentUdi;
191 }
192 
vendor() const193 QString WinDevice::vendor() const
194 {
195     return m_vendor;
196 }
197 
product() const198 QString WinDevice::product() const
199 {
200     return m_product;
201 }
202 
description() const203 QString WinDevice::description() const
204 {
205     return m_description.isEmpty() ? m_product : m_description;
206 }
207 
icon() const208 QString WinDevice::icon() const
209 {
210     if (parentUdi().isEmpty()) {
211         return QLatin1String("computer");
212     }
213 
214     QString icon;
215     switch (type()) {
216     case Solid::DeviceInterface::OpticalDrive:
217         icon = QLatin1String("drive-optical");
218         break;
219     case Solid::DeviceInterface::OpticalDisc: {
220         WinOpticalDisc disk(const_cast<WinDevice *>(this));
221         if (disk.availableContent() & Solid::OpticalDisc::Audio) {
222             icon = QLatin1String("media-optical-audio");
223         } else {
224             icon = QLatin1String("drive-optical");
225         }
226         break;
227     }
228     case Solid::DeviceInterface::StorageDrive:
229     case Solid::DeviceInterface::StorageVolume: {
230         WinStorageDrive storage(const_cast<WinDevice *>(this));
231         if (storage.bus() == Solid::StorageDrive::Usb) {
232             icon = QLatin1String("drive-removable-media-usb-pendrive");
233         } else {
234             icon = QLatin1String("drive-harddisk");
235         }
236         break;
237     }
238     case Solid::DeviceInterface::Processor:
239         icon = QLatin1String("cpu");
240         break;
241     case Solid::DeviceInterface::Battery:
242         icon = QLatin1String("battery");
243         break;
244     case Solid::DeviceInterface::StorageAccess:
245         icon = QLatin1String("drive-harddisk");
246         break;
247     default:
248         break;
249     }
250     return icon;
251 }
252 
emblems() const253 QStringList WinDevice::emblems() const
254 {
255     QStringList icons;
256     switch (type()) {
257     case Solid::DeviceInterface::StorageAccess:
258         icons << QLatin1String("emblem-symbolic-link");
259         break;
260     default:
261         break;
262     }
263     return icons;
264 }
265 
queryDeviceInterface(const Solid::DeviceInterface::Type & queryType) const266 bool WinDevice::queryDeviceInterface(const Solid::DeviceInterface::Type &queryType) const
267 {
268     if (queryType == Solid::DeviceInterface::GenericInterface) {
269         return true;
270     }
271 
272     QList<Solid::DeviceInterface::Type> interfaceList;
273     interfaceList << type();
274 
275     switch (type()) {
276     case Solid::DeviceInterface::GenericInterface:
277         break;
278     case Solid::DeviceInterface::Block:
279         break;
280     case Solid::DeviceInterface::StorageAccess:
281         break;
282     case Solid::DeviceInterface::StorageDrive:
283         break;
284     case Solid::DeviceInterface::OpticalDrive:
285         interfaceList << Solid::DeviceInterface::Block << Solid::DeviceInterface::StorageDrive;
286         break;
287     case Solid::DeviceInterface::StorageVolume:
288         interfaceList << Solid::DeviceInterface::Block << Solid::DeviceInterface::StorageAccess;
289         break;
290     case Solid::DeviceInterface::OpticalDisc:
291         interfaceList << Solid::DeviceInterface::Block << Solid::DeviceInterface::StorageVolume << Solid::DeviceInterface::StorageAccess;
292         break;
293     case Solid::DeviceInterface::PortableMediaPlayer:
294         break;
295     case Solid::DeviceInterface::Unknown:
296     case Solid::DeviceInterface::Last:
297     default:
298         break;
299     }
300 
301     if (interfaceList.size() == 0) {
302         qWarning() << "no interface found for type" << type();
303     }
304     return interfaceList.contains(queryType);
305 }
306 
createDeviceInterface(const Solid::DeviceInterface::Type & type)307 QObject *WinDevice::createDeviceInterface(const Solid::DeviceInterface::Type &type)
308 {
309     if (!queryDeviceInterface(type)) {
310         return 0;
311     }
312     WinInterface *iface = 0;
313 
314     switch (type) {
315     case Solid::DeviceInterface::GenericInterface:
316         iface = new WinGenericInterface(this);
317         break;
318     case Solid::DeviceInterface::Block:
319         iface = new WinBlock(this);
320         break;
321     case Solid::DeviceInterface::Processor:
322         iface = new WinProcessor(this);
323         break;
324     case Solid::DeviceInterface::StorageAccess:
325         iface = new WinStorageAccess(this);
326         break;
327     case Solid::DeviceInterface::StorageDrive:
328         iface = new WinStorageDrive(this);
329         break;
330     case Solid::DeviceInterface::OpticalDrive:
331         iface = new WinOpticalDrive(this);
332         break;
333     case Solid::DeviceInterface::StorageVolume:
334         iface = new WinStorageVolume(this);
335         break;
336     case Solid::DeviceInterface::OpticalDisc:
337         iface = new WinOpticalDisc(this);
338         break;
339     //      case Solid::DeviceInterface::PortableMediaPlayer:
340     //          iface = new PortableMediaPlayer(this);
341     //          break;
342     case Solid::DeviceInterface::Battery:
343         iface = new WinBattery(this);
344         break;
345     case Solid::DeviceInterface::Unknown:
346     case Solid::DeviceInterface::Last:
347         break;
348     default:
349         break;
350     }
351 
352     return iface;
353 }
354 
type() const355 Solid::DeviceInterface::Type WinDevice::type() const
356 {
357     return m_type;
358 }
359