#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
#include "ExternalMedia.h"
#include "PMAssertions.h"
#include "PrivateLib.h"
#if !TARGET_OS_EMBEDDED
#define _kExternalMediaAssertionName "com.apple.powermanagement.externalmediamounted"
static bool weLikeTheDisk(DADiskRef disk);
static void adjustExternalDiskAssertion(void);
static void _DiskDisappeared(DADiskRef disk, void *context);
static void _DiskAppeared(DADiskRef disk, void * context);
static CFMutableSetRef gExternalMediaSet = NULL;
static IOPMAssertionID gDiskAssertionID = kIOPMNullAssertionID;
static DASessionRef gDASession = NULL;
__private_extern__ void ExternalMedia_prime(void)
{
gExternalMediaSet = CFSetCreateMutable(0, 0, &kCFTypeSetCallBacks);
if (!gExternalMediaSet)
return;
gDASession = DASessionCreate(0);
DARegisterDiskAppearedCallback(gDASession, kDADiskDescriptionMatchVolumeMountable, _DiskAppeared, NULL);
DARegisterDiskDisappearedCallback(gDASession, kDADiskDescriptionMatchVolumeMountable, _DiskDisappeared, NULL);
DASessionScheduleWithRunLoop(gDASession, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
static void _DiskDisappeared(DADiskRef disk, void *context)
{
if (weLikeTheDisk(disk))
{
CFSetRemoveValue(gExternalMediaSet, disk);
adjustExternalDiskAssertion();
}
}
static void _DiskAppeared(DADiskRef disk, void * context)
{
if (weLikeTheDisk(disk))
{
CFSetSetValue(gExternalMediaSet, disk);
adjustExternalDiskAssertion();
}
}
static bool weLikeTheDisk(DADiskRef disk)
{
CFDictionaryRef description = NULL;
CFStringRef protocol = NULL;
bool ret = false;
description = DADiskCopyDescription(disk);
if (description) {
protocol = CFDictionaryGetValue(description, kDADiskDescriptionDeviceProtocolKey);
if (protocol &&
(CFEqual(protocol, CFSTR(kIOPropertyPhysicalInterconnectTypeUSB)) ||
CFEqual(protocol, CFSTR(kIOPropertyPhysicalInterconnectTypeSecureDigital))))
{
ret = true;
}
CFRelease(description);
}
return ret;
}
static CFMutableDictionaryRef _IOPMAssertionDescriptionCreate(
CFStringRef AssertionType,
CFStringRef Name,
CFStringRef Details,
CFStringRef HumanReadableReason,
CFStringRef LocalizationBundlePath,
CFTimeInterval Timeout,
CFStringRef TimeoutBehavior)
{
CFMutableDictionaryRef descriptor = NULL;
if (!AssertionType || !Name) {
return NULL;
}
descriptor = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!descriptor) {
return NULL;
}
CFDictionarySetValue(descriptor, kIOPMAssertionNameKey, Name);
int _on = kIOPMAssertionLevelOn;
CFNumberRef _on_num = CFNumberCreate(0, kCFNumberIntType, &_on);
CFDictionarySetValue(descriptor, kIOPMAssertionLevelKey, _on_num);
CFRelease(_on_num);
CFDictionarySetValue(descriptor, kIOPMAssertionTypeKey, AssertionType);
if (Details) {
CFDictionarySetValue(descriptor, kIOPMAssertionDetailsKey, Details);
}
if (HumanReadableReason) {
CFDictionarySetValue(descriptor, kIOPMAssertionHumanReadableReasonKey, HumanReadableReason);
}
if (LocalizationBundlePath) {
CFDictionarySetValue(descriptor, kIOPMAssertionLocalizationBundlePathKey, LocalizationBundlePath);
}
if (Timeout) {
CFNumberRef Timeout_num = CFNumberCreate(0, kCFNumberDoubleType, &Timeout);
CFDictionarySetValue(descriptor, kIOPMAssertionTimeoutKey, Timeout_num);
CFRelease(Timeout_num);
}
if (TimeoutBehavior)
{
CFDictionarySetValue(descriptor, kIOPMAssertionTimeoutActionKey, TimeoutBehavior);
}
return descriptor;
}
static void adjustExternalDiskAssertion()
{
CFIndex deviceCount = CFSetGetCount(gExternalMediaSet);
if (0 == deviceCount)
{
InternalReleaseAssertion(&gDiskAssertionID);
return;
}
if (0 < deviceCount)
{
CFMutableDictionaryRef assertionDescription = NULL;
assertionDescription = _IOPMAssertionDescriptionCreate(
_kIOPMAssertionTypeExternalMedia,
CFSTR(_kExternalMediaAssertionName),
NULL, CFSTR("An external media device is attached."), NULL, 0, NULL);
InternalCreateAssertion(assertionDescription,
&gDiskAssertionID);
CFRelease(assertionDescription);
return;
}
return;
}
#endif