AppleUSBProKbd.cpp [plain text]
#include "AppleUSBProKbd.h"
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOService.h>
#include <IOKit/IOMessage.h>
#include <IOKit/hidsystem/ev_keymap.h>
#include <IOKit/hidsystem/IOHIDDescriptorParser.h>
#include <IOKit/hidsystem/IOHIKeyboard.h>
#include <IOKit/hidsystem/IOHIDShared.h>
#include <IOKit/usb/IOUSBInterface.h>
#include <IOKit/usb/IOUSBPipe.h>
#include <IOKit/usb/USB.h>
#include <IOKit/usb/IOUSBLog.h>
#define super IOHIKeyboard
#define DEBUGGING_LEVEL 0
#define kMaxValues 32
#define kVolumeUp 0x06
#define kVolumeDown 0x07
#define kVolumeMute 0x08
#define kEject 0x10
OSDefineMetaClassAndStructors( AppleUSBProKbd, IOHIKeyboard )
#pragma mark -
#pragma mark еее inherited еее
bool
AppleUSBProKbd::init(OSDictionary *properties)
{
if (!super::init(properties)) return false;
mSoundUpIsPressed = false;
mSoundDownIsPressed = false;
mOutstandingIO = 0;
mNeedToClose = false;
return true;
}
bool AppleUSBProKbd::start( IOService * provider )
{
IOReturn err = kIOReturnSuccess;
IOWorkLoop *wl;
USBLog(3, "%s[%p]::start - beginning - retain count = %d", getName(), this, getRetainCount());
mInterface = OSDynamicCast(IOUSBInterface, provider);
if (!mInterface)
return false;
if( mInterface->open( this ) == false )
{
USBError(1, "%s[%p]::start - unable to open provider. returning false", getName(), this);
return false;
}
do {
mGate = IOCommandGate::commandGate(this);
if(!mGate)
{
USBError(1, "%s[%p]::start - unable to create command gate", getName(), this);
break;
}
wl = getWorkLoop();
if (!wl)
{
USBError(1, "%s[%p]::start - unable to find my workloop", getName(), this);
break;
}
if (wl->addEventSource(mGate) != kIOReturnSuccess)
{
USBError(1, "%s[%p]::start - unable to add gate to work loop", getName(), this);
break;
}
IOUSBFindEndpointRequest endpointRequest;
endpointRequest.type = kUSBInterrupt;
endpointRequest.direction = kUSBIn;
mInterruptPipe = mInterface->FindNextPipe( NULL, &endpointRequest );
if( !mInterruptPipe )
{
USBError(1, "%s[%p]::start - unable to get interrupt pipe", getName(), this);
break;
}
if( VerifyNewDevice() == false )
{
USBError(1, "%s[%p]::start - VerifyNewDevice was not successful. Ignoring this device", getName(), this);
break;
}
mMaxPacketSize = endpointRequest.maxPacketSize;
mReadDataBuffer = IOBufferMemoryDescriptor::withCapacity( mMaxPacketSize, kIODirectionIn );
mCompletionRoutine.target = (void *) this;
mCompletionRoutine.action = (IOUSBCompletionAction) AppleUSBProKbd::InterruptReadHandlerEntry;
mCompletionRoutine.parameter = (void *) 0;
mReadDataBuffer->setLength( mMaxPacketSize );
IncrementOutstandingIO();
if( (err = mInterruptPipe->Read( mReadDataBuffer, &mCompletionRoutine )) )
{
USBError(1, "%s[%p]::start - err (%x) in interrupt read, retain count %d after release", getName(), this, err, getRetainCount());
DecrementOutstandingIO();
break;
}
USBError(1, "%s[%p]::start AppleUSBProKeyboard @ %d (0x%x)", getName(), this, mInterface->GetDevice()->GetAddress(), strtol(mInterface->GetDevice()->getLocation(), (char **)NULL, 16));
if( !super::start(mInterface))
{
USBError(1, "%s[%p]::start - unable to start superclass. returning false", getName(), this);
break; }
return true;
} while (false);
USBLog(3, "%s[%p]::start aborting. err = 0x%x", getName(), this, err);
if( mPreparsedReportDescriptorData )
HIDCloseReportDescriptor( mPreparsedReportDescriptorData );
if ( mInterruptPipe )
{
mInterruptPipe->Abort();
mInterruptPipe = NULL;
}
stop( provider );
provider->close( this );
return false;
}
void AppleUSBProKbd::stop( IOService * provider )
{
if( mPreparsedReportDescriptorData )
HIDCloseReportDescriptor( mPreparsedReportDescriptorData );
if (mGate)
{
IOWorkLoop *wl = getWorkLoop();
if (wl)
wl->removeEventSource(mGate);
mGate->release();
mGate = NULL;
}
super::stop( provider );
}
unsigned AppleUSBProKbd::eventFlags()
{
return( mEventFlags );
}
bool AppleUSBProKbd::alphaLock()
{
return( mCapsLockOn );
}
const unsigned char * AppleUSBProKbd::defaultKeymapOfLength( UInt32 * length )
{
static const unsigned char AppleProKeyboardKeyMap[] =
{
0x00,0x00,
0x00,
0x00,
0x00,
0x04,
NX_KEYTYPE_SOUND_UP, kVolumeUp,
NX_KEYTYPE_SOUND_DOWN, kVolumeDown,
NX_KEYTYPE_MUTE, kVolumeMute,
NX_KEYTYPE_EJECT, kEject
};
if( length ) *length = sizeof( AppleProKeyboardKeyMap );
return( AppleProKeyboardKeyMap );
}
#pragma mark -
#pragma mark еее Implementation еее
bool AppleUSBProKbd::VerifyNewDevice ()
{
IOReturn err;
bool success = true;
UInt16 size = 0;
OSStatus result;
IOUSBDevRequest devReq;
IOUSBHIDDescriptor hidDescriptor;
HIDButtonCaps buttonCaps[kMaxValues];
UInt32 buttonCapsSize = kMaxValues;
UInt8 * reportDescriptor = NULL;
do
{
devReq.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBInterface);
devReq.bRequest = kUSBRqGetDescriptor;
devReq.wValue = (0x20 | kUSBHIDDesc) << 8;
devReq.wIndex = mInterface->GetInterfaceNumber();
devReq.wLength = sizeof(IOUSBHIDDescriptor);
devReq.pData = &hidDescriptor;
err = mInterface->DeviceRequest(&devReq);
if( err )
{
IOLog ("****AppleUSBProKbd: error making deviceRequest on interface. err=0x%x\n", err);
success = false;
break;
}
size = (hidDescriptor.hidDescriptorLengthHi * 256) + hidDescriptor.hidDescriptorLengthLo;
reportDescriptor = (UInt8*) IOMalloc( size );
devReq.wValue = ((0x20 | kUSBReportDesc) << 8);
devReq.wLength = size;
devReq.pData = reportDescriptor;
err = mInterface->DeviceRequest( &devReq );
if( err )
{
IOLog ("****AppleUSBProKbd: deviceRequest failed. err=0x%x\n", err);
success = false;
}
result = HIDOpenReportDescriptor( reportDescriptor, size, &mPreparsedReportDescriptorData, 0 );
if( result != noErr )
{
IOLog ("****AppleUSBProKbd: error opening HID report descriptor. err=0x%lx\n", result );
success = false;
}
buttonCapsSize = kMaxValues;
result = HIDGetSpecificButtonCaps( kHIDInputReport,
kHIDPage_Consumer,
0,
kHIDUsage_Csmr_Eject,
buttonCaps,
&buttonCapsSize,
mPreparsedReportDescriptorData );
if( (result == noErr) && (buttonCapsSize > 0) )
{
}
else
{
IOLog( "****AppleUSBProKbd::ParseHIDDescriptor - HIDGetSpecifcButtonCaps returned an error = %d\n", (int) result );
success = false;
}
buttonCapsSize = kMaxValues;
result = HIDGetSpecificButtonCaps( kHIDInputReport,
kHIDPage_Consumer,
0,
kHIDUsage_Csmr_VolumeIncrement,
buttonCaps,
&buttonCapsSize,
mPreparsedReportDescriptorData );
if( (result == noErr) && (buttonCapsSize > 0) )
{
}
else
{
IOLog( "****AppleUSBProKbd::ParseHIDDescriptor - HIDGetSpecifcButtonCaps returned an error = %d\n", (int) result );
success = false;
}
buttonCapsSize = kMaxValues;
result = HIDGetSpecificButtonCaps( kHIDInputReport,
kHIDPage_Consumer,
0,
kHIDUsage_Csmr_VolumeDecrement,
buttonCaps,
&buttonCapsSize,
mPreparsedReportDescriptorData );
if( (result == noErr) && (buttonCapsSize > 0) )
{
}
else
{
IOLog( "****AppleUSBProKbd::ParseHIDDescriptor - HIDGetSpecifcButtonCaps returned an error = %d\n", (int) result );
success = false;
}
buttonCapsSize = kMaxValues;
result = HIDGetSpecificButtonCaps( kHIDInputReport,
kHIDPage_Consumer,
0,
kHIDUsage_Csmr_Mute,
buttonCaps,
&buttonCapsSize,
mPreparsedReportDescriptorData );
if( (result == noErr) && (buttonCapsSize > 0) )
{
}
else
{
IOLog( "****AppleUSBProKbd::ParseHIDDescriptor- HIDGetSpecifcButtonCaps returned an error = %d\n", (int) result );
success = false;
}
}
while ( false );
if( reportDescriptor )
{
IOFree ( reportDescriptor, size );
}
return ( success );
}
void
AppleUSBProKbd::InterruptReadHandlerEntry(OSObject *target, void *param, IOReturn status, UInt32 bufferSizeRemaining)
{
AppleUSBProKbd * me = OSDynamicCast(AppleUSBProKbd, target);
if (!me)
return;
me->InterruptReadHandler(status, bufferSizeRemaining);
me->DecrementOutstandingIO();
}
void
AppleUSBProKbd::InterruptReadHandler(IOReturn status, UInt32 bufferSizeRemaining)
{
bool queueAnother = true;
IOReturn err = kIOReturnSuccess;
switch ( status )
{
case kIOReturnOverrun:
USBLog(3, "%s[%p]::InterruptReadHandler kIOReturnOverrun error", getName(), this);
case kIOReturnSuccess:
HandleSpecialButtons( (UInt8*) mReadDataBuffer->getBytesNoCopy(),((UInt32)mMaxPacketSize - bufferSizeRemaining) );
break;
case kIOReturnNotResponding:
USBLog(3, "%s[%p]::InterruptReadHandler kIOReturnNotResponding error", getName(), this);
if ( isInactive() )
{
queueAnother = false;
}
break;
case kIOReturnAborted:
if (isInactive())
{
USBLog(3,"%s[%p]::InterruptReadHandler Read aborted. We are terminating", getName(), this);
queueAnother = false;
}
else
{
USBLog(3,"%s[%p]::InterruptReadHandler Read aborted. Don't know why. Trying again", getName(), this);
}
break;
default:
USBLog(3, "%s[%p]::InterruptReadHandler error (0x%x) reading interrupt pipe", getName(), this, status);
break;
}
if (queueAnother)
{
mReadDataBuffer->setLength( mMaxPacketSize );
IncrementOutstandingIO();
err = mInterruptPipe->Read( mReadDataBuffer, &mCompletionRoutine );
if ( err != kIOReturnSuccess)
{
USBError(1, "%s[%p]::InterruptReadHandler - immediate error 0x%x queueing read\n", getName(), this, err);
DecrementOutstandingIO();
}
}
}
void AppleUSBProKbd::HandleSpecialButtons( UInt8 * inBufferData,
UInt32 bufferSize )
{
HIDUsageAndPage usageList[kMaxValues];
OSStatus err;
UInt32 usageListSize = kMaxValues;
UInt32 reportIndex = 0;
AbsoluteTime now;
Boolean soundUpIsPressed = FALSE;
Boolean soundDownIsPressed = FALSE;
err = HIDGetButtons( kHIDInputReport, 0, usageList, &usageListSize, mPreparsedReportDescriptorData,
inBufferData, bufferSize );
if( err )
{
USBError(1, "%s[%p]::HandleSpecialButtons - HIDGetButtons failed (0x%x)", getName(), this, err);
return;
}
clock_get_uptime( &now );
FindKeyboardsAndGetModifiers();
for( reportIndex = 0; reportIndex < usageListSize; reportIndex++ )
{
if( usageList[reportIndex].usagePage == kHIDPage_Consumer )
{
switch( usageList[reportIndex].usage )
{
case( kHIDUsage_Csmr_VolumeIncrement ):
{
soundUpIsPressed = TRUE;
break;
}
case( kHIDUsage_Csmr_VolumeDecrement ):
{
soundDownIsPressed = TRUE;
break;
}
case( kHIDUsage_Csmr_Mute ):
{
dispatchKeyboardEvent( kVolumeMute, TRUE, now );
dispatchKeyboardEvent( kVolumeMute, FALSE, now );
break;
}
case( kHIDUsage_Csmr_Eject ):
{
dispatchKeyboardEvent( kEject, TRUE, now );
dispatchKeyboardEvent( kEject, FALSE, now );
break;
}
default:
USBError(1, "%s[%p]::HandleSpecialButtons - no usage found for report. Usage = %d", getName(), this, usageList[reportIndex].usagePage);
break;
}
}
}
if( soundUpIsPressed != mSoundUpIsPressed )
{
dispatchKeyboardEvent( kVolumeUp, soundUpIsPressed, now );
}
if( soundDownIsPressed != mSoundDownIsPressed )
{
dispatchKeyboardEvent( kVolumeDown, soundDownIsPressed, now );
}
mSoundUpIsPressed = soundUpIsPressed;
mSoundDownIsPressed = soundDownIsPressed;
}
UInt32 AppleUSBProKbd::FindKeyboardsAndGetModifiers()
{
OSIterator *iterator = NULL;
OSDictionary *matchingDictionary = NULL;
IOHIKeyboard *device = NULL;
Boolean value = false;
OSObject *adbProperty;
const char *adbKey;
mEventFlags = 0;
mCapsLockOn = FALSE;
matchingDictionary = IOService::serviceMatching( "IOHIKeyboard" );
if( !matchingDictionary )
{
USBError(1, "%s[%p]::FindKeyboardsAndGetModifiers - could not get a matching dictionary", getName(), this);
goto exit;
}
iterator = IOService::getMatchingServices( matchingDictionary );
if( !iterator )
{
USBError(1, "%s[%p]::FindKeyboardsAndGetModifiers - getMatchingServices failed", getName(), this);
goto exit;
}
while( (device = (IOHIKeyboard*) iterator->getNextObject()) )
{
adbProperty = device->getProperty("ADB Match");
if (adbProperty)
{
adbKey = ((OSString *)adbProperty)->getCStringNoCopy();
if( *adbKey != '2' ) continue;
}
value = false;
if( device->alphaLock() )
{
mCapsLockOn = TRUE;
}
mEventFlags |= device->eventFlags();
}
exit:
if( matchingDictionary ) matchingDictionary->release();
if( iterator ) iterator->release();
return( mEventFlags );
}
IOReturn AppleUSBProKbd::message( UInt32 type, IOService * provider, void * argument )
{
switch ( type )
{
case kIOMessageServiceIsTerminated:
USBLog(6, "%s[%p]::message - kIOMessageServiceIsTerminated - ignoring now", getName(), this);
break;
case kIOMessageServiceIsSuspended:
case kIOMessageServiceIsResumed:
case kIOMessageServiceIsRequestingClose:
case kIOMessageServiceWasClosed:
case kIOMessageServiceBusyStateChange:
default:
break;
}
return kIOReturnSuccess;
}
bool
AppleUSBProKbd::willTerminate( IOService * provider, IOOptionBits options )
{
USBLog(5, "%s[%p]::willTerminate isInactive = %d", getName(), this, isInactive());
if (mReadDataBuffer)
{
mReadDataBuffer->release();
mReadDataBuffer = NULL;
}
if (mInterruptPipe)
{
mInterruptPipe->Abort();
mInterruptPipe = NULL;
}
return super::willTerminate(provider, options);
}
bool
AppleUSBProKbd::didTerminate( IOService * provider, IOOptionBits options, bool * defer )
{
USBLog(5, "%s[%p]::didTerminate isInactive = %d, outstandingIO = %d", getName(), this, isInactive(), mOutstandingIO);
if (!mOutstandingIO)
mInterface->close(this);
else
mNeedToClose = true;
return super::didTerminate(provider, options, defer);
}
void
AppleUSBProKbd::DecrementOutstandingIO(void)
{
if (!mGate)
{
if (!--mOutstandingIO && mNeedToClose)
{
USBLog(3, "%s[%p]::DecrementOutstandingIO isInactive = %d, outstandingIO = %d - closing device", getName(), this, isInactive(), mOutstandingIO);
mInterface->close(this);
}
return;
}
mGate->runAction(ChangeOutstandingIO, (void*)-1);
}
void
AppleUSBProKbd::IncrementOutstandingIO(void)
{
if (!mGate)
{
mOutstandingIO++;
return;
}
mGate->runAction(ChangeOutstandingIO, (void*)1);
}
IOReturn
AppleUSBProKbd::ChangeOutstandingIO(OSObject *target, void *param1, void *param2, void *param3, void *param4)
{
AppleUSBProKbd *me = OSDynamicCast(AppleUSBProKbd, target);
UInt32 direction = (UInt32)param1;
if (!me)
{
USBLog(1, "AppleUSBProKbd::ChangeOutstandingIO - invalid target");
return kIOReturnSuccess;
}
switch (direction)
{
case 1:
me->mOutstandingIO++;
break;
case -1:
if (!--me->mOutstandingIO && me->mNeedToClose)
{
USBLog(5, "%s[%p]::ChangeOutstandingIO isInactive = %d, outstandingIO = %d - closing device", me->getName(), me, me->isInactive(), me->mOutstandingIO);
me->mInterface->close(me);
}
break;
default:
USBLog(1, "%s[%p]::ChangeOutstandingIO - invalid direction", me->getName(), me);
}
return kIOReturnSuccess;
}