HID_Utilities.c
// File: HID_Utilities.c |
// Abstract: Implementation of the HID 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. |
// |
//*************************************************** |
#pragma mark - includes & imports |
//----------------------------------------------------- |
#include <AssertMacros.h> |
#include "HID_Utilities_External.h" |
//*************************************************** |
#pragma mark - typedefs, enums, defines, etc. |
//----------------------------------------------------- |
#define FAKE_MISSING_NAMES 1 // set this to true while debuging to get more explicit element names; false for |
// the |
// generic ones |
#define kPercentMove 10 // precent of overall range a element must move to register |
#define kNameKeyCFStringRef CFSTR("Name") // dictionary key |
//*************************************************** |
#pragma mark - local ( static ) function prototypes |
//----------------------------------------------------- |
static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context); |
static CFComparisonResult CFDeviceArrayComparatorFunction(const void *val1, const void *val2, void *context); |
static CFMutableDictionaryRef hu_SetUpMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage); |
//*************************************************** |
#pragma mark - exported globals |
//----------------------------------------------------- |
IOHIDManagerRef gIOHIDManagerRef = NULL; |
CFMutableArrayRef gDeviceCFArrayRef = NULL; |
CFIndex gDeviceIndex; |
CFArrayRef gElementCFArrayRef = NULL; |
//*************************************************** |
#pragma mark - local ( static ) globals |
//----------------------------------------------------- |
//*************************************************** |
#pragma mark - exported function implementations |
//----------------------------------------------------- |
//************************************************************************* |
// |
// HIDBuildMultiDeviceList( inUsagePages, inUsages, inNumDeviceTypes ) |
// |
// Purpose: builds list of devices with elements |
// |
// Inputs: inUsagePages - inNumDeviceTypes sized array of matching usage pages |
// inUsages - inNumDeviceTypes sized array of matching usages |
// inNumDeviceTypes - number of usage pages & usages |
// |
// Returns: Boolean - if successful |
// |
Boolean HIDBuildMultiDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes) { |
Boolean result = FALSE; // assume failure ( pessimist! ) |
Boolean first = (!gIOHIDManagerRef); // not yet created? |
if ( first ) { |
// create the manager |
gIOHIDManagerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); |
} |
if ( gIOHIDManagerRef ) { |
CFMutableArrayRef hidMatchingCFMutableArrayRef = NULL; |
if ( inUsages && inUsagePages && inNumDeviceTypes ) { |
hidMatchingCFMutableArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); |
if ( hidMatchingCFMutableArrayRef ) { |
int idx; |
for ( idx = 0; idx < inNumDeviceTypes; idx++ ) { // for all usage and usage page types |
// Set up matching dictionary. returns NULL on error. |
CFMutableDictionaryRef hidMatchingCFDictRef = hu_SetUpMatchingDictionary(inUsagePages[idx], inUsages[idx]); |
if ( hidMatchingCFDictRef ) { |
CFArrayAppendValue(hidMatchingCFMutableArrayRef, (void *) hidMatchingCFDictRef); |
CFRelease(hidMatchingCFDictRef); |
} else { |
fprintf(stderr, "%s: Couldn’t create a matching dictionary.", __PRETTY_FUNCTION__); |
} |
} |
} else { |
fprintf(stderr, "%s: Couldn’t create a matching array.", __PRETTY_FUNCTION__); |
} |
} |
// set it for IOHIDManager to use to match against |
IOHIDManagerSetDeviceMatchingMultiple(gIOHIDManagerRef, hidMatchingCFMutableArrayRef); |
if ( hidMatchingCFMutableArrayRef ) { |
CFRelease(hidMatchingCFMutableArrayRef); |
} |
if ( first ) { |
// open it |
IOReturn tIOReturn = IOHIDManagerOpen(gIOHIDManagerRef, kIOHIDOptionsTypeNone); |
if ( kIOReturnSuccess != tIOReturn ) { |
fprintf(stderr, "%s: Couldn’t open IOHIDManager.", __PRETTY_FUNCTION__); |
goto Oops; |
} |
} |
HIDRebuildDevices(); |
result = TRUE; |
} else { |
fprintf(stderr, "%s: Couldn’t create a IOHIDManager.", __PRETTY_FUNCTION__); |
} |
Oops: ; |
return (result); |
} // HIDBuildMultiDeviceList |
/************************************************************************* |
* |
* HIDBuildDeviceList( inUsagePage, inUsage ) |
* |
* Purpose: builds list of devices with elements |
* |
* Notes: same as above but this uses a single inUsagePage and usage |
* allocates memory and captures devices |
* list is allocated 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 ) |
* |
* Inputs: inUsagePage - usage page |
* inUsage - usages |
* |
* Returns: Boolean - if successful |
*/ |
Boolean HIDBuildDeviceList(UInt32 inUsagePage, UInt32 inUsage) { |
return ( HIDBuildMultiDeviceList(&inUsagePage, &inUsage, 1) ); // call HIDBuildMultiDeviceList with a single usage |
} |
/************************************************************************* |
* |
* HIDUpdateDeviceList( inUsagePages, inUsages, inNumDeviceTypes ) |
* |
* Purpose: updates the current device list for any new/removed devices |
* |
* Notes: if this is called before HIDBuildDeviceList then it functions like HIDBuildMultiDeviceList |
* inUsagePage & inUsage are each a inNumDeviceTypes sized array of matching usage and usage pages |
* |
* Inputs: inUsagePages - inNumDeviceTypes sized array of matching usage pages |
* inUsages - inNumDeviceTypes sized array of matching usages |
* inNumDeviceTypes - number of usage pages & usages |
* |
* Returns: Boolean - TRUE if the device config changed |
*/ |
Boolean HIDUpdateDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes) { |
return ( HIDBuildMultiDeviceList(inUsagePages, inUsages, inNumDeviceTypes) ); |
} |
/************************************************************************* |
* |
* HIDReleaseDeviceList( void ) |
* |
* Purpose: release list built by above functions |
* |
* Notes: 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 |
* |
* Inputs: none |
* |
* Returns: none |
*/ |
void HIDReleaseDeviceList(void) { |
if ( gDeviceCFArrayRef ) { |
CFRelease(gDeviceCFArrayRef); |
gDeviceCFArrayRef = NULL; |
} |
} // HIDReleaseDeviceList |
/************************************************************************* |
* |
* HIDHaveDeviceList( void ) |
* |
* Purpose: does a device list exist? |
* |
* Inputs: none |
* |
* Returns: Boolean - TRUE if we have previously built a device list |
*/ |
Boolean HIDHaveDeviceList(void) { |
return (NULL != gDeviceCFArrayRef); |
} |
//************************************************************************* |
// |
// HIDRebuildDevices( ) |
// |
// Purpose: rebuilds the (internal) list of IOHIDDevices |
// |
// Inputs: none |
// |
// Returns: none |
// |
void HIDRebuildDevices(void) { |
// get the set of devices from the IOHID manager |
CFSetRef devCFSetRef = IOHIDManagerCopyDevices(gIOHIDManagerRef); |
if ( devCFSetRef ) { |
// if the existing array isn't empty... |
if ( gDeviceCFArrayRef ) { |
// release it |
CFRelease(gDeviceCFArrayRef); |
} |
// create an empty array |
gDeviceCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); |
// now copy the set to the array |
CFSetApplyFunction(devCFSetRef, CFSetApplierFunctionCopyToCFArray, (void *) gDeviceCFArrayRef); |
// now sort the array by location ID's |
CFIndex cnt = CFArrayGetCount(gDeviceCFArrayRef); |
CFArraySortValues(gDeviceCFArrayRef, CFRangeMake(0, cnt), CFDeviceArrayComparatorFunction, NULL); |
// and release the set we copied from the IOHID manager |
CFRelease(devCFSetRef); |
} |
} // HIDRebuildDevices |
// --------------------------------- |
// how many HID devices have been found |
// returns 0 if no device list exist |
UInt32 HIDCountDevices(void) { |
return ( CFArrayGetCount(gDeviceCFArrayRef) ); |
} |
// --------------------------------- |
// how many elements does a specific device have |
// returns 0 if device is invlaid or NULL |
UInt32 HIDCountDeviceElements(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask) { |
int count = 0; |
if ( inIOHIDDeviceRef ) { |
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); |
gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); |
if ( gElementCFArrayRef ) { |
CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef); |
for ( idx = 0; idx < cnt; idx++ ) { |
IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, idx); |
if ( !tIOHIDElementRef ) { |
continue; |
} |
IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); |
switch ( type ) { |
case kIOHIDElementTypeInput_Misc: |
case kIOHIDElementTypeInput_Button: |
case kIOHIDElementTypeInput_Axis: |
case kIOHIDElementTypeInput_ScanCodes: |
{ |
if ( typeMask & kHIDElementTypeInput ) { |
count++; |
} |
break; |
} |
case kIOHIDElementTypeOutput: |
{ |
if ( typeMask & kHIDElementTypeOutput ) { |
count++; |
} |
break; |
} |
case kIOHIDElementTypeFeature: |
{ |
if ( typeMask & kHIDElementTypeFeature ) { |
count++; |
} |
break; |
} |
case kIOHIDElementTypeCollection: |
{ |
if ( typeMask & kHIDElementTypeCollection ) { |
count++; |
} |
break; |
} |
default: { |
break; |
} |
} // switch ( type ) |
} // next idx |
CFRelease(gElementCFArrayRef); |
gElementCFArrayRef = NULL; |
} // if ( gElementCFArrayRef ) |
} // if ( inIOHIDDeviceRef ) |
return (count); |
} /* HIDCountDeviceElements */ |
// --------------------------------- |
// get the first device in the device list |
// returns NULL if no list exists or it's empty |
IOHIDDeviceRef HIDGetFirstDevice(void) { |
IOHIDDeviceRef result = NULL; |
gDeviceIndex = 0; |
if ( gDeviceCFArrayRef ) { |
CFIndex count = CFArrayGetCount(gDeviceCFArrayRef); |
if ( (gDeviceIndex >= 0) && (gDeviceIndex < count) ) { |
result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, gDeviceIndex); |
} |
} |
return (result); |
} /* HIDGetFirstDevice */ |
// --------------------------------- |
// get next device in list given current device as parameter |
// returns NULL if end of list |
IOHIDDeviceRef HIDGetNextDevice(IOHIDDeviceRef inIOHIDDeviceRef) { |
IOHIDDeviceRef result = NULL; |
if ( gDeviceCFArrayRef && inIOHIDDeviceRef ) { |
CFIndex idx, cnt = CFArrayGetCount(gDeviceCFArrayRef); |
// quick case to verify the current device index is valid for current device |
if ( (gDeviceIndex >= 0) && (gDeviceIndex < cnt) ) { |
result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, gDeviceIndex); |
if ( result && (result == inIOHIDDeviceRef) ) { |
result = NULL; |
gDeviceIndex++; // bump index |
} else { |
// previous index was invalid; |
gDeviceIndex = -1; |
// search for current device's index |
for ( idx = 0; idx < cnt; idx++ ) { |
result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, idx); |
if ( (result) && (result == inIOHIDDeviceRef) ) { |
gDeviceIndex = idx + 1; // found valid index; bump to next one |
break; |
} |
} |
result = NULL; |
} |
if ( (gDeviceIndex >= 0) && (gDeviceIndex < cnt) ) { |
result = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, gDeviceIndex); |
} |
} // if valid index |
} // if ( gDeviceCFArrayRef && inIOHIDDeviceRef ) |
return (result); |
} /* HIDGetNextDevice */ |
// --------------------------------- |
// get the first element of device passed in as parameter |
// returns NULL if no list exists or device does not exists or is NULL |
IOHIDElementRef HIDGetFirstDeviceElement(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask) { |
IOHIDElementRef result = NULL; |
if ( inIOHIDDeviceRef ) { |
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); |
gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); |
if ( gElementCFArrayRef ) { |
CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef); |
for ( idx = 0; idx < cnt; idx++ ) { |
IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, idx); |
if ( !tIOHIDElementRef ) { |
continue; |
} |
IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); |
switch ( type ) { |
case kIOHIDElementTypeInput_Misc: |
case kIOHIDElementTypeInput_Button: |
case kIOHIDElementTypeInput_Axis: |
case kIOHIDElementTypeInput_ScanCodes: |
{ |
if ( typeMask & kHIDElementTypeInput ) { |
result = tIOHIDElementRef; |
} |
break; |
} |
case kIOHIDElementTypeOutput: |
{ |
if ( typeMask & kHIDElementTypeOutput ) { |
result = tIOHIDElementRef; |
} |
break; |
} |
case kIOHIDElementTypeFeature: |
{ |
if ( typeMask & kHIDElementTypeFeature ) { |
result = tIOHIDElementRef; |
} |
break; |
} |
case kIOHIDElementTypeCollection: |
{ |
if ( typeMask & kHIDElementTypeCollection ) { |
result = tIOHIDElementRef; |
} |
break; |
} |
default: { |
break; |
} |
} // switch ( type ) |
if ( result ) { |
break; // DONE! |
} |
} // next idx |
CFRelease(gElementCFArrayRef); |
gElementCFArrayRef = NULL; |
} // if ( gElementCFArrayRef ) |
} // if ( inIOHIDDeviceRef ) |
return (result); |
} /* HIDGetFirstDeviceElement */ |
// --------------------------------- |
// 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 |
IOHIDElementRef HIDGetNextDeviceElement(IOHIDElementRef inIOHIDElementRef, HIDElementTypeMask typeMask) { |
IOHIDElementRef result = NULL; |
if ( inIOHIDElementRef ) { |
assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) ); |
IOHIDDeviceRef tIOHIDDeviceRef = IOHIDElementGetDevice(inIOHIDElementRef); |
if ( tIOHIDDeviceRef ) { |
Boolean found = FALSE; |
gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone); |
if ( gElementCFArrayRef ) { |
CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef); |
for ( idx = 0; idx < cnt; idx++ ) { |
IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, idx); |
if ( !tIOHIDElementRef ) { |
continue; |
} |
if ( !found ) { |
if ( inIOHIDElementRef == tIOHIDElementRef ) { |
found = TRUE; |
} |
continue; // next element |
} else { |
// we've found the current element; now find the next one of the right type |
IOHIDElementType type = IOHIDElementGetType(tIOHIDElementRef); |
switch ( type ) { |
case kIOHIDElementTypeInput_Misc: |
case kIOHIDElementTypeInput_Button: |
case kIOHIDElementTypeInput_Axis: |
case kIOHIDElementTypeInput_ScanCodes: |
{ |
if ( typeMask & kHIDElementTypeInput ) { |
result = tIOHIDElementRef; |
} |
break; |
} |
case kIOHIDElementTypeOutput: |
{ |
if ( typeMask & kHIDElementTypeOutput ) { |
result = tIOHIDElementRef; |
} |
break; |
} |
case kIOHIDElementTypeFeature: |
{ |
if ( typeMask & kHIDElementTypeFeature ) { |
result = tIOHIDElementRef; |
} |
break; |
} |
case kIOHIDElementTypeCollection: |
{ |
if ( typeMask & kHIDElementTypeCollection ) { |
result = tIOHIDElementRef; |
} |
break; |
} |
default: { |
break; |
} |
} // switch ( type ) |
if ( result ) { |
break; // DONE! |
} |
} // if ( !found ) |
} // next idx |
CFRelease(gElementCFArrayRef); |
gElementCFArrayRef = NULL; |
} // if ( gElementCFArrayRef ) |
} // if ( inIOHIDDeviceRef ) |
} // if ( inIOHIDElementRef ) |
return (result); |
} /* HIDGetNextDeviceElement */ |
#if 0 |
// --------------------------------- |
// 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 |
IOHIDElementRef HIDGetPreviousDeviceElement(IOHIDElementRef pElement, HIDElementTypeMask typeMask) { |
IOHIDElementRef pPreviousElement = pElement->pPrevious; |
// walk back up tree to element prior |
while ( pPreviousElement && !HIDMatchElementTypeMask(pPreviousElement->type, typeMask) ) { |
pElement = pPreviousElement; // look at previous element |
pPreviousElement = pElement->pPrevious; |
} |
return (pPreviousElement); // return this element |
} /* HIDGetPreviousDeviceElement */ |
#endif |
// utility routine to dump device info |
void HIDDumpDeviceInfo(IOHIDDeviceRef inIOHIDDeviceRef) { |
char cstring[256]; |
printf("Device: %p = { ", inIOHIDDeviceRef); |
char manufacturer[256] = ""; // name of manufacturer |
CFStringRef tCFStringRef = IOHIDDevice_GetManufacturer(inIOHIDDeviceRef); |
if ( tCFStringRef ) { |
verify( CFStringGetCString(tCFStringRef, manufacturer, sizeof(manufacturer), kCFStringEncodingUTF8) ); |
} |
char product[256] = ""; // name of product |
tCFStringRef = IOHIDDevice_GetProduct(inIOHIDDeviceRef); |
if ( tCFStringRef ) { |
verify( CFStringGetCString(tCFStringRef, product, sizeof(product), kCFStringEncodingUTF8) ); |
} |
printf("%s - %s, ", manufacturer, product); |
long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef); |
if ( vendorID ) { |
#if 1 |
printf(" vendorID: 0x%04lX, ", vendorID); |
#else |
if ( HIDGetVendorNameFromVendorID(vendorID, cstring) ) { |
printf(" vendorID: 0x%04lX (\"%s\"), ", vendorID, cstring); |
} else { |
printf(" vendorID: 0x%04lX, ", vendorID); |
} |
#endif |
} |
long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef); |
if ( productID ) { |
#if 1 |
printf(" productID: 0x%04lX, ", productID); |
#else |
if ( HIDGetProductNameFromVendorProductID(vendorID, productID, cstring) ) { |
printf(" productID: 0x%04lX (\"%s\"), ", productID, cstring); |
} else { |
printf(" productID: 0x%04lX, ", productID); |
} |
#endif |
} |
uint32_t usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef); |
uint32_t usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef); |
if ( !usagePage || !usage ) { |
usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef); |
usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef); |
} |
printf("usage: 0x%04lX:0x%04lX, ", (long unsigned int) usagePage, (long unsigned int) usage); |
#if 1 |
tCFStringRef = HIDCopyUsageName(usagePage, usage); |
if ( tCFStringRef ) { |
verify( CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8) ); |
printf("\"%s\", ", cstring); |
CFRelease(tCFStringRef); |
} |
#endif |
#if 1 |
tCFStringRef = IOHIDDevice_GetTransport(inIOHIDDeviceRef); |
if ( tCFStringRef ) { |
verify( CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8) ); |
printf("Transport: \"%s\", ", cstring); |
} |
long vendorIDSource = IOHIDDevice_GetVendorIDSource(inIOHIDDeviceRef); |
if ( vendorIDSource ) { |
printf("VendorIDSource: %ld, ", vendorIDSource); |
} |
long version = IOHIDDevice_GetVersionNumber(inIOHIDDeviceRef); |
if ( version ) { |
printf("version: %ld, ", version); |
} |
tCFStringRef = IOHIDDevice_GetSerialNumber(inIOHIDDeviceRef); |
if ( tCFStringRef ) { |
verify( CFStringGetCString(tCFStringRef, cstring, sizeof(cstring), kCFStringEncodingUTF8) ); |
printf("SerialNumber: \"%s\", ", cstring); |
} |
long country = IOHIDDevice_GetCountryCode(inIOHIDDeviceRef); |
if ( country ) { |
printf("CountryCode: %ld, ", country); |
} |
long locationID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef); |
if ( locationID ) { |
printf("locationID: 0x%08lX, ", locationID); |
} |
#if 0 |
CFArrayRef pairs = IOHIDDevice_GetUsagePairs(inIOHIDDeviceRef); |
if ( pairs ) { |
CFIndex idx, cnt = CFArrayGetCount(pairs); |
for ( idx = 0; idx < cnt; idx++ ) { |
const void *pair = CFArrayGetValueAtIndex(pairs, idx); |
CFShow(pair); |
} |
} |
#endif |
long maxInputReportSize = IOHIDDevice_GetMaxInputReportSize(inIOHIDDeviceRef); |
if ( maxInputReportSize ) { |
printf("MaxInputReportSize: %ld, ", maxInputReportSize); |
} |
long maxOutputReportSize = IOHIDDevice_GetMaxOutputReportSize(inIOHIDDeviceRef); |
if ( maxOutputReportSize ) { |
printf("MaxOutputReportSize: %ld, ", maxOutputReportSize); |
} |
long maxFeatureReportSize = IOHIDDevice_GetMaxFeatureReportSize(inIOHIDDeviceRef); |
if ( maxFeatureReportSize ) { |
printf("MaxFeatureReportSize: %ld, ", maxOutputReportSize); |
} |
long reportInterval = IOHIDDevice_GetReportInterval(inIOHIDDeviceRef); |
if ( reportInterval ) { |
printf("ReportInterval: %ld, ", reportInterval); |
} |
IOHIDQueueRef queueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); |
if ( queueRef ) { |
printf("queue: %p, ", queueRef); |
} |
IOHIDTransactionRef transactionRef = IOHIDDevice_GetTransaction(inIOHIDDeviceRef); |
if ( transactionRef ) { |
printf("transaction: %p, ", transactionRef); |
} |
#endif |
printf("}\n"); |
fflush(stdout); |
} // HIDDumpDeviceInfo |
// utility routine to dump element info |
void HIDDumpElementInfo(IOHIDElementRef inIOHIDElementRef) { |
if ( inIOHIDElementRef ) { |
printf(" Element: %p = { ", inIOHIDElementRef); |
#if 0 |
IOHIDDeviceRef tIOHIDDeviceRef = IOHIDElementGetDevice(inIOHIDElementRef); |
printf("Device: %p, ", tIOHIDDeviceRef); |
#endif |
IOHIDElementRef parentIOHIDElementRef = IOHIDElementGetParent(inIOHIDElementRef); |
printf("parent: %p, ", parentIOHIDElementRef); |
#if 0 |
CFArrayRef childrenCFArrayRef = IOHIDElementGetChildren(inIOHIDElementRef); |
printf("children: %p: { ", childrenCFArrayRef); |
fflush(stdout); |
CFShow(childrenCFArrayRef); |
fflush(stdout); |
printf(" }, "); |
#endif |
IOHIDElementCookie tIOHIDElementCookie = IOHIDElementGetCookie(inIOHIDElementRef); |
printf("cookie: %p, ", tIOHIDElementCookie); |
IOHIDElementType tIOHIDElementType = IOHIDElementGetType(inIOHIDElementRef); |
switch ( tIOHIDElementType ) { |
case kIOHIDElementTypeInput_Misc: |
{ |
printf("type: Misc, "); |
break; |
} |
case kIOHIDElementTypeInput_Button: |
{ |
printf("type: Button, "); |
break; |
} |
case kIOHIDElementTypeInput_Axis: |
{ |
printf("type: Axis, "); |
break; |
} |
case kIOHIDElementTypeInput_ScanCodes: |
{ |
printf("type: ScanCodes, "); |
break; |
} |
case kIOHIDElementTypeOutput: |
{ |
printf("type: Output, "); |
break; |
} |
case kIOHIDElementTypeFeature: |
{ |
printf("type: Feature, "); |
break; |
} |
case kIOHIDElementTypeCollection: |
{ |
IOHIDElementCollectionType tIOHIDElementCollectionType = IOHIDElementGetCollectionType(inIOHIDElementRef); |
switch ( tIOHIDElementCollectionType ) { |
case kIOHIDElementCollectionTypePhysical: |
{ |
printf("type: Physical Collection, "); |
break; |
} |
case kIOHIDElementCollectionTypeApplication: |
{ |
printf("type: Application Collection, "); |
break; |
} |
case kIOHIDElementCollectionTypeLogical: |
{ |
printf("type: Logical Collection, "); |
break; |
} |
case kIOHIDElementCollectionTypeReport: |
{ |
printf("type: Report Collection, "); |
break; |
} |
case kIOHIDElementCollectionTypeNamedArray: |
{ |
printf("type: Named Array Collection, "); |
break; |
} |
case kIOHIDElementCollectionTypeUsageSwitch: |
{ |
printf("type: Usage Switch Collection, "); |
break; |
} |
case kIOHIDElementCollectionTypeUsageModifier: |
{ |
printf("type: Usage Modifier Collection, "); |
break; |
} |
default: |
{ |
printf("type: %p Collection, ", (void *) tIOHIDElementCollectionType); |
break; |
} |
} // switch |
break; |
} |
default: |
{ |
printf("type: %p, ", (void *) tIOHIDElementType); |
break; |
} |
} /* switch */ |
uint32_t usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef); |
uint32_t usage = IOHIDElementGetUsage(inIOHIDElementRef); |
printf("usage: 0x%04lX:0x%04lX, ", (long unsigned int) usagePage, (long unsigned int) usage); |
#if 1 |
CFStringRef tCFStringRef = HIDCopyUsageName(usagePage, usage); |
if ( tCFStringRef ) { |
char usageString[256] = ""; |
verify( CFStringGetCString(tCFStringRef, usageString, sizeof(usageString), kCFStringEncodingUTF8) ); |
printf("\"%s\", ", usageString); |
CFRelease(tCFStringRef); |
} |
#endif |
CFStringRef nameCFStringRef = IOHIDElementGetName(inIOHIDElementRef); |
char buffer[256]; |
if ( nameCFStringRef && CFStringGetCString(nameCFStringRef, buffer, sizeof(buffer), kCFStringEncodingUTF8) ) { |
printf("name: %s, ", buffer); |
} |
uint32_t reportID = IOHIDElementGetReportID(inIOHIDElementRef); |
uint32_t reportSize = IOHIDElementGetReportSize(inIOHIDElementRef); |
uint32_t reportCount = IOHIDElementGetReportCount(inIOHIDElementRef); |
printf("report: { ID: %lu, Size: %lu, Count: %lu }, ", |
(long unsigned int) reportID, (long unsigned int) reportSize, (long unsigned int) reportCount); |
uint32_t unit = IOHIDElementGetUnit(inIOHIDElementRef); |
uint32_t unitExp = IOHIDElementGetUnitExponent(inIOHIDElementRef); |
if ( unit || unitExp ) { |
printf("unit: %lu * 10^%lu, ", (long unsigned int) unit, (long unsigned int) unitExp); |
} |
CFIndex logicalMin = IOHIDElementGetLogicalMin(inIOHIDElementRef); |
CFIndex logicalMax = IOHIDElementGetLogicalMax(inIOHIDElementRef); |
if ( logicalMin != logicalMax ) { |
printf("logical: {min: %ld, max: %ld}, ", logicalMin, logicalMax); |
} |
CFIndex physicalMin = IOHIDElementGetPhysicalMin(inIOHIDElementRef); |
CFIndex physicalMax = IOHIDElementGetPhysicalMax(inIOHIDElementRef); |
if ( physicalMin != physicalMax ) { |
printf("physical: {min: %ld, max: %ld}, ", physicalMin, physicalMax); |
} |
Boolean isVirtual = IOHIDElementIsVirtual(inIOHIDElementRef); |
if ( isVirtual ) { |
printf("isVirtual, "); |
} |
Boolean isRelative = IOHIDElementIsRelative(inIOHIDElementRef); |
if ( isRelative ) { |
printf("isRelative, "); |
} |
Boolean isWrapping = IOHIDElementIsWrapping(inIOHIDElementRef); |
if ( isWrapping ) { |
printf("isWrapping, "); |
} |
Boolean isArray = IOHIDElementIsArray(inIOHIDElementRef); |
if ( isArray ) { |
printf("isArray, "); |
} |
Boolean isNonLinear = IOHIDElementIsNonLinear(inIOHIDElementRef); |
if ( isNonLinear ) { |
printf("isNonLinear, "); |
} |
Boolean hasPreferredState = IOHIDElementHasPreferredState(inIOHIDElementRef); |
if ( hasPreferredState ) { |
printf("hasPreferredState, "); |
} |
Boolean hasNullState = IOHIDElementHasNullState(inIOHIDElementRef); |
if ( hasNullState ) { |
printf("hasNullState, "); |
} |
printf(" }\n"); |
} |
} // HIDDumpElementInfo |
void HIDDumpElementCalibrationInfo(IOHIDElementRef inIOHIDElementRef) { |
printf(" Element: %p = { ", inIOHIDElementRef); |
CFIndex calMin = IOHIDElement_GetCalibrationMin(inIOHIDElementRef); |
CFIndex calMax = IOHIDElement_GetCalibrationMax(inIOHIDElementRef); |
printf("cal: {min: %ld, max: %ld}, ", calMin, calMax); |
CFIndex satMin = IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef); |
CFIndex satMax = IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef); |
printf("sat: {min: %ld, max: %ld}, ", satMin, satMax); |
CFIndex deadMin = IOHIDElement_GetCalibrationDeadZoneMin(inIOHIDElementRef); |
CFIndex deadMax = IOHIDElement_GetCalibrationDeadZoneMax(inIOHIDElementRef); |
printf("dead: {min: %ld, max: %ld}, ", deadMin, deadMax); |
double_t granularity = IOHIDElement_GetCalibrationGranularity(inIOHIDElementRef); |
printf("granularity: %6.2f }\n", granularity); |
} // HIDDumpElementCalibrationInfo |
//*************************************************** |
#pragma mark - local ( static ) function implementations |
//----------------------------------------------------- |
//************************************************************************* |
// |
// CFSetApplierFunctionCopyToCFArray( value, context ) |
// |
// Purpose: CFSetApplierFunction to copy the CFSet to a CFArray |
// |
// Notes: called one time for each item in the CFSet |
// |
// Inputs: value - the current element of the CFSet |
// context - the CFMutableArrayRef we're adding the CFSet elements to |
// |
// Returns: nothing |
// |
static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context) { |
// printf( "%s: 0x%08lX\n", __PRETTY_FUNCTION__, (long unsigned int) value ); |
CFArrayAppendValue( (CFMutableArrayRef) context, value ); |
} // CFSetApplierFunctionCopyToCFArray |
// --------------------------------- |
// used to sort the CFDevice array after copying it from the (unordered) (CF)set. |
// we compare based on the location ID's since they're consistant (across boots & launches). |
// |
static CFComparisonResult CFDeviceArrayComparatorFunction(const void *val1, const void *val2, void *context) { |
#pragma unused( context ) |
CFComparisonResult result = kCFCompareEqualTo; |
long loc1 = IOHIDDevice_GetLocationID( (IOHIDDeviceRef) val1 ); |
long loc2 = IOHIDDevice_GetLocationID( (IOHIDDeviceRef) val2 ); |
if ( loc1 < loc2 ) { |
result = kCFCompareLessThan; |
} else if ( loc1 > loc2 ) { |
result = kCFCompareGreaterThan; |
} |
return (result); |
} // CFDeviceArrayComparatorFunction |
//************************************************************************* |
// |
// hu_SetUpMatchingDictionary( inUsagePage, inUsage ) |
// |
// Purpose: builds a matching dictionary based on usage page and usage |
// |
// Notes: Only called by HIDBuildMultiDeviceList |
// |
// Inputs: inUsagePage - usage page |
// inUsage - usages |
// |
// Returns: CFMutableDictionaryRef - the matching dictionary |
// |
static CFMutableDictionaryRef hu_SetUpMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage) { |
// create a dictionary to add usage page/usages to |
CFMutableDictionaryRef refHIDMatchDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, |
0, |
&kCFTypeDictionaryKeyCallBacks, |
&kCFTypeDictionaryValueCallBacks); |
if ( refHIDMatchDictionary ) { |
if ( inUsagePage ) { |
// Add key for device type to refine the matching dictionary. |
CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage); |
if ( pageCFNumberRef ) { |
CFDictionarySetValue(refHIDMatchDictionary, |
CFSTR(kIOHIDPrimaryUsagePageKey), pageCFNumberRef); |
CFRelease(pageCFNumberRef); |
// note: the usage is only valid if the usage page is also defined |
if ( inUsage ) { |
CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage); |
if ( usageCFNumberRef ) { |
CFDictionarySetValue(refHIDMatchDictionary, |
CFSTR(kIOHIDPrimaryUsageKey), usageCFNumberRef); |
CFRelease(usageCFNumberRef); |
} else { |
fprintf(stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__); |
} |
} |
} else { |
fprintf(stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__); |
} |
} |
} else { |
fprintf(stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__); |
} |
return (refHIDMatchDictionary); |
} // hu_SetUpMatchingDictionary |
Copyright © 2009 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2009-10-01