7#include "iokitopticaldrive.h"
12#ifdef EJECT_USING_DISKARBITRATION
14#include <private/qcore_mac_p.h>
16#include <QStandardPaths>
19#include <CoreFoundation/CoreFoundation.h>
20#include <DiskArbitration/DiskArbitration.h>
21#include <IOKit/scsi/IOSCSIMultimediaCommandsDevice.h>
23using namespace Solid::Backends::IOKit;
25class IOKitOpticalDrive::Private
28 Private(
const IOKitDevice *device,
const QVariantMap &devCharMap)
30 , m_deviceCharacteristics(devCharMap)
39 return m_deviceCharacteristics.value(key);
42 const IOKitDevice *m_device;
43 const QVariantMap m_deviceCharacteristics;
49#ifdef EJECT_USING_DISKARBITRATION
59 typedef struct DAContext {
60 const IOKitDevice *device;
65 CFRunLoopSourceRef cancel_signal;
68 static void cancelEjectRunloop(
void *){};
70 static void daEjectCallback(DADiskRef disk, DADissenterRef dissenter,
void *context)
73 DAContext *daContext =
static_cast<DAContext *
>(context);
76 CFStringRef
status = DADissenterGetStatusString(dissenter);
78 qWarning() <<
"Warning while ejecting" << daContext->device->property(
"BSD Name").toString() <<
":" <<
QString::fromCFString(
status);
83 daContext->success = dissenter ? false :
true;
84 daContext->completed = TRUE;
85 CFRunLoopSourceSignal(daContext->cancel_signal);
86 CFRunLoopWakeUp(daContext->runloop);
89 static void daUnmountCallback(DADiskRef disk, DADissenterRef dissenter,
void *context)
91 DAContext *daContext = (DAContext *)context;
94 DADiskEject(disk, kDADiskEjectOptionDefault, daEjectCallback, context);
95 daContext->success = (daContext->success == -1 ? true : daContext->success);
97 daContext->success =
false;
98 daContext->completed =
true;
99 CFRunLoopSourceSignal(daContext->cancel_signal);
100 CFRunLoopWakeUp(daContext->runloop);
104 bool eject(
double timeoutSeconds)
106 CFDictionaryRef description =
nullptr;
107 CFRunLoopSourceContext cancelRunLoopSourceContext = {.perform = cancelEjectRunloop};
108 DAContext daContext = {m_device, -1,
false, 0, CFRunLoopGetCurrent(), 0};
109 QCFType<CFRunLoopSourceRef>
cancel = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &cancelRunLoopSourceContext);
110 if (!(daContext.cancel_signal = cancel)) {
111 qWarning() << Q_FUNC_INFO <<
"failed to create cancel runloop source";
114 QCFType<DASessionRef> session = DASessionCreate(kCFAllocatorDefault);
115 if (!(daContext.session = session)) {
116 qWarning() << Q_FUNC_INFO <<
"failed to create DiskArbitration session";
119 const QString devName = m_device->property(QStringLiteral(
"BSD Name")).
toString();
120 QCFType<DADiskRef> daRef = DADiskCreateFromBSDName(kCFAllocatorDefault, daContext.session, devName.
toStdString().c_str());
122 qWarning() << Q_FUNC_INFO <<
"failed to create DiskArbitration reference for" << devName;
125 description = DADiskCopyDescription(daRef);
127 DASessionScheduleWithRunLoop(daContext.session, daContext.runloop, kCFRunLoopDefaultMode);
128 CFRunLoopAddSource(daContext.runloop, daContext.cancel_signal, kCFRunLoopDefaultMode);
129 if (CFDictionaryGetValueIfPresent(description, kDADiskDescriptionVolumePathKey,
nullptr)) {
130 DADiskUnmount(daRef, kDADiskUnmountOptionWhole, daUnmountCallback, &daContext);
132 DADiskEject(daRef, kDADiskEjectOptionDefault, daEjectCallback, &daContext);
133 daContext.success = (daContext.success == -1 ? true : daContext.success);
134 while (!daContext.completed) {
135 if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeoutSeconds,
true) == kCFRunLoopRunTimedOut) {
139 if (daContext.completed) {
140 qWarning() << Q_FUNC_INFO <<
"ejected" << devName;
142 qWarning() << Q_FUNC_INFO <<
"timeout ejecting" << devName;
144 CFRunLoopRemoveSource(daContext.runloop, daContext.cancel_signal, kCFRunLoopDefaultMode);
145 DASessionSetDispatchQueue(daContext.session, 0);
146 DASessionUnscheduleFromRunLoop(daContext.session, daContext.runloop, kCFRunLoopDefaultMode);
147 CFRelease(description);
149 qWarning() << Q_FUNC_INFO <<
"failed to fetch DiskArbitration description for" << devName;
151 return daContext.success == -1 ? false : daContext.success;
157 {Solid::OpticalDrive::Cdr, kCDFeaturesWriteOnceMask},
158 {Solid::OpticalDrive::Cdrw, kCDFeaturesReWriteableMask},
161 {Solid::OpticalDrive::Dvd, kDVDFeaturesReadStructuresMask},
162 {Solid::OpticalDrive::Dvdr, kDVDFeaturesWriteOnceMask},
163 {Solid::OpticalDrive::Dvdrw, kDVDFeaturesReWriteableMask},
164 {Solid::OpticalDrive::Dvdram, kDVDFeaturesRandomWriteableMask},
165 {Solid::OpticalDrive::Dvdplusr, kDVDFeaturesPlusRMask},
166 {Solid::OpticalDrive::Dvdplusrw, kDVDFeaturesPlusRWMask},
170 {Solid::OpticalDrive::HdDvd, kDVDFeaturesHDReadMask},
171 {Solid::OpticalDrive::HdDvdr, kDVDFeaturesHDRMask},
172 {Solid::OpticalDrive::HdDvdrw, kDVDFeaturesHDRWMask},
175 {Solid::OpticalDrive::Bd, kBDFeaturesReadMask},
176 {Solid::OpticalDrive::Bdr, kBDFeaturesWriteMask},
179IOKitOpticalDrive::IOKitOpticalDrive(IOKitDevice *device)
180 : IOKitStorage(device)
185 IOKitDevice ioDVDServices(IOKitDevice(device->parentUdi()).parentUdi());
186 QVariantMap devCharMap;
187 if (!ioDVDServices.iOKitPropertyExists(QStringLiteral(
"Device Characteristics"))) {
188 qWarning() << Q_FUNC_INFO <<
"Grandparent of" << m_device->udi() <<
"doesn't have the \"Device Characteristics\" but is" << ioDVDServices.udi();
190 const QVariant devCharVar = ioDVDServices.property(QStringLiteral(
"Device Characteristics"));
191 devCharMap = devCharVar.
toMap();
193 d =
new Private(device, devCharMap);
196IOKitOpticalDrive::~IOKitOpticalDrive()
276 uint32_t cdFeatures = d->property(QStringLiteral(
"CD Features")).
toInt();
277 uint32_t dvdFeatures = d->property(QStringLiteral(
"DVD Features")).
toInt();
278 uint32_t bdFeatures = d->property(QStringLiteral(
"BD Features")).
toInt();
280 qDebug() << Q_FUNC_INFO <<
"cdFeatures" << cdFeatures <<
"dvdFeatures" << dvdFeatures <<
"bdFeatures" << bdFeatures;
282 for (
auto it = d->cdTypeMap.
cbegin(); it != d->cdTypeMap.
cend(); ++it) {
283 if (cdFeatures & it.value()) {
284 supported |= it.key();
287 for (
auto it = d->dvdTypeMap.
cbegin(); it != d->dvdTypeMap.
cend(); ++it) {
288 if (dvdFeatures & it.value()) {
289 supported |= it.key();
292 for (
auto it = d->bdTypeMap.
cbegin(); it != d->bdTypeMap.
cend(); ++it) {
293 const uint32_t value = it.value();
294 if (bdFeatures & value) {
295 supported |= it.key();
296 if (value == kBDFeaturesWriteMask) {
297 supported |= Solid::OpticalDrive::Bdre;
305int IOKitOpticalDrive::readSpeed()
const
310int IOKitOpticalDrive::writeSpeed()
const
315QList<int> IOKitOpticalDrive::writeSpeeds()
const
320bool IOKitOpticalDrive::eject()
322#ifdef EJECT_USING_DISKARBITRATION
324 int error = !d->eject(30.0);
329 {QStringLiteral(
"detach"), QStringLiteral(
"-verbose"), QStringLiteral(
"/dev/") + m_device->property(QStringLiteral(
"BSD Name")).
toString()});
331 qWarning() <<
"hdiutil returned" <<
error <<
"trying to eject" << m_device->product();
335 Q_EMIT ejectDone(Solid::ErrorType::OperationFailed,
QVariant(), m_device->udi());
338 Q_EMIT ejectDone(Solid::ErrorType::NoError,
QVariant(), m_device->udi());
343#include "moc_iokitopticaldrive.cpp"
Q_SCRIPTABLE CaptureState status()
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
const_iterator cbegin() const const
const_iterator cend() const const
int execute(const QString &program, const QStringList &arguments)
QString findExecutable(const QString &executableName, const QStringList &paths)
QString fromCFString(CFStringRef string)
std::string toStdString() const const
int toInt(bool *ok) const const
QMap< QString, QVariant > toMap() const const
QString toString() const const