#include <IOKit/IOLib.h>
#include <IOKit/assert.h>
#include <IOKit/hidsystem/IOHIDParameter.h>
#include <IOKit/hidsystem/IOHIDShared.h>
#include <IOKit/hidsystem/IOHIDDescriptorParser.h>
#include "IOHIDPointing.h"
#include "IOHIDKeys.h"
#define kMaxButtons 32 // Is this defined anywhere in the event headers?
#define kMaxValues 32 // This should be plenty big to find the X, Y and wheel values - is there some absolute max?
#define kDefaultFixedResolution (400 << 16)
#define kDefaultScrollFixedResolution (10 << 16)
#define super IOHIPointing
OSDefineMetaClassAndStructors(IOHIDPointing, IOHIPointing);
bool IOHIDPointing::init(OSDictionary * properties)
{
if (!super::init(properties)) return false;
_numButtons = 1;
_resolution = kDefaultFixedResolution;
_scrollResolution = kDefaultScrollFixedResolution;
_preparsedReportDescriptorData = NULL;
_buttonCollection = -1;
_xCollection = -1;
_yCollection = -1;
_tipPressureCollection = -1;
_digitizerButtonCollection = -1;
_scrollWheelCollection = -1;
_horzScrollCollection = -1;
_hasInRangeReport = false;
_tipPressureMin = 255;
_tipPressureMax = 255;
return true;
}
bool IOHIDPointing::start(IOService *provider)
{
IOMemoryDescriptor *descriptor;
IOReturn ret;
_provider = OSDynamicCast(IOHIDDevice, provider);
if (!provider)
return false;
propagateProperties();
ret = _provider->newReportDescriptor(&descriptor);
if ((ret != kIOReturnSuccess) || !descriptor)
{
if (descriptor)
descriptor->release();
return false;
}
ret = parseReportDescriptor(descriptor);
descriptor->release();
if (ret != kIOReturnSuccess)
return false;
return super::start(provider);
}
void IOHIDPointing::free()
{
if (_preparsedReportDescriptorData)
{
HIDCloseReportDescriptor(_preparsedReportDescriptorData);
}
super::free();
}
IOReturn IOHIDPointing::parseReportDescriptor( IOMemoryDescriptor * report,
IOOptionBits options )
{
OSStatus result;
void * reportData;
IOByteCount reportLength;
IOByteCount segmentSize;
IOReturn ret = kIOReturnSuccess;
HIDButtonCapabilities buttonCaps[kMaxButtons];
UInt32 numButtonCaps = kMaxButtons;
HIDValueCapabilities valueCaps[kMaxValues];
UInt32 numValueCaps = kMaxValues;
reportData = report->getVirtualSegment(0, &segmentSize);
reportLength = report->getLength();
if ( segmentSize != reportLength )
{
reportData = IOMalloc( reportLength );
if ( reportData == 0 )
return kIOReturnNoMemory;
report->readBytes( 0, reportData, reportLength );
}
result = HIDOpenReportDescriptor(
reportData,
reportLength,
&_preparsedReportDescriptorData,
0 );
if ( segmentSize != reportLength )
{
IOFree( reportData, reportLength );
}
if ( result != kHIDSuccess )
{
return kIOReturnError;
}
do {
result = HIDGetSpecificButtonCapabilities(kHIDInputReport,
kHIDPage_Button,
0,
0,
buttonCaps,
&numButtonCaps,
_preparsedReportDescriptorData);
if ((result == noErr) && (numButtonCaps > 0))
{
_buttonCollection = buttonCaps[0].collection; if (buttonCaps[0].isRange)
{
_numButtons = buttonCaps[0].u.range.usageMax - buttonCaps[0].u.range.usageMin + 1;
}
if (_numButtons > 1)
{
setProperty(kIOHIDPointerButtonCountKey, buttonCount());
}
}
numButtonCaps = kMaxButtons;
result = HIDGetSpecificButtonCapabilities(kHIDInputReport,
kHIDPage_Digitizer,
0,
0,
buttonCaps,
&numButtonCaps,
_preparsedReportDescriptorData);
if ((result == noErr) && (numButtonCaps > 0)) {
_digitizerButtonCollection = buttonCaps[0].collection;
}
numButtonCaps = kMaxButtons;
result = HIDGetSpecificButtonCapabilities(kHIDInputReport,
kHIDPage_Digitizer,
0,
kHIDUsage_Dig_InRange,
buttonCaps,
&numButtonCaps,
_preparsedReportDescriptorData);
if (result == noErr) {
_hasInRangeReport = true;
}
result = HIDGetSpecificValueCapabilities(kHIDInputReport,
kHIDPage_GenericDesktop,
0,
kHIDUsage_GD_X,
valueCaps,
&numValueCaps,
_preparsedReportDescriptorData);
if ((result == noErr) && (numValueCaps > 0))
{
_xCollection = valueCaps[0].collection;
_absoluteCoordinates = valueCaps[0].isAbsolute;
_bounds.minx = valueCaps[0].logicalMin;
_bounds.maxx = valueCaps[0].logicalMax;
if (valueCaps[0].physicalMin != valueCaps[0].logicalMin &&
valueCaps[0].physicalMax != valueCaps[0].logicalMax)
{
SInt32 logicalDiff = (valueCaps[0].logicalMax - valueCaps[0].logicalMin);
SInt32 physicalDiff = (valueCaps[0].physicalMax - valueCaps[0].physicalMin);
SInt32 resExponent = valueCaps[0].unitExponent & 0x0F;
if (resExponent < 8)
{
for (int i = resExponent; i > 0; i--)
{
physicalDiff *= 10;
}
}
else
{
for (int i = 0x10 - resExponent; i > 0; i--)
{
logicalDiff *= 10;
}
}
_resolution = (logicalDiff / physicalDiff) << 16;
#if (DEBUGGING_LEVEL > 2)
IOLog (" _resolution = %lx\n", _resolution);
#endif
}
} else {
IOLog ("%s: error getting X axis information from HID report descriptor. err=0x%lx\n", getName(), result);
ret = kIOReturnError;
break;
}
if (_provider)
{
OSNumber *resolution = OSNumber::withNumber(_resolution, 32);
if (resolution)
_provider->setProperty(kIOHIDPointerResolutionKey, resolution);
}
numValueCaps = kMaxValues;
result = HIDGetSpecificValueCapabilities(kHIDInputReport,
kHIDPage_GenericDesktop,
0,
kHIDUsage_GD_Y,
valueCaps,
&numValueCaps,
_preparsedReportDescriptorData);
if ((result == noErr) && (numValueCaps > 0)) {
_yCollection = valueCaps[0].collection;
_bounds.miny = valueCaps[0].logicalMin;
_bounds.maxy = valueCaps[0].logicalMax;
} else {
IOLog ("%s: error getting Y axis information from HID report descriptor. err=0x%lx\n", getName(), result);
ret = kIOReturnError;
break;
}
numValueCaps = kMaxValues;
result = HIDGetSpecificValueCapabilities(kHIDInputReport,
kHIDPage_Digitizer,
0,
kHIDUsage_Dig_TipPressure,
valueCaps,
&numValueCaps,
_preparsedReportDescriptorData);
if ((result == noErr) && (numValueCaps > 0)) {
_tipPressureCollection = valueCaps[0].collection;
_tipPressureMin = valueCaps[0].logicalMin;
_tipPressureMax = valueCaps[0].logicalMax;
}
numValueCaps = kMaxValues;
result = HIDGetSpecificValueCapabilities(kHIDInputReport,
kHIDPage_GenericDesktop,
0,
kHIDUsage_GD_Wheel,
valueCaps,
&numValueCaps,
_preparsedReportDescriptorData);
if ((result == noErr) && (numValueCaps > 0)) {
_scrollWheelCollection = valueCaps[0].collection;
if (valueCaps[0].physicalMin != valueCaps[0].logicalMin &&
valueCaps[0].physicalMax != valueCaps[0].logicalMax)
{
SInt32 logicalDiff = (valueCaps[0].logicalMax - valueCaps[0].logicalMin);
SInt32 physicalDiff = (valueCaps[0].physicalMax - valueCaps[0].physicalMin);
SInt32 resExponent = valueCaps[0].unitExponent & 0x0F;
if (resExponent < 8)
{
for (int i = resExponent; i > 0; i--)
{
physicalDiff *= 10;
}
}
else
{
for (int i = 0x10 - resExponent; i > 0; i--)
{
logicalDiff *= 10;
}
}
_scrollResolution = (logicalDiff / physicalDiff) << 16;
#if (DEBUGGING_LEVEL > 2)
IOLog (" _scrollResolution = %lx\n", _scrollResolution);
#endif
}
OSNumber *scrollResolution = OSNumber::withNumber(_scrollResolution, 32);
if (scrollResolution)
setProperty(kIOHIDScrollResolutionKey, scrollResolution);
}
numValueCaps = kMaxValues;
result = HIDGetSpecificValueCapabilities(kHIDInputReport,
kHIDPage_GenericDesktop,
0,
kHIDUsage_GD_Z,
valueCaps,
&numValueCaps,
_preparsedReportDescriptorData);
if ((result == noErr) && (numValueCaps > 0)) {
_horzScrollCollection = valueCaps[0].collection;
}
} while (false);
return ret;
}
IOReturn IOHIDPointing::handleReport(
IOMemoryDescriptor * report,
IOOptionBits options)
{
OSStatus status;
HIDUsage usageList[kMaxButtons];
UInt32 usageListSize = kMaxButtons;
UInt32 buttonState = 0;
SInt32 usageValue;
SInt32 pressure = MAXPRESSURE;
int dx = 0, dy = 0, scrollWheelDelta = 0, horzScrollDelta = 0;
AbsoluteTime now;
bool inRange = !_hasInRangeReport;
UInt8 * mouseData;
IOByteCount ret_bufsize;
IOByteCount segmentSize;
mouseData = (UInt8 *)report->getVirtualSegment(0, &segmentSize);
ret_bufsize = report->getLength();
if ( ret_bufsize == 0 )
return kIOReturnBadArgument;
if ( segmentSize != ret_bufsize )
{
mouseData = (UInt8 *)IOMalloc( ret_bufsize );
if ( mouseData == 0 )
return kIOReturnNoMemory;
report->readBytes( 0, mouseData, ret_bufsize );
}
if (_buttonCollection != -1) {
status = HIDGetButtonsOnPage (kHIDInputReport,
kHIDPage_Button,
_buttonCollection,
usageList,
&usageListSize,
_preparsedReportDescriptorData,
mouseData,
ret_bufsize);
if (status == noErr) {
UInt32 usageNum;
for (usageNum = 0; usageNum < usageListSize; usageNum++) {
if (usageList[usageNum] <= kMaxButtons) {
buttonState |= (1 << (usageList[usageNum] - 1));
}
}
}
}
if (_tipPressureCollection != -1) {
status = HIDGetUsageValue (kHIDInputReport,
kHIDPage_Digitizer,
_tipPressureCollection,
kHIDUsage_Dig_TipPressure,
&usageValue,
_preparsedReportDescriptorData,
mouseData,
ret_bufsize);
if (status == noErr) {
pressure = usageValue;
}
}
if (_digitizerButtonCollection != -1) {
usageListSize = kMaxButtons;
status = HIDGetButtonsOnPage (kHIDInputReport,
kHIDPage_Digitizer,
_digitizerButtonCollection,
usageList,
&usageListSize,
_preparsedReportDescriptorData,
mouseData,
ret_bufsize);
if (status == noErr) {
UInt32 usageNum;
for (usageNum = 0; usageNum < usageListSize; usageNum++) {
switch (usageList[usageNum]) {
case kHIDUsage_Dig_BarrelSwitch:
buttonState |= 2; break;
case kHIDUsage_Dig_TipSwitch:
buttonState |= 1; break;
case kHIDUsage_Dig_InRange:
inRange = 1;
break;
default:
break;
}
}
}
}
if (_scrollWheelCollection != -1) {
status = HIDGetUsageValue (kHIDInputReport,
kHIDPage_GenericDesktop,
_scrollWheelCollection,
kHIDUsage_GD_Wheel,
&usageValue,
_preparsedReportDescriptorData,
mouseData,
ret_bufsize);
if (status == noErr) {
scrollWheelDelta = usageValue;
}
}
if (_horzScrollCollection != -1) {
status = HIDGetUsageValue (kHIDInputReport,
kHIDPage_GenericDesktop,
_horzScrollCollection,
kHIDUsage_GD_Z,
&usageValue,
_preparsedReportDescriptorData,
mouseData,
ret_bufsize);
if (status == noErr) {
horzScrollDelta = usageValue;
}
}
status = HIDGetUsageValue (kHIDInputReport,
kHIDPage_GenericDesktop,
_xCollection,
kHIDUsage_GD_X,
&usageValue,
_preparsedReportDescriptorData,
mouseData,
ret_bufsize);
if (status == noErr) {
dx = usageValue;
}
status = HIDGetUsageValue (kHIDInputReport,
kHIDPage_GenericDesktop,
_yCollection,
kHIDUsage_GD_Y,
&usageValue,
_preparsedReportDescriptorData,
mouseData,
ret_bufsize);
if (status == noErr) {
dy = usageValue;
}
clock_get_uptime(&now);
if (_absoluteCoordinates) {
Point newLoc;
newLoc.x = dx;
newLoc.y = dy;
dispatchAbsolutePointerEvent(&newLoc, &_bounds, buttonState, inRange, pressure, _tipPressureMin, _tipPressureMax, 90, now);
} else {
dispatchRelativePointerEvent(dx, dy, buttonState, now);
}
if (scrollWheelDelta != 0 || horzScrollDelta != 0) {
dispatchScrollWheelEvent(scrollWheelDelta, horzScrollDelta, 0, now);
}
return kIOReturnSuccess;
}
IOItemCount IOHIDPointing::buttonCount()
{
return _numButtons;
}
IOFixed IOHIDPointing::resolution()
{
return _resolution;
}
void IOHIDPointing::propagateProperties()
{
OSData *data = NULL;
if (_provider) {
data = OSDynamicCast( OSData, _provider->getProperty( kIOHIDPointerAccelerationTableKey ));
if (data)
setProperty(kIOHIDPointerAccelerationTableKey, data);
data = OSDynamicCast( OSData, _provider->getProperty( kIOHIDScrollAccelerationTableKey ));
if (data)
setProperty(kIOHIDScrollAccelerationTableKey, data);
}
}