#include <IOKit/IOLib.h>
#include <IOKit/hidsystem/IOHIKeyboard.h>
#include <IOKit/hidsystem/IOHIKeyboardMapper.h>
#include <IOKit/hidsystem/IOLLEvent.h>
#include <IOKit/hidsystem/IOHIDParameter.h>
#include "IOHIDSystem.h"
#include "IOHIDKeyboardDevice.h"
#define super IOHIDevice
OSDefineMetaClassAndStructors(IOHIKeyboard, IOHIDevice);
bool IOHIKeyboard::init(OSDictionary * properties)
{
if (!super::init(properties)) return false;
_deviceLock = IOLockAlloc();
_keyMap = 0;
_keyStateSize = 4*((maxKeyCodes()+(EVK_BITS_PER_UNIT-1))/EVK_BITS_PER_UNIT);
_keyState = (UInt32 *) IOMalloc(_keyStateSize);
if (!_deviceLock || !_keyState) return false;
bzero(_keyState, _keyStateSize);
return true;
}
bool IOHIKeyboard::start(IOService * provider)
{
if (!super::start(provider)) return false;
registerService();
return true;
}
void IOHIKeyboard::free()
{
IOLock * lock = NULL;
if ( _deviceLock )
{
lock = _deviceLock;
IOLockLock( lock);
_deviceLock = NULL;
}
if ( _keyMap ) {
_keyMap->release();
}
if( _keyState )
IOFree( _keyState, _keyStateSize);
if ( lock )
{
IOLockUnlock( lock);
IOLockFree( lock);
}
super::free();
}
IOHIDKind IOHIKeyboard::hidKind()
{
return kHIKeyboardDevice;
}
bool IOHIKeyboard::updateProperties( void )
{
bool ok;
ok = setProperty( kIOHIDKeyMappingKey, _keyMap );
return( ok & super::updateProperties() );
}
IOReturn IOHIKeyboard::setParamProperties( OSDictionary * dict )
{
OSData * data;
IOReturn err = kIOReturnSuccess, err2;
unsigned char * map;
IOHIKeyboardMapper * oldMap;
bool updated = false;
UInt64 nano;
if( dict->getObject(kIOHIDResetKeyboardKey))
resetKeyboard();
IOLockLock( _deviceLock);
if( (data = OSDynamicCast( OSData,
dict->getObject(kIOHIDKeyRepeatKey))))
{
nano = *((UInt64 *)(data->getBytesNoCopy()));
if( nano < EV_MINKEYREPEAT)
nano = EV_MINKEYREPEAT;
nanoseconds_to_absolutetime(nano, &_keyRepeat);
updated = true;
}
if( (data = OSDynamicCast( OSData,
dict->getObject(kIOHIDInitialKeyRepeatKey))))
{
nano = *((UInt64 *)(data->getBytesNoCopy()));
if( nano < EV_MINKEYREPEAT)
nano = EV_MINKEYREPEAT;
nanoseconds_to_absolutetime(nano, &_initialKeyRepeat);
updated = true;
}
if( (data = OSDynamicCast( OSData, dict->getObject(kIOHIDKeyMappingKey))))
{
map = (unsigned char *)IOMalloc( data->getLength() );
bcopy( data->getBytesNoCopy(), map, data->getLength() );
oldMap = _keyMap;
_keyMap = IOHIKeyboardMapper::keyboardMapper(this, map, data->getLength(), true);
if (_keyMap)
{
_keyMap->setKeyboardTarget((IOService *) _keyboardEventTarget);
if (oldMap)
oldMap->release();
updated = true;
}
else
{
_keyMap = oldMap;
err = kIOReturnBadArgument;
}
}
IOLockUnlock( _deviceLock);
if (_keyMap)
err2 = _keyMap->setParamProperties(dict);
if( updated )
updateProperties();
if (err == kIOReturnSuccess)
err = err2;
return( err );
}
bool IOHIKeyboard::resetKeyboard()
{
const unsigned char *defaultKeymap;
UInt32 defaultKeymapLength;
IOLockLock( _deviceLock);
if ( _keyMap )
_keyMap->release();
defaultKeymap = defaultKeymapOfLength(&defaultKeymapLength);
_keyMap = IOHIKeyboardMapper::keyboardMapper( this,
defaultKeymap,
defaultKeymapLength,
false );
if (_keyMap)
{
_keyMap->setKeyboardTarget((IOService *) _keyboardEventTarget);
clock_interval_to_absolutetime_interval( EV_DEFAULTKEYREPEAT,
kNanosecondScale, &_keyRepeat);
clock_interval_to_absolutetime_interval( EV_DEFAULTINITIALREPEAT,
kNanosecondScale, &_initialKeyRepeat);
}
updateProperties();
_interfaceType = interfaceID();
_deviceType = deviceType();
_guid = getGUID();
IOLockUnlock( _deviceLock);
return (_keyMap) ? true : false;
}
void IOHIKeyboard::scheduleAutoRepeat()
{
if ( _calloutPending == true )
{
thread_call_func_cancel(_autoRepeat, this, true);
_calloutPending = false;
}
if ( AbsoluteTime_to_scalar(&_downRepeatTime) )
{
AbsoluteTime deadline;
clock_absolutetime_interval_to_deadline(_downRepeatTime, &deadline);
thread_call_func_delayed(_autoRepeat, this, deadline);
_calloutPending = true;
}
}
void IOHIKeyboard::_autoRepeat(thread_call_param_t arg,
thread_call_param_t)
{
IOHIKeyboard *self = (IOHIKeyboard *) arg;
self->autoRepeat();
}
void IOHIKeyboard::autoRepeat()
{
IOLockLock( _deviceLock);
if ( _calloutPending == false )
{
IOLockUnlock( _deviceLock);
return;
}
_calloutPending = false;
_isRepeat = true;
if ( AbsoluteTime_to_scalar(&_downRepeatTime) )
{
if (_keyMap) _keyMap->translateKeyCode(_codeToRepeat,
true,
_keyState);
_downRepeatTime = _keyRepeat;
}
_isRepeat = false;
scheduleAutoRepeat();
IOLockUnlock( _deviceLock);
}
void IOHIKeyboard::setRepeat(unsigned eventType, unsigned keyCode)
{
if ( _isRepeat == false ) {
if (eventType == NX_KEYDOWN) {
_downRepeatTime = _initialKeyRepeat; _codeToRepeat = keyCode;
scheduleAutoRepeat();
}
else if (eventType == NX_KEYUP) {
if (_codeToRepeat == keyCode)
{
AbsoluteTime_to_scalar(&_downRepeatTime) = 0;
_codeToRepeat = (unsigned)-1;
scheduleAutoRepeat();
}
}
}
}
void IOHIKeyboard::keyboardEvent(unsigned eventType,
unsigned flags,
unsigned keyCode,
unsigned charCode,
unsigned charSet,
unsigned origCharCode,
unsigned origCharSet)
{
if (_keyboardEventAction)
{
(*_keyboardEventAction)(_keyboardEventTarget,
eventType,
flags,
keyCode,
charCode,
charSet,
origCharCode,
origCharSet,
_deviceType,
_isRepeat,
_lastEventTime);
}
if( keyCode == _keyMap->getParsedSpecialKey(NX_KEYTYPE_CAPS_LOCK) ||
keyCode == _keyMap->getParsedSpecialKey(NX_POWER_KEY) ||
keyCode == _keyMap->getParsedSpecialKey(NX_KEYTYPE_EJECT) ||
keyCode == _keyMap->getParsedSpecialKey(NX_KEYTYPE_VIDMIRROR))
{
if (_interfaceType == NX_EVS_DEVICE_INTERFACE_ADB)
{
return;
}
}
setRepeat(eventType, keyCode);
}
void IOHIKeyboard::keyboardSpecialEvent(unsigned eventType,
unsigned flags,
unsigned keyCode,
unsigned flavor)
{
if (_keyboardSpecialEventAction)
{
(*_keyboardSpecialEventAction)(_keyboardSpecialEventTarget,
eventType,
flags,
keyCode,
flavor,
_guid,
_isRepeat,
_lastEventTime);
}
if ( (flavor != NX_KEYTYPE_CAPS_LOCK) && (flavor != NX_KEYTYPE_NUM_LOCK) &&
(flavor != NX_POWER_KEY) && (flavor != NX_KEYTYPE_EJECT) &&
(flavor != NX_KEYTYPE_VIDMIRROR))
{
setRepeat(eventType, keyCode);
}
}
void IOHIKeyboard::updateEventFlags(unsigned flags)
{
if (_updateEventFlagsAction)
{
(*_updateEventFlagsAction)(_updateEventFlagsTarget, flags);
}
}
unsigned IOHIKeyboard::eventFlags()
{
return _eventFlags;
}
unsigned IOHIKeyboard::deviceFlags()
{
return _eventFlags;
}
void IOHIKeyboard::setDeviceFlags(unsigned flags)
{
_eventFlags = flags;
}
bool IOHIKeyboard::alphaLock()
{
return _alphaLock;
}
void IOHIKeyboard::setAlphaLock(bool val)
{
_alphaLock = val;
setAlphaLockFeedback(val);
}
bool IOHIKeyboard::numLock()
{
return _numLock;
}
void IOHIKeyboard::setNumLock(bool val)
{
_numLock = val;
setNumLockFeedback(val);
}
bool IOHIKeyboard::charKeyActive()
{
return _charKeyActive;
}
void IOHIKeyboard::setCharKeyActive(bool val)
{
_charKeyActive = val;
}
void IOHIKeyboard::dispatchKeyboardEvent(unsigned int keyCode,
bool goingDown,
AbsoluteTime time)
{
IOHIKeyboardMapper * theKeyMap;
_lastEventTime = time;
IOLockLock( _deviceLock);
if (_keyMap) _keyMap->translateKeyCode(keyCode,
goingDown,
_keyState);
theKeyMap = _keyMap;
IOLockUnlock( _deviceLock);
if (theKeyMap)
{
theKeyMap->retain();
theKeyMap->keyEventPostProcess();
theKeyMap->release();
}
}
const unsigned char * IOHIKeyboard::defaultKeymapOfLength(UInt32 * length)
{
*length = 0;
return NULL;
}
void IOHIKeyboard::setAlphaLockFeedback(bool )
{
return;
}
void IOHIKeyboard::setNumLockFeedback(bool )
{
return;
}
UInt32 IOHIKeyboard::maxKeyCodes()
{
return( 0x80);
}
bool IOHIKeyboard:: doesKeyLock ( unsigned key)
{
return false;
}
unsigned IOHIKeyboard:: getLEDStatus ()
{
return 0;
}
bool IOHIKeyboard::open(IOService * client,
IOOptionBits options,
KeyboardEventAction keAction,
KeyboardSpecialEventAction kseAction,
UpdateEventFlagsAction uefAction)
{
if ( (!_keyMap) && (!resetKeyboard())) return false;
if (super::open(client, options))
{
if (_keyMap)
_keyMap->setKeyboardTarget(client);
_keyboardEventTarget = client;
_keyboardEventAction = keAction;
_keyboardSpecialEventTarget = client;
_keyboardSpecialEventAction = kseAction;
_updateEventFlagsTarget = client;
_updateEventFlagsAction = uefAction;
return true;
}
return false;
}
void IOHIKeyboard::close(IOService * client, IOOptionBits)
{
AbsoluteTime_to_scalar(&_downRepeatTime) = 0;
_codeToRepeat = (unsigned)-1;
scheduleAutoRepeat();
setAlphaLock(false);
if (_updateEventFlagsAction)
(*_updateEventFlagsAction)(_updateEventFlagsTarget, 0); _eventFlags = 0;
bzero(_keyState, _keyStateSize);
_keyboardEventAction = NULL;
_keyboardEventTarget = 0;
_keyboardSpecialEventAction = NULL;
_keyboardSpecialEventTarget = 0;
_updateEventFlagsAction = NULL;
_updateEventFlagsTarget = 0;
super::close(client);
}
IOReturn IOHIKeyboard::message( UInt32 type, IOService * provider,
void * argument)
{
IOReturn ret = kIOReturnSuccess;
switch(type)
{
case kIOHIDSystem508MouseClickMessage:
if (_keyMap)
ret = _keyMap->message(type, this);
break;
default:
ret = super::message(type, provider, argument);
break;
}
return ret;
}