IOHIDKeyboardEventDevice.cpp [plain text]
#include <IOKit/IOLib.h>
#include "IOHIDKeyboardEventDevice.h"
#include "IOHIKeyboard.h"
#include "IOHIDPrivateKeys.h"
#include "IOHIDDebug.h"
#include "ev_keymap.h"
#include "IOHIDUsageTables.h"
typedef struct __attribute__((packed)) GenericKeyboardRpt {
UInt8 modifiers;
UInt8 reserved;
UInt8 keys[6];
UInt8 consumerKeys[6];
} GenericKeyboardRpt;
static UInt8 gGenLEDKeyboardDesc[] = {
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x02, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x02, 0x91, 0x02, 0x95, 0x01, 0x75, 0x06, 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x05, 0x07, 0x19, 0x00, 0x29, 0xFF, 0x81, 0x00, 0x05, 0x0c, 0x19, 0x00, 0x2A, 0xFF, 0x00, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x81, 0x00, 0xC0, };
static UInt8 gGenKeyboardDesc[] = {
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x05, 0x07, 0x19, 0x00, 0x29, 0xFF, 0x81, 0x00, 0x05, 0x0c, 0x19, 0x00, 0x2A, 0xFF, 0x00, 0x95, 0x05, 0x75, 0x08, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x81, 0x00, 0xC0, };
extern unsigned int hid_adb_2_usb_keymap[]; extern unsigned int hid_adb_2_usb_keymap_length;
#define super IOHIDDeviceShim
OSDefineMetaClassAndStructors( IOHIDKeyboardEventDevice, IOHIDDeviceShim )
IOHIDKeyboardEventDevice * IOHIDKeyboardEventDevice::newKeyboardDeviceAndStart(IOService * owner, UInt32 location)
{
IOHIDKeyboardEventDevice * device = new IOHIDKeyboardEventDevice;
if (device)
{
if ( device->initWithParameters(location, true) && device->attach(owner) )
{
device->setProperty("HIDDefaultBehavior", kOSBooleanTrue);
device->setProperty(kIOHIDCompatibilityInterface, kOSBooleanTrue);
if (!device->start(owner))
{
device->detach(owner);
device->release();
device = 0;
}
}
else
{
device->release();
device = 0;
}
}
return device;
}
bool IOHIDKeyboardEventDevice::initWithLocation( UInt32 location )
{
if (!super::initWithLocation(location))
return false;
_report = 0;
_cachedLEDState = 0;
_inputReportOnly = true;
return true;
}
void IOHIDKeyboardEventDevice::free()
{
if (_report) _report->release();
super::free();
}
bool IOHIDKeyboardEventDevice::handleStart( IOService * provider )
{
if (!super::handleStart(provider)) {
return false;
}
if ((_keyboard = OSDynamicCast(IOHIKeyboard, provider))) {
_inputReportOnly = ((transport() == kIOHIDTransportADB) && (_keyboard->deviceType() >= 0xc3));
_cachedLEDState = _keyboard->getLEDStatus() & 0x3;
}
_report = IOBufferMemoryDescriptor::withCapacity(
sizeof(GenericKeyboardRpt), kIODirectionNone, true);
if (_report) {
bzero(_report->getBytesNoCopy(), sizeof(GenericKeyboardRpt));
}
return (_report) ? true : false;
}
bool IOHIDKeyboardEventDevice::start( IOService * provider ) {
bool success = false;
if ( !super::start(provider) ) {
HIDLogError ("failed");
return false;
}
success = ((IOHIKeyboard*)provider)->open(
this,
kIOServiceSeize,
0,
(KeyboardEventCallback) _keyboardEvent,
(KeyboardSpecialEventCallback) _keyboardSpecialEvent,
(UpdateEventFlagsCallback) _updateEventFlags
);
provider->setProperty(kIOHIDResetPointerKey, kOSBooleanTrue);
return success;
}
IOReturn IOHIDKeyboardEventDevice::newReportDescriptor(IOMemoryDescriptor ** descriptor ) const
{
void * desc;
UInt8 * descBytes;
UInt8 descSize;
if (!descriptor)
return kIOReturnBadArgument;
if (_inputReportOnly)
{
descSize = sizeof(gGenKeyboardDesc);
descBytes = gGenKeyboardDesc;
}
else
{
descSize = sizeof(gGenLEDKeyboardDesc);
descBytes = gGenLEDKeyboardDesc;
}
*descriptor = IOBufferMemoryDescriptor::withCapacity(
descSize,
kIODirectionNone,
true);
if (! *descriptor)
return kIOReturnNoMemory;
desc = ((IOBufferMemoryDescriptor *)(*descriptor))->getBytesNoCopy();
bcopy(descBytes, desc, descSize);
return kIOReturnSuccess;
}
IOReturn IOHIDKeyboardEventDevice::getReport(IOMemoryDescriptor *report,
IOHIDReportType reportType,
IOOptionBits options __unused )
{
if (!report)
return kIOReturnError;
if ( reportType != kIOHIDReportTypeInput)
return kIOReturnUnsupported;
report->writeBytes(0, _report->getBytesNoCopy(), min(report->getLength(), _report->getLength()));
return kIOReturnSuccess;
}
IOReturn IOHIDKeyboardEventDevice::setReport(IOMemoryDescriptor * report,
IOHIDReportType reportType __unused,
IOOptionBits options )
{
UInt8 ledState = 0;
UInt8 mask;
if ((options & 0xff) || (_inputReportOnly) || !_keyboard)
return kIOReturnError;
report->readBytes( 0, (void *)&ledState, sizeof(UInt8) );
mask = (1 << (kHIDUsage_LED_NumLock - 1));
if ( (ledState & mask) && !(_cachedLEDState & mask) )
{
_keyboard->setNumLockFeedback(true);
}
else if ( !(ledState & mask) && (_cachedLEDState & mask) )
{
_keyboard->setNumLockFeedback(false);
}
mask = (1 << (kHIDUsage_LED_CapsLock - 1));
if ( (ledState & mask) && !(_cachedLEDState & mask) )
{
_keyboard->setAlphaLockFeedback(true);
}
else if ( !(ledState & mask) && (_cachedLEDState & mask) )
{
_keyboard->setAlphaLockFeedback(false);
}
_cachedLEDState = ledState;
return kIOReturnSuccess;
}
void IOHIDKeyboardEventDevice::setCapsLockLEDElement(bool state)
{
UInt8 mask = (1 << (kHIDUsage_LED_CapsLock-1));
if (_inputReportOnly)
return;
if (state)
_cachedLEDState |= mask;
else
_cachedLEDState &= ~mask;
*(UInt8 *)(_report->getBytesNoCopy()) = _cachedLEDState;
handleReport(_report, kIOHIDReportTypeOutput);
}
void IOHIDKeyboardEventDevice::setNumLockLEDElement(bool state)
{
UInt8 mask = (1 << (kHIDUsage_LED_NumLock-1));
if (_inputReportOnly)
return;
if (state)
_cachedLEDState |= mask;
else
_cachedLEDState &= ~mask;
*(UInt8 *)(_report->getBytesNoCopy()) = _cachedLEDState;
handleReport(_report, kIOHIDReportTypeOutput);
}
#define SET_MODIFIER_BIT(bitField, key, down) \
if (down) {bitField |= (1 << (key - 0xe0));} \
else {bitField &= ~(1 << (key - 0xe0));}
void IOHIDKeyboardEventDevice::postKeyboardEvent(UInt8 key, bool keyDown)
{
GenericKeyboardRpt *report = (GenericKeyboardRpt *)_report->getBytesNoCopy();
UInt8 usbKey;
if (!report)
return;
if (key == 0x90 || key == 0x91) {
postConsumerEvent(key - 0x90 + 1, keyDown);
return;
}
if (key >= hid_adb_2_usb_keymap_length || !(usbKey = hid_adb_2_usb_keymap[key]))
return;
if ((usbKey >= 0xe0) && (usbKey <= 0xe7))
{
SET_MODIFIER_BIT(report->modifiers, usbKey, keyDown);
}
else
{
for (int i=0; i<6; i++)
{
if (report->keys[i] == usbKey)
{
if (keyDown) return;
for (int j=i; j<5; j++)
report->keys[j] = report->keys[j+1];
report->keys[5] = 0;
break;
}
else if ((report->keys[i] == 0) && keyDown)
{
report->keys[i] = usbKey;
break;
}
}
}
handleReport(_report);
}
void IOHIDKeyboardEventDevice::postConsumerEvent(UInt8 key, bool keyDown)
{
GenericKeyboardRpt *report = (GenericKeyboardRpt *)_report->getBytesNoCopy();
UInt8 usbKey;
switch (key) {
case NX_KEYTYPE_BRIGHTNESS_UP:
usbKey = kHIDUsage_Csmr_DisplayBrightnessIncrement;
break;
case NX_KEYTYPE_BRIGHTNESS_DOWN:
usbKey = kHIDUsage_Csmr_DisplayBrightnessDecrement;
break;
case NX_KEYTYPE_SOUND_UP:
usbKey = kHIDUsage_Csmr_VolumeIncrement;
break;
case NX_KEYTYPE_SOUND_DOWN:
usbKey = kHIDUsage_Csmr_VolumeDecrement;
break;
case NX_KEYTYPE_EJECT:
usbKey = kHIDUsage_Csmr_Eject;
break;
case NX_POWER_KEY:
usbKey = kHIDUsage_Csmr_Power;
break;
case NX_KEYTYPE_MUTE:
usbKey = kHIDUsage_Csmr_Mute;
break;
case NX_KEYTYPE_PLAY:
usbKey = kHIDUsage_Csmr_Play;
break;
case NX_KEYTYPE_NEXT:
usbKey = kHIDUsage_Csmr_ScanNextTrack;
break;
case NX_KEYTYPE_PREVIOUS:
usbKey = kHIDUsage_Csmr_ScanPreviousTrack;
break;
case NX_KEYTYPE_FAST:
usbKey = kHIDUsage_Csmr_FastForward;
break;
case NX_KEYTYPE_REWIND:
usbKey = kHIDUsage_Csmr_Rewind;
break;
default:
return;
}
for (unsigned int i=0; i< (sizeof(report->consumerKeys)); i++)
{
if (report->consumerKeys[i] == usbKey)
{
if (keyDown) return;
for (unsigned int j=i; j<(sizeof(report->consumerKeys) - 1); j++)
report->consumerKeys[j] = report->consumerKeys[j+1];
report->consumerKeys[sizeof(report->consumerKeys) - 1] = 0;
break;
}
else if ((report->consumerKeys[i] == 0) && keyDown)
{
report->consumerKeys[i] = usbKey;
break;
}
}
handleReport(_report);
}
OSNumber * IOHIDKeyboardEventDevice::newVendorIDNumber() const
{
return OSNumber::withNumber(0x5ac, 32);
}
OSNumber * IOHIDKeyboardEventDevice::newProductIDNumber() const
{
return OSNumber::withNumber(0xffff, 32);
}
OSString * IOHIDKeyboardEventDevice::newManufacturerString() const
{
return OSString::withCString("Apple");
}
enum {
kUSB_LEFT_CONTROL_BIT = 0x01,
kUSB_LEFT_SHIFT_BIT = 0x02,
kUSB_LEFT_ALT_BIT = 0x04,
kUSB_LEFT_FLOWER_BIT = 0x08,
kUSB_RIGHT_CONTROL_BIT = 0x10,
kUSB_RIGHT_SHIFT_BIT = 0x20,
kUSB_RIGHT_ALT_BIT = 0x040,
kUSB_RIGHT_FLOWER_BIT = 0x80
};
void IOHIDKeyboardEventDevice::postFlagKeyboardEvent(UInt32 flags)
{
GenericKeyboardRpt *report = (GenericKeyboardRpt *)_report->getBytesNoCopy();
UInt32 flagDelta = (flags ^ _lastFlags);
if (!flagDelta)
return;
report->modifiers = 0;
_lastFlags = flags;
if ( flagDelta & 0x0000ffff )
{
if( flags & NX_DEVICELSHIFTKEYMASK )
report->modifiers |= kUSB_LEFT_SHIFT_BIT;
if( flags & NX_DEVICELCTLKEYMASK )
report->modifiers |= kUSB_LEFT_CONTROL_BIT;
if( flags & NX_DEVICELALTKEYMASK )
report->modifiers |= kUSB_LEFT_ALT_BIT;
if( flags & NX_DEVICELCMDKEYMASK )
report->modifiers |= kUSB_LEFT_FLOWER_BIT;
if( flags & NX_DEVICERSHIFTKEYMASK )
report->modifiers |= kUSB_RIGHT_SHIFT_BIT;
if( flags & NX_DEVICERCTLKEYMASK )
report->modifiers |= kUSB_RIGHT_CONTROL_BIT;
if( flags & NX_DEVICERALTKEYMASK )
report->modifiers |= kUSB_RIGHT_ALT_BIT;
if( flags & NX_DEVICERCMDKEYMASK )
report->modifiers |= kUSB_RIGHT_FLOWER_BIT;
}
else if ( flagDelta & 0xffff0000 )
{
if( flags & NX_SHIFTMASK )
report->modifiers |= kUSB_LEFT_SHIFT_BIT;
if( flags & NX_CONTROLMASK )
report->modifiers |= kUSB_LEFT_CONTROL_BIT;
if( flags & NX_ALTERNATEMASK )
report->modifiers |= kUSB_LEFT_ALT_BIT;
if( flags & NX_COMMANDMASK )
report->modifiers |= kUSB_LEFT_FLOWER_BIT;
}
if ( flagDelta & NX_ALPHASHIFTMASK )
{
postKeyboardEvent(0x39, flags & NX_ALPHASHIFTMASK);
return;
}
handleReport(_report);
}
OSString * IOHIDKeyboardEventDevice::newProductString() const
{
OSString * string = 0;
if ( !(string = super::newProductString()) )
string = OSString::withCString("Virtual Keyboard");
return string;
}
IOReturn IOHIDKeyboardEventDevice::message(UInt32 type, IOService * provider, void * argument __unused)
{
IOReturn status = kIOReturnSuccess;
switch (type) {
case kIOMessageServiceIsTerminated:
if (provider) {
provider->close( this );
}
break;
}
return status;
}
void IOHIDKeyboardEventDevice::_keyboardEvent (
IOHIDKeyboardEventDevice * self,
unsigned eventType,
unsigned flags,
unsigned key,
unsigned charCode __unused,
unsigned charSet __unused,
unsigned origCharCode __unused,
unsigned origCharSet __unused,
unsigned keyboardType __unused,
bool repeat,
AbsoluteTime ts __unused,
OSObject * sender __unused,
void * refcon __unused)
{
if (repeat) {
return;
}
switch (eventType) {
case NX_KEYDOWN:
case NX_KEYUP:
self->postKeyboardEvent(key, eventType == NX_KEYDOWN);
break;
case NX_FLAGSCHANGED:
self->postFlagKeyboardEvent(flags);
break;
}
}
void IOHIDKeyboardEventDevice::_keyboardSpecialEvent(
IOHIDKeyboardEventDevice * self,
unsigned eventType,
unsigned flags __unused,
unsigned key __unused,
unsigned flavor,
UInt64 guid __unused,
bool repeat __unused,
AbsoluteTime ts __unused,
OSObject * sender __unused,
void * refcon __unused)
{
switch (eventType) {
case NX_KEYDOWN:
case NX_KEYUP:
self->postConsumerEvent(flavor, eventType == NX_KEYDOWN);
break;
}
}
void IOHIDKeyboardEventDevice::_updateEventFlags(
IOHIDKeyboardEventDevice * self,
unsigned flags,
OSObject * sender __unused,
void * refcon __unused)
{
self->postFlagKeyboardEvent(flags);
}