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}