Sample Code
OSX Driver and Kext Samples/ HID_Utilities/ HID_Utilities/ HID_Config_Utilities.c/
// File: HID_Config_Utilities.c // Abstract: Implementation of the HID configuration utilities // Version: 2.0 // // Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple // 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 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. // // Copyright (C) 2009 Apple Inc. All Rights Reserved. // //***************************************************** #define LOG_SCORING 0 #include <stdlib.h> // malloc #include <time.h> // clock #include <AssertMacros.h> #include "HID_Utilities_External.h" // --------------------------------- // polls single device's elements for a change greater than kPercentMove. Times out after given time // returns 1 and pointer to element if found // returns 0 and NULL for both parameters if not found unsigned char HIDConfigureSingleDeviceAction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float timeout) { if ( !inIOHIDDeviceRef ) { return (0); } if ( 0 == HIDHaveDeviceList() ) { // if we do not have a device list return (0); // return 0 } Boolean found = FALSE; // build list of device and elements to save current values int maxElements = HIDCountDeviceElements(inIOHIDDeviceRef, kHIDElementTypeInput); int *saveValueArray = (int *) calloc( maxElements, sizeof(int) ); // 2D array to save values // store initial values on first pass / compare to initial value on subsequent passes Boolean first = TRUE; // get all the elements from this device CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); // if that worked... if ( elementCFArrayRef ) { clock_t start = clock(), end; // poll all devices and elements while ( !found ) { int currElementIndex = 0; CFIndex idx, cnt = CFArrayGetCount(elementCFArrayRef); for ( idx = 0; idx < cnt; idx++ ) { *outIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); if ( !*outIOHIDElementRef ) { continue; } // is this an input element? IOHIDElementType type = IOHIDElementGetType(*outIOHIDElementRef); switch ( type ) { // these types are inputs case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: case kIOHIDElementTypeInput_Axis: case kIOHIDElementTypeInput_ScanCodes: default: { break; } case kIOHIDElementTypeOutput: case kIOHIDElementTypeFeature: case kIOHIDElementTypeCollection: { *outIOHIDElementRef = NULL; // these types are not ( Skip them ) break; } } /* switch */ if ( !*outIOHIDElementRef ) { continue; // skip this element } // get this elements current value int value = 0; // default value is zero IOHIDValueRef tIOHIDValueRef; IOReturn ioReturn = IOHIDDeviceGetValue(inIOHIDDeviceRef, *outIOHIDElementRef, &tIOHIDValueRef); if ( kIOReturnSuccess == ioReturn ) { value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical); } if ( first ) { saveValueArray[currElementIndex] = value; } else { CFIndex min = IOHIDElementGetLogicalMin(*outIOHIDElementRef); CFIndex max = IOHIDElementGetLogicalMax(*outIOHIDElementRef); int initialValue = saveValueArray[currElementIndex]; int delta = (float)(max - min) * kPercentMove * 0.01; // is the new value within +/- delta of the initial value? if ( ( (initialValue + delta) < value ) || ( (initialValue - delta) > value ) ) { found = 1; // ( yes! ) mark as found break; } } // if ( first ) currElementIndex++; // bump element index } // next idx first = FALSE; // no longer the first pass // are we done? end = clock(); double secs = (double)(end - start) / CLOCKS_PER_SEC; if ( secs > timeout ) { break; // ( yes ) timeout } } // while ( !found ) CFRelease(elementCFArrayRef); } // if ( elementCFArrayRef ) // return device and element moved if ( found ) { return (1); } else { *outIOHIDElementRef = NULL; return (0); } } // HIDConfigureSingleDeviceAction //************************************************************************* // // HIDConfigureAction( outIOHIDDeviceRef, outIOHIDElementRef, inTimeout ) // // Purpose: polls all devices and elements for a change greater than kPercentMove. // Times out after given time returns 1 and pointer to device and element // if found; returns 0 and NULL for both parameters if not found // // Inputs: outIOHIDDeviceRef - address where to store the device // outIOHIDElementRef - address where to store the element // inTimeout - the timeout // Returns: Boolean - if successful // outIOHIDDeviceRef - the device // outIOHIDElementRef - the element // Boolean HIDConfigureAction(IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float inTimeout) { // param error? if ( !outIOHIDDeviceRef || !outIOHIDElementRef ) { return (0); } if ( !gDeviceCFArrayRef ) { // if we do not have a device list // and we can't build another list if ( !HIDBuildDeviceList(0, 0) || !gDeviceCFArrayRef ) { return (FALSE); // bail } } IOHIDDeviceRef tIOHIDDeviceRef; IOHIDElementRef tIOHIDElementRef; // remember when we start; used to calculate timeout clock_t start = clock(), end; // determine the maximum number of elements CFIndex maxElements = 0; CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef); for ( devIndex = 0; devIndex < devCount; devIndex++ ) { tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); if ( !tIOHIDDeviceRef ) { continue; // skip this one } CFIndex count = HIDCountDeviceElements(tIOHIDDeviceRef, kHIDElementTypeInput); if ( count > maxElements ) { maxElements = count; } } // allocate an array of int's in which to store devCount * maxElements values double *saveValueArray = (double *) calloc( devCount * maxElements, sizeof(double) ); // clear 2D array to save values // on first pass store initial values / compare current values to initial values on subsequent passes Boolean found = FALSE, first = TRUE; while ( !found ) { double maxDeltaPercent = 0; // we want to find the one that moves the most ( percentage wise ) for ( devIndex = 0; devIndex < devCount; devIndex++ ) { tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); if ( !tIOHIDDeviceRef ) { continue; // skip this one } #ifdef DEBUG long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); #endif gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); if ( gElementCFArrayRef ) { CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef); for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex); if ( !tIOHIDElementRef ) { continue; } IOHIDElementType tIOHIDElementType = IOHIDElementGetType(tIOHIDElementRef); // only care about inputs (no outputs or features) if ( tIOHIDElementType <= kIOHIDElementTypeInput_ScanCodes ) { if ( IOHIDElementIsArray(tIOHIDElementRef) ) { //printf( "ARRAY!\n" ); continue; // skip array elements } uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); uint32_t reportCount = IOHIDElementGetReportCount(tIOHIDElementRef); #ifdef DEBUG if ( first ) { IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); printf("%s, dev: {ref:%p, ven: 0x%08lX, pro: 0x%08lX}, ele: {ref:%p, cookie: %p, usage:%04lX:%08lX}\n", __PRETTY_FUNCTION__, tIOHIDDeviceRef, vendorID, productID, tIOHIDElementRef, cookie, (long unsigned int) usagePage, (long unsigned int) usage); fflush(stdout); if ( (0x054C == vendorID) && (0x0268 == productID) && (0x001E == (UInt32) cookie) ) { //printf( "DING!\n" ); } } #endif #if 1 // work-around for IOHIDValueGetScaledValue crash (when element report count > 1) if ( reportCount > 1 ) { //printf( "REPORT!\n" ); continue; // skip reports } #endif // ignore PID elements and arrays if ( (kHIDPage_PID != usagePage) && (((uint32_t)-1) != usage) ) { // get this elements current value double value = 0.0; // default value is zero IOHIDValueRef tIOHIDValueRef; IOReturn ioReturn = IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &tIOHIDValueRef); if ( kIOReturnSuccess == ioReturn ) { value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical); } if ( first ) { saveValueArray[(devIndex * maxElements) + eleIndex] = value; } else { double initialValue = saveValueArray[(devIndex * maxElements) + eleIndex]; CFIndex valueMin = IOHIDElementGetPhysicalMin(tIOHIDElementRef); CFIndex valueMax = IOHIDElementGetPhysicalMax(tIOHIDElementRef); double deltaPercent = fabs( (initialValue - value) * 100.0 / (valueMax - valueMin) ); #if 0 // debug code useful to dump out value info for specific (vendorID, productID, usagePage and usage) device if ( !first ) { // Device: 0x13b6a0 = { Logitech Inc. - WingMan Force 3D, vendorID: 0x046D, productID: 0xC283, // usage: 0x0001:0x0004, "Generic Desktop Joystick" if ( (vendorID == 0x046D) && (productID == 0xC283) ) { if ( (kHIDPage_GenericDesktop == usagePage) && (kHIDUsage_GD_Rz == usage) ) { printf("initial: %6.2f, value: %6.2f, diff: %6.2f, delta percent: %6.2f!\n", initialValue, value, fabs(initialValue - value), deltaPercent); } } } deltaPercent = 0.0; #endif if ( deltaPercent >= kPercentMove ) { found = TRUE; if ( deltaPercent > maxDeltaPercent ) { maxDeltaPercent = deltaPercent; *outIOHIDDeviceRef = tIOHIDDeviceRef; *outIOHIDElementRef = tIOHIDElementRef; } break; } } // if first } // if usage } // if type } // for elements... CFRelease(gElementCFArrayRef); gElementCFArrayRef = NULL; } // if ( gElementCFArrayRef ) if ( found ) { // HIDDumpElementInfo( tIOHIDElementRef ); break; // DONE! } } // for devices first = FALSE; // no longer the first pass // are we done? end = clock(); double secs = (double)(end - start) / CLOCKS_PER_SEC; if ( secs > inTimeout ) { break; // ( yes ) timeout } } // while ( !found ) // return device and element moved if ( !found ) { *outIOHIDDeviceRef = NULL; *outIOHIDElementRef = NULL; } return (found); } // HIDConfigureAction //************************************************************************* // // HIDSaveElementPref( inKeyCFStringRef, inAppCFStringRef, inIOHIDDeviceRef, inIOHIDElementRef ) // // Purpose: Save the device & element values into the specified key in the specified applications preferences // // Inputs: inKeyCFStringRef - the preference key // inAppCFStringRef - the application identifier // inIOHIDDeviceRef - the device // inIOHIDElementRef - the element // Returns: Boolean - if successful // Boolean HIDSaveElementPref(const CFStringRef inKeyCFStringRef, CFStringRef inAppCFStringRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { Boolean success = FALSE; if ( inKeyCFStringRef && inAppCFStringRef && inIOHIDDeviceRef && inIOHIDElementRef ) { long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); require(vendorID, Oops); long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); require(productID, Oops); long locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); require(locID, Oops); uint32_t usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); uint32_t usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); if ( !usagePage || !usage ) { usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef); usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef); } require(usagePage && usage, Oops); uint32_t usagePageE = IOHIDElementGetUsagePage(inIOHIDElementRef); uint32_t usageE = IOHIDElementGetUsage(inIOHIDElementRef); IOHIDElementCookie eleCookie = IOHIDElementGetCookie(inIOHIDElementRef); CFStringRef prefCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("d:{v:%ld, p:%ld, l:%ld, p:%ld, u:%ld}, e:{p:%ld, u:%ld, c:%ld}"), vendorID, productID, locID, usagePage, usage, usagePageE, usageE, eleCookie); if ( prefCFStringRef ) { CFPreferencesSetAppValue(inKeyCFStringRef, prefCFStringRef, inAppCFStringRef); CFRelease(prefCFStringRef); success = TRUE; } } Oops: ; return (success); } // HIDSaveElementPref //************************************************************************* // // HIDRestoreElementPref( inKeyCFStringRef, inAppCFStringRef, outIOHIDDeviceRef, outIOHIDElementRef ) // // Purpose: Find the specified preference in the specified application // // Inputs: inKeyCFStringRef - the preference key // inAppCFStringRef - the application identifier // outIOHIDDeviceRef - address where to restore the device // outIOHIDElementRef - address where to restore the element // Returns: Boolean - if successful // outIOHIDDeviceRef - the device // outIOHIDElementRef - the element // Boolean HIDRestoreElementPref(CFStringRef inKeyCFStringRef, CFStringRef inAppCFStringRef, IOHIDDeviceRef * outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef) { Boolean found = FALSE; if ( inKeyCFStringRef && inAppCFStringRef && outIOHIDDeviceRef && outIOHIDElementRef ) { CFPropertyListRef prefCFPropertyListRef = CFPreferencesCopyAppValue(inKeyCFStringRef, inAppCFStringRef); if ( prefCFPropertyListRef ) { if ( CFStringGetTypeID() == CFGetTypeID(prefCFPropertyListRef) ) { char buffer[256]; if ( CFStringGetCString( (CFStringRef) prefCFPropertyListRef, buffer, sizeof(buffer), kCFStringEncodingUTF8 ) ) { HID_info_rec searchHIDInfo; int count = sscanf(buffer, "d:{v:%d, p:%d, l:%d, p:%d, u:%d}, e:{p:%d, u:%d, c:%ld}", &searchHIDInfo.device.vendorID, &searchHIDInfo.device.productID, &searchHIDInfo.device.locID, &searchHIDInfo.device.usagePage, &searchHIDInfo.device.usage, &searchHIDInfo.element.usagePage, &searchHIDInfo.element.usage, (long *) &searchHIDInfo.element.cookie); if ( 8 == count ) { // if we found all eight parametersā¦ // and can find a device & element that matches theseā¦ if ( HIDFindDeviceAndElement(&searchHIDInfo, outIOHIDDeviceRef, outIOHIDElementRef) ) { found = TRUE; } } } } else { // We found the entry with this key but it's the wrong type; delete it. CFPreferencesSetAppValue(inKeyCFStringRef, NULL, inAppCFStringRef); (void) CFPreferencesAppSynchronize(inAppCFStringRef); } CFRelease(prefCFPropertyListRef); } } return (found); } // HIDRestoreElementPref //************************************************************************* // // HIDFindDeviceAndElement( inSearchInfo, outFoundDevice, outFoundElement ) // // Purpose: find the closest matching device and element for this action // // Notes: matches device: serial, vendorID, productID, location, inUsagePage, usage // matches element: cookie, inUsagePage, usage, // // Inputs: inSearchInfo - the device & element info we searching for // outFoundDevice - the address of the best matching device // outFoundElement - the address of the best matching element // // Returns: Boolean - TRUE if we find a match // outFoundDevice - the best matching device // outFoundElement - the best matching element // Boolean HIDFindDeviceAndElement(const HID_info_rec *inSearchInfo, IOHIDDeviceRef *outFoundDevice, IOHIDElementRef *outFoundElement) { Boolean result = FALSE; IOHIDDeviceRef bestIOHIDDeviceRef = NULL; IOHIDElementRef bestIOHIDElementRef = NULL; long bestScore = 0; CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef); for ( devIndex = 0; devIndex < devCount; devIndex++ ) { long deviceScore = 1; IOHIDDeviceRef tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex); if ( !tIOHIDDeviceRef ) { continue; } // match vendorID, productID (+10, +8) if ( inSearchInfo->device.vendorID ) { long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); if ( vendorID ) { if ( inSearchInfo->device.vendorID == vendorID ) { deviceScore += 10; if ( inSearchInfo->device.productID ) { long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); if ( productID ) { if ( inSearchInfo->device.productID == productID ) { deviceScore += 8; } // if ( inSearchInfo->device.productID == productID ) } // if ( productID ) } // if ( inSearchInfo->device.productID ) } // if (inSearchInfo->device.vendorID == vendorID) } // if vendorID } // if search->device.vendorID // match usagePage & usage (+9) if ( inSearchInfo->device.usagePage && inSearchInfo->device.usage ) { uint32_t usagePage = IOHIDDevice_GetUsagePage(tIOHIDDeviceRef) ; uint32_t usage = IOHIDDevice_GetUsage(tIOHIDDeviceRef); if ( !usagePage || !usage ) { usagePage = IOHIDDevice_GetPrimaryUsagePage(tIOHIDDeviceRef); usage = IOHIDDevice_GetPrimaryUsage(tIOHIDDeviceRef); } if ( usagePage ) { if ( inSearchInfo->device.usagePage == usagePage ) { if ( usage ) { if ( inSearchInfo->device.usage == usage ) { deviceScore += 9; } // if ( inSearchInfo->usage == usage ) } // if ( usage ) } // if ( inSearchInfo->usagePage == usagePage ) } // if ( usagePage ) } // if ( inSearchInfo->usagePage && inSearchInfo->usage ) // match location ID (+5) if ( inSearchInfo->device.locID ) { long locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); if ( locID ) { if ( inSearchInfo->device.locID == locID ) { deviceScore += 5; } } } // iterate over all elements of this device gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, 0); if ( gElementCFArrayRef ) { CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef); for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) { IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex); if ( !tIOHIDElementRef ) { continue; } long score = deviceScore; // match usage page, usage & cookie if ( inSearchInfo->element.usagePage && inSearchInfo->element.usage ) { uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); if ( inSearchInfo->element.usagePage == usagePage ) { uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); if ( inSearchInfo->element.usage == usage ) { score += 5; IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); if ( inSearchInfo->element.cookie == cookie ) { score += 4; } // cookies match } else { score = 0; } // usages match } else { score = 0; } // usage pages match } // if ( search usage page & usage ) #if LOG_SCORING if ( kHIDPage_KeyboardOrKeypad != tElementRef->usagePage ) { // skip keyboards here printf("%s: ( %ld:%ld )-I-Debug, score: %ld\t", __PRETTY_FUNCTION__, inSearchInfo->element.usagePage, inSearchInfo->element.usage, score); HIDPrintElement(tIOHIDElementRef); } #endif // LOG_SCORING if ( score > bestScore ) { bestIOHIDDeviceRef = tIOHIDDeviceRef; bestIOHIDElementRef = tIOHIDElementRef; bestScore = score; #if LOG_SCORING printf("%s: ( %ld:%ld )-I-Debug, better score: %ld\t", __PRETTY_FUNCTION__, inSearchInfo->element.usagePage, inSearchInfo->element.usage, score); HIDPrintElement(bestIOHIDElementRef); #endif // LOG_SCORING } } // for elements... CFRelease(gElementCFArrayRef); gElementCFArrayRef = NULL; } // if ( gElementCFArrayRef ) } // for ( devIndex = 0; devIndex < devCount; devIndex++ ) if ( bestIOHIDDeviceRef || bestIOHIDElementRef ) { *outFoundDevice = bestIOHIDDeviceRef; *outFoundElement = bestIOHIDElementRef; #if LOG_SCORING printf("%s: ( %ld:%ld )-I-Debug, best score: %ld\t", __PRETTY_FUNCTION__, inSearchInfo->element.usagePage, inSearchInfo->element.usage, bestScore); HIDPrintElement(bestIOHIDElementRef); #endif // LOG_SCORING result = TRUE; } return (result); } // HIDFindDeviceAndElement // --------------------------------- // takes input records, save required info // assume file is open and at correct position. // will always write to file (if file exists) size of HID_info_rec, even if device and or element is bad void HIDSaveElementConfig(FILE *fileRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef, int actionCookie) { // must save: // actionCookie // Device: serial,vendorID, productID, location, usagePage, usage // Element: cookie, usagePage, usage, HID_info_rec hidInfoRec; HIDSetElementConfig(&hidInfoRec, inIOHIDDeviceRef, inIOHIDElementRef, actionCookie); // write to file if ( fileRef ) { fwrite( (void *)&hidInfoRec, sizeof(HID_info_rec), 1, fileRef ); } } // HIDSaveElementConfig // --------------------------------- // take file, read one record (assume file position is correct and file is open) // search for matching device // return pDevice, pElement and cookie for action int HIDRestoreElementConfig(FILE *fileRef, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef) { // Device: serial,vendorID, productID, location, usagePage, usage // Element: cookie, usagePage, usage, HID_info_rec hidInfoRec; fread( (void *) &hidInfoRec, 1, sizeof(HID_info_rec), fileRef ); return ( HIDGetElementConfig(&hidInfoRec, outIOHIDDeviceRef, outIOHIDElementRef) ); } // HIDRestoreElementConfig // --------------------------------- // Set up a config record for saving // takes an input records, returns record user can save as they want // Note: the save rec must be pre-allocated by the calling app and will be filled out void HIDSetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef, int actionCookie) { // must save: // actionCookie // Device: serial,vendorID, productID, location, usagePage, usage // Element: cookie, usagePage, usage, inHIDInfoPtr->actionCookie = actionCookie; // device // need to add serial number when I have a test case if ( inIOHIDDeviceRef && inIOHIDElementRef ) { inHIDInfoPtr->device.vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); inHIDInfoPtr->device.productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); inHIDInfoPtr->device.locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); inHIDInfoPtr->device.usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); inHIDInfoPtr->device.usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); inHIDInfoPtr->element.usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); inHIDInfoPtr->element.usage = IOHIDElementGetUsage(inIOHIDElementRef); inHIDInfoPtr->element.minReport = IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef); inHIDInfoPtr->element.maxReport = IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef); inHIDInfoPtr->element.cookie = IOHIDElementGetCookie(inIOHIDElementRef); } else { inHIDInfoPtr->device.vendorID = 0; inHIDInfoPtr->device.productID = 0; inHIDInfoPtr->device.locID = 0; inHIDInfoPtr->device.usage = 0; inHIDInfoPtr->device.usagePage = 0; inHIDInfoPtr->element.usagePage = 0; inHIDInfoPtr->element.usage = 0; inHIDInfoPtr->element.minReport = 0; inHIDInfoPtr->element.maxReport = 0; inHIDInfoPtr->element.cookie = 0; } } // HIDSetElementConfig // --------------------------------- #if 0 // debug utility function to dump config record void HIDDumpConfig(HID_info_ptr inHIDInfoPtr) { printf( "Config Record for action: %d\n\t vendor: %d product: %d location: %d\n\t usage: %d usagePage: %d\n\t element.usagePage: %d element.usage: %d\n\t minReport: %d maxReport: %d\n\t cookie: %d\n", inHIDInfoPtr->actionCookie, inHIDInfoPtr->device.vendorID, inHIDInfoPtr->device.productID, inHIDInfoPtr->locID, inHIDInfoPtr->usage, inHIDInfoPtr->usagePage, inHIDInfoPtr->element.usagePage, inHIDInfoPtr->element.usage, inHIDInfoPtr->minReport, inHIDInfoPtr->maxReport, inHIDInfoPtr->cookie); } // HIDDumpConfig #endif // 0 // --------------------------------- // Get matching element from config record // takes a pre-allocated and filled out config record // search for matching device // return pDevice, pElement and cookie for action int HIDGetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef) { if ( !inHIDInfoPtr->device.locID && !inHIDInfoPtr->device.vendorID && !inHIDInfoPtr->device.productID && !inHIDInfoPtr->device.usage && !inHIDInfoPtr->device.usagePage ) // { // // early out *outIOHIDDeviceRef = NULL; *outIOHIDElementRef = NULL; return (inHIDInfoPtr->actionCookie); } IOHIDDeviceRef tIOHIDDeviceRef, foundIOHIDDeviceRef = NULL; IOHIDElementRef tIOHIDElementRef, foundIOHIDElementRef = NULL; CFIndex devIdx, devCnt, idx, cnt; // compare to current device list for matches // look for device if ( inHIDInfoPtr->device.locID && inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID ) { // look for specific device // type plug in to same port devCnt = CFArrayGetCount(gDeviceCFArrayRef); for ( devIdx = 0; devIdx < devCnt; devIdx++ ) { tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIdx); if ( !tIOHIDDeviceRef ) { continue; // skip this device } long locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); if ( (inHIDInfoPtr->device.locID == locID) && (inHIDInfoPtr->device.vendorID == vendorID) && (inHIDInfoPtr->device.productID == productID) ) { foundIOHIDDeviceRef = tIOHIDDeviceRef; } if ( foundIOHIDDeviceRef ) { break; } } // next devIdx if ( foundIOHIDDeviceRef ) { CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); if ( elementCFArrayRef ) { cnt = CFArrayGetCount(elementCFArrayRef); for ( idx = 0; idx < cnt; idx++ ) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); if ( !tIOHIDElementRef ) { continue; // skip this element } IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); if ( inHIDInfoPtr->element.cookie == cookie ) { foundIOHIDElementRef = tIOHIDElementRef; } if ( foundIOHIDElementRef ) { break; } } if ( !foundIOHIDElementRef ) { cnt = CFArrayGetCount(elementCFArrayRef); for ( idx = 0; idx < cnt; idx++ ) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); if ( !tIOHIDElementRef ) { continue; // skip this element } uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); if ( (inHIDInfoPtr->element.usage == usage) && (inHIDInfoPtr->element.usagePage == usagePage) ) { foundIOHIDElementRef = tIOHIDElementRef; } if ( foundIOHIDElementRef ) { break; } } // next idx } // if ( !foundIOHIDElementRef ) if ( foundIOHIDElementRef ) { // if same device // setup the calibration IOHIDElement_SetupCalibration(tIOHIDElementRef); IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, inHIDInfoPtr->element.minReport); IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, inHIDInfoPtr->element.maxReport); } CFRelease(elementCFArrayRef); } // if ( elementCFArrayRef ) } // if ( foundIOHIDDeviceRef ) // if we have not found a match, look at just vendor and product if ( (!foundIOHIDDeviceRef) && (inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID) ) { devCnt = CFArrayGetCount(gDeviceCFArrayRef); for ( devIdx = 0; devIdx < devCnt; devIdx++ ) { tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIdx); if ( !tIOHIDDeviceRef ) { continue; // skip this device } long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); if ( (inHIDInfoPtr->device.vendorID == vendorID) && (inHIDInfoPtr->device.productID == productID) ) { foundIOHIDDeviceRef = tIOHIDDeviceRef; } if ( foundIOHIDDeviceRef ) { break; } } // match elements by cookie since same device type if ( foundIOHIDDeviceRef ) { CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); if ( elementCFArrayRef ) { cnt = CFArrayGetCount(elementCFArrayRef); for ( idx = 0; idx < cnt; idx++ ) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); if ( !tIOHIDElementRef ) { continue; // skip this element } IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); if ( inHIDInfoPtr->element.cookie == cookie ) { foundIOHIDElementRef = tIOHIDElementRef; } if ( foundIOHIDElementRef ) { break; } } // if no cookie match (should NOT occur) match on usage if ( !foundIOHIDElementRef ) { cnt = CFArrayGetCount(elementCFArrayRef); for ( idx = 0; idx < cnt; idx++ ) { tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx); if ( !tIOHIDElementRef ) { continue; // skip this element } uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); if ( (inHIDInfoPtr->element.usage == usage) && (inHIDInfoPtr->element.usagePage == usagePage) ) { foundIOHIDElementRef = tIOHIDElementRef; } if ( foundIOHIDElementRef ) { break; } } // next idx } // if ( !foundIOHIDElementRef ) if ( foundIOHIDElementRef ) { // if same device // setup the calibration IOHIDElement_SetupCalibration(tIOHIDElementRef); IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, inHIDInfoPtr->element.minReport); IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, inHIDInfoPtr->element.maxReport); } CFRelease(elementCFArrayRef); } // if ( elementCFArrayRef ) } // if ( foundIOHIDDeviceRef ) } // if ( device not found & vendorID & productID ) } // if ( inHIDInfoPtr->locID && inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID ) // can't find matching device return NULL, do not return first device if ( (!foundIOHIDDeviceRef) || (!foundIOHIDElementRef) ) { // no HID device *outIOHIDDeviceRef = NULL; *outIOHIDElementRef = NULL; return (inHIDInfoPtr->actionCookie); } else { // HID device *outIOHIDDeviceRef = foundIOHIDDeviceRef; *outIOHIDElementRef = foundIOHIDElementRef; return (inHIDInfoPtr->actionCookie); } } // HIDGetElementConfig
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials