// File: HID_Queue_Utilities.c // Abstract: HID Queue 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. // //***************************************************** #include "HID_Utilities_External.h" // ================================== // private functions // creates a queue for a device, creates and opens device interface if required static IOReturn HIDCreateQueue(IOHIDDeviceRef inIOHIDDeviceRef) { IOReturn result = kIOReturnSuccess; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); // do we already have a queue? IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); if ( tIOHIDQueueRef ) { // (yes) assert( IOHIDQueueGetTypeID() == CFGetTypeID(tIOHIDQueueRef) ); } else { tIOHIDQueueRef = IOHIDQueueCreate(kCFAllocatorDefault, inIOHIDDeviceRef, kDeviceQueueSize, kIOHIDOptionsTypeNone); if ( tIOHIDQueueRef ) { // did that work IOHIDDevice_SetQueue(inIOHIDDeviceRef, tIOHIDQueueRef); result = kIOReturnSuccess; } else { HIDReportErrorNum("Failed to create queue via create", result); } } } else { HIDReportErrorNum("HID device ref does not exist for queue creation", result); } return (result); } /* HIDCreateQueue */ // --------------------------------- // returns true if queue is empty false otherwise // error if no device, empty if no queue static unsigned char HIDIsDeviceQueueEmpty(IOHIDDeviceRef inIOHIDDeviceRef) { if ( inIOHIDDeviceRef ) { // need device and queue assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); if ( tIOHIDQueueRef ) { IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO); while ( tIOHIDElementRef ) { if ( IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) { return (false); } tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO); } } else { HIDReportError("NULL device passed to HIDIsDeviceQueueEmpty."); } } else { HIDReportError("NULL device passed to HIDIsDeviceQueueEmpty."); } return (true); } /* HIDIsDeviceQueueEmpty */ // --------------------------------- // disposes and releases queue, sets queue to NULL,. // Note: will have no effect if device or queue do not exist static IOReturn HIDDisposeReleaseQueue(IOHIDDeviceRef inIOHIDDeviceRef) { IOReturn result = kIOReturnSuccess; if ( inIOHIDDeviceRef ) { IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); if ( tIOHIDQueueRef ) { // stop queue IOHIDQueueStop(tIOHIDQueueRef); // release the queue CFRelease(tIOHIDQueueRef); } } else { HIDReportError("NULL device passed to HIDDisposeReleaseQueue."); } return (result); } /* HIDDisposeReleaseQueue */ // ================================== // public functions // ---------------------------------- // queues specific element, performing any device queue set up required // queue is started and ready to return events on exit from this function int HIDQueueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { IOReturn result = kIOReturnSuccess; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); if ( inIOHIDElementRef ) { assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) ); IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); if ( !tIOHIDQueueRef ) { // if no queue create queue result = HIDCreateQueue(inIOHIDDeviceRef); if ( kIOReturnSuccess == result ) { tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); } } if ( tIOHIDQueueRef ) { // stop queue IOHIDQueueStop(tIOHIDQueueRef); // queue element if ( !IOHIDQueueContainsElement(tIOHIDQueueRef, inIOHIDElementRef) ) { IOHIDQueueAddElement(tIOHIDQueueRef, inIOHIDElementRef); } // restart queue IOHIDQueueStart(tIOHIDQueueRef); } else { HIDReportError("No queue for device passed to HIDQueueElement."); if ( kIOReturnSuccess == result ) { result = kIOReturnError; } } } else { HIDReportError("NULL element passed to HIDQueueElement."); result = kIOReturnBadArgument; } } else { HIDReportError("NULL device passed to HIDQueueElement."); result = kIOReturnBadArgument; } return (result); } /* HIDQueueElement */ // --------------------------------- // adds all elements to queue, performing any device queue set up required // queue is started and ready to return events on exit from this function int HIDQueueDevice(IOHIDDeviceRef inIOHIDDeviceRef) { IOReturn result = kIOReturnSuccess; // error checking if ( !inIOHIDDeviceRef ) { HIDReportError("Device does not exist, cannot queue device."); return (kIOReturnBadArgument); } if ( !inIOHIDDeviceRef ) { // must have interface HIDReportError("Device does not have hid device ref, cannot queue device."); return (kIOReturnError); } IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); if ( !tIOHIDQueueRef ) { // if no queue create queue result = HIDCreateQueue(inIOHIDDeviceRef); if ( kIOReturnSuccess == result ) { tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); } } if ( (kIOReturnSuccess != result) || (!tIOHIDQueueRef) ) { HIDReportErrorNum("Could not queue device due to problem creating queue.", result); if ( kIOReturnSuccess != result ) { return (result); } else { return (kIOReturnError); } } // stop queue IOHIDQueueStop(tIOHIDQueueRef); // queue element IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO); while ( tIOHIDElementRef ) { if ( !IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) { IOHIDQueueAddElement(tIOHIDQueueRef, tIOHIDElementRef); } tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO); } // restart queue IOHIDQueueStart(tIOHIDQueueRef); return (result); } /* HIDQueueDevice */ // --------------------------------- // removes element for queue, if last element in queue will release queue and closes device interface int HIDDequeueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) { IOReturn result = kIOReturnSuccess; if ( inIOHIDDeviceRef ) { assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) ); if ( inIOHIDElementRef ) { assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) ); IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); if ( tIOHIDQueueRef ) { // stop queue IOHIDQueueStop(tIOHIDQueueRef); // de-queue element if ( IOHIDQueueContainsElement(tIOHIDQueueRef, inIOHIDElementRef) ) { IOHIDQueueRemoveElement(tIOHIDQueueRef, inIOHIDElementRef); } // release device queue and close interface if queue empty if ( HIDIsDeviceQueueEmpty(inIOHIDDeviceRef) ) { result = HIDDisposeReleaseQueue(inIOHIDDeviceRef); if ( kIOReturnSuccess != result ) { HIDReportErrorNum("Failed to dispose and release queue.", result); } } else { // not empty so restart queue IOHIDQueueStart(tIOHIDQueueRef); } } else { HIDReportError("No queue for device passed to HIDDequeueElement."); if ( kIOReturnSuccess == result ) { result = kIOReturnError; } } } else { HIDReportError("NULL element passed to HIDDequeueElement."); result = kIOReturnBadArgument; } } else { HIDReportError("NULL device passed to HIDDequeueElement."); result = kIOReturnBadArgument; } return (result); } /* HIDDequeueElement */ // --------------------------------- // completely removes all elements from queue and releases queue and closes device interface // does not release device interfaces, application must call ReleaseHIDDeviceList on exit int HIDDequeueDevice(IOHIDDeviceRef inIOHIDDeviceRef) { IOReturn result = kIOReturnSuccess; // error checking if ( !inIOHIDDeviceRef ) { HIDReportError("Device does not exist, cannot queue device."); return (kIOReturnBadArgument); } if ( !inIOHIDDeviceRef ) { // must have interface HIDReportError("Device does not have hid device ref, cannot queue device."); return (kIOReturnError); } IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); if ( tIOHIDQueueRef ) { // iterate through elements and if queued, remove IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO); while ( tIOHIDElementRef ) { // de-queue element if ( IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) { IOHIDQueueRemoveElement(tIOHIDQueueRef, tIOHIDElementRef); } tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO); } // ensure queue is disposed and released result = HIDDisposeReleaseQueue(inIOHIDDeviceRef); if ( kIOReturnSuccess != result ) { HIDReportErrorNum("Failed to dispose and release queue.", result); } } else { HIDReportError("No queue for device passed to HIDDequeueElement."); if ( kIOReturnSuccess == result ) { result = kIOReturnError; } } return (result); } /* HIDDequeueDevice */ // --------------------------------- // releases all device queues for quit or rebuild (must be called) // does not release device interfaces, application must call ReleaseHIDDeviceList on exit IOReturn HIDReleaseAllDeviceQueues(void) { IOReturn result = kIOReturnSuccess; IOHIDDeviceRef tIOHIDDeviceRef = HIDGetFirstDevice(); while ( tIOHIDDeviceRef ) { result = HIDDequeueDevice(tIOHIDDeviceRef); if ( kIOReturnSuccess != result ) { HIDReportErrorNum("Could not dequeue device.", result); } tIOHIDDeviceRef = HIDGetNextDevice(tIOHIDDeviceRef); } return (result); } /* HIDReleaseAllDeviceQueues */ // --------------------------------- // Get the next event in the queue for a device // elements or entire device should be queued prior to calling this with HIDQueueElement or HIDQueueDevice // returns true if an event is avialable for the element and fills out *pHIDEvent structure, returns false otherwise // Note: kIOReturnUnderrun returned from getNextEvent indicates an empty queue not an error condition // Note: application should pass in a pointer to a IOHIDEventStruct cast to a void (for CFM compatibility) unsigned char HIDGetEvent(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDValueRef *pIOHIDValueRef) { if ( inIOHIDDeviceRef ) { IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef); if ( tIOHIDQueueRef ) { if ( pIOHIDValueRef ) { *pIOHIDValueRef = IOHIDQueueCopyNextValueWithTimeout(tIOHIDQueueRef, 0.0); if ( *pIOHIDValueRef ) { return (true); } } } else { HIDReportError("Could not get HID event, hid queue reference does not exist."); } } else { HIDReportError("Could not get HID event, device does not exist."); } return (false); // did not get event } /* HIDGetEvent */