#ifndef STAND_ALONE_TEST_TOOL
#define STAND_ALONE_TEST_TOOL 0
#endif
#if STAND_ALONE_TEST_TOOL
#define UPS_DEBUG 0
#define UPS_TOOL_DEBUG 1
#else
#define UPS_DEBUG 0
#define UPS_TOOL_DEBUG 0
#endif
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#if UPS_DEBUG
#include <SystemConfiguration/SCPrivate.h>
#endif
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/hid/IOHIDKeys.h>
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/hid/IOHIDUsageTables.h>
#include <IOKit/ps/IOPSKeys.h>
#include <IOKit/usb/IOUSBLib.h>
#define HID_MANAGER_ELEMENTS_INITIALIZED 0
enum
{
kVoltageIndex = 0,
kCurrentIndex,
kChargingIndex,
kDischargingIndex,
kRemainingCapacityIndex,
kRunTimeToEmptyIndex,
kACPresentIndex,
kCapacityModeIndex,
kNumberOfUPSElements
};
typedef struct UPSElementInfo {
IOHIDElementCookie cookie;
SInt32 currentValue;
} UPSElementInfo;
typedef struct UPSDeviceData {
UInt32 index; io_object_t notification; UInt32 vendorID; UInt32 productID; UInt32 locationID; UInt32 primaryUsagePage; UInt32 primaryUsage; IOHIDDeviceInterface * * hidDeviceInterface; IOHIDQueueInterface * * hidQueue; CFMutableDictionaryRef upsDictRef;
SCDynamicStoreRef upsStore; CFStringRef upsStoreKey;
CFStringRef nameStr;
UPSElementInfo elementInfo[kNumberOfUPSElements];
bool isPresent;
} UPSDeviceData;
enum
{
kMaxPowerDevices = 10
};
static IONotificationPortRef gNotifyPort; static io_iterator_t gAddedIter; static UPSDeviceData * gUPSDataRef[kMaxPowerDevices];
static kern_return_t CreatePowerManagerUPSEntry(UPSDeviceData *upsDataRef)
{
CFMutableDictionaryRef upsDictRef;
SCDynamicStoreRef upsStore;
CFStringRef upsStoreKey;
kern_return_t status = kIOReturnSuccess;
int elementValue = 0;
CFNumberRef elementNumRef;
char upsLabelString[] = {'/', 'U', 'S', 'B', '_', 'U', 'P', 'S', 0, 0};
#define kDefaultUPSName "USB UPS"
#if UPS_TOOL_DEBUG
printf ("Entering CreatePowerManagerUPSEntry\n");
#endif
upsDictRef = CFDictionaryCreateMutable(kCFAllocatorDefault, 10, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(upsDictRef, CFSTR(kIOPSIsPresentKey), kCFBooleanTrue);
CFDictionaryAddValue(upsDictRef, CFSTR(kIOPSIsChargingKey), kCFBooleanTrue);
CFDictionaryAddValue(upsDictRef, CFSTR(kIOPSTransportTypeKey), CFSTR(kIOPSUSBTransportType));
CFDictionaryAddValue(upsDictRef, CFSTR(kIOPSNameKey), CFSTR(kDefaultUPSName));
CFDictionaryAddValue(upsDictRef, CFSTR(kIOPSPowerSourceStateKey), CFSTR(kIOPSACPowerValue));
if ( upsDataRef->elementInfo[kRemainingCapacityIndex].cookie != 0 )
{
elementValue = 100;
elementNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionaryAddValue(upsDictRef, CFSTR(kIOPSCurrentCapacityKey), elementNumRef);
CFRelease(elementNumRef);
}
elementValue = 100;
elementNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionaryAddValue(upsDictRef, CFSTR(kIOPSMaxCapacityKey), elementNumRef);
CFRelease(elementNumRef);
if ( upsDataRef->elementInfo[kRunTimeToEmptyIndex].cookie != 0 )
{
elementValue = 100;
elementNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionaryAddValue(upsDictRef, CFSTR(kIOPSTimeToEmptyKey), elementNumRef);
CFRelease(elementNumRef);
}
if ( upsDataRef->elementInfo[kVoltageIndex].cookie != 0 )
{
elementValue = 13 * 1000 / 100;
elementNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionaryAddValue(upsDictRef, CFSTR(kIOPSVoltageKey), elementNumRef);
CFRelease(elementNumRef);
}
if ( upsDataRef->elementInfo[kCurrentIndex].cookie != 0 )
{
elementValue = 1; elementNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionaryAddValue(upsDictRef, CFSTR(kIOPSCurrentKey), elementNumRef);
CFRelease(elementNumRef);
}
upsStore = SCDynamicStoreCreate(NULL, CFSTR("UPS Power Manager"), NULL, NULL);
if ((upsDataRef->index > 0) && (upsDataRef->index < kMaxPowerDevices))
upsLabelString[8] = '0' + upsDataRef->index;
#if 0
SCLog(TRUE, LOG_NOTICE, CFSTR("What does CreatePowerManagerUPSEntry think our key name is?"));
SCLog(TRUE, LOG_NOTICE, CFSTR(" %@%@%@"), kSCDynamicStoreDomainState, CFSTR(kIOPSDynamicStorePath),
CFStringCreateWithCStringNoCopy(NULL, upsLabelString, kCFStringEncodingMacRoman, kCFAllocatorNull));
#endif
upsStoreKey = SCDynamicStoreKeyCreate(kCFAllocatorDefault, CFSTR("%@%@%@"),
kSCDynamicStoreDomainState, CFSTR(kIOPSDynamicStorePath),
CFStringCreateWithCStringNoCopy(NULL, upsLabelString, kCFStringEncodingMacRoman, kCFAllocatorNull));
if(!SCDynamicStoreSetValue(upsStore, upsStoreKey, upsDictRef))
{
status = SCError();
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: Encountered SCDynamicStoreSetValue error 0x%x"), status);
#endif
}
upsDataRef->upsDictRef = upsDictRef;
upsDataRef->upsStore = upsStore;
upsDataRef->upsStoreKey = upsStoreKey;
return status;
}
kern_return_t GetCurrentValueForElement( UPSDeviceData *upsDataRef, IOHIDEventStruct *upsEvent, int elementIndex )
{
kern_return_t status = kIOReturnSuccess;
#if UPS_TOOL_DEBUG
printf (" GetElementValueForCookie (%d)\n", elementIndex);
#endif
if ((upsDataRef == NULL) || (upsDataRef->hidDeviceInterface == NULL) || (upsDataRef->upsDictRef == NULL))
{
#if UPS_TOOL_DEBUG
printf ("GetElementValueForCookie: some setup routine failed (%p,%p,%p)\n", upsDataRef, upsDataRef->hidDeviceInterface, upsDataRef->upsDictRef );
#endif
return kIOReturnNoResources;
}
if (upsDataRef->elementInfo[elementIndex].cookie == 0)
{
#if UPS_TOOL_DEBUG
printf ("GetElementValueForCookie: No cookie for element (%d)\n", elementIndex);
#endif
return kIOReturnNoResources;
}
status = (*upsDataRef->hidDeviceInterface)->open(upsDataRef->hidDeviceInterface, 0);
if ( status != kIOReturnSuccess )
{
#if UPS_TOOL_DEBUG
printf ("GetElementValueForCookie: could not open HIDLib: 0x%x\n", status);
#endif
return kIOReturnNoResources;
}
status = (*upsDataRef->hidDeviceInterface)->getElementValue(upsDataRef->hidDeviceInterface,
upsDataRef->elementInfo[elementIndex].cookie,
upsEvent);
if ( (*upsDataRef->hidDeviceInterface)->close(upsDataRef->hidDeviceInterface) != kIOReturnSuccess)
{
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: could not close IOHIDLib\n" );
#endif
}
return status;
}
static void UpdateHIDMgrElement(UPSDeviceData *upsDataRef, UInt8 index)
{
IOHIDEventStruct upsEvent;
UInt32 elementValue;
CFNumberRef numRef;
kern_return_t status;
#if UPS_TOOL_DEBUG
printf (" UpdateHIDMgrElement %d\n", index);
#endif
if (index >= kNumberOfUPSElements)
{
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: index out of range (%d,%d)\n", kNumberOfUPSElements, index);
#endif
return;
}
if ((upsDataRef == NULL) || (upsDataRef->hidDeviceInterface == NULL) || (upsDataRef->upsDictRef == NULL))
{
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: some setup routine failed (%p,%p,%p)\n", upsDataRef, upsDataRef->hidDeviceInterface, upsDataRef->upsDictRef );
#endif
return;
}
if (upsDataRef->elementInfo[index].cookie == 0)
{
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: element %d does not have a valid cookie\n", index );
#endif
return;
}
status = (*upsDataRef->hidDeviceInterface)->open(upsDataRef->hidDeviceInterface, 0);
if ( status != kIOReturnSuccess )
{
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: could not open HIDLib: 0x%x\n", status);
#endif
return;
}
status = (*upsDataRef->hidDeviceInterface)->getElementValue(upsDataRef->hidDeviceInterface,
upsDataRef->elementInfo[index].cookie,
&upsEvent);
if (!status)
{
elementValue = upsEvent.value;
switch (index)
{
case kVoltageIndex:
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: kVoltageIndex: %ld\n", elementValue);
#endif
numRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSVoltageKey), numRef);
CFRelease(numRef);
break;
case kCurrentIndex:
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: kCurrentIndex: %ld\n", elementValue);
#endif
numRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSCurrentKey), numRef);
CFRelease(numRef);
break;
case kRemainingCapacityIndex:
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: kRemainingCapacityIndex: %ld\n", elementValue);
#endif
numRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSCurrentCapacityKey), numRef);
CFRelease(numRef);
break;
case kRunTimeToEmptyIndex:
elementValue = elementValue / 60;
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: kRunTimeToEmptyIndex: %ld\n", elementValue);
#endif
numRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &elementValue);
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSTimeToEmptyKey), numRef);
CFRelease(numRef);
break;
case kDischargingIndex:
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: kDischargingIndex: %ld\n", elementValue);
#endif
elementValue = (elementValue) ? FALSE : TRUE;
case kChargingIndex:
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: kChargingIndex: %ld\n", elementValue);
#endif
if (elementValue)
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSIsChargingKey), kCFBooleanTrue);
else
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSIsChargingKey), kCFBooleanFalse);
upsDataRef->elementInfo[kChargingIndex].currentValue = elementValue;
upsDataRef->elementInfo[kDischargingIndex].currentValue = (elementValue) ? FALSE : TRUE;
case kACPresentIndex:
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: kACPresentIndex: %ld\n", elementValue);
#endif
if (elementValue)
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSPowerSourceStateKey), CFSTR(kIOPSACPowerValue));
else
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSPowerSourceStateKey), CFSTR(kIOPSBatteryPowerValue));
upsDataRef->elementInfo[kACPresentIndex].currentValue = elementValue;
return;
}
upsDataRef->elementInfo[index].currentValue = elementValue;
}
else
{
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: getElementValue for %d returned 0x%x\n", index, status );
#endif
}
status = (*upsDataRef->hidDeviceInterface)->close(upsDataRef->hidDeviceInterface);
if ( status != kIOReturnSuccess )
{
#if UPS_TOOL_DEBUG
printf ("UpdateHIDMgrElement: could not close IOHIDLib\n" );
#endif
return;
}
}
static void StorePowerCookies(long type, long usagePage, long usage, IOHIDElementCookie cookie, UPSDeviceData *upsDataRef)
{
if (type < kIOHIDElementTypeInput_Misc || type > kIOHIDElementTypeInput_ScanCodes)
{
return;
}
if ( usagePage == kHIDPage_PowerDevice )
{
switch (usage)
{
case kHIDUsage_PD_Voltage:
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: found voltage cookie"));
#endif
upsDataRef->elementInfo[kVoltageIndex].cookie = cookie;
break;
case kHIDUsage_PD_Current:
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: found current cookie"));
#endif
upsDataRef->elementInfo[kCurrentIndex].cookie = cookie;
break;
}
}
else if (usagePage == kHIDPage_BatterySystem)
{
switch (usage)
{
case kHIDUsage_BS_Charging:
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: found kHIDUsage_BS_Charging cookie"));
#endif
upsDataRef->elementInfo[kChargingIndex].cookie = cookie;
break;
case kHIDUsage_BS_Discharging:
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: found kHIDUsage_BS_Discharging cookie"));
#endif
upsDataRef->elementInfo[kDischargingIndex].cookie = cookie;
break;
case kHIDUsage_BS_RemainingCapacity:
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: found kHIDUsage_BS_RemainingCapacity cookie"));
#endif
upsDataRef->elementInfo[kRemainingCapacityIndex].cookie = cookie;
break;
case kHIDUsage_BS_RunTimeToEmpty:
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: found kHIDUsage_BS_RunTimeToEmpty cookie"));
#endif
upsDataRef->elementInfo[kRunTimeToEmptyIndex].cookie = cookie;
break;
case kHIDUsage_BS_ACPresent:
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: found kHIDUsage_BS_ACPresent cookie"));
#endif
upsDataRef->elementInfo[kACPresentIndex].cookie = cookie;
break;
case kHIDUsage_BS_CapacityMode:
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: found kHIDUsage_BS_CapacityMode cookie"));
#endif
upsDataRef->elementInfo[kCapacityModeIndex].cookie = cookie;
break;
}
}
}
static void UPSDictionaryHandler(const void * value, void * refcon)
{
CFTypeRef refCF = 0;
IOHIDElementCookie cookie = 0;
long number = 0;
long type = 0;
long usage = 0;
long usagePage = 0;
if (CFGetTypeID(value) != CFDictionaryGetTypeID())
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: No dictionary for UPSDictionaryHandler"));
#endif
return;
}
refCF = CFDictionaryGetValue(value, CFSTR(kIOHIDElementCookieKey));
if ( (refCF != 0) && (CFGetTypeID(refCF) == CFNumberGetTypeID()) )
if ( CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &number))
cookie = (IOHIDElementCookie)number;
refCF = CFDictionaryGetValue(value, CFSTR(kIOHIDElementUsageKey));
if ( (refCF != 0) && (CFGetTypeID(refCF) == CFNumberGetTypeID()) )
CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &usage);
refCF = CFDictionaryGetValue(value, CFSTR(kIOHIDElementUsagePageKey));
if ( (refCF != 0) && (CFGetTypeID(refCF) == CFNumberGetTypeID()) )
CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &usagePage);
refCF = CFDictionaryGetValue(value, CFSTR(kIOHIDElementTypeKey));
if ( (refCF != 0) && (CFGetTypeID(refCF) == CFNumberGetTypeID()) )
CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &type);
if (kIOHIDElementTypeCollection == type)
{
CFTypeRef elementRef;
elementRef = CFDictionaryGetValue(value, CFSTR(kIOHIDElementKey));
if (elementRef)
{
CFRange range = { 0, CFArrayGetCount(elementRef) };
CFArrayApplyFunction (elementRef, range, UPSDictionaryHandler, refcon);
}
}
else
{
StorePowerCookies(type, usagePage, usage, cookie, refcon);
}
}
static void FindUPSCookies(CFMutableDictionaryRef properties, UPSDeviceData *upsDataRef)
{
CFTypeRef refCFTopElement = 0;
int i;
for (i = 0; i < kNumberOfUPSElements; i++)
upsDataRef->elementInfo[i].cookie = 0;
refCFTopElement = CFDictionaryGetValue(properties, CFSTR(kIOHIDElementKey));
if (refCFTopElement)
{
CFRange range = { 0, CFArrayGetCount(refCFTopElement) };
CFArrayApplyFunction (refCFTopElement, range, UPSDictionaryHandler, upsDataRef );
}
}
static void UPSPollingTimer(CFRunLoopTimerRef timer, void *info)
{
IOHIDEventStruct event;
IOHIDEventStruct hidEvent;
AbsoluteTime zeroTime = {0,0};
HRESULT result;
UInt8 i;
SInt32 minutes;
CFNumberRef numRef;
Boolean update;
Boolean updateIsCharging;
Boolean newIsCharging;
IOReturn status;
for ( i = 0; i < kMaxPowerDevices; i++ )
{
if ( gUPSDataRef[i] != NULL && gUPSDataRef[i]->hidQueue != NULL)
{
while ( (result = (*(gUPSDataRef[i]->hidQueue))->getNextEvent(gUPSDataRef[i]->hidQueue, &event, zeroTime, 0)) != kIOReturnUnderrun)
{
update = FALSE;
updateIsCharging = FALSE;
newIsCharging = FALSE;
if (result != kIOReturnSuccess)
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: queue getNextEvent result error: 0x%lx"),result);
#endif
}
else
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: queue: event:[0x%lx] %ld"),(unsigned long) event.elementCookie, event.value);
#endif
#if UPS_TOOL_DEBUG
printf ("queue: event:[0x%lx] %ld\n", (unsigned long) event.elementCookie, event.value);
#endif
if (event.elementCookie == gUPSDataRef[i]->elementInfo[kRemainingCapacityIndex].cookie)
{
#if UPS_TOOL_DEBUG
printf (" Remaining Capacity (%ld, %ld, %ld)\n", event.value, gUPSDataRef[i]->elementInfo[kRemainingCapacityIndex].currentValue, gUPSDataRef[i]->elementInfo[kChargingIndex].currentValue );
#endif
if (gUPSDataRef[i]->elementInfo[kRemainingCapacityIndex].currentValue != event.value)
{
gUPSDataRef[i]->elementInfo[kRemainingCapacityIndex].currentValue = event.value;
numRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &event.value);
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSCurrentCapacityKey), numRef);
update = TRUE;
CFRelease( numRef );
status = GetCurrentValueForElement( gUPSDataRef[i], &hidEvent, kRunTimeToEmptyIndex);
if ( status == kIOReturnSuccess )
{
#if UPS_TOOL_DEBUG
printf(" Got new value for kRunTimeToEmpty after capacity changed (%ld)\n", hidEvent.value);
#endif
minutes = hidEvent.value / 60;
if (gUPSDataRef[i]->elementInfo[kRunTimeToEmptyIndex].currentValue != minutes)
{
#if UPS_TOOL_DEBUG
printf(" Updating kRunTimeToEmpty after capacity changed (%ld)\n", minutes);
#endif
gUPSDataRef[i]->elementInfo[kRunTimeToEmptyIndex].currentValue = minutes;
numRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &minutes);
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSTimeToEmptyKey), numRef);
CFRelease( numRef );
}
}
}
}
else if (event.elementCookie == gUPSDataRef[i]->elementInfo[kRunTimeToEmptyIndex].cookie)
{
#if UPS_TOOL_DEBUG
printf (" Run Time to Empty (%ld, %ld, %ld)\n", event.value, gUPSDataRef[i]->elementInfo[kRunTimeToEmptyIndex].currentValue, gUPSDataRef[i]->elementInfo[kChargingIndex].currentValue );
#endif
minutes = event.value / 60;
if (gUPSDataRef[i]->elementInfo[kRunTimeToEmptyIndex].currentValue != minutes)
{
gUPSDataRef[i]->elementInfo[kRunTimeToEmptyIndex].currentValue = minutes;
numRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &minutes);
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSTimeToEmptyKey), numRef);
update = TRUE;
CFRelease( numRef );
}
}
else if (event.elementCookie == gUPSDataRef[i]->elementInfo[kVoltageIndex].cookie)
{
#if UPS_TOOL_DEBUG
printf (" Voltage\n");
#endif
if (gUPSDataRef[i]->elementInfo[kVoltageIndex].currentValue != event.value)
{
gUPSDataRef[i]->elementInfo[kVoltageIndex].currentValue = event.value;
numRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &event.value);
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSVoltageKey), numRef);
update = TRUE;
CFRelease( numRef );
}
}
else if (event.elementCookie == gUPSDataRef[i]->elementInfo[kCurrentIndex].cookie)
{
#if UPS_TOOL_DEBUG
printf (" Current\n");
#endif
if (gUPSDataRef[i]->elementInfo[kCurrentIndex].currentValue != event.value)
{
gUPSDataRef[i]->elementInfo[kCurrentIndex].currentValue = event.value;
numRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &event.value);
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSCurrentKey), numRef);
update = TRUE;
CFRelease( numRef );
}
}
else if (event.elementCookie == gUPSDataRef[i]->elementInfo[kACPresentIndex].cookie)
{
#if UPS_TOOL_DEBUG
printf (" AC Present\n");
#endif
if (gUPSDataRef[i]->elementInfo[kACPresentIndex].currentValue != event.value)
{
gUPSDataRef[i]->elementInfo[kACPresentIndex].currentValue = event.value;
if (event.value)
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSPowerSourceStateKey),
CFSTR(kIOPSACPowerValue));
else
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSPowerSourceStateKey),
CFSTR(kIOPSBatteryPowerValue));
update = TRUE;
}
}
else if (event.elementCookie == gUPSDataRef[i]->elementInfo[kDischargingIndex].cookie)
{
#if UPS_TOOL_DEBUG
printf (" Discharging\n");
#endif
updateIsCharging = TRUE;
newIsCharging = event.value ? FALSE : TRUE;
}
else if (event.elementCookie == gUPSDataRef[i]->elementInfo[kChargingIndex].cookie)
{
#if UPS_TOOL_DEBUG
printf (" Charging\n");
#endif
updateIsCharging = TRUE;
newIsCharging = event.value ? TRUE : FALSE;
}
if (updateIsCharging)
{
if (newIsCharging)
{
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSIsChargingKey),
kCFBooleanTrue);
gUPSDataRef[i]->elementInfo[kChargingIndex].currentValue = TRUE;
gUPSDataRef[i]->elementInfo[kDischargingIndex].currentValue = FALSE;
if (gUPSDataRef[i]->elementInfo[kACPresentIndex].currentValue != TRUE)
{
#if UPS_TOOL_DEBUG
printf (" Updating dictionary to AC Present\n");
#endif
gUPSDataRef[i]->elementInfo[kACPresentIndex].currentValue = TRUE;
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSPowerSourceStateKey),
CFSTR(kIOPSACPowerValue));
}
}
else
{
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSIsChargingKey),
kCFBooleanFalse);
gUPSDataRef[i]->elementInfo[kChargingIndex].currentValue = FALSE;
gUPSDataRef[i]->elementInfo[kDischargingIndex].currentValue = TRUE;
if (gUPSDataRef[i]->elementInfo[kACPresentIndex].currentValue != FALSE)
{
#if UPS_TOOL_DEBUG
printf (" Updating dictionary to Battery Power\n");
#endif
gUPSDataRef[i]->elementInfo[kACPresentIndex].currentValue = FALSE;
CFDictionarySetValue(gUPSDataRef[i]->upsDictRef, CFSTR(kIOPSPowerSourceStateKey),
CFSTR(kIOPSBatteryPowerValue));
}
}
update = TRUE;
}
if (update)
{
#if UPS_TOOL_DEBUG
printf ("calling SCDynamicStoreSetValue\n");
#endif
SCDynamicStoreSetValue(gUPSDataRef[i]->upsStore, gUPSDataRef[i]->upsStoreKey,
gUPSDataRef[i]->upsDictRef);
} } } } } }
static void InitializeUPSTimer()
{
CFRunLoopTimerRef upsTimer;
upsTimer = CFRunLoopTimerCreate(NULL,
CFAbsoluteTimeGetCurrent(), (CFTimeInterval)5.0, NULL, 0, UPSPollingTimer, NULL);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), upsTimer, kCFRunLoopDefaultMode);
CFRelease(upsTimer);
}
void DeviceNotification( void * refCon,
io_service_t service,
natural_t messageType,
void * messageArgument )
{
kern_return_t kr;
UPSDeviceData *upsDataRef = (UPSDeviceData *) refCon;
if ( (messageType == kIOMessageServiceIsTerminated) && (upsDataRef != NULL) )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: Device (%ld/%ld) at location 0x%lx was removed"), upsDataRef->vendorID,upsDataRef->productID,upsDataRef->locationID);
#endif
#if UPS_TOOL_DEBUG
printf("UPSSupport: Device (%ld/%ld) at location 0x%lx was removed\n", upsDataRef->vendorID,upsDataRef->productID,upsDataRef->locationID);
#endif
if (upsDataRef->hidQueue != NULL)
{
kr = (*upsDataRef->hidQueue)->stop(upsDataRef->hidQueue);
kr = (*upsDataRef->hidQueue)->dispose(upsDataRef->hidQueue);
kr = (*upsDataRef->hidQueue)->Release(upsDataRef->hidQueue);
upsDataRef->hidQueue = NULL;
}
if (upsDataRef->hidDeviceInterface != NULL)
{
kr = (*upsDataRef->hidDeviceInterface)->Release (upsDataRef->hidDeviceInterface);
upsDataRef->hidDeviceInterface = NULL;
}
if (upsDataRef->notification != NULL)
{
kr = IOObjectRelease(upsDataRef->notification);
upsDataRef->notification = NULL;
}
upsDataRef->locationID = 0;
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSIsPresentKey), kCFBooleanFalse);
upsDataRef->isPresent = FALSE;
SCDynamicStoreSetValue(upsDataRef->upsStore, upsDataRef->upsStoreKey,
upsDataRef->upsDictRef);
}
}
static void AddUPSElementsToHIDQueue( UPSDeviceData *upsDataRef )
{
kern_return_t kr;
if ( upsDataRef->elementInfo[kVoltageIndex].cookie != 0 )
{
kr = (*upsDataRef->hidQueue)->addElement (upsDataRef->hidQueue, upsDataRef->elementInfo[kVoltageIndex].cookie, 0);
if ( KERN_SUCCESS != kr )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't add voltageCookie to HID queue (0x%08x)"), kr);
#endif
}
}
if ( upsDataRef->elementInfo[kCurrentIndex].cookie != 0 )
{
kr = (*upsDataRef->hidQueue)->addElement (upsDataRef->hidQueue, upsDataRef->elementInfo[kCurrentIndex].cookie, 0);
if ( KERN_SUCCESS != kr )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't add currentCookie to HID queue (0x%08x)"), kr);
#endif
}
}
if ( upsDataRef->elementInfo[kChargingIndex].cookie != 0 )
{
kr = (*upsDataRef->hidQueue)->addElement (upsDataRef->hidQueue, upsDataRef->elementInfo[kChargingIndex].cookie, 0);
if ( KERN_SUCCESS != kr )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't add chargingCookie to HID queue (0x%08x)"), kr);
#endif
}
}
if ( upsDataRef->elementInfo[kDischargingIndex].cookie != 0 )
{
kr = (*upsDataRef->hidQueue)->addElement (upsDataRef->hidQueue, upsDataRef->elementInfo[kDischargingIndex].cookie, 0);
if ( KERN_SUCCESS != kr )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't add dischargingCookie to HID queue (0x%08x)"), kr);
#endif
}
}
if ( upsDataRef->elementInfo[kRemainingCapacityIndex].cookie != 0 )
{
kr = (*upsDataRef->hidQueue)->addElement (upsDataRef->hidQueue, upsDataRef->elementInfo[kRemainingCapacityIndex].cookie, 0);
if ( KERN_SUCCESS != kr )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't add remainingCapacityCookie to HID queue (0x%08x)"), kr);
#endif
}
}
if ( upsDataRef->elementInfo[kRunTimeToEmptyIndex].cookie != 0 )
{
kr = (*upsDataRef->hidQueue)->addElement (upsDataRef->hidQueue, upsDataRef->elementInfo[kRunTimeToEmptyIndex].cookie, 0);
if ( KERN_SUCCESS != kr )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't add runTimeToEmptyCookie to HID queue (0x%08x)"), kr);
#endif
}
}
if ( upsDataRef->elementInfo[kACPresentIndex].cookie != 0 )
{
kr = (*upsDataRef->hidQueue)->addElement (upsDataRef->hidQueue, upsDataRef->elementInfo[kACPresentIndex].cookie, 0);
if ( KERN_SUCCESS != kr )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't add acPresentCookie to HID queue (0x%08x)"), kr);
#endif
}
}
}
kern_return_t SetupQueueForUPSReports( io_object_t hidDevice, UPSDeviceData * upsDataRef )
{
kern_return_t kr;
kern_return_t localErr;
upsDataRef->hidQueue = (*upsDataRef->hidDeviceInterface)->allocQueue (upsDataRef->hidDeviceInterface);
if (upsDataRef->hidQueue == NULL)
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't allocate a HID queue"));
#endif
upsDataRef->hidQueue = NULL;
kr = E_OUTOFMEMORY;
goto ErrorExit;
}
kr = (*upsDataRef->hidQueue)->create (upsDataRef->hidQueue, 0, 8);
if ( KERN_SUCCESS != kr )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't create a HID queue"));
#endif
goto ErrorExit;
}
AddUPSElementsToHIDQueue( upsDataRef );
kr = (*upsDataRef->hidQueue)->start (upsDataRef->hidQueue);
if ( KERN_SUCCESS != kr )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't start a HID queue"));
#endif
goto ErrorExit;
}
kr = IOServiceAddInterestNotification( gNotifyPort, hidDevice, kIOGeneralInterest, DeviceNotification, upsDataRef, &(upsDataRef->notification) );
if (KERN_SUCCESS != kr)
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: IOServiceAddInterestNotification returned 0x%08x"), kr);
#endif
}
return KERN_SUCCESS;
ErrorExit:
if ( upsDataRef->notification != NULL )
{
localErr = IOObjectRelease(upsDataRef->notification);
upsDataRef->notification = NULL;
}
if ( upsDataRef->hidQueue != NULL )
{
localErr = (*upsDataRef->hidQueue)->stop(upsDataRef->hidQueue);
localErr = (*upsDataRef->hidQueue)->dispose(upsDataRef->hidQueue);
localErr = (*upsDataRef->hidQueue)->Release(upsDataRef->hidQueue);
upsDataRef->hidQueue = NULL;
}
return kr;
}
void InformPowerMangerOfUPS( UPSDeviceData * upsDataRef )
{
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSNameKey), upsDataRef->nameStr);
CFDictionarySetValue(upsDataRef->upsDictRef, CFSTR(kIOPSIsPresentKey), kCFBooleanTrue);
upsDataRef->isPresent = TRUE;
SCDynamicStoreSetValue(upsDataRef->upsStore, upsDataRef->upsStoreKey,
upsDataRef->upsDictRef);
}
kern_return_t CreateCFPluginForDevice( io_object_t hidDevice, UPSDeviceData * upsDataRef )
{
kern_return_t kr = kIOReturnSuccess;
IOCFPlugInInterface **plugInInterface = NULL;
SInt32 score;
HRESULT result = S_FALSE;
kr = IOCreatePlugInInterfaceForService(hidDevice, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
&plugInInterface, &score);
if ( KERN_SUCCESS == kr )
{
result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
(LPVOID)&upsDataRef->hidDeviceInterface);
(*plugInInterface)->Release(plugInInterface);
if ( result != S_OK )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: couldn't create a device interface (%08x)"), result);
#endif
if ( upsDataRef->hidDeviceInterface != NULL )
{
(*upsDataRef->hidDeviceInterface)->Release (upsDataRef->hidDeviceInterface);
upsDataRef->hidDeviceInterface = NULL;
}
kr = E_OUTOFMEMORY;
}
}
else
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: unable to create a plugin (0x%x)"), kr);
#endif
}
return kr;
}
UPSDeviceData * GetPrivateData( CFMutableDictionaryRef properties )
{
UPSDeviceData *upsDataRef = NULL;
UInt32 deviceVendorID = 0;
UInt32 deviceProductID = 0;
CFNumberRef number; CFStringRef upsName;
UInt32 i = 0;
number = (CFNumberRef) CFDictionaryGetValue( properties, CFSTR( kIOHIDVendorIDKey ) );
if ( number )
CFNumberGetValue(number, kCFNumberSInt32Type, &deviceVendorID );
number = (CFNumberRef) CFDictionaryGetValue( properties, CFSTR( kIOHIDProductIDKey ) );
if ( number )
CFNumberGetValue(number, kCFNumberSInt32Type, &deviceProductID );
for ( i = 0; i < kMaxPowerDevices; i++)
{
if ( gUPSDataRef[i] == NULL )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("Creating new UPSDeviceData at index %d"), i);
#endif
upsDataRef = malloc(sizeof(UPSDeviceData));
if ( upsDataRef )
{
bzero(upsDataRef, sizeof(UPSDeviceData));
gUPSDataRef[i] = upsDataRef;
upsDataRef->index = i;
}
break;
}
else if (!(gUPSDataRef[i]->isPresent) && (gUPSDataRef[i]->vendorID == deviceVendorID) &&
(gUPSDataRef[i]->productID == deviceProductID))
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("Reusing UPSDeviceData at index %d"), i);
#endif
upsDataRef = gUPSDataRef[i];
break;
}
}
if ( upsDataRef != NULL )
{
upsDataRef->vendorID = deviceVendorID;
upsDataRef->productID = deviceProductID;
upsDataRef->primaryUsagePage = 0;
number = (CFNumberRef) CFDictionaryGetValue( properties, CFSTR( kIOHIDPrimaryUsagePageKey ) );
if ( number )
CFNumberGetValue(number, kCFNumberSInt32Type, &upsDataRef->primaryUsagePage );
upsDataRef->primaryUsage = 0;
number = (CFNumberRef) CFDictionaryGetValue( properties, CFSTR( kIOHIDPrimaryUsageKey ) );
if ( number )
CFNumberGetValue(number, kCFNumberSInt32Type, &upsDataRef->primaryUsage );
upsDataRef->locationID = 0;
number = (CFNumberRef) CFDictionaryGetValue( properties, CFSTR( kIOHIDLocationIDKey ) );
if ( number )
CFNumberGetValue(number, kCFNumberSInt32Type, &upsDataRef->locationID );
upsName = (CFStringRef) CFDictionaryGetValue( properties, CFSTR( kIOHIDProductKey ) );
if ( !upsName )
upsName = (CFStringRef) CFDictionaryGetValue( properties, CFSTR( kIOHIDManufacturerKey ) );
if ( !upsName )
upsName = CFSTR( kDefaultUPSName );
upsDataRef->nameStr = upsName;
}
return upsDataRef;
}
static void HIDDeviceAdded(void *refCon, io_iterator_t iterator)
{
kern_return_t kr;
io_object_t hidDevice = NULL;
UPSDeviceData *upsDataRef = NULL;
CFMutableDictionaryRef properties = NULL;
CFNumberRef number = NULL;
UInt32 primaryUsagePage = 0;
int i;
#if UPS_TOOL_DEBUG
printf ("Entering HIDDeviceAdded\n");
#endif
while ( (hidDevice = IOIteratorNext(iterator)) )
{
kr = IORegistryEntryCreateCFProperties (hidDevice, &properties, kCFAllocatorDefault, kNilOptions);
if ((kr == KERN_SUCCESS) && properties)
{
number = (CFNumberRef) CFDictionaryGetValue( properties, CFSTR( kIOHIDPrimaryUsagePageKey ) );
if ( number )
CFNumberGetValue(number, kCFNumberSInt32Type, &primaryUsagePage );
if (primaryUsagePage == kHIDPage_PowerDevice || primaryUsagePage == kHIDPage_BatterySystem)
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: HIDDevice 0x%08x added"), hidDevice);
#endif
upsDataRef = GetPrivateData( properties );
if ( upsDataRef )
{
#if UPS_TOOL_DEBUG
printf("UPSSupport: Device added at location 0x%8.8lx\n", upsDataRef->locationID);
#endif
kr = CreateCFPluginForDevice( hidDevice, upsDataRef );
if ( kr == kIOReturnSuccess )
{
FindUPSCookies(properties, upsDataRef);
if (!(upsDataRef->upsDictRef))
{
kr = CreatePowerManagerUPSEntry(upsDataRef);
}
if (kr == KERN_SUCCESS)
{
for (i = 0; i < kNumberOfUPSElements; i++)
{
UpdateHIDMgrElement(upsDataRef, i);
}
InformPowerMangerOfUPS( upsDataRef );
kr = SetupQueueForUPSReports( hidDevice, upsDataRef );
if ( kr != KERN_SUCCESS )
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("SetupForUPSReports error (0x%x) so no reports for this device."), kr);
#endif
}
}
else
{
#if UPS_TOOL_DEBUG
printf("UPSSupport: Error (0x%8.8x) creating PowerManagerUPSEntry. Probably not running as root?\n", kr);
#endif
}
}
else
{
#if UPS_TOOL_DEBUG
printf("UPSSupport: Error (0x%8.8x) creating plugIn for device.\n", kr);
#endif
}
}
else
{
#if UPS_TOOL_DEBUG
printf("UPSSupport: No upsDataRef!\n");
#endif
}
}
CFRelease (properties);
}
kr = IOObjectRelease(hidDevice);
}
}
static void RegisterForUSBHIDNotifications( mach_port_t * masterPort)
{
CFMutableDictionaryRef matchingDict;
UInt32 usagePage = kHIDPage_PowerDevice;
kern_return_t kr;
CFNumberRef refUsagePage = NULL;
matchingDict = IOServiceMatching(kIOHIDDeviceKey);
if (!matchingDict)
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: Can't create a kIOHIDDeviceKey matching dictionary"));
#endif
return;
}
refUsagePage = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usagePage);
CFRelease(refUsagePage);
gNotifyPort = IONotificationPortCreate(*masterPort);
CFRunLoopAddSource( CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(gNotifyPort),
kCFRunLoopDefaultMode);
kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification, matchingDict, HIDDeviceAdded, NULL, &gAddedIter );
if (KERN_SUCCESS != kr)
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: IOServiceAddMatchingNotification returned 0x%08x"), kr);
#endif
}
HIDDeviceAdded(NULL, gAddedIter);
}
void InitUPSNotifications()
{
mach_port_t masterPort;
kern_return_t kr;
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("In InitUPSNotifications"));
#endif
#if UPS_TOOL_DEBUG
printf ("Entering InitUPSNotifications\n");
#endif
kr = IOMasterPort(bootstrap_port, &masterPort);
if (kr || !masterPort)
{
#if UPS_DEBUG
SCLog(TRUE, LOG_NOTICE, CFSTR("UPSSupport: Couldn't create a master Port(0x%x)"), kr);
#endif
return;
}
RegisterForUSBHIDNotifications( &masterPort );
}
#if STAND_ALONE_TEST_TOOL
static void SignalHandler(int sigraised)
{
printf("\nInterrupted\n");
IONotificationPortDestroy(gNotifyPort);
if (gAddedIter)
{
IOObjectRelease(gAddedIter);
gAddedIter = 0;
}
exit(0);
}
int main (int argc, const char *argv[])
{
sig_t oldHandler;
bzero( gUPSDataRef, sizeof(gUPSDataRef) );
oldHandler = signal(SIGINT, SignalHandler);
if (oldHandler == SIG_ERR)
printf("Could not establish new signal handler");
InitUPSNotifications ();
InitializeUPSTimer();
CFRunLoopRun();
return 0;
}
#else
void load(CFBundleRef bundle, Boolean bundleVerbose)
{
bzero( gUPSDataRef, sizeof(gUPSDataRef) );
InitUPSNotifications ();
InitializeUPSTimer();
}
#endif