AppleUserHIDEventDriver.cpp [plain text]
#include <assert.h>
#include <AssertMacros.h>
#include <stdio.h>
#include <stdlib.h>
#include <DriverKit/DriverKit.h>
#include <DriverKit/OSCollections.h>
#include <HIDDriverKit/HIDDriverKit_Private.h>
#include "AppleUserHIDEventDriver.h"
#undef super
#define super IOUserHIDEventDriver
#define kIOHIDAppleVendorID 1452
#define kDFRV2ProductID 33538
#define kDFRV1ProductID 34304
enum {
kDebugDisableDriver = 1 << 0
};
struct AppleUserHIDEventDriver_IVars
{
OSDictionaryPtr properties;
struct {
bool appleVendorSupported;
OSArray *elements;
} keyboard;
struct {
OSArray *elements;
OSArray *pendingEvents;
} vendor;
};
#define _properties ivars->properties
#define _keyboard ivars->keyboard
#define _vendor ivars->vendor
bool AppleUserHIDEventDriver::init()
{
bool result = false;
result = super::init();
require_action(result, exit, HIDLogError("Init:%x", result));
ivars = IONewZero(AppleUserHIDEventDriver_IVars, 1);
require(ivars, exit);
exit:
return result;
}
void AppleUserHIDEventDriver::free()
{
if (ivars) {
OSSafeReleaseNULL(_properties);
OSSafeReleaseNULL(_keyboard.elements);
OSSafeReleaseNULL(_vendor.elements);
OSSafeReleaseNULL(_vendor.pendingEvents);
}
IOSafeDeleteNULL(ivars, AppleUserHIDEventDriver_IVars, 1);
super::free();
}
void AppleUserHIDEventDriver::printDescription()
{
OSNumber *up, *u, *vid, *pid;
OSString *product = NULL;
require(_properties, exit);
product = OSDynamicCast(OSString, OSDictionaryGetValue(_properties, kIOHIDProductKey));
up = OSDynamicCast(OSNumber, OSDictionaryGetValue(_properties, kIOHIDPrimaryUsagePageKey));
u = OSDynamicCast(OSNumber, OSDictionaryGetValue(_properties, kIOHIDPrimaryUsageKey));
vid = OSDynamicCast(OSNumber, OSDictionaryGetValue(_properties, kIOHIDVendorIDKey));
pid = OSDynamicCast(OSNumber, OSDictionaryGetValue(_properties, kIOHIDProductIDKey));
HIDServiceLog("%{public}s usagePage: %d usage: %d vid: %d pid: %d",
product ? product->getCStringNoCopy() : "",
up ? up->unsigned32BitValue() : 0,
u ? u->unsigned32BitValue() : 0,
vid ? vid->unsigned32BitValue() : 0,
pid ? pid->unsigned32BitValue() : 0);
exit:
return;
}
bool AppleUserHIDEventDriver::deviceSupported()
{
bool result = false;
__block bool supported = true;
OSArray *pairs = NULL;
OSNumber *vid = NULL;
OSNumber *pid = NULL;
require(_properties, exit);
vid = OSDynamicCast(OSNumber, OSDictionaryGetValue(_properties, kIOHIDVendorIDKey));
pid = OSDynamicCast(OSNumber, OSDictionaryGetValue(_properties, kIOHIDProductIDKey));
if (vid && pid) {
if (vid->unsigned32BitValue() == kIOHIDAppleVendorID &&
(pid->unsigned32BitValue() == kDFRV2ProductID ||
pid->unsigned32BitValue() == kDFRV1ProductID)) {
result = true;
goto exit;
}
if (vid->unsigned32BitValue() == kIOHIDAppleVendorID) {
OSNumber *up = OSDynamicCast(OSNumber, OSDictionaryGetValue(_properties, kIOHIDPrimaryUsagePageKey));
OSNumber *u = OSDynamicCast(OSNumber, OSDictionaryGetValue(_properties, kIOHIDPrimaryUsageKey));
require(up && u, exit);
require(up->unsigned32BitValue() == kHIDPage_Consumer &&
u->unsigned32BitValue() == kHIDUsage_Csmr_ConsumerControl, exit);
}
}
pairs = OSDynamicCast(OSArray, OSDictionaryGetValue(_properties, kIOHIDDeviceUsagePairsKey));
require(pairs, exit);
pairs->iterateObjects(^bool(OSObject *object) {
OSDictionary *entry = OSDynamicCast(OSDictionary, object);
OSNumber *up = NULL;
OSNumber *u = NULL;
uint32_t usagePage = 0;
uint32_t usage = 0;
if (!entry) {
return false;
}
up = OSDynamicCast(OSNumber, OSDictionaryGetValue(entry, kIOHIDDeviceUsagePageKey));
u = OSDynamicCast(OSNumber, OSDictionaryGetValue(entry, kIOHIDDeviceUsageKey));
if (!up || !u) {
return false;
}
usagePage = up->unsigned32BitValue();
usage = u->unsigned32BitValue();
if (usagePage == kHIDPage_GenericDesktop &&
(usage == kHIDUsage_GD_GamePad ||
usage == kHIDUsage_GD_Joystick ||
usage == kHIDUsage_GD_MultiAxisController)) {
supported = false;
return true;
}
return false;
});
result = true;
exit:
return (result && supported);
}
bool AppleUserHIDEventDriver::parseElements(OSArray *elements)
{
bool result = false;
result = super::parseElements(elements);
for (unsigned int i = 0; i < elements->getCount(); i++) {
IOHIDElement *element = (IOHIDElement *)elements->getObject(i);
if (element->getType() == kIOHIDElementTypeCollection ||
!element->getUsage()) {
continue;
}
if (parseVendorElement(element)) {
result = true;
}
}
HIDServiceLog("parseElements: vendor: %d",
_vendor.elements ? _vendor.elements-> getCount() : 0);
return result;
}
bool AppleUserHIDEventDriver::parseKeyboardElement(IOHIDElement *element)
{
bool result = false;
uint32_t usagePage = element->getUsagePage();
uint32_t usage = element->getUsage();
result = super::parseKeyboardElement(element);
require_quiet(!result, exit);
switch (usagePage) {
case kHIDPage_AppleVendorTopCase:
if (_keyboard.appleVendorSupported) {
switch (usage) {
case kHIDUsage_AV_TopCase_BrightnessDown:
case kHIDUsage_AV_TopCase_BrightnessUp:
case kHIDUsage_AV_TopCase_IlluminationDown:
case kHIDUsage_AV_TopCase_IlluminationUp:
case kHIDUsage_AV_TopCase_KeyboardFn:
result = true;
break;
}
}
break;
case kHIDPage_AppleVendorKeyboard:
if (_keyboard.appleVendorSupported) {
switch (usage) {
case kHIDUsage_AppleVendorKeyboard_Spotlight:
case kHIDUsage_AppleVendorKeyboard_Dashboard:
case kHIDUsage_AppleVendorKeyboard_Function:
case kHIDUsage_AppleVendorKeyboard_Launchpad:
case kHIDUsage_AppleVendorKeyboard_Reserved:
case kHIDUsage_AppleVendorKeyboard_CapsLockDelayEnable:
case kHIDUsage_AppleVendorKeyboard_PowerState:
case kHIDUsage_AppleVendorKeyboard_Expose_All:
case kHIDUsage_AppleVendorKeyboard_Expose_Desktop:
case kHIDUsage_AppleVendorKeyboard_Brightness_Up:
case kHIDUsage_AppleVendorKeyboard_Brightness_Down:
case kHIDUsage_AppleVendorKeyboard_Language:
result = true;
break;
}
}
break;
default:
break;
}
require_quiet(result, exit);
if (!_keyboard.elements) {
_keyboard.elements = OSArray::withCapacity(1);
require(_keyboard.elements, exit);
}
_keyboard.elements->setObject(element);
exit:
return result;
}
bool AppleUserHIDEventDriver::parseVendorElement(IOHIDElement *element)
{
IOHIDElement *parent = NULL;
bool result = false;
IOHIDElementCollectionType type;
parent = element->getParentElement();
require_quiet(parent, exit);
type = parent->getCollectionType();
require_quiet(type == kIOHIDElementCollectionTypeApplication ||
type == kIOHIDElementCollectionTypePhysical, exit);
require_quiet(parent->getUsagePage() == kHIDPage_AppleVendor &&
parent->getUsage() == kHIDUsage_AppleVendor_Message, exit);
if (!_vendor.elements) {
_vendor.elements = OSArray::withCapacity(1);
}
require(_vendor.elements, exit);
_vendor.elements->setObject(element);
result = true;
exit:
return result;
}
void AppleUserHIDEventDriver::handleReport(uint64_t timestamp,
uint8_t *report,
uint32_t reportLength,
IOHIDReportType type,
uint32_t reportID)
{
handleVendorReport(timestamp, reportID);
super::handleReport(timestamp, report, reportLength, type, reportID);
}
void AppleUserHIDEventDriver::handleKeyboardReport(uint64_t timestamp,
uint32_t reportID)
{
super::handleKeyboardReport(timestamp, reportID);
require_quiet(_keyboard.elements, exit);
for (unsigned int i = 0; i < _keyboard.elements->getCount(); i++) {
IOHIDElementPrivate *element = NULL;
uint64_t elementTimeStamp;
uint32_t usagePage, usage, value, preValue;
element = (IOHIDElementPrivate *)_keyboard.elements->getObject(i);
if (element->getReportID() != reportID) {
continue;
}
elementTimeStamp = element->getTimeStamp();
if (timestamp != elementTimeStamp) {
continue;
}
preValue = element->getValue(kIOHIDValueOptionsFlagPrevious) != 0;
value = element->getValue() != 0;
if (value == preValue) {
continue;
}
usagePage = element->getUsagePage();
usage = element->getUsage();
dispatchKeyboardEvent(timestamp, usagePage, usage, value, 0, true);
}
exit:
return;
}
void AppleUserHIDEventDriver::handleVendorReport(uint64_t timestamp,
uint32_t reportID)
{
require_quiet(_vendor.elements, exit);
if (!_vendor.pendingEvents) {
_vendor.pendingEvents = OSArray::withCapacity(1);
}
require(_vendor.pendingEvents, exit);
for (unsigned int i = 0; i < _vendor.elements->getCount(); i++) {
IOHIDElementPrivate *element = NULL;
uint64_t elementTimeStamp;
OSData *value = NULL;
uint32_t usagePage, usage;
IOHIDEvent *event = NULL;
element = OSDynamicCast(IOHIDElementPrivate, _vendor.elements->getObject(i));
if (!element) {
continue;
}
if (element->getReportID() != reportID) {
continue;
}
elementTimeStamp = element->getTimeStamp();
if (timestamp != elementTimeStamp) {
continue;
}
usagePage = element->getUsagePage();
usage = element->getUsage();
value = element->getDataValue();
if (!value || !value->getLength()) {
continue;
}
event = IOHIDEvent::vendorDefinedEvent(timestamp,
usagePage,
usage,
0,
(uint8_t *)value->getBytesNoCopy(),
value->getLength(),
0);
if (!event) {
continue;
}
_vendor.pendingEvents->setObject(event);
event->release();
}
exit:
return;
}
void AppleUserHIDEventDriver::dispatchEvent(IOHIDEvent *event)
{
require_quiet(_vendor.pendingEvents && _vendor.pendingEvents->getCount(), exit);
for (unsigned int i = 0; i < _vendor.pendingEvents->getCount(); i++) {
IOHIDEvent *child = OSDynamicCast(IOHIDEvent, _vendor.pendingEvents->getObject(i));
if (child) {
event->appendChild(child);
}
}
_vendor.pendingEvents->flushCollection();
exit:
super::dispatchEvent(event);
}
kern_return_t
IMPL(AppleUserHIDEventDriver, Start)
{
kern_return_t ret = kIOReturnUnsupported;
uint32_t debugMask = 0;
IOParseBootArgNumber("AppleUserHIDEventDriver-debug", &debugMask, sizeof(debugMask));
if (debugMask & kDebugDisableDriver) {
return kIOReturnUnsupported;
}
_keyboard.appleVendorSupported = true;
provider->CopyProperties(&_properties);
if (!_properties) {
return kIOReturnError;
}
if (!deviceSupported()) {
return kIOReturnUnsupported;
}
ret = Start(provider, SUPERDISPATCH);
require_noerr_action(ret, exit, HIDServiceLogError("Start: 0x%x", ret));
ret = RegisterService();
require_noerr_action(ret, exit, HIDServiceLogError("RegisterService: 0x%x", ret));
exit:
if (ret != kIOReturnSuccess) {
HIDServiceLogFault("Start failed: 0x%x", ret);
Stop(provider);
}
HIDServiceLog("Start ret: 0x%x", ret);
printDescription();
return ret;
}
kern_return_t
IMPL(AppleUserHIDEventDriver, Stop)
{
kern_return_t ret;
ret = Stop(provider, SUPERDISPATCH);
HIDServiceLog("Stop: 0x%x", ret);
return ret;
}