#include <IOKit/IOLib.h> // IOMalloc/IOFree
#include <IOKit/IOBufferMemoryDescriptor.h>
#include "IOHIDDevice.h"
#include "IOHIDElement.h"
#include "IOHIDParserPriv.h"
#undef super
#define super IOService
OSDefineMetaClassAndAbstractStructors( IOHIDDevice, IOService )
#define kReportHandlerSlots 8
#define GetReportHandlerSlot(id) ((id) & (kReportHandlerSlots - 1))
#define GetElement(index) \
(IOHIDElement *) _elementArray->getObject((UInt32)index)
#define ELEMENT_LOCK IOLockLock( _elementLock )
#define ELEMENT_UNLOCK IOLockUnlock( _elementLock )
struct IOHIDReportHandler
{
IOHIDElement * head[ kIOHIDReportTypeCount ];
};
#define GetHeadElement(slot, type) _reportHandlers[slot].head[type]
#ifdef DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
bool IOHIDDevice::init( OSDictionary * dict )
{
return super::init(dict);
}
void IOHIDDevice::free()
{
if ( _reportHandlers )
{
IOFree( _reportHandlers,
sizeof(IOHIDReportHandler) * kReportHandlerSlots );
_reportHandlers = 0;
}
if ( _elementArray )
{
_elementArray->release();
_elementArray = 0;
}
if ( _elementValuesDescriptor )
{
_elementValuesDescriptor->release();
_elementValuesDescriptor = 0;
}
if ( _elementLock )
{
IOLockFree( _elementLock );
_elementLock = 0;
}
return super::free();
}
bool IOHIDDevice::start( IOService * provider )
{
IOMemoryDescriptor * reportDescriptor;
IOReturn ret;
if ( super::start(provider) != true )
return false;
_elementLock = IOLockAlloc();
if ( _elementLock == 0 )
return false;
_reportHandlers = (IOHIDReportHandler *)
IOMalloc( sizeof(IOHIDReportHandler) *
kReportHandlerSlots );
if ( _reportHandlers == 0 )
return false;
bzero( _reportHandlers, sizeof(IOHIDReportHandler) * kReportHandlerSlots );
if ( handleStart(provider) != true )
return false;
if ( ( newReportDescriptor(&reportDescriptor) != kIOReturnSuccess ) ||
( reportDescriptor == 0 ) )
return false;
ret = parseReportDescriptor( reportDescriptor );
reportDescriptor->release();
if ( ret != kIOReturnSuccess )
return false;
_readyForInputReports = true;
if ( publishProperties(provider) != true )
return false;
registerService();
return true;
}
void IOHIDDevice::stop(IOService * provider)
{
handleStop(provider);
if ( _elementLock )
{
ELEMENT_LOCK;
_readyForInputReports = false;
ELEMENT_UNLOCK;
}
super::stop(provider);
}
bool IOHIDDevice::publishProperties(IOService * provider)
{
OSObject * prop;
#define SET_PROP(func, key) \
do { \
prop = func ## (); \
if (prop) { \
setProperty(key, prop); \
prop->release(); \
} \
} while (0)
SET_PROP( newTransportString, kIOHIDTransportKey );
SET_PROP( newVendorIDNumber, kIOHIDVendorIDKey );
SET_PROP( newProductIDNumber, kIOHIDProductIDKey );
SET_PROP( newVersionNumber, kIOHIDVersionNumberKey );
SET_PROP( newManufacturerString, kIOHIDManufacturerKey );
SET_PROP( newProductString, kIOHIDProductKey );
SET_PROP( newSerialNumber, kIOHIDSerialNumberKey );
SET_PROP( newPrimaryUsageNumber, kIOHIDPrimaryUsageKey );
SET_PROP( newPrimaryUsagePageNumber, kIOHIDPrimaryUsagePageKey );
return true;
}
bool IOHIDDevice::handleStart(IOService * provider)
{
return true;
}
void IOHIDDevice::handleStop(IOService * provider)
{
}
IOReturn IOHIDDevice::newUserClient( task_t owningTask,
void * security_id,
UInt32 type,
IOUserClient ** handler )
{
return super::newUserClient(owningTask, security_id, type, handler);
}
OSString * IOHIDDevice::newTransportString() const
{
return 0;
}
OSString * IOHIDDevice::newManufacturerString() const
{
return 0;
}
OSString * IOHIDDevice::newProductString() const
{
return 0;
}
OSNumber * IOHIDDevice::newVendorIDNumber() const
{
return 0;
}
OSNumber * IOHIDDevice::newProductIDNumber() const
{
return 0;
}
OSNumber * IOHIDDevice::newVersionNumber() const
{
return 0;
}
OSNumber * IOHIDDevice::newSerialNumber() const
{
return 0;
}
OSNumber * IOHIDDevice::newPrimaryUsageNumber() const
{
return 0;
}
OSNumber * IOHIDDevice::newPrimaryUsagePageNumber() const
{
return 0;
}
IOReturn IOHIDDevice::handleReport( IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options )
{
AbsoluteTime currentTime;
void * reportData;
IOByteCount reportLength;
IOByteCount segmentSize;
IOReturn ret = kIOReturnNotReady;
if ( reportType != kIOHIDReportTypeInput )
return kIOReturnUnsupported;
clock_get_uptime( ¤tTime );
reportData = report->getVirtualSegment(0, &segmentSize);
reportLength = report->getLength();
if ( reportLength == 0 )
return kIOReturnBadArgument;
if ( segmentSize != reportLength )
{
reportData = IOMalloc( reportLength );
if ( reportData == 0 )
return kIOReturnNoMemory;
report->readBytes( 0, reportData, reportLength );
}
ELEMENT_LOCK;
if ( _readyForInputReports )
{
IOHIDElement * element;
UInt8 reportID;
reportID = ( _reportCount > 1 ) ? *((UInt8 *) reportData) : 0;
element = GetHeadElement( GetReportHandlerSlot(reportID),
kIOHIDReportTypeInput );
while ( element )
{
element->processReport( reportID,
reportData,
reportLength << 3,
¤tTime,
&element );
}
ret = kIOReturnSuccess;
}
ELEMENT_UNLOCK;
if ( segmentSize != reportLength )
{
IOFree( reportData, reportLength );
}
#if 0 // XXX - debugging
{
UInt32 * buf = (UInt32 *) _elementValuesDescriptor->getBytesNoCopy();
for (UInt32 words = 0; words < (_elementValuesDescriptor->getLength() / 4);
words+=6, buf+=6)
{
IOLog("%3ld: %08lx %08lx %08lx %08lx %08lx %08lx\n",
words,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
}
}
#endif
return ret;
}
IOReturn IOHIDDevice::getReport( IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options )
{
return kIOReturnUnsupported;
}
IOReturn IOHIDDevice::setReport( IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options = 0 )
{
return kIOReturnUnsupported;
}
IOReturn IOHIDDevice::parseReportDescriptor( IOMemoryDescriptor * report,
IOOptionBits options )
{
OSStatus status;
HIDPreparsedDataRef parseData;
void * reportData;
IOByteCount reportLength;
IOByteCount segmentSize;
IOReturn ret;
reportData = report->getVirtualSegment(0, &segmentSize);
reportLength = report->getLength();
if ( segmentSize != reportLength )
{
reportData = IOMalloc( reportLength );
if ( reportData == 0 )
return kIOReturnNoMemory;
report->readBytes( 0, reportData, reportLength );
}
status = HIDOpenReportDescriptor(
reportData,
reportLength,
&parseData,
0 );
if ( segmentSize != reportLength )
{
IOFree( reportData, reportLength );
}
if ( status != kHIDSuccess )
{
return kIOReturnError;
}
ret = createElementHierarchy( parseData );
getReportCountAndSizes( parseData );
HIDCloseReportDescriptor( parseData );
return ret;
}
IOReturn
IOHIDDevice::createElementHierarchy( HIDPreparsedDataRef parseData )
{
OSStatus status;
HIDCaps caps;
IOReturn ret = kIOReturnNoMemory;
bool success;
do {
status = HIDGetCaps( parseData, &caps );
if ( status != kHIDSuccess )
{
ret = kIOReturnError;
break;
}
DLOG("Report bytes: input:%ld output:%ld feature:%ld\n",
caps.inputReportByteLength,
caps.outputReportByteLength,
caps.featureReportByteLength);
DLOG("Collections : %ld\n", caps.numberCollectionNodes);
DLOG("Buttons : input:%ld output:%ld feature:%ld\n",
caps.numberInputButtonCaps,
caps.numberOutputButtonCaps,
caps.numberFeatureButtonCaps);
DLOG("Values : input:%ld output:%ld feature:%ld\n",
caps.numberInputValueCaps,
caps.numberOutputValueCaps,
caps.numberFeatureValueCaps);
_maxInputReportSize = caps.inputReportByteLength;
_maxOutputReportSize = caps.outputReportByteLength;
_maxFeatureReportSize = caps.featureReportByteLength;
_elementArray = OSArray::withCapacity(
caps.numberCollectionNodes +
caps.numberInputButtonCaps +
caps.numberInputValueCaps +
caps.numberOutputButtonCaps +
caps.numberOutputValueCaps +
caps.numberFeatureButtonCaps +
caps.numberFeatureValueCaps +
10 );
if ( _elementArray == 0 ) break;
_elementArray->setCapacityIncrement(10);
success = createCollectionElements(
parseData,
_elementArray,
caps.numberCollectionNodes );
if ( success != true )
break;
_dataElementIndex = _elementArray->getCount();
if ( !createButtonElements( parseData,
_elementArray,
kHIDInputReport,
kIOHIDElementTypeInput_Button,
caps.numberInputButtonCaps ) ) break;
if ( !createButtonElements( parseData,
_elementArray,
kHIDOutputReport,
kIOHIDElementTypeOutput,
caps.numberOutputButtonCaps ) ) break;
if ( !createButtonElements( parseData,
_elementArray,
kHIDFeatureReport,
kIOHIDElementTypeFeature,
caps.numberFeatureButtonCaps ) ) break;
if ( !createValueElements( parseData,
_elementArray,
kHIDInputReport,
kIOHIDElementTypeInput_Misc,
caps.numberInputValueCaps ) ) break;
if ( !createValueElements( parseData,
_elementArray,
kHIDOutputReport,
kIOHIDElementTypeOutput,
caps.numberOutputValueCaps ) ) break;
if ( !createValueElements( parseData,
_elementArray,
kHIDFeatureReport,
kIOHIDElementTypeFeature,
caps.numberFeatureValueCaps ) ) break;
_elementValuesDescriptor = createMemoryForElementValues();
if ( _elementValuesDescriptor == 0 )
break;
IOHIDElement * root = (IOHIDElement *) _elementArray->getObject( 0 );
if ( root )
{
setProperty( kIOHIDElementKey, root->getChildArray() );
}
ret = kIOReturnSuccess;
}
while ( false );
return ret;
}
bool IOHIDDevice::getReportCountAndSizes( HIDPreparsedDataRef parseData )
{
HIDPreparsedDataPtr data = (HIDPreparsedDataPtr) parseData;
HIDReportSizes * report = data->reports;
_reportCount = data->reportCount;
DLOG("Report count: %ld\n", _reportCount);
for ( UInt32 num = 0; num < data->reportCount; num++, report++ )
{
DLOG("Report ID: %ld input:%ld output:%ld feature:%ld\n",
report->reportID,
report->inputBitCount,
report->outputBitCount,
report->featureBitCount);
setReportSize( report->reportID,
kIOHIDReportTypeInput,
report->inputBitCount );
setReportSize( report->reportID,
kIOHIDReportTypeOutput,
report->outputBitCount );
setReportSize( report->reportID,
kIOHIDReportTypeFeature,
report->featureBitCount );
}
return true;
}
bool IOHIDDevice::setReportSize( UInt8 reportID,
IOHIDReportType reportType,
UInt32 numberOfBits )
{
IOHIDElement * element;
bool ret = false;
element = GetHeadElement( GetReportHandlerSlot(reportID), reportType );
while ( element )
{
if ( element->getReportID() == reportID )
{
element->setReportSize( numberOfBits );
ret = true;
break;
}
element = element->getNextReportHandler();
}
return ret;
}
bool
IOHIDDevice::createCollectionElements( HIDPreparsedDataRef parseData,
OSArray * array,
UInt32 maxCount )
{
OSStatus status;
HIDCollectionNodePtr collections;
UInt32 count = maxCount;
bool ret = false;
UInt32 index;
do {
collections = (HIDCollectionNodePtr)
IOMalloc( maxCount * sizeof(HIDCollectionNode) );
if ( collections == 0 ) break;
status = HIDGetCollectionNodes(
collections,
&count,
parseData );
if ( status != kHIDSuccess ) break;
for ( index = 0; index < count; index++ )
{
IOHIDElement * element;
element = IOHIDElement::collectionElement(
this,
kIOHIDElementTypeCollection,
&collections[index] );
if ( element == 0 ) break;
element->release();
}
if ( index < count ) break;
for ( index = 1; index < count; index++ )
{
if ( !linkToParent( array, collections[index].parent, index ) )
break;
}
if ( index < count ) break;
ret = true;
}
while ( false );
if ( collections )
IOFree( collections, maxCount * sizeof(HIDCollectionNode) );
return ret;
}
bool IOHIDDevice::linkToParent( const OSArray * array,
UInt32 parentIndex,
UInt32 childIndex )
{
IOHIDElement * child = (IOHIDElement *) array->getObject( childIndex );
IOHIDElement * parent = (IOHIDElement *) array->getObject( parentIndex );
return ( parent ) ? parent->addChildElement( child ) : false;
}
bool IOHIDDevice::createButtonElements( HIDPreparsedDataRef parseData,
OSArray * array,
UInt32 hidReportType,
IOHIDElementType elementType,
UInt32 maxCount )
{
OSStatus status;
HIDButtonCapsPtr buttons = 0;
UInt32 count = maxCount;
bool ret = false;
IOHIDElement * element;
IOHIDElement * parent;
do {
if ( maxCount == 0 )
{
ret = true;
break;
}
buttons = (HIDButtonCapsPtr) IOMalloc( maxCount *
sizeof(HIDButtonCaps) );
if ( buttons == 0 ) break;
status = HIDGetButtonCaps( hidReportType,
buttons,
&count,
parseData );
if ( status != kHIDSuccess ) break;
ret = true;
for ( UInt32 i = 0; i < count; i++ )
{
parent = (IOHIDElement *) array->getObject(
buttons[i].collection );
element = IOHIDElement::buttonElement(
this,
elementType,
&buttons[i],
parent );
if ( element == 0 )
{
ret = false;
break;
}
element->release();
}
}
while ( false );
if ( buttons )
IOFree( buttons, maxCount * sizeof(HIDButtonCaps) );
return ret;
}
bool IOHIDDevice::createValueElements( HIDPreparsedDataRef parseData,
OSArray * array,
UInt32 hidReportType,
IOHIDElementType elementType,
UInt32 maxCount )
{
OSStatus status;
HIDValueCapsPtr values = 0;
UInt32 count = maxCount;
bool ret = false;
IOHIDElement * element;
IOHIDElement * parent;
do {
if ( maxCount == 0 )
{
ret = true;
break;
}
values = (HIDValueCapsPtr) IOMalloc( maxCount *
sizeof(HIDValueCaps) );
if ( values == 0 ) break;
status = HIDGetValueCaps( hidReportType,
values,
&count,
parseData );
if ( status != kHIDSuccess ) break;
ret = true;
for ( UInt32 i = 0; i < count; i++ )
{
parent = (IOHIDElement *) array->getObject(
values[i].collection );
element = IOHIDElement::valueElement(
this,
elementType,
&values[i],
parent );
if ( element == 0 )
{
ret = false;
break;
}
element->release();
}
}
while ( false );
if ( values )
IOFree( values, maxCount * sizeof(HIDValueCaps) );
return ret;
}
bool IOHIDDevice::registerElement( IOHIDElement * element,
IOHIDElementCookie * cookie )
{
IOHIDReportType reportType;
UInt32 index = _elementArray->getCount();
if ( _elementArray->setObject( index, element ) != true )
{
return false;
}
if ( element->getReportType( &reportType ) )
{
IOHIDReportHandler * reportHandler;
UInt32 slot;
slot = GetReportHandlerSlot( element->getReportID() );
reportHandler = &_reportHandlers[slot];
if ( reportHandler->head[reportType] )
{
element->setNextReportHandler( reportHandler->head[reportType] );
}
reportHandler->head[reportType] = element;
}
*cookie = (IOHIDElementCookie) index;
return true;
}
IOBufferMemoryDescriptor * IOHIDDevice::createMemoryForElementValues()
{
IOBufferMemoryDescriptor * descriptor;
IOHIDElement * element;
UInt32 capacity = 0;
UInt8 * start;
UInt8 * buffer;
for ( UInt32 slot = 0; slot < kReportHandlerSlots; slot++ )
{
for ( UInt32 type = 0; type < kIOHIDReportTypeCount; type++ )
{
element = GetHeadElement(slot, type);
while ( element )
{
capacity += element->getElementValueSize();
element = element->getNextReportHandler();
}
}
}
DLOG("Element value capacity %ld\n", capacity);
descriptor = IOBufferMemoryDescriptor::withOptions(
kIOMemorySharingTypeMask,
capacity );
if ( ( descriptor == 0 ) || ( descriptor->getBytesNoCopy() == 0 ) )
{
if ( descriptor ) descriptor->release();
return 0;
}
start = buffer = (UInt8 *) descriptor->getBytesNoCopy();
for ( UInt32 slot = 0; slot < kReportHandlerSlots; slot++ )
{
for ( UInt32 type = 0; type < kIOHIDReportTypeCount; type++ )
{
element = GetHeadElement(slot, type);
while ( element )
{
assert ( buffer < (start + capacity) );
element->setMemoryForElementValue( (IOVirtualAddress) buffer,
(void *) (buffer - start) );
buffer += element->getElementValueSize();
element = element->getNextReportHandler();
}
}
}
return descriptor;
}
IOMemoryDescriptor * IOHIDDevice::getMemoryWithCurrentElementValues() const
{
return _elementValuesDescriptor;
}
IOReturn IOHIDDevice::startEventDelivery( IOHIDEventQueue * queue,
IOHIDElementCookie cookie,
IOOptionBits options )
{
IOHIDElement * element;
UInt32 elementIndex = (UInt32) cookie;
IOReturn ret = kIOReturnBadArgument;
if ( ( queue == 0 ) || ( elementIndex < _dataElementIndex ) )
return kIOReturnBadArgument;
ELEMENT_LOCK;
do {
if (( element = GetElement(elementIndex) ) == 0)
break;
ret = element->addEventQueue( queue ) ?
kIOReturnSuccess : kIOReturnNoMemory;
}
while ( false );
ELEMENT_UNLOCK;
return ret;
}
IOReturn IOHIDDevice::stopEventDelivery( IOHIDEventQueue * queue,
IOHIDElementCookie cookie )
{
IOHIDElement * element;
UInt32 elementIndex = (UInt32) cookie;
bool removed = false;
if ( elementIndex == 0 )
elementIndex = _dataElementIndex;
else if ( (queue == 0 ) || ( elementIndex < _dataElementIndex ) )
return kIOReturnBadArgument;
ELEMENT_LOCK;
do {
if (( element = GetElement(elementIndex++) ) == 0)
break;
removed = element->removeEventQueue( queue ) || removed;
}
while ( cookie == 0 );
ELEMENT_UNLOCK;
return removed ? kIOReturnSuccess : kIOReturnNotFound;
}
IOReturn IOHIDDevice::checkEventDelivery( IOHIDEventQueue * queue,
IOHIDElementCookie cookie,
bool * started )
{
IOHIDElement * element = GetElement( cookie );
if ( !queue || !element || !started )
return kIOReturnBadArgument;
ELEMENT_LOCK;
*started = element->hasEventQueue( queue );
ELEMENT_UNLOCK;
return kIOReturnSuccess;
}
OSMetaClassDefineReservedUnused(IOHIDDevice, 0);
OSMetaClassDefineReservedUnused(IOHIDDevice, 1);
OSMetaClassDefineReservedUnused(IOHIDDevice, 2);
OSMetaClassDefineReservedUnused(IOHIDDevice, 3);
OSMetaClassDefineReservedUnused(IOHIDDevice, 4);
OSMetaClassDefineReservedUnused(IOHIDDevice, 5);
OSMetaClassDefineReservedUnused(IOHIDDevice, 6);
OSMetaClassDefineReservedUnused(IOHIDDevice, 7);
OSMetaClassDefineReservedUnused(IOHIDDevice, 8);
OSMetaClassDefineReservedUnused(IOHIDDevice, 9);
OSMetaClassDefineReservedUnused(IOHIDDevice, 10);
OSMetaClassDefineReservedUnused(IOHIDDevice, 11);
OSMetaClassDefineReservedUnused(IOHIDDevice, 12);
OSMetaClassDefineReservedUnused(IOHIDDevice, 13);
OSMetaClassDefineReservedUnused(IOHIDDevice, 14);
OSMetaClassDefineReservedUnused(IOHIDDevice, 15);
OSMetaClassDefineReservedUnused(IOHIDDevice, 16);
OSMetaClassDefineReservedUnused(IOHIDDevice, 17);
OSMetaClassDefineReservedUnused(IOHIDDevice, 18);
OSMetaClassDefineReservedUnused(IOHIDDevice, 19);
OSMetaClassDefineReservedUnused(IOHIDDevice, 20);
OSMetaClassDefineReservedUnused(IOHIDDevice, 21);
OSMetaClassDefineReservedUnused(IOHIDDevice, 22);
OSMetaClassDefineReservedUnused(IOHIDDevice, 23);
OSMetaClassDefineReservedUnused(IOHIDDevice, 24);
OSMetaClassDefineReservedUnused(IOHIDDevice, 25);
OSMetaClassDefineReservedUnused(IOHIDDevice, 26);
OSMetaClassDefineReservedUnused(IOHIDDevice, 27);
OSMetaClassDefineReservedUnused(IOHIDDevice, 28);
OSMetaClassDefineReservedUnused(IOHIDDevice, 29);
OSMetaClassDefineReservedUnused(IOHIDDevice, 30);
OSMetaClassDefineReservedUnused(IOHIDDevice, 31);