File indexing completed on 2024-05-26 04:05:33
0001 /* 0002 SPDX-FileCopyrightText: 2009 Harald Fernengel <harry@kdevelop.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "iokitmanager.h" 0008 #include "iokitdevice.h" 0009 0010 #include <qdebug.h> 0011 0012 #include <IOKit/IOKitLib.h> 0013 #include <IOKit/network/IOEthernetInterface.h> 0014 #include <IOKit/usb/IOUSBLib.h> 0015 0016 #include <CoreFoundation/CoreFoundation.h> 0017 0018 namespace Solid 0019 { 0020 namespace Backends 0021 { 0022 namespace IOKit 0023 { 0024 class IOKitManagerPrivate 0025 { 0026 public: 0027 inline IOKitManagerPrivate() 0028 : port(nullptr) 0029 , source(nullptr) 0030 { 0031 } 0032 0033 IONotificationPortRef port; 0034 CFRunLoopSourceRef source; 0035 0036 static const char *typeToName(Solid::DeviceInterface::Type type); 0037 static QStringList devicesFromRegistry(io_iterator_t it); 0038 0039 QSet<Solid::DeviceInterface::Type> supportedInterfaces; 0040 }; 0041 0042 // gets all registry paths from an iterator 0043 QStringList IOKitManagerPrivate::devicesFromRegistry(io_iterator_t it) 0044 { 0045 QStringList result; 0046 io_object_t obj; 0047 while ((obj = IOIteratorNext(it))) { 0048 CFStringRef pathRef = IORegistryEntryCopyPath(obj, kIOServicePlane); 0049 const QString path = QString::fromCFString(pathRef); 0050 CFRelease(pathRef); 0051 0052 if (path.isEmpty()) { 0053 qWarning() << Q_FUNC_INFO << "IORegistryEntryCopyPath failed"; 0054 continue; 0055 } 0056 result += path; 0057 const kern_return_t ret = IOObjectRelease(obj); 0058 if (ret != KERN_SUCCESS) { 0059 // very unlikely to happen - keep it a qDebug just in case. 0060 // compiler will nuke this code in release builds. 0061 qDebug() << Q_FUNC_INFO << "Unable to release object reference"; 0062 } 0063 } 0064 IOObjectRelease(it); 0065 0066 return result; 0067 } 0068 0069 const char *IOKitManagerPrivate::typeToName(Solid::DeviceInterface::Type type) 0070 { 0071 switch (type) { 0072 case Solid::DeviceInterface::Unknown: 0073 return 0; 0074 case Solid::DeviceInterface::Processor: 0075 return "AppleACPICPU"; 0076 case Solid::DeviceInterface::Battery: 0077 return "AppleSmartBattery"; 0078 0079 // Solid::DeviceInterface::GenericInterface: 0080 // Solid::DeviceInterface::Block: 0081 case Solid::DeviceInterface::StorageAccess: 0082 case Solid::DeviceInterface::StorageDrive: 0083 case Solid::DeviceInterface::StorageVolume: 0084 return "IOMedia"; 0085 case Solid::DeviceInterface::OpticalDrive: 0086 case Solid::DeviceInterface::OpticalDisc: 0087 return "IOCDMedia"; 0088 // Solid::DeviceInterface::Camera: 0089 // Solid::DeviceInterface::PortableMediaPlayer: 0090 } 0091 0092 return 0; 0093 } 0094 0095 IOKitManager::IOKitManager(QObject *parent) 0096 : Solid::Ifaces::DeviceManager(parent) 0097 , d(new IOKitManagerPrivate) 0098 { 0099 d->port = IONotificationPortCreate(kIOMasterPortDefault); 0100 if (!d->port) { 0101 qWarning() << Q_FUNC_INFO << "Unable to create notification port"; 0102 return; 0103 } 0104 0105 d->source = IONotificationPortGetRunLoopSource(d->port); 0106 if (!d->source) { 0107 qWarning() << Q_FUNC_INFO << "Unable to create notification source"; 0108 return; 0109 } 0110 0111 CFRunLoopAddSource(CFRunLoopGetCurrent(), d->source, kCFRunLoopDefaultMode); 0112 // clang-format off 0113 d->supportedInterfaces << Solid::DeviceInterface::GenericInterface 0114 << Solid::DeviceInterface::Processor 0115 << Solid::DeviceInterface::Block 0116 << Solid::DeviceInterface::StorageAccess 0117 << Solid::DeviceInterface::StorageDrive 0118 << Solid::DeviceInterface::OpticalDrive 0119 << Solid::DeviceInterface::StorageVolume 0120 << Solid::DeviceInterface::OpticalDisc 0121 << Solid::DeviceInterface::Camera 0122 << Solid::DeviceInterface::PortableMediaPlayer 0123 << Solid::DeviceInterface::Battery; 0124 // clang-format on 0125 } 0126 0127 IOKitManager::~IOKitManager() 0128 { 0129 if (d->source) { 0130 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), d->source, kCFRunLoopDefaultMode); 0131 } 0132 if (d->port) { 0133 IONotificationPortDestroy(d->port); 0134 } 0135 0136 delete d; 0137 } 0138 0139 QString IOKitManager::udiPrefix() const 0140 { 0141 return QString(); // FIXME: We should probably use a prefix there... has to be tested on Mac 0142 } 0143 0144 QSet<Solid::DeviceInterface::Type> IOKitManager::supportedInterfaces() const 0145 { 0146 return d->supportedInterfaces; 0147 } 0148 0149 QStringList IOKitManager::allDevices() 0150 { 0151 // use an IORegistry Iterator to iterate over all devices in the service plane 0152 0153 io_iterator_t it; 0154 kern_return_t ret = IORegistryCreateIterator(kIOMasterPortDefault, kIOServicePlane, kIORegistryIterateRecursively, &it); 0155 if (ret != KERN_SUCCESS) { 0156 qWarning() << Q_FUNC_INFO << "unable to create iterator"; 0157 return QStringList(); 0158 } 0159 0160 return IOKitManagerPrivate::devicesFromRegistry(it); 0161 } 0162 0163 QStringList IOKitManager::devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type) 0164 { 0165 QStringList result; 0166 0167 if (type == Solid::DeviceInterface::Unknown) { 0168 // match all device interfaces 0169 result = allDevices(); 0170 } else { 0171 const char *deviceClassName = IOKitManagerPrivate::typeToName(type); 0172 if (!deviceClassName) { 0173 return QStringList(); 0174 } 0175 0176 CFMutableDictionaryRef matchingDict = IOServiceMatching(deviceClassName); 0177 0178 if (!matchingDict) { 0179 return QStringList(); 0180 } 0181 0182 io_iterator_t it = 0; 0183 0184 // note - IOServiceGetMatchingServices dereferences the dict 0185 kern_return_t ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &it); 0186 0187 result = IOKitManagerPrivate::devicesFromRegistry(it); 0188 } 0189 0190 // if the parentUdi is an empty string, return all matches 0191 if (parentUdi.isEmpty()) { 0192 return result; 0193 } 0194 0195 // return only matches that start with the parent's UDI 0196 QStringList filtered; 0197 for (const QString &udi : std::as_const(result)) { 0198 if (udi.startsWith(parentUdi)) { 0199 filtered += udi; 0200 } 0201 } 0202 0203 return filtered; 0204 } 0205 0206 QObject *IOKitManager::createDevice(const QString &udi) 0207 { 0208 CFStringRef path = udi.toCFString(); 0209 io_registry_entry_t entry = IORegistryEntryCopyFromPath(kIOMasterPortDefault, path); 0210 CFRelease(path); 0211 0212 // we have to do IOObjectConformsTo - comparing the class names is not good enough 0213 // if (IOObjectConformsTo(entry, kIOEthernetInterfaceClass)) { 0214 //} 0215 0216 if (entry == MACH_PORT_NULL) { 0217 return 0; 0218 } 0219 0220 return new IOKitDevice(udi, entry); 0221 } 0222 0223 } 0224 } 0225 } // namespaces 0226 0227 #include "moc_iokitmanager.cpp"