/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The 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, QUIET ENJOYMENT 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(element) \ ((element->_flags & kHIDDataArrayBit) == kHIDDataArray) #define IsArrayReportHandler(reportHandler) \ (reportHandler == _arrayReportHandler) #define IsArrayElementTheReportHandler(element) \ (element == element->_arrayReportHandler) #define IsButtonElement(element) \ (element->_reportBits == 1) #define IsDuplicateElement(element) \ (element->_duplicateReportHandler) #define IsDuplicateReportHandler(reportHandler) \ (reportHandler == _duplicateReportHandler) #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; _duplicateReportHandler = 0; _arrayReportHandler = 0; _colArrayReportHandlers = 0; _arrayItems = 0; _duplicateElements = 0; _oldArraySelectors = 0; _usagePage = 0; _usageMin = _usageMax = 0; _isInterruptReportHandler = 0; 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; if (!IsArrayElement(element)) { element->_reportCount = 1; } } else { element->_usageMin = button->u.notRange.usage; element->_usageMax = button->u.notRange.usage; } if (IsArrayElement(element)) { // 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; if (element->_reportCount > 1) { element->_duplicateReportHandler = element; element->_duplicateElements = OSArray::withCapacity(element->_reportCount); if (element->_duplicateElements == NULL) goto BUTTON_ELEMENT_RELEASE; } } // Register with owner and parent, then spawn sub-elements. if ( ( parent && ( parent->addChildElement(element, IsArrayElement(element)) == true ) ) && ( owner->registerElement( element, &element->_cookie ) == true ) && ( element->createSubElements() == true )) { return element; } BUTTON_ELEMENT_RELEASE: 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; element->_reportCount = 1; } else { element->_usageMin = value->u.notRange.usage; element->_usageMax = value->u.notRange.usage; } if (element->_reportCount > 1) { element->_duplicateReportHandler = element; element->_duplicateElements = OSArray::withCapacity(element->_reportCount); if (element->_duplicateElements == NULL) goto VALUE_ELEMENT_RELEASE; } // Register with owner and parent, then spawn sub-elements. if ( ( owner->registerElement( element, &element->_cookie ) == true ) && ( ( parent && ( parent->addChildElement(element, IsArrayElement(element)) == true ) ) ) && ( element->createSubElements() == true )) { return element; } VALUE_ELEMENT_RELEASE: element->release(); element = 0; return element; } //--------------------------------------------------------------------------- // IOHIDElement * IOHIDElement::collectionElement( IOHIDDevice * owner, IOHIDElementType type, HIDCollectionExtendedNodePtr 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; element->_collectionType = collection->data; // 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::reportHandlerElement( IOHIDDevice * owner, IOHIDElementType type, UInt32 reportID, UInt32 reportBits ) { IOHIDElement * element = new IOHIDElement; if ( ( reportBits == 0 ) || ( element->init( owner, type ) == false ) ) { element->release(); return 0; } element->_isInterruptReportHandler = true; element->_flags = kHIDDataVariable | kHIDDataRelative; element->_reportCount = 1; element->_reportID = reportID; element->_reportBits = element->_reportSize = reportBits; // Register with owner. if ( owner->registerElement( element, &element->_cookie ) == 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 ) || ( 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; element->_arrayReportHandler = _arrayReportHandler; 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; // RY: Special handling for array elements. // FYI, if this an array and button element, then we // know that this is a dummy array element. The start // is not used to process the report, but instead used // to identify the which arrayHandler is belongs to. // Therefore, all subelements should contain the same // start bit. if ( IsArrayElement(this) && IsButtonElement(this) ) { element->_reportStartBit = _reportStartBit; } if (_duplicateElements) { _duplicateElements->setObject(element); element->_duplicateReportHandler = _duplicateReportHandler; } // 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 = getStartingRangeIndex(); 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 ( _duplicateElements ) { _duplicateElements->release(); _duplicateElements = 0; } if (_oldArraySelectors) { IOFree (_oldArraySelectors, sizeof(UInt32) * _reportCount); _oldArraySelectors = 0; } if (_colArrayReportHandlers) { _colArrayReportHandlers->release(); _colArrayReportHandlers = 0; } super::free(); } //--------------------------------------------------------------------------- // bool IOHIDElement::addChildElement( IOHIDElement * child, bool arrayHeader) { if ( _childArray == 0 ) { _childArray = OSArray::withCapacity(4); } if ( !_childArray ) return false; // Perform special processing if this is an array item // that doesn't directly handle the report. 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: // 8bits: reportID // 32bits: startBit // 32bits: elementType if ( (child->_type != kIOHIDElementTypeCollection) && IsArrayElement(child) && !IsArrayElementTheReportHandler(child) && (arrayHeader || !IsDuplicateElement(child))) { if (_colArrayReportHandlers ==0) { _colArrayReportHandlers = OSDictionary::withCapacity(1); } if (! _colArrayReportHandlers) return false; IOHIDElement * arrayReportHandler; char uniqueID[32]; sprintf(uniqueID, "%4.4x%4.4x%4.4x",child->_type, child->_reportStartBit, child->_reportID); 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); 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 ) ) { goto ARRAY_HANDLER_ELEMENT_RELEASE; } 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->_usageMax - child->_usageMin) + 1); if (element->_arrayItems == NULL) goto ARRAY_HANDLER_ELEMENT_RELEASE; // 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) goto ARRAY_HANDLER_ELEMENT_RELEASE; if (element->_reportCount > 1) { element->_duplicateReportHandler = element; element->_duplicateElements = OSArray::withCapacity(element->_reportCount); if (element->_duplicateElements == NULL) goto ARRAY_HANDLER_ELEMENT_RELEASE; } if ( (owner->registerElement( element, &element->_cookie ) == true ) && ( parent && ( parent->addChildElement(element) == true )) && ( element->createSubElements() == true )) { return element; } ARRAY_HANDLER_ELEMENT_RELEASE: 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 ) { entry->setProperty( kIOHIDElementCollectionTypeKey, _collectionType, 32 ); ret = true; break; } entry->setProperty( kIOHIDElementSizeKey, (_reportBits * _reportCount), 32 ); if (_reportCount > 1) { entry->setProperty( kIOHIDElementReportSizeKey, _reportBits, 32 ); entry->setProperty( kIOHIDElementReportCountKey, _reportCount, 32 ); } entry->setProperty( kIOHIDElementValueLocationKey, (UInt32) _elementValueLocation, 32 ); if ( _isInterruptReportHandler ) { ret = true; break; } 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(this) ); entry->setProperty( kIOHIDElementMaxKey, _logicalMax, 32 ); entry->setProperty( kIOHIDElementMinKey, _logicalMin, 32 ); entry->setProperty( kIOHIDElementScaledMaxKey, _physicalMax, 32 ); entry->setProperty( kIOHIDElementScaledMinKey, _physicalMin, 32 ); if (IsDuplicateElement(this) && !IsDuplicateReportHandler(this)) { entry->setProperty( kIOHIDElementDuplicateIndexKey, _rangeIndex, 32); } // RY: No reason to publish the unit and unit exponent // for array elements. if ( !IsArrayElement(this) ) { 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 readReportBits( 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 writeReportBits( 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 (next) { *next = _nextReportHandler; if (IsArrayElement(this) && !IsArrayReportHandler(this)) { *next = _arrayReportHandler; return false; } // Verify incoming report size. if ( _reportSize && ( reportBits < _reportSize ) ) { *next = 0; return false; } } do { // Ignore report that does not match our report ID. if ( _reportID != reportID ) 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. readReportBits( (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; if (IsArrayElement(this) && IsArrayReportHandler(this)) processArrayReport(reportID, reportData, reportBits, timestamp); 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; if (next) *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 ) { if (next) *next = 0; break; } bzero( reportData, *reportLength ); } //------------------------------------------------ // Set next pointer to the next report handler in the chain. // If this is an array or duplicate, set the next to the // appropriate handler. if (next) { if (IsArrayElement(this)) { if (!IsArrayReportHandler(this)) { *next = _arrayReportHandler; break; } // RY: Only bother creating an array report is this element // is idle. if (_transactionState == kIOHIDTransactionStateIdle) return createArrayReport(reportID, reportData, reportLength); } else if (IsDuplicateElement(this)) { if (!IsDuplicateReportHandler(this)) { *next = _duplicateReportHandler; break; } // RY: Only bother creating a report if the duplicate report // elements are idle. if (_transactionState == kIOHIDTransactionStateIdle) return createDuplicateReport(reportID, reportData, reportLength); } } // If this element has not been set, an out of bounds // value must be set. This will cause the device // to ignore the report for this element. if ( _transactionState == kIOHIDTransactionStateIdle ) { setOutOfBoundsValue(); } // Set the element value to the report. if ( reportData ) { writeReportBits( _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? // Check to see if we have multiple elements with the same usage. // If so, return the _reportCount return (_reportCount > 1) ? _reportCount : (_usageMax-_usageMin + 1 ); } //--------------------------------------------------------------------------- // Return the number of elements in a usage range. UInt32 IOHIDElement::getStartingRangeIndex() const { // Check to see if we have multiple elements with the same usage. return (_reportCount > 1) ? 0 : 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 not dealing with long element value type if ( _elementValue->totalSize == sizeof(IOHIDElementValue)) { // 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; } } } bool IOHIDElement::createDuplicateReport(UInt8 reportID, void * reportData, UInt32 * reportLength) { bool pending = false; IOHIDElement *element; // RY: Then, check the other duplicates to see if they are currently // pending. for (int i=0; _duplicateElements && i<_duplicateElements->getCount(); i++) { if ((element = _duplicateElements->getObject(i)) && (element->_transactionState == kIOHIDTransactionStatePending)) { pending = true; } element->createReport(reportID, reportData, reportLength, 0); } return pending; } bool IOHIDElement::createArrayReport(UInt8 reportID, void * reportData, UInt32 * reportLength) { IOHIDElement *element, *arrayElement; bool changed; UInt32 arraySel; UInt32 reportIndex = 0; if (createDuplicateReport(reportID, reportData, reportLength)) return true; for (int i=0; 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); if (arrayElement = (_duplicateElements) ? _duplicateElements->getObject(reportIndex) : this) { arrayElement->_elementValue->value[0] = arraySel; arrayElement->_transactionState = kIOHIDTransactionStatePending; arrayElement->createReport(reportID, reportData, reportLength, 0); } 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++) { if (arrayElement = (_duplicateElements) ? _duplicateElements->getObject(reportIndex) : this) { arrayElement->_elementValue->value[0] = arraySel; arrayElement->_transactionState = kIOHIDTransactionStatePending; arrayElement->createReport(reportID, reportData, reportLength, 0); } } return true; } 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 ); } } } bool IOHIDElement::processArrayReport( UInt8 reportID, void * reportData, UInt32 reportBits, const AbsoluteTime * timestamp) { IOHIDElement *element; 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: Process the arry selector elements. If any of their values // haven't changed, don't bother with any further processing. if (_duplicateElements) { changed = false; for (iNewArray = 0; iNewArray < _reportCount; iNewArray ++) { if (element = _duplicateElements->getObject(iNewArray)) { changed |= element->processReport(reportID, reportData, reportBits, timestamp, 0); } } if (!changed) return 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 ++) { element = (_duplicateElements) ? _duplicateElements->getObject(iNewArray) : this; if (element && (arraySel == element->_elementValue->value[0])) { 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 ++) { if (!(element = (_duplicateElements) ? _duplicateElements->getObject(iNewArray) : this)) continue; arraySel = element->_elementValue->value[0]; // 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 ++) { if (element = (_duplicateElements) ? _duplicateElements->getObject(iOldArray) : this) _oldArraySelectors[iOldArray] = element->_elementValue->value[0]; } return changed; }