[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [solid] src/solid/devices: Mac/IOKit backend: support for drives, discs and volumes
From:       René J.V. Bertin <null () kde ! org>
Date:       2018-02-17 10:33:00
Message-ID: E1emzng-0003RT-Nd () code ! kde ! org
[Download RAW message or body]

Git commit cbe5085a646e9ff9468c9712aabd468df9ad0689 by René J.V. Bertin.
Committed on 17/02/2018 at 10:31.
Pushed by rjvbb into branch 'master'.

Mac/IOKit backend: support for drives, discs and volumes

This updates and improves the existing skeleton IOKit backend for
Solid, notably adding support for drives, discs and volumes.

Differential revision: https://phabricator.kde.org/D7401

M  +1    -1    src/solid/devices/CMakeLists.txt
M  +12   -1    src/solid/devices/backends/iokit/CMakeLists.txt
M  +18   -0    src/solid/devices/backends/iokit/cfhelper.cpp
A  +98   -0    src/solid/devices/backends/iokit/dadictionary.cpp     [License: LGPL]
C  +22   -15   src/solid/devices/backends/iokit/dadictionary_p.h [from: \
src/solid/devices/backends/iokit/iokitdeviceinterface.h - 058% similarity] M  +99   \
-17   src/solid/devices/backends/iokit/iokitbattery.cpp M  +38   -37   \
src/solid/devices/backends/iokit/iokitbattery.h C  +16   -16   \
src/solid/devices/backends/iokit/iokitblock.cpp [from: \
src/solid/devices/backends/iokit/iokitprocessor.cpp - 061% similarity] C  +13   -15   \
src/solid/devices/backends/iokit/iokitblock.h [from: \
src/solid/devices/backends/iokit/iokitprocessor.h - 062% similarity] M  +333  -28   \
src/solid/devices/backends/iokit/iokitdevice.cpp M  +13   -10   \
src/solid/devices/backends/iokit/iokitdevice.h M  +14   -1    \
src/solid/devices/backends/iokit/iokitdeviceinterface.cpp M  +6    -0    \
src/solid/devices/backends/iokit/iokitdeviceinterface.h M  +1    -1    \
src/solid/devices/backends/iokit/iokitgenericinterface.cpp M  +8    -6    \
src/solid/devices/backends/iokit/iokitmanager.cpp A  +120  -0    \
src/solid/devices/backends/iokit/iokitopticaldisc.cpp     [License: LGPL] A  +58   -0 \
src/solid/devices/backends/iokit/iokitopticaldisc.h     [License: LGPL] A  +358  -0   \
src/solid/devices/backends/iokit/iokitopticaldrive.cpp     [License: LGPL] A  +62   \
-0    src/solid/devices/backends/iokit/iokitopticaldrive.h     [License: LGPL] M  +44 \
-3    src/solid/devices/backends/iokit/iokitprocessor.cpp M  +6    -4    \
src/solid/devices/backends/iokit/iokitprocessor.h A  +119  -0    \
src/solid/devices/backends/iokit/iokitstorage.cpp     [License: LGPL] C  +27   -17   \
src/solid/devices/backends/iokit/iokitstorage.h [from: \
src/solid/devices/backends/iokit/iokitprocessor.h - 050% similarity] A  +110  -0    \
src/solid/devices/backends/iokit/iokitstorageaccess.cpp     [License: LGPL] A  +67   \
-0    src/solid/devices/backends/iokit/iokitstorageaccess.h     [License: LGPL] A  \
+113  -0    src/solid/devices/backends/iokit/iokitvolume.cpp     [License: LGPL] A  \
+67   -0    src/solid/devices/backends/iokit/iokitvolume.h     [License: LGPL]

https://commits.kde.org/solid/cbe5085a646e9ff9468c9712aabd468df9ad0689

diff --git a/src/solid/devices/CMakeLists.txt b/src/solid/devices/CMakeLists.txt
index bd4153e..fcc1319 100644
--- a/src/solid/devices/CMakeLists.txt
+++ b/src/solid/devices/CMakeLists.txt
@@ -172,7 +172,7 @@ if(WIN32)
 endif()
 
 if(APPLE)
-   set(solid_OPTIONAL_LIBS ${IOKIT_LIBRARY})
+   set(solid_OPTIONAL_LIBS ${IOKIT_LIBRARY} "-framework DiskArbitration")
 endif()
 
 if ( UDEV_FOUND )
diff --git a/src/solid/devices/backends/iokit/CMakeLists.txt \
b/src/solid/devices/backends/iokit/CMakeLists.txt index dcbc666..dcc9e5f 100644
--- a/src/solid/devices/backends/iokit/CMakeLists.txt
+++ b/src/solid/devices/backends/iokit/CMakeLists.txt
@@ -1,9 +1,20 @@
+include_directories(
+    ${Qt5Core_PRIVATE_INCLUDE_DIRS}
+)
+
 set(solid_LIB_SRCS ${solid_LIB_SRCS}
     devices/backends/iokit/iokitmanager.cpp
     devices/backends/iokit/iokitdevice.cpp
     devices/backends/iokit/cfhelper.cpp
+    devices/backends/iokit/dadictionary.cpp
     devices/backends/iokit/iokitdeviceinterface.cpp
     devices/backends/iokit/iokitgenericinterface.cpp
     devices/backends/iokit/iokitprocessor.cpp
     devices/backends/iokit/iokitbattery.cpp
-)
\ No newline at end of file
+    devices/backends/iokit/iokitblock.cpp
+    devices/backends/iokit/iokitstorage.cpp
+    devices/backends/iokit/iokitvolume.cpp
+    devices/backends/iokit/iokitstorageaccess.cpp
+    devices/backends/iokit/iokitopticaldrive.cpp
+    devices/backends/iokit/iokitopticaldisc.cpp
+)
diff --git a/src/solid/devices/backends/iokit/cfhelper.cpp \
b/src/solid/devices/backends/iokit/cfhelper.cpp index cb33bd3..e78131f 100644
--- a/src/solid/devices/backends/iokit/cfhelper.cpp
+++ b/src/solid/devices/backends/iokit/cfhelper.cpp
@@ -1,5 +1,6 @@
 /*
     Copyright 2009 Harald Fernengel <harry@kdevelop.org>
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
@@ -27,6 +28,8 @@
 
 #include <CoreFoundation/CoreFoundation.h>
 
+#include <sys/sysctl.h>
+
 /* helper classes to convert from CF types to Qt */
 
 static QString q_toString(const CFStringRef &str)
@@ -175,3 +178,18 @@ QMap<QString, QVariant> q_toVariantMap(const \
CFMutableDictionaryRef &dict)  return result;
 }
 
+bool q_sysctlbyname(const char *name, QString &result)
+{
+    char *property = nullptr;
+    size_t size = 0;
+    int error = 0;
+    if (name && sysctlbyname(name, nullptr, &size, nullptr, 0) == 0 && size > 0) {
+        property = new char [size];
+        error = sysctlbyname(name, property, &size, nullptr, 0);
+        if (!error) {
+            result = QLatin1String(property);
+        }
+        delete[] property;
+    }
+    return !error;
+}
diff --git a/src/solid/devices/backends/iokit/dadictionary.cpp \
b/src/solid/devices/backends/iokit/dadictionary.cpp new file mode 100644
index 0000000..af9d3de
--- /dev/null
+++ b/src/solid/devices/backends/iokit/dadictionary.cpp
@@ -0,0 +1,98 @@
+/*
+    Copyright 2018 René J.V. Bertin <rjvbertin@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dadictionary_p.h"
+
+using namespace Solid::Backends::IOKit;
+
+DADictionary::DADictionary(const IOKitDevice *device)
+    : device(device)
+    , daDict(nullptr)
+{
+    daSession = DASessionCreate(kCFAllocatorDefault);
+    if (daSession) {
+        const QString devName = device->property(QStringLiteral("BSD \
Name")).toString(); +        daRef = DADiskCreateFromBSDName(kCFAllocatorDefault, \
daSession, devName.toStdString().c_str()); +    } else {
+        daRef = nullptr;
+    }
+}
+
+DADictionary::~DADictionary()
+{
+    releaseDict();
+    if (daRef) {
+        CFRelease(daRef);
+        daRef = nullptr;
+    }
+    if (daSession) {
+        CFRelease(daSession);
+        daSession = nullptr;
+    }
+}
+
+bool DADictionary::getDict()
+{
+    if (daRef) {
+        daDict = DADiskCopyDescription(daRef);
+    }
+    return daRef != nullptr;
+}
+
+void DADictionary::releaseDict()
+{
+    if (daDict) {
+        CFRelease(daDict);
+        daDict = nullptr;
+    }
+}
+
+const QString DADictionary::stringForKey(const CFStringRef key)
+{
+    QString ret;
+    if (getDict()) {
+        ret = QString::fromCFString((const CFStringRef) CFDictionaryGetValue(daDict, \
key)); +    }
+    releaseDict();
+    return ret;
+}
+
+CFURLRef DADictionary::cfUrLRefForKey(const CFStringRef key)
+{
+    CFURLRef ret = nullptr;
+    if (getDict()) {
+        ret = (const CFURLRef) CFDictionaryGetValue(daDict, key);
+    }
+    // we cannot release the dictionary here, or else we'd need to
+    // copy the CFURLRef and oblige our caller to release the return value.
+    return ret;
+}
+
+bool DADictionary::boolForKey(const CFStringRef key, bool &value)
+{
+    if (getDict()) {
+        const CFBooleanRef boolRef = (const CFBooleanRef) \
CFDictionaryGetValue(daDict, key); +        if (boolRef) {
+            value = CFBooleanGetValue(boolRef);
+        }
+        return boolRef != nullptr;
+    }
+    return false;
+}
diff --git a/src/solid/devices/backends/iokit/iokitdeviceinterface.h \
b/src/solid/devices/backends/iokit/dadictionary_p.h similarity index 58%
copy from src/solid/devices/backends/iokit/iokitdeviceinterface.h
copy to src/solid/devices/backends/iokit/dadictionary_p.h
index 38eacf4..28cfc7f 100644
--- a/src/solid/devices/backends/iokit/iokitdeviceinterface.h
+++ b/src/solid/devices/backends/iokit/dadictionary_p.h
@@ -1,5 +1,5 @@
 /*
-    Copyright 2009 Harald Fernengel <harry@kdevelop.org>
+    Copyright 2018 René J.V. Bertin <rjvbertin@gmail.com>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
@@ -18,14 +18,15 @@
     License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */
 
-#ifndef SOLID_BACKENDS_IOKIT_DEVICEINTERFACE_H
-#define SOLID_BACKENDS_IOKIT_DEVICEINTERFACE_H
+#ifndef SOLID_BACKENDS_IOKIT_DADICTIONARY_H
+#define SOLID_BACKENDS_IOKIT_DADICTIONARY_H
+
+#include <QString>
 
-#include <solid/devices/ifaces/deviceinterface.h>
 #include "iokitdevice.h"
 
-#include <QtCore/QObject>
-#include <QtCore/QStringList>
+#include <CoreFoundation/CoreFoundation.h>
+#include <DiskArbitration/DiskArbitration.h>
 
 namespace Solid
 {
@@ -33,19 +34,25 @@ namespace Backends
 {
 namespace IOKit
 {
-class DeviceInterface : public QObject, virtual public \
Solid::Ifaces::DeviceInterface +class DADictionary
 {
-    Q_OBJECT
-    Q_INTERFACES(Solid::Ifaces::DeviceInterface)
 public:
-    DeviceInterface(IOKitDevice *device);
-    virtual ~DeviceInterface();
-
-protected:
-    IOKitDevice *m_device;
+    DADictionary(const IOKitDevice *device);
+    virtual ~DADictionary();
+
+    bool getDict();
+    void releaseDict();
+    const QString stringForKey(const CFStringRef key);
+    CFURLRef cfUrLRefForKey(const CFStringRef key);
+    bool boolForKey(const CFStringRef key, bool &value);
+
+    const IOKitDevice *device;
+    DASessionRef daSession;
+    DADiskRef daRef;
+    CFDictionaryRef daDict;
 };
 }
 }
 }
 
-#endif // SOLID_BACKENDS_IOKIT_DEVICEINTERFACE_H
+#endif // SOLID_BACKENDS_IOKIT_DADICTIONARY_H
diff --git a/src/solid/devices/backends/iokit/iokitbattery.cpp \
b/src/solid/devices/backends/iokit/iokitbattery.cpp index 04646cc..69391aa 100644
--- a/src/solid/devices/backends/iokit/iokitbattery.cpp
+++ b/src/solid/devices/backends/iokit/iokitbattery.cpp
@@ -1,5 +1,6 @@
 /*
     Copyright 2009 Harald Fernengel <harry@kdevelop.org>
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
@@ -21,7 +22,7 @@
 #include "iokitbattery.h"
 #include "iokitdevice.h"
 
-#include <QtCore/qdebug.h>
+#include <QDebug>
 
 // TODO - emit the signals
 
@@ -34,57 +35,138 @@ Battery::Battery(IOKitDevice *device)
 
 Battery::~Battery()
 {
+}
+
+// properties: QMap(("AdapterInfo", QVariant(int, 0))
+//     ("Amperage", QVariant(int, 0))
+//     ("AvgTimeToEmpty", QVariant(int, 65535))
+//     ("AvgTimeToFull", QVariant(int, 65535))
+//     ("BatteryInstalled", QVariant(bool, true))
+//     ("BatteryInvalidWakeSeconds", QVariant(int, 30))
+//     ("BatterySerialNumber", QVariant(QString, "W01286PEED3BA"))
+//     ("BootPathUpdated", QVariant(int, 1501532930))
+//     ("CellVoltage", QVariant(QVariantList, (QVariant(int, 4136), QVariant(int, \
4134), QVariant(int, 4134), QVariant(int, 0)))) +//     ("CurrentCapacity", \
QVariant(int, 5552)) +//     ("CycleCount", QVariant(int, 16))
+//     ("DesignCapacity", QVariant(int, 5770))
+//     ("DesignCycleCount", QVariant(int, 1000))
+//     ("DeviceName", QVariant(QString, "bq20z451"))
+//     ("ExternalChargeCapable", QVariant(bool, true))
+//     ("ExternalConnected", QVariant(bool, true))
+//     ("FirmwareSerialNumber", QVariant(int, 48))
+//     ("FullPathUpdated", QVariant(int, 1502790621))
+//     ("FullyCharged", QVariant(bool, true))
+//     ("IOGeneralInterest", QVariant(QString, "IOCommand is not serializable"))
+//     ("InstantAmperage", QVariant(int, 0))
+//     ("InstantTimeToEmpty", QVariant(int, 65535))
+//     ("IsCharging", QVariant(bool, false))
+//     ("LegacyBatteryInfo", QVariant(QVariantMap, QMap(("Amperage", QVariant(int, \
0)) +//         ("Capacity", QVariant(int, 5814))
+//         ("Current", QVariant(int, 5552))
+//         ("Cycle Count", QVariant(int, 16))
+//         ("Flags", QVariant(int, 5))
+//         ("Voltage", QVariant(int, 12403)))))
+//     ("Location", QVariant(int, 0))
+//     ("ManufactureDate", QVariant(int, 16106))
+//     ("Manufacturer", QVariant(QString, "SMP"))
+//     ("ManufacturerData", QVariant(QByteArray, \
"\x00\x00\x00\x00\x02\x01\x00\n\x01X\x00\x00\x02K6c\x03""00A\x03""ATL\x00\x12\x00\x00"))
 +//     ("MaxCapacity", QVariant(int, 5814))
+//     ("MaxErr", QVariant(int, 1))
+//     ("OperationStatus", QVariant(int, 58435))
+//     ("PackReserve", QVariant(int, 200))
+//     ("PermanentFailureStatus", QVariant(int, 0))
+//     ("PostChargeWaitSeconds", QVariant(int, 120))
+//     ("PostDischargeWaitSeconds", QVariant(int, 120))
+//     ("Temperature", QVariant(int, 2965))
+//     ("TimeRemaining", QVariant(int, 0))
+//     ("UserVisiblePathUpdated", QVariant(int, 1502790679))
+//     ("Voltage", QVariant(int, 12403))
+//     ("className", QVariant(QString, "AppleSmartBattery")))
+
+qlonglong Battery::timeToEmpty() const
+{
+    if (chargeState() != Solid::Battery::Charging) {
+        int t = m_device->property(QStringLiteral("AvgTimeToEmpty")).toInt();
+        return t == 65535 ? -1 : t * 60;
+    }
+    return -1;
+}
+
+qlonglong Battery::timeToFull() const
+{
+    if (chargeState() == Solid::Battery::Charging) {
+        int t = m_device->property(QStringLiteral("AvgTimeToFull")).toInt();
+        return t == 65535 ? -1 : t * 60;
+    }
+    return -1;
+}
+
+double Battery::voltage() const
+{
+    return m_device->property(QStringLiteral("Voltage")).toInt() / 1000.0;
+}
+
+double Battery::temperature() const
+{
+    return m_device->property(QStringLiteral("Temperature")).toInt() / 100.0;
+}
 
+
+QString Battery::serial() const
+{
+    return m_device->property(QStringLiteral("BatterySerialNumber")).toString();
 }
 
 bool Battery::isPresent() const
 {
-    return m_device->property(QLatin1String("ExternalConnected")).toBool();
+    return m_device->property(QStringLiteral("ExternalConnected")).toBool();
 }
 
 Solid::Battery::BatteryType Battery::type() const
 {
-    // TODO - how to figure that one out?
-    return Solid::Battery::UnknownBattery;
+    // TODO - how to figure that one out? Just presume we're
+    // only called with the main battery.
+    return Solid::Battery::PrimaryBattery;
 }
 
 int Battery::chargePercent() const
 {
-    if (m_device->property(QLatin1String("FullyCharged")).toBool()) {
-        return 100;
-    }
-
-    int maxCapacity = m_device->property(QLatin1String("MaxCapacity")).toInt();
+    // always calculate since FullyCharged remains true down to 92% or so.
+    int maxCapacity = m_device->property(QStringLiteral("MaxCapacity")).toInt();
     if (maxCapacity == 0) {
         return 0;    // prevent divide by 0
     }
-    return m_device->property(QLatin1String("CurrentCapacity")).toInt() / \
maxCapacity; +    return \
int(m_device->property(QStringLiteral("CurrentCapacity")).toInt() * 100.0 / \
maxCapacity + 0.5);  }
 
 int Battery::capacity() const
 {
-    // TODO
+    if (m_device->iOKitPropertyExists(QStringLiteral("PermanentFailureStatus"))
+        && m_device->property(QStringLiteral("PermanentFailureStatus")).toInt()) {
+        return 0;
+    }
     return 100;
 }
 
 bool Battery::isRechargeable() const
 {
-    return m_device->property(QLatin1String("DesignCycleCount")).toInt() > 1;
+    return m_device->property(QStringLiteral("CycleCount")).toInt() > 1;
 }
 
 bool Battery::isPowerSupply() const
 {
-    // TODO
-    return true;
+    return m_device->iOKitPropertyExists(QStringLiteral("BatteryInstalled"))
+        ? m_device->property(QStringLiteral("BatteryInstalled")).toBool()
+        : true;
 }
 
 Solid::Battery::ChargeState Battery::chargeState() const
 {
-    if (m_device->property(QLatin1String("IsCharging")).toBool()) {
+    if (m_device->property(QStringLiteral("IsCharging")).toBool()) {
         return Solid::Battery::Charging;
     }
-    if (m_device->property(QLatin1String("FullyCharged")).toBool()) {
-        return Solid::Battery::NoCharge;
+    if (m_device->property(QStringLiteral("FullyCharged")).toBool()) {
+        return Solid::Battery::FullyCharged;
     }
     return Solid::Battery::Discharging;
 }
diff --git a/src/solid/devices/backends/iokit/iokitbattery.h \
b/src/solid/devices/backends/iokit/iokitbattery.h index 19224a7..5aff592 100644
--- a/src/solid/devices/backends/iokit/iokitbattery.h
+++ b/src/solid/devices/backends/iokit/iokitbattery.h
@@ -41,50 +41,51 @@ public:
     Battery(IOKitDevice *device);
     virtual ~Battery();
 
-    bool isPresent() const;
-    Solid::Battery::BatteryType type() const;
+    bool isPresent() const Q_DECL_OVERRIDE;
+    Solid::Battery::BatteryType type() const Q_DECL_OVERRIDE;
 
-    int chargePercent() const;
-    int capacity() const;
+    int chargePercent() const Q_DECL_OVERRIDE;
+    int capacity() const Q_DECL_OVERRIDE;
 
-    bool isRechargeable() const;
-    bool isPowerSupply() const;
+    bool isRechargeable() const Q_DECL_OVERRIDE;
+    bool isPowerSupply() const Q_DECL_OVERRIDE;
 
-    Solid::Battery::ChargeState chargeState() const;
+    Solid::Battery::ChargeState chargeState() const Q_DECL_OVERRIDE;
+
+    qlonglong timeToEmpty() const Q_DECL_OVERRIDE;
+    qlonglong timeToFull() const Q_DECL_OVERRIDE;
+    double voltage() const Q_DECL_OVERRIDE;
+    double temperature() const Q_DECL_OVERRIDE;
+    QString serial() const Q_DECL_OVERRIDE;
 
     // ### the ones below are TODO
-    qlonglong timeToEmpty() const { return 0; }
-    qlonglong timeToFull() const { return 0; }
-    Solid::Battery::Technology technology() const { return \
                Solid::Battery::UnknownTechnology; }
-    double energy() const { return 0.0; }
-    double energyFull() const { return 0.0; }
-    double energyFullDesign() const { return 0.0; }
-    double energyRate() const { return 0.0; }
-    double voltage() const { return 0.0; }
-    double temperature() const { return 0.0; }
-
-    bool isRecalled() const { return false; }
-    QString recallVendor() const { return QString(); }
-    QString recallUrl() const { return QString(); }
-    QString serial() const { return QString(); }
-
-    qlonglong remainingTime() const { return -1; }
+    Solid::Battery::Technology technology() const Q_DECL_OVERRIDE { return \
Solid::Battery::UnknownTechnology; } +    double energy() const Q_DECL_OVERRIDE { \
return 0.0; } +    double energyFull() const Q_DECL_OVERRIDE { return 0.0; }
+    double energyFullDesign() const Q_DECL_OVERRIDE { return 0.0; }
+    double energyRate() const Q_DECL_OVERRIDE { return 0.0; }
+
+    bool isRecalled() const Q_DECL_OVERRIDE { return false; }
+    QString recallVendor() const Q_DECL_OVERRIDE { return QString(); }
+    QString recallUrl() const Q_DECL_OVERRIDE { return QString(); }
+
+    qlonglong remainingTime() const Q_DECL_OVERRIDE { return -1; }
 
 Q_SIGNALS:
-    void energyChanged(double energy, const QString &udi);
-    void energyFullChanged(double energyFull, const QString &udi);
-    void energyFullDesignChanged(double energyFullDesign, const QString &udi);
-    void energyRateChanged(double energyRate, const QString &udi);
-    void chargePercentChanged(int value, const QString &udi);
-    void capacityChanged(int value, const QString &udi);
-    void chargeStateChanged(int newState, const QString &udi);
-    void presentStateChanged(bool newState, const QString &udi);
-    void powerSupplyStateChanged(bool newState, const QString &udi);
-    void timeToEmptyChanged(qlonglong time, const QString &udi);
-    void timeToFullChanged(qlonglong time, const QString &udi);
-    void temperatureChanged(double temperature, const QString &udi);
-    void voltageChanged(double voltage, const QString &udi);
-    void remainingTimeChanged(qlonglong time, const QString &udi);
+    void energyChanged(double energy, const QString &udi) Q_DECL_OVERRIDE;
+    void energyFullChanged(double energyFull, const QString &udi) Q_DECL_OVERRIDE;
+    void energyFullDesignChanged(double energyFullDesign, const QString &udi) \
Q_DECL_OVERRIDE; +    void energyRateChanged(double energyRate, const QString &udi) \
Q_DECL_OVERRIDE; +    void chargePercentChanged(int value, const QString &udi) \
Q_DECL_OVERRIDE; +    void capacityChanged(int value, const QString &udi) \
Q_DECL_OVERRIDE; +    void chargeStateChanged(int newState, const QString &udi) \
Q_DECL_OVERRIDE; +    void presentStateChanged(bool newState, const QString &udi) \
Q_DECL_OVERRIDE; +    void powerSupplyStateChanged(bool newState, const QString &udi) \
Q_DECL_OVERRIDE; +    void timeToEmptyChanged(qlonglong time, const QString &udi) \
Q_DECL_OVERRIDE; +    void timeToFullChanged(qlonglong time, const QString &udi) \
Q_DECL_OVERRIDE; +    void temperatureChanged(double temperature, const QString &udi) \
Q_DECL_OVERRIDE; +    void voltageChanged(double voltage, const QString &udi) \
Q_DECL_OVERRIDE; +    void remainingTimeChanged(qlonglong time, const QString &udi) \
Q_DECL_OVERRIDE;  };
 }
 }
diff --git a/src/solid/devices/backends/iokit/iokitprocessor.cpp \
b/src/solid/devices/backends/iokit/iokitblock.cpp similarity index 61%
copy from src/solid/devices/backends/iokit/iokitprocessor.cpp
copy to src/solid/devices/backends/iokit/iokitblock.cpp
index 1f2282a..44d1612 100644
--- a/src/solid/devices/backends/iokit/iokitprocessor.cpp
+++ b/src/solid/devices/backends/iokit/iokitblock.cpp
@@ -1,5 +1,5 @@
 /*
-    Copyright 2009 Harald Fernengel <harry@kdevelop.org>
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
@@ -18,41 +18,41 @@
     License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include "iokitprocessor.h"
-#include "iokitdevice.h"
+#include "iokitblock.h"
 
-#include <QtCore/qdebug.h>
+#include "iokitdevice.h"
 
 using namespace Solid::Backends::IOKit;
 
-Processor::Processor(IOKitDevice *device)
+Block::Block(IOKitDevice *device)
     : DeviceInterface(device)
 {
-    //IOKitDevice parent(device->parentUdi());
 }
 
-Processor::~Processor()
+Block::Block(const IOKitDevice *device)
+    : DeviceInterface(device)
 {
-
 }
 
-int Processor::number() const
+Block::~Block()
 {
-    return m_device->property(QLatin1String("IOCPUNumber")).toInt();
 }
 
-int Processor::maxSpeed() const
+int Block::deviceMajor() const
 {
-    return 0; // TODO
+    return m_device->property(QLatin1String("BSD Major")).toInt();
 }
 
-bool Processor::canChangeFrequency() const
+int Block::deviceMinor() const
 {
-    return false; // TODO
+    return m_device->property(QLatin1String("BSD Minor")).toInt();
 }
 
-Solid::Processor::InstructionSets Processor::instructionSets() const
+QString Block::device() const
 {
-    return 0; // TODO
+    if (m_device->iOKitPropertyExists(QStringLiteral("BSD Name"))) {
+        return QStringLiteral("/dev/") + m_device->property(QLatin1String("BSD \
Name")).toString(); +    }
+    return QString();
 }
 
diff --git a/src/solid/devices/backends/iokit/iokitprocessor.h \
b/src/solid/devices/backends/iokit/iokitblock.h similarity index 62%
copy from src/solid/devices/backends/iokit/iokitprocessor.h
copy to src/solid/devices/backends/iokit/iokitblock.h
index 4fd6394..b3e1777 100644
--- a/src/solid/devices/backends/iokit/iokitprocessor.h
+++ b/src/solid/devices/backends/iokit/iokitblock.h
@@ -1,5 +1,5 @@
 /*
-    Copyright 2009 Harald Fernengel <harry@kdevelop.org>
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
@@ -18,10 +18,10 @@
     License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */
 
-#ifndef SOLID_BACKENDS_IOKIT_PROCESSOR_H
-#define SOLID_BACKENDS_IOKIT_PROCESSOR_H
+#ifndef SOLID_BACKENDS_IOKIT_BLOCK_H
+#define SOLID_BACKENDS_IOKIT_BLOCK_H
 
-#include <solid/devices/ifaces/processor.h>
+#include <solid/devices/ifaces/block.h>
 #include "iokitdeviceinterface.h"
 
 namespace Solid
@@ -30,24 +30,22 @@ namespace Backends
 {
 namespace IOKit
 {
-class IOKitDevice;
-
-class Processor : public DeviceInterface, virtual public Solid::Ifaces::Processor
+class Block : public DeviceInterface, virtual public Solid::Ifaces::Block
 {
     Q_OBJECT
-    Q_INTERFACES(Solid::Ifaces::Processor)
+    Q_INTERFACES(Solid::Ifaces::Block)
 
 public:
-    Processor(IOKitDevice *device);
-    virtual ~Processor();
+    Block(IOKitDevice *device);
+    Block(const IOKitDevice *device);
+    virtual ~Block();
 
-    virtual int number() const;
-    virtual int maxSpeed() const;
-    virtual bool canChangeFrequency() const;
-    virtual Solid::Processor::InstructionSets instructionSets() const;
+    virtual int deviceMajor() const Q_DECL_OVERRIDE;
+    virtual int deviceMinor() const Q_DECL_OVERRIDE;
+    virtual QString device() const Q_DECL_OVERRIDE;
 };
 }
 }
 }
 
-#endif // SOLID_BACKENDS_IOKIT_PROCESSOR_H
+#endif // SOLID_BACKENDS_IOKIT_BLOCK_H
diff --git a/src/solid/devices/backends/iokit/iokitdevice.cpp \
b/src/solid/devices/backends/iokit/iokitdevice.cpp index 1e7d9ae..26d33a6 100644
--- a/src/solid/devices/backends/iokit/iokitdevice.cpp
+++ b/src/solid/devices/backends/iokit/iokitdevice.cpp
@@ -1,5 +1,6 @@
 /*
     Copyright 2009 Harald Fernengel <harry@kdevelop.org>
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
@@ -22,8 +23,19 @@
 #include "iokitgenericinterface.h"
 #include "iokitprocessor.h"
 #include "iokitbattery.h"
+#include "iokitstorage.h"
+#include "iokitstorageaccess.h"
+#include "iokitvolume.h"
+#include "iokitopticaldrive.h"
+#include "iokitopticaldisc.h"
 
-#include <QtCore/qdebug.h>
+#include <QDebug>
+#include <QSet>
+#include <QPointer>
+#include <QUrl>
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
 
 #include <IOKit/IOKitLib.h>
 #include <IOKit/usb/IOUSBLib.h>
@@ -33,6 +45,9 @@
 
 // from cfhelper.cpp
 extern QMap<QString, QVariant> q_toVariantMap(const CFMutableDictionaryRef &dict);
+extern bool q_sysctlbyname(const char *name, QString &result);
+
+typedef QSet<Solid::DeviceInterface::Type> DeviceInterfaceTypes;
 
 namespace Solid
 {
@@ -42,17 +57,48 @@ namespace IOKit
 {
 
 // returns a solid type from an entry and its properties
-static Solid::DeviceInterface::Type typeFromEntry(const io_registry_entry_t &entry,
-        const QMap<QString, QVariant> &properties)
+static DeviceInterfaceTypes typesFromEntry(const io_registry_entry_t &entry,
+        const QMap<QString, QVariant> &properties,
+        Solid::DeviceInterface::Type &mainType)
 {
+    DeviceInterfaceTypes types;
+    mainType = Solid::DeviceInterface::Unknown;
     if (IOObjectConformsTo(entry, "AppleACPICPU")) {
-        return Solid::DeviceInterface::Processor;
+        mainType = Solid::DeviceInterface::Processor;
+        types << mainType;
     }
     if (IOObjectConformsTo(entry, "AppleSmartBattery")) {
-        return Solid::DeviceInterface::Battery;
+        mainType = Solid::DeviceInterface::Battery;
+        types << mainType;
+    }
+    const QString bsdName = QStringLiteral("BSD Name"),
+        leaf = QStringLiteral("Leaf");
+    if (IOObjectConformsTo(entry, "IOCDMedia")
+        || IOObjectConformsTo(entry, "IODVDMedia")
+        || IOObjectConformsTo(entry, "IOBDMedia")) {
+        mainType = Solid::DeviceInterface::OpticalDrive;
+        types << mainType
+            << Solid::DeviceInterface::OpticalDisc;
+    }
+    if (properties.contains(bsdName) && \
properties.value(bsdName).toString().startsWith(QStringLiteral("disk"))) { +        \
if ((properties.contains(leaf) && properties.value(leaf).toBool() == false) +         \
|| mainType == Solid::DeviceInterface::OpticalDrive) { +            if (mainType == \
Solid::DeviceInterface::Unknown) { +                mainType = \
Solid::DeviceInterface::StorageDrive; +            }
+            types << Solid::DeviceInterface::StorageDrive;
+        } else if (mainType == Solid::DeviceInterface::Unknown) {
+            mainType = Solid::DeviceInterface::StorageVolume;
+        }
+        types << Solid::DeviceInterface::StorageVolume;
+    }
+
+    if (types.isEmpty()) {
+        types << mainType;
+//         qWarning() << "unsupported entry" << entry << "with properties" << \
properties;  }
 
-    return Solid::DeviceInterface::Unknown;
+    return types;
 }
 
 // gets all properties from an entry into a QMap
@@ -68,6 +114,10 @@ static QMap<QString, QVariant> getProperties(const \
io_registry_entry_t &entry)  
     CFRelease(propertyDict);
 
+    io_name_t className;
+    IOObjectGetClass(entry, className);
+    result["className"] = QString::fromUtf8(className);
+
     return result;
 }
 
@@ -94,19 +144,38 @@ static QString getParentDeviceUdi(const io_registry_entry_t \
&entry)  return result;
 }
 
+static const QString computerModel()
+{
+    QString qModel;
+    q_sysctlbyname("hw.model", qModel);
+    return qModel;
+}
+
 class IOKitDevicePrivate
 {
 public:
     inline IOKitDevicePrivate()
-        : type(Solid::DeviceInterface::Unknown)
-    {}
+        : type({Solid::DeviceInterface::Unknown})
+        , parentDevice(nullptr)
+    {
+    }
+    ~IOKitDevicePrivate()
+    {
+        if (parentDevice) {
+            delete parentDevice;
+            parentDevice = nullptr;
+        }
+    }
 
     void init(const QString &udiString, const io_registry_entry_t &entry);
+    IOKitDevice* getParentDevice();
 
     QString udi;
     QString parentUdi;
     QMap<QString, QVariant> properties;
-    Solid::DeviceInterface::Type type;
+    DeviceInterfaceTypes type;
+    Solid::DeviceInterface::Type mainType;
+    IOKitDevice *parentDevice;
 };
 
 void IOKitDevicePrivate::init(const QString &udiString, const io_registry_entry_t \
&entry) @@ -117,16 +186,26 @@ void IOKitDevicePrivate::init(const QString &udiString, \
const io_registry_entry_  
     properties = getProperties(entry);
 
-    io_name_t className;
-    IOObjectGetClass(entry, className);
-    properties["className"] = QString::fromUtf8(className);
-
     parentUdi = getParentDeviceUdi(entry);
-    type = typeFromEntry(entry, properties);
+    type = typesFromEntry(entry, properties, mainType);
+    if (udi.contains(QStringLiteral("IOBD")) || udi.contains(QStringLiteral("BD \
PX"))) { +        qWarning() << "Solid: BlueRay entry" << entry << "mainType=" << \
mainType << "typeList:" << type +            << "with properties" << properties;
+    }
+    if (mainType != Solid::DeviceInterface::Unknown) {
+    }
 
     IOObjectRelease(entry);
 }
 
+IOKitDevice* IOKitDevicePrivate::getParentDevice()
+{
+    if (!parentDevice) {
+        parentDevice = new IOKitDevice(parentUdi);
+    }
+    return parentDevice;
+}
+
 IOKitDevice::IOKitDevice(const QString &udi, const io_registry_entry_t &entry)
     : d(new IOKitDevicePrivate)
 {
@@ -136,23 +215,63 @@ IOKitDevice::IOKitDevice(const QString &udi, const \
io_registry_entry_t &entry)  IOKitDevice::IOKitDevice(const QString &udi)
     : d(new IOKitDevicePrivate)
 {
+    if (udi.isEmpty()) {
+        qWarning() << Q_FUNC_INFO << "Tried to create Device from empty UDI";
+        return;
+    }
+
     io_registry_entry_t entry = IORegistryEntryFromPath(
                                     kIOMasterPortDefault,
                                     udi.toLocal8Bit().constData());
 
     if (entry == MACH_PORT_NULL) {
-        qDebug() << Q_FUNC_INFO << "Tried to create Device from invalid UDI" << udi;
+        qWarning() << Q_FUNC_INFO << "Tried to create Device from invalid UDI" << \
udi;  return;
     }
 
     d->init(udi, entry);
 }
 
+IOKitDevice::IOKitDevice(const IOKitDevice &device)
+    : d(new IOKitDevicePrivate)
+{
+    if (device.udi().isEmpty()) {
+        qWarning() << Q_FUNC_INFO << "Tried to create Device from empty UDI";
+        return;
+    }
+
+    io_registry_entry_t entry = IORegistryEntryFromPath(
+                                    kIOMasterPortDefault,
+                                    device.udi().toLocal8Bit().constData());
+
+    if (entry == MACH_PORT_NULL) {
+        qWarning() << Q_FUNC_INFO << "Tried to create Device from invalid UDI" << \
device.udi(); +        return;
+    }
+
+    d->init(device.udi(), entry);
+}
+
 IOKitDevice::~IOKitDevice()
 {
     delete d;
 }
 
+bool IOKitDevice::conformsToIOKitClass(const QString &className) const
+{
+    bool conforms = false;
+    if (!className.isEmpty()) {
+        io_registry_entry_t entry = IORegistryEntryFromPath(
+                                    kIOMasterPortDefault,
+                                    udi().toLocal8Bit().constData());
+        if (entry != MACH_PORT_NULL) {
+            conforms = IOObjectConformsTo(entry, \
className.toLocal8Bit().constData()); +            IOObjectRelease(entry);
+        }
+    }
+    return conforms;
+}
+
 QString IOKitDevice::udi() const
 {
     return d->udi;
@@ -165,31 +284,167 @@ QString IOKitDevice::parentUdi() const
 
 QString IOKitDevice::vendor() const
 {
-    return QString(); // TODO
+    QString vendor;
+    if (parentUdi().isEmpty()) {
+        return QStringLiteral("Apple");
+    }
+    switch (d->mainType) {
+    case Solid::DeviceInterface::Processor:
+        return Processor::vendor();
+        break;
+    case Solid::DeviceInterface::Battery:
+        return property(QStringLiteral("Manufacturer")).toString();
+        break;
+    case Solid::DeviceInterface::StorageDrive:
+    case Solid::DeviceInterface::OpticalDrive:
+    case Solid::DeviceInterface::OpticalDisc:
+        return IOKitStorage(this).vendor();
+        break;
+    case Solid::DeviceInterface::StorageVolume:
+        return IOKitVolume(this).vendor();
+        break;
+    default:
+        return QString();
+        break;
+    }
+    return QString();
 }
 
 QString IOKitDevice::product() const
 {
+    if (parentUdi().isEmpty()) {
+        return computerModel();
+    }
+    switch (d->mainType) {
+    case Solid::DeviceInterface::Processor:
+        return Processor::product();
+        break;
+    case Solid::DeviceInterface::Battery:
+        return property(QStringLiteral("DeviceName")).toString();
+        break;
+    case Solid::DeviceInterface::StorageDrive:
+    case Solid::DeviceInterface::OpticalDrive:
+    case Solid::DeviceInterface::OpticalDisc:
+        return IOKitStorage(this).product();
+        break;
+    case Solid::DeviceInterface::StorageVolume:
+        return IOKitVolume(this).product();
+        break;
+    }
     return QString(); // TODO
 }
 
-QString IOKitDevice::icon() const
+QString IOKitDevice::description() const
 {
-    return QString(); // TODO
+    switch (d->mainType) {
+    case Solid::DeviceInterface::Processor:
+        return QStringLiteral("Processor");
+        break;
+    case Solid::DeviceInterface::Battery:
+        return QStringLiteral("Apple Smart Battery");
+        break;
+    case Solid::DeviceInterface::StorageDrive:
+    case Solid::DeviceInterface::OpticalDrive:
+    case Solid::DeviceInterface::OpticalDisc:
+        return IOKitStorage(this).description();
+        break;
+    case Solid::DeviceInterface::StorageVolume: {
+        const QString volLabel = IOKitVolume(this).description();
+        const QString mountPoint = IOKitStorageAccess(this).filePath();
+        if (volLabel.isEmpty()) {
+            return QUrl::fromLocalFile(mountPoint).fileName();
+        } else if (mountPoint.startsWith(QStringLiteral("/Volumes/"))) {
+            // Mac users will expect to see the name under which the volume is \
mounted here. +            return QString(QStringLiteral("%1 \
(%2)")).arg(QUrl::fromLocalFile(mountPoint).fileName()).arg(volLabel); +        }
+        return volLabel;
+        break;
+    }
+    }
+    return product(); // TODO
 }
 
-QStringList IOKitDevice::emblems() const
+QString IOKitDevice::icon() const
 {
-    return QStringList(); // TODO
+    // adapted from HalDevice::icon()
+    if (parentUdi().isEmpty()) {
+        if (computerModel().contains(QStringLiteral("MacBook"))) {
+            return QStringLiteral("computer-laptop");
+        } else {
+            return QStringLiteral("computer");
+        }
+
+    } else if (d->type.contains(Solid::DeviceInterface::StorageDrive)) {
+        IOKitStorage drive(this);
+        Solid::StorageDrive::DriveType driveType = drive.driveType();
+
+        switch (driveType) {
+        case Solid::StorageDrive::Floppy:
+            // why not :)
+            return QStringLiteral("media-floppy");
+            break;
+        case Solid::StorageDrive::CdromDrive: {
+            const IOKitOpticalDisc disc(this);
+            if (disc.availableContent() == Solid::OpticalDisc::Audio ) {
+                return QStringLiteral("media-optical-audio");
+            } else switch (disc.discType()) {
+                case Solid::OpticalDisc::CdRom:
+                    return QStringLiteral("media-optical-data");
+                    break;
+                case Solid::OpticalDisc::CdRecordable:
+                case Solid::OpticalDisc::CdRewritable:
+                    return QStringLiteral("media-optical-recordable");
+                    break;
+                case Solid::OpticalDisc::BluRayRom:
+                case Solid::OpticalDisc::BluRayRecordable:
+                case Solid::OpticalDisc::BluRayRewritable:
+                    return QStringLiteral("media-optical-blu-ray");
+                    break;
+            }
+            break;
+        }
+        case Solid::StorageDrive::SdMmc:
+            return QStringLiteral("media-flash-sd-mmc");
+            break;
+        case Solid::StorageDrive::CompactFlash:
+            return QStringLiteral("media-flash-cf");
+            break;
+        }
+        if (drive.bus() == Solid::StorageDrive::Usb) {
+            return QStringLiteral("drive-removable-media-usb");
+        }
+        if (drive.isRemovable()) {
+            return QStringLiteral("drive-removable-media");
+        }
+        return QStringLiteral("drive-harddisk");
+
+    } else if (d->mainType == Solid::DeviceInterface::StorageVolume) {
+    } else if (d->mainType == Solid::DeviceInterface::Battery) {
+        return QStringLiteral("battery");
+    } else if (d->mainType == Solid::DeviceInterface::Processor) {
+        return QStringLiteral("cpu"); // FIXME: Doesn't follow icon spec
+    } else {
+        QString iconName = d->getParentDevice()->icon();
+
+        if (!iconName.isEmpty()) {
+            return iconName;
+        }
+
+        return QStringLiteral("drive-harddisk");
+    }
+    return QString();
 }
 
-QString IOKitDevice::description() const
+QStringList IOKitDevice::emblems() const
 {
-    return product(); // TODO
+    return QStringList(); // TODO
 }
 
 QVariant IOKitDevice::property(const QString &key) const
 {
+    if (!d->properties.contains(key)) {
+        return QObject::property(key.toUtf8());
+    }
     return d->properties.value(key);
 }
 
@@ -198,35 +453,85 @@ QMap<QString, QVariant> IOKitDevice::allProperties() const
     return d->properties;
 }
 
-bool IOKitDevice::propertyExists(const QString &key) const
+bool IOKitDevice::iOKitPropertyExists(const QString &key) const
 {
     return d->properties.contains(key);
 }
 
 bool IOKitDevice::queryDeviceInterface(const Solid::DeviceInterface::Type &type) \
const  {
-    return (type == Solid::DeviceInterface::GenericInterface
-            || type == d->type);
+    switch (type) {
+    case Solid::DeviceInterface::GenericInterface:
+        return true;
+        break;
+    case Solid::DeviceInterface::StorageAccess:
+        if (d->type.contains(Solid::DeviceInterface::StorageDrive)
+            || d->type.contains(Solid::DeviceInterface::StorageVolume)) {
+            return true;
+        }
+        break;
+    default:
+        return d->type.contains(type);
+        break;
+    }
+    return false;
 }
 
 QObject *IOKitDevice::createDeviceInterface(const Solid::DeviceInterface::Type \
&type)  {
-    QObject *iface = 0;
+    QObject *iface = nullptr;
 
     switch (type) {
     case Solid::DeviceInterface::GenericInterface:
         iface = new GenericInterface(this);
         break;
     case Solid::DeviceInterface::Processor:
-        if (d->type == Solid::DeviceInterface::Processor) {
+        if (d->type.contains(Solid::DeviceInterface::Processor)) {
             iface = new Processor(this);
         }
         break;
     case Solid::DeviceInterface::Battery:
-        if (d->type == Solid::DeviceInterface::Battery) {
+        if (d->type.contains(Solid::DeviceInterface::Battery)) {
             iface = new Battery(this);
         }
         break;
+    case Solid::DeviceInterface::OpticalDrive:
+        if (d->type.contains(Solid::DeviceInterface::OpticalDrive)) {
+            iface = new IOKitOpticalDrive(this);
+        }
+        break;
+    case Solid::DeviceInterface::OpticalDisc:
+        if (d->type.contains(Solid::DeviceInterface::OpticalDisc)) {
+            iface = new IOKitOpticalDisc(this);
+        }
+        break;
+    case Solid::DeviceInterface::StorageDrive:
+        if (d->type.contains(Solid::DeviceInterface::StorageDrive)) {
+            iface = new IOKitStorage(this);
+        }
+        break;
+    case Solid::DeviceInterface::Block:
+        if (d->type.contains(Solid::DeviceInterface::OpticalDisc)) {
+            iface = new IOKitOpticalDisc(this);
+        } else if (d->type.contains(Solid::DeviceInterface::OpticalDrive)) {
+            iface = new IOKitOpticalDrive(this);
+        } else if (d->type.contains(Solid::DeviceInterface::StorageVolume)) {
+            iface = new IOKitVolume(this);
+        } else if (d->type.contains(Solid::DeviceInterface::StorageDrive)) {
+            iface = new IOKitStorage(this);
+        }
+        break;
+    case Solid::DeviceInterface::StorageVolume:
+        if (d->type.contains(Solid::DeviceInterface::StorageVolume)) {
+            iface = new IOKitVolume(this);
+        }
+        break;
+    case Solid::DeviceInterface::StorageAccess:
+        if (d->type.contains(Solid::DeviceInterface::StorageDrive)
+            || d->type.contains(Solid::DeviceInterface::StorageVolume)) {
+            iface = new IOKitStorageAccess(this);
+        }
+        break;
         // the rest is TODO
     }
 
diff --git a/src/solid/devices/backends/iokit/iokitdevice.h \
b/src/solid/devices/backends/iokit/iokitdevice.h index 73d61de..3e0a9e2 100644
--- a/src/solid/devices/backends/iokit/iokitdevice.h
+++ b/src/solid/devices/backends/iokit/iokitdevice.h
@@ -39,25 +39,28 @@ class IOKitDevice : public Solid::Ifaces::Device
 
 public:
     IOKitDevice(const QString &udi);
+    IOKitDevice(const IOKitDevice &device);
     virtual ~IOKitDevice();
 
-    virtual QString udi() const;
-    virtual QString parentUdi() const;
+    virtual QString udi() const Q_DECL_OVERRIDE;
+    virtual QString parentUdi() const Q_DECL_OVERRIDE;
 
-    virtual QString vendor() const;
-    virtual QString product() const;
-    virtual QString icon() const;
-    virtual QStringList emblems() const;
-    virtual QString description() const;
+    virtual QString vendor() const Q_DECL_OVERRIDE;
+    virtual QString product() const Q_DECL_OVERRIDE;
+    virtual QString icon() const Q_DECL_OVERRIDE;
+    virtual QStringList emblems() const Q_DECL_OVERRIDE;
+    virtual QString description() const Q_DECL_OVERRIDE;
 
     virtual QVariant property(const QString &key) const;
 
     virtual QMap<QString, QVariant> allProperties() const;
 
-    virtual bool propertyExists(const QString &key) const;
+    virtual bool iOKitPropertyExists(const QString &key) const;
 
-    virtual bool queryDeviceInterface(const Solid::DeviceInterface::Type &type) \
                const;
-    virtual QObject *createDeviceInterface(const Solid::DeviceInterface::Type \
&type); +    virtual bool queryDeviceInterface(const Solid::DeviceInterface::Type \
&type) const Q_DECL_OVERRIDE; +    virtual QObject *createDeviceInterface(const \
Solid::DeviceInterface::Type &type) Q_DECL_OVERRIDE; +
+    bool conformsToIOKitClass(const QString &className) const;
 
 Q_SIGNALS:
     void propertyChanged(const QMap<QString, int> &changes);
diff --git a/src/solid/devices/backends/iokit/iokitdeviceinterface.cpp \
b/src/solid/devices/backends/iokit/iokitdeviceinterface.cpp index c6dc746..d5a5d7e \
                100644
--- a/src/solid/devices/backends/iokit/iokitdeviceinterface.cpp
+++ b/src/solid/devices/backends/iokit/iokitdeviceinterface.cpp
@@ -1,5 +1,6 @@
 /*
     Copyright 2009 Harald Fernengel <harry@kdevelop.org>
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
@@ -23,11 +24,23 @@
 using namespace Solid::Backends::IOKit;
 
 DeviceInterface::DeviceInterface(IOKitDevice *device)
-    : QObject(device), m_device(device)
+    : QObject(device)
+    , m_device(device)
+    , m_deviceCopy(nullptr)
 {
 }
 
+DeviceInterface::DeviceInterface(const IOKitDevice *device)
+    : QObject(device->parent()), m_deviceCopy(new IOKitDevice(*device))
+{
+    m_device = m_deviceCopy;
+}
+
 DeviceInterface::~DeviceInterface()
 {
+    if (m_deviceCopy) {
+        delete m_deviceCopy;
+        m_deviceCopy = nullptr;
+    }
 }
 
diff --git a/src/solid/devices/backends/iokit/iokitdeviceinterface.h \
b/src/solid/devices/backends/iokit/iokitdeviceinterface.h index 38eacf4..a1c26ff \
                100644
--- a/src/solid/devices/backends/iokit/iokitdeviceinterface.h
+++ b/src/solid/devices/backends/iokit/iokitdeviceinterface.h
@@ -1,5 +1,6 @@
 /*
     Copyright 2009 Harald Fernengel <harry@kdevelop.org>
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
@@ -39,10 +40,15 @@ class DeviceInterface : public QObject, virtual public \
Solid::Ifaces::DeviceInte  Q_INTERFACES(Solid::Ifaces::DeviceInterface)
 public:
     DeviceInterface(IOKitDevice *device);
+    // the ctor taking a const device* argument makes a deep
+    // copy of the IOKitDevice; any property changes made via 
+    // the resulting instance will not affect the original device.
+    DeviceInterface(const IOKitDevice *device);
     virtual ~DeviceInterface();
 
 protected:
     IOKitDevice *m_device;
+    IOKitDevice *m_deviceCopy;
 };
 }
 }
diff --git a/src/solid/devices/backends/iokit/iokitgenericinterface.cpp \
b/src/solid/devices/backends/iokit/iokitgenericinterface.cpp index 29231f4..0a0a1d2 \
                100644
--- a/src/solid/devices/backends/iokit/iokitgenericinterface.cpp
+++ b/src/solid/devices/backends/iokit/iokitgenericinterface.cpp
@@ -46,6 +46,6 @@ QMap<QString, QVariant> GenericInterface::allProperties() const
 
 bool GenericInterface::propertyExists(const QString &key) const
 {
-    return m_device->propertyExists(key);
+    return m_device->iOKitPropertyExists(key);
 }
 
diff --git a/src/solid/devices/backends/iokit/iokitmanager.cpp \
b/src/solid/devices/backends/iokit/iokitmanager.cpp index 6b86f98..869ce93 100644
--- a/src/solid/devices/backends/iokit/iokitmanager.cpp
+++ b/src/solid/devices/backends/iokit/iokitmanager.cpp
@@ -40,7 +40,7 @@ class IOKitManagerPrivate
 {
 public:
     inline IOKitManagerPrivate()
-        : port(0), source(0)
+        : port(nullptr), source(nullptr)
     {}
 
     IONotificationPortRef port;
@@ -89,11 +89,13 @@ const char \
*IOKitManagerPrivate::typeToName(Solid::DeviceInterface::Type type)  
         //Solid::DeviceInterface::GenericInterface:
         //Solid::DeviceInterface::Block:
-        //Solid::DeviceInterface::StorageAccess:
-        //Solid::DeviceInterface::StorageDrive:
-        //Solid::DeviceInterface::OpticalDrive:
-        //Solid::DeviceInterface::StorageVolume:
-        //Solid::DeviceInterface::OpticalDisc:
+    case Solid::DeviceInterface::StorageAccess:
+    case Solid::DeviceInterface::StorageDrive:
+    case Solid::DeviceInterface::StorageVolume:
+        return "IOMedia";
+    case Solid::DeviceInterface::OpticalDrive:
+    case Solid::DeviceInterface::OpticalDisc:
+        return "IOCDMedia";
         //Solid::DeviceInterface::Camera:
         //Solid::DeviceInterface::PortableMediaPlayer:
     }
diff --git a/src/solid/devices/backends/iokit/iokitopticaldisc.cpp \
b/src/solid/devices/backends/iokit/iokitopticaldisc.cpp new file mode 100644
index 0000000..1fc8171
--- /dev/null
+++ b/src/solid/devices/backends/iokit/iokitopticaldisc.cpp
@@ -0,0 +1,120 @@
+/*
+    Copyright 2006 Kevin Ottens <ervin@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "iokitopticaldisc.h"
+
+#include <QDebug>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+using namespace Solid::Backends::IOKit;
+
+IOKitOpticalDisc::IOKitOpticalDisc(IOKitDevice *device)
+    : IOKitVolume(device)
+{
+}
+
+IOKitOpticalDisc::IOKitOpticalDisc(const IOKitDevice *device)
+    : IOKitVolume(device)
+{
+}
+
+IOKitOpticalDisc::~IOKitOpticalDisc()
+{
+}
+
+QString IOKitOpticalDisc::device() const
+{
+    const QString devName = m_device->property(QLatin1String("BSD \
Name")).toString(); +    if (devName.startsWith(QLatin1Char('r'))) {
+        return QStringLiteral("/dev/") + devName;
+    } else {
+        return QStringLiteral("/dev/r") + devName;
+    }
+}
+
+Solid::OpticalDisc::ContentTypes IOKitOpticalDisc::availableContent() const
+{
+    if (fsType() == QStringLiteral("cddafs")) {
+        return Solid::OpticalDisc::Audio;
+    }
+    return Solid::OpticalDisc::Data;
+}
+
+Solid::OpticalDisc::DiscType IOKitOpticalDisc::discType() const
+{
+    QString type = m_device->property(QStringLiteral("Type")).toString();
+
+    if (type == "CD-ROM") {
+        return Solid::OpticalDisc::CdRom;
+    } else if (type == "CD-R") {
+        return Solid::OpticalDisc::CdRecordable;
+    } else if (type == "CD-RW") {
+        return Solid::OpticalDisc::CdRewritable;
+    } else if (type == "DVD-ROM") {
+        return Solid::OpticalDisc::DvdRom;
+    } else if (type == "DVD-RAM") {
+        return Solid::OpticalDisc::DvdRam;
+    } else if (type == "DVD-R") {
+        return Solid::OpticalDisc::DvdRecordable;
+    } else if (type == "DVD-RW") {
+        return Solid::OpticalDisc::DvdRewritable;
+    } else if (type == "DVD+R") {
+        return Solid::OpticalDisc::DvdPlusRecordable;
+    } else if (type == "DVD+RW") {
+        return Solid::OpticalDisc::DvdPlusRewritable;
+    } else if (type == "BD-ROM") {
+        return Solid::OpticalDisc::BluRayRom;
+    } else if (type == "BD-R") {
+        return Solid::OpticalDisc::BluRayRecordable;
+    } else if (type == "BD-RE") {
+        return Solid::OpticalDisc::BluRayRewritable;
+    } else if (type == "HD DVD-ROM") {
+        return Solid::OpticalDisc::HdDvdRom;
+    } else if (type == "HD DVD-R") {
+        return Solid::OpticalDisc::HdDvdRecordable;
+    } else if (type == "HD DVD-RW") {
+        return Solid::OpticalDisc::HdDvdRewritable;
+    } else {
+        return Solid::OpticalDisc::UnknownDiscType;
+    }
+}
+
+bool IOKitOpticalDisc::isAppendable() const
+{
+    // TODO!
+    return isRewritable();
+}
+
+bool IOKitOpticalDisc::isBlank() const
+{
+    // TODO!
+    return isRewritable();
+}
+
+bool IOKitOpticalDisc::isRewritable() const
+{
+    return m_device->property(QStringLiteral("Writable")).toBool();
+}
+
+qulonglong IOKitOpticalDisc::capacity() const
+{
+    return size();
+}
diff --git a/src/solid/devices/backends/iokit/iokitopticaldisc.h \
b/src/solid/devices/backends/iokit/iokitopticaldisc.h new file mode 100644
index 0000000..a903736
--- /dev/null
+++ b/src/solid/devices/backends/iokit/iokitopticaldisc.h
@@ -0,0 +1,58 @@
+/*
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SOLID_BACKENDS_IOKIT_OPTICALDISC_H
+#define SOLID_BACKENDS_IOKIT_OPTICALDISC_H
+
+#include <solid/devices/ifaces/opticaldisc.h>
+#include "iokitvolume.h"
+
+namespace Solid
+{
+namespace Backends
+{
+namespace IOKit
+{
+class IOKitOpticalDisc : public IOKitVolume, virtual public \
Solid::Ifaces::OpticalDisc +{
+    Q_OBJECT
+    Q_INTERFACES(Solid::Ifaces::OpticalDisc)
+
+public:
+    IOKitOpticalDisc(IOKitDevice *device);
+    IOKitOpticalDisc(const IOKitDevice *device);
+    virtual ~IOKitOpticalDisc();
+
+    // overriden from IOKit::Block because optical discs must
+    // be accessed through the raw device.
+    virtual QString device() const Q_DECL_OVERRIDE;
+
+    virtual Solid::OpticalDisc::ContentTypes availableContent() const \
Q_DECL_OVERRIDE; +    virtual Solid::OpticalDisc::DiscType discType() const \
Q_DECL_OVERRIDE; +    virtual bool isAppendable() const Q_DECL_OVERRIDE;
+    virtual bool isBlank() const Q_DECL_OVERRIDE;
+    virtual bool isRewritable() const Q_DECL_OVERRIDE;
+    virtual qulonglong capacity() const Q_DECL_OVERRIDE;
+};
+}
+}
+}
+
+#endif // SOLID_BACKENDS_IOKIT_OPTICALDISC_H
diff --git a/src/solid/devices/backends/iokit/iokitopticaldrive.cpp \
b/src/solid/devices/backends/iokit/iokitopticaldrive.cpp new file mode 100644
index 0000000..5dc1ccc
--- /dev/null
+++ b/src/solid/devices/backends/iokit/iokitopticaldrive.cpp
@@ -0,0 +1,358 @@
+/*
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "iokitopticaldrive.h"
+
+#include <QStringList>
+#include <QDebug>
+#include <QProcess>
+
+#ifdef EJECT_USING_DISKARBITRATION
+// for QCFType:
+#include <QtCore/private/qcore_mac_p.h>
+#else
+#include <QStandardPaths>
+#endif
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <DiskArbitration/DiskArbitration.h>
+#include <IOKit/scsi/IOSCSIMultimediaCommandsDevice.h>
+
+using namespace Solid::Backends::IOKit;
+
+
+class IOKitOpticalDrive::Private
+{
+public:
+    Private(const IOKitDevice *device, const QVariantMap &devCharMap)
+        : m_device(device)
+        , m_deviceCharacteristics(devCharMap)
+    {
+    }
+    virtual ~Private()
+    {
+    }
+
+    QVariant property(const QString &key) const
+    {
+        return m_deviceCharacteristics.value(key);
+    }
+
+    const IOKitDevice *m_device;
+    const QVariantMap m_deviceCharacteristics;
+
+    static const QMap<Solid::OpticalDrive::MediumType, uint32_t> cdTypeMap;
+    static const QMap<Solid::OpticalDrive::MediumType, uint32_t> dvdTypeMap;
+    static const QMap<Solid::OpticalDrive::MediumType, uint32_t> bdTypeMap;
+
+#ifdef EJECT_USING_DISKARBITRATION
+    // DiskArbitration-based ejection based on the implementation in libcdio's osx.c
+    // in turn based on VideoLAN (VLC) code.
+    // Not activated by default ATM because I have only been able to test it with \
the +    // solid-hardware5 utility and that one remains stuck after a successful \
return +    // from IOKitOpticalDrive::eject(). It does so too when using the hdiutil \
external +    // utility which cannot be due to using QProcess (to the best of my \
knowledge). +    // NB: the full-fledged approach using a cancel sourc ref \
(cancel_signal) etc. may +    // well be too complicated.
+
+    typedef struct DAContext {
+        const IOKitDevice *device;
+        int success;
+        bool completed;
+        DASessionRef session;
+        CFRunLoopRef runloop;
+        CFRunLoopSourceRef cancel_signal;
+    } DAContext;
+
+    static void cancelEjectRunloop(void*) {};
+
+    static void daEjectCallback(DADiskRef disk, DADissenterRef dissenter, void \
*context) +    {
+        Q_UNUSED(disk);
+        DAContext *daContext = static_cast<DAContext*>(context);
+
+        if (dissenter) {
+            CFStringRef status = DADissenterGetStatusString(dissenter);
+            if (status){
+                qWarning() << "Warning while ejecting" << \
daContext->device->property("BSD Name").toString() +                    << ":" << \
QString::fromCFString(status); +                CFRelease( status );
+            }
+        }
+
+        daContext->success = dissenter ? false : true;
+        daContext->completed = TRUE;
+        CFRunLoopSourceSignal(daContext->cancel_signal);
+        CFRunLoopWakeUp(daContext->runloop);
+    }
+
+    static void daUnmountCallback(DADiskRef disk, DADissenterRef dissenter, void \
*context) +    {
+        DAContext *daContext = (DAContext *)context;
+
+        if (!dissenter) {
+            DADiskEject(disk, kDADiskEjectOptionDefault, daEjectCallback, context);
+            daContext->success = (daContext->success == -1 ? true : \
daContext->success); +        }
+        else {
+            daContext->success = false;
+            daContext->completed = true;
+            CFRunLoopSourceSignal(daContext->cancel_signal);
+            CFRunLoopWakeUp(daContext->runloop);
+        }
+    }
+
+    bool eject(double timeoutSeconds)
+    {
+        CFDictionaryRef description = nullptr;
+        CFRunLoopSourceContext cancelRunLoopSourceContext = {
+            .perform = cancelEjectRunloop
+        };
+        DAContext daContext = {m_device, -1 , false, 0,
+            CFRunLoopGetCurrent(), 0};
+        QCFType<CFRunLoopSourceRef> cancel = \
CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &cancelRunLoopSourceContext); +        \
if (!(daContext.cancel_signal = cancel)) { +            qWarning() << Q_FUNC_INFO << \
"failed to create cancel runloop source"; +            return false;
+        }
+        QCFType<DASessionRef> session = DASessionCreate(kCFAllocatorDefault);
+        if (!(daContext.session = session)) {
+            qWarning() << Q_FUNC_INFO << "failed to create DiskArbitration session";
+            return false;
+        }
+        const QString devName = m_device->property(QStringLiteral("BSD \
Name")).toString(); +        QCFType<DADiskRef> daRef = \
DADiskCreateFromBSDName(kCFAllocatorDefault, daContext.session, \
devName.toStdString().c_str()); +        if (!daRef) {
+            qWarning() << Q_FUNC_INFO << "failed to create DiskArbitration reference \
for" << devName; +            return false;
+        }
+        description = DADiskCopyDescription(daRef);
+        if (description ){
+            DASessionScheduleWithRunLoop(daContext.session, daContext.runloop, \
kCFRunLoopDefaultMode); +            CFRunLoopAddSource(daContext.runloop, \
daContext.cancel_signal, kCFRunLoopDefaultMode); +            if \
(CFDictionaryGetValueIfPresent(description, kDADiskDescriptionVolumePathKey, \
nullptr)) { +                DADiskUnmount(daRef, kDADiskUnmountOptionWhole, \
daUnmountCallback, &daContext); +            }
+            DADiskEject(daRef, kDADiskEjectOptionDefault, daEjectCallback, \
&daContext); +            daContext.success = (daContext.success == -1 ? true : \
daContext.success); +            while (!daContext.completed) {
+                if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeoutSeconds, true) \
== kCFRunLoopRunTimedOut) { +                    break;
+                }
+            }
+            if (daContext.completed) {
+                qWarning() << Q_FUNC_INFO << "ejected" << devName;
+            } else{
+                qWarning() << Q_FUNC_INFO << "timeout ejecting" << devName;
+            }
+            CFRunLoopRemoveSource(daContext.runloop, daContext.cancel_signal, \
kCFRunLoopDefaultMode); +            DASessionSetDispatchQueue(daContext.session, 0);
+            DASessionUnscheduleFromRunLoop(daContext.session, daContext.runloop, \
kCFRunLoopDefaultMode); +            CFRelease(description);
+        } else {
+            qWarning() << Q_FUNC_INFO << "failed to fetch DiskArbitration \
description for" << devName; +        }
+        return daContext.success == -1 ? false : daContext.success;
+    }
+#endif //EJECT_USING_DISKARBITRATION
+};
+
+const QMap<Solid::OpticalDrive::MediumType, uint32_t> \
IOKitOpticalDrive::Private::cdTypeMap = { +    {Solid::OpticalDrive::Cdr, \
kCDFeaturesWriteOnceMask}, +    {Solid::OpticalDrive::Cdrw, \
kCDFeaturesReWriteableMask}}; +const QMap<Solid::OpticalDrive::MediumType, uint32_t> \
IOKitOpticalDrive::Private::dvdTypeMap = { +    {Solid::OpticalDrive::Dvd, \
kDVDFeaturesReadStructuresMask}, +    {Solid::OpticalDrive::Dvdr, \
kDVDFeaturesWriteOnceMask}, +    {Solid::OpticalDrive::Dvdrw, \
kDVDFeaturesReWriteableMask}, +    {Solid::OpticalDrive::Dvdram, \
kDVDFeaturesRandomWriteableMask}, +    {Solid::OpticalDrive::Dvdplusr, \
kDVDFeaturesPlusRMask}, +    {Solid::OpticalDrive::Dvdplusrw, \
kDVDFeaturesPlusRWMask}, +// not supported:
+//         {Solid::OpticalDrive::Dvdplusdl, "dvdplusrdl"}
+//         {Solid::OpticalDrive::Dvdplusdlrw, "dvdplusrwdl"}
+    {Solid::OpticalDrive::HdDvd, kDVDFeaturesHDReadMask},
+    {Solid::OpticalDrive::HdDvdr, kDVDFeaturesHDRMask},
+    {Solid::OpticalDrive::HdDvdrw, kDVDFeaturesHDRWMask}};
+const QMap<Solid::OpticalDrive::MediumType, uint32_t> \
IOKitOpticalDrive::Private::bdTypeMap = { +    {Solid::OpticalDrive::Bd, \
kBDFeaturesReadMask}, +    {Solid::OpticalDrive::Bdr, kBDFeaturesWriteMask}}; // also \
Solid::OpticalDrive::Bdre +
+IOKitOpticalDrive::IOKitOpticalDrive(IOKitDevice *device)
+    : IOKitStorage(device)
+{
+    // walk up the IOKit chain to find the parent that has the "Device \
Characteristics" property +    // In the examples I've seen this is always the 2nd \
parent but if ever that turns out +    // to be non-guaranteed we'll need to do a \
true walk. +    IOKitDevice \
ioDVDServices(IOKitDevice(device->parentUdi()).parentUdi()); +    QVariantMap \
devCharMap; +    if (!ioDVDServices.iOKitPropertyExists(QStringLiteral("Device \
Characteristics"))) { +        qWarning() << Q_FUNC_INFO << "Grandparent of" << \
m_device->udi() +            << "doesn't have the \"Device Characteristics\" but is" \
<< ioDVDServices.udi(); +    } else {
+        const QVariant devCharVar = ioDVDServices.property(QStringLiteral("Device \
Characteristics")); +        devCharMap = devCharVar.toMap();
+    }
+    d = new Private(device, devCharMap);
+}
+
+IOKitOpticalDrive::~IOKitOpticalDrive()
+{
+}
+
+// // Example properties: QMap(("BSD Major", QVariant(int, 1))
+//     ("BSD Minor", QVariant(int, 12))
+//     ("BSD Name", QVariant(QString, "disk3"))
+//     ("BSD Unit", QVariant(int, 3))
+//     ("Content", QVariant(QString, "CD_partition_scheme"))
+//     ("Content Hint", QVariant(QString, ""))
+//     ("Ejectable", QVariant(bool, true))
+//     ("IOBusyInterest", QVariant(QString, "IOCommand is not serializable"))
+//     ("IOGeneralInterest", QVariant(QString, "IOCommand is not serializable"))
+//     ("IOMediaIcon", QVariant(QVariantMap, QMap(("CFBundleIdentifier", \
QVariant(QString, "com.apple.iokit.IOCDStorageFamily")) +//         \
("IOBundleResourceFile", QVariant(QString, "CD.icns"))))) +//     ("Leaf", \
QVariant(bool, false)) +//     ("Open", QVariant(bool, true))
+//     ("Preferred Block Size", QVariant(qlonglong, 2352))
+//     ("Removable", QVariant(bool, true))
+//     ("Size", QVariant(qlonglong, 750932448))
+//     ("TOC", QVariant(QByteArray, \
"\x00\xA7\x01\x01\x01\x10\x00\xA0\x00\x00\x00\x00\x01\x00\x00\x01\x12\x00\xA1\x00\x00\ \
x00\x00\f\x00\x00\x01\x12\x00\xA2\x00\x00\x00\x00""F:J\x01\x12\x00\x01\x00\x00\x00\x00 \
\x00\x02\x00\x01\x12\x00\x02\x00\x00\x00\x00\x07/\b\x01\x12\x00\x03\x00\x00\x00\x00\x1 \
2\b\x0E\x01\x12\x00\x04\x00\x00\x00\x00\x17\x12""0\x01\x12\x00\x05\x00\x00\x00\x00\x1B+ \
\x01\x12\x00\x06\x00\x00\x00\x00 \
\x11\n\x01\x12\x00\x07\x00\x00\x00\x00!-\n\x01\x12\x00\b\x00\x00\x00\x00'\f\x1F\x01\x1 \
2\x00\t\x00\x00\x00\x00-\x13;\x01\x12\x00\n\x00\x00\x00\x00""4%\x1E\x01\x12\x00\x0B\x00\x00\x00\x00""62 \
\x01\x12\x00\f\x00\x00\x00\x00""C\x06""E")) +//     ("Type", QVariant(QString, \
"CD-ROM")) +//     ("Whole", QVariant(bool, true))
+//     ("Writable", QVariant(bool, false))
+//     ("className", QVariant(QString, "IOCDMedia")))
+// // related useful entry: QMap(("Device Characteristics", QVariant(QVariantMap, \
QMap(("Async Notification", QVariant(bool, false)) +//         ("BD Features", \
QVariant(int, 0)) +//         ("CD Features", QVariant(int, 2047))
+//         ("DVD Features", QVariant(int, 503))
+//         ("Fast Spindown", QVariant(bool, true))
+//         ("Loading Mechanism", QVariant(QString, "Slot"))
+//         ("Low Power Polling", QVariant(bool, false))
+//         ("Power Off", QVariant(bool, true))
+//         ("Product Name", QVariant(QString, "DVD-R   UJ-8A8"))
+//         ("Product Revision Level", QVariant(QString, "HA13"))
+//         ("Vendor Name", QVariant(QString, "MATSHITA")))))
+//     ("IOCFPlugInTypes", QVariant(QVariantMap, \
QMap(("97ABCF2C-23CC-11D5-A0E8-003065704866", QVariant(QString, \
"IOSCSIArchitectureModelFamily.kext/Contents/PlugIns/SCSITaskUserClient.kext/Contents/PlugIns/SCSITaskLib.plugin")))))
 +//     ("IOGeneralInterest", QVariant(QString, "IOCommand is not serializable"))
+//     ("IOMatchCategory", QVariant(QString, "SCSITaskUserClientIniter"))
+//     ("IOMinimumSegmentAlignmentByteCount", QVariant(qlonglong, 4))
+//     ("IOUserClientClass", QVariant(QString, "SCSITaskUserClient"))
+//     ("Protocol Characteristics", QVariant(QVariantMap, QMap(("AHCI Port Number", \
QVariant(qlonglong, 0)) +//         ("ATAPI", QVariant(bool, true))
+//         ("Physical Interconnect", QVariant(QString, "SATA"))
+//         ("Physical Interconnect Location", QVariant(QString, "Internal"))
+//         ("Port Speed", QVariant(QString, "1.5 Gigabit"))
+//         ("Read Time Out Duration", QVariant(qlonglong, 15000))
+//         ("Retry Count", QVariant(qlonglong, 1))
+//         ("Write Time Out Duration", QVariant(qlonglong, 15000)))))
+//     ("SCSITaskDeviceCategory", QVariant(QString, "SCSITaskAuthoringDevice"))
+//     ("SCSITaskUserClient GUID", QVariant(QByteArray, \
"\x00]\x0F""F\x80\xFF\xFF\xFFg\xB6\xAB\x1B\x00\x00\x00\x00")) +//     ("className", \
QVariant(QString, "IODVDServices")) +//     ("device-type", QVariant(QString, \
"DVD"))) +// //                       QMap(("CFBundleIdentifier", QVariant(QString, \
"com.apple.iokit.IODVDStorageFamily")) +//     ("IOClass", QVariant(QString, \
"IODVDBlockStorageDriver")) +//     ("IOGeneralInterest", QVariant(QString, \
"IOCommand is not serializable")) +//     ("IOMatchCategory", QVariant(QString, \
"IODefaultMatchCategory")) +//     ("IOProbeScore", QVariant(int, 0))
+//     ("IOPropertyMatch", QVariant(QVariantMap, QMap(("device-type", \
QVariant(QString, "DVD"))))) +//     ("IOProviderClass", QVariant(QString, \
"IODVDBlockStorageDevice")) +//     ("Statistics", QVariant(QVariantMap, QMap(("Bytes \
(Read)", QVariant(qlonglong, 578020608)) +//         ("Bytes (Write)", \
QVariant(qlonglong, 0)) +//         ("Errors (Read)", QVariant(qlonglong, 0))
+//         ("Errors (Write)", QVariant(qlonglong, 0))
+//         ("Latency Time (Read)", QVariant(qlonglong, 0))
+//         ("Latency Time (Write)", QVariant(qlonglong, 0))
+//         ("Operations (Read)", QVariant(qlonglong, 18475))
+//         ("Operations (Write)", QVariant(qlonglong, 0))
+//         ("Retries (Read)", QVariant(qlonglong, 0))
+//         ("Retries (Write)", QVariant(qlonglong, 0))
+//         ("Total Time (Read)", QVariant(qlonglong, 219944025102))
+//         ("Total Time (Write)", QVariant(qlonglong, 0)))))
+//     ("className", QVariant(QString, "IODVDBlockStorageDriver")))
+
+Solid::OpticalDrive::MediumTypes IOKitOpticalDrive::supportedMedia() const
+{
+    Solid::OpticalDrive::MediumTypes supported;
+
+    uint32_t cdFeatures = d->property(QStringLiteral("CD Features")).toInt();
+    uint32_t dvdFeatures = d->property(QStringLiteral("DVD Features")).toInt();
+    uint32_t bdFeatures = d->property(QStringLiteral("BD Features")).toInt();
+
+    qDebug() << Q_FUNC_INFO << "cdFeatures" << cdFeatures << "dvdFeatures" << \
dvdFeatures << "bdFeatures" << bdFeatures; +
+    foreach (const Solid::OpticalDrive::MediumType type, d->cdTypeMap.keys()) {
+        if (cdFeatures & d->cdTypeMap[type]) {
+            supported |= type;
+        }
+    }
+    foreach (const Solid::OpticalDrive::MediumType type, d->dvdTypeMap.keys()) {
+        if (dvdFeatures & d->dvdTypeMap[type]) {
+            supported |= type;
+        }
+    }
+    foreach (const Solid::OpticalDrive::MediumType type, d->bdTypeMap.keys()) {
+        if (bdFeatures & d->bdTypeMap[type]) {
+            supported |= type;
+            if (d->bdTypeMap[type] == kBDFeaturesWriteMask) {
+                supported |= Solid::OpticalDrive::Bdre;
+            }
+        }
+    }
+
+    return supported;
+}
+
+int IOKitOpticalDrive::readSpeed() const
+{
+    return 0;
+}
+
+int IOKitOpticalDrive::writeSpeed() const
+{
+    return 0;
+}
+
+QList<int> IOKitOpticalDrive::writeSpeeds() const
+{
+    return {};
+}
+
+bool IOKitOpticalDrive::eject()
+{
+#ifdef EJECT_USING_DISKARBITRATION
+    // give the devices 30 seconds to eject
+    int error = !d->eject(30.0);
+#else
+    QProcess ejectJob;
+    int error = ejectJob.execute(QStandardPaths::findExecutable(QStringLiteral("hdiutil")),
 +        {QStringLiteral("detach"), QStringLiteral("-verbose"),
+            QStringLiteral("/dev/") + m_device->property(QStringLiteral("BSD \
Name")).toString()}); +    if (error) {
+        qWarning() << "hdiutil returned" << error << "trying to eject" << \
m_device->product(); +    }
+#endif //EJECT_USING_DISKARBITRATION
+    if (error) {
+        emit ejectDone(Solid::ErrorType::OperationFailed, QVariant(), \
m_device->udi()); +        return false;
+    } else {
+        emit ejectDone(Solid::ErrorType::NoError, QVariant(), m_device->udi());
+        return true;
+    }
+}
+
diff --git a/src/solid/devices/backends/iokit/iokitopticaldrive.h \
b/src/solid/devices/backends/iokit/iokitopticaldrive.h new file mode 100644
index 0000000..229c324
--- /dev/null
+++ b/src/solid/devices/backends/iokit/iokitopticaldrive.h
@@ -0,0 +1,62 @@
+/*
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SOLID_BACKENDS_IOKIT_OPTICALDRIVE_H
+#define SOLID_BACKENDS_IOKIT_OPTICALDRIVE_H
+
+#include <solid/devices/ifaces/opticaldrive.h>
+#include "iokitstorage.h"
+
+namespace Solid
+{
+namespace Backends
+{
+namespace IOKit
+{
+class IOKitOpticalDrive : public IOKitStorage, virtual public \
Solid::Ifaces::OpticalDrive +{
+    Q_OBJECT
+    Q_INTERFACES(Solid::Ifaces::OpticalDrive)
+
+public:
+    explicit IOKitOpticalDrive(IOKitDevice *device);
+    virtual ~IOKitOpticalDrive();
+
+public Q_SLOTS:
+    Solid::OpticalDrive::MediumTypes supportedMedia() const Q_DECL_OVERRIDE;
+    int readSpeed() const Q_DECL_OVERRIDE;
+    int writeSpeed() const Q_DECL_OVERRIDE;
+    QList<int> writeSpeeds() const Q_DECL_OVERRIDE;
+    bool eject() Q_DECL_OVERRIDE;
+
+Q_SIGNALS:
+    void ejectPressed(const QString &udi) Q_DECL_OVERRIDE;
+    void ejectDone(Solid::ErrorType error, QVariant errorData, const QString &udi) \
Q_DECL_OVERRIDE; +    void ejectRequested(const QString &udi);
+
+private:
+    class Private;
+    Private *d;
+};
+}
+}
+}
+
+#endif // SOLID_BACKENDS_IOKIT_OPTICALDRIVE_H
diff --git a/src/solid/devices/backends/iokit/iokitprocessor.cpp \
b/src/solid/devices/backends/iokit/iokitprocessor.cpp index 1f2282a..434d862 100644
--- a/src/solid/devices/backends/iokit/iokitprocessor.cpp
+++ b/src/solid/devices/backends/iokit/iokitprocessor.cpp
@@ -1,5 +1,6 @@
 /*
     Copyright 2009 Harald Fernengel <harry@kdevelop.org>
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
@@ -23,6 +24,15 @@
 
 #include <QtCore/qdebug.h>
 
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <errno.h>
+
+#include "../shared/cpufeatures.h"
+
+// from cfhelper.cpp
+extern bool q_sysctlbyname(const char *name, QString &result);
+
 using namespace Solid::Backends::IOKit;
 
 Processor::Processor(IOKitDevice *device)
@@ -43,16 +53,47 @@ int Processor::number() const
 
 int Processor::maxSpeed() const
 {
-    return 0; // TODO
+    uint64_t freq = 0;
+    size_t size = sizeof(freq);
+
+    if (sysctlbyname("hw.cpufrequency", &freq, &size, nullptr, 0) < 0) {
+        qWarning() << "sysctl error reading hw.cpufrequency:" << strerror(errno);
+        return 0;
+    } else {
+        return int(freq / 1000000);
+    }
 }
 
 bool Processor::canChangeFrequency() const
 {
-    return false; // TODO
+    uint64_t minFreq = 0, maxFreq = 0;
+    size_t size = sizeof(uint64_t);
+
+    if (sysctlbyname("hw.cpufrequency_min", &minFreq, &size, nullptr, 0) == 0
+        && sysctlbyname("hw.cpufrequency_max", &maxFreq, &size, nullptr, 0) == 0) {
+        return maxFreq > minFreq;
+    }
+    return false;
 }
 
 Solid::Processor::InstructionSets Processor::instructionSets() const
 {
-    return 0; // TODO
+    // use sysctlbyname() and "machdep.cpu.features" + "machdep.cpu.extfeatures"
+    static Solid::Processor::InstructionSets cpuextensions = \
Solid::Backends::Shared::cpuFeatures(); +
+    return cpuextensions;
 }
 
+QString Processor::vendor()
+{
+    QString qVendor;
+    q_sysctlbyname("machdep.cpu.vendor", qVendor);
+    return qVendor;
+}
+
+QString Processor::product()
+{
+    QString product;
+    q_sysctlbyname("machdep.cpu.brand_string", product);
+    return product;
+}
diff --git a/src/solid/devices/backends/iokit/iokitprocessor.h \
b/src/solid/devices/backends/iokit/iokitprocessor.h index 4fd6394..5a16a6b 100644
--- a/src/solid/devices/backends/iokit/iokitprocessor.h
+++ b/src/solid/devices/backends/iokit/iokitprocessor.h
@@ -41,10 +41,12 @@ public:
     Processor(IOKitDevice *device);
     virtual ~Processor();
 
-    virtual int number() const;
-    virtual int maxSpeed() const;
-    virtual bool canChangeFrequency() const;
-    virtual Solid::Processor::InstructionSets instructionSets() const;
+    virtual int number() const Q_DECL_OVERRIDE;
+    virtual int maxSpeed() const Q_DECL_OVERRIDE;
+    virtual bool canChangeFrequency() const Q_DECL_OVERRIDE;
+    virtual Solid::Processor::InstructionSets instructionSets() const \
Q_DECL_OVERRIDE; +    static QString vendor();
+    static QString product();
 };
 }
 }
diff --git a/src/solid/devices/backends/iokit/iokitstorage.cpp \
b/src/solid/devices/backends/iokit/iokitstorage.cpp new file mode 100644
index 0000000..b80d4c0
--- /dev/null
+++ b/src/solid/devices/backends/iokit/iokitstorage.cpp
@@ -0,0 +1,119 @@
+/*
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "iokitstorage.h"
+
+#include <QDebug>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <DiskArbitration/DiskArbitration.h>
+
+using namespace Solid::Backends::IOKit;
+
+IOKitStorage::IOKitStorage(IOKitDevice *device)
+    : Block(device)
+    , daDict(new DADictionary(device))
+{
+}
+
+IOKitStorage::IOKitStorage(const IOKitDevice *device)
+    : Block(device)
+    , daDict(new DADictionary(device))
+{
+}
+
+IOKitStorage::~IOKitStorage()
+{
+    delete daDict;
+}
+
+Solid::StorageDrive::Bus IOKitStorage::bus() const
+{
+    const QString udi = m_device->udi();
+    // TODO: figure out how to return something useful here.
+    if (udi.contains(QStringLiteral("/SATA@"))) {
+        return Solid::StorageDrive::Sata;
+    }
+    if (udi.contains(QStringLiteral("/SDXC@"))) {
+        // TODO: return something finer grained; the built-in card reader
+        // is NOT connected via USB on Macs, for instance (but there's no PCI \
option) +        return Solid::StorageDrive::Usb;
+    }
+    if (udi.contains(QStringLiteral("/IOUSBInterface@"))) {
+        return Solid::StorageDrive::Usb;
+    }
+    if (daDict->stringForKey(kDADiskDescriptionDeviceProtocolKey) == \
QStringLiteral("USB")) { +        return Solid::StorageDrive::Usb;
+    }
+    return Solid::StorageDrive::Platform;
+}
+
+Solid::StorageDrive::DriveType IOKitStorage::driveType() const
+{
+    const QString udi = m_device->udi();
+    const QString type = m_device->property(QLatin1String("className")).toString();
+
+    if (type == QStringLiteral("IOCDMedia")
+        || type == QStringLiteral("IOBDMedia")
+        || type == QStringLiteral("IODVDMedia")) {
+        return Solid::StorageDrive::CdromDrive;
+    }
+    if (udi.contains(QStringLiteral("/SDXC@"))) {
+        return Solid::StorageDrive::SdMmc;
+    }
+    if (daDict->stringForKey(kDADiskDescriptionDeviceModelKey) == \
QStringLiteral("Compact Flash")) { +        return Solid::StorageDrive::CompactFlash;
+    }
+    return Solid::StorageDrive::HardDisk;
+}
+
+bool IOKitStorage::isRemovable() const
+{
+    bool isExternal = false;
+    daDict->boolForKey(kDADiskDescriptionDeviceInternalKey, isExternal);
+    return isExternal || m_device->property(QLatin1String("Removable")).toBool();
+}
+
+bool IOKitStorage::isHotpluggable() const
+{
+    const Solid::StorageDrive::DriveType type = driveType();
+    return bus() == Solid::StorageDrive::Usb
+        || type == Solid::StorageDrive::CdromDrive || type == \
Solid::StorageDrive::SdMmc; +}
+
+qulonglong IOKitStorage::size() const
+{
+    return m_device->property(QLatin1String("Size")).toULongLong();
+}
+
+QString IOKitStorage::vendor() const
+{
+    return daDict->stringForKey(kDADiskDescriptionDeviceVendorKey);
+}
+
+QString IOKitStorage::product() const
+{
+    return daDict->stringForKey(kDADiskDescriptionDeviceModelKey);
+}
+
+QString IOKitStorage::description() const
+{
+    return daDict->stringForKey(kDADiskDescriptionMediaNameKey);
+}
diff --git a/src/solid/devices/backends/iokit/iokitprocessor.h \
b/src/solid/devices/backends/iokit/iokitstorage.h similarity index 50%
copy from src/solid/devices/backends/iokit/iokitprocessor.h
copy to src/solid/devices/backends/iokit/iokitstorage.h
index 4fd6394..80dcab8 100644
--- a/src/solid/devices/backends/iokit/iokitprocessor.h
+++ b/src/solid/devices/backends/iokit/iokitstorage.h
@@ -1,5 +1,5 @@
 /*
-    Copyright 2009 Harald Fernengel <harry@kdevelop.org>
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
@@ -18,11 +18,13 @@
     License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */
 
-#ifndef SOLID_BACKENDS_IOKIT_PROCESSOR_H
-#define SOLID_BACKENDS_IOKIT_PROCESSOR_H
+#ifndef SOLID_BACKENDS_IOKIT_IOKITSTORAGE_H
+#define SOLID_BACKENDS_IOKIT_IOKITSTORAGE_H
 
-#include <solid/devices/ifaces/processor.h>
-#include "iokitdeviceinterface.h"
+#include "iokitblock.h"
+#include "dadictionary_p.h"
+
+#include <solid/devices/ifaces/storagedrive.h>
 
 namespace Solid
 {
@@ -30,24 +32,32 @@ namespace Backends
 {
 namespace IOKit
 {
-class IOKitDevice;
-
-class Processor : public DeviceInterface, virtual public Solid::Ifaces::Processor
+class IOKitStorage : public Block, virtual public Solid::Ifaces::StorageDrive
 {
     Q_OBJECT
-    Q_INTERFACES(Solid::Ifaces::Processor)
+    Q_INTERFACES(Solid::Ifaces::StorageDrive)
 
 public:
-    Processor(IOKitDevice *device);
-    virtual ~Processor();
-
-    virtual int number() const;
-    virtual int maxSpeed() const;
-    virtual bool canChangeFrequency() const;
-    virtual Solid::Processor::InstructionSets instructionSets() const;
+    explicit IOKitStorage(IOKitDevice *device);
+    explicit IOKitStorage(const IOKitDevice *device);
+    ~IOKitStorage();
+
+    QString vendor() const;
+    QString product() const;
+    QString description() const;
+
+public Q_SLOTS:
+    Solid::StorageDrive::Bus bus() const Q_DECL_OVERRIDE;
+    Solid::StorageDrive::DriveType driveType() const Q_DECL_OVERRIDE;
+
+    bool isRemovable() const Q_DECL_OVERRIDE;
+    bool isHotpluggable() const Q_DECL_OVERRIDE;
+    qulonglong size() const Q_DECL_OVERRIDE;
+private:
+    DADictionary *daDict;
 };
 }
 }
 }
 
-#endif // SOLID_BACKENDS_IOKIT_PROCESSOR_H
+#endif // SOLID_BACKENDS_IOKIT_IOKITSTORAGE_H
diff --git a/src/solid/devices/backends/iokit/iokitstorageaccess.cpp \
b/src/solid/devices/backends/iokit/iokitstorageaccess.cpp new file mode 100644
index 0000000..3627415
--- /dev/null
+++ b/src/solid/devices/backends/iokit/iokitstorageaccess.cpp
@@ -0,0 +1,110 @@
+/*
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "iokitstorageaccess.h"
+
+#include <QDebug>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <DiskArbitration/DiskArbitration.h>
+
+using namespace Solid::Backends::IOKit;
+
+IOKitStorageAccess::IOKitStorageAccess(IOKitDevice *device)
+    : DeviceInterface(device)
+    , daDict(new DADictionary(device))
+{
+    connect(device, SIGNAL(propertyChanged(QMap<QString,int>)),
+            this, SLOT(onPropertyChanged(QMap<QString,int>)));
+}
+
+IOKitStorageAccess::IOKitStorageAccess(const IOKitDevice *device)
+    : DeviceInterface(device)
+    , daDict(new DADictionary(device))
+{
+    connect(device, SIGNAL(propertyChanged(QMap<QString,int>)),
+            this, SLOT(onPropertyChanged(QMap<QString,int>)));
+}
+
+IOKitStorageAccess::~IOKitStorageAccess()
+{
+    delete daDict;
+}
+
+bool IOKitStorageAccess::isAccessible() const
+{
+    filePath();
+    const QVariant isMounted = m_device->property(QStringLiteral("isMounted"));
+    return isMounted.isValid() && isMounted.toBool();
+}
+
+QString IOKitStorageAccess::filePath() const
+{
+    // mount points can change (but can they between invocations of filePath()?)
+    QString mountPoint;
+    if (const CFURLRef urlRef  = \
daDict->cfUrLRefForKey(kDADiskDescriptionVolumePathKey)) { +        const CFStringRef \
mpRef = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle); +        mountPoint = \
QString::fromCFString(mpRef); +        CFRelease(mpRef);
+        m_device->setProperty("mountPoint", QVariant(mountPoint));
+        bool isMounted = !mountPoint.isEmpty();
+        const QString isMountedKey = QStringLiteral("isMounted");
+        const QVariant wasMounted = m_device->property(isMountedKey);
+        if (wasMounted.isValid() && wasMounted.toBool() != isMounted) {
+            IOKitStorageAccess(m_device).onPropertyChanged(QMap<QString,int>{{isMountedKey,isMounted}});
 +        }
+        m_device->setProperty("isMounted", QVariant(isMounted));
+    }
+    return mountPoint;
+}
+
+bool IOKitStorageAccess::isIgnored() const
+{
+    if (m_device->iOKitPropertyExists(QStringLiteral("Open"))) {
+        // ignore storage volumes that aren't mounted
+        bool isIgnored = m_device->property(QStringLiteral("Open")).toBool() == \
false; +        m_device->setProperty("isIgnored", QVariant(isIgnored));
+        return isIgnored;
+    }
+    const QVariant isIgnored = m_device->property(QStringLiteral("isIgnored"));
+    return isIgnored.isValid() && isIgnored.toBool();
+}
+
+bool IOKitStorageAccess::setup()
+{
+    // TODO?
+    return false;
+}
+
+bool IOKitStorageAccess::teardown()
+{
+    // TODO?
+    return false;
+}
+
+void IOKitStorageAccess::onPropertyChanged(const QMap<QString, int> &changes)
+{
+    Q_FOREACH (const QString &property, changes.keys()) {
+        if (property == QStringLiteral("isMounted")) {
+            emit accessibilityChanged(m_device->property(QStringLiteral("isMounted")).toBool(), \
m_device->udi()); +        }
+    }
+}
+
diff --git a/src/solid/devices/backends/iokit/iokitstorageaccess.h \
b/src/solid/devices/backends/iokit/iokitstorageaccess.h new file mode 100644
index 0000000..6ae239b
--- /dev/null
+++ b/src/solid/devices/backends/iokit/iokitstorageaccess.h
@@ -0,0 +1,67 @@
+/*
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SOLID_BACKENDS_IOKIT_STORAGEACCESS_H
+#define SOLID_BACKENDS_IOKIT_STORAGEACCESS_H
+
+#include <solid/devices/ifaces/storageaccess.h>
+#include "iokitdeviceinterface.h"
+#include "dadictionary_p.h"
+
+namespace Solid
+{
+namespace Backends
+{
+namespace IOKit
+{
+class IOKitStorageAccess : public DeviceInterface, virtual public \
Solid::Ifaces::StorageAccess +{
+    Q_OBJECT
+    Q_INTERFACES(Solid::Ifaces::StorageAccess)
+
+public:
+    IOKitStorageAccess(IOKitDevice *device);
+    IOKitStorageAccess(const IOKitDevice *device);
+    virtual ~IOKitStorageAccess();
+
+    bool isAccessible() const Q_DECL_OVERRIDE;
+    QString filePath() const Q_DECL_OVERRIDE;
+    bool isIgnored() const Q_DECL_OVERRIDE;
+public Q_SLOTS:
+    bool setup() Q_DECL_OVERRIDE;
+    bool teardown() Q_DECL_OVERRIDE;
+
+Q_SIGNALS:
+    void accessibilityChanged(bool accessible, const QString &udi) Q_DECL_OVERRIDE;
+    void setupDone(Solid::ErrorType error, QVariant errorData, const QString &udi) \
Q_DECL_OVERRIDE; +    void teardownDone(Solid::ErrorType error, QVariant errorData, \
const QString &udi) Q_DECL_OVERRIDE; +    void setupRequested(const QString &udi) \
Q_DECL_OVERRIDE; +    void teardownRequested(const QString &udi) Q_DECL_OVERRIDE;
+
+private Q_SLOTS:
+    void onPropertyChanged(const QMap<QString, int> &changes);
+private:
+    DADictionary *daDict;
+};
+}
+}
+}
+
+#endif // SOLID_BACKENDS_IOKIT_STORAGEACCESS_H
diff --git a/src/solid/devices/backends/iokit/iokitvolume.cpp \
b/src/solid/devices/backends/iokit/iokitvolume.cpp new file mode 100644
index 0000000..7d454b0
--- /dev/null
+++ b/src/solid/devices/backends/iokit/iokitvolume.cpp
@@ -0,0 +1,113 @@
+/*
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "iokitvolume.h"
+#include "iokitgenericinterface.h"
+
+#include <QDebug>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <DiskArbitration/DiskArbitration.h>
+
+using namespace Solid::Backends::IOKit;
+
+IOKitVolume::IOKitVolume(IOKitDevice *device)
+    : Block(device)
+    , daDict(new DADictionary(device))
+{
+}
+
+IOKitVolume::IOKitVolume(const IOKitDevice *device)
+    : Block(device)
+    , daDict(new DADictionary(device))
+{
+}
+
+IOKitVolume::~IOKitVolume()
+{
+    delete daDict;
+}
+
+bool IOKitVolume::isIgnored() const
+{
+    // ignore storage volumes that aren't mounted
+    bool isIgnored = m_device->property(QStringLiteral("Open")).toBool() == false;
+    m_device->setProperty("isIgnored", isIgnored);
+    m_device->setProperty("isMounted", !isIgnored);
+    return isIgnored;
+}
+
+Solid::StorageVolume::UsageType IOKitVolume::usage() const
+{
+    const QString content = \
m_device->property(QStringLiteral("Content")).toString(); +    if (content == \
QStringLiteral("CD_DA")) { +        // this is (probably) a CD track
+        return Solid::StorageVolume::Other;
+    }
+    if (content.contains(QStringLiteral("partition_scheme"))) {
+        return Solid::StorageVolume::PartitionTable;
+    }
+    return Solid::StorageVolume::FileSystem;
+}
+
+QString IOKitVolume::fsType() const
+{
+    return daDict->stringForKey(kDADiskDescriptionVolumeKindKey);
+}
+
+QString IOKitVolume::label() const
+{
+    return daDict->stringForKey(kDADiskDescriptionVolumeNameKey);
+}
+
+QString IOKitVolume::uuid() const
+{
+    return m_device->property(QStringLiteral("UUID")).toString();
+}
+
+qulonglong IOKitVolume::size() const
+{
+    return m_device->property(QStringLiteral("Size")).toULongLong();
+}
+
+QString IOKitVolume::encryptedContainerUdi() const
+{
+    return QString();
+}
+
+QString IOKitVolume::vendor() const
+{
+    return daDict->stringForKey(kDADiskDescriptionDeviceVendorKey);
+}
+
+QString IOKitVolume::product() const
+{
+    return daDict->stringForKey(kDADiskDescriptionDeviceModelKey);
+}
+
+QString IOKitVolume::description() const
+{
+    return daDict->stringForKey(kDADiskDescriptionMediaNameKey);
+}
+
+DADiskRef IOKitVolume::daRef() const
+{
+    return daDict->daRef;
+}
diff --git a/src/solid/devices/backends/iokit/iokitvolume.h \
b/src/solid/devices/backends/iokit/iokitvolume.h new file mode 100644
index 0000000..4dc3718
--- /dev/null
+++ b/src/solid/devices/backends/iokit/iokitvolume.h
@@ -0,0 +1,67 @@
+/*
+    Copyright 2017 René J.V. Bertin <rjvbertin@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SOLID_BACKENDS_IOKIT_VOLUME_H
+#define SOLID_BACKENDS_IOKIT_VOLUME_H
+
+#include <solid/devices/ifaces/storagevolume.h>
+#include "iokitblock.h"
+#include "dadictionary_p.h"
+
+#include <DiskArbitration/DiskArbitration.h>
+
+namespace Solid
+{
+namespace Backends
+{
+namespace IOKit
+{
+class IOKitVolume : public Block, virtual public Solid::Ifaces::StorageVolume
+{
+    Q_OBJECT
+    Q_INTERFACES(Solid::Ifaces::StorageVolume)
+
+public:
+    IOKitVolume(IOKitDevice *device);
+    IOKitVolume(const IOKitDevice *device);
+    virtual ~IOKitVolume();
+
+    bool isIgnored() const Q_DECL_OVERRIDE;
+    Solid::StorageVolume::UsageType usage() const Q_DECL_OVERRIDE;
+    QString fsType() const Q_DECL_OVERRIDE;
+    QString label() const Q_DECL_OVERRIDE;
+    QString uuid() const Q_DECL_OVERRIDE;
+    qulonglong size() const Q_DECL_OVERRIDE;
+    QString encryptedContainerUdi() const Q_DECL_OVERRIDE;
+
+    QString vendor() const;
+    QString product() const;
+    QString description() const;
+
+    DADiskRef daRef() const;
+
+private:
+    DADictionary *daDict;
+};
+}
+}
+}
+
+#endif // SOLID_BACKENDS_IOKIT_VOLUME_H


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic