HID_Calibrator/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) 2014 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, |
double 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 |
CFIndex maxElements = HIDCountDeviceElements(inIOHIDDeviceRef, |
kHIDElementTypeInput); |
double *saveValueArray = (double *) calloc(maxElements, |
sizeof(double)); // 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) { |
uint32_t 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 |
double 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); |
double initialValue = saveValueArray[currElementIndex]; |
double delta = (double) (max - |
min) * |
kPercentMove * |
0.01f; |
// 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 |
if (first) { |
first = false; // no longer the first pass |
} else { |
// 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) |
if (saveValueArray) { |
free(saveValueArray); |
} |
// 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 HIDConfigureActionOfType(actionTypeMask inActionTypeMask, |
double inTimeout, |
IOHIDDeviceRef * outIOHIDDeviceRef, |
IOHIDElementRef * outIOHIDElementRef) { |
// param error? |
if (!outIOHIDDeviceRef || |
!outIOHIDElementRef) |
{ |
return (false); |
} |
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; |
IOHIDElementType elementType = 0; |
switch (inActionTypeMask) { |
case kActionTypeButton: |
{ |
elementType = kIOHIDElementTypeInput_Button; |
break; |
} |
case kActionTypeAxis: |
{ |
elementType = kIOHIDElementTypeInput_Misc; |
break; |
} |
case kActionTypeAll: |
default: |
{ |
elementType = 0; |
break; |
} |
} // switch |
// 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 |
} |
// HIDDumpDeviceInfo(tIOHIDDeviceRef); |
CFIndex count = HIDCountDeviceElementsOfType(tIOHIDDeviceRef, |
elementType); |
if (count > maxElements) { |
maxElements = count; |
} |
} |
if (!(devCount * |
maxElements)) |
{ |
return (false); |
} |
#if true |
// NSDictionary * matchDictionary = @{@(kIOHIDElementTypeKey): @(elementType)}; |
const void *keys[] = {CFSTR(kIOHIDElementTypeKey)}; |
const void *vals[] = {CFNumberCreate(kCFAllocatorDefault, |
kCFNumberIntType, |
&elementType)}; |
CFDictionaryRef matchingDict = CFDictionaryCreate(kCFAllocatorDefault, |
keys, |
vals, |
1, |
&kCFTypeDictionaryKeyCallBacks, |
&kCFTypeDictionaryValueCallBacks); |
CFRelease(vals[0]); |
#endif // if 1 |
// 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 |
// remember when we start; used to calculate timeout |
clock_t start = clock(), |
end; |
// 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 |
} |
gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, |
matchingDict, |
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 |
} |
if (elementType && |
((tIOHIDElementType != elementType))) |
{ |
continue; |
} |
uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef); |
uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef); |
uint32_t reportCount = IOHIDElementGetReportCount(tIOHIDElementRef); |
#ifdef DEBUG |
if (first) { |
HIDDumpElementInfo(tIOHIDElementRef); |
fflush(stdout); |
uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); |
uint32_t productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef); |
IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef); |
if ((0x054C == vendorID) && |
(0x0268 == productID) && |
(0x001E == (uint32_t) cookie)) |
{ |
// printf("DING!\n"); |
} |
} |
#endif // ifdef DEBUG |
#if true // work-around for IOHIDValueGetScaledValue crash |
// (when element report count > 1) |
if (reportCount > 1) { |
// printf("REPORT!\n"); |
continue; // skip reports |
} |
#endif // if 1 |
// ignore PID elements and arrays |
if ((kHIDPage_PID != usagePage) && |
((-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 false // 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 false |
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) |
if (saveValueArray) { |
free(saveValueArray); |
} |
// return device and element moved |
if (!found) { |
*outIOHIDDeviceRef = NULL; |
*outIOHIDElementRef = NULL; |
} |
CFRelease(matchingDict); |
return (found); |
} // HIDConfigureAction |
// ************************************************************************* |
// |
// 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 - true if successful |
// outIOHIDDeviceRef - the device |
// outIOHIDElementRef - the element |
// |
Boolean HIDConfigureAction(IOHIDDeviceRef * outIOHIDDeviceRef, |
IOHIDElementRef *outIOHIDElementRef, |
double inTimeout) { |
return (HIDConfigureActionOfType(kActionTypeAll, |
inTimeout, |
outIOHIDDeviceRef, |
outIOHIDElementRef)); |
} // 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) |
{ |
uint32_t vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); |
require(vendorID, |
Oops); |
uint32_t productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); |
require(productID, |
Oops); |
uint32_t 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:%d, p:%d, l:%d, p:%d, u:%d}, e:{p:%d, u:%d, c:%d}"), |
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; |
uint32_t count = sscanf(buffer, |
"d:{v:%d, p:%d, l:%d, p:%d, u:%d}, e:{p:%d, u:%d, c:%d}", |
&searchHIDInfo.device.vendorID, |
&searchHIDInfo.device.productID, |
&searchHIDInfo.device.locID, |
&searchHIDInfo.device.usagePage, |
&searchHIDInfo.device.usage, |
&searchHIDInfo.element.usagePage, |
&searchHIDInfo.element.usage, |
(IOHIDElementCookie *) &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; |
int bestScore = 0; |
CFIndex devIndex, |
devCount = CFArrayGetCount(gDeviceCFArrayRef); |
for (devIndex = 0; devIndex < devCount; devIndex++) { |
int deviceScore = 1; |
IOHIDDeviceRef tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, |
devIndex); |
if (!tIOHIDDeviceRef) { |
continue; |
} |
// match vendorID, productID (+10, +8) |
if (inSearchInfo->device.vendorID) { |
uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); |
if (vendorID) { |
if (inSearchInfo->device.vendorID == vendorID) { |
deviceScore += 10; |
if (inSearchInfo->device.productID) { |
uint32_t 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) { |
uint32_t 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; |
} |
int 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, |
IOHIDElementCookie 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 IOHIDDeviceRef, IOHIDElementRef and cookie for action |
IOHIDElementCookie 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, |
IOHIDElementCookie 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); |
if (!inHIDInfoPtr->device.usagePage || |
!inHIDInfoPtr->device.usage) |
{ |
inHIDInfoPtr->device.usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef); |
inHIDInfoPtr->device.usage = IOHIDDevice_GetPrimaryUsage(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 false // 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 IOHIDDeviceRef, IOHIDElementRef and cookie for action |
IOHIDElementCookie 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 = NULL, |
foundIOHIDDeviceRef = NULL; |
IOHIDElementRef tIOHIDElementRef = NULL, |
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 |
} |
uint32_t locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef); |
uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); |
uint32_t 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 |
} |
uint32_t vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef); |
uint32_t 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 |
Copyright © 2014 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2014-02-17