Source to iokit/Drivers/usb/drvAppleMouse/AppleMouse.cpp
/*
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
*
* HISTORY
*
*/
#include "AppleMouse.h"
#include <IOKit/hidsystem/IOHIPointing.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/usb/IOUSBInterface.h>
#include <IOKit/usb/IOUSBPipe.h>
#include <libkern/OSByteOrder.h>
#include <IOKit/hidsystem/IOHIDShared.h>
#include <IOKit/hidsystem/IOHIDDescriptorParser.h>
#define super IOHIPointing
#define DEBUGGING_LEVEL 0
#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?
OSDefineMetaClassAndStructors(AppleMouse, IOHIPointing)
bool AppleMouse::start(IOService * provider)
{
IOReturn err = 0;
if( !super::start(provider))
return (false);
if( !provider->open(this))
return (false);
// remember my device
_preparsedReportDescriptorData = NULL;
_buttonCollection = -1;
_xCollection = -1;
_yCollection = -1;
_tipPressureCollection = -1;
_digitizerButtonCollection = -1;
_scrollWheelCollection = -1;
_hasInRangeReport = false;
_tipPressureMin = 255;
_tipPressureMax = 255;
_numButtons = 1;
_interface = OSDynamicCast(IOUSBInterface, provider);
_deviceDescriptor = _interface->deviceDescriptor();
do {
IOUSBFindEndpointRequest request;
IOLog("%s: USB Generic Mouse @ %d\n", getName(), _interface->address());
request.type = kUSBInterrupt;
request.direction = kUSBIn;
_interruptPipe = _interface->findNextPipe(NULL, &request);
if(!_interruptPipe)
return false;
if (!parseHIDDescriptor()) {
break;
}
_maxPacketSize = request.maxPacketSize;
_buffer = IOBufferMemoryDescriptor::withCapacity(8, kIODirectionIn);
_completion.target = (void *)this;
_completion.action = (IOUSBCompletionAction) &AppleMouse::readHandler;
_completion.parameter = (void *)0; // not used
_buffer->setLength(_maxPacketSize);
if ((err = _interruptPipe->read(_buffer, &_completion)))
break;
#if (DEBUGGING_LEVEL > 0)
printInterfaceDescriptor(_interface->descriptor);
#endif
return(true);
} while (false);
IOLog("%s: aborting startup. err = %d\n", getName(), err);
provider->close(this);
stop(provider);
return(false);
}
bool AppleMouse::parseHIDDescriptor()
{
bool success = true;
IOReturn err;
IOUSBDevRequest devReq;
IOUSBHIDDescriptor hidDescriptor;
UInt8 * reportDescriptor = NULL;
UInt16 size = 0;
OSStatus result;
HIDButtonCaps buttonCaps[kMaxButtons];
UInt32 numButtonCaps = kMaxButtons;
HIDValueCaps valueCaps[kMaxValues];
UInt32 numValueCaps = kMaxValues;
do {
devReq.rqDirection = kUSBIn;
devReq.rqType = kUSBStandard;
devReq.rqRecipient = kUSBInterface;
devReq.bRequest = kUSBRqGetDescriptor;
OSWriteLittleInt16(&devReq.wValue, 0, ((0x20 | kUSBHIDDesc) << 8));
OSWriteLittleInt16(&devReq.wIndex, 0, _interface->interfaceDescriptor()->interfaceNumber);
OSWriteLittleInt16(&devReq.wLength, 0, sizeof(IOUSBHIDDescriptor));
devReq.pData = &hidDescriptor;
err = _interface->deviceRequest(&devReq);
if (err) {
IOLog ("%s: error getting HID Descriptor. err=0x%x\n", getName(), err);
success = false;
break;
}
size = (hidDescriptor.hidDescriptorLengthHi * 256) + hidDescriptor.hidDescriptorLengthLo;
reportDescriptor = (UInt8 *)IOMalloc(size);
OSWriteLittleInt16(&devReq.wValue, 0, ((0x20 | kUSBReportDesc) << 8));
OSWriteLittleInt16(&devReq.wLength, 0, size);
devReq.pData = reportDescriptor;
err = _interface->deviceRequest(&devReq);
if (err) {
IOLog ("%s: error getting HID report descriptor. err=0x%x\n", getName(), err);
success = false;
break;
}
result = HIDOpenReportDescriptor (reportDescriptor, size, &_preparsedReportDescriptorData, 0);
if (result != noErr) {
IOLog ("%s: error parsing HID report descriptor. err=0x%lx\n", getName(), result);
success = false;
break;
}
result = HIDGetSpecificButtonCaps(kHIDInputReport,
kHIDPage_Button,
0,
0,
buttonCaps,
&numButtonCaps,
_preparsedReportDescriptorData);
if ((result == noErr) && (numButtonCaps > 0)) {
_buttonCollection = buttonCaps[0].collection; // Do we actually need to look at and store all of the button page collections?
if (buttonCaps[0].isRange) {
_numButtons = buttonCaps[0].u.range.usageMax - buttonCaps[0].u.range.usageMin + 1;
}
}
numButtonCaps = kMaxButtons;
result = HIDGetSpecificButtonCaps(kHIDInputReport,
kHIDPage_Digitizer,
0,
0,
buttonCaps,
&numButtonCaps,
_preparsedReportDescriptorData);
if ((result == noErr) && (numButtonCaps > 0)) {
_digitizerButtonCollection = buttonCaps[0].collection;
}
numButtonCaps = kMaxButtons;
result = HIDGetSpecificButtonCaps(kHIDInputReport,
kHIDPage_Digitizer,
0,
kHIDUsage_Dig_InRange,
buttonCaps,
&numButtonCaps,
_preparsedReportDescriptorData);
if (result == noErr) {
_hasInRangeReport = true;
}
result = HIDGetSpecificValueCaps(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;
} else {
IOLog ("%s: error getting X axis information from HID report descriptor. err=0x%lx\n", getName(), result);
success = false;
break;
}
numValueCaps = kMaxValues;
result = HIDGetSpecificValueCaps(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);
success = false;
break;
}
numValueCaps = kMaxValues;
result = HIDGetSpecificValueCaps(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 = HIDGetSpecificValueCaps(kHIDInputReport,
kHIDPage_GenericDesktop,
0,
kHIDUsage_GD_Wheel,
valueCaps,
&numValueCaps,
_preparsedReportDescriptorData);
if ((result == noErr) && (numValueCaps > 0)) {
_scrollWheelCollection = valueCaps[0].collection;
}
} while (false);
if (reportDescriptor) {
IOFree(reportDescriptor, size);
}
return success;
}
void AppleMouse::stop(IOService * provider)
{
if (_buffer) {
_buffer->release();
_buffer = 0;
}
if (_preparsedReportDescriptorData) {
HIDCloseReportDescriptor(_preparsedReportDescriptorData);
}
super::stop(provider);
}
void AppleMouse::readHandler(void * parameter,
IOReturn status,
UInt32 bufferSizeRemaining)
{
switch (status)
{
case kIOReturnSuccess:
break;
case kIOReturnOverrun:
// Not sure what to do with this error. It means more data
// came back than the size of a descriptor. Hmmm. For now
// just ignore it and assume the data that did come back is
// useful.
IOLog("%s: overrun error. ignoring.\n", getName());
break;
case kIOReturnNotResponding:
// This probably means the device was unplugged. Now
// we need to close the driver.
IOLog("%s: Device unplugged. Goodbye\n", getName());
goto close;
default:
// We should handle other errors more intelligently, but
// for now just return and assume the error is recoverable.
IOLog("%s: error reading interrupt pipe\n", getName());
goto queueAnother;
}
// Handle the data
MoveMouse((UInt8 *) _buffer->getBytesNoCopy(),
(UInt32) _maxPacketSize - bufferSizeRemaining);
queueAnother:
// Reset the buffer
_buffer->setLength(_maxPacketSize);
// Queue up another one before we leave.
if ((status = _interruptPipe->read(_buffer, &_completion)))
{
// This is bad. We probably shouldn't continue on from here.
IOLog("%s: immediate error %d queueing read\n", getName(), status);
goto close;
}
return;
close:
_interface->close(this);
return;
}
void AppleMouse::MoveMouse(UInt8 * mouseData,
UInt32 ret_bufsize)
{
OSStatus status;
HIDUsage usageList[kMaxButtons];
UInt32 usageListSize = kMaxButtons;
UInt32 buttonState = 0;
SInt32 usageValue;
SInt32 pressure = MAXPRESSURE;
int dx = 0, dy = 0, scrollWheelDelta = 0;
AbsoluteTime now;
bool inRange = !_hasInRangeReport;
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; // Set the right (secondary) button for the barrel switch
break;
case kHIDUsage_Dig_TipSwitch:
buttonState |= 1; // Set the left (primary) button for the tip switch
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;
}
}
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) {
dispatchScrollWheelEvent(scrollWheelDelta, 0, 0, now);
}
}
UInt32 AppleMouse::interfaceID( void )
{
return( NX_EVS_DEVICE_INTERFACE_OTHER );
}
UInt32 AppleMouse::deviceType( void )
{
return( 0 );
}
/*
* This was taken directly from the AppleADBMouse driver.
* We need to determine the best generic acceleration curve for USB devices.
*/
void AppleMouse::accelerationTable ( IOHIAccelerationPoint ** table, IOItemCount * numEntries )
{
static IOHIAccelerationPoint defaultTable[] = {
{ 0x0000000, 0x000000 },
{ 0x000713b, 0x006000 },
{ 0x0010000, 0x010000 },
{ 0x0044ec5, 0x108000 },
{ 0x00c0000, 0x5f0000 },
{ 0x016ec4f, 0x8b0000 },
{ 0x01d3b14, 0x948000 },
{ 0x0227627, 0x960000 },
{ 0x7ffffff, 0x960000 }
};
*table = defaultTable;
*numEntries = sizeof(defaultTable) / sizeof(defaultTable[0]);
}
IOFixed AppleMouse::resolution()
{
return (400 << 16);
}
IOItemCount AppleMouse::buttonCount()
{
return _numButtons;
}