#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/hidsystem/IOHIDParameter.h>
#include "ApplePS2Mouse.h"
#define super IOHIPointing
OSDefineMetaClassAndStructors(ApplePS2Mouse, IOHIPointing);
UInt32 ApplePS2Mouse::deviceType() { return NX_EVS_DEVICE_TYPE_MOUSE; };
UInt32 ApplePS2Mouse::interfaceID() { return NX_EVS_DEVICE_INTERFACE_BUS_ACE; };
IOItemCount ApplePS2Mouse::buttonCount() { return 3; };
IOFixed ApplePS2Mouse::resolution() { return _resolution; };
bool ApplePS2Mouse::init(OSDictionary * properties)
{
if (!super::init(properties)) return false;
_device = 0;
_interruptHandlerInstalled = false;
_packetByteCount = 0;
_packetLength = kPacketLengthStandard;
_resolution = (150) << 16; _type = kMouseTypeStandard;
_mouseInfoBytes = (UInt32)-1;
return true;
}
ApplePS2Mouse * ApplePS2Mouse::probe(IOService * provider, SInt32 * score)
{
ApplePS2MouseDevice * device = (ApplePS2MouseDevice *)provider;
PS2Request * request = device->allocateRequest();
bool success;
if (!super::probe(provider, score)) return 0;
request->commands[0].command = kPS2C_WriteCommandPort;
request->commands[0].inOrOut = kCP_TransmitToMouse;
request->commands[1].command = kPS2C_WriteDataPort;
request->commands[1].inOrOut = kDP_GetMouseInformation;
request->commands[2].command = kPS2C_ReadDataPortAndCompare;
request->commands[2].inOrOut = kSC_Acknowledge;
request->commands[3].command = kPS2C_ReadDataPort;
request->commands[3].inOrOut = 0;
request->commands[4].command = kPS2C_ReadDataPort;
request->commands[4].inOrOut = 0;
request->commands[5].command = kPS2C_ReadDataPort;
request->commands[5].inOrOut = 0;
request->commandsCount = 6;
device->submitRequestAndBlock(request);
success = (request->commandsCount == 6);
device->freeRequest(request);
return (success) ? this : 0;
}
bool ApplePS2Mouse::start(IOService * provider)
{
if (!super::start(provider)) return false;
_mouseResetThreadCall = thread_call_allocate(handleMouseReset, this);
if (!_mouseResetThreadCall) return false;
_device = (ApplePS2MouseDevice *)provider;
_device->retain();
resetMouse();
return true;
}
void ApplePS2Mouse::stop(IOService * provider)
{
assert(_device == provider);
setMouseEnable(false);
setCommandByte(kCB_DisableMouseClock, kCB_EnableMouseIRQ);
if ( _interruptHandlerInstalled ) _device->uninstallInterruptAction();
_interruptHandlerInstalled = false;
_device->release();
_device = 0;
super::stop(provider);
}
void ApplePS2Mouse::free()
{
if (_mouseResetThreadCall)
{
thread_call_free(_mouseResetThreadCall);
_mouseResetThreadCall = 0;
}
super::free();
}
void ApplePS2Mouse::handleMouseReset(thread_call_param_t param0,
thread_call_param_t param1)
{
ApplePS2Mouse * self = (ApplePS2Mouse *) param0;
assert(self);
self->resetMouse();
self->release();
}
void ApplePS2Mouse::resetMouse()
{
if (_mouseInfoBytes == (UInt32)-1)
{
_mouseInfoBytes = getMouseInformation();
switch (_mouseInfoBytes & 0x00FF00)
{
case 0x0000: _resolution = (25) << 16; break; case 0x0100: _resolution = (50) << 16; break; case 0x0200: _resolution = (100) << 16; break; case 0x0300: _resolution = (200) << 16; break; default: _resolution = (150) << 16; break; }
}
else
{
setMouseResolution(_mouseInfoBytes >> 8);
}
if ( setIntellimouseMode() == true )
{
_packetLength = kPacketLengthIntellimouse;
_type = kMouseTypeIntellimouse;
setProperty(kIOHIDScrollResolutionKey, (10 << 16), 32);
}
else
{
_packetLength = kPacketLengthStandard;
_type = kMouseTypeStandard;
removeProperty(kIOHIDScrollResolutionKey);
}
_packetByteCount = 0;
setCommandByte(kCB_EnableMouseIRQ, kCB_DisableMouseClock);
_device->installInterruptAction(this,
(PS2InterruptAction)&ApplePS2Mouse::interruptOccurred);
_interruptHandlerInstalled = true;
setMouseEnable(true);
}
void ApplePS2Mouse::scheduleMouseReset()
{
setMouseEnable(false);
if ( _interruptHandlerInstalled ) _device->uninstallInterruptAction();
_interruptHandlerInstalled = false;
if (thread_call_enter(_mouseResetThreadCall) == FALSE) retain();
}
void ApplePS2Mouse::interruptOccurred(UInt8 data) {
if (_packetByteCount == 0 && ((data == kSC_Acknowledge) || !(data & 0x08)))
{
IOLog("%s: Unexpected data from PS/2 controller.\n", getName());
if (_mouseResetCount < 5)
{
_mouseResetCount++;
scheduleMouseReset();
}
return;
}
_packetBuffer[_packetByteCount++] = data;
if (_packetByteCount == _packetLength)
{
dispatchRelativePointerEventWithPacket(_packetBuffer, _packetLength);
_packetByteCount = 0;
_mouseResetCount = 0;
}
else if (_packetByteCount == 2 && _packetBuffer[0] == 0xAA)
{
scheduleMouseReset();
}
}
void ApplePS2Mouse::dispatchRelativePointerEventWithPacket(UInt8 * packet,
UInt32 packetSize)
{
UInt32 buttons = 0;
SInt32 dx;
SInt32 dy;
SInt16 dz = 0;
AbsoluteTime now;
if ( (packet[0] & 0x1) ) buttons |= 0x1; if ( (packet[0] & 0x2) ) buttons |= 0x2; if ( (packet[0] & 0x4) ) buttons |= 0x4;
dx = ((packet[0] & 0x10) ? 0xffffff00 : 0 ) | packet[1];
dy = -(((packet[0] & 0x20) ? 0xffffff00 : 0 ) | packet[2]);
clock_get_uptime(&now);
dispatchRelativePointerEvent(dx, dy, buttons, now);
if ( packetSize > 3 )
{
dz = (SInt16)((SInt8)packet[3]);
if ( dz )
{
dispatchScrollWheelEvent(-dz, 0, 0, now);
}
}
return;
}
void ApplePS2Mouse::setMouseEnable(bool enable)
{
PS2Request * request = _device->allocateRequest();
request->commands[0].command = kPS2C_WriteCommandPort;
request->commands[0].inOrOut = kCP_TransmitToMouse;
request->commands[1].command = kPS2C_WriteDataPort;
request->commands[1].inOrOut = (enable)?kDP_Enable:kDP_SetDefaultsAndDisable;
request->commands[2].command = kPS2C_ReadDataPortAndCompare;
request->commands[2].inOrOut = kSC_Acknowledge;
request->commandsCount = 3;
_device->submitRequest(request); }
void ApplePS2Mouse::setMouseSampleRate(UInt8 sampleRate)
{
PS2Request * request = _device->allocateRequest();
request->commands[0].command = kPS2C_WriteCommandPort;
request->commands[0].inOrOut = kCP_TransmitToMouse;
request->commands[1].command = kPS2C_WriteDataPort;
request->commands[1].inOrOut = kDP_SetMouseSampleRate;
request->commands[2].command = kPS2C_ReadDataPortAndCompare;
request->commands[2].inOrOut = kSC_Acknowledge;
request->commands[3].command = kPS2C_WriteCommandPort;
request->commands[3].inOrOut = kCP_TransmitToMouse;
request->commands[4].command = kPS2C_WriteDataPort;
request->commands[4].inOrOut = sampleRate;
request->commands[5].command = kPS2C_ReadDataPortAndCompare;
request->commands[5].inOrOut = kSC_Acknowledge;
request->commandsCount = 6;
_device->submitRequest(request); }
void ApplePS2Mouse::setMouseResolution(UInt8 resolution)
{
PS2Request * request = _device->allocateRequest();
request->commands[0].command = kPS2C_WriteCommandPort;
request->commands[0].inOrOut = kCP_TransmitToMouse;
request->commands[1].command = kPS2C_WriteDataPort;
request->commands[1].inOrOut = kDP_SetMouseResolution;
request->commands[2].command = kPS2C_ReadDataPortAndCompare;
request->commands[2].inOrOut = kSC_Acknowledge;
request->commands[3].command = kPS2C_WriteCommandPort;
request->commands[3].inOrOut = kCP_TransmitToMouse;
request->commands[4].command = kPS2C_WriteDataPort;
request->commands[4].inOrOut = resolution;
request->commands[5].command = kPS2C_ReadDataPortAndCompare;
request->commands[5].inOrOut = kSC_Acknowledge;
request->commandsCount = 6;
_device->submitRequest(request); }
bool ApplePS2Mouse::setIntellimouseMode()
{
bool isIntellimouse;
setMouseSampleRate(200);
setMouseSampleRate(100);
setMouseSampleRate(80 );
IOSleep(50);
isIntellimouse = ( getMouseID() == kMouseTypeIntellimouse );
setMouseSampleRate(_mouseInfoBytes & 0x0000FF);
return isIntellimouse;
}
UInt32 ApplePS2Mouse::getMouseInformation()
{
PS2Request * request = _device->allocateRequest();
UInt32 returnValue = (UInt32)(-1);
request->commands[0].command = kPS2C_WriteCommandPort;
request->commands[0].inOrOut = kCP_TransmitToMouse;
request->commands[1].command = kPS2C_WriteDataPort;
request->commands[1].inOrOut = kDP_GetMouseInformation;
request->commands[2].command = kPS2C_ReadDataPortAndCompare;
request->commands[2].inOrOut = kSC_Acknowledge;
request->commands[3].command = kPS2C_ReadDataPort;
request->commands[3].inOrOut = 0;
request->commands[4].command = kPS2C_ReadDataPort;
request->commands[4].inOrOut = 0;
request->commands[5].command = kPS2C_ReadDataPort;
request->commands[5].inOrOut = 0;
request->commandsCount = 6;
_device->submitRequestAndBlock(request);
if (request->commandsCount == 6) {
returnValue = ((UInt32)request->commands[3].inOrOut << 16) |
((UInt32)request->commands[4].inOrOut << 8 ) |
((UInt32)request->commands[5].inOrOut);
}
_device->freeRequest(request);
return returnValue;
}
UInt8 ApplePS2Mouse::getMouseID()
{
PS2Request * request = _device->allocateRequest();
UInt8 returnValue = (UInt8)(-1);
request->commands[0].command = kPS2C_WriteCommandPort;
request->commands[0].inOrOut = kCP_TransmitToMouse;
request->commands[1].command = kPS2C_WriteDataPort;
request->commands[1].inOrOut = kDP_GetId;
request->commands[2].command = kPS2C_ReadDataPortAndCompare;
request->commands[2].inOrOut = kSC_Acknowledge;
request->commands[3].command = kPS2C_ReadDataPort;
request->commands[3].inOrOut = 0;
request->commandsCount = 4;
_device->submitRequestAndBlock(request);
if (request->commandsCount == 4) returnValue = request->commands[3].inOrOut;
_device->freeRequest(request);
return returnValue;
}
void ApplePS2Mouse::setCommandByte(UInt8 setBits, UInt8 clearBits)
{
UInt8 commandByte;
UInt8 commandByteNew;
PS2Request * request = _device->allocateRequest();
do
{
request->commands[0].command = kPS2C_WriteCommandPort;
request->commands[0].inOrOut = kCP_GetCommandByte;
request->commands[1].command = kPS2C_ReadDataPort;
request->commands[1].inOrOut = 0;
request->commandsCount = 2;
_device->submitRequestAndBlock(request);
commandByte = request->commands[1].inOrOut;
commandByteNew = (commandByte | setBits) & (~clearBits);
request->commands[0].command = kPS2C_WriteCommandPort;
request->commands[0].inOrOut = kCP_GetCommandByte;
request->commands[1].command = kPS2C_ReadDataPortAndCompare;
request->commands[1].inOrOut = commandByte;
request->commands[2].command = kPS2C_WriteCommandPort;
request->commands[2].inOrOut = kCP_SetCommandByte;
request->commands[3].command = kPS2C_WriteDataPort;
request->commands[3].inOrOut = commandByteNew;
request->commandsCount = 4;
_device->submitRequestAndBlock(request);
} while (request->commandsCount != 4);
_device->freeRequest(request);
}