/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include "IOHIDElement.h" #include "IOHIDDevice.h" #include "IOHIDEventQueue.h" #include "IOHIDParserPriv.h" #define IsRange() \ (_usageMin != _usageMax) #define IsArrayElement(flags) \ ((flags & kHIDDataArrayBit) == kHIDDataArray) #define IsArrayReportHandler(reportHandler) \ (reportHandler == _arrayReportHandler) #define GetArrayItemIndex(sel) \ (sel - _logicalMin) #define GetArrayItemSel(index) \ (index + _logicalMin) #define super OSObject OSDefineMetaClassAndStructors( IOHIDElement, OSObject ) //--------------------------------------------------------------------------- // bool IOHIDElement::init( IOHIDDevice * owner, IOHIDElementType type ) { if ( ( super::init() != true ) || ( owner == 0 ) ) { return false; } _owner = owner; _type = type; _reportSize = 0; _reportCount = 1; return true; } //--------------------------------------------------------------------------- // IOHIDElement * IOHIDElement::buttonElement( IOHIDDevice * owner, IOHIDElementType type, HIDButtonCapabilitiesPtr button, IOHIDElement * parent ) { IOHIDElement * element = new IOHIDElement; // Check arguments and call init(). if ( ( button == 0 ) || ( element == 0 ) || ( element->init( owner, type ) == false ) ) { if ( element ) element->release(); return 0; } // Set HID properties. element->_flags = button->bitField; element->_reportStartBit = button->startBit; element->_reportID = button->reportID; element->_usagePage = button->usagePage; element->_rangeIndex = 0; element->_logicalMin = element->_physicalMin = 0; element->_logicalMax = element->_physicalMax = 1; if ( button->isRange ) { element->_usageMin = button->u.range.usageMin; element->_usageMax = button->u.range.usageMax; } else { element->_usageMin = button->u.notRange.usage; element->_usageMax = button->u.notRange.usage; } if (IsArrayElement(element->_flags)) { // RY: Hack to gain the logical min/max for // elements. element->_logicalMin = element->_physicalMin = button->u.notRange.reserved2; element->_logicalMax = element->_physicalMax = button->u.notRange.reserved3; // RY: Hack to gain the report size and report // count for Array type elements. This works // out because array elements do not make use // of the unit and unit exponent. Plus, this // keeps us binary compatible. Appropriate // changes have been made to the HIDParser to // support this. element->_reportBits = button->unitExponent; element->_reportCount = button->units; } else { element->_reportBits = 1; element->_units = button->units; element->_unitExponent = button->unitExponent; } // Register with owner and parent, then spawn sub-elements. if ( ( parent && ( parent->addChildElement(element) == false ) ) || ( owner->registerElement( element, &element->_cookie ) == false ) || ( element->createSubElements() == false )) { element->release(); element = 0; } return element; } //--------------------------------------------------------------------------- // IOHIDElement * IOHIDElement::valueElement( IOHIDDevice * owner, IOHIDElementType type, HIDValueCapabilitiesPtr value, IOHIDElement * parent ) { IOHIDElement * element = new IOHIDElement; // Check arguments and call init(). if ( ( value == 0 ) || ( element == 0 ) || ( element->init( owner, type ) == false ) ) { if ( element ) element->release(); return 0; } // Set HID properties. element->_flags = value->bitField; element->_reportBits = value->bitSize; element->_reportCount = value->reportCount; element->_reportStartBit = value->startBit; element->_reportID = value->reportID; element->_usagePage = value->usagePage; element->_logicalMin = value->logicalMin; element->_logicalMax = value->logicalMax; element->_physicalMin = value->physicalMin; element->_physicalMax = value->physicalMax; element->_units = value->units; element->_unitExponent = value->unitExponent; element->_rangeIndex = 0; if ( value->isRange ) { element->_usageMin = value->u.range.usageMin; element->_usageMax = value->u.range.usageMax; } else { element->_usageMin = value->u.notRange.usage; element->_usageMax = value->u.notRange.usage; } // Register with owner and parent, then spawn sub-elements. if ( ( owner->registerElement( element, &element->_cookie ) == false ) || ( ( parent && ( parent->addChildElement(element) == false ) ) ) || ( element->createSubElements() == false )) { element->release(); element = 0; } return element; } //--------------------------------------------------------------------------- // IOHIDElement * IOHIDElement::collectionElement( IOHIDDevice * owner, IOHIDElementType type, HIDCollectionNodePtr collection, IOHIDElement * parent ) { IOHIDElement * element = new IOHIDElement; // Check arguments and call init(). if ( ( collection == 0 ) || ( element == 0 ) || ( element->init( owner, type ) == false ) ) { if ( element ) element->release(); return 0; } // Set HID properties. element->_usagePage = collection->collectionUsagePage; element->_usageMin = collection->collectionUsage; element->_usageMax = collection->collectionUsage; // Register with owner and parent. if ( ( owner->registerElement( element, &element->_cookie ) == false ) || ( ( parent && ( parent->addChildElement(element) == false ) ) ) ) { element->release(); element = 0; } return element; } //--------------------------------------------------------------------------- // IOHIDElement * IOHIDElement::newSubElement( UInt16 rangeIndex ) const { IOHIDElement * element = new IOHIDElement; // Check arguments and call init(). if ( (element == 0 ) || (rangeIndex == 0) || ( element->init( _owner, _type ) == false ) ) { if ( element ) element->release(); return 0; } // Set HID properties. element->_flags = _flags; element->_reportID = _reportID; element->_usagePage = _usagePage; element->_usageMin = _usageMin; element->_usageMax = _usageMax; element->_rangeIndex = rangeIndex; if ( IsArrayElement(_flags) ) { element->_reportBits = 1; element->_logicalMin = element->_physicalMin = 0; element->_logicalMax = element->_physicalMax = 1; element->_reportStartBit = _reportStartBit; } else { element->_reportBits = _reportBits; element->_reportStartBit = _reportStartBit + (rangeIndex * _reportBits); element->_logicalMin = _logicalMin; element->_logicalMax = _logicalMax; element->_physicalMin = _physicalMin; element->_physicalMax = _physicalMax; element->_units = _units; element->_unitExponent = _unitExponent; } // Register with owner and parent. if ( ( _owner->registerElement( element, &element->_cookie ) == false ) || ( _parent && ( _parent->addChildElement(element) == false ) ) ) { element->release(); element = 0; } return element; } //--------------------------------------------------------------------------- // bool IOHIDElement::createSubElements() { IOHIDElement * element; UInt32 count = getRangeCount(); UInt32 index = 1; bool ret = true; while ( index < count) { element = newSubElement( index++ ); if ( element == 0 ) { ret = false; break; } element->release(); } return ret; } //--------------------------------------------------------------------------- // void IOHIDElement::free() { if ( _childArray ) { _childArray->release(); _childArray = 0; } if ( _queueArray ) { _queueArray->release(); _queueArray = 0; } if ( _arrayItems ) { _arrayItems->release(); _arrayItems = 0; } if (_oldArraySelectors) { IOFree (_oldArraySelectors, sizeof(UInt32) * _reportCount); _oldArraySelectors = 0; } if (_colArrayReportHandlers) { _colArrayReportHandlers->release(); _colArrayReportHandlers = 0; } super::free(); } //--------------------------------------------------------------------------- // bool IOHIDElement::addChildElement( IOHIDElement * child ) { if ( _childArray == 0 ) { _childArray = OSArray::withCapacity(4); } if ( !_childArray ) return false; // Perform special processing if this is an array item. // Basically we want to group all related array elements // together. This will help out for elements that are // not part of a range. Since collections can span // multiple reports, we will use the following as a // unique ID: // Lower16bits: startBit // Upper16bits: // UpperByte: reportType // LowerByte: reportID if ((child->_type != kIOHIDElementTypeCollection) && IsArrayElement(child->_flags)) { if (_colArrayReportHandlers ==0) { _colArrayReportHandlers = OSDictionary::withCapacity(1); } if (! _colArrayReportHandlers) return false; IOHIDElement * arrayReportHandler; char uniqueID[33]; sprintf(uniqueID, "%d",((child->_reportType << 24) | (child->_reportID << 16) | (child->_reportStartBit))); uniqueID[32] = 0; arrayReportHandler = _colArrayReportHandlers->getObject(uniqueID); if (arrayReportHandler) { child->_arrayReportHandler = arrayReportHandler; } else { // We need to create array head based on info from // the child. arrayReportHandler = arrayHandlerElement(child->_owner, child->_type, child, this); if ( arrayReportHandler == 0 ) { return false; } // Register this new element with this collection _colArrayReportHandlers->setObject(uniqueID, arrayReportHandler); _childArray->setObject( arrayReportHandler ); arrayReportHandler->release(); } // Now that we have the info from the child, revert // it back to a button. child->_arrayReportHandler = arrayReportHandler; child->_reportBits = 1; child->_reportCount = 1; child->_logicalMin = child->_physicalMin = 0; child->_logicalMax = child->_physicalMax = 1; // Add the chile to the array list arrayReportHandler->_arrayItems->setObject(child); } _childArray->setObject( child ); child->_parent = this; return true; } IOHIDElement * IOHIDElement::arrayHandlerElement( IOHIDDevice * owner, IOHIDElementType type, IOHIDElement * child, IOHIDElement * parent) { IOHIDElement * element = new IOHIDElement; // Check arguments and call init(). if ( (element == 0 ) || ( element->init( owner, type ) == false ) ) { if ( element ) element->release(); return 0; } element->_arrayReportHandler = element; element->_parent = parent; element->_flags = child->_flags; element->_reportID = child->_reportID; element->_usagePage = child->_usagePage; element->_usageMin = 0xffffffff; element->_usageMax = 0xffffffff; element->_reportBits = child->_reportBits; element->_reportCount = child->_reportCount; element->_reportStartBit = child->_reportStartBit; element->_logicalMin = child->_logicalMin; element->_logicalMax = child->_logicalMax; element->_physicalMin = child->_physicalMin; element->_physicalMax = child->_physicalMax; // Allocate the array for the array elements. element->_arrayItems = OSArray::withCapacity(child->getRangeCount() ? child->getRangeCount() : 4); if (element->_arrayItems == NULL) return false; // RY: Allocate a buffer that will contain the old Array selector. // This needed to compare the old report to the new report to // deterine which array items need to be turned on/off. element->_oldArraySelectors = (UInt32 *)IOMalloc(sizeof(UInt32) * element->_reportCount); if (element->_oldArraySelectors == NULL) return false; if ( owner->registerElement( element, &element->_cookie ) == false ) { element->release(); element = 0; } return element; } //--------------------------------------------------------------------------- // bool IOHIDElement::serialize( OSSerialize * s ) const { IORegistryEntry * entry; UInt32 usage; bool ret = false; do { entry = new IORegistryEntry; if ( entry == 0 ) break; if ( entry->init() == false ) break; usage = (_usageMax != _usageMin) ? _usageMin + _rangeIndex : _usageMin; entry->setProperty( kIOHIDElementKey, _childArray ); entry->setProperty( kIOHIDElementCookieKey, (UInt32) _cookie, 32 ); entry->setProperty( kIOHIDElementTypeKey, _type, 32 ); entry->setProperty( kIOHIDElementUsageKey, usage, 32 ); entry->setProperty( kIOHIDElementUsagePageKey, _usagePage, 32 ); if ( _type == kIOHIDElementTypeCollection ) { ret = true; break; } entry->setProperty( kIOHIDElementValueLocationKey, (UInt32) _elementValueLocation, 32 ); entry->setProperty( kIOHIDElementHasNullStateKey, _flags & kHIDDataNullState ); entry->setProperty( kIOHIDElementHasPreferredStateKey, !(_flags & kHIDDataNoPreferred) ); entry->setProperty( kIOHIDElementIsNonLinearKey, _flags & kHIDDataNonlinear ); entry->setProperty( kIOHIDElementIsRelativeKey, _flags & kHIDDataRelative ); entry->setProperty( kIOHIDElementIsWrappingKey, _flags & kHIDDataWrap ); entry->setProperty( kIOHIDElementIsArrayKey, IsArrayElement(_flags) ); entry->setProperty( kIOHIDElementMaxKey, _logicalMax, 32 ); entry->setProperty( kIOHIDElementMinKey, _logicalMin, 32 ); entry->setProperty( kIOHIDElementScaledMaxKey, _physicalMax, 32 ); entry->setProperty( kIOHIDElementScaledMinKey, _physicalMin, 32 ); entry->setProperty( kIOHIDElementSizeKey, (_reportBits * _reportCount), 32 ); entry->setProperty( kIOHIDElementReportSizeKey, _reportBits, 32 ); entry->setProperty( kIOHIDElementReportCountKey, _reportCount, 32 ); // RY: No reason to publish the unit and unit exponent // for array elements. if ( !IsArrayElement(_flags) ) { entry->setProperty( kIOHIDElementUnitKey, _units, 32 ); entry->setProperty( kIOHIDElementUnitExponentKey, _unitExponent, 32 ); } ret = true; } while ( false ); if ( entry ) { if ( ret ) ret = entry->serializeProperties(s); entry->release(); } return ret; } //--------------------------------------------------------------------------- // UInt32 IOHIDElement::getElementValueSize() const { UInt32 size = sizeof(IOHIDElementValue); UInt32 reportWords = (_reportBits * _reportCount) / (sizeof(UInt32) * 8); // RY: Don't forget the remainder. reportWords += ((_reportBits * _reportCount) % (sizeof(UInt32) * 8)) ? 1 : 0; if ( reportWords > 1 ) { size += ((reportWords - 1) * sizeof(UInt32)); } return size; } //--------------------------------------------------------------------------- // Not very efficient, will do for now. #define BIT_MASK(bits) ((1 << (bits)) - 1) #define UpdateByteOffsetAndShift(bits, offset, shift) \ do { offset = bits >> 3; shift = bits & 0x07; } while (0) #define UpdateWordOffsetAndShift(bits, offset, shift) \ do { offset = bits >> 5; shift = bits & 0x1f; } while (0) static void getReportBits( const UInt8 * src, UInt32 * dst, UInt32 srcStartBit, UInt32 bitsToCopy, bool shouldSignExtend, bool * valueChanged ) { UInt32 srcOffset; UInt32 srcShift; UInt32 dstShift = 0; UInt32 dstStartBit = 0; UInt32 dstOffset = 0; UInt32 lastDstOffset = 0; UInt32 word = 0; UInt8 bitsProcessed; UInt32 totalBitsProcessed = 0; while ( bitsToCopy ) { UInt32 tmp; UpdateByteOffsetAndShift( srcStartBit, srcOffset, srcShift ); bitsProcessed = min( bitsToCopy, min( 8 - srcShift, 32 - dstShift ) ); tmp = (src[srcOffset] >> srcShift) & BIT_MASK(bitsProcessed); word |= ( tmp << dstShift ); dstStartBit += bitsProcessed; srcStartBit += bitsProcessed; bitsToCopy -= bitsProcessed; totalBitsProcessed += bitsProcessed; UpdateWordOffsetAndShift( dstStartBit, dstOffset, dstShift ); if ( ( dstOffset != lastDstOffset ) || ( bitsToCopy == 0 ) ) { // sign extend negative values // if this is the leftmost word of the result if ((lastDstOffset == 0) && // and the logical min or max is less than zero // so we should sign extend (shouldSignExtend)) { // SInt32 temp = word; // is this less than a full word if ((totalBitsProcessed < 32) && // and the value negative (high bit set) (word & (1 << (totalBitsProcessed - 1)))) // or in all 1s above the significant bit word |= ~(BIT_MASK(totalBitsProcessed)); } if ( dst[lastDstOffset] != word ) { dst[lastDstOffset] = word; *valueChanged = true; } word = 0; lastDstOffset = dstOffset; } } } static void setReportBits( const UInt32 * src, UInt8 * dst, UInt32 dstStartBit, UInt32 bitsToCopy) { UInt32 dstOffset; UInt32 dstShift; UInt32 srcShift = 0; UInt32 srcStartBit = 0; UInt32 srcOffset = 0; UInt8 bitsProcessed; UInt32 tmp; while ( bitsToCopy ) { UpdateByteOffsetAndShift( dstStartBit, dstOffset, dstShift ); bitsProcessed = min( bitsToCopy, min( 8 - dstShift, 32 - srcShift ) ); tmp = (src[srcOffset] >> srcShift) & BIT_MASK(bitsProcessed); dst[dstOffset] |= ( tmp << dstShift ); dstStartBit += bitsProcessed; srcStartBit += bitsProcessed; bitsToCopy -= bitsProcessed; UpdateWordOffsetAndShift( srcStartBit, srcOffset, srcShift ); } } //--------------------------------------------------------------------------- // bool IOHIDElement::processReport( UInt8 reportID, void * reportData, UInt32 reportBits, const AbsoluteTime * timestamp, IOHIDElement ** next ) { IOHIDEventQueue * queue; bool changed = false; // Set next pointer to the next report handler in the chain. // If this is an array, set the report handler to the one // the array. if (IsArrayElement(_flags) && !IsArrayReportHandler(this)) { *next = _arrayReportHandler; return false; } *next = _nextReportHandler; do { // Ignore report that does not match our report ID. if ( _reportID != reportID ) break; // Verify incoming report size. if ( _reportSize && ( reportBits < _reportSize ) ) { *next = 0; break; } // The generation is incremented before and after // processing the report. An odd value tells us // that the information is incomplete and should // not be trusted. An even value tells us that // the value is complete. _elementValue->generation++; // Get the element value from the report. getReportBits( (UInt8 *) reportData, /* source buffer */ _elementValue->value, /* destination buffer */ _reportStartBit, /* source start bit */ (_reportBits * _reportCount), /* bits to copy */ (((SInt32)_logicalMin < 0) || ((SInt32)_logicalMax < 0)), /* should sign extend */ &changed ); /* did value change? */ // Set a timestamp to indicate the last modification time. // We should set the time stamp if the generation is 1 regardless if the value // changed. This will insure that an initial value of 0 will have the correct // timestamp if ( changed || (_flags & kHIDDataRelativeBit) || (_elementValue->generation == 1)) { _elementValue->timestamp = *timestamp; // RY: see processArrayReport for explanation of why // we parse this report twice. if (IsArrayElement(_flags) && IsArrayReportHandler(this)) processArrayReport(reportData); if ( _queueArray ) { for ( UInt32 i = 0; (queue = (IOHIDEventQueue *) _queueArray->getObject(i)); i++ ) { queue->enqueue( (void *) _elementValue, _elementValue->totalSize ); } } } _elementValue->generation++; // If this element is part of a transaction // set its state to idle if (_transactionState) _transactionState = kIOHIDTransactionStateIdle; } while ( false ); return changed; } //--------------------------------------------------------------------------- // bool IOHIDElement::createReport( UInt8 reportID, void * reportData, // this report should be alloced outisde this method. UInt32 * reportLength, IOHIDElement ** next ) { bool handled = false; *next = _nextReportHandler; do { // Ignore report that does not match our report ID. if ( _reportID != reportID ) break; //------------------------------------------------ // Changed this portion of the method. // The report is now allocated outside of the // method if ( _reportSize ) { *reportLength = _reportSize / 8; if ( reportData == 0 ) { *next = 0; break; } bzero( reportData, *reportLength ); } //------------------------------------------------ // Set next pointer to the next report handler in the chain. // If this is an array, set the report handler to the one // the array. if (IsArrayElement(_flags) && !IsArrayReportHandler(this)) { *next = _arrayReportHandler; break; } // If this element has not been set, a out of bounds // value must be set. This will cause the device // to ignore the report for this element. if ( _transactionState == kIOHIDTransactionStateIdle ) { if (!IsArrayElement(_flags)) { setOutOfBoundsValue(); } // If the IOHIDLib is not modifying this element // directly, have it run though the array elements // and create a report; else { createArrayReport(reportData); handled = true; break; } } // Set the element value to the report. if ( reportData ) { setReportBits( _elementValue->value, /* source buffer */ (UInt8 *) reportData, /* destination buffer */ _reportStartBit, /* dst start bit */ (_reportBits * _reportCount));/* bits to copy */ handled = true; // Clear the transaction state _transactionState = kIOHIDTransactionStateIdle; } } while ( false ); return handled; } //--------------------------------------------------------------------------- // bool IOHIDElement::setMemoryForElementValue( IOVirtualAddress address, void * location ) { _elementValue = (IOHIDElementValue *) address; _elementValueLocation = location; // Clear memory block, and set the invariants. bzero( _elementValue, getElementValueSize() ); _elementValue->cookie = _cookie; _elementValue->totalSize = getElementValueSize(); return true; } //--------------------------------------------------------------------------- // Return the number of elements in a usage range. UInt32 IOHIDElement::getRangeCount() const { // FIXME - shouldn't we use logical min/max? return ( _usageMax - _usageMin + 1 ); } //--------------------------------------------------------------------------- // IOHIDElement * IOHIDElement::setNextReportHandler( IOHIDElement * element ) { IOHIDElement * prev = _nextReportHandler; _nextReportHandler = element; return prev; } //--------------------------------------------------------------------------- // bool IOHIDElement::getReportType( IOHIDReportType * reportType ) const { if ( _type <= kIOHIDElementTypeInput_ScanCodes ) *reportType = kIOHIDReportTypeInput; else if ( _type == kIOHIDElementTypeOutput ) *reportType = kIOHIDReportTypeOutput; else if ( _type == kIOHIDElementTypeFeature ) *reportType = kIOHIDReportTypeFeature; else return false; return true; } //--------------------------------------------------------------------------- // bool IOHIDElement::addEventQueue( IOHIDEventQueue * queue ) { if ( _queueArray == 0 ) { _queueArray = OSArray::withCapacity(4); } if ( hasEventQueue(queue) == true ) return false; return _queueArray ? _queueArray->setObject( queue ) : false; } //--------------------------------------------------------------------------- // bool IOHIDElement::removeEventQueue( IOHIDEventQueue * queue ) { OSObject * obj = 0; for ( UInt32 i = 0; _queueArray && (obj = _queueArray->getObject(i)); i++ ) { if ( obj == (OSObject *) queue ) { _queueArray->removeObject(i); if ( _queueArray->getCount() == 0 ) { _queueArray->release(); _queueArray = 0; } break; } } return (obj != 0); } //--------------------------------------------------------------------------- // bool IOHIDElement::hasEventQueue( IOHIDEventQueue * queue ) { OSObject * obj = 0; for ( UInt32 i = 0; _queueArray && (obj = _queueArray->getObject(i)); i++ ) { if ( obj == (OSObject *) queue ) break; } return (obj != 0); } //--------------------------------------------------------------------------- // UInt32 IOHIDElement::setReportSize( UInt32 numberOfBits ) { UInt32 oldSize = _reportSize; _reportSize = numberOfBits; return oldSize; } //--------------------------------------------------------------------------- // This methods will set an out of bounds element value. This value will // be based on the _logicalMin or _logicalMax depending on bit space. If // no room is available to go outside the range, the value will remain // unchanged. void IOHIDElement::setOutOfBoundsValue() { // Make sure we are only dealing with a non-array element value type if ( _elementValue->totalSize == sizeof(IOHIDElementValue) && (_reportCount == 1)) { // Simple case: If the _logicalMin > 0, then we can just // set the elementValue to 0 if ( _logicalMin > 0 ) { _elementValue->value[0] = 0; } // Other case: _logicalMin <= 0, thus, we need to set the // elementValue to _logicalMax + 1 or _logicalMin - 1. // This could be tricky due to bit space. else { if ( ( BIT_MASK(_reportBits) - _logicalMax ) > 0 ) _elementValue->value[0] = _logicalMax + 1; else if ( ( -(BIT_MASK(_reportBits)) - _logicalMin ) < 0 ) _elementValue->value[0] = _logicalMin - 1; } } } void IOHIDElement::createArrayReport(void * reportData) { IOHIDElement *element; bool changed; UInt32 arraySel; UInt32 reportIndex = 0; UInt32 startBit = _reportStartBit; for (int i=0; _arrayItems && i<_arrayItems->getCount(); i++) { element = (IOHIDElement *)(_arrayItems->getObject(i)); if (!element) continue; if ( element->_transactionState == kIOHIDTransactionStateIdle ) continue; if (element->_elementValue->value[0] == 0) continue; arraySel = GetArrayItemSel(i); setReportBits( &arraySel, /* source buffer */ (UInt8 *) reportData, /* destination buffer */ startBit, /* dst start bit */ _reportBits); /* bits to copy */ startBit += _reportBits; reportIndex ++; element->_transactionState = kIOHIDTransactionStateIdle; // Make sure we don't add to many usages to the report if (reportIndex >= _reportCount) break; } // Clear out the remaining portions of the report for this array arraySel = 0; for (i=reportIndex; i<_reportCount; i++) { setReportBits( &arraySel, /* source buffer */ (UInt8 *) reportData, /* destination buffer */ startBit, /* dst start bit */ _reportBits); /* bits to copy */ startBit += _reportBits; } // RY: This is expensive, but let's fill in the elementValue for // this element. Hopefully, a developer will find this usefull, // as this will show exactly what was pushed to the device. getReportBits( (UInt8 *) reportData, // Src _elementValue->value, // Dst _reportStartBit, // Src Start Bit _reportBits * _reportCount, // Bits to copy (((SInt32)_logicalMin < 0) || ((SInt32)_logicalMax < 0)), // Should Sign extend &changed ); // Changed } void IOHIDElement::setArrayElementValue(UInt32 index, UInt32 value) { IOHIDElement *element; IOHIDEventQueue *queue; if ( !_arrayItems || (index > _arrayItems->getCount())) return; element = (IOHIDElement *)(_arrayItems->getObject(index)); if (!element) return; // Bump the generation count. An odd value tells us // that the information is incomplete and should not // be trusted. An even value tells us that the value // is complete. element->_elementValue->generation ++; element->_elementValue->value[0] = value; element->_elementValue->timestamp = _elementValue->timestamp; element->_elementValue->generation ++; if ( element->_queueArray ) { for ( UInt32 i = 0; (queue = (IOHIDEventQueue *) element->_queueArray->getObject(i)); i++ ) { queue->enqueue( (void *) element->_elementValue, element->_elementValue->totalSize ); } } } void IOHIDElement::processArrayReport(void * reportData) { UInt32 newArray[_reportCount]; UInt32 arraySel, prevArraySel; UInt32 iNewArray, iOldArray, startBit; bool found, changed; // If the generation == 1, we know that this is first time that // we have processed this array report. Set the time stamp on // all array elements. if (_elementValue->generation == 1) { for (int i=0; i < _arrayItems->getCount(); i++) setArrayElementValue(i, 0); } // RY: We parse the report here to pick of the individual array // selector. Since a arraySel can be as big as UInt32 we should // pick them off one at a time with getReportBits. // As you might notice we are duplicating the efforts of // processReport. This is the desired behavior, just in case // we decided to expose the entire array report in the future. bzero(newArray, (_reportCount * sizeof(UInt32))); startBit = _reportStartBit; for (iNewArray = 0; iNewArray < _reportCount; iNewArray ++) { startBit += _reportBits * iNewArray; getReportBits( (UInt8 *) reportData, // Src &newArray[iNewArray], // Dst startBit, // Src Start Bit _reportBits, // Bits to copy (((SInt32)_logicalMin < 0) || ((SInt32)_logicalMax < 0)), // Should Sign extend &changed ); // Changed } // Check the existing indexes against the originals for (iOldArray = 0; iOldArray < _reportCount; iOldArray ++) { arraySel = _oldArraySelectors[iOldArray]; // If we've seen this value before, // we can break out of this loop. if ((iOldArray > 0) && (prevArraySel == arraySel)) break; prevArraySel = arraySel; found = false; for (iNewArray = 0; iNewArray < _reportCount; iNewArray ++) { if (arraySel == newArray[iNewArray]) { found = true; break; } } // The index is no longer present. Set its value to 0. if (!found) setArrayElementValue(GetArrayItemIndex(arraySel), 0); } // Now add new indexes to _oldArraySelectors for (iNewArray = 0; iNewArray < _reportCount; iNewArray ++) { arraySel = newArray[iNewArray]; // If we've seen this value before, // we can break out of this loop. if ((iNewArray > 0) && (prevArraySel == arraySel)) break; prevArraySel = arraySel; found = false; for (iOldArray = 0; iOldArray < _reportCount; iOldArray ++) { if (arraySel == _oldArraySelectors[iOldArray]) { found = true; break; } } // This is a new index. Set its value to 1. if (!found) setArrayElementValue(GetArrayItemIndex(arraySel), 1); } // save the new array to _oldArraySelectors for future reference for (iOldArray = 0; iOldArray < _reportCount; iOldArray ++) _oldArraySelectors[iOldArray] = newArray[iOldArray]; }