1/* 2 * Copyright (c) 1998-2003 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23//--------------------------------------------------------------------------- 24// Includes 25//--------------------------------------------------------------------------- 26 27#include <syslog.h> 28#include <CoreFoundation/CoreFoundation.h> 29#include <mach/mach.h> 30#include <mach/mach_host.h> 31#include <mach/mach_error.h> 32#include <libc.h> 33#include <servers/bootstrap.h> 34#include <sysexits.h> 35#include <notify.h> 36 37#include <IOKit/IOKitLib.h> 38#include <IOKit/IOKitServer.h> 39#include <IOKit/IOCFURLAccess.h> 40#include <IOKit/IOCFSerialize.h> 41#include <IOKit/IOCFUnserialize.h> 42#include <IOKit/IOMessage.h> 43#include <IOKit/hid/IOHIDKeys.h> 44#include <IOKit/ps/IOPSKeys.h> 45#include <IOKit/ps/IOPowerSources.h> 46#include <IOKit/ps/IOPowerSourcesPrivate.h> 47 48#include <SystemConfiguration/SystemConfiguration.h> 49#if UPS_DEBUG 50 #include <SystemConfiguration/SCPrivate.h> 51#endif 52 53#include "IOUPSPlugIn.h" 54#include "IOUPSPrivate.h" 55 56#define kDefaultUPSName "Generic UPS" 57 58//--------------------------------------------------------------------------- 59// Globals 60//--------------------------------------------------------------------------- 61static CFRunLoopSourceRef gClientRequestRunLoopSource = NULL; 62static CFRunLoopRef gMainRunLoop = NULL; 63static CFMutableArrayRef gUPSDataArrayRef = NULL; 64static unsigned int gUPSCount = 0; 65static IONotificationPortRef gNotifyPort = NULL; 66static io_iterator_t gAddedIter = MACH_PORT_NULL; 67 68//--------------------------------------------------------------------------- 69// TypeDefs 70//--------------------------------------------------------------------------- 71typedef struct UPSData { 72 IOPSPowerSourceID powerSourceID; 73 io_object_t notification; 74 IOUPSPlugInInterface **upsPlugInInterface; 75 int upsID; 76 Boolean isPresent; 77 CFMutableDictionaryRef upsStoreDict; 78 CFRunLoopSourceRef upsEventSource; 79 CFRunLoopTimerRef upsEventTimer; 80} UPSData; 81 82typedef UPSData *UPSDataRef; 83 84 85//--------------------------------------------------------------------------- 86// Methods 87//--------------------------------------------------------------------------- 88void CleanupAndExit(void); 89static void SignalHandler(int sigraised); 90static void InitUPSNotifications(); 91static void ProcessUPSEventSource(CFTypeRef typeRef, CFRunLoopTimerRef * pTimer, CFRunLoopSourceRef * pSource); 92static void UPSDeviceAdded(void *refCon, io_iterator_t iterator); 93static void DeviceNotification(void *refCon, io_service_t service, 94 natural_t messageType, void *messageArgument); 95static void UPSEventCallback(void * target, IOReturn result, void *refcon, 96 void *sender, CFDictionaryRef event); 97static void ProcessUPSEvent(UPSDataRef upsDataRef, CFDictionaryRef event); 98static UPSDataRef GetPrivateData( CFDictionaryRef properties ); 99static IOReturn CreatePowerManagerUPSEntry(UPSDataRef upsDataRef, 100 CFDictionaryRef properties, 101 CFSetRef capabilities); 102static Boolean SetupMIGServer(); 103 104//--------------------------------------------------------------------------- 105// main 106// 107//--------------------------------------------------------------------------- 108int main (int argc, const char *argv[]) { 109 openlog("upsd", LOG_PID|LOG_NDELAY, LOG_USER); 110 signal(SIGINT, SignalHandler); 111 SetupMIGServer(); 112 // Listen for any HID Power Devices or Battery Systems 113 InitUPSNotifications(kIOPowerDeviceUsageKey); 114 InitUPSNotifications(kIOBatterySystemUsageKey); 115 CFRunLoopRun(); 116 117 return 0; 118} 119 120 121//--------------------------------------------------------------------------- 122// SignalHandler 123//--------------------------------------------------------------------------- 124void CleanupAndExit(void) { 125 // Clean up here 126 IONotificationPortDestroy(gNotifyPort); 127 if (gAddedIter) { 128 IOObjectRelease(gAddedIter); 129 gAddedIter = 0; 130 } 131 exit(0); 132} 133 134 135//--------------------------------------------------------------------------- 136// SignalHandler 137//--------------------------------------------------------------------------- 138void SignalHandler(int sigraised) { 139 syslog(LOG_INFO, "upsd: exiting SIGINT\n"); 140 CleanupAndExit(); 141} 142 143 144//--------------------------------------------------------------------------- 145// SetupMIGServer 146//--------------------------------------------------------------------------- 147extern void upsd_mach_port_callback(CFMachPortRef port, void *msg, CFIndex size, 148 void *info); 149 150Boolean SetupMIGServer() { 151 Boolean result = true; 152 kern_return_t kern_result = KERN_SUCCESS; 153 CFMachPortRef upsdMachPort = NULL; // must release 154 mach_port_t ups_port = MACH_PORT_NULL; 155 156 /*if (IOUPSMIGServerIsRunning(&bootstrap_port, NULL)) { 157 result = false; 158 goto finish; 159 }*/ 160 161 kern_result = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); 162 if (kern_result != KERN_SUCCESS) { 163 result = false; 164 goto finish; 165 } 166 167 gMainRunLoop = CFRunLoopGetCurrent(); 168 if (!gMainRunLoop) { 169 result = false; 170 goto finish; 171 } 172 173 174 kern_result = bootstrap_check_in(bootstrap_port, kIOUPSPlugInServerName, 175 &ups_port); 176 177 if (BOOTSTRAP_SUCCESS != kern_result) { 178 syslog(LOG_ERR, "ioupsd: bootstrap_check_in \"%s\" error = %d\n", 179 kIOUPSPlugInServerName, kern_result); 180 } else { 181 182 upsdMachPort = CFMachPortCreateWithPort(kCFAllocatorDefault, ups_port, 183 upsd_mach_port_callback, NULL, 184 NULL); 185 gClientRequestRunLoopSource = CFMachPortCreateRunLoopSource( 186 kCFAllocatorDefault, upsdMachPort, 0); 187 if (!gClientRequestRunLoopSource) { 188 result = false; 189 goto finish; 190 } 191 CFRunLoopAddSource(gMainRunLoop, gClientRequestRunLoopSource, 192 kCFRunLoopDefaultMode); 193 } 194finish: 195 if (gClientRequestRunLoopSource) CFRelease(gClientRequestRunLoopSource); 196 if (upsdMachPort) CFRelease(upsdMachPort); 197 198 return result; 199} 200 201//--------------------------------------------------------------------------- 202// InitUPSNotifications 203// 204// This routine just creates our master port for IOKit and turns around 205// and calls the routine that will alert us when a UPS Device is plugged in. 206//--------------------------------------------------------------------------- 207 208void InitUPSNotifications(int usagePage) { 209 // Create a notification port and add its run loop event source to our run 210 // loop. This is how async notifications get set up. 211 // 212 gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault); 213 CFRunLoopAddSource(CFRunLoopGetCurrent(), 214 IONotificationPortGetRunLoopSource(gNotifyPort), 215 kCFRunLoopDefaultMode); 216 217 218 CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOHIDDeviceKey); 219 if (!matchingDict) { 220 return; 221 } 222 223 // We need to box the Usage Page value up into a CFNumber... sorry bout that 224 CFNumberRef cfUsagePageKey = CFNumberCreate(kCFAllocatorDefault, 225 kCFNumberIntType, 226 &usagePage); 227 if (!cfUsagePageKey) { 228 CFRelease(matchingDict); 229 return; 230 } 231 232 233 CFDictionarySetValue(matchingDict, 234 CFSTR(kIOHIDPrimaryUsagePageKey), 235 cfUsagePageKey); 236 CFRelease(cfUsagePageKey); 237 238 239 // Now set up a notification to be called when a device is first matched by 240 // I/O Kit. Note that this will not catch any devices that were already 241 // plugged in so we take care of those later. 242 kern_return_t kr = 243 IOServiceAddMatchingNotification(gNotifyPort, 244 kIOFirstMatchNotification, 245 matchingDict, 246 UPSDeviceAdded, 247 NULL, 248 &gAddedIter); 249 250 matchingDict = 0; // reference consumed by AddMatchingNotification 251 if (kr == kIOReturnSuccess) { 252 // Check for existing matching devices 253 UPSDeviceAdded(NULL, gAddedIter); 254 } 255} 256 257//--------------------------------------------------------------------------- 258// ProcessUPSEventSource 259// 260// Performs cast on EventSource to determine if this is a timer or normal 261// event source. 262//--------------------------------------------------------------------------- 263void ProcessUPSEventSource(CFTypeRef typeRef, CFRunLoopTimerRef * pTimer, CFRunLoopSourceRef * pSource) 264{ 265 if ( CFGetTypeID(typeRef) == CFRunLoopTimerGetTypeID() ) 266 { 267 *pTimer = (CFRunLoopTimerRef)typeRef; 268 } 269 else if ( CFGetTypeID(typeRef) == CFRunLoopSourceGetTypeID() ) 270 { 271 *pSource = (CFRunLoopSourceRef)typeRef; 272 } 273} 274 275//--------------------------------------------------------------------------- 276// UPSDeviceAdded 277// 278// This routine is the callback for our IOServiceAddMatchingNotification. 279// When we get called we will look at all the devices that were added and 280// we will: 281// 282// Create some private data to relate to each device 283// 284// Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for 285// this device using the refCon field to store a pointer to our private data. 286// When we get called with this interest notification, we can grab the refCon 287// and access our private data. 288//--------------------------------------------------------------------------- 289 290void UPSDeviceAdded(void *refCon, io_iterator_t iterator) { 291 io_object_t upsDevice = MACH_PORT_NULL; 292 UPSDataRef upsDataRef = NULL; 293 CFDictionaryRef upsProperties = NULL; 294 CFDictionaryRef upsEvent = NULL; 295 CFSetRef upsCapabilites = NULL; 296 CFRunLoopSourceRef upsEventSource = NULL; 297 CFRunLoopTimerRef upsEventTimer = NULL; 298 CFTypeRef typeRef = NULL; 299 IOCFPlugInInterface ** plugInInterface = NULL; 300 IOUPSPlugInInterface_v140 ** upsPlugInInterface = NULL; 301 HRESULT result = S_FALSE; 302 IOReturn kr; 303 SInt32 score; 304 305 while ( (upsDevice = IOIteratorNext(iterator)) ) { 306 // Create the CF plugin for this device 307 kr = IOCreatePlugInInterfaceForService(upsDevice, kIOUPSPlugInTypeID, 308 kIOCFPlugInInterfaceID, 309 &plugInInterface, &score); 310 311 if (kr != kIOReturnSuccess) 312 goto UPSDEVICEADDED_NONPLUGIN_CLEANUP; 313 314 // Grab the new v140 interface 315 result = (*plugInInterface)->QueryInterface(plugInInterface, 316 CFUUIDGetUUIDBytes(kIOUPSPlugInInterfaceID_v140), 317 (LPVOID)&upsPlugInInterface); 318 319 if ( ( result == S_OK ) && upsPlugInInterface ) 320 { 321 kr = (*upsPlugInInterface)->createAsyncEventSource(upsPlugInInterface, &typeRef); 322 323 if ((kr != kIOReturnSuccess) || !typeRef) 324 goto UPSDEVICEADDED_FAIL; 325 326 if ( CFGetTypeID(typeRef) == CFArrayGetTypeID() ) 327 { 328 CFArrayRef arrayRef = (CFArrayRef)typeRef; 329 CFIndex index, count; 330 331 for (index=0, count=CFArrayGetCount(typeRef); index<count; index++) 332 ProcessUPSEventSource(CFArrayGetValueAtIndex(arrayRef, index), &upsEventTimer, &upsEventSource); 333 } 334 else 335 { 336 ProcessUPSEventSource(typeRef, &upsEventTimer, &upsEventSource); 337 } 338 339 if ( upsEventSource ) 340 CFRunLoopAddSource(CFRunLoopGetCurrent(), upsEventSource, kCFRunLoopDefaultMode); 341 342 if ( upsEventTimer ) 343 CFRunLoopAddTimer(CFRunLoopGetCurrent(), upsEventTimer, kCFRunLoopDefaultMode); 344 345 if ( typeRef ) 346 CFRelease(typeRef); 347 } 348 // Couldn't grab the new interface. Fallback on the old. 349 else 350 { 351 result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUPSPlugInInterfaceID), 352 (LPVOID)&upsPlugInInterface); 353 } 354 355 // Got the interface 356 if ( ( result == S_OK ) && upsPlugInInterface ) 357 { 358 kr = (*upsPlugInInterface)->getProperties(upsPlugInInterface, &upsProperties); 359 360 if (kr != kIOReturnSuccess) 361 goto UPSDEVICEADDED_FAIL; 362 363 upsDataRef = GetPrivateData(upsProperties); 364 365 if ( !upsDataRef ) 366 goto UPSDEVICEADDED_FAIL; 367 368 upsDataRef->upsPlugInInterface = (IOUPSPlugInInterface **)upsPlugInInterface; 369 upsDataRef->upsEventSource = upsEventSource; 370 upsDataRef->upsEventTimer = upsEventTimer; 371 upsDataRef->isPresent = true; 372 373 kr = (*upsPlugInInterface)->getCapabilities(upsPlugInInterface, &upsCapabilites); 374 375 if (kr != kIOReturnSuccess) 376 goto UPSDEVICEADDED_FAIL; 377 378 kr = CreatePowerManagerUPSEntry(upsDataRef, upsProperties, upsCapabilites); 379 380 if (kr != kIOReturnSuccess) 381 goto UPSDEVICEADDED_FAIL; 382 383 kr = (*upsPlugInInterface)->getEvent(upsPlugInInterface, &upsEvent); 384 385 if (kr != kIOReturnSuccess) 386 goto UPSDEVICEADDED_FAIL; 387 388 ProcessUPSEvent(upsDataRef, upsEvent); 389 390 (*upsPlugInInterface)->setEventCallback(upsPlugInInterface, 391 UPSEventCallback, NULL, 392 upsDataRef); 393 394 IOServiceAddInterestNotification( 395 gNotifyPort, // notifyPort 396 upsDevice, // service 397 kIOGeneralInterest, // interestType 398 DeviceNotification, // callback 399 upsDataRef, // refCon 400 &(upsDataRef->notification)); // notification 401 notify_post(kIOPSNotifyAttach); 402 goto UPSDEVICEADDED_CLEANUP; 403 } 404 405 406UPSDEVICEADDED_FAIL: 407 // Failed to allocate a UPS interface. Do some cleanup 408 if (upsPlugInInterface) { 409 (*upsPlugInInterface)->Release(upsPlugInInterface); 410 upsPlugInInterface = NULL; 411 } 412 413 if (upsEventSource) { 414 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), upsEventSource, 415 kCFRunLoopDefaultMode); 416 upsEventSource = NULL; 417 } 418 419 if (upsEventTimer) { 420 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), upsEventTimer, 421 kCFRunLoopDefaultMode); 422 upsEventSource = NULL; 423 } 424 425UPSDEVICEADDED_CLEANUP: 426 // Clean up 427 (*plugInInterface)->Release(plugInInterface); 428 429UPSDEVICEADDED_NONPLUGIN_CLEANUP: 430 IOObjectRelease(upsDevice); 431 } 432} 433 434//--------------------------------------------------------------------------- 435// DeviceNotification 436// 437// This routine will get called whenever any kIOGeneralInterest notification 438// happens. 439//--------------------------------------------------------------------------- 440 441void DeviceNotification(void *refCon, io_service_t service, 442 natural_t messageType, void *messageArgument ) { 443 UPSDataRef upsDataRef = (UPSDataRef) refCon; 444 445 if ((upsDataRef != NULL) && 446 (messageType == kIOMessageServiceIsTerminated)) { 447 upsDataRef->isPresent = FALSE; 448 449 notify_post(kIOPSNotifyAttach); 450 451 if (upsDataRef->upsEventSource) { 452 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), 453 upsDataRef->upsEventSource, 454 kCFRunLoopDefaultMode); 455 CFRelease(upsDataRef->upsEventSource); 456 upsDataRef->upsEventSource = NULL; 457 } 458 459 if (upsDataRef->upsEventTimer) { 460 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), 461 upsDataRef->upsEventTimer, 462 kCFRunLoopDefaultMode); 463 CFRelease(upsDataRef->upsEventTimer); 464 upsDataRef->upsEventTimer = NULL; 465 } 466 467 if (upsDataRef->upsPlugInInterface != NULL) { 468 (*(upsDataRef->upsPlugInInterface))->Release(upsDataRef->upsPlugInInterface); 469 upsDataRef->upsPlugInInterface = NULL; 470 } 471 472 if (upsDataRef->notification != MACH_PORT_NULL) { 473 IOObjectRelease(upsDataRef->notification); 474 upsDataRef->notification = MACH_PORT_NULL; 475 } 476 477 if (upsDataRef->upsStoreDict) { 478 CFRelease(upsDataRef->upsStoreDict); 479 upsDataRef->upsStoreDict = NULL; 480 } 481 482 if (upsDataRef->powerSourceID) { 483 IOReturn result = IOPSReleasePowerSource(upsDataRef->powerSourceID); 484 gUPSCount--; 485 if (result != kIOReturnSuccess) { 486 syslog(LOG_ERR, "IOPSReleasePowerSource failed (IOReturn: %d\n", 487 result); 488 } 489 upsDataRef->powerSourceID = NULL; 490 } 491 492 if (gUPSCount == 0) { 493 CFRelease(gUPSDataArrayRef); 494 CleanupAndExit(); 495 } 496 } 497} 498 499//--------------------------------------------------------------------------- 500// UPSEventCallback 501// 502// This routine will get called whenever any data is available from the UPS 503//--------------------------------------------------------------------------- 504void UPSEventCallback(void *target, IOReturn result, void *refcon, void *sender, 505 CFDictionaryRef event) { 506 ProcessUPSEvent((UPSDataRef) refcon, event); 507} 508 509//--------------------------------------------------------------------------- 510// ProcessUPSEvent 511// 512//--------------------------------------------------------------------------- 513void ProcessUPSEvent(UPSDataRef upsDataRef, CFDictionaryRef event) { 514 UInt32 count, index; 515 516 if (!upsDataRef || !event) 517 return; 518 519 if ((count = CFDictionaryGetCount(event))) { 520 CFTypeRef *keys = (CFTypeRef *) malloc(sizeof(CFTypeRef) * count); 521 CFTypeRef *values = (CFTypeRef *) malloc(sizeof(CFTypeRef) * count); 522 523 CFDictionaryGetKeysAndValues(event, (const void **)keys, 524 (const void **)values); 525 526 for (index = 0; index < count; index++) { 527 CFDictionarySetValue(upsDataRef->upsStoreDict, keys[index], 528 values[index]); 529 } 530 531 free (keys); 532 free (values); 533 534 IOReturn result = IOPSSetPowerSourceDetails(upsDataRef->powerSourceID, 535 upsDataRef->upsStoreDict); 536 if (result != kIOReturnSuccess) { 537 // TODO: do I need to deal with this? 538 } 539 notify_post(kIOPSNotifyTimeRemaining); 540 } 541} 542 543 544//--------------------------------------------------------------------------- 545// GetPrivateData 546// 547// Now that UPS entries remain in the System Configuration store, we also 548// preserve the UPSDeviceData struct that is associated with it. Before 549// getting a null entry from the gUPSDataRef that means we will have to 550// create a new UPSDeviceData struct, we check the existing ones to see if 551// there is a matching one that we can just reactivate. If we can't find an 552// existing UPSDeviceData struct, we will create the storage that is 553// necessary to keep track of the UPS. We also update the global array of 554// UPSDeviceData and fill in that data ref with the values that we want to 555// track from the UPS 556//--------------------------------------------------------------------------- 557 558UPSDataRef GetPrivateData(CFDictionaryRef properties) { 559 UPSDataRef upsDataRef = NULL; 560 CFMutableDataRef data = NULL; 561 UInt32 i = 0; 562 UInt32 count = 0; 563 564 565 // Allocated the global array if necessary 566 if (!gUPSDataArrayRef && 567 !(gUPSDataArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, 568 &kCFTypeArrayCallBacks))) { 569 return NULL; 570 } 571 572 // Find an empty location in our array 573 count = CFArrayGetCount(gUPSDataArrayRef); 574 for (i = 0; i < count; i++) { 575 data = (CFMutableDataRef)CFArrayGetValueAtIndex(gUPSDataArrayRef, i); 576 if (!data) 577 continue; 578 579 upsDataRef = (UPSDataRef)CFDataGetMutableBytePtr(data); 580 581 if (upsDataRef && !(upsDataRef->isPresent)) 582 break; 583 584 upsDataRef = NULL; 585 } 586 587 // No valid upsDataRef was found, so let's go ahead and allocate one 588 if ((upsDataRef == NULL) && 589 (data = CFDataCreateMutable(kCFAllocatorDefault, 590 sizeof(UPSData)))) { 591 upsDataRef = (UPSDataRef)CFDataGetMutableBytePtr(data); 592 bzero(upsDataRef, sizeof(UPSData)); 593 594 CFArrayAppendValue(gUPSDataArrayRef, data); 595 CFRelease(data); 596 } 597 598 // If we have a pointer to our global, then fill in some of the field in that structure 599 // 600 if (upsDataRef != NULL) { 601 upsDataRef->upsID = i; 602 } 603 604 return upsDataRef; 605} 606 607 608//--------------------------------------------------------------------------- 609// CreatePowerManagerUPSEntry 610// 611//--------------------------------------------------------------------------- 612#define kInternalUPSLabelLength 20 613 614IOReturn CreatePowerManagerUPSEntry(UPSDataRef upsDataRef, 615 CFDictionaryRef properties, 616 CFSetRef capabilities) { 617 CFMutableDictionaryRef upsStoreDict = NULL; 618 CFStringRef upsName = NULL; 619 CFStringRef transport = NULL; 620 CFNumberRef number = NULL; 621 622 623 IOReturn result = kIOReturnSuccess; 624 int elementValue = 0; 625 char upsLabelString[kInternalUPSLabelLength]; 626 627 if (!upsDataRef || !properties || !capabilities) 628 return kIOReturnError; 629 630 upsStoreDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 631 &kCFTypeDictionaryKeyCallBacks, 632 &kCFTypeDictionaryValueCallBacks); 633 634 // Set some Store values 635 if (upsStoreDict) { 636 // We need to save a name for this device. First, try to see if we have 637 // a USB Product Name. If that fails then use the manufacturer and if 638 // that fails, then use a generic name. Couldn't we use a serial # here? 639 // 640 upsName = (CFStringRef) CFDictionaryGetValue(properties, 641 CFSTR(kIOPSNameKey)); 642 if (!upsName) 643 upsName = CFSTR(kDefaultUPSName); 644 645 transport = (CFStringRef) CFDictionaryGetValue(properties, 646 CFSTR(kIOPSTransportTypeKey)); 647 648 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSNameKey), upsName); 649 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSTransportTypeKey), transport); 650 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSIsPresentKey), kCFBooleanTrue); 651 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSIsChargingKey), kCFBooleanTrue); 652 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSPowerSourceStateKey), CFSTR(kIOPSACPowerValue)); 653 654 number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, 655 &upsDataRef->upsID); 656 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSPowerSourceIDKey), number); 657 CFRelease(number); 658 659 elementValue = 100; 660 number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, 661 &elementValue); 662 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSMaxCapacityKey), number); 663 CFRelease(number); 664 665 if (CFSetContainsValue(capabilities, CFSTR(kIOPSCurrentCapacityKey))) { 666 // Initialize kIOPSCurrentCapacityKey 667 // 668 // For Power Manager, we will be sharing capacity with Power Book 669 // battery capacities, so we want a consistent measure. For now we 670 // have settled on percentage of full capacity. 671 // 672 elementValue = 100; 673 number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, 674 &elementValue); 675 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSCurrentCapacityKey), 676 number); 677 CFRelease(number); 678 } 679 680 if (CFSetContainsValue(capabilities, CFSTR(kIOPSTimeToEmptyKey))) { 681 // Initialize kIOPSTimeToEmptyKey 682 // (OS 9 PowerClass.c assumed 100 milliwatt-hours) 683 // 684 elementValue = 100; 685 number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, 686 &elementValue); 687 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSTimeToEmptyKey), 688 number); 689 CFRelease(number); 690 } 691 692 if (CFSetContainsValue(capabilities, CFSTR(kIOPSVoltageKey))) { 693 // Initialize kIOPSVoltageKey (OS 9 PowerClass.c assumed millivolts. 694 // (Shouldn't that be 130,000 millivolts for AC?)) 695 // Actually, Power Devices Usage Tables say units will be in Volts. 696 // However we have to check what exponent is used because that may 697 // make the value we get in centiVolts (exp = -2). So it looks like 698 // OS 9 sources said millivolts, but used centivolts. Our final 699 // answer should device by proper exponent to get back to Volts. 700 // 701 elementValue = 13 * 1000 / 100; 702 number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, 703 &elementValue); 704 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSVoltageKey), number); 705 CFRelease(number); 706 } 707 708 if (CFSetContainsValue(capabilities, CFSTR(kIOPSCurrentKey))) { 709 // Initialize kIOPSCurrentKey (What would be a good amperage to 710 // initialize to?) Same discussion as for Volts, where the unit 711 // for current is Amps. But with typical exponents (-2), we get 712 // centiAmps. Hmm... typical current for USB may be 500 milliAmps, 713 // which would be .5 A. Since that is not an integer, that may be 714 // why our displays get larger numbers 715 // 716 elementValue = 1; // Just a guess! 717 number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, 718 &elementValue); 719 CFDictionarySetValue(upsStoreDict, CFSTR(kIOPSCurrentKey), number); 720 CFRelease(number); 721 } 722 } 723 724 // Uniquely name each Sys Config key 725 // 726 snprintf(upsLabelString, kInternalUPSLabelLength, "/UPS%d", 727 upsDataRef->upsID); 728 729 result = IOPSCreatePowerSource(&(upsDataRef->powerSourceID)); 730 gUPSCount++; 731 732 // TODO: possible trouble spot. 733 // Shouldn't need to check/release since it only exists if we succeed. 734 if (result != kIOReturnSuccess) { 735 upsDataRef->powerSourceID = NULL; 736 return result; 737 } 738 739 result = IOPSSetPowerSourceDetails(upsDataRef->powerSourceID, upsStoreDict); 740 741 if (result == kIOReturnSuccess) { 742 // Store our SystemConfiguration variables in our private data 743 // 744 upsDataRef->upsStoreDict = upsStoreDict; 745 746 } else if (upsStoreDict) { 747 CFRelease(upsStoreDict); 748 } 749 750 return result; 751} 752 753 754//=========================================================================== 755// MIG Routines 756//=========================================================================== 757 758//--------------------------------------------------------------------------- 759// _io_ups_send_command 760// 761// This routine allow remote processes to issue commands to the UPS. It is 762// expected that command will come in as a serialized CFDictionaryRef. 763//--------------------------------------------------------------------------- 764kern_return_t _io_ups_send_command(mach_port_t server, int upsID, 765 void *commandBuffer, IOByteCount commandSize) { 766 CFDictionaryRef command; 767 CFMutableDataRef data; 768 UPSDataRef upsDataRef; 769 IOReturn res = kIOReturnError; 770 771 command = (CFDictionaryRef)IOCFUnserialize(commandBuffer, 772 kCFAllocatorDefault, 773 kNilOptions, NULL); 774 if (command) { 775 if (!gUPSDataArrayRef || (upsID >= CFArrayGetCount(gUPSDataArrayRef))) { 776 res = kIOReturnBadArgument; 777 } else { 778 data = (CFMutableDataRef)CFArrayGetValueAtIndex(gUPSDataArrayRef, 779 upsID); 780 upsDataRef =(UPSDataRef)CFDataGetMutableBytePtr(data); 781 782 if (upsDataRef && upsDataRef->upsPlugInInterface) 783 res = (*upsDataRef->upsPlugInInterface)->sendCommand(upsDataRef->upsPlugInInterface, command); 784 } 785 CFRelease(command); 786 } 787 788 return res; 789} 790 791//--------------------------------------------------------------------------- 792// _io_ups_get_event 793// 794// This routine allow remote processes to issue commands to the UPS. It will 795// return a CFDictionaryRef that is serialized. 796//--------------------------------------------------------------------------- 797kern_return_t _io_ups_get_event( mach_port_t server, int upsID, 798 void **eventBufferPtr, 799 IOByteCount *eventBufferSizePtr) { 800 CFDictionaryRef event; 801 CFMutableDataRef data; 802 CFDataRef serializedData; 803 UPSDataRef upsDataRef; 804 IOReturn res = kIOReturnError; 805 806 if (!eventBufferPtr || !eventBufferSizePtr || 807 !gUPSDataArrayRef || (upsID >= CFArrayGetCount(gUPSDataArrayRef))) { 808 809 return kIOReturnBadArgument; 810 } 811 812 data = (CFMutableDataRef)CFArrayGetValueAtIndex(gUPSDataArrayRef, upsID); 813 upsDataRef = (UPSDataRef)CFDataGetMutableBytePtr(data); 814 815 if (!upsDataRef || !upsDataRef->upsPlugInInterface) 816 return kIOReturnBadArgument; 817 818 res = (*upsDataRef->upsPlugInInterface)->getEvent(upsDataRef->upsPlugInInterface, &event); 819 820 if ((res != kIOReturnSuccess) || !event) 821 return kIOReturnError; 822 823 824 serializedData = (CFDataRef)IOCFSerialize(event, kNilOptions); 825 826 if (!serializedData) 827 return kIOReturnError; 828 829 *eventBufferSizePtr = CFDataGetLength(serializedData); 830 831 vm_allocate(mach_task_self(), (vm_address_t *)eventBufferPtr, 832 *eventBufferSizePtr, TRUE); 833 834 if(*eventBufferPtr) 835 memcpy(*eventBufferPtr, CFDataGetBytePtr(serializedData), 836 *eventBufferSizePtr); 837 838 CFRelease(serializedData); 839 840 return res; 841} 842 843//--------------------------------------------------------------------------- 844// _io_ups_get_capabilities 845// 846// This routine allow remote processes to issue commands to the UPS. It will 847// return a CFSetRef that is serialized. 848//--------------------------------------------------------------------------- 849kern_return_t _io_ups_get_capabilities(mach_port_t server, int upsID, 850 void **capabilitiesBufferPtr, 851 IOByteCount *capabilitiesBufferSizePtr) { 852 CFSetRef capabilities; 853 CFMutableDataRef data; 854 CFDataRef serializedData; 855 UPSDataRef upsDataRef; 856 IOReturn res = kIOReturnError; 857 858 if (!capabilitiesBufferPtr || !capabilitiesBufferSizePtr || 859 !gUPSDataArrayRef || (upsID >= CFArrayGetCount(gUPSDataArrayRef))) { 860 861 return kIOReturnBadArgument; 862 } 863 864 data = (CFMutableDataRef)CFArrayGetValueAtIndex(gUPSDataArrayRef, upsID); 865 upsDataRef = (UPSDataRef)CFDataGetMutableBytePtr(data); 866 867 if (!upsDataRef || !upsDataRef->upsPlugInInterface) 868 return kIOReturnBadArgument; 869 870 res = (*upsDataRef->upsPlugInInterface)->getCapabilities(upsDataRef->upsPlugInInterface, 871 &capabilities); 872 873 if ((res != kIOReturnSuccess) || !capabilities) 874 return kIOReturnError; 875 876 877 serializedData = (CFDataRef)IOCFSerialize(capabilities, kNilOptions); 878 879 if (!serializedData) 880 return kIOReturnError; 881 882 *capabilitiesBufferSizePtr = CFDataGetLength(serializedData); 883 884 vm_allocate(mach_task_self(), (vm_address_t *)capabilitiesBufferPtr, 885 *capabilitiesBufferSizePtr, TRUE); 886 887 if (*capabilitiesBufferPtr) 888 memcpy(*capabilitiesBufferPtr, CFDataGetBytePtr(serializedData), 889 *capabilitiesBufferSizePtr); 890 891 CFRelease(serializedData); 892 893 return res; 894}