#include <TargetConditionals.h>
#include <IOKit/IOLib.h> // IOMalloc/IOFree
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/hidsystem/IOHIDSystem.h>
#include "IOHIDDevice.h"
#include "IOHIDElementPrivate.h"
#include "IOHIDParserPriv.h"
#include "IOHIDInterface.h"
#include "IOHIDPrivateKeys.h"
#include "IOHIDFamilyPrivate.h"
#include "IOHIDLibUserClient.h"
#include "IOHIDFamilyTrace.h"
#if !TARGET_OS_EMBEDDED
#include "IOHIKeyboard.h"
#include "IOHIPointing.h"
#endif
#ifndef kIOMessageDeviceSignaledWakeup
#define kIOMessageDeviceSignaledWakeup iokit_common_msg(0x350)
#endif
#undef super
#define super IOService
OSDefineMetaClassAndAbstractStructors( IOHIDDevice, IOService )
#define _clientSet _reserved->clientSet
#define _seizedClient _reserved->seizedClient
#define _eventDeadline _reserved->eventDeadline
#define _inputInterruptElementArray _reserved->inputInterruptElementArray
#define _publishDisplayNotify _reserved->publishDisplayNotify
#define _performTickle _reserved->performTickle
#define _performWakeTickle _reserved->performWakeTickle
#define _interfaceNub _reserved->interfaceNub
#define _rollOverElement _reserved->rollOverElement
#define _hierarchElements _reserved->hierarchElements
#define kIOHIDEventThreshold 10
#define kReportHandlerSlots 8
#define GetReportHandlerSlot(id) ((id) & (kReportHandlerSlots - 1))
#define GetElement(index) \
(IOHIDElementPrivate *) _elementArray->getObject((UInt32)index)
#define ELEMENT_LOCK IORecursiveLockLock( _elementLock )
#define ELEMENT_UNLOCK IORecursiveLockUnlock( _elementLock )
struct IOHIDReportHandler
{
IOHIDElementPrivate * 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
#ifndef kIOUserClientCrossEndianKey
#define kIOUserClientCrossEndianKey "IOUserClientCrossEndian"
#endif
#ifndef kIOUserClientCrossEndianCompatibleKey
#define kIOUserClientCrossEndianCompatibleKey "IOUserClientCrossEndianCompatible"
#endif
static SInt32 g3DGameControllerCount = 0;
static IOService *gDisplayManager = 0;
static IONotifier *gDeviceMatchedNotifier = 0;
bool IOHIDDevice::_publishDisplayNotificationHandler(void * target,
void * ,
IOService * newService,
IONotifier * )
{
IOHIDDevice * self = (IOHIDDevice *) target;
if( newService->metaCast("IODisplayWrangler")) {
if( !gDisplayManager) {
gDisplayManager = newService;
}
if ( self->_publishDisplayNotify ) {
self->_publishDisplayNotify->remove();
self->_publishDisplayNotify = 0;
}
}
return true;
}
bool IOHIDDevice::init( OSDictionary * dict )
{
_reserved = IONew( ExpansionData, 1 );
if (!_reserved)
return false;
bzero(_reserved, sizeof(ExpansionData));
_clientSet = OSSet::withCapacity(2);
if ( _clientSet == 0 )
return false;
return super::init(dict);
}
void IOHIDDevice::free()
{
if ( _reportHandlers )
{
IOFree( _reportHandlers,
sizeof(IOHIDReportHandler) * kReportHandlerSlots );
_reportHandlers = 0;
}
if ( _elementArray )
{
_elementArray->release();
_elementArray = 0;
}
if ( _hierarchElements )
{
_hierarchElements->release();
_hierarchElements = 0;
}
if ( _elementValuesDescriptor )
{
_elementValuesDescriptor->release();
_elementValuesDescriptor = 0;
}
if ( _elementLock )
{
IORecursiveLockFree( _elementLock );
_elementLock = 0;
}
if ( _clientSet )
{
assert(_clientSet->getCount() == 0);
_clientSet->release();
_clientSet = 0;
}
if (_publishDisplayNotify)
{
_publishDisplayNotify->remove();
_publishDisplayNotify = 0;
}
if (_inputInterruptElementArray)
{
_inputInterruptElementArray->release();
_inputInterruptElementArray = 0;
}
if ( _reserved )
{
IODelete( _reserved, ExpansionData, 1 );
}
return super::free();
}
static inline OSArray * CreateHierarchticalElementList(IOHIDElement * root)
{
OSArray * resultArray = 0;
OSArray * subElements = 0;
OSArray * elements = 0;
IOHIDElement * element = 0;
IOItemCount count;
if ( !root ) return NULL;
elements = root->getChildElements();
if ( !elements ) return NULL;
count = elements->getCount();
resultArray = OSArray::withCapacity(count);
if ( !resultArray ) return NULL;
for ( UInt32 index=0; index < count; index++ )
{
element = OSDynamicCast(IOHIDElement, elements->getObject(index));
if ( !element ) continue;
resultArray->setObject(element);
subElements = CreateHierarchticalElementList(element);
if ( subElements )
{
resultArray->merge(subElements);
subElements->release();
subElements = 0;
}
}
return resultArray;
}
bool IOHIDDevice::start( IOService * provider )
{
IOMemoryDescriptor * reportDescriptor;
IOReturn ret;
if ( super::start(provider) != true )
return false;
_elementLock = IORecursiveLockAlloc();
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;
#if 0
IOMemoryMap *tempMap = reportDescriptor->map();
if (tempMap) {
OSData *descriptor = OSData::withBytes((void*)tempMap->getVirtualAddress(), tempMap->getSize());
if (descriptor) {
setProperty(kIOHIDReportDescriptorKey, descriptor);
descriptor->release();
}
tempMap->release();
}
#endif
ret = parseReportDescriptor( reportDescriptor );
reportDescriptor->release();
if ( ret != kIOReturnSuccess )
return false;
_hierarchElements = CreateHierarchticalElementList((IOHIDElement *)_elementArray->getObject( 0 ));
if ( _hierarchElements == NULL )
return false;
_interfaceNub = IOHIDInterface::withElements( _hierarchElements );
if ( _interfaceNub == NULL )
return false;
_readyForInputReports = true;
if ( publishProperties(provider) != true )
return false;
OSNumber *primaryUsagePage = OSDynamicCast(OSNumber,
getProperty(kIOHIDPrimaryUsagePageKey));
OSNumber *primaryUsage = OSDynamicCast(OSNumber,
getProperty(kIOHIDPrimaryUsageKey));
if ((primaryUsagePage && (primaryUsagePage->unsigned32BitValue() == 0x05)) &&
(primaryUsage && (primaryUsage->unsigned32BitValue() == 0x01))) {
OSIncrementAtomic(&g3DGameControllerCount);
}
if (!gDeviceMatchedNotifier) {
OSDictionary * propertyMatch = serviceMatching("IOHIDDevice");
gDeviceMatchedNotifier = addMatchingNotification(gIOFirstMatchNotification,
propertyMatch,
IOHIDDevice::_publishDeviceNotificationHandler,
NULL);
propertyMatch->release();
}
registerService();
return true;
}
bool IOHIDDevice::_publishDeviceNotificationHandler(void * target __unused,
void * refCon __unused,
IOService * newService,
IONotifier * notifier __unused)
{
IOHIDDevice *self = OSDynamicCast(IOHIDDevice, newService);
if (self) {
if ( self->_interfaceNub->attach(self) )
{
if (!self->_interfaceNub->start(self))
{
self->_interfaceNub->detach(self);
self->_interfaceNub->release();
self->_interfaceNub = 0;
}
}
else
{
self->_interfaceNub->release();
self->_interfaceNub = 0;
}
}
return true;
}
void IOHIDDevice::stop(IOService * provider)
{
OSNumber *primaryUsagePage = OSDynamicCast(OSNumber,
getProperty(kIOHIDPrimaryUsagePageKey));
OSNumber *primaryUsage = OSDynamicCast(OSNumber,
getProperty(kIOHIDPrimaryUsageKey));
if ((primaryUsagePage && (primaryUsagePage->unsigned32BitValue() == 0x05)) &&
(primaryUsage && (primaryUsage->unsigned32BitValue() == 0x01))) {
OSDecrementAtomic(&g3DGameControllerCount);
}
handleStop(provider);
if ( _elementLock )
{
ELEMENT_LOCK;
_readyForInputReports = false;
ELEMENT_UNLOCK;
}
if (_interfaceNub)
{
_interfaceNub->release();
_interfaceNub = 0;
}
super::stop(provider);
}
bool IOHIDDevice::matchPropertyTable(OSDictionary * table, SInt32 * score)
{
bool match = true;
if (super::matchPropertyTable(table, score) == false) return false;
match = MatchPropertyTable(this, table, score);
if (!match && (g3DGameControllerCount <= 0) && table) {
OSNumber *primaryUsage = OSDynamicCast(OSNumber, table->getObject(kIOHIDPrimaryUsageKey));
OSNumber *primaryUsagePage = OSDynamicCast(OSNumber, table->getObject(kIOHIDPrimaryUsagePageKey));
if ((primaryUsage && (primaryUsage->unsigned32BitValue() == 0x01)) &&
(primaryUsagePage && (primaryUsagePage->unsigned32BitValue() == 0x05))) {
match = true;
IOLog("IOHIDManager: It appears that an application is attempting to locate an invalid device. A workaround is in currently in place, but will be removed after version 10.2\n");
}
}
return match;
}
bool IOHIDDevice::publishProperties(IOService * provider)
{
OSObject * prop;
#define SET_PROP_FROM_VALUE(key, value) \
do { \
prop = value; \
if (prop) { \
setProperty(key, prop); \
_interfaceNub->setProperty(key, prop); \
} \
} while (0)
#define SET_PROP_FROM_FUNCTION(key, func) \
do { \
prop = func (); \
if (prop) { \
setProperty(key, prop); \
_interfaceNub->setProperty(key, prop); \
prop->release(); \
} \
} while (0)
SET_PROP_FROM_FUNCTION( kIOHIDTransportKey, newTransportString );
SET_PROP_FROM_FUNCTION( kIOHIDVendorIDKey, newVendorIDNumber );
SET_PROP_FROM_FUNCTION( kIOHIDVendorIDSourceKey, newVendorIDSourceNumber );
SET_PROP_FROM_FUNCTION( kIOHIDProductIDKey, newProductIDNumber );
SET_PROP_FROM_FUNCTION( kIOHIDVersionNumberKey, newVersionNumber );
SET_PROP_FROM_FUNCTION( kIOHIDManufacturerKey, newManufacturerString );
SET_PROP_FROM_FUNCTION( kIOHIDProductKey, newProductString );
SET_PROP_FROM_FUNCTION( kIOHIDLocationIDKey, newLocationIDNumber );
SET_PROP_FROM_FUNCTION( kIOHIDCountryCodeKey, newCountryCodeNumber );
SET_PROP_FROM_FUNCTION( kIOHIDSerialNumberKey, newSerialNumberString );
SET_PROP_FROM_FUNCTION( kIOHIDPrimaryUsageKey, newPrimaryUsageNumber );
SET_PROP_FROM_FUNCTION( kIOHIDPrimaryUsagePageKey, newPrimaryUsagePageNumber );
SET_PROP_FROM_FUNCTION( kIOHIDReportIntervalKey, newReportIntervalNumber );
SET_PROP_FROM_FUNCTION( kIOHIDDeviceUsagePairsKey, newDeviceUsagePairs );
if ( getProvider() )
{
SET_PROP_FROM_VALUE("BootProtocol", getProvider()->getProperty("bInterfaceProtocol"));
SET_PROP_FROM_VALUE("HIDDefaultBehavior", getProperty("HIDDefaultBehavior"));
}
return true;
}
bool IOHIDDevice::handleStart(IOService * provider __unused)
{
return true;
}
void IOHIDDevice::handleStop(IOService * provider __unused)
{
}
static inline bool ShouldPostDisplayActivityTickles(IOService *device, OSSet * clientSet, bool isSeized)
{
OSNumber * primaryUsagePage;
if (!clientSet->getCount() ||
!(primaryUsagePage = OSDynamicCast(OSNumber, device->getProperty(kIOHIDPrimaryUsagePageKey))) ||
(primaryUsagePage->unsigned32BitValue() != kHIDPage_GenericDesktop))
return false;
OSCollectionIterator * iterator;
OSObject * object;
bool returnValue = true;
if ( !isSeized && (iterator = OSCollectionIterator::withCollection(clientSet)) )
{
bool done = false;
while (!done) {
iterator->reset();
while (!done && (NULL != (object = iterator->getNextObject()))) {
if ( object->metaCast("IOHIDEventService"))
{
returnValue = false;
done = true;
}
}
if (iterator->isValid()) {
done = true;
}
}
iterator->release();
}
return returnValue;
}
static inline bool ShouldPostDisplayActivityTicklesForWakeDevice(
IOService *device, OSSet * clientSet, bool isSeized)
{
OSNumber * primaryUsagePage = OSDynamicCast(OSNumber, device->getProperty(kIOHIDPrimaryUsagePageKey));
if (!primaryUsagePage)
return false;
if (primaryUsagePage->unsigned32BitValue() == kHIDPage_Consumer)
return true;
if (!clientSet->getCount() ||
(primaryUsagePage->unsigned32BitValue() != kHIDPage_GenericDesktop))
return false;
OSCollectionIterator * iterator;
OSObject * object;
bool returnValue = false;
if ( !isSeized && (iterator = OSCollectionIterator::withCollection(clientSet)) )
{
bool done = false;
while (!done) {
iterator->reset();
while (!done && (NULL != (object = iterator->getNextObject()))) {
if (object->metaCast("IOHIDEventService"))
{
returnValue = true;
done = true;
}
}
if (iterator->isValid()) {
done = true;
}
}
iterator->release();
}
return returnValue;
}
bool IOHIDDevice::handleOpen(IOService * client,
IOOptionBits options,
void * argument __unused)
{
bool accept = false;
do {
if ( _seizedClient )
break;
if ( _clientSet->containsObject(client) )
{
DLOG("%s: multiple opens from client %lx\n",
getName(), (UInt32) client);
accept = true;
break;
}
if ( _clientSet->setObject(client) == false )
break;
if (options & kIOServiceSeize)
{
messageClients( kIOMessageServiceIsRequestingClose, (void *) options);
_seizedClient = client;
#if !TARGET_OS_EMBEDDED
IOHIKeyboard * keyboard = OSDynamicCast(IOHIKeyboard, getProvider());
IOHIPointing * pointing = OSDynamicCast(IOHIPointing, getProvider());
if ( keyboard )
keyboard->IOHIKeyboard::message(kIOHIDSystemDeviceSeizeRequestMessage, this, (void *)true);
else if ( pointing )
pointing->IOHIPointing::message(kIOHIDSystemDeviceSeizeRequestMessage, this, (void *)true);
#endif
}
accept = true;
}
while (false);
_performTickle = ShouldPostDisplayActivityTickles(this, _clientSet, _seizedClient);
_performWakeTickle = ShouldPostDisplayActivityTicklesForWakeDevice(this, _clientSet, _seizedClient);
if ( (_performTickle || _performWakeTickle) && !gDisplayManager )
{
OSDictionary *matching = serviceMatching("IODisplayWrangler");
_publishDisplayNotify = addMatchingNotification(gIOPublishNotification,
matching,
&IOHIDDevice::_publishDisplayNotificationHandler,
this, 0 );
matching->release();
}
return accept;
}
void IOHIDDevice::handleClose(IOService * client, IOOptionBits options __unused)
{
if ( _clientSet->containsObject(client) )
{
_clientSet->removeObject(client);
if (client == _seizedClient)
{
_seizedClient = 0;
#if !TARGET_OS_EMBEDDED
IOHIKeyboard * keyboard = OSDynamicCast(IOHIKeyboard, getProvider());
IOHIPointing * pointing = OSDynamicCast(IOHIPointing, getProvider());
if ( keyboard )
keyboard->IOHIKeyboard::message(kIOHIDSystemDeviceSeizeRequestMessage, this, (void *)false);
else if ( pointing )
pointing->IOHIPointing::message(kIOHIDSystemDeviceSeizeRequestMessage, this, (void *)false);
#endif
}
_performTickle = ShouldPostDisplayActivityTickles(this, _clientSet, _seizedClient);
_performWakeTickle = ShouldPostDisplayActivityTicklesForWakeDevice(this, _clientSet, _seizedClient);
}
}
bool IOHIDDevice::handleIsOpen(const IOService * client) const
{
if (client)
return _clientSet->containsObject(client);
else
return (_clientSet->getCount() > 0);
}
IOReturn IOHIDDevice::newUserClient( task_t owningTask,
void * security_id,
UInt32 type,
OSDictionary * properties,
IOUserClient ** handler )
{
if ( type == kIOHIDLibUserClientConnectManager ) {
if ( isInactive() ) {
IOLog( "IOHIDDevice::newUserClient called on an inactive device\n" );
*handler = NULL;
return kIOReturnNotReady;
}
if ( properties ) {
properties->setObject( kIOUserClientCrossEndianCompatibleKey, kOSBooleanTrue );
}
IOWorkLoop *loop = getWorkLoop();
IOReturn result = kIOReturnNotReady;
if ( loop ) {
result = loop->runAction( OSMemberFunctionCast( IOWorkLoop::Action, this, &IOHIDDevice::newUserClientGated ),
this, owningTask, security_id, properties, handler );
}
else {
IOLog( "IOHIDDevice::newUserClient failed to get a workloop\n" );
}
return result;
}
return super::newUserClient( owningTask, security_id, type, properties, handler );
}
IOReturn IOHIDDevice::newUserClientGated( task_t owningTask,
void * security_id,
OSDictionary * properties,
IOUserClient ** handler )
{
IOUserClient * client = new IOHIDLibUserClient;
if ( !client->initWithTask( owningTask, security_id, kIOHIDLibUserClientConnectManager, properties ) ) {
client->release();
return kIOReturnBadArgument;
}
if ( !client->attach( this ) ) {
client->release();
return kIOReturnUnsupported;
}
if ( !client->start( this ) ) {
client->detach( this );
client->release();
return kIOReturnUnsupported;
}
*handler = client;
return kIOReturnSuccess;
}
IOReturn IOHIDDevice::message( UInt32 type, IOService * provider, void * argument )
{
if ((kIOMessageDeviceSignaledWakeup == type) &&
_performWakeTickle && gDisplayManager)
{
gDisplayManager->activityTickle(0,0);
return kIOReturnSuccess;
}
return super::message(type, provider, argument);
}
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
{
OSArray * childArray;
IOHIDElementPrivate * child;
IOHIDElementPrivate * root;
if ( (root = (IOHIDElementPrivate *) _elementArray->getObject(0)) &&
(childArray = root->getChildElements()) &&
(child = (IOHIDElementPrivate *) childArray->getObject(0)) )
{
return OSNumber::withNumber(child->getUsage(), 32);
}
return 0;
}
OSNumber * IOHIDDevice::newPrimaryUsagePageNumber() const
{
OSArray * childArray;
IOHIDElementPrivate * child;
IOHIDElementPrivate * root;
if ( (root = (IOHIDElementPrivate *) _elementArray->getObject(0)) &&
(childArray = root->getChildElements()) &&
(child = (IOHIDElementPrivate *) childArray->getObject(0)) )
{
return OSNumber::withNumber(child->getUsagePage(), 32);
}
return 0;
}
IOReturn IOHIDDevice::handleReport( IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options )
{
AbsoluteTime currentTime;
clock_get_uptime( ¤tTime );
return handleReportWithTime( currentTime, report, reportType, options );
}
IOReturn IOHIDDevice::getReport( IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options )
{
return getReport(report, reportType, options, 0, 0);
}
IOReturn IOHIDDevice::setReport( IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options)
{
return setReport(report, reportType, options, 0, 0);
}
IOReturn IOHIDDevice::parseReportDescriptor( IOMemoryDescriptor * report,
IOOptionBits options __unused )
{
OSStatus status = kIOReturnError;
HIDPreparsedDataRef parseData;
void * reportData;
IOByteCount reportLength;
IOReturn ret;
reportLength = report->getLength();
if ( !reportLength )
return kIOReturnBadArgument;
reportData = IOMalloc(reportLength);
if ( !reportData )
return kIOReturnNoMemory;
report->readBytes( 0, reportData, reportLength );
status = HIDOpenReportDescriptor(
reportData,
reportLength,
&parseData,
0 );
IOFree( reportData, reportLength );
if ( status != kHIDSuccess )
{
return kIOReturnError;
}
ret = createElementHierarchy( parseData );
getReportCountAndSizes( parseData );
HIDCloseReportDescriptor( parseData );
return ret;
}
IOReturn
IOHIDDevice::createElementHierarchy( HIDPreparsedDataRef parseData )
{
OSStatus status;
HIDCapabilities caps;
IOReturn ret = kIOReturnNoMemory;
do {
status = HIDGetCapabilities( 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;
setProperty(kIOHIDMaxInputReportSizeKey, _maxInputReportSize, 32);
setProperty(kIOHIDMaxOutputReportSizeKey, _maxOutputReportSize, 32);
setProperty(kIOHIDMaxFeatureReportSizeKey, _maxFeatureReportSize, 32);
_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);
if ( !createCollectionElements(
parseData,
_elementArray,
caps.numberCollectionNodes ) ) 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;
if ( !createReportHandlerElements(parseData) ) break;
_elementValuesDescriptor = createMemoryForElementValues();
if ( _elementValuesDescriptor == 0 )
break;
IOHIDElementPrivate * root = (IOHIDElementPrivate *) _elementArray->getObject( 0 );
if ( root )
{
setProperty( kIOHIDElementKey, root->getChildElements() );
}
setProperty(kIOHIDInputReportElementsKey,
_inputInterruptElementArray);
ret = kIOReturnSuccess;
}
while ( false );
return ret;
}
static OSDictionary * CreateDeviceUsagePairFromElement(IOHIDElementPrivate * element)
{
OSDictionary * pair = 0;
OSNumber * usage = 0;
OSNumber * usagePage = 0;
OSNumber * type = 0;
pair = OSDictionary::withCapacity(2);
usage = OSNumber::withNumber(element->getUsage(), 32);
usagePage = OSNumber::withNumber(element->getUsagePage(), 32);
type = OSNumber::withNumber(element->getCollectionType(), 32);
pair->setObject(kIOHIDDeviceUsageKey, usage);
pair->setObject(kIOHIDDeviceUsagePageKey, usagePage);
usage->release();
usagePage->release();
type->release();
return pair;
}
OSArray * IOHIDDevice::newDeviceUsagePairs()
{
IOHIDElementPrivate * element = 0;
OSArray * functions = 0;
OSDictionary * pair = 0;
UInt32 elementCount = _elementArray->getCount();
if ( elementCount <= 1 ) return NULL;
functions = OSArray::withCapacity(2);
for (unsigned i=1; i<elementCount; i++)
{
element = (IOHIDElementPrivate *)_elementArray->getObject(i);
if ((element->getType() == kIOHIDElementTypeCollection) &&
((element->getCollectionType() == kIOHIDElementCollectionTypeApplication) ||
(element->getCollectionType() == kIOHIDElementCollectionTypePhysical)))
{
pair = CreateDeviceUsagePairFromElement(element);
UInt32 pairCount = functions->getCount();
bool found = false;
for(unsigned j=0; j<pairCount; j++)
{
OSDictionary *tempPair = (OSDictionary *)functions->getObject(j);
found = tempPair->isEqualTo(pair);
if (found)
break;
}
if (!found)
{
functions->setObject(functions->getCount(), pair);
}
pair->release();
}
}
if ( ! functions->getCount() ) {
pair = CreateDeviceUsagePairFromElement((IOHIDElementPrivate *)_elementArray->getObject(1));
functions->setObject(pair);
pair->release();
}
return functions;
}
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 )
{
IOHIDElementPrivate * 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;
HIDCollectionExtendedNodePtr collections;
UInt32 count = maxCount;
bool ret = false;
UInt32 index;
do {
collections = (HIDCollectionExtendedNodePtr)
IOMalloc( maxCount * sizeof(HIDCollectionExtendedNode) );
if ( collections == 0 ) break;
status = HIDGetCollectionExtendedNodes(
collections,
&count,
parseData );
if ( status != kHIDSuccess ) break;
for ( index = 0; index < count; index++ )
{
IOHIDElementPrivate * element;
element = IOHIDElementPrivate::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(HIDCollectionExtendedNode) );
return ret;
}
bool IOHIDDevice::linkToParent( const OSArray * array,
UInt32 parentIndex,
UInt32 childIndex )
{
IOHIDElementPrivate * child = (IOHIDElementPrivate *) array->getObject( childIndex );
IOHIDElementPrivate * parent = (IOHIDElementPrivate *) array->getObject( parentIndex );
return ( parent ) ? parent->addChildElement( child ) : false;
}
bool IOHIDDevice::createButtonElements( HIDPreparsedDataRef parseData,
OSArray * array,
UInt32 hidReportType,
IOHIDElementType elementType,
UInt32 maxCount )
{
OSStatus status;
HIDButtonCapabilitiesPtr buttons = 0;
UInt32 count = maxCount;
bool ret = false;
IOHIDElementPrivate * element;
IOHIDElementPrivate * parent;
do {
if ( maxCount == 0 )
{
ret = true;
break;
}
buttons = (HIDButtonCapabilitiesPtr) IOMalloc( maxCount *
sizeof(HIDButtonCapabilities) );
if ( buttons == 0 ) break;
status = HIDGetButtonCapabilities( hidReportType,
buttons,
&count,
parseData );
if ( status != kHIDSuccess ) break;
ret = true;
for ( UInt32 i = 0; i < count; i++ )
{
parent = (IOHIDElementPrivate *) array->getObject(
buttons[i].collection );
element = IOHIDElementPrivate::buttonElement(
this,
elementType,
&buttons[i],
parent );
if ( element == 0 )
{
ret = false;
break;
}
element->release();
}
}
while ( false );
if ( buttons )
IOFree( buttons, maxCount * sizeof(HIDButtonCapabilities) );
return ret;
}
bool IOHIDDevice::createValueElements( HIDPreparsedDataRef parseData,
OSArray * array,
UInt32 hidReportType,
IOHIDElementType elementType,
UInt32 maxCount )
{
OSStatus status;
HIDValueCapabilitiesPtr values = 0;
UInt32 count = maxCount;
bool ret = false;
IOHIDElementPrivate * element;
IOHIDElementPrivate * parent;
do {
if ( maxCount == 0 )
{
ret = true;
break;
}
values = (HIDValueCapabilitiesPtr) IOMalloc( maxCount *
sizeof(HIDValueCapabilities) );
if ( values == 0 ) break;
status = HIDGetValueCapabilities( hidReportType,
values,
&count,
parseData );
if ( status != kHIDSuccess ) break;
ret = true;
for ( UInt32 i = 0; i < count; i++ )
{
parent = (IOHIDElementPrivate *) array->getObject(
values[i].collection );
element = IOHIDElementPrivate::valueElement(
this,
elementType,
&values[i],
parent );
if ( element == 0 )
{
ret = false;
break;
}
element->release();
}
}
while ( false );
if ( values )
IOFree( values, maxCount * sizeof(HIDValueCapabilities) );
return ret;
}
bool IOHIDDevice::createReportHandlerElements( HIDPreparsedDataRef parseData)
{
HIDPreparsedDataPtr data = (HIDPreparsedDataPtr) parseData;
HIDReportSizes * report = data->reports;
IOHIDElementPrivate * element = 0;
if ( !(_inputInterruptElementArray = OSArray::withCapacity(data->reportCount)))
return false;
for ( UInt32 num = 0; num < data->reportCount; num++, report++ )
{
element = IOHIDElementPrivate::reportHandlerElement(
this,
kIOHIDElementTypeInput_Misc,
report->reportID,
report->inputBitCount);
if ( element == 0 )
continue;
_inputInterruptElementArray->setObject(element);
element->release();
}
return true;
}
bool IOHIDDevice::registerElement( IOHIDElementPrivate * 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;
if ( element->getUsagePage() == kHIDPage_KeyboardOrKeypad )
{
UInt32 usage = element->getUsage();
if ( usage == kHIDUsage_KeyboardErrorRollOver)
_rollOverElement = element;
if ( usage >= kHIDUsage_KeyboardLeftControl && usage <= kHIDUsage_KeyboardRightGUI )
element->setRollOverElementPtr(&(_rollOverElement));
}
}
*cookie = (IOHIDElementCookie) index;
return true;
}
IOBufferMemoryDescriptor * IOHIDDevice::createMemoryForElementValues()
{
IOBufferMemoryDescriptor * descriptor;
IOHIDElementPrivate * element;
UInt32 capacity = 0;
UInt8 * beginning;
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(
kIOMemoryKernelUserShared,
capacity );
if ( ( descriptor == 0 ) || ( descriptor->getBytesNoCopy() == 0 ) ) {
if ( descriptor ) descriptor->release();
return 0;
}
beginning = 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 < (beginning + capacity) );
element->setMemoryForElementValue( (IOVirtualAddress) buffer,
(void *) (buffer - beginning));
buffer += element->getElementValueSize();
element = element->getNextReportHandler();
}
}
}
return descriptor;
}
IOMemoryDescriptor * IOHIDDevice::getMemoryWithCurrentElementValues() const
{
return _elementValuesDescriptor;
}
IOReturn IOHIDDevice::startEventDelivery( IOHIDEventQueue * queue,
IOHIDElementCookie cookie,
IOOptionBits options __unused)
{
IOHIDElementPrivate * 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 )
{
IOHIDElementPrivate * 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 )
{
IOHIDElementPrivate * element = GetElement( cookie );
if ( !queue || !element || !started )
return kIOReturnBadArgument;
ELEMENT_LOCK;
*started = element->hasEventQueue( queue );
ELEMENT_UNLOCK;
return kIOReturnSuccess;
}
#define SetCookiesTransactionState(element, cookies, count, state, index, offset) \
for (index = offset; index < count; index++) { \
element = GetElement(cookies[index]); \
if (element == NULL) \
continue; \
element->setTransactionState (state); \
}
OSMetaClassDefineReservedUsed(IOHIDDevice, 0);
IOReturn IOHIDDevice::updateElementValues(IOHIDElementCookie *cookies, UInt32 cookieCount) {
IOMemoryDescriptor * report = NULL;
IOHIDElementPrivate * element = NULL;
IOHIDReportType reportType;
IOByteCount maxReportLength;
UInt8 reportID;
UInt32 index;
IOReturn ret = kIOReturnError;
maxReportLength = max(_maxOutputReportSize,
max(_maxFeatureReportSize, _maxInputReportSize));
report = IOBufferMemoryDescriptor::withCapacity(maxReportLength, kIODirectionNone);
if (report == NULL)
return kIOReturnNoMemory;
ELEMENT_LOCK;
SetCookiesTransactionState(element, cookies,
cookieCount, kIOHIDTransactionStatePending, index, 0);
for (index = 0; index < cookieCount; index++) {
element = GetElement(cookies[index]);
if (element == NULL)
continue;
if ( element->getTransactionState()
!= kIOHIDTransactionStatePending )
continue;
if ( !element->getReportType(&reportType) )
continue;
reportID = element->getReportID();
ELEMENT_UNLOCK;
ret = getReport(report, reportType, reportID);
ELEMENT_LOCK;
if (ret != kIOReturnSuccess)
break;
ret = handleReport(report, reportType, kIOHIDReportOptionNotInterrupt);
if (ret != kIOReturnSuccess)
break;
}
report->release();
SetCookiesTransactionState(element, cookies,
cookieCount, kIOHIDTransactionStateIdle, index, 0);
ELEMENT_UNLOCK;
return ret;
}
OSMetaClassDefineReservedUsed(IOHIDDevice, 1);
IOReturn IOHIDDevice::postElementValues(IOHIDElementCookie * cookies, UInt32 cookieCount)
{
IOBufferMemoryDescriptor *report = NULL;
IOHIDElementPrivate *element = NULL;
IOHIDElementPrivate *cookieElement = NULL;
UInt8 *reportData = NULL;
UInt32 maxReportLength = 0;
UInt32 reportLength = 0;
IOHIDReportType reportType;
UInt8 reportID = 0;
UInt32 index;
IOReturn ret = kIOReturnError;
if (cookieCount == 0)
return ret;
maxReportLength = max(_maxOutputReportSize, _maxFeatureReportSize);
report = IOBufferMemoryDescriptor::withCapacity(maxReportLength, kIODirectionNone);
if ( report == NULL )
return kIOReturnNoMemory;
ELEMENT_LOCK;
SetCookiesTransactionState(cookieElement, cookies,
cookieCount, kIOHIDTransactionStatePending, index, 0);
reportData = (UInt8 *)report->getBytesNoCopy();
for (index = 0; index < cookieCount; index ++) {
cookieElement = GetElement(cookies[index]);
if ( cookieElement == NULL )
continue;
if ( cookieElement->getTransactionState()
!= kIOHIDTransactionStatePending )
continue;
if ( !cookieElement->getReportType(&reportType) )
continue;
reportID = cookieElement->getReportID();
element = GetHeadElement(GetReportHandlerSlot(reportID), reportType);
while ( element ) {
element->createReport(reportID, reportData, &reportLength, &element);
if ( reportLength ) {
report->setLength(reportLength);
reportLength = 0;
}
}
if ( _reportCount > 1 )
reportData[0] = reportID;
ELEMENT_UNLOCK;
ret = setReport( report, reportType, reportID);
ELEMENT_LOCK;
if ( ret != kIOReturnSuccess )
break;
}
SetCookiesTransactionState(cookieElement, cookies,
cookieCount, kIOHIDTransactionStateIdle, index, 0);
ELEMENT_UNLOCK;
if ( report )
report->release();
return ret;
}
OSMetaClassDefineReservedUsed(IOHIDDevice, 2);
OSString * IOHIDDevice::newSerialNumberString() const
{
OSString * string = 0;
OSNumber * number = newSerialNumber();
if ( number )
{
char str[11];
snprintf(str, sizeof (str), "%d", number->unsigned32BitValue());
string = OSString::withCString(str);
number->release();
}
return string;
}
OSMetaClassDefineReservedUsed(IOHIDDevice, 3);
OSNumber * IOHIDDevice::newLocationIDNumber() const
{
return 0;
}
OSMetaClassDefineReservedUsed(IOHIDDevice, 4);
IOReturn IOHIDDevice::getReport( IOMemoryDescriptor * report __unused,
IOHIDReportType reportType __unused,
IOOptionBits options __unused,
UInt32 completionTimeout __unused,
IOHIDCompletion * completion __unused)
{
return kIOReturnUnsupported;
}
OSMetaClassDefineReservedUsed(IOHIDDevice, 5);
IOReturn IOHIDDevice::setReport( IOMemoryDescriptor * report __unused,
IOHIDReportType reportType __unused,
IOOptionBits options __unused,
UInt32 completionTimeout __unused,
IOHIDCompletion * completion __unused)
{
return kIOReturnUnsupported;
}
OSMetaClassDefineReservedUsed(IOHIDDevice, 6);
OSNumber * IOHIDDevice::newVendorIDSourceNumber() const
{
return 0;
}
OSMetaClassDefineReservedUsed(IOHIDDevice, 7);
OSNumber * IOHIDDevice::newCountryCodeNumber() const
{
return 0;
}
OSMetaClassDefineReservedUsed(IOHIDDevice, 8);
IOReturn IOHIDDevice::handleReportWithTime(
AbsoluteTime timeStamp,
IOMemoryDescriptor * report,
IOHIDReportType reportType,
IOOptionBits options)
{
void * reportData;
IOByteCount reportLength;
IOReturn ret = kIOReturnNotReady;
bool changed = false;
bool shouldTickle = false;
UInt8 reportID = 0;
IOHID_DEBUG(kIOHIDDebugCode_HandleReport, reportType, options, __OSAbsoluteTime(timeStamp), this);
if ( !report )
return kIOReturnBadArgument;
reportLength = report->getLength();
if ( !reportLength )
return kIOReturnBadArgument;
reportData = IOMalloc(reportLength);
if ( !reportData )
return kIOReturnNoMemory;
report->readBytes( 0, reportData, reportLength );
ELEMENT_LOCK;
if ( _readyForInputReports ) {
IOHIDElementPrivate * element;
reportID = ( _reportCount > 1 ) ? *((UInt8 *) reportData) : 0;
element = GetHeadElement( GetReportHandlerSlot(reportID),
reportType);
while ( element ) {
shouldTickle |= element->shouldTickleActivity();
changed |= element->processReport( reportID,
reportData,
reportLength << 3,
&timeStamp,
&element,
options );
}
ret = kIOReturnSuccess;
}
if ( ( reportType == kIOHIDReportTypeInput ) &&
(( options & kIOHIDReportOptionNotInterrupt ) == 0 ) && _interfaceNub && !_seizedClient) {
_interfaceNub->handleReport(timeStamp, report, reportType, reportID, options);
}
ELEMENT_UNLOCK;
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
if (gDisplayManager
&& changed
&& shouldTickle
&& _performTickle
&& (CMP_ABSOLUTETIME(&timeStamp, &_eventDeadline) > 0))
{
AbsoluteTime ts;
nanoseconds_to_absolutetime(kIOHIDEventThreshold, &ts);
_eventDeadline = ts;
ADD_ABSOLUTETIME(&_eventDeadline, &timeStamp);
gDisplayManager->activityTickle(0,0);
}
return ret;
}
OSMetaClassDefineReservedUsed(IOHIDDevice, 9);
OSNumber * IOHIDDevice::newReportIntervalNumber() const
{
UInt32 interval = 8000; OSNumber *number = OSDynamicCast(OSNumber, getProperty(kIOHIDReportIntervalKey, gIOServicePlane, kIORegistryIterateRecursively | kIORegistryIterateParents));
if ( number )
interval = number->unsigned32BitValue();
return OSNumber::withNumber(interval, 32);
}
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);
#ifndef __ppc__
OSMetaClassDefineReservedUnused(IOHIDDevice, 32);
OSMetaClassDefineReservedUnused(IOHIDDevice, 33);
OSMetaClassDefineReservedUnused(IOHIDDevice, 34);
OSMetaClassDefineReservedUnused(IOHIDDevice, 35);
OSMetaClassDefineReservedUnused(IOHIDDevice, 36);
OSMetaClassDefineReservedUnused(IOHIDDevice, 37);
OSMetaClassDefineReservedUnused(IOHIDDevice, 38);
OSMetaClassDefineReservedUnused(IOHIDDevice, 39);
OSMetaClassDefineReservedUnused(IOHIDDevice, 40);
#endif