Solid

iokitmanager.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Harald Fernengel <harry@kdevelop.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "iokitmanager.h"
8#include "iokitdevice.h"
9
10#include <qdebug.h>
11
12#include <IOKit/IOKitLib.h>
13#include <IOKit/network/IOEthernetInterface.h>
14#include <IOKit/usb/IOUSBLib.h>
15
16#include <CoreFoundation/CoreFoundation.h>
17
18namespace Solid
19{
20namespace Backends
21{
22namespace IOKit
23{
24class IOKitManagerPrivate
25{
26public:
27 inline IOKitManagerPrivate()
28 : port(nullptr)
29 , source(nullptr)
30 {
31 }
32
33 IONotificationPortRef port;
34 CFRunLoopSourceRef source;
35
36 static const char *typeToName(Solid::DeviceInterface::Type type);
37 static QStringList devicesFromRegistry(io_iterator_t it);
38
39 QSet<Solid::DeviceInterface::Type> supportedInterfaces;
40};
41
42// gets all registry paths from an iterator
43QStringList IOKitManagerPrivate::devicesFromRegistry(io_iterator_t it)
44{
45 QStringList result;
46 io_object_t obj;
47 while ((obj = IOIteratorNext(it))) {
48 CFStringRef pathRef = IORegistryEntryCopyPath(obj, kIOServicePlane);
49 const QString path = QString::fromCFString(pathRef);
50 CFRelease(pathRef);
51
52 if (path.isEmpty()) {
53 qWarning() << Q_FUNC_INFO << "IORegistryEntryCopyPath failed";
54 continue;
55 }
56 result += path;
57 const kern_return_t ret = IOObjectRelease(obj);
58 if (ret != KERN_SUCCESS) {
59 // very unlikely to happen - keep it a qDebug just in case.
60 // compiler will nuke this code in release builds.
61 qDebug() << Q_FUNC_INFO << "Unable to release object reference";
62 }
63 }
64 IOObjectRelease(it);
65
66 return result;
67}
68
69const char *IOKitManagerPrivate::typeToName(Solid::DeviceInterface::Type type)
70{
71 switch (type) {
72 case Solid::DeviceInterface::Unknown:
73 return 0;
74 case Solid::DeviceInterface::Processor:
75 return "AppleACPICPU";
76 case Solid::DeviceInterface::Battery:
77 return "AppleSmartBattery";
78
79 // Solid::DeviceInterface::GenericInterface:
80 // Solid::DeviceInterface::Block:
81 case Solid::DeviceInterface::StorageAccess:
82 case Solid::DeviceInterface::StorageDrive:
83 case Solid::DeviceInterface::StorageVolume:
84 return "IOMedia";
85 case Solid::DeviceInterface::OpticalDrive:
86 case Solid::DeviceInterface::OpticalDisc:
87 return "IOCDMedia";
88 // Solid::DeviceInterface::Camera:
89 // Solid::DeviceInterface::PortableMediaPlayer:
90 }
91
92 return 0;
93}
94
95IOKitManager::IOKitManager(QObject *parent)
96 : Solid::Ifaces::DeviceManager(parent)
97 , d(new IOKitManagerPrivate)
98{
99 d->port = IONotificationPortCreate(kIOMasterPortDefault);
100 if (!d->port) {
101 qWarning() << Q_FUNC_INFO << "Unable to create notification port";
102 return;
103 }
104
105 d->source = IONotificationPortGetRunLoopSource(d->port);
106 if (!d->source) {
107 qWarning() << Q_FUNC_INFO << "Unable to create notification source";
108 return;
109 }
110
111 CFRunLoopAddSource(CFRunLoopGetCurrent(), d->source, kCFRunLoopDefaultMode);
112 // clang-format off
113 d->supportedInterfaces << Solid::DeviceInterface::GenericInterface
114 << Solid::DeviceInterface::Processor
115 << Solid::DeviceInterface::Block
116 << Solid::DeviceInterface::StorageAccess
117 << Solid::DeviceInterface::StorageDrive
118 << Solid::DeviceInterface::OpticalDrive
119 << Solid::DeviceInterface::StorageVolume
120 << Solid::DeviceInterface::OpticalDisc
121 << Solid::DeviceInterface::Camera
122 << Solid::DeviceInterface::PortableMediaPlayer
123 << Solid::DeviceInterface::Battery;
124 // clang-format on
125}
126
127IOKitManager::~IOKitManager()
128{
129 if (d->source) {
130 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), d->source, kCFRunLoopDefaultMode);
131 }
132 if (d->port) {
133 IONotificationPortDestroy(d->port);
134 }
135
136 delete d;
137}
138
139QString IOKitManager::udiPrefix() const
140{
141 return QString(); // FIXME: We should probably use a prefix there... has to be tested on Mac
142}
143
144QSet<Solid::DeviceInterface::Type> IOKitManager::supportedInterfaces() const
145{
146 return d->supportedInterfaces;
147}
148
149QStringList IOKitManager::allDevices()
150{
151 // use an IORegistry Iterator to iterate over all devices in the service plane
152
153 io_iterator_t it;
154 kern_return_t ret = IORegistryCreateIterator(kIOMasterPortDefault, kIOServicePlane, kIORegistryIterateRecursively, &it);
155 if (ret != KERN_SUCCESS) {
156 qWarning() << Q_FUNC_INFO << "unable to create iterator";
157 return QStringList();
158 }
159
160 return IOKitManagerPrivate::devicesFromRegistry(it);
161}
162
163QStringList IOKitManager::devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type)
164{
165 QStringList result;
166
167 if (type == Solid::DeviceInterface::Unknown) {
168 // match all device interfaces
169 result = allDevices();
170 } else {
171 const char *deviceClassName = IOKitManagerPrivate::typeToName(type);
172 if (!deviceClassName) {
173 return QStringList();
174 }
175
176 CFMutableDictionaryRef matchingDict = IOServiceMatching(deviceClassName);
177
178 if (!matchingDict) {
179 return QStringList();
180 }
181
182 io_iterator_t it = 0;
183
184 // note - IOServiceGetMatchingServices dereferences the dict
185 kern_return_t ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &it);
186
187 result = IOKitManagerPrivate::devicesFromRegistry(it);
188 }
189
190 // if the parentUdi is an empty string, return all matches
191 if (parentUdi.isEmpty()) {
192 return result;
193 }
194
195 // return only matches that start with the parent's UDI
197 for (const QString &udi : std::as_const(result)) {
198 if (udi.startsWith(parentUdi)) {
199 filtered += udi;
200 }
201 }
202
203 return filtered;
204}
205
206QObject *IOKitManager::createDevice(const QString &udi)
207{
208 CFStringRef path = udi.toCFString();
209 io_registry_entry_t entry = IORegistryEntryCopyFromPath(kIOMasterPortDefault, path);
210 CFRelease(path);
211
212 // we have to do IOObjectConformsTo - comparing the class names is not good enough
213 // if (IOObjectConformsTo(entry, kIOEthernetInterfaceClass)) {
214 //}
215
216 if (entry == MACH_PORT_NULL) {
217 return 0;
218 }
219
220 return new IOKitDevice(udi, entry);
221}
222
223}
224}
225} // namespaces
226
227#include "moc_iokitmanager.cpp"
Type
This enum type defines the type of device interface that a Device can have.
QString path(const QString &relativePath)
QString fromCFString(CFStringRef string)
bool isEmpty() const const
CFStringRef toCFString() const const
QFuture< typename qValueType< Iterator >::value_type > filtered(Iterator begin, Iterator end, KeepFunctor &&filterFunction)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:14:22 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.