AppleUSBProKbd.cpp [plain text]
#include "AppleUSBProKbd.h"
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOService.h>
#include <IOKit/hidsystem/ev_keymap.h>
#include <IOKit/hidsystem/IOHIDDescriptorParser.h>
#include <IOKit/hidsystem/IOHIKeyboard.h>
#include <IOKit/hidsystem/IOHIDShared.h>
#include <IOKit/hidsystem/IOHIDSystem.h>
#include <IOKit/usb/IOUSBInterface.h>
#include <IOKit/usb/IOUSBPipe.h>
#include <IOKit/usb/USB.h>
#include <IOKit/IOMessage.h>
#include <libkern/OSByteOrder.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::start( IOService * provider )
{
IOReturn err;
mSoundUpIsPressed = FALSE;
mSoundDownIsPressed = FALSE;
if( !super::start( provider ) ) return( false );
mInterface = OSDynamicCast(IOUSBInterface, provider);
if (!mInterface)
return false;
if( mInterface->open( this ) == false )
{
IOLog("****AppleUSBProKbd::start - interface open failed.\n");
return false;
}
fTerminating = FALSE;
IOUSBFindEndpointRequest endpointRequest;
endpointRequest.type = kUSBInterrupt;
endpointRequest.direction = kUSBIn;
mInterruptPipe = mInterface->FindNextPipe( NULL, &endpointRequest );
if( !mInterruptPipe )
{
IOLog( "****AppleUSBProKbd::start - could not get the interrupt pipe.\n" );
goto failedExit;
}
if( VerifyNewDevice() == false )
{
IOLog("****AppleUSBProKbd::start - VerifyNewDevice was not successful. Ignoring this device.\n" );
goto failedExit;
}
mMaxPacketSize = endpointRequest.maxPacketSize;
mReadDataBuffer = IOBufferMemoryDescriptor::withCapacity( 8, kIODirectionIn );
mCompletionRoutine.target = (void *) this;
mCompletionRoutine.action = (IOUSBCompletionAction) AppleUSBProKbd::ReadHandler;
mCompletionRoutine.parameter = (void *) 0;
mReadDataBuffer->setLength( mMaxPacketSize );
retain();
if( err = mInterruptPipe->Read( mReadDataBuffer, &mCompletionRoutine ) )
{
IOLog("****AppleProUSBbd::start - failed to do a read from the interrupt pipe.\n" );
release();
goto failedExit;
}
return ( true );
failedExit:
if( mPreparsedReportDescriptorData )
{
HIDCloseReportDescriptor( mPreparsedReportDescriptorData );
}
provider->close( this );
stop( provider );
return( false );
}
void AppleUSBProKbd::stop( IOService * provider )
{
if( mPreparsedReportDescriptorData )
{
HIDCloseReportDescriptor( mPreparsedReportDescriptorData );
}
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::ReadHandler( OSObject * inTarget,
void * inParameter,
IOReturn inStatus,
UInt32 inBufferSizeRemaining )
{
AppleUSBProKbd * obj = (AppleUSBProKbd*) inTarget;
switch ( inStatus )
{
case ( kIOReturnSuccess ):
{
break;
}
case ( kIOReturnOverrun):
{
IOLog("****AppleUSBProKbd: overrun error. ignoring.\n" );
break;
}
case ( kIOReturnNotResponding ):
{
goto errorExit;
}
case kIOReturnAborted:
if (obj->fTerminating)
{
goto errorExit;
}
IOLog("%s: Read aborted. Don't know why. Trying again\n", obj->getName());
goto queueAnother;
break;
default:
IOLog("****AppleUSBProKbd: error reading interrupt pipe\n" );
goto queueAnother;
}
obj->HandleSpecialButtons( (UInt8*) obj->mReadDataBuffer->getBytesNoCopy (),
((UInt32) obj->mMaxPacketSize - inBufferSizeRemaining) );
queueAnother:
obj->mReadDataBuffer->setLength( obj->mMaxPacketSize );
obj->retain();
if( (inStatus = obj->mInterruptPipe->Read( obj->mReadDataBuffer, &obj->mCompletionRoutine )) )
{
IOLog( "****AppleUSBProKbd: immediate error %d queueing read\n", inStatus );
obj->release();
goto errorExit;
}
obj->release();
return;
errorExit:
obj->mInterface->close( obj );
obj->release();
return;
}
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 )
{
IOLog( "****AppleUSBProKbd::HandleSpecialButtons - HIDGetButtonsfailed. Error = %d.\n", (int) 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:
IOLog( "****AppleUSBProKbd::HandleSpecialButtons - no usage found for report. Usage = %d\n",
(int) 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;
mEventFlags = 0;
mCapsLockOn = FALSE;
matchingDictionary = IOService::serviceMatching( "IOHIKeyboard" );
if( !matchingDictionary )
{
IOLog( "****AppleUSBProKeyboard - could not get a matching dictionary.\n" );
goto exit;
}
iterator = IOService::getMatchingServices( matchingDictionary );
if( !iterator )
{
IOLog( "***AppleUSBProKeyboard - getMatchingServices failed.\n" );
goto exit;
}
while( (device = (IOHIKeyboard*) iterator->getNextObject()) )
{
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 = 0 )
{
switch ( type )
{
case kIOMessageServiceIsTerminated:
fTerminating = TRUE; if (mInterruptPipe)
mInterruptPipe->Abort();
break;
case kIOMessageServiceIsSuspended:
case kIOMessageServiceIsResumed:
case kIOMessageServiceIsRequestingClose:
case kIOMessageServiceWasClosed:
case kIOMessageServiceBusyStateChange:
default:
break;
}
return kIOReturnSuccess;
}