IOHIDElementPrivate.cpp [plain text]
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <AssertMacros.h>
#include <DriverKit/DriverKit.h>
#include <DriverKit/OSCollections.h>
#include <HIDDriverKit/HIDDriverKit_Private.h>
#include "IOHIDDescriptorParser.h"
#include "IOHIDDescriptorParserPrivate.h"
typedef struct _IOHIDElementValue
{
IOHIDElementCookie cookie;
uint32_t flags:8;
uint32_t totalSize:24;
uint64_t timestamp;
uint32_t generation;
uint32_t value[1];
} IOHIDElementValue;
struct IOHIDElementPrivate_IVars
{
IOHIDElementContainer *owner;
IOHIDElementType type;
IOHIDElementCookie cookie;
IOHIDElementPrivate *nextReportHandler;
IOHIDElementValue *elementValue;
void *elementValueLocation;
IOHIDElementPrivate *parent;
OSArray *childArray;
uint32_t flags;
IOHIDElementCollectionType collectionType;
uint32_t reportSize;
uint32_t reportCount;
uint32_t rawReportCount;
uint32_t reportStartBit;
uint32_t reportBits;
uint32_t reportID;
uint32_t usagePage;
uint32_t usage;
uint32_t usageMin;
uint32_t usageMax;
uint32_t logicalMin;
uint32_t logicalMax;
uint32_t physicalMin;
uint32_t physicalMax;
uint32_t unitExponent;
uint32_t units;
uint32_t transactionState;
OSData *dataValue;
IOHIDElementPrivate *duplicateReportHandler;
IOHIDElementPrivate *arrayReportHandler;
IOHIDElementPrivate **rollOverElementPtr;
OSDictionary *colArrayReportHandlers;
OSArray *arrayItems;
OSArray *duplicateElements;
uint32_t *oldArraySelectors;
bool isInterruptReportHandler;
bool shouldTickleActivity;
uint8_t variableSize;
uint32_t currentReportSizeBits;
uint32_t previousValue;
struct {
int32_t satMin;
int32_t satMax;
int32_t dzMin;
int32_t dzMax;
int32_t min;
int32_t max;
IOFixed gran;
} calibration;
};
#define _owner ivars->owner
#define _type ivars->type
#define _cookie ivars->cookie
#define _nextReportHandler ivars->nextReportHandler
#define _elementValue ivars->elementValue
#define _elementValueLocation ivars->elementValueLocation
#define _parent ivars->parent
#define _childArray ivars->childArray
#define _flags ivars->flags
#define _collectionType ivars->collectionType
#define _reportSize ivars->reportSize
#define _reportCount ivars->reportCount
#define _reportStartBit ivars->reportStartBit
#define _reportBits ivars->reportBits
#define _reportID ivars->reportID
#define _usagePage ivars->usagePage
#define _usage ivars->usage
#define _usageMin ivars->usageMin
#define _usageMax ivars->usageMax
#define _logicalMin ivars->logicalMin
#define _logicalMax ivars->logicalMax
#define _physicalMin ivars->physicalMin
#define _physicalMax ivars->physicalMax
#define _unitExponent ivars->unitExponent
#define _units ivars->units
#define _transactionState ivars->transactionState
#define _dataValue ivars->dataValue
#define _duplicateReportHandler ivars->duplicateReportHandler
#define _arrayReportHandler ivars->arrayReportHandler
#define _rollOverElementPtr ivars->rollOverElementPtr
#define _colArrayReportHandlers ivars->colArrayReportHandlers
#define _arrayItems ivars->arrayItems
#define _duplicateElements ivars->duplicateElements
#define _oldArraySelectors ivars->oldArraySelectors
#define _isInterruptReportHandler ivars->isInterruptReportHandler
#define _shouldTickleActivity ivars->shouldTickleActivity
#define _variableSize ivars->variableSize
#define _currentReportSizeBits ivars->currentReportSizeBits
#define _previousValue ivars->previousValue
#define _calibration ivars->calibration
#define _rawReportCount ivars->rawReportCount
#define IsArrayElement(element) \
((element->_flags & kHIDDataArrayBit) == kHIDDataArray)
#define GetArrayItemIndex(sel) \
(sel - _logicalMin)
#define super OSContainer
#ifndef min
#define min(a, b) ((a < b) ? a : b)
#endif
bool IOHIDElementPrivate::init(IOHIDElementContainer * owner,
IOHIDElementType type)
{
bool ret = false;
ret = super::init();
require_action(ret, exit, HIDLogError("Init:%x", ret));
ivars = IONewZero(IOHIDElementPrivate_IVars, 1);
_owner = owner;
_type = type;
_reportCount = 1;
_rawReportCount = _reportCount;
ret = true;
exit:
return ret;
}
void IOHIDElementPrivate::free()
{
OSSafeReleaseNULL(_childArray);
OSSafeReleaseNULL(_arrayItems);
OSSafeReleaseNULL(_duplicateElements);
OSSafeReleaseNULL(_colArrayReportHandlers);
OSSafeReleaseNULL(_dataValue);
OSSafeReleaseNULL(_childArray);
if (_oldArraySelectors) {
IOFree(_oldArraySelectors, sizeof(UInt32) * _reportCount);
}
IOSafeDeleteNULL(ivars, IOHIDElementPrivate_IVars, 1);
super::free();
}
IOHIDElementPrivate *IOHIDElementPrivate::buttonElement(
IOHIDElementContainer *owner,
IOHIDElementType type,
HIDButtonCapabilitiesPtr button,
IOHIDElementPrivate *parent)
{
IOHIDElementPrivate *element = NULL;
bool result = false;
require(button, exit);
element = OSTypeAlloc(IOHIDElementPrivate);
require(element && element->init(owner, type), exit);
element->_flags = button->bitField;
element->_reportStartBit = button->startBit;
element->_reportID = button->reportID;
element->_usagePage = button->usagePage;
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;
}
element->_usage = element->_usageMin;
if (IsArrayElement(element)) {
element->_logicalMin = element->_physicalMin = button->u.notRange.reserved2;
element->_logicalMax = element->_physicalMax = button->u.notRange.reserved3;
element->_reportBits = button->unitExponent;
element->_reportCount = button->units;
if (button->isRange &&
element->_usagePage == kHIDPage_KeyboardOrKeypad &&
element->_usageMax < (kHIDUsage_KeyboardLeftControl - 1)) {
element->_usageMax = kHIDUsage_KeyboardLeftControl - 1;
}
} else {
element->_reportBits = 1;
element->_units = button->units;
element->_unitExponent = button->unitExponent;
}
element->_rawReportCount = element->_reportCount;
element->_currentReportSizeBits = element->_reportBits * element->_reportCount;
if (parent) {
require(parent->addChildElement(element, IsArrayElement(element)), exit);
}
owner->registerElement(element, &element->_cookie);
require(element->createSubElements(), exit);
result = true;
exit:
if (!result && element) {
element->release();
element = NULL;
}
return element;
}
IOHIDElementPrivate *IOHIDElementPrivate::valueElement(
IOHIDElementContainer *owner,
IOHIDElementType type,
HIDValueCapabilitiesPtr value,
IOHIDElementPrivate *parent)
{
IOHIDElementPrivate *element = NULL;
bool result = false;
require(value, exit);
element = OSTypeAlloc(IOHIDElementPrivate);
require(element && element->init(owner, type), exit);
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->_rawReportCount = element->_reportCount;
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;
}
element->_usage = element->_usageMin;
element->_currentReportSizeBits = element->_reportBits * element->_reportCount;
if (element->_reportCount > 1)
{
element->_reportBits *= element->_reportCount;
element->_reportCount = 1;
}
if (parent && parent->getUsagePage() == kHIDPage_AppleVendor &&
(parent->getUsage() == kHIDUsage_AppleVendor_Message ||
parent->getUsage() == kHIDUsage_AppleVendor_Payload)) {
element->_variableSize |= kIOHIDElementVariableSizeElement;
}
owner->registerElement(element, &element->_cookie);
if (parent) {
require(parent->addChildElement(element, IsArrayElement(element)), exit);
}
require(element->createSubElements(), exit);
result = true;
exit:
if (!result && element) {
element->release();
element = NULL;
}
return element;
}
IOHIDElementPrivate *IOHIDElementPrivate::collectionElement(
IOHIDElementContainer *owner,
IOHIDElementType type,
HIDCollectionExtendedNodePtr collection,
IOHIDElementPrivate *parent)
{
IOHIDElementPrivate *element = NULL;
bool result = false;
require(collection, exit);
element = OSTypeAlloc(IOHIDElementPrivate);
require(element && element->init(owner, type), exit);
element->_usagePage = collection->collectionUsagePage;
element->_usage = collection->collectionUsage;
element->_usageMin = collection->collectionUsage;
element->_usageMax = collection->collectionUsage;
element->_collectionType = (IOHIDElementCollectionType)collection->data;
element->_shouldTickleActivity = (element->_usagePage == kHIDPage_GenericDesktop);
owner->registerElement(element, &element->_cookie);
if (parent) {
require(parent->addChildElement(element, false), exit);
}
result = true;
exit:
if (!result && element) {
element->release();
element = NULL;
}
return element;
}
IOHIDElementPrivate *IOHIDElementPrivate::nullElement(
IOHIDElementContainer *owner,
uint32_t reportID,
IOHIDElementPrivate *parent)
{
IOHIDElementPrivate *element = NULL;
bool result = false;
element = OSTypeAlloc(IOHIDElementPrivate);
require(element && element->init(owner, kIOHIDElementTypeInput_NULL), exit);
element->_reportID = reportID;
owner->registerElement(element, &element->_cookie);
if (parent) {
parent->addChildElement(element, false);
}
result = true;
exit:
if (!result && element) {
element->release();
element = NULL;
}
return element;
}
IOHIDElementPrivate *IOHIDElementPrivate::reportHandlerElement(
IOHIDElementContainer *owner,
IOHIDElementType type,
uint32_t reportID,
uint32_t reportBits)
{
IOHIDElementPrivate *element = NULL;
bool result = false;
require(reportBits, exit);
element = OSTypeAlloc(IOHIDElementPrivate);
require(element && element->init(owner, type), exit);
element->_isInterruptReportHandler = true;
element->_flags = kHIDDataVariable | kHIDDataRelative;
element->_reportCount = 1;
element->_reportID = reportID;
element->_reportBits = element->_reportSize = reportBits;
element->_currentReportSizeBits = element->_reportBits * element->_reportCount;
owner->registerElement(element, &element->_cookie);
result = true;
exit:
if (!result && element) {
element->release();
element = NULL;
}
return element;
}
IOHIDElementPrivate *IOHIDElementPrivate::newSubElement(uint16_t rangeIndex) const
{
IOHIDElementPrivate *element = NULL;
bool result = false;
element = OSTypeAlloc(IOHIDElementPrivate);
require(element && element->init(_owner, _type), exit);
element->_flags = _flags;
element->_reportID = _reportID;
element->_usagePage = _usagePage;
element->_usageMin = _usageMin;
element->_usageMax = _usageMax;
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;
element->_rawReportCount = _reportCount;
element->_currentReportSizeBits = element->_reportBits * element->_reportCount;
if (element->_usageMax == element->_usageMin) {
element->_usage = element->_usageMin;
} else {
element->_usage = element->_usageMin + rangeIndex;
}
if (IsArrayElement(this) && _reportBits == 1) {
element->_reportStartBit = _reportStartBit;
}
if (_duplicateElements) {
_duplicateElements->setObject(element);
element->_duplicateReportHandler = _duplicateReportHandler;
}
_owner->registerElement(element, &element->_cookie);
if (_parent) {
require(_parent->addChildElement(element, false), exit);
}
result = true;
exit:
if (!result && element) {
element->release();
element = NULL;
}
return element;
}
bool IOHIDElementPrivate::createSubElements()
{
bool ret = false;
uint32_t rangeCount, rangeIndex;
if (_reportCount > 1) {
rangeCount = _reportCount;
rangeIndex = 0;
} else {
rangeCount = _usageMax - _usageMin + 1;
rangeIndex = 1;
}
while(rangeIndex < rangeCount) {
IOHIDElementPrivate *element = newSubElement(rangeIndex++);
require(element, exit);
element->release();
}
ret = true;
exit:
return ret;
}
bool IOHIDElementPrivate::addChildElement(IOHIDElementPrivate *child,
bool arrayHeader)
{
bool result = false;
if (!_childArray) {
_childArray = OSArray::withCapacity(4);
}
require(_childArray, exit);
if (child->_type != kIOHIDElementTypeCollection &&
IsArrayElement(child) &&
child != child->_arrayReportHandler &&
(arrayHeader || !child->_duplicateReportHandler))
{
IOHIDElementPrivate *arrayReportHandler;
char uniqueID[32];
if (!_colArrayReportHandlers) {
_colArrayReportHandlers = OSDictionary::withCapacity(1);
}
require(_colArrayReportHandlers, exit);
snprintf(uniqueID, sizeof(uniqueID), "%4.4x%4.4x%4.4x",
(unsigned)child->_type, (unsigned)child->_reportStartBit, (unsigned)child->_reportID);
arrayReportHandler = (IOHIDElementPrivate *)_colArrayReportHandlers->getObject(uniqueID);
if (arrayReportHandler) {
child->_arrayReportHandler = arrayReportHandler;
} else {
arrayReportHandler = arrayHandlerElement(child->_owner, child->_type, child, this);
require(arrayReportHandler, exit);
_colArrayReportHandlers->setObject(uniqueID, arrayReportHandler);
arrayReportHandler->release();
}
child->_arrayReportHandler = arrayReportHandler;
child->_reportBits = 1;
child->_reportCount = 1;
child->_logicalMin = child->_physicalMin = 0;
child->_logicalMax = child->_physicalMax = 1;
arrayReportHandler->_arrayItems->setObject(child);
}
_childArray->setObject(child);
child->_parent = this;
if (_cookie != 0) {
child->_shouldTickleActivity = _shouldTickleActivity;
}
result = true;
exit:
return result;
}
IOHIDElementPrivate *IOHIDElementPrivate::arrayHandlerElement(
IOHIDElementContainer *owner,
IOHIDElementType type,
IOHIDElementPrivate *child,
IOHIDElementPrivate *parent)
{
IOHIDElementPrivate *element = NULL;
bool result = false;
element = OSTypeAlloc(IOHIDElementPrivate);
require(element && element->init(owner, type), exit);
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->_usage = 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;
element->_rawReportCount = child->_reportCount;
element->_currentReportSizeBits = child->_reportBits * child->_reportCount;
element->_arrayItems = OSArray::withCapacity((child->_usageMax - child->_usageMin) + 1);
require(element->_arrayItems, exit);
element->_oldArraySelectors = (uint32_t *)IOMalloc(sizeof(UInt32) * element->_reportCount);
require(element->_oldArraySelectors, exit);
bzero (element->_oldArraySelectors, sizeof(UInt32) * element->_reportCount);
if (element->_reportCount > 1) {
element->_duplicateReportHandler = element;
element->_duplicateElements = OSArray::withCapacity(element->_reportCount);
require(element->_duplicateElements, exit);
}
owner->registerElement(element, &element->_cookie);
if (parent) {
require(parent->addChildElement(element, false), exit);
}
require(element->createSubElements(), exit);
result = true;
exit:
if (!result && element) {
element->release();
element = NULL;
}
return element;
}
uint32_t IOHIDElementPrivate::getElementValueSize() const
{
uint32_t size = sizeof(IOHIDElementValue);
uint32_t reportWords = (_reportBits * _reportCount) / (sizeof(UInt32) * 8);
reportWords += ((_reportBits * _reportCount) % (sizeof(UInt32) * 8)) ? 1 : 0;
if (reportWords > 1) {
size += ((reportWords - 1) * sizeof(UInt32));
}
return size;
}
#define BIT_MASK(bits) ((1UL << (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_t *src,
uint32_t *dst,
uint32_t bitsToCopy,
uint32_t srcStartBit = 0,
bool shouldSignExtend = false,
bool *valueChanged = 0)
{
uint32_t srcOffset = 0;
uint32_t srcShift = 0;
uint32_t dstShift = 0;
uint32_t dstStartBit = 0;
uint32_t dstOffset = 0;
uint32_t lastDstOffset = 0;
uint32_t word = 0;
uint8_t bitsProcessed = 0;
uint32_t totalBitsProcessed = 0;
while (bitsToCopy) {
uint32_t 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) {
if (lastDstOffset == 0 &&
shouldSignExtend) {
if (totalBitsProcessed < 32 &&
(word & (1 << (totalBitsProcessed - 1))))
{
word |= ~(BIT_MASK(totalBitsProcessed));
}
}
if (dst[lastDstOffset] != word) {
dst[lastDstOffset] = word;
if (valueChanged) {
*valueChanged = true;
}
}
word = 0;
lastDstOffset = dstOffset;
}
}
}
static void writeReportBits(const uint32_t *src,
uint8_t *dst,
uint32_t bitsToCopy,
uint32_t dstStartBit = 0)
{
uint32_t dstOffset = 0;
uint32_t dstShift = 0;
uint32_t srcShift = 0;
uint32_t srcStartBit = 0;
uint32_t srcOffset = 0;
uint8_t bitsProcessed = 0;
uint32_t tmp = 0;
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 IOHIDElementPrivate::processReport(uint8_t reportID,
void *reportData,
uint32_t reportBits,
uint64_t timestamp,
IOHIDElementPrivate **next,
IOOptionBits options)
{
bool changed = false;
bool shouldProcess = false;
uint32_t readSize = 0;
if (_type == kIOHIDElementTypeInput_NULL
&& reportID == _reportID) {
_elementValue->timestamp = timestamp;
*next = NULL;
goto exit;
}
if (next) {
*next = _nextReportHandler;
require(_reportID == reportID, exit);
if (!_variableSize && _reportSize && (reportBits < _reportSize)) {
*next = 0;
return false;
}
if (_isInterruptReportHandler && (options & kIOHIDReportOptionNotInterrupt)) {
return false;
}
if (IsArrayElement(this) && this != _arrayReportHandler) {
*next = _arrayReportHandler;
return false;
}
}
require(_reportID == reportID, exit);
if (_variableSize & kIOHIDElementVariableSizeElement) {
require(_reportStartBit < reportBits, exit);
} else {
uint32_t startingBit = _reportStartBit + (_reportBits * _reportCount);
require(startingBit <= reportBits, exit);
}
if (_usagePage == kHIDPage_KeyboardOrKeypad &&
_usage >= kHIDUsage_KeyboardLeftControl &&
_usage <= kHIDUsage_KeyboardRightGUI &&
_rollOverElementPtr &&
*_rollOverElementPtr &&
(*_rollOverElementPtr)->getValue())
{
uint64_t rollOverTS = (*_rollOverElementPtr)->getTimeStamp();
require(timestamp != rollOverTS, exit);
}
_elementValue->generation++;
_previousValue = _elementValue->value[0];
readSize = _reportBits * _reportCount;
if (_variableSize & kIOHIDElementVariableSizeElement) {
uint32_t remainingBitSize = reportBits - _reportStartBit;
readSize = (remainingBitSize < readSize) ? remainingBitSize : readSize;
}
readReportBits((uint8_t *) reportData,
_elementValue->value,
readSize,
_reportStartBit,
(((int32_t )_logicalMin < 0) || ((int32_t )_logicalMax < 0)),
&changed);
_currentReportSizeBits = readSize;
shouldProcess = (changed || _isInterruptReportHandler || (_flags & kHIDDataRelativeBit));
if (shouldProcess) {
if (((_flags & kHIDDataRelativeBit) == 0) || (_reportBits > 32) || changed || _previousValue) {
_elementValue->timestamp = timestamp;
}
if (IsArrayElement(this) && this == _arrayReportHandler) {
processArrayReport(reportID, reportData, reportBits, _elementValue->timestamp);
}
}
_elementValue->generation++;
if (_transactionState) {
_transactionState = kIOHIDTransactionStateIdle;
}
exit:
return changed;
}
bool IOHIDElementPrivate::createReport(uint8_t reportID,
void *reportData,
uint32_t *reportLength,
IOHIDElementPrivate **next)
{
bool handled = false;
if (_type == kIOHIDElementTypeInput_NULL
&& reportID == _reportID) {
*next = NULL;
goto exit;
}
if (next) {
*next = _nextReportHandler;
}
require(_reportID == reportID, exit);
if (_reportSize) {
*reportLength = _reportSize / 8;
require_action(reportData, exit, {
if (next) {
*next = NULL;
}
});
bzero(reportData, *reportLength);
}
if (next) {
if (IsArrayElement(this)) {
require_action(this == _arrayReportHandler, exit, {
*next = _arrayReportHandler;
});
if (_transactionState == kIOHIDTransactionStateIdle) {
return createArrayReport(reportID, reportData, reportLength);
}
} else if (_duplicateReportHandler) {
require_action(this == _duplicateReportHandler, exit, {
*next = _duplicateReportHandler;
});
if (_transactionState == kIOHIDTransactionStateIdle) {
return createDuplicateReport(reportID, reportData, reportLength);
}
}
}
if (_transactionState == kIOHIDTransactionStateIdle) {
setOutOfBoundsValue();
}
if (reportData) {
writeReportBits(_elementValue->value,
(uint8_t *) reportData,
(_reportBits * _reportCount),
_reportStartBit);
handled = true;
_transactionState = kIOHIDTransactionStateIdle;
}
exit:
return handled;
}
void IOHIDElementPrivate::setMemoryForElementValue(IOVirtualAddress address,
void *location)
{
_elementValue = (IOHIDElementValue *)address;
_elementValueLocation = location;
bzero(_elementValue, getElementValueSize());
_elementValue->cookie = _cookie;
_elementValue->totalSize = getElementValueSize();
}
bool IOHIDElementPrivate::getReportType(IOHIDReportType *reportType) const
{
if (_type <= kIOHIDElementTypeInput_NULL) {
*reportType = kIOHIDReportTypeInput;
} else if (_type == kIOHIDElementTypeOutput) {
*reportType = kIOHIDReportTypeOutput;
} else if (_type == kIOHIDElementTypeFeature) {
*reportType = kIOHIDReportTypeFeature;
} else {
return false;
}
return true;
}
void IOHIDElementPrivate::setOutOfBoundsValue()
{
if (_elementValue->totalSize == sizeof(IOHIDElementValue)) {
if (_logicalMin > 0) {
_elementValue->value[0] = 0;
} else {
if (((_logicalMax - _logicalMin) + 1) < (1 << _reportBits)) {
if (((_logicalMax + 1) & BIT_MASK(_reportBits)) == (_logicalMax + 1)) {
_elementValue->value[0] = _logicalMax + 1;
} else {
_elementValue->value[0] = _logicalMin - 1;
}
}
}
}
}
bool IOHIDElementPrivate::createDuplicateReport(uint8_t reportID,
void *reportData,
uint32_t *reportLength)
{
bool pending = false;
IOHIDElementPrivate *element;
for (unsigned int i = 0; _duplicateElements && i < _duplicateElements->getCount(); i++) {
element = (IOHIDElementPrivate *)_duplicateElements->getObject(i);
if (element->_transactionState == kIOHIDTransactionStatePending) {
pending = true;
}
element->createReport(reportID, reportData, reportLength, 0);
}
return pending;
}
bool IOHIDElementPrivate::createArrayReport(uint8_t reportID,
void *reportData,
uint32_t *reportLength)
{
IOHIDElementPrivate *element, *arrayElement;
uint32_t arraySel;
uint32_t i, reportIndex = 0;
if (createDuplicateReport(reportID, reportData, reportLength)) {
return true;
}
for (i = 0; i < _arrayItems->getCount(); i++) {
element = (IOHIDElementPrivate *)_arrayItems->getObject(i);
if (!element) {
continue;
}
if (element->_transactionState == kIOHIDTransactionStateIdle) {
continue;
}
if (element->_elementValue->value[0] == 0) {
continue;
}
arraySel = i + _logicalMin;
if (_duplicateElements) {
arrayElement = (IOHIDElementPrivate *)_duplicateElements->getObject(reportIndex);
} else {
arrayElement = this;
}
if (arrayElement) {
arrayElement->_elementValue->value[0] = arraySel;
arrayElement->_transactionState = kIOHIDTransactionStatePending;
arrayElement->createReport(reportID, reportData, reportLength, 0);
}
reportIndex++;
element->_transactionState = kIOHIDTransactionStateIdle;
if (reportIndex >= _reportCount) {
break;
}
}
arraySel = 0;
for (i = reportIndex; i < _reportCount; i++) {
if (_duplicateElements) {
arrayElement = (IOHIDElementPrivate *)_duplicateElements->getObject(reportIndex);
} else {
arrayElement = this;
}
if (arrayElement) {
arrayElement->_elementValue->value[0] = arraySel;
arrayElement->_transactionState = kIOHIDTransactionStatePending;
arrayElement->createReport(reportID, reportData, reportLength, 0);
}
}
return true;
}
void IOHIDElementPrivate::setArrayElementValue(uint32_t index, uint32_t value)
{
IOHIDElementPrivate *element;
if (!_arrayItems || (index > _arrayItems->getCount())) {
return;
}
element = (IOHIDElementPrivate *)(_arrayItems->getObject(index));
if (!element) {
return;
}
element->_elementValue->generation ++;
element->_previousValue = element->_elementValue->value[0];
element->_elementValue->value[0] = value;
element->_elementValue->timestamp = _elementValue->timestamp;
element->_elementValue->generation ++;
}
bool IOHIDElementPrivate::processArrayReport(uint8_t reportID,
void *reportData,
uint32_t reportBits,
uint64_t timestamp)
{
IOHIDElementPrivate *element = NULL;
uint32_t arraySel = 0;
uint32_t iNewArray = 0;
uint32_t iOldArray = 0;
bool found = false;
bool changed = false;
if (_duplicateElements) {
bool keyboard = found = (_usagePage == kHIDPage_KeyboardOrKeypad);
for (iNewArray = 0; iNewArray < _reportCount; iNewArray++) {
element = (IOHIDElementPrivate *)_duplicateElements->getObject(iNewArray);
if (!element) {
continue;
}
changed |= element->processReport(reportID, reportData, reportBits, timestamp, 0, 0);
if (keyboard && (element->_elementValue->value[0] != kHIDUsage_KeyboardErrorRollOver)) {
found = false;
}
}
if (!changed) {
return changed;
} else if (keyboard) {
setArrayElementValue(GetArrayItemIndex(kHIDUsage_KeyboardErrorRollOver), (found ? 1 : 0));
if (found) {
return false;
}
}
}
for (iOldArray = 0; iOldArray < _reportCount; iOldArray++) {
arraySel = _oldArraySelectors[iOldArray];
found = false;
for (iNewArray = 0; iNewArray < _reportCount; iNewArray++) {
if (_duplicateElements) {
element = (IOHIDElementPrivate *)_duplicateElements->getObject(iNewArray);
} else {
element = this;
}
if (!element) {
continue;
}
if (arraySel == element->_elementValue->value[0]) {
found = true;
break;
}
}
if (!found) {
setArrayElementValue(GetArrayItemIndex(arraySel), 0);
}
}
for (iNewArray = 0; iNewArray < _reportCount; iNewArray++) {
if (_duplicateElements) {
element = (IOHIDElementPrivate *)_duplicateElements->getObject(iNewArray);
} else {
element = this;
}
if (!element) {
continue;
}
arraySel = element->_elementValue->value[0];
found = false;
for (iOldArray = 0; iOldArray < _reportCount; iOldArray++) {
if (arraySel == _oldArraySelectors[iOldArray]) {
found = true;
break;
}
}
if (!found) {
setArrayElementValue(GetArrayItemIndex(arraySel), 1);
}
}
for (iOldArray = 0; iOldArray < _reportCount; iOldArray++) {
if (_duplicateElements) {
element = (IOHIDElementPrivate *)_duplicateElements->getObject(iOldArray);
} else {
element = this;
}
if (!element) {
continue;
}
_oldArraySelectors[iOldArray] = element->_elementValue->value[0];
}
return changed;
}
OSData *IOHIDElementPrivate::getDataValue()
{
uint32_t byteSize = (UInt32)getCurrentByteSize();
#if defined(__LITTLE_ENDIAN__)
if (_dataValue && _dataValue->getLength() == byteSize) {
bcopy((const void *)_elementValue->value, (void *)_dataValue->getBytesNoCopy(), byteSize);
} else {
OSSafeReleaseNULL(_dataValue);
_dataValue = OSData::withBytes((const void *)_elementValue->value, byteSize);
}
#else
uint32_t bitsToCopy = _currentReportSizeBits;
if (!_dataValue || _dataValue->getLength() != byteSize) {
uint8_t * bytes[byteSize];
OSSafeReleaseNULL(_dataValue);
_dataValue = OSData::withBytes(bytes, byteSize);
}
if (_dataValue) {
bzero((void *)_dataValue->getBytesNoCopy(), byteSize);
writeReportBits((const UInt32*)_elementValue->value, (uint8_t *)_dataValue->getBytesNoCopy(), bitsToCopy);
}
#endif
return _dataValue;
}
OSData *IOHIDElementPrivate::getDataValue(IOOptionBits options)
{
if (options & kIOHIDValueOptionsUpdateElementValues) {
IOReturn status = _owner->updateElementValues(&_cookie, 1);
if (status) {
HIDLogError("getDataValue failed (%lu):%x", (uintptr_t)_cookie, status);
}
}
return getDataValue();
}
void IOHIDElementPrivate::setValue(uint32_t value)
{
uint32_t previousValue = _elementValue->value[0];
if (previousValue == value && !(_flags & kHIDDataRelativeBit)) {
return;
}
_elementValue->value[0] = value;
}
void IOHIDElementPrivate::setDataValue(OSData *value)
{
OSData *previousValue;
if (!value) {
return;
}
previousValue = getDataValue();
setDataBits(value);
}
void IOHIDElementPrivate::setDataBits(OSData *value)
{
uint32_t bitsToCopy;
if (!value) {
return;
}
bitsToCopy = min((value->getLength() << 3), (_reportBits * _reportCount));
readReportBits((const UInt8*)value->getBytesNoCopy(), _elementValue->value, bitsToCopy);
}
IOByteCount IOHIDElementPrivate::getByteSize() const
{
IOByteCount byteSize;
uint32_t bitCount = _reportBits * _reportCount;
byteSize = bitCount >> 3;
byteSize += (bitCount % 8) ? 1 : 0;
return byteSize;
}
IOByteCount IOHIDElementPrivate::getCurrentByteSize()
{
IOByteCount byteSize;
uint32_t bitCount = _currentReportSizeBits;
byteSize = bitCount >> 3;
byteSize += (bitCount % 8) ? 1 : 0;
return byteSize;
}
void IOHIDElementPrivate::setCalibration(uint32_t min,
uint32_t max,
uint32_t saturationMin,
uint32_t saturationMax,
uint32_t deadZoneMin,
uint32_t deadZoneMax,
IOFixed granularity)
{
_calibration.satMin = saturationMin;
_calibration.satMax = saturationMax;
_calibration.dzMin = deadZoneMin;
_calibration.dzMax = deadZoneMax;
_calibration.min = min;
_calibration.max = max;
_calibration.gran = granularity;
}
uint32_t IOHIDElementPrivate::getScaledValue(IOHIDValueScaleType type)
{
int64_t logicalValue = (int32_t )getValue();
int64_t logicalMin = (int32_t )getLogicalMin();
int64_t logicalMax = (int32_t )getLogicalMax();
int64_t logicalRange = 0;
int64_t scaledMin = 0;
int64_t scaledMax = 0;
int64_t scaledRange = 0;
int64_t returnValue = 0;
if (type == kIOHIDValueScaleTypeCalibrated) {
if (_calibration.min != _calibration.max) {
scaledMin = _calibration.min;
scaledMax = _calibration.max;
} else {
scaledMin = -1;
scaledMax = 1;
}
if (_calibration.satMin != _calibration.satMax) {
if (logicalValue <= _calibration.satMin) {
return (UInt32)scaledMin;
}
if (logicalValue >= _calibration.satMax) {
return (UInt32)scaledMax;
}
logicalMin = _calibration.satMin;
logicalMax = _calibration.satMax;
}
if (_calibration.dzMin != _calibration.dzMax) {
int64_t scaledMid = scaledMin + ((scaledMax - scaledMin) / 2);
if (logicalValue < _calibration.dzMin) {
logicalMax = _calibration.dzMin;
scaledMax = scaledMid;
} else if (logicalValue > _calibration.dzMax) {
logicalMin = _calibration.dzMax;
scaledMin = scaledMid;
} else {
return (UInt32)scaledMid;
}
}
} else { scaledMin = getPhysicalMin();
scaledMax = getPhysicalMax();
}
logicalRange = logicalMax - logicalMin;
scaledRange = scaledMax - scaledMin;
if (logicalRange) {
returnValue = ((logicalValue - logicalMin) * scaledRange / logicalRange) + scaledMin;
} else {
returnValue = logicalValue;
}
return (UInt32)returnValue;
}
IOFixed IOHIDElementPrivate::getScaledFixedValue(IOHIDValueScaleType type,
IOOptionBits options)
{
if (options & kIOHIDValueOptionsUpdateElementValues) {
IOReturn status = _owner->updateElementValues(&_cookie, 1);
if (status) {
HIDLogError("updateElementValues failed (%lu):%x", (uintptr_t)_cookie, status);
}
}
return getScaledFixedValue(type);
}
IOFixed IOHIDElementPrivate::getScaledFixedValue(IOHIDValueScaleType type)
{
int64_t logicalValue = (int32_t )getValue();
int64_t logicalMin = (int32_t )getLogicalMin();
int64_t logicalMax = (int32_t )getLogicalMax();
int64_t logicalRange = 0;
int64_t physicalMin = (int32_t )getPhysicalMin();
int64_t physicalMax = (int32_t )getPhysicalMax();
IOFixed returnValue = 0;
if (type == kIOHIDValueScaleTypeCalibrated) {
if (_calibration.min != _calibration.max) {
physicalMin = _calibration.min;
physicalMax = _calibration.max;
} else {
physicalMin = -1;
physicalMax = 1;
}
if (_calibration.satMin != _calibration.satMax) {
if (logicalValue <= _calibration.satMin) {
return (IOFixed) (physicalMin << 16);
}
if (logicalValue >= _calibration.satMax) {
return (IOFixed) (physicalMax << 16);
}
logicalMin = _calibration.satMin;
logicalMax = _calibration.satMax;
}
if (_calibration.dzMin != _calibration.dzMax) {
int64_t physicalMid = physicalMin + ((physicalMax - physicalMin) / 2);
if (logicalValue < _calibration.dzMin) {
logicalMax = _calibration.dzMin;
physicalMax = physicalMid;
} else if (logicalValue > _calibration.dzMax) {
logicalMin = _calibration.dzMax;
physicalMin = physicalMid;
} else {
return (IOFixed)(physicalMid << 16);
}
}
}
int64_t physicalRange = physicalMax - physicalMin;
logicalRange = logicalMax - logicalMin;
if (!logicalRange) {
logicalRange = 1;
}
int64_t num = (logicalValue - logicalMin) * physicalRange + physicalMin * logicalRange;
int64_t denom = logicalRange;
if (type == kIOHIDValueScaleTypeExponent) {
int resExponent = _unitExponent & 0x0F;
if (resExponent < 8) {
for (int i = resExponent; i > 0; i--) {
num *= 10;
}
} else {
for (int i = 0x10 - resExponent; i > 0; i--) {
denom *= 10;
}
}
}
returnValue = (IOFixed)((num << 16) / denom);
return returnValue;
}
uint32_t IOHIDElementPrivate::getValue(IOOptionBits options)
{
uint32_t newValue = 0;
if ((_reportBits * _reportCount) <= 32) {
if (options & kIOHIDValueOptionsUpdateElementValues) {
IOReturn status = _owner->updateElementValues(&_cookie, 1);
if (status) {
HIDLogError("updateElementValues failed (%lu):%x", (uintptr_t)_cookie, status);
}
}
newValue = (options & kIOHIDValueOptionsFlagPrevious) ? _previousValue : _elementValue->value[0];
if (options & kIOHIDValueOptionsFlagRelativeSimple) {
if ((_flags & kIOHIDElementFlagsWrapMask) && newValue == getLogicalMin() && _previousValue == getLogicalMax()) {
newValue = 1;
} else if ((_flags & kIOHIDElementFlagsWrapMask) && newValue == getLogicalMax() && _previousValue == getLogicalMin()) {
newValue = -1;
} else {
newValue -= _previousValue;
}
}
}
return newValue;
}
IOReturn IOHIDElementPrivate::commit(IOHIDElementCommitDirection direction)
{
IOReturn ret = kIOReturnError;
if (direction == kIOHIDElementCommitDirectionIn) {
ret = _owner->updateElementValues(&_cookie, 1);
} else {
ret = _owner->postElementValues(&_cookie, 1);
}
if (ret != kIOReturnSuccess) {
HIDLogError("commit failed (%lu %d): 0x%x", (uintptr_t)_cookie, direction, ret);
}
return ret;
}
void IOHIDElementPrivate::setRollOverElementPtr(IOHIDElementPrivate **rollOverElementPtr)
{
_rollOverElementPtr = rollOverElementPtr;
}
void IOHIDElementPrivate::setReportSize(uint32_t numberOfBits)
{
_reportSize = numberOfBits;
}
bool IOHIDElementPrivate::shouldTickleActivity() const
{
return _shouldTickleActivity;
}
IOHIDElementPrivate *IOHIDElementPrivate::getNextReportHandler() const
{
return _nextReportHandler;
}
void IOHIDElementPrivate::setNextReportHandler(IOHIDElementPrivate *element)
{
_nextReportHandler = element;
}
uint32_t IOHIDElementPrivate::getTransactionState() const
{
return _transactionState;
}
void IOHIDElementPrivate::setTransactionState(uint32_t state)
{
_transactionState = state;
}
IOHIDElementCookie IOHIDElementPrivate::getCookie(void)
{
return _cookie;
}
IOHIDElementType IOHIDElementPrivate::getType(void)
{
return _type;
}
IOHIDElementCollectionType IOHIDElementPrivate::getCollectionType(void)
{
return _collectionType;
}
OSArray *IOHIDElementPrivate::getChildElements(void)
{
return _childArray;
}
IOHIDElementPrivate *IOHIDElementPrivate::getParentElement(void)
{
return _parent;
}
uint32_t IOHIDElementPrivate::getUsagePage(void)
{
return _usagePage;
}
uint32_t IOHIDElementPrivate::getUsage(void)
{
return _usage;
}
uint32_t IOHIDElementPrivate::getReportID(void)
{
return _reportID;
}
uint32_t IOHIDElementPrivate::getFlags(void)
{
return _flags;
}
uint32_t IOHIDElementPrivate::getReportSize(void)
{
return _reportSize;
}
uint32_t IOHIDElementPrivate::getReportBits(void)
{
return _reportBits;
}
uint32_t IOHIDElementPrivate::getReportCount(void)
{
return _reportCount;
}
uint32_t IOHIDElementPrivate::getLogicalMin(void)
{
return _logicalMin;
}
uint32_t IOHIDElementPrivate::getLogicalMax(void)
{
return _logicalMax;
}
uint32_t IOHIDElementPrivate::getPhysicalMin(void)
{
return _physicalMin;
}
uint32_t IOHIDElementPrivate::getPhysicalMax(void)
{
return _physicalMax;
}
uint32_t IOHIDElementPrivate::getUnit(void)
{
return _units;
}
uint32_t IOHIDElementPrivate::getUnitExponent(void)
{
return _unitExponent;
}
uint64_t IOHIDElementPrivate::getTimeStamp(void)
{
return _elementValue->timestamp;
}
uint32_t IOHIDElementPrivate::getValue(void)
{
return getValue(0);
}
bool IOHIDElementPrivate::isVariableSize()
{
return _variableSize & kIOHIDElementVariableSizeElement;
}
void IOHIDElementPrivate::setVariableSizeInfo(uint8_t variableSize)
{
_variableSize = variableSize;
}
uint8_t IOHIDElementPrivate::getVariableSizeInfo()
{
return _variableSize;
}