From 6b2aa4a4a7816d604ee63c974131c03fed9a4b7c Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 28 Jan 2010 00:37:54 +0000 Subject: first working version of hidio+HIDUtilities build on Mac OS X using autotools, still needs work svn path=/trunk/externals/io/hidio/; revision=13107 --- HID_Utilities_Source/HID_Utilities.c | 2198 ++++++++++++++++++++++++++++++++++ 1 file changed, 2198 insertions(+) create mode 100644 HID_Utilities_Source/HID_Utilities.c (limited to 'HID_Utilities_Source/HID_Utilities.c') diff --git a/HID_Utilities_Source/HID_Utilities.c b/HID_Utilities_Source/HID_Utilities.c new file mode 100644 index 0000000..b7b22c5 --- /dev/null +++ b/HID_Utilities_Source/HID_Utilities.c @@ -0,0 +1,2198 @@ +/* + File: HID_Utilities.c + + Contains: Implementation of HID Utilities + + DRI: George Warner + + Copyright: Copyright © 2002 Apple Computer, Inc., All Rights Reserved + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleŐs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "HID_Utilities_Internal.h" +#include "HID_Utilities_External.h" + +#include +#include +#include +#include + +//================================================================================================ +#define USE_NOTIFICATIONS 1 +//================================================================================================ +// local (static) functions +//================================================================================================ +static void hid_GetCollectionElements (CFMutableDictionaryRef deviceProperties, pRecElement *ppCurrentCollection); +static pRecDevice hid_DisposeDevice (pRecDevice ppDevice); +//================================================================================================ +// globals +//================================================================================================ +#if USE_NOTIFICATIONS +static IONotificationPortRef gNotifyPort; +static io_iterator_t gAddedIter; +static CFRunLoopRef gRunLoop; +#endif USE_NOTIFICATIONS + +// for element retrieval +static pRecDevice gCurrentGetDevice = NULL; +static Boolean gAddAsChild = false; +static int gDepth = false; + +static pRecDevice gpDeviceList = NULL; +static UInt32 gNumDevices = 0; + +#pragma mark private functions +// ================================== +// private functions + +static UInt32 hid_CountCurrentDevices (void); + +// extracts actual specific element information from each element CF dictionary entry +static void hid_GetElementInfo (CFTypeRef refElement, pRecElement pElement) +{ + long number; + CFTypeRef refType; + // type, usagePage, usage already stored + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementCookieKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->cookie = (IOHIDElementCookie) number; + else + pElement->cookie = (IOHIDElementCookie) 0; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMinKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->min = number; + else + pElement->min = 0; + +// pElement->calMax = pElement->min; + pElement->userMin = kDefaultUserMin; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementMaxKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->max = number; + else + pElement->max = 0; + +// pElement->calMin = pElement->max; + pElement->userMax = kDefaultUserMax; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMinKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->scaledMin = number; + else + pElement->scaledMin = 0; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementScaledMaxKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->scaledMax = number; + else + pElement->scaledMax = 0; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementSizeKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->size = number; + else + pElement->size = 0; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsRelativeKey)); + if (refType) + pElement->relative = CFBooleanGetValue (refType); + else + pElement->relative = 0; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsWrappingKey)); + if (refType) + pElement->wrapping = CFBooleanGetValue (refType); + else + pElement->wrapping = false; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementIsNonLinearKey)); + if (refType) + pElement->nonLinear = CFBooleanGetValue (refType); + else + pElement->wrapping = false; + +#ifdef kIOHIDElementHasPreferredStateKey + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferredStateKey)); +#else // Mac OS X 10.0 has spelling error + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasPreferedStateKey)); +#endif + if (refType) + pElement->preferredState = CFBooleanGetValue (refType); + else + pElement->preferredState = false; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementHasNullStateKey)); + if (refType) + pElement->nullState = CFBooleanGetValue (refType); + else + pElement->nullState = false; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUnitKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->units = number; + else + pElement->units = 0; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUnitExponentKey)); + if (refType && CFNumberGetValue (refType, kCFNumberLongType, &number)) + pElement->unitExp = number; + else + pElement->unitExp = 0; + + refType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementNameKey)); + if (refType) + if (!CFStringGetCString (refType, pElement->name, 256, CFStringGetSystemEncoding ())) + HIDReportError ("CFStringGetCString error retrieving pElement->name."); + if (!*pElement->name) + { + // set name from vendor id, product id & usage info look up + if (!HIDGetElementNameFromVendorProductUsage (gCurrentGetDevice->vendorID, gCurrentGetDevice->productID, pElement->usagePage, pElement->usage, pElement->name)) + { + // set name from vendor id/product id look up + HIDGetElementNameFromVendorProductCookie (gCurrentGetDevice->vendorID, gCurrentGetDevice->productID, (long) pElement->cookie, pElement->name); + if (!*pElement->name) { // if no name + HIDGetUsageName (pElement->usagePage, pElement->usage, pElement->name); + if (!*pElement->name) // if not usage + sprintf (pElement->name, "Element"); + } + } + } +} + +// --------------------------------- +// examines CF dictionary vlaue in device element hierarchy to determine if it is element of interest or a collection of more elements +// if element of interest allocate storage, add to list and retrieve element specific info +// if collection then pass on to deconstruction collection into additional individual elements + +static void hid_AddElement (CFTypeRef refElement, pRecElement * ppElementCurrent) +{ + pRecDevice pDevice = gCurrentGetDevice; + pRecElement pElement = NULL; + long elementType, usagePage, usage; + CFTypeRef refElementType = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementTypeKey)); + CFTypeRef refUsagePage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsagePageKey)); + CFTypeRef refUsage = CFDictionaryGetValue (refElement, CFSTR(kIOHIDElementUsageKey)); + + if (refElementType) + CFNumberGetValue (refElementType, kCFNumberLongType, &elementType); + if (refUsagePage) + CFNumberGetValue (refUsagePage, kCFNumberLongType, &usagePage); + if (refUsage) + CFNumberGetValue (refUsage, kCFNumberLongType, &usage); + + if (NULL == pDevice) + return; + + if (elementType) + { + // look at types of interest + if (elementType != kIOHIDElementTypeCollection) + { + if (usagePage && usage) // if valid usage and page + { + switch (usagePage) // only interested in kHIDPage_GenericDesktop and kHIDPage_Button + { + case kHIDPage_GenericDesktop: + { + switch (usage) // look at usage to determine function + { + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: + pElement = (pRecElement) malloc (sizeof (recElement)); + if (pElement) pDevice->axis++; + break; + case kHIDUsage_GD_Slider: + pElement = (pRecElement) malloc (sizeof (recElement)); + if (pElement) pDevice->sliders++; + break; + case kHIDUsage_GD_Dial: + pElement = (pRecElement) malloc (sizeof (recElement)); + if (pElement) pDevice->dials++; + break; + case kHIDUsage_GD_Wheel: + pElement = (pRecElement) malloc (sizeof (recElement)); + if (pElement) pDevice->wheels++; + break; + case kHIDUsage_GD_Hatswitch: + pElement = (pRecElement) malloc (sizeof (recElement)); + if (pElement) pDevice->hats++; + break; + default: + pElement = (pRecElement) malloc (sizeof (recElement)); + break; + } + } + break; + case kHIDPage_Button: + pElement = (pRecElement) malloc (sizeof (recElement)); + if (pElement) pDevice->buttons++; + break; + default: + // just add a generic element + pElement = (pRecElement) malloc (sizeof (recElement)); + break; + } + } +#if 0 + else + HIDReportError ("CFNumberGetValue error when getting value for refUsage or refUsagePage."); +#endif 0 + } + else // collection + pElement = (pRecElement) malloc (sizeof (recElement)); + } + else + HIDReportError ("CFNumberGetValue error when getting value for refElementType."); + + if (pElement) // add to list + { + // this code builds a binary tree based on the collection hierarchy of inherent in the device element layout + // it preserves the structure of the lements as collections have children and elements are siblings to each other + + // clear record + bzero(pElement,sizeof(recElement)); + + // get element info + pElement->type = elementType; + pElement->usagePage = usagePage; + pElement->usage = usage; + pElement->depth = 0; // assume root object + hid_GetElementInfo (refElement, pElement); + + // count elements + pDevice->totalElements++; + + switch (pElement->type) + { + case kIOHIDElementTypeInput_Misc: + case kIOHIDElementTypeInput_Button: + case kIOHIDElementTypeInput_Axis: + case kIOHIDElementTypeInput_ScanCodes: + pDevice->inputs++; + break; + case kIOHIDElementTypeOutput: + pDevice->outputs++; + break; + case kIOHIDElementTypeFeature: + pDevice->features++; + break; + case kIOHIDElementTypeCollection: + pDevice->collections++; + break; + default: + HIDReportErrorNum ("Unknown element type : ", pElement->type); + } + + if (NULL == *ppElementCurrent) // if at list head + { + pDevice->pListElements = pElement; // add current element + *ppElementCurrent = pElement; // set current element to element we just added + } + else // have exsiting structure + { + if (gAddAsChild) // if the previous element was a collection, let's add this as a child of the previous + { + // this iteration should not be needed but there maybe some untested degenerate case which this code will ensure works + while ((*ppElementCurrent)->pChild) // step down tree until free child node found + *ppElementCurrent = (*ppElementCurrent)->pChild; + (*ppElementCurrent)->pChild = pElement; // insert there + pElement->depth = (*ppElementCurrent)->depth + 1; + } + else // add as sibling + { + // this iteration should not be needed but there maybe some untested degenerate case which this code will ensure works + while ((*ppElementCurrent)->pSibling) // step down tree until free sibling node found + *ppElementCurrent = (*ppElementCurrent)->pSibling; + (*ppElementCurrent)->pSibling = pElement; // insert there + pElement->depth = (*ppElementCurrent)->depth; + } + pElement->pPrevious = *ppElementCurrent; // point to previous + *ppElementCurrent = pElement; // set current to our collection + } + + if (elementType == kIOHIDElementTypeCollection) // if this element is a collection of other elements + { + gAddAsChild = true; // add next set as children to this element + gDepth++; + hid_GetCollectionElements ((CFMutableDictionaryRef) refElement, &pElement); // recursively process the collection + gDepth--; + } + gAddAsChild = false; // add next as this elements sibling (when return from a collection or with non-collections) + } +#if 0 + else + HIDReportError ("hid_AddElement - no element added."); +#endif +} + +// --------------------------------- +// collects information from each array member in device element list (each array memeber = element) + +static void hid_GetElementsCFArrayHandler (const void * value, void * parameter) +{ + if (CFGetTypeID (value) == CFDictionaryGetTypeID ()) + hid_AddElement ((CFTypeRef) value, (pRecElement *) parameter); +} + +// --------------------------------- +// handles retrieval of element information from arrays of elements in device IO registry information + +static void hid_GetElements (CFTypeRef refElementCurrent, pRecElement *ppCurrentElement) +{ + CFTypeID type = CFGetTypeID (refElementCurrent); + if (type == CFArrayGetTypeID()) // if element is an array + { + CFRange range = {0, CFArrayGetCount (refElementCurrent)}; + // CountElementsCFArrayHandler called for each array member + CFArrayApplyFunction (refElementCurrent, range, hid_GetElementsCFArrayHandler, ppCurrentElement); + } +} + +// --------------------------------- +// handles extracting element information from element collection CF types +// used from top level element decoding and hierarchy deconstruction to flatten device element list + +static void hid_GetCollectionElements (CFMutableDictionaryRef deviceProperties, pRecElement *ppCurrentCollection) +{ + CFTypeRef refElementTop = CFDictionaryGetValue (deviceProperties, CFSTR(kIOHIDElementKey)); + if (refElementTop) + hid_GetElements (refElementTop, ppCurrentCollection); + else + HIDReportError ("hid_GetCollectionElements: CFDictionaryGetValue error when creating CFTypeRef for kIOHIDElementKey."); +} + +// --------------------------------- +// use top level element usage page and usage to discern device usage page and usage setting appropriate values in device record + +static void hid_TopLevelElementHandler (const void * value, void * parameter) +{ + CFTypeRef refCF = 0; + if ((NULL == value) || (NULL == parameter)) + return; // (kIOReturnBadArgument) + if (CFGetTypeID (value) != CFDictionaryGetTypeID ()) + return; // (kIOReturnBadArgument) + refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsagePageKey)); + if (!CFNumberGetValue (refCF, kCFNumberLongType, &((pRecDevice) parameter)->usagePage)) + HIDReportError ("CFNumberGetValue error retrieving pDevice->usagePage."); + refCF = CFDictionaryGetValue (value, CFSTR(kIOHIDElementUsageKey)); + if (!CFNumberGetValue (refCF, kCFNumberLongType, &((pRecDevice) parameter)->usage)) + HIDReportError ("CFNumberGetValue error retrieving pDevice->usage."); +} + +// --------------------------------- +// extracts device info from CF dictionary records in IO registry + +static void hid_GetDeviceInfo (io_object_t hidDevice, CFMutableDictionaryRef hidProperties, pRecDevice pDevice) +{ + CFMutableDictionaryRef usbProperties = 0; + io_registry_entry_t parent1, parent2; + + // Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also + // get dictionary for usb properties: step up two levels and get CF dictionary for USB properties + if ((KERN_SUCCESS == IORegistryEntryGetParentEntry (hidDevice, kIOServicePlane, &parent1)) && + (KERN_SUCCESS == IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2)) && + (KERN_SUCCESS == IORegistryEntryCreateCFProperties (parent2, &usbProperties, kCFAllocatorDefault, kNilOptions))) + { + if (usbProperties) + { + CFTypeRef refCF = 0; + // get device info + // try hid dictionary first, if fail then go to usb dictionary + + // get transport + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDTransportKey)); + if (refCF) + { + if (!CFStringGetCString (refCF, pDevice->transport, 256, CFStringGetSystemEncoding ())) + HIDReportError ("CFStringGetCString error retrieving pDevice->transport."); + } + + // get vendorID + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDVendorIDKey)); + if (!refCF) + refCF = CFDictionaryGetValue (usbProperties, CFSTR("idVendor")); + if (refCF) + { + if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->vendorID)) + HIDReportError ("CFNumberGetValue error retrieving pDevice->vendorID."); + } + + // get product ID + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductIDKey)); + if (!refCF) + refCF = CFDictionaryGetValue (usbProperties, CFSTR("idProduct")); + if (refCF) + { + if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->productID)) + HIDReportError ("CFNumberGetValue error retrieving pDevice->productID."); + } + + // get product version + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDVersionNumberKey)); + if (refCF) + { + if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->version)) + HIDReportError ("CFNumberGetValue error retrieving pDevice->version."); + } + + // get manufacturer name + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDManufacturerKey)); + if (!refCF) + refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Vendor Name")); + if (refCF) + { + if (!CFStringGetCString (refCF, pDevice->manufacturer, 256, CFStringGetSystemEncoding ())) + HIDReportError ("CFStringGetCString error retrieving pDevice->manufacturer."); + } + + // get product name + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductKey)); + if (!refCF) + refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Product Name")); + if (refCF) + { + if (!CFStringGetCString (refCF, pDevice->product, 256, CFStringGetSystemEncoding ())) + HIDReportError ("CFStringGetCString error retrieving pDevice->product."); + } + + // get serial + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDSerialNumberKey)); + if (refCF) + { + if (!CFStringGetCString (refCF, pDevice->serial, 256, CFStringGetSystemEncoding ())) + HIDReportError ("CFStringGetCString error retrieving pDevice->serial."); + } + + // get location ID + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDLocationIDKey)); + if (!refCF) + refCF = CFDictionaryGetValue (usbProperties, CFSTR("locationID")); + if (refCF) + { + if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->locID)) + HIDReportError ("CFNumberGetValue error retrieving pDevice->locID."); + } + + // get usage page and usage + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey)); + if (refCF) + { + if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usagePage)) + HIDReportError ("CFNumberGetValue error retrieving pDevice->usagePage."); + refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsageKey)); + if (refCF) + if (!CFNumberGetValue (refCF, kCFNumberLongType, &pDevice->usage)) + HIDReportError ("CFNumberGetValue error retrieving pDevice->usage."); + } + if (NULL == refCF) // get top level element HID usage page or usage + { + // use top level element instead + CFTypeRef refCFTopElement = 0; + refCFTopElement = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDElementKey)); + { + // refCFTopElement points to an array of element dictionaries + CFRange range = {0, CFArrayGetCount (refCFTopElement)}; + CFArrayApplyFunction (refCFTopElement, range, hid_TopLevelElementHandler, NULL); + } + } + } + else + HIDReportError ("IORegistryEntryCreateCFProperties failed to create usbProperties."); + + CFRelease (usbProperties); + if (kIOReturnSuccess != IOObjectRelease (parent2)) + HIDReportError ("IOObjectRelease error with parent2."); + if (kIOReturnSuccess != IOObjectRelease (parent1)) + HIDReportError ("IOObjectRelease error with parent1."); + } +} + +// --------------------------------- +// adds device to linked list of devices passed in (handles NULL lists properly) +// (returns where you just stored it) +static pRecDevice* hid_AddDevice (pRecDevice *ppListDeviceHead, pRecDevice pNewDevice) +{ + pRecDevice* result = NULL; + + if (NULL == *ppListDeviceHead) + result = ppListDeviceHead; + else + { + pRecDevice pDevicePrevious = NULL, pDevice = *ppListDeviceHead; + while (pDevice) + { + pDevicePrevious = pDevice; + pDevice = pDevicePrevious->pNext; + } + result = &pDevicePrevious->pNext; + } + pNewDevice->pNext = NULL; + + *result = pNewDevice; + + return result; +} + +// --------------------------------- +// given a IO device object build a flat device record including device info and elements + +static pRecDevice hid_BuildDevice (io_object_t hidDevice) +{ + pRecDevice pDevice = (pRecDevice) malloc (sizeof (recDevice)); + + if (NULL != pDevice) + { + // get dictionary for HID properties + CFMutableDictionaryRef hidProperties = 0; + kern_return_t result = IORegistryEntryCreateCFProperties (hidDevice, &hidProperties, kCFAllocatorDefault, kNilOptions); + + // clear record + bzero(pDevice, sizeof(recDevice)); + + if ((result == KERN_SUCCESS) && (NULL != hidProperties)) + { + pRecElement pCurrentElement = NULL; + // create device interface + result = HIDCreateOpenDeviceInterface (hidDevice, pDevice); + if (kIOReturnSuccess != result) + HIDReportErrorNum ("HIDCreateOpenDeviceInterface failed.", result); + hid_GetDeviceInfo (hidDevice, hidProperties, pDevice); // hidDevice used to find parents in registry tree + // set current device for use in getting elements + gCurrentGetDevice = pDevice; + // Add all elements + hid_GetCollectionElements (hidProperties, &pCurrentElement); + gCurrentGetDevice = NULL; + CFRelease (hidProperties); + } + else + HIDReportErrorNum ("IORegistryEntryCreateCFProperties error when creating deviceProperties.", result); + } + else + HIDReportError ("malloc error when allocating pRecDevice."); + return pDevice; +} + +#if USE_NOTIFICATIONS +//================================================================================================ +// +// hid_DeviceNotification +// +// This routine will get called whenever any kIOGeneralInterest notification happens. We are +// interested in the kIOMessageServiceIsTerminated message so that's what we look for. Other +// messages are defined in IOMessage.h. +// +//================================================================================================ +// +static void hid_DeviceNotification( void *refCon, + io_service_t service, + natural_t messageType, + void *messageArgument ) +{ + pRecDevice pDevice = (pRecDevice) refCon; + + if (messageType == kIOMessageServiceIsTerminated) + { + printf("Device 0x%08x \"%s\"removed.\n", service, pDevice->product); + + // Free the data we're no longer using now that the device is going away + hid_DisposeDevice (pDevice); + } +} +#else + +static void hid_RemovalCallbackFunction(void * target, IOReturn result, void * refcon, void * sender) +{ + hid_DisposeDevice ((pRecDevice) target); +} + +#endif USE_NOTIFICATIONS + +//================================================================================================ +// +// hid_AddDevices +// +// This routine is the callback for our IOServiceAddMatchingNotification. When we get called +// we will look at all the devices that were added and we will: +// +// 1. Create some private data to relate to each device. +// 2. Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for this device, +// using the refCon field to store a pointer to our data. When we get called with +// this interest notification, we can grab the refCon and access our private data. +// +//================================================================================================ +// +// --------------------------------- +// given a IO device iterator, iterate it and add all its devices + +static void hid_AddDevices (void *refCon, io_iterator_t iterator) +{ + // NOTE: refcon passed in is used to point to the device list head + pRecDevice* pListDeviceHead = (pRecDevice*) refCon; + IOReturn result = kIOReturnSuccess; + io_object_t ioHIDDeviceObject = NULL; + + while (ioHIDDeviceObject = IOIteratorNext (iterator)) + { + pRecDevice* pNewDeviceAt = NULL; + pRecDevice pNewDevice = hid_BuildDevice (ioHIDDeviceObject); + if (pNewDevice) + { +#if 0 // set true for verbose output + printf("\nhid_AddDevices: pNewDevice = {t: \"%s\", v: %ld, p: %ld, v: %ld, m: \"%s\", " \ + "p: \"%s\", l: %ld, u: %4.4lX:%4.4lX, #e: %ld, #f: %ld, #i: %ld, #o: %ld, " \ + "#c: %ld, #a: %ld, #b: %ld, #h: %ld, #s: %ld, #d: %ld, #w: %ld}.", + pNewDevice->transport, + pNewDevice->vendorID, + pNewDevice->productID, + pNewDevice->version, + pNewDevice->manufacturer, + pNewDevice->product, + pNewDevice->locID, + pNewDevice->usagePage, + pNewDevice->usage, + pNewDevice->totalElements, + pNewDevice->features, + pNewDevice->inputs, + pNewDevice->outputs, + pNewDevice->collections, + pNewDevice->axis, + pNewDevice->buttons, + pNewDevice->hats, + pNewDevice->sliders, + pNewDevice->dials, + pNewDevice->wheels + ); + fflush(stdout); +#elif 0 // otherwise output brief description + printf("\nhid_AddDevices: pNewDevice = {m: \"%s\" p: \"%s\", vid: %ld, pid: %ld, loc: %8.8lX, usage: %4.4lX:%4.4lX}.", + pNewDevice->manufacturer, + pNewDevice->product, + pNewDevice->vendorID, + pNewDevice->productID, + pNewDevice->locID, + pNewDevice->usagePage, + pNewDevice->usage + ); + fflush(stdout); +#endif + pNewDeviceAt = hid_AddDevice (pListDeviceHead, pNewDevice); + } + +#if USE_NOTIFICATIONS + // Register for an interest notification of this device being removed. Use a reference to our + // private data as the refCon which will be passed to the notification callback. + result = IOServiceAddInterestNotification( gNotifyPort, // notifyPort + ioHIDDeviceObject, // service + kIOGeneralInterest, // interestType + hid_DeviceNotification, // callback + pNewDevice, // refCon + (io_object_t*) &pNewDevice->notification); // notification + if (KERN_SUCCESS != result) + HIDReportErrorNum ("hid_AddDevices: IOServiceAddInterestNotification error: x0%8.8lX.", result); +#else + result = (*(IOHIDDeviceInterface**)pNewDevice->interface)->setRemovalCallback (pNewDevice->interface, hid_RemovalCallbackFunction,pNewDeviceAt,0); +#endif USE_NOTIFICATIONS + + // release the device object, it is no longer needed + result = IOObjectRelease (ioHIDDeviceObject); + if (KERN_SUCCESS != result) + HIDReportErrorNum ("hid_AddDevices: IOObjectRelease error with ioHIDDeviceObject.", result); + } +} + +// --------------------------------- +// disposes of the element list associated with a device and the memory associated with the list +// uses depthwise recursion to dispose both collections and elements. + +static void hid_DisposeDeviceElements (pRecElement pElement) +{ + if (pElement) + { + if (pElement->pChild) + hid_DisposeDeviceElements (pElement->pChild); + if (pElement->pSibling) + hid_DisposeDeviceElements (pElement->pSibling); + free (pElement); + } +} + +// --------------------------------- +// disposes of a single device, closing and releaseing interface, freeing memory fro device and elements, setting device pointer to NULL +// all your device no longer belong to us... (i.e., you do not 'own' the device anymore) + +static pRecDevice hid_DisposeDevice (pRecDevice pDevice) +{ + kern_return_t result = KERN_SUCCESS; + pRecDevice pDeviceNext = NULL; + + if (HIDIsValidDevice(pDevice)) + { + // save next device prior to disposing of this device + pDeviceNext = pDevice->pNext; + + result = HIDDequeueDevice (pDevice); +#if 0 + if (kIOReturnSuccess != result) + HIDReportErrorNum ("hid_DisposeDevice: HIDDequeueDevice error: 0x%8.8X.", result); +#endif 1 + + hid_DisposeDeviceElements (pDevice->pListElements); + pDevice->pListElements = NULL; + + result = HIDCloseReleaseInterface (pDevice); // function sanity checks interface value (now application does not own device) + if (kIOReturnSuccess != result) + HIDReportErrorNum ("hid_DisposeDevice: HIDCloseReleaseInterface error: 0x%8.8X.", result); + +#if USE_NOTIFICATIONS + if (pDevice->interface) + { + // replace (*pDevice->interface)->Release(pDevice->interface); + result = IODestroyPlugInInterface (pDevice->interface); + if (kIOReturnSuccess != result) + HIDReportErrorNum ("hid_DisposeDevice: IODestroyPlugInInterface error: 0x%8.8X.", result); + } + + if (pDevice->notification) + { + result = IOObjectRelease((io_object_t) pDevice->notification); + if (kIOReturnSuccess != result) + HIDReportErrorNum ("hid_DisposeDevice: IOObjectRelease error: 0x%8.8X.", result); + } +#endif USE_NOTIFICATIONS + + // remove this device from the device list + if (gpDeviceList == pDevice) // head of list? + gpDeviceList = pDeviceNext; + else + { + pRecDevice pDeviceTemp = pDeviceNext = gpDeviceList; // we're going to return this if we don't find ourselfs in the list + while (pDeviceTemp) + { + if (pDeviceTemp->pNext == pDevice) // found us! + { + // take us out of linked list + pDeviceTemp->pNext = pDeviceNext = pDevice->pNext; + break; + } + pDeviceTemp = pDeviceTemp->pNext; + } + } + free (pDevice); + } + + // update device count + gNumDevices = hid_CountCurrentDevices (); + + return pDeviceNext; +} + +// --------------------------------- +// count number of devices in global device list (gpDeviceList) +static UInt32 hid_CountCurrentDevices (void) +{ + pRecDevice pDevice = gpDeviceList; + UInt32 devices = 0; + while (pDevice) + { + devices++; + pDevice = pDevice->pNext; + } + return devices; +} + +// --------------------------------- +// matches type masks passed in to actual element types (which are not set up to be used as a mask +static Boolean hid_MatchElementTypeMask (IOHIDElementType type, HIDElementTypeMask typeMask) +{ + if (typeMask & kHIDElementTypeInput) + if ((type == kIOHIDElementTypeInput_Misc) || (type == kIOHIDElementTypeInput_Button) || (type == kIOHIDElementTypeInput_Axis) || (type == kIOHIDElementTypeInput_ScanCodes)) + return true; + if (typeMask & kHIDElementTypeOutput) + if (type == kIOHIDElementTypeOutput) + return true; + if (typeMask & kHIDElementTypeFeature) + if (type == kIOHIDElementTypeFeature) + return true; + if (typeMask & kHIDElementTypeCollection) + if (type == kIOHIDElementTypeCollection) + return true; + return false; +} + +// --------------------------------- + +static pRecElement hid_GetDeviceElement (pRecElement pElement, HIDElementTypeMask typeMask) +{ + // we are asking for this element + if (NULL != pElement) + { + if (hid_MatchElementTypeMask (pElement->type, typeMask)) // if the type match what we are looking for + return pElement; // return the element + else + return HIDGetNextDeviceElement (pElement, typeMask); // else get the next one + } + return NULL; +} + +#define FAKE_IT 0 // set true for debugging; returns the usage & usage page as numbers + +// --------------------------------- +// Load the usage strings from the resource (XML) file into a CFPropertyListRef +static CFPropertyListRef xml_load_usage_strings(void) +{ + CFPropertyListRef tCFPropertyListRef = NULL; + CFURLRef resFileCFURLRef = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("HID_usage_strings"), CFSTR("plist"), NULL); + + if (NULL != resFileCFURLRef) + { + CFDataRef resCFDataRef; + + if (CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, resFileCFURLRef, &resCFDataRef, nil, nil, nil)) + { + if (NULL != resCFDataRef) + { + CFStringRef errorString; + + tCFPropertyListRef = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resCFDataRef, kCFPropertyListImmutable, &errorString); + if (NULL == tCFPropertyListRef) + CFShow(errorString); + CFRelease(resCFDataRef); + } + } + CFRelease(resFileCFURLRef); + } + return tCFPropertyListRef; +} + +// --------------------------------- +// Find a usage string in the resource (XML) file + +static Boolean xml_GetUsageName(const long valueUsagePage, const long valueUsage, char* pCstr) +{ + static CFPropertyListRef tCFPropertyListRef = NULL; + Boolean results = false; + + if (NULL == tCFPropertyListRef) + tCFPropertyListRef = xml_load_usage_strings(); + if (NULL != tCFPropertyListRef) + { + if (CFDictionaryGetTypeID() == CFGetTypeID(tCFPropertyListRef)) + { + CFDictionaryRef pageCFDictionaryRef; + CFStringRef pageKeyCFStringRef; + pageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("0x%4.4lX"), valueUsagePage); + + if (CFDictionaryGetValueIfPresent(tCFPropertyListRef, pageKeyCFStringRef, (const void**) &pageCFDictionaryRef)) + { + CFStringRef pageCFStringRef; + + if (CFDictionaryGetValueIfPresent(pageCFDictionaryRef, CFSTR("Name"), (const void**) &pageCFStringRef)) + { + //CFShow(pageCFStringRef); + } + + { + CFStringRef fullCFStringRef = NULL; + CFStringRef usageKeyCFStringRef; + CFStringRef usageCFStringRef; + + usageKeyCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("0x%4.4lX"), valueUsage); + + if (CFDictionaryGetValueIfPresent(pageCFDictionaryRef, usageKeyCFStringRef, (const void**) &usageCFStringRef)) + { + fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ %@"), + pageCFStringRef, usageCFStringRef); + // CFShow(usageCFStringRef); + } +#if FAKE_IT + else + { + fullCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ #%@"), + pageCFStringRef, usageKeyCFStringRef); + } +#endif + if (fullCFStringRef) + { + // CFShow(fullCFStringRef); + results = CFStringGetCString( + fullCFStringRef, pCstr, CFStringGetLength(fullCFStringRef) * sizeof(UniChar) + 1, kCFStringEncodingMacRoman); + CFRelease(fullCFStringRef); + } + CFRelease(usageKeyCFStringRef); + } + } + CFRelease(pageKeyCFStringRef); + } + //++CFRelease(tCFPropertyListRef); // Leak this! + } + return results; +} + +#pragma mark public functions +// ================================= +// public functions + +// --------------------------------- +// Create and open an interface to device, required prior to extracting values or building queues +// Note: appliction now owns the device and must close and release it prior to exiting + +unsigned long HIDCreateOpenDeviceInterface (UInt32 hidDevice, pRecDevice pDevice) +{ + IOReturn result = kIOReturnSuccess; + HRESULT plugInResult = S_OK; + SInt32 score = 0; + IOCFPlugInInterface ** ppPlugInInterface = NULL; + + if (NULL == pDevice->interface) + { + result = IOCreatePlugInInterfaceForService (hidDevice, kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &ppPlugInInterface, &score); + if (kIOReturnSuccess == result) + { + // Call a method of the intermediate plug-in to create the device interface + plugInResult = (*ppPlugInInterface)->QueryInterface (ppPlugInInterface, + CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID), (void *) &(pDevice->interface)); + if (S_OK != plugInResult) + HIDReportErrorNum ("CouldnŐt query HID class device interface from plugInInterface", plugInResult); + IODestroyPlugInInterface (ppPlugInInterface); // replace (*ppPlugInInterface)->Release (ppPlugInInterface) + } + else + HIDReportErrorNum ("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.", result); + } + if (NULL != pDevice->interface) + { + result = (*(IOHIDDeviceInterface**)pDevice->interface)->open (pDevice->interface, 0); + if (kIOReturnSuccess != result) + HIDReportErrorNum ("Failed to open pDevice->interface via open.", result); + } + return result; +} + +// --------------------------------- +// builds list of device with elements (allocates memory and captures devices) +// list is allcoated internally within HID Utilites and can be accessed via accessor functions +// structures within list are considered flat and user accessable, but not user modifiable +// can be called again to rebuild list to account for new devices (will do the right thing in case of disposing existing list) +// returns true if succesful + +Boolean HIDBuildDeviceList (UInt32 usagePage, UInt32 usage) +{ + IOReturn result = kIOReturnSuccess; + mach_port_t masterPort = NULL; + + if (NULL != gpDeviceList) + HIDReleaseDeviceList (); + + result = IOMasterPort (bootstrap_port, &masterPort); + if (kIOReturnSuccess != result) + HIDReportErrorNum ("IOMasterPort error with bootstrap_port.", result); + else + { + CFMutableDictionaryRef hidMatchDictionary = NULL; + + // Set up matching dictionary to search the I/O Registry for HID devices we are interested in. Dictionary reference is NULL if error. + { + CFNumberRef refUsage = NULL, refUsagePage = NULL; + + // Set up a matching dictionary to search I/O Registry by class name for all HID class devices. + hidMatchDictionary = IOServiceMatching (kIOHIDDeviceKey); + if (NULL != hidMatchDictionary) + { + if (usagePage) + { + // Add key for device type (joystick, in this case) to refine the matching dictionary. + refUsagePage = CFNumberCreate (kCFAllocatorDefault, kCFNumberLongType, &usagePage); + CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsagePageKey), refUsagePage); + CFRelease (refUsagePage); + if (usage) + { + refUsage = CFNumberCreate (kCFAllocatorDefault, kCFNumberLongType, &usage); + CFDictionarySetValue (hidMatchDictionary, CFSTR (kIOHIDPrimaryUsageKey), refUsage); + CFRelease (refUsage); + } + } + CFRetain(hidMatchDictionary); + } + else + HIDReportError ("Failed to get HID CFMutableDictionaryRef via IOServiceMatching."); + } + +#if USE_NOTIFICATIONS + // Create a notification port and add its run loop event source to our run loop + // This is how async notifications get set up. + { + CFRunLoopSourceRef runLoopSource; + + gNotifyPort = IONotificationPortCreate(masterPort); + runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort); + + gRunLoop = CFRunLoopGetCurrent(); + CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode); + + // Now set up a notification to be called when a device is first matched by I/O Kit. + result = IOServiceAddMatchingNotification(gNotifyPort, // notifyPort + kIOFirstMatchNotification, // notificationType + hidMatchDictionary, // matching + hid_AddDevices, // callback + &gpDeviceList, // refCon + &gAddedIter // notification + ); + + // call it now to add all existing devices + hid_AddDevices(&gpDeviceList,gAddedIter); + } +#else + { + io_iterator_t hidObjectIterator = NULL; + + // Now search I/O Registry for matching devices. + result = IOServiceGetMatchingServices (masterPort, hidMatchDictionary, &hidObjectIterator); + if (kIOReturnSuccess != result) + HIDReportErrorNum ("Failed to create IO object iterator, error:", result); + else if (NULL == hidObjectIterator) // likely no HID devices which matched selection criteria are connected + HIDReportError ("Warning: Could not find any matching devices, thus iterator creation failed."); + + if (NULL != hidObjectIterator) + { + hid_AddDevices(&gpDeviceList,hidObjectIterator); + + result = IOObjectRelease (hidObjectIterator); // release the iterator + if (kIOReturnSuccess != result) + HIDReportErrorNum ("IOObjectRelease error with hidObjectIterator.", result); + + gNumDevices = hid_CountCurrentDevices (); + return true; + } + } +#endif USE_NOTIFICATIONS + // IOServiceGetMatchingServices consumes a reference to the dictionary, so we don't need to release the dictionary ref. + hidMatchDictionary = NULL; + } + return false; +} + +// --------------------------------- +// release list built by above function +// MUST be called prior to application exit to properly release devices +// if not called (or app crashes) devices can be recovered by pluging into different location in USB chain + +void HIDReleaseDeviceList (void) +{ + while (NULL != gpDeviceList) + gpDeviceList = hid_DisposeDevice (gpDeviceList); // dispose current device return next device will set gpDeviceList to NULL + gNumDevices = 0; +} + +// --------------------------------- +// does a device list exist + +Boolean HIDHaveDeviceList (void) +{ + if (NULL != gpDeviceList) + return true; + return false; +} + +// --------------------------------- +// how many HID devices have been found +// returns 0 if no device list exist + +UInt32 HIDCountDevices (void) +{ + gNumDevices = hid_CountCurrentDevices (); + + return gNumDevices; +} + +// --------------------------------- +// how many elements does a specific device have +// returns 0 if device is invlaid or NULL + +UInt32 HIDCountDeviceElements (pRecDevice pDevice, HIDElementTypeMask typeMask) +{ + long count = 0; + if (HIDIsValidDevice(pDevice)) + { + if (typeMask & kHIDElementTypeInput) + count += pDevice->inputs; + if (typeMask & kHIDElementTypeOutput) + count += pDevice->outputs; + if (typeMask & kHIDElementTypeFeature) + count += pDevice->features; + if (typeMask & kHIDElementTypeCollection) + count += pDevice->collections; + } + return count; +} + +// --------------------------------- +// get the first device in the device list +// returns NULL if no list exists + +pRecDevice HIDGetFirstDevice (void) +{ + return gpDeviceList; +} + +// --------------------------------- +// get next device in list given current device as parameter +// returns NULL if end of list + +pRecDevice HIDGetNextDevice (pRecDevice pDevice) +{ + if (NULL != pDevice) + return pDevice->pNext; + else + return NULL; +} + +// --------------------------------- +// get the first element of device passed in as parameter +// returns NULL if no list exists or device does not exists or is NULL +pRecElement HIDGetFirstDeviceElement (pRecDevice pDevice, HIDElementTypeMask typeMask) +{ + if (HIDIsValidDevice(pDevice)) + { + if (hid_MatchElementTypeMask (pDevice->pListElements->type, typeMask)) // ensure first type matches + return pDevice->pListElements; + else + return HIDGetNextDeviceElement (pDevice->pListElements, typeMask); + } + else + return NULL; +} + +// --------------------------------- +// get next element of given device in list given current element as parameter +// will walk down each collection then to next element or collection (depthwise traverse) +// returns NULL if end of list +// uses mask of HIDElementTypeMask to restrict element found +// use kHIDElementTypeIO to get previous HIDGetNextDeviceElement functionality +pRecElement HIDGetNextDeviceElement (pRecElement pElement, HIDElementTypeMask typeMask) +{ + // should only have elements passed in (though someone could mix calls and pass us a collection) + // collection means return the next child or sibling (in that order) + // element means returnt he next sibling (as elements can't have children + if (NULL != pElement) + { + if (pElement->pChild) + { + if (pElement->type != kIOHIDElementTypeCollection) + HIDReportError ("Malformed element list: found child of element."); + else + return hid_GetDeviceElement (pElement->pChild, typeMask); // return the child of this element + } + else if (pElement->pSibling) + { + return hid_GetDeviceElement (pElement->pSibling, typeMask); //return the sibling of this element + } + else // at end back up correctly + { + pRecElement pPreviousElement = NULL; + // malformed device ending in collection + if (pElement->type == kIOHIDElementTypeCollection) + HIDReportError ("Malformed device: found collection at end of element chain."); + // walk back up tree to element prior to first collection ecountered and take next element + while (NULL != pElement->pPrevious) + { + pPreviousElement = pElement; + pElement = pElement->pPrevious; // look at previous element + // if we have a collection and the previous element is the branch element (should have both a colection and next element attached to it) + // if we found a collection, which we are not at the sibling level that actually does have siblings + if (((pElement->type == kIOHIDElementTypeCollection) && (pPreviousElement != pElement->pSibling) && pElement->pSibling) || + // or if we are at the top + (NULL == pElement->pPrevious)) // at top of tree + break; + } + if (NULL == pElement->pPrevious) + return NULL; // got to top of list with only a collection as the first element + // now we must have been down the child route so go down the sibling route + pElement = pElement->pSibling; // element of interest + return hid_GetDeviceElement (pElement, typeMask); // otherwise return this element + } + } + return NULL; +} + +// --------------------------------- +// get previous element of given device in list given current element as parameter +// this wlaks directly up the tree to the top element and does not search at each level +// returns NULL if beginning of list +// uses mask of HIDElementTypeMask to restrict element found +// use kHIDElementTypeIO to get non-collection elements +pRecElement HIDGetPreviousDeviceElement (pRecElement pElement, HIDElementTypeMask typeMask) +{ + pRecElement pPreviousElement = pElement->pPrevious; + // walk back up tree to element prior + while (pPreviousElement && !hid_MatchElementTypeMask (pPreviousElement->type, typeMask)) + { + pElement = pPreviousElement; // look at previous element + pPreviousElement = pElement->pPrevious; + } + return pPreviousElement; // return this element +} + +// --------------------------------- +// returns C string type name given a type enumeration passed in as parameter (see IOHIDKeys.h) +// returns "Unknown Type" for invalid types + +void HIDGetTypeName (IOHIDElementType theType, char * cstrName) +{ + switch (theType) + { + case kIOHIDElementTypeInput_Misc: + sprintf(cstrName, "Miscellaneous Input"); + break; + case kIOHIDElementTypeInput_Button: + sprintf(cstrName, "Button Input"); + break; + case kIOHIDElementTypeInput_Axis: + sprintf(cstrName, "Axis Input"); + break; + case kIOHIDElementTypeInput_ScanCodes: + sprintf(cstrName, "Scan Code Input"); + break; + case kIOHIDElementTypeOutput: + sprintf(cstrName, "Output"); + break; + case kIOHIDElementTypeFeature: + sprintf(cstrName, "Feature"); + break; + case kIOHIDElementTypeCollection: + sprintf(cstrName, "Collection"); + break; + default: + sprintf(cstrName, "Unknown Type"); + break; + } +} + +// --------------------------------- +// returns C string usage given usage page and usage passed in as parameters (see IOUSBHIDParser.h) +// returns usage page and usage values in string form for unknown values + +void HIDGetUsageName (const long valueUsagePage, const long valueUsage, char * cstrName) +{ + if (xml_GetUsageName(valueUsagePage, valueUsage, cstrName)) + return; + + switch (valueUsagePage) + { + case kHIDPage_Undefined: + switch (valueUsage) + { + default: sprintf (cstrName, "Undefined Page, Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_GenericDesktop: + switch (valueUsage) + { + case kHIDUsage_GD_Pointer: sprintf (cstrName, "Pointer"); break; + case kHIDUsage_GD_Mouse: sprintf (cstrName, "Mouse"); break; + case kHIDUsage_GD_Joystick: sprintf (cstrName, "Joystick"); break; + case kHIDUsage_GD_GamePad: sprintf (cstrName, "GamePad"); break; + case kHIDUsage_GD_Keyboard: sprintf (cstrName, "Keyboard"); break; + case kHIDUsage_GD_Keypad: sprintf (cstrName, "Keypad"); break; + case kHIDUsage_GD_MultiAxisController: sprintf (cstrName, "Multi-Axis Controller"); break; + + case kHIDUsage_GD_X: sprintf (cstrName, "X-Axis"); break; + case kHIDUsage_GD_Y: sprintf (cstrName, "Y-Axis"); break; + case kHIDUsage_GD_Z: sprintf (cstrName, "Z-Axis"); break; + case kHIDUsage_GD_Rx: sprintf (cstrName, "X-Rotation"); break; + case kHIDUsage_GD_Ry: sprintf (cstrName, "Y-Rotation"); break; + case kHIDUsage_GD_Rz: sprintf (cstrName, "Z-Rotation"); break; + case kHIDUsage_GD_Slider: sprintf (cstrName, "Slider"); break; + case kHIDUsage_GD_Dial: sprintf (cstrName, "Dial"); break; + case kHIDUsage_GD_Wheel: sprintf (cstrName, "Wheel"); break; + case kHIDUsage_GD_Hatswitch: sprintf (cstrName, "Hatswitch"); break; + case kHIDUsage_GD_CountedBuffer: sprintf (cstrName, "Counted Buffer"); break; + case kHIDUsage_GD_ByteCount: sprintf (cstrName, "Byte Count"); break; + case kHIDUsage_GD_MotionWakeup: sprintf (cstrName, "Motion Wakeup"); break; + case kHIDUsage_GD_Start: sprintf (cstrName, "Start"); break; + case kHIDUsage_GD_Select: sprintf (cstrName, "Select"); break; + + case kHIDUsage_GD_Vx: sprintf (cstrName, "X-Velocity"); break; + case kHIDUsage_GD_Vy: sprintf (cstrName, "Y-Velocity"); break; + case kHIDUsage_GD_Vz: sprintf (cstrName, "Z-Velocity"); break; + case kHIDUsage_GD_Vbrx: sprintf (cstrName, "X-Rotation Velocity"); break; + case kHIDUsage_GD_Vbry: sprintf (cstrName, "Y-Rotation Velocity"); break; + case kHIDUsage_GD_Vbrz: sprintf (cstrName, "Z-Rotation Velocity"); break; + case kHIDUsage_GD_Vno: sprintf (cstrName, "Vno"); break; + + case kHIDUsage_GD_SystemControl: sprintf (cstrName, "System Control"); break; + case kHIDUsage_GD_SystemPowerDown: sprintf (cstrName, "System Power Down"); break; + case kHIDUsage_GD_SystemSleep: sprintf (cstrName, "System Sleep"); break; + case kHIDUsage_GD_SystemWakeUp: sprintf (cstrName, "System Wake Up"); break; + case kHIDUsage_GD_SystemContextMenu: sprintf (cstrName, "System Context Menu"); break; + case kHIDUsage_GD_SystemMainMenu: sprintf (cstrName, "System Main Menu"); break; + case kHIDUsage_GD_SystemAppMenu: sprintf (cstrName, "System App Menu"); break; + case kHIDUsage_GD_SystemMenuHelp: sprintf (cstrName, "System Menu Help"); break; + case kHIDUsage_GD_SystemMenuExit: sprintf (cstrName, "System Menu Exit"); break; + case kHIDUsage_GD_SystemMenu: sprintf (cstrName, "System Menu"); break; + case kHIDUsage_GD_SystemMenuRight: sprintf (cstrName, "System Menu Right"); break; + case kHIDUsage_GD_SystemMenuLeft: sprintf (cstrName, "System Menu Left"); break; + case kHIDUsage_GD_SystemMenuUp: sprintf (cstrName, "System Menu Up"); break; + case kHIDUsage_GD_SystemMenuDown: sprintf (cstrName, "System Menu Down"); break; + + case kHIDUsage_GD_DPadUp: sprintf (cstrName, "DPad Up"); break; + case kHIDUsage_GD_DPadDown: sprintf (cstrName, "DPad Down"); break; + case kHIDUsage_GD_DPadRight: sprintf (cstrName, "DPad Right"); break; + case kHIDUsage_GD_DPadLeft: sprintf (cstrName, "DPad Left"); break; + + case kHIDUsage_GD_Reserved: sprintf (cstrName, "Reserved"); break; + + default: sprintf (cstrName, "Generic Desktop Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_Simulation: + switch (valueUsage) + { + default: sprintf (cstrName, "Simulation Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_VR: + switch (valueUsage) + { + default: sprintf (cstrName, "VR Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_Sport: + switch (valueUsage) + { + default: sprintf (cstrName, "Sport Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_Game: + switch (valueUsage) + { + default: sprintf (cstrName, "Game Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_KeyboardOrKeypad: + switch (valueUsage) + { + default: sprintf (cstrName, "Keyboard Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_LEDs: + switch (valueUsage) + { + // some LED usages + case kHIDUsage_LED_IndicatorRed: sprintf (cstrName, "Red LED"); break; + case kHIDUsage_LED_IndicatorGreen: sprintf (cstrName, "Green LED"); break; + case kHIDUsage_LED_IndicatorAmber: sprintf (cstrName, "Amber LED"); break; + case kHIDUsage_LED_GenericIndicator: sprintf (cstrName, "Generic LED"); break; + case kHIDUsage_LED_SystemSuspend: sprintf (cstrName, "System Suspend LED"); break; + case kHIDUsage_LED_ExternalPowerConnected: sprintf (cstrName, "External Power LED"); break; + default: sprintf (cstrName, "LED Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_Button: + switch (valueUsage) + { + default: sprintf (cstrName, "Button #%ld", valueUsage); break; + } + break; + case kHIDPage_Ordinal: + switch (valueUsage) + { + default: sprintf (cstrName, "Ordinal Instance %lx", valueUsage); break; + } + break; + case kHIDPage_Telephony: + switch (valueUsage) + { + default: sprintf (cstrName, "Telephony Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_Consumer: + switch (valueUsage) + { + default: sprintf (cstrName, "Consumer Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_Digitizer: + switch (valueUsage) + { + default: sprintf (cstrName, "Digitizer Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_PID: + if (((valueUsage >= 0x02) && (valueUsage <= 0x1F)) || ((valueUsage >= 0x29) && (valueUsage <= 0x2F)) || + ((valueUsage >= 0x35) && (valueUsage <= 0x3F)) || ((valueUsage >= 0x44) && (valueUsage <= 0x4F)) || + (valueUsage == 0x8A) || (valueUsage == 0x93) || ((valueUsage >= 0x9D) && (valueUsage <= 0x9E)) || + ((valueUsage >= 0xA1) && (valueUsage <= 0xA3)) || ((valueUsage >= 0xAD) && (valueUsage <= 0xFFFF))) + sprintf (cstrName, "PID Reserved"); + else + switch (valueUsage) + { + case 0x00: sprintf (cstrName, "PID Undefined Usage"); break; + case kHIDUsage_PID_PhysicalInterfaceDevice: sprintf (cstrName, "Physical Interface Device"); break; + case kHIDUsage_PID_Normal: sprintf (cstrName, "Normal Force"); break; + + case kHIDUsage_PID_SetEffectReport: sprintf (cstrName, "Set Effect Report"); break; + case kHIDUsage_PID_EffectBlockIndex: sprintf (cstrName, "Effect Block Index"); break; + case kHIDUsage_PID_ParamBlockOffset: sprintf (cstrName, "Parameter Block Offset"); break; + case kHIDUsage_PID_ROM_Flag: sprintf (cstrName, "ROM Flag"); break; + + case kHIDUsage_PID_EffectType: sprintf (cstrName, "Effect Type"); break; + case kHIDUsage_PID_ET_ConstantForce: sprintf (cstrName, "Effect Type Constant Force"); break; + case kHIDUsage_PID_ET_Ramp: sprintf (cstrName, "Effect Type Ramp"); break; + case kHIDUsage_PID_ET_CustomForceData: sprintf (cstrName, "Effect Type Custom Force Data"); break; + case kHIDUsage_PID_ET_Square: sprintf (cstrName, "Effect Type Square"); break; + case kHIDUsage_PID_ET_Sine: sprintf (cstrName, "Effect Type Sine"); break; + case kHIDUsage_PID_ET_Triangle: sprintf (cstrName, "Effect Type Triangle"); break; + case kHIDUsage_PID_ET_SawtoothUp: sprintf (cstrName, "Effect Type Sawtooth Up"); break; + case kHIDUsage_PID_ET_SawtoothDown: sprintf (cstrName, "Effect Type Sawtooth Down"); break; + case kHIDUsage_PID_ET_Spring: sprintf (cstrName, "Effect Type Spring"); break; + case kHIDUsage_PID_ET_Damper: sprintf (cstrName, "Effect Type Damper"); break; + case kHIDUsage_PID_ET_Inertia: sprintf (cstrName, "Effect Type Inertia"); break; + case kHIDUsage_PID_ET_Friction: sprintf (cstrName, "Effect Type Friction"); break; + case kHIDUsage_PID_Duration: sprintf (cstrName, "Effect Duration"); break; + case kHIDUsage_PID_SamplePeriod: sprintf (cstrName, "Effect Sample Period"); break; + case kHIDUsage_PID_Gain: sprintf (cstrName, "Effect Gain"); break; + case kHIDUsage_PID_TriggerButton: sprintf (cstrName, "Effect Trigger Button"); break; + case kHIDUsage_PID_TriggerRepeatInterval: sprintf (cstrName, "Effect Trigger Repeat Interval"); break; + + case kHIDUsage_PID_AxesEnable: sprintf (cstrName, "Axis Enable"); break; + case kHIDUsage_PID_DirectionEnable: sprintf (cstrName, "Direction Enable"); break; + + case kHIDUsage_PID_Direction: sprintf (cstrName, "Direction"); break; + + case kHIDUsage_PID_TypeSpecificBlockOffset: sprintf (cstrName, "Type Specific Block Offset"); break; + + case kHIDUsage_PID_BlockType: sprintf (cstrName, "Block Type"); break; + + case kHIDUsage_PID_SetEnvelopeReport: sprintf (cstrName, "Set Envelope Report"); break; + case kHIDUsage_PID_AttackLevel: sprintf (cstrName, "Envelope Attack Level"); break; + case kHIDUsage_PID_AttackTime: sprintf (cstrName, "Envelope Attack Time"); break; + case kHIDUsage_PID_FadeLevel: sprintf (cstrName, "Envelope Fade Level"); break; + case kHIDUsage_PID_FadeTime: sprintf (cstrName, "Envelope Fade Time"); break; + + case kHIDUsage_PID_SetConditionReport: sprintf (cstrName, "Set Condition Report"); break; + case kHIDUsage_PID_CP_Offset: sprintf (cstrName, "Condition CP Offset"); break; + case kHIDUsage_PID_PositiveCoefficient: sprintf (cstrName, "Condition Positive Coefficient"); break; + case kHIDUsage_PID_NegativeCoefficient: sprintf (cstrName, "Condition Negative Coefficient"); break; + case kHIDUsage_PID_PositiveSaturation: sprintf (cstrName, "Condition Positive Saturation"); break; + case kHIDUsage_PID_NegativeSaturation: sprintf (cstrName, "Condition Negative Saturation"); break; + case kHIDUsage_PID_DeadBand: sprintf (cstrName, "Condition Dead Band"); break; + + case kHIDUsage_PID_DownloadForceSample: sprintf (cstrName, "Download Force Sample"); break; + case kHIDUsage_PID_IsochCustomForceEnable: sprintf (cstrName, "Isoch Custom Force Enable"); break; + + case kHIDUsage_PID_CustomForceDataReport: sprintf (cstrName, "Custom Force Data Report"); break; + case kHIDUsage_PID_CustomForceData: sprintf (cstrName, "Custom Force Data"); break; + + case kHIDUsage_PID_CustomForceVendorDefinedData: sprintf (cstrName, "Custom Force Vendor Defined Data"); break; + case kHIDUsage_PID_SetCustomForceReport: sprintf (cstrName, "Set Custom Force Report"); break; + case kHIDUsage_PID_CustomForceDataOffset: sprintf (cstrName, "Custom Force Data Offset"); break; + case kHIDUsage_PID_SampleCount: sprintf (cstrName, "Custom Force Sample Count"); break; + + case kHIDUsage_PID_SetPeriodicReport: sprintf (cstrName, "Set Periodic Report"); break; + case kHIDUsage_PID_Offset: sprintf (cstrName, "Periodic Offset"); break; + case kHIDUsage_PID_Magnitude: sprintf (cstrName, "Periodic Magnitude"); break; + case kHIDUsage_PID_Phase: sprintf (cstrName, "Periodic Phase"); break; + case kHIDUsage_PID_Period: sprintf (cstrName, "Periodic Period"); break; + + case kHIDUsage_PID_SetConstantForceReport: sprintf (cstrName, "Set Constant Force Report"); break; + + case kHIDUsage_PID_SetRampForceReport: sprintf (cstrName, "Set Ramp Force Report"); break; + case kHIDUsage_PID_RampStart: sprintf (cstrName, "Ramp Start"); break; + case kHIDUsage_PID_RampEnd: sprintf (cstrName, "Ramp End"); break; + + case kHIDUsage_PID_EffectOperationReport: sprintf (cstrName, "Effect Operation Report"); break; + + case kHIDUsage_PID_EffectOperation: sprintf (cstrName, "Effect Operation"); break; + case kHIDUsage_PID_OpEffectStart: sprintf (cstrName, "Op Effect Start"); break; + case kHIDUsage_PID_OpEffectStartSolo: sprintf (cstrName, "Op Effect Start Solo"); break; + case kHIDUsage_PID_OpEffectStop: sprintf (cstrName, "Op Effect Stop"); break; + case kHIDUsage_PID_LoopCount: sprintf (cstrName, "Op Effect Loop Count"); break; + + case kHIDUsage_PID_DeviceGainReport: sprintf (cstrName, "Device Gain Report"); break; + case kHIDUsage_PID_DeviceGain: sprintf (cstrName, "Device Gain"); break; + + case kHIDUsage_PID_PoolReport: sprintf (cstrName, "PID Pool Report"); break; + case kHIDUsage_PID_RAM_PoolSize: sprintf (cstrName, "RAM Pool Size"); break; + case kHIDUsage_PID_ROM_PoolSize: sprintf (cstrName, "ROM Pool Size"); break; + case kHIDUsage_PID_ROM_EffectBlockCount: sprintf (cstrName, "ROM Effect Block Count"); break; + case kHIDUsage_PID_SimultaneousEffectsMax: sprintf (cstrName, "Simultaneous Effects Max"); break; + case kHIDUsage_PID_PoolAlignment: sprintf (cstrName, "Pool Alignment"); break; + + case kHIDUsage_PID_PoolMoveReport: sprintf (cstrName, "PID Pool Move Report"); break; + case kHIDUsage_PID_MoveSource: sprintf (cstrName, "Move Source"); break; + case kHIDUsage_PID_MoveDestination: sprintf (cstrName, "Move Destination"); break; + case kHIDUsage_PID_MoveLength: sprintf (cstrName, "Move Length"); break; + + case kHIDUsage_PID_BlockLoadReport: sprintf (cstrName, "PID Block Load Report"); break; + + case kHIDUsage_PID_BlockLoadStatus: sprintf (cstrName, "Block Load Status"); break; + case kHIDUsage_PID_BlockLoadSuccess: sprintf (cstrName, "Block Load Success"); break; + case kHIDUsage_PID_BlockLoadFull: sprintf (cstrName, "Block Load Full"); break; + case kHIDUsage_PID_BlockLoadError: sprintf (cstrName, "Block Load Error"); break; + case kHIDUsage_PID_BlockHandle: sprintf (cstrName, "Block Handle"); break; + + case kHIDUsage_PID_BlockFreeReport: sprintf (cstrName, "PID Block Free Report"); break; + + case kHIDUsage_PID_TypeSpecificBlockHandle: sprintf (cstrName, "Type Specific Block Handle"); break; + + case kHIDUsage_PID_StateReport: sprintf (cstrName, "PID State Report"); break; + case kHIDUsage_PID_EffectPlaying: sprintf (cstrName, "Effect Playing"); break; + + case kHIDUsage_PID_DeviceControlReport: sprintf (cstrName, "PID Device Control Report"); break; + + case kHIDUsage_PID_DeviceControl: sprintf (cstrName, "PID Device Control"); break; + case kHIDUsage_PID_DC_EnableActuators: sprintf (cstrName, "Device Control Enable Actuators"); break; + case kHIDUsage_PID_DC_DisableActuators: sprintf (cstrName, "Device Control Disable Actuators"); break; + case kHIDUsage_PID_DC_StopAllEffects: sprintf (cstrName, "Device Control Stop All Effects"); break; + case kHIDUsage_PID_DC_DeviceReset: sprintf (cstrName, "Device Control Reset"); break; + case kHIDUsage_PID_DC_DevicePause: sprintf (cstrName, "Device Control Pause"); break; + case kHIDUsage_PID_DC_DeviceContinue: sprintf (cstrName, "Device Control Continue"); break; + case kHIDUsage_PID_DevicePaused: sprintf (cstrName, "Device Paused"); break; + case kHIDUsage_PID_ActuatorsEnabled: sprintf (cstrName, "Actuators Enabled"); break; + case kHIDUsage_PID_SafetySwitch: sprintf (cstrName, "Safety Switch"); break; + case kHIDUsage_PID_ActuatorOverrideSwitch: sprintf (cstrName, "Actuator Override Switch"); break; + case kHIDUsage_PID_ActuatorPower: sprintf (cstrName, "Actuator Power"); break; + case kHIDUsage_PID_StartDelay: sprintf (cstrName, "Start Delay"); break; + + case kHIDUsage_PID_ParameterBlockSize: sprintf (cstrName, "Parameter Block Size"); break; + case kHIDUsage_PID_DeviceManagedPool: sprintf (cstrName, "Device Managed Pool"); break; + case kHIDUsage_PID_SharedParameterBlocks: sprintf (cstrName, "Shared Parameter Blocks"); break; + + case kHIDUsage_PID_CreateNewEffectReport: sprintf (cstrName, "Create New Effect Report"); break; + case kHIDUsage_PID_RAM_PoolAvailable: sprintf (cstrName, "RAM Pool Available"); break; + default: sprintf (cstrName, "PID Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_Unicode: + switch (valueUsage) + { + default: sprintf (cstrName, "Unicode Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_PowerDevice: + if (((valueUsage >= 0x06) && (valueUsage <= 0x0F)) || ((valueUsage >= 0x26) && (valueUsage <= 0x2F)) || + ((valueUsage >= 0x39) && (valueUsage <= 0x3F)) || ((valueUsage >= 0x48) && (valueUsage <= 0x4F)) || + ((valueUsage >= 0x58) && (valueUsage <= 0x5F)) || (valueUsage == 0x6A) || + ((valueUsage >= 0x74) && (valueUsage <= 0xFC))) + sprintf (cstrName, "Power Device Reserved"); + else + switch (valueUsage) + { + case kHIDUsage_PD_Undefined: sprintf (cstrName, "Power Device Undefined Usage"); break; + case kHIDUsage_PD_iName: sprintf (cstrName, "Power Device Name Index"); break; + case kHIDUsage_PD_PresentStatus: sprintf (cstrName, "Power Device Present Status"); break; + case kHIDUsage_PD_ChangedStatus: sprintf (cstrName, "Power Device Changed Status"); break; + case kHIDUsage_PD_UPS: sprintf (cstrName, "Uninterruptible Power Supply"); break; + case kHIDUsage_PD_PowerSupply: sprintf (cstrName, "Power Supply"); break; + + case kHIDUsage_PD_BatterySystem: sprintf (cstrName, "Battery System Power Module"); break; + case kHIDUsage_PD_BatterySystemID: sprintf (cstrName, "Battery System ID"); break; + case kHIDUsage_PD_Battery: sprintf (cstrName, "Battery"); break; + case kHIDUsage_PD_BatteryID: sprintf (cstrName, "Battery ID"); break; + case kHIDUsage_PD_Charger: sprintf (cstrName, "Charger"); break; + case kHIDUsage_PD_ChargerID: sprintf (cstrName, "Charger ID"); break; + case kHIDUsage_PD_PowerConverter: sprintf (cstrName, "Power Converter Power Module"); break; + case kHIDUsage_PD_PowerConverterID: sprintf (cstrName, "Power Converter ID"); break; + case kHIDUsage_PD_OutletSystem: sprintf (cstrName, "Outlet System power module"); break; + case kHIDUsage_PD_OutletSystemID: sprintf (cstrName, "Outlet System ID"); break; + case kHIDUsage_PD_Input: sprintf (cstrName, "Power Device Input"); break; + case kHIDUsage_PD_InputID: sprintf (cstrName, "Power Device Input ID"); break; + case kHIDUsage_PD_Output: sprintf (cstrName, "Power Device Output"); break; + case kHIDUsage_PD_OutputID: sprintf (cstrName, "Power Device Output ID"); break; + case kHIDUsage_PD_Flow: sprintf (cstrName, "Power Device Flow"); break; + case kHIDUsage_PD_FlowID: sprintf (cstrName, "Power Device Flow ID"); break; + case kHIDUsage_PD_Outlet: sprintf (cstrName, "Power Device Outlet"); break; + case kHIDUsage_PD_OutletID: sprintf (cstrName, "Power Device Outlet ID"); break; + case kHIDUsage_PD_Gang: sprintf (cstrName, "Power Device Gang"); break; + case kHIDUsage_PD_GangID: sprintf (cstrName, "Power Device Gang ID"); break; + case kHIDUsage_PD_PowerSummary: sprintf (cstrName, "Power Device Power Summary"); break; + case kHIDUsage_PD_PowerSummaryID: sprintf (cstrName, "Power Device Power Summary ID"); break; + + case kHIDUsage_PD_Voltage: sprintf (cstrName, "Power Device Voltage"); break; + case kHIDUsage_PD_Current: sprintf (cstrName, "Power Device Current"); break; + case kHIDUsage_PD_Frequency: sprintf (cstrName, "Power Device Frequency"); break; + case kHIDUsage_PD_ApparentPower: sprintf (cstrName, "Power Device Apparent Power"); break; + case kHIDUsage_PD_ActivePower: sprintf (cstrName, "Power Device RMS Power"); break; + case kHIDUsage_PD_PercentLoad: sprintf (cstrName, "Power Device Percent Load"); break; + case kHIDUsage_PD_Temperature: sprintf (cstrName, "Power Device Temperature"); break; + case kHIDUsage_PD_Humidity: sprintf (cstrName, "Power Device Humidity"); break; + case kHIDUsage_PD_BadCount: sprintf (cstrName, "Power Device Bad Condition Count"); break; + + case kHIDUsage_PD_ConfigVoltage: sprintf (cstrName, "Power Device Nominal Voltage"); break; + case kHIDUsage_PD_ConfigCurrent: sprintf (cstrName, "Power Device Nominal Current"); break; + case kHIDUsage_PD_ConfigFrequency: sprintf (cstrName, "Power Device Nominal Frequency"); break; + case kHIDUsage_PD_ConfigApparentPower: sprintf (cstrName, "Power Device Nominal Apparent Power"); break; + case kHIDUsage_PD_ConfigActivePower: sprintf (cstrName, "Power Device Nominal RMS Power"); break; + case kHIDUsage_PD_ConfigPercentLoad: sprintf (cstrName, "Power Device Nominal Percent Load"); break; + case kHIDUsage_PD_ConfigTemperature: sprintf (cstrName, "Power Device Nominal Temperature"); break; + + case kHIDUsage_PD_ConfigHumidity: sprintf (cstrName, "Power Device Nominal Humidity"); break; + case kHIDUsage_PD_SwitchOnControl: sprintf (cstrName, "Power Device Switch On Control"); break; + case kHIDUsage_PD_SwitchOffControl: sprintf (cstrName, "Power Device Switch Off Control"); break; + case kHIDUsage_PD_ToggleControl: sprintf (cstrName, "Power Device Toogle Sequence Control"); break; + case kHIDUsage_PD_LowVoltageTransfer: sprintf (cstrName, "Power Device Min Transfer Voltage"); break; + case kHIDUsage_PD_HighVoltageTransfer: sprintf (cstrName, "Power Device Max Transfer Voltage"); break; + case kHIDUsage_PD_DelayBeforeReboot: sprintf (cstrName, "Power Device Delay Before Reboot"); break; + case kHIDUsage_PD_DelayBeforeStartup: sprintf (cstrName, "Power Device Delay Before Startup"); break; + case kHIDUsage_PD_DelayBeforeShutdown: sprintf (cstrName, "Power Device Delay Before Shutdown"); break; + case kHIDUsage_PD_Test: sprintf (cstrName, "Power Device Test Request/Result"); break; + case kHIDUsage_PD_ModuleReset: sprintf (cstrName, "Power Device Reset Request/Result"); break; + case kHIDUsage_PD_AudibleAlarmControl: sprintf (cstrName, "Power Device Audible Alarm Control"); break; + + case kHIDUsage_PD_Present: sprintf (cstrName, "Power Device Present"); break; + case kHIDUsage_PD_Good: sprintf (cstrName, "Power Device Good"); break; + case kHIDUsage_PD_InternalFailure: sprintf (cstrName, "Power Device Internal Failure"); break; + case kHIDUsage_PD_VoltageOutOfRange: sprintf (cstrName, "Power Device Voltage Out Of Range"); break; + case kHIDUsage_PD_FrequencyOutOfRange: sprintf (cstrName, "Power Device Frequency Out Of Range"); break; + case kHIDUsage_PD_Overload: sprintf (cstrName, "Power Device Overload"); break; + case kHIDUsage_PD_OverCharged: sprintf (cstrName, "Power Device Over Charged"); break; + case kHIDUsage_PD_OverTemperature: sprintf (cstrName, "Power Device Over Temperature"); break; + case kHIDUsage_PD_ShutdownRequested: sprintf (cstrName, "Power Device Shutdown Requested"); break; + + case kHIDUsage_PD_ShutdownImminent: sprintf (cstrName, "Power Device Shutdown Imminent"); break; + case kHIDUsage_PD_SwitchOnOff: sprintf (cstrName, "Power Device On/Off Switch Status"); break; + case kHIDUsage_PD_Switchable: sprintf (cstrName, "Power Device Switchable"); break; + case kHIDUsage_PD_Used: sprintf (cstrName, "Power Device Used"); break; + case kHIDUsage_PD_Boost: sprintf (cstrName, "Power Device Boosted"); break; + case kHIDUsage_PD_Buck: sprintf (cstrName, "Power Device Bucked"); break; + case kHIDUsage_PD_Initialized: sprintf (cstrName, "Power Device Initialized"); break; + case kHIDUsage_PD_Tested: sprintf (cstrName, "Power Device Tested"); break; + case kHIDUsage_PD_AwaitingPower: sprintf (cstrName, "Power Device Awaiting Power"); break; + case kHIDUsage_PD_CommunicationLost: sprintf (cstrName, "Power Device Communication Lost"); break; + + case kHIDUsage_PD_iManufacturer: sprintf (cstrName, "Power Device Manufacturer String Index"); break; + case kHIDUsage_PD_iProduct: sprintf (cstrName, "Power Device Product String Index"); break; + case kHIDUsage_PD_iserialNumber: sprintf (cstrName, "Power Device Serial Number String Index"); break; + default: sprintf (cstrName, "Power Device Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_BatterySystem: + if (((valueUsage >= 0x0A) && (valueUsage <= 0x0F)) || ((valueUsage >= 0x1E) && (valueUsage <= 0x27)) || + ((valueUsage >= 0x30) && (valueUsage <= 0x3F)) || ((valueUsage >= 0x4C) && (valueUsage <= 0x5F)) || + ((valueUsage >= 0x6C) && (valueUsage <= 0x7F)) || ((valueUsage >= 0x90) && (valueUsage <= 0xBF)) || + ((valueUsage >= 0xC3) && (valueUsage <= 0xCF)) || ((valueUsage >= 0xDD) && (valueUsage <= 0xEF)) || + ((valueUsage >= 0xF2) && (valueUsage <= 0xFF))) + sprintf (cstrName, "Power Device Reserved"); + else + switch (valueUsage) + { + case kHIDUsage_BS_Undefined: sprintf (cstrName, "Battery System Undefined"); break; + case kHIDUsage_BS_SMBBatteryMode: sprintf (cstrName, "SMB Mode"); break; + case kHIDUsage_BS_SMBBatteryStatus: sprintf (cstrName, "SMB Status"); break; + case kHIDUsage_BS_SMBAlarmWarning: sprintf (cstrName, "SMB Alarm Warning"); break; + case kHIDUsage_BS_SMBChargerMode: sprintf (cstrName, "SMB Charger Mode"); break; + case kHIDUsage_BS_SMBChargerStatus: sprintf (cstrName, "SMB Charger Status"); break; + case kHIDUsage_BS_SMBChargerSpecInfo: sprintf (cstrName, "SMB Charger Extended Status"); break; + case kHIDUsage_BS_SMBSelectorState: sprintf (cstrName, "SMB Selector State"); break; + case kHIDUsage_BS_SMBSelectorPresets: sprintf (cstrName, "SMB Selector Presets"); break; + case kHIDUsage_BS_SMBSelectorInfo: sprintf (cstrName, "SMB Selector Info"); break; + case kHIDUsage_BS_OptionalMfgFunction1: sprintf (cstrName, "Battery System Optional SMB Mfg Function 1"); break; + case kHIDUsage_BS_OptionalMfgFunction2: sprintf (cstrName, "Battery System Optional SMB Mfg Function 2"); break; + case kHIDUsage_BS_OptionalMfgFunction3: sprintf (cstrName, "Battery System Optional SMB Mfg Function 3"); break; + case kHIDUsage_BS_OptionalMfgFunction4: sprintf (cstrName, "Battery System Optional SMB Mfg Function 4"); break; + case kHIDUsage_BS_OptionalMfgFunction5: sprintf (cstrName, "Battery System Optional SMB Mfg Function 5"); break; + case kHIDUsage_BS_ConnectionToSMBus: sprintf (cstrName, "Battery System Connection To System Management Bus"); break; + case kHIDUsage_BS_OutputConnection: sprintf (cstrName, "Battery System Output Connection Status"); break; + case kHIDUsage_BS_ChargerConnection: sprintf (cstrName, "Battery System Charger Connection"); break; + case kHIDUsage_BS_BatteryInsertion: sprintf (cstrName, "Battery System Battery Insertion"); break; + case kHIDUsage_BS_Usenext: sprintf (cstrName, "Battery System Use Next"); break; + case kHIDUsage_BS_OKToUse: sprintf (cstrName, "Battery System OK To Use"); break; + case kHIDUsage_BS_BatterySupported: sprintf (cstrName, "Battery System Battery Supported"); break; + case kHIDUsage_BS_SelectorRevision: sprintf (cstrName, "Battery System Selector Revision"); break; + case kHIDUsage_BS_ChargingIndicator: sprintf (cstrName, "Battery System Charging Indicator"); break; + case kHIDUsage_BS_ManufacturerAccess: sprintf (cstrName, "Battery System Manufacturer Access"); break; + case kHIDUsage_BS_RemainingCapacityLimit: sprintf (cstrName, "Battery System Remaining Capacity Limit"); break; + case kHIDUsage_BS_RemainingTimeLimit: sprintf (cstrName, "Battery System Remaining Time Limit"); break; + case kHIDUsage_BS_AtRate: sprintf (cstrName, "Battery System At Rate..."); break; + case kHIDUsage_BS_CapacityMode: sprintf (cstrName, "Battery System Capacity Mode"); break; + case kHIDUsage_BS_BroadcastToCharger: sprintf (cstrName, "Battery System Broadcast To Charger"); break; + case kHIDUsage_BS_PrimaryBattery: sprintf (cstrName, "Battery System Primary Battery"); break; + case kHIDUsage_BS_ChargeController: sprintf (cstrName, "Battery System Charge Controller"); break; + case kHIDUsage_BS_TerminateCharge: sprintf (cstrName, "Battery System Terminate Charge"); break; + case kHIDUsage_BS_TerminateDischarge: sprintf (cstrName, "Battery System Terminate Discharge"); break; + case kHIDUsage_BS_BelowRemainingCapacityLimit: sprintf (cstrName, "Battery System Below Remaining Capacity Limit"); break; + case kHIDUsage_BS_RemainingTimeLimitExpired: sprintf (cstrName, "Battery System Remaining Time Limit Expired"); break; + case kHIDUsage_BS_Charging: sprintf (cstrName, "Battery System Charging"); break; + case kHIDUsage_BS_Discharging: sprintf (cstrName, "Battery System Discharging"); break; + case kHIDUsage_BS_FullyCharged: sprintf (cstrName, "Battery System Fully Charged"); break; + case kHIDUsage_BS_FullyDischarged: sprintf (cstrName, "Battery System Fully Discharged"); break; + case kHIDUsage_BS_ConditioningFlag: sprintf (cstrName, "Battery System Conditioning Flag"); break; + case kHIDUsage_BS_AtRateOK: sprintf (cstrName, "Battery System At Rate OK"); break; + case kHIDUsage_BS_SMBErrorCode: sprintf (cstrName, "Battery System SMB Error Code"); break; + case kHIDUsage_BS_NeedReplacement: sprintf (cstrName, "Battery System Need Replacement"); break; + case kHIDUsage_BS_AtRateTimeToFull: sprintf (cstrName, "Battery System At Rate Time To Full"); break; + case kHIDUsage_BS_AtRateTimeToEmpty: sprintf (cstrName, "Battery System At Rate Time To Empty"); break; + case kHIDUsage_BS_AverageCurrent: sprintf (cstrName, "Battery System Average Current"); break; + case kHIDUsage_BS_Maxerror: sprintf (cstrName, "Battery System Max Error"); break; + case kHIDUsage_BS_RelativeStateOfCharge: sprintf (cstrName, "Battery System Relative State Of Charge"); break; + case kHIDUsage_BS_AbsoluteStateOfCharge: sprintf (cstrName, "Battery System Absolute State Of Charge"); break; + case kHIDUsage_BS_RemainingCapacity: sprintf (cstrName, "Battery System Remaining Capacity"); break; + case kHIDUsage_BS_FullChargeCapacity: sprintf (cstrName, "Battery System Full Charge Capacity"); break; + case kHIDUsage_BS_RunTimeToEmpty: sprintf (cstrName, "Battery System Run Time To Empty"); break; + case kHIDUsage_BS_AverageTimeToEmpty: sprintf (cstrName, "Battery System Average Time To Empty"); break; + case kHIDUsage_BS_AverageTimeToFull: sprintf (cstrName, "Battery System Average Time To Full"); break; + case kHIDUsage_BS_CycleCount: sprintf (cstrName, "Battery System Cycle Count"); break; + case kHIDUsage_BS_BattPackModelLevel: sprintf (cstrName, "Battery System Batt Pack Model Level"); break; + case kHIDUsage_BS_InternalChargeController: sprintf (cstrName, "Battery System Internal Charge Controller"); break; + case kHIDUsage_BS_PrimaryBatterySupport: sprintf (cstrName, "Battery System Primary Battery Support"); break; + case kHIDUsage_BS_DesignCapacity: sprintf (cstrName, "Battery System Design Capacity"); break; + case kHIDUsage_BS_SpecificationInfo: sprintf (cstrName, "Battery System Specification Info"); break; + case kHIDUsage_BS_ManufacturerDate: sprintf (cstrName, "Battery System Manufacturer Date"); break; + case kHIDUsage_BS_SerialNumber: sprintf (cstrName, "Battery System Serial Number"); break; + case kHIDUsage_BS_iManufacturerName: sprintf (cstrName, "Battery System Manufacturer Name Index"); break; + case kHIDUsage_BS_iDevicename: sprintf (cstrName, "Battery System Device Name Index"); break; + case kHIDUsage_BS_iDeviceChemistry: sprintf (cstrName, "Battery System Device Chemistry Index"); break; + case kHIDUsage_BS_ManufacturerData: sprintf (cstrName, "Battery System Manufacturer Data"); break; + case kHIDUsage_BS_Rechargable: sprintf (cstrName, "Battery System Rechargable"); break; + case kHIDUsage_BS_WarningCapacityLimit: sprintf (cstrName, "Battery System Warning Capacity Limit"); break; + case kHIDUsage_BS_CapacityGranularity1: sprintf (cstrName, "Battery System Capacity Granularity 1"); break; + case kHIDUsage_BS_CapacityGranularity2: sprintf (cstrName, "Battery System Capacity Granularity 2"); break; + case kHIDUsage_BS_iOEMInformation: sprintf (cstrName, "Battery System OEM Information Index"); break; + case kHIDUsage_BS_InhibitCharge: sprintf (cstrName, "Battery System Inhibit Charge"); break; + case kHIDUsage_BS_EnablePolling: sprintf (cstrName, "Battery System Enable Polling"); break; + case kHIDUsage_BS_ResetToZero: sprintf (cstrName, "Battery System Reset To Zero"); break; + case kHIDUsage_BS_ACPresent: sprintf (cstrName, "Battery System AC Present"); break; + case kHIDUsage_BS_BatteryPresent: sprintf (cstrName, "Battery System Battery Present"); break; + case kHIDUsage_BS_PowerFail: sprintf (cstrName, "Battery System Power Fail"); break; + case kHIDUsage_BS_AlarmInhibited: sprintf (cstrName, "Battery System Alarm Inhibited"); break; + case kHIDUsage_BS_ThermistorUnderRange: sprintf (cstrName, "Battery System Thermistor Under Range"); break; + case kHIDUsage_BS_ThermistorHot: sprintf (cstrName, "Battery System Thermistor Hot"); break; + case kHIDUsage_BS_ThermistorCold: sprintf (cstrName, "Battery System Thermistor Cold"); break; + case kHIDUsage_BS_ThermistorOverRange: sprintf (cstrName, "Battery System Thermistor Over Range"); break; + case kHIDUsage_BS_VoltageOutOfRange: sprintf (cstrName, "Battery System Voltage Out Of Range"); break; + case kHIDUsage_BS_CurrentOutOfRange: sprintf (cstrName, "Battery System Current Out Of Range"); break; + case kHIDUsage_BS_CurrentNotRegulated: sprintf (cstrName, "Battery System Current Not Regulated"); break; + case kHIDUsage_BS_VoltageNotRegulated: sprintf (cstrName, "Battery System Voltage Not Regulated"); break; + case kHIDUsage_BS_MasterMode: sprintf (cstrName, "Battery System Master Mode"); break; + case kHIDUsage_BS_ChargerSelectorSupport: sprintf (cstrName, "Battery System Charger Support Selector"); break; + case kHIDUsage_BS_ChargerSpec: sprintf (cstrName, "attery System Charger Specification"); break; + case kHIDUsage_BS_Level2: sprintf (cstrName, "Battery System Charger Level 2"); break; + case kHIDUsage_BS_Level3: sprintf (cstrName, "Battery System Charger Level 3"); break; + default: sprintf (cstrName, "Battery System Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_AlphanumericDisplay: + switch (valueUsage) + { + default: sprintf (cstrName, "Alphanumeric Display Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_BarCodeScanner: + switch (valueUsage) + { + default: sprintf (cstrName, "Bar Code Scanner Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_Scale: + switch (valueUsage) + { + default: sprintf (cstrName, "Scale Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_CameraControl: + switch (valueUsage) + { + default: sprintf (cstrName, "Camera Control Usage 0x%lx", valueUsage); break; + } + break; + case kHIDPage_Arcade: + switch (valueUsage) + { + default: sprintf (cstrName, "Arcade Usage 0x%lx", valueUsage); break; + } + break; + default: + if (valueUsagePage > kHIDPage_VendorDefinedStart) + sprintf (cstrName, "Vendor Defined Usage 0x%lx", valueUsage); + else + sprintf (cstrName, "Page: 0x%lx, Usage: 0x%lx", valueUsagePage, valueUsage); + break; + } +} + +// --------------------------------- +// returns calibrated value given raw value passed in +// calibrated value is equal to min and max values returned by HIDGetElementValue since device list built scaled to element reported min and max values +/* +SInt32 HIDCalibrateValue (SInt32 value, pRecElement pElement) +{ + if (NULL != pElement) + { + float deviceScale = pElement->max - pElement->min; + float readScale = pElement->calMax - pElement->calMin; + if (readScale == 0) + return value; // no scaling as + else + return ((value - pElement->calMin) * deviceScale / readScale) + pElement->min; + } + else + return 0; // bad element passed in +} +*/ +// --------------------------------- +// returns scaled value given raw value passed in +// scaled value is equal to current value assumed to be in the range of element reported min and max values scaled to user min and max scaled values + +SInt32 HIDScaleValue (SInt32 value, pRecElement pElement) +{ + float deviceScale = pElement->userMax - pElement->userMin; + float readScale = pElement->max - pElement->min; + if (readScale == 0) + return value; + else + return (value - pElement->min) * deviceScale / readScale + pElement->userMin; +} + +// --------------------------------- +// convert an element type to a mask +HIDElementTypeMask HIDConvertElementTypeToMask (const long type) +{ + HIDElementTypeMask result = kHIDElementTypeAll; + + switch (type) + { + case kIOHIDElementTypeInput_Misc: + case kIOHIDElementTypeInput_Button: + case kIOHIDElementTypeInput_Axis: + case kIOHIDElementTypeInput_ScanCodes: + result = kHIDElementTypeInput; + break; + case kIOHIDElementTypeOutput: + result = kHIDElementTypeOutput; + break; + case kIOHIDElementTypeFeature: + result = kHIDElementTypeFeature; + break; + case kIOHIDElementTypeCollection: + result = kHIDElementTypeCollection; + break; + default: + result = kHIDElementTypeAll; + break; + } + return result; +} + +Boolean HIDFindDevice(const pRecDevice pSearchDevice, pRecDevice *ppFoundDevice) +{ + pRecDevice pDevice, pBestDevice = NULL; + long bestScore = 0; + + // iterate over all devices + pDevice = HIDGetFirstDevice(); + while (pDevice) + { + long deviceScore = 1; + + if (pSearchDevice->vendorID && (pSearchDevice->vendorID == pDevice->vendorID)) + { + deviceScore += 10; + if (pSearchDevice->productID && (pSearchDevice->productID == pDevice->productID)) + deviceScore += 8; + } + + if ((pSearchDevice->usagePage && (pSearchDevice->usagePage == pDevice->usagePage)) && + (pSearchDevice->usage && (pSearchDevice->usage == pDevice->usage))) + deviceScore += 9; + + if (pSearchDevice->locID && (pSearchDevice->locID == pDevice->locID)) + deviceScore += 5; + + if (deviceScore > bestScore) + { + pBestDevice = pDevice; + bestScore = deviceScore; +#if 0 // set true to output scoring informaton + printf("\n-HIDFindDevice(%ld:%ld)-I-Debug, better score: %ld.",pSearchElement->usagePage, pSearchElement->usage, score); + HIDPrintElement(pBestElement); +#endif + } + pDevice = HIDGetNextDevice(pDevice); + } + + if (NULL != pBestDevice) + { + *ppFoundDevice = pBestDevice; +#if 0 // set true to output scoring informaton + printf("\n-HIDFindDevice(%ld:%ld)-I-Debug, best score: %ld.",pSearchElement->usagePage, pSearchElement->usage, bestScore); + HIDPrintElement(pBestElement); + printf("\n"); +#endif + return true; + } + else + return false; +} + +// --------------------------------- +// find the device and element for this action +// Device: serial, vendorID, productID, location, usagePage, usage +// Element: cookie, usagePage, usage, + +Boolean HIDFindActionDeviceAndElement(const pRecDevice pSearchDevice, const pRecElement pSearchElement, + pRecDevice *ppFoundDevice, pRecElement *ppFoundElement) +{ + pRecDevice pDevice, pBestDevice = NULL; + pRecElement pElement, pBestElement = NULL; + HIDElementTypeMask hidMask = HIDConvertElementTypeToMask (pSearchElement->type); + long bestScore = 0; + + // iterate over all devices + pDevice = HIDGetFirstDevice(); + while (pDevice) + { + long deviceScore = 1; + + if (pSearchDevice->vendorID && (pSearchDevice->vendorID == pDevice->vendorID)) + { + deviceScore += 10; + if (pSearchDevice->productID && (pSearchDevice->productID == pDevice->productID)) + deviceScore += 8; + } + if ((pSearchDevice->usagePage && (pSearchDevice->usagePage == pDevice->usagePage)) && + (pSearchDevice->usage && (pSearchDevice->usage == pDevice->usage))) + deviceScore += 9; + + if (pSearchDevice->locID && (pSearchDevice->locID == pDevice->locID)) + deviceScore += 5; + + // iterate over all elements of this device + pElement = HIDGetFirstDeviceElement(pDevice, hidMask); + while (pElement) + { + long score = deviceScore; + + if ((pSearchElement->usagePage && (pSearchElement->usagePage == pElement->usagePage)) && + (pSearchElement->usage && (pSearchElement->usage == pElement->usage))) + { + score += 5; + + if (pSearchElement->cookie && (pSearchElement->cookie == pElement->cookie)) + score += 4; + } + else + score = 0; +#if 0 // set true to output scoring informaton + if (kHIDPage_KeyboardOrKeypad != pElement->usagePage) // skip keyboards here + { + printf("\n-HIDFindActionDeviceAndElement(%ld:%ld)-I-Debug, score: %ld.",pSearchElement->usagePage, pSearchElement->usage, score); + HIDPrintElement(pElement); + } +#endif + if (score > bestScore) + { + pBestDevice = pDevice; + pBestElement = pElement; + bestScore = score; +#if 0 // set true to output scoring informaton + printf("\n-HIDFindActionDeviceAndElement(%ld:%ld)-I-Debug, better score: %ld.",pSearchElement->usagePage, pSearchElement->usage, score); + HIDPrintElement(pBestElement); +#endif + } + pElement = HIDGetNextDeviceElement(pElement, hidMask); + } + pDevice = HIDGetNextDevice(pDevice); + } + + if ((NULL != pBestDevice) || (NULL != pBestElement)) + { + *ppFoundDevice = pBestDevice; + *ppFoundElement = pBestElement; +#if 0 // set true to output scoring informaton + printf("\n-HIDFindActionDeviceAndElement(%ld:%ld)-I-Debug, best score: %ld.",pSearchElement->usagePage, pSearchElement->usage, bestScore); + HIDPrintElement(pBestElement); + printf("\n"); +#endif + return true; + } + else + return false; +} + +// --------------------------------- +// find the device and element for this action +// Device: serial, vendorID, productID, location, usagePage, usage +// Element: cookie, usagePage, usage, + +Boolean HIDFindSubElement(const pRecElement pStartElement, const pRecElement pSearchElement, pRecElement *ppFoundElement) +{ + pRecElement pElement, pBestElement = NULL; + HIDElementTypeMask hidMask = HIDConvertElementTypeToMask (pSearchElement->type); + long bestScore = 0; + + if ((NULL == pStartElement) || (NULL == pSearchElement) || (NULL == ppFoundElement)) + return false; + + // iterate over all children of this element + pElement = pStartElement->pChild; + + while (pElement) + { + long score = 0; +#if 0 // set true to output searching informaton + printf("\n-HIDFindSubElement, search = {t:%.2lX, u:%.4lX:%.4lX}, match = {t:%.2lX, u:%.4lX:%.4lX, s:\"%s\"}", + pSearchElement->type, pSearchElement->usagePage, pSearchElement->usage, + pElement->type, pElement->usagePage, pElement->usage, pElement->name); + fflush(stdout); +#endif + if ((pSearchElement->usagePage && (pSearchElement->usagePage == pElement->usagePage)) && (pSearchElement->usage && (pSearchElement->usage == pElement->usage))) + { + score += 4; + if (pSearchElement->cookie && (pSearchElement->cookie == pElement->cookie)) + score += 5; + } +#if 0 // set true to output searching informaton + if (kHIDPage_KeyboardOrKeypad != pElement->usagePage) // skip keyboards here + { + printf("\n-HIDFindSubElement(%ld:%ld)-I-Debug, score: %ld.",pSearchElement->usagePage, pSearchElement->usage, score); + HIDPrintElement(pElement); + } +#endif + + if (score > bestScore) + { + pBestElement = pElement; + bestScore = score; +#if 0 // set true to output searching informaton + printf("\n-HIDFindSubElement(%ld:%ld)-I-Debug, better score: %ld.",pSearchElement->usagePage, pSearchElement->usage, score); + HIDPrintElement(pBestElement); +#endif + } + pElement = HIDGetNextDeviceElement(pElement, hidMask); + } +#if 0 // set true to output searching informaton + if (pBestElement) + { + printf("\n-HIDFindSubElement(%ld:%ld)-I-Debug, best score: %ld.",pSearchElement->usagePage, pSearchElement->usage, bestScore); + HIDPrintElement(pBestElement); + printf("\n"); + } +#endif + + *ppFoundElement = pBestElement; + return (NULL != pBestElement); +} + +// print out all of an elements information +int HIDPrintElement(const pRecElement pElement) +{ + int results; + int count; + + printf("\n"); + + if (gDepth != pElement->depth) + printf("%d",gDepth); + for (count = 0;count < pElement->depth;count++) + printf(" | "); + +#if 0 // this is verbose + results = + printf("-HIDPrintElement = {name: \"%s\", t: 0x%.2lX, u:%ld:%ld, c: %ld, min/max: %ld/%ld, scaled: %ld/%ld, size: %ld, rel: %s, wrap: %s, nonLinear: %s, preferred: %s, nullState: %s, units: %ld, exp: %ld, cal: %ld/%ld, user: %ld/%ld, depth: %ld}.", + pElement->name, // name of element (c string) + pElement->type, // the type defined by IOHIDElementType in IOHIDKeys.h + pElement->usagePage, // usage page from IOUSBHIDParser.h which defines general usage + pElement->usage, // usage within above page from IOUSBHIDParser.h which defines specific usage + (long) pElement->cookie, // unique value (within device of specific vendorID and productID) which identifies element, will NOT change + pElement->min, // reported min value possible + pElement->max, // reported max value possible + pElement->scaledMin, // reported scaled min value possible + pElement->scaledMax, // reported scaled max value possible + pElement->size, // size in bits of data return from element + pElement->relative ? "YES" : "NO", // are reports relative to last report (deltas) + pElement->wrapping ? "YES" : "NO", // does element wrap around (one value higher than max is min) + pElement->nonLinear ? "YES" : "NO", // are the values reported non-linear relative to element movement + pElement->preferredState ? "YES" : "NO",// does element have a preferred state (such as a button) + pElement->nullState ? "YES" : "NO", // does element have null state + pElement->units, // units value is reported in (not used very often) + pElement->unitExp, // exponent for units (also not used very often) + pElement->calMin, // min returned value (for calibrate call) + pElement->calMax, // max returned value + pElement->userMin, // user set min to scale to (for scale call) + pElement->userMax, // user set max + pElement->depth + ); +#else // this is brief + results = + printf("-HIDPrintElement = {t: 0x%lX, u:%ld:%ld, c: %ld, name: \"%s\", d: %ld}.", + pElement->type, // the type defined by IOHIDElementType in IOHIDKeys.h + pElement->usagePage, // usage page from IOUSBHIDParser.h which defines general usage + pElement->usage, // usage within above page from IOUSBHIDParser.h which defines specific usage + (long) pElement->cookie, // unique value (within device of specific vendorID and productID) which identifies element, will NOT change + pElement->name, // name of element (c string) + pElement->depth + ); +#endif + fflush(stdout); + return results; +} + +// return true if this is a valid device pointer +Boolean HIDIsValidDevice(const pRecDevice pSearchDevice) +{ + pRecDevice pDevice = gpDeviceList; + + while (pDevice) + { + if (pDevice == pSearchDevice) + return true; + pDevice = pDevice->pNext; + } + return false; +} + +// return true if this is a valid element pointer for this device +Boolean HIDIsValidElement(const pRecDevice pSearchDevice, const pRecElement pSearchElement) +{ + if (HIDIsValidDevice(pSearchDevice)) + { + pRecElement pRecElementTemp = HIDGetFirstDeviceElement(pSearchDevice,kHIDElementTypeAll); + while (pRecElementTemp) + { + if (pRecElementTemp == pSearchElement) + return true; + pRecElementTemp = HIDGetNextDeviceElement(pRecElementTemp,kHIDElementTypeAll); + } + } + return false; +} -- cgit v1.2.1