mirror of
https://github.com/zhigang1992/OS-X-Voodoo-PS2-Controller.git
synced 2026-01-12 17:32:48 +08:00
2188 lines
66 KiB
C++
2188 lines
66 KiB
C++
/*
|
|
* 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@
|
|
*/
|
|
|
|
#define DISABLE_CLOCKS_IRQS_BEFORE_SLEEP 1
|
|
#define FULL_INIT_AFTER_WAKE 1
|
|
|
|
#include <IOKit/IOService.h>
|
|
#include <IOKit/IOWorkLoop.h>
|
|
#include <IOKit/IOCommandGate.h>
|
|
#include <IOKit/IOTimerEventSource.h>
|
|
#include "ApplePS2KeyboardDevice.h"
|
|
#include "ApplePS2MouseDevice.h"
|
|
#include "VoodooPS2Controller.h"
|
|
|
|
enum {
|
|
kPS2PowerStateSleep = 0,
|
|
kPS2PowerStateDoze = 1,
|
|
kPS2PowerStateNormal = 2,
|
|
kPS2PowerStateCount
|
|
};
|
|
|
|
static const IOPMPowerState PS2PowerStateArray[ kPS2PowerStateCount ] =
|
|
{
|
|
{ 1,0,0,0,0,0,0,0,0,0,0,0 },
|
|
{ 1,kIOPMDeviceUsable, kIOPMDoze, kIOPMDoze, 0,0,0,0,0,0,0,0 },
|
|
{ 1,kIOPMDeviceUsable, IOPMPowerOn, IOPMPowerOn, 0,0,0,0,0,0,0,0 }
|
|
};
|
|
|
|
// =============================================================================
|
|
// Interrupt-Time Support Functions
|
|
//
|
|
|
|
//static
|
|
void ApplePS2Controller::interruptHandlerMouse(OSObject*, void* refCon, IOService*, int)
|
|
{
|
|
ApplePS2Controller* me = (ApplePS2Controller*)refCon;
|
|
if (me->_ignoreInterrupts)
|
|
return;
|
|
|
|
//
|
|
// Wake our workloop to service the interrupt. This is an edge-triggered
|
|
// interrupt, so returning from this routine without clearing the interrupt
|
|
// condition is perfectly normal.
|
|
//
|
|
#if HANDLE_INTERRUPT_DATA_LATER
|
|
me->_interruptSourceMouse->interruptOccurred(0, 0, 0);
|
|
#else
|
|
me->handleInterrupt(kDT_Mouse);
|
|
#endif
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
//static
|
|
void ApplePS2Controller::interruptHandlerKeyboard(OSObject*, void* refCon, IOService*, int)
|
|
{
|
|
ApplePS2Controller* me = (ApplePS2Controller*)refCon;
|
|
if (me->_ignoreInterrupts)
|
|
return;
|
|
|
|
#if DEBUGGER_SUPPORT
|
|
//
|
|
// The keyboard interrupt handler reads in the pending scan code and stores
|
|
// it on our internal queue; should it completes a debugger escape sequence,
|
|
// we jump to the debugger function immediately.
|
|
//
|
|
|
|
UInt8 key;
|
|
UInt8 status;
|
|
int state;
|
|
|
|
// Lock out the keyboard interrupt handler [redundant here] and claim
|
|
// exclusive access to the internal keyboard queue.
|
|
|
|
me->lockController(&state);
|
|
|
|
// Verify that data is available on the controller's input port.
|
|
|
|
if ( ((status = inb(kCommandPort)) & kOutputReady) )
|
|
{
|
|
// Verify that the data is keyboard data, otherwise call mouse handler.
|
|
// This case should never really happen, but if it does, we handle it.
|
|
|
|
if ( (status & kMouseData) )
|
|
{
|
|
interruptHandlerMouse(0, refCon, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
// Retrieve the keyboard data on the controller's input port.
|
|
|
|
IODelay(kDataDelay);
|
|
key = inb(kDataPort);
|
|
|
|
// Call the debugger-key-sequence checking code (if a debugger sequence
|
|
// completes, the debugger function will be invoked immediately within
|
|
// doEscape). The doEscape call may insist that we drop the scan code
|
|
// we just received in some cases (a true return) -- we don't question
|
|
// it's judgement and comply. No escape check if debugging is disabled.
|
|
|
|
if (me->_debuggingEnabled == false || me->doEscape(key) == false)
|
|
me->enqueueKeyboardData(key);
|
|
|
|
// In all cases, we wake up our workloop to service the interrupt data.
|
|
me->_interruptSourceKeyboard->interruptOccurred(0, 0, 0);
|
|
}
|
|
}
|
|
|
|
// Remove the lockout on the keyboard interrupt handler [ineffective here]
|
|
// and release our exclusive access to the internal keyboard queue.
|
|
|
|
me->unlockController(state);
|
|
#else
|
|
//
|
|
// Wake our workloop to service the interrupt. This is an edge-triggered
|
|
// interrupt, so returning from this routine without clearing the interrupt
|
|
// condition is perfectly normal.
|
|
//
|
|
#if HANDLE_INTERRUPT_DATA_LATER
|
|
me->_interruptSourceKeyboard->interruptOccurred(0, 0, 0);
|
|
#else
|
|
me->handleInterrupt(kDT_Keyboard);
|
|
#endif
|
|
|
|
#endif //DEBUGGER_SUPPORT
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
#if WATCHDOG_TIMER
|
|
|
|
void ApplePS2Controller::onWatchdogTimer()
|
|
{
|
|
if (!_ignoreInterrupts)
|
|
handleInterrupt(kDT_Watchdog);
|
|
_watchdogTimer->setTimeoutMS(kWatchdogTimerInterval);
|
|
}
|
|
|
|
#endif // WATCHDOG_TIMER
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
#if !HANDLE_INTERRUPT_DATA_LATER
|
|
|
|
void ApplePS2Controller::handleInterrupt(PS2DeviceType deviceType)
|
|
{
|
|
////IOLog("%s:handleInterrupt(%s)\n", getName(), deviceType == kDT_Keyboard ? "kDT_Keyboard" : deviceType == kDT_Watchdog ? "kDT_Watchdog" : "kDT_Mouse");
|
|
|
|
// Loop only while there is data currently on the input stream.
|
|
|
|
bool wakeMouse = false;
|
|
bool wakeKeyboard = false;
|
|
while (1)
|
|
{
|
|
// while getting status and reading the port, no interrupts...
|
|
bool enable = ml_set_interrupts_enabled(false);
|
|
IODelay(kDataDelay);
|
|
UInt8 status = inb(kCommandPort);
|
|
if (!(status & kOutputReady))
|
|
{
|
|
// no data available, so break out and return
|
|
ml_set_interrupts_enabled(enable);
|
|
break;
|
|
}
|
|
|
|
#if WATCHDOG_TIMER
|
|
// do not process mouse data in watchdog timer
|
|
if (deviceType == kDT_Watchdog && (status & kMouseData))
|
|
{
|
|
ml_set_interrupts_enabled(enable);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
// read the data
|
|
IODelay(kDataDelay);
|
|
UInt8 data = inb(kDataPort);
|
|
|
|
// now ok for interrupts, we have read status, and found data...
|
|
// (it does not matter [too much] if keyboard data is delivered out of order)
|
|
ml_set_interrupts_enabled(enable);
|
|
|
|
#if WATCHDOG_TIMER
|
|
//REVIEW: remove this debug eventually...
|
|
if (deviceType == kDT_Watchdog)
|
|
IOLog("%s:handleInterrupt(kDT_Watchdog): %s = %02x\n", getName(), status & kMouseData ? "mouse" : "keyboard", data);
|
|
#endif
|
|
if (status & kMouseData)
|
|
{
|
|
// Dispatch the data to the mouse driver.
|
|
if (kPS2IR_packetReady == _dispatchDriverInterrupt(kDT_Mouse, data))
|
|
wakeMouse = true;
|
|
}
|
|
else
|
|
{
|
|
// Dispatch the data to the keyboard driver.
|
|
if (kPS2IR_packetReady == _dispatchDriverInterrupt(kDT_Keyboard, data))
|
|
wakeKeyboard = true;
|
|
}
|
|
} // while (forever)
|
|
|
|
// wake up workloop based mouse interrupt source if needed
|
|
if (wakeMouse)
|
|
_interruptSourceMouse->interruptOccurred(0, 0, 0);
|
|
// wake up workloop based keyboard interrupt source if needed
|
|
if (wakeKeyboard)
|
|
_interruptSourceKeyboard->interruptOccurred(0, 0, 0);
|
|
}
|
|
|
|
#else // HANDLE_INTERRUPT_DATA_LATER
|
|
|
|
void ApplePS2Controller::handleInterrupt(PS2DeviceType deviceType)
|
|
{
|
|
////IOLog("%s:handleInterrupt(%s)\n", getName(), deviceType == kDT_Keyboard ? "kDT_Keyboard" : deviceType == kDT_Watchdog ? "kDT_Watchdog" : "kDT_Mouse");
|
|
|
|
// Loop only while there is data currently on the input stream.
|
|
|
|
UInt8 status;
|
|
IODelay(kDataDelay);
|
|
while ((status = inb(kCommandPort)) & kOutputReady)
|
|
{
|
|
#if WATCHDOG_TIMER
|
|
if (deviceType == kDT_Watchdog && (status & kMouseData))
|
|
break;
|
|
#endif
|
|
|
|
IODelay(kDataDelay);
|
|
UInt8 data = inb(kDataPort);
|
|
#if WATCHDOG_TIMER
|
|
//REVIEW: remove this debug eventually...
|
|
if (deviceType == kDT_Watchdog)
|
|
IOLog("%s:handleInterrupt(kDT_Watchdog): %s = %02x\n", getName(), status & kMouseData ? "mouse" : "keyboard", data);
|
|
#endif
|
|
dispatchDriverInterrupt(status & kMouseData ? kDT_Mouse : kDT_Keyboard, data);
|
|
IODelay(kDataDelay);
|
|
}
|
|
}
|
|
|
|
#endif // HANDLE_INTERRUPT_DATA_LATER
|
|
|
|
// =============================================================================
|
|
// ApplePS2Controller Class Implementation
|
|
//
|
|
|
|
OSDefineMetaClassAndStructors(ApplePS2Controller, IOService);
|
|
|
|
bool ApplePS2Controller::init(OSDictionary* dict)
|
|
{
|
|
if (!super::init(dict))
|
|
return false;
|
|
|
|
// find config specific to Platform Profile
|
|
OSDictionary* list = OSDynamicCast(OSDictionary, dict->getObject(kPlatformProfile));
|
|
OSDictionary* config = ApplePS2Controller::makeConfigurationNode(list);
|
|
#ifdef DEBUG
|
|
if (config)
|
|
setProperty(kMergedConfiguration, config);
|
|
#endif
|
|
|
|
//
|
|
// Initialize minimal state.
|
|
//
|
|
|
|
_workLoop = 0;
|
|
|
|
_interruptSourceKeyboard = 0;
|
|
_interruptSourceMouse = 0;
|
|
_interruptTargetKeyboard = 0;
|
|
_interruptTargetMouse = 0;
|
|
_interruptActionKeyboard = NULL;
|
|
_interruptActionMouse = NULL;
|
|
_packetActionKeyboard = NULL;
|
|
_packetActionMouse = NULL;
|
|
_interruptInstalledKeyboard = false;
|
|
_interruptInstalledMouse = false;
|
|
_ignoreInterrupts = 0;
|
|
_ignoreOutOfOrder = 0;
|
|
|
|
_powerControlTargetKeyboard = 0;
|
|
_powerControlTargetMouse = 0;
|
|
_powerControlActionKeyboard = 0;
|
|
_powerControlActionMouse = 0;
|
|
_powerControlInstalledKeyboard = false;
|
|
_powerControlInstalledMouse = false;
|
|
|
|
_messageTargetKeyboard = 0;
|
|
_messageTargetMouse = 0;
|
|
_messageActionKeyboard = 0;
|
|
_messageActionMouse = 0;
|
|
_messageInstalledKeyboard = false;
|
|
_messageInstalledMouse = false;
|
|
|
|
_mouseDevice = 0;
|
|
_keyboardDevice = 0;
|
|
|
|
_suppressTimeout = false;
|
|
|
|
#ifdef NEWIRQ
|
|
_newIRQLayout = false; // turbo
|
|
#endif
|
|
|
|
_wakedelay = 10;
|
|
_cmdGate = 0;
|
|
|
|
_requestQueueLock = 0;
|
|
_cmdbyteLock = 0;
|
|
|
|
#if WATCHDOG_TIMER
|
|
_watchdogTimer = 0;
|
|
#endif
|
|
|
|
queue_init(&_requestQueue);
|
|
|
|
_currentPowerState = kPS2PowerStateNormal;
|
|
|
|
#if DEBUGGER_SUPPORT
|
|
_extendedState = false;
|
|
_modifierState = 0x00;
|
|
_debuggingEnabled = false;
|
|
|
|
_keyboardQueueAlloc = NULL;
|
|
queue_init(&_keyboardQueue);
|
|
queue_init(&_keyboardQueueUnused);
|
|
|
|
_controllerLock = IOSimpleLockAlloc();
|
|
if (!_controllerLock) return false;
|
|
#endif //DEBUGGER_SUPPORT
|
|
|
|
setPropertiesGated(config);
|
|
OSSafeRelease(config);
|
|
|
|
return true;
|
|
}
|
|
|
|
#if DEBUGGER_SUPPORT
|
|
void ApplePS2Controller::free(void)
|
|
{
|
|
if (_controllerLock)
|
|
{
|
|
IOSimpleLockFree(_controllerLock);
|
|
_controllerLock = 0;
|
|
}
|
|
super::free();
|
|
}
|
|
#endif
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
IOReturn ApplePS2Controller::setPropertiesGated(OSObject* props)
|
|
{
|
|
OSDictionary* dict = OSDynamicCast(OSDictionary, props);
|
|
if (!dict)
|
|
return kIOReturnSuccess;
|
|
|
|
// get wakedelay
|
|
if (OSNumber* num = OSDynamicCast(OSNumber, dict->getObject("WakeDelay")))
|
|
{
|
|
_wakedelay = (int)num->unsigned32BitValue();
|
|
setProperty("WakeDelay", _wakedelay, 32);
|
|
}
|
|
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
IOReturn ApplePS2Controller::setProperties(OSObject* props)
|
|
{
|
|
if (_cmdGate)
|
|
{
|
|
// syncronize through workloop...
|
|
IOReturn result = _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Controller::setPropertiesGated), props);
|
|
if (kIOReturnSuccess != result)
|
|
return result;
|
|
}
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::resetController(void)
|
|
{
|
|
_suppressTimeout = true;
|
|
UInt8 commandByte;
|
|
|
|
// Disable keyboard and mouse
|
|
writeCommandPort(kCP_DisableKeyboardClock);
|
|
writeCommandPort(kCP_DisableMouseClock);
|
|
// Flush any data
|
|
while ( inb(kCommandPort) & kOutputReady )
|
|
{
|
|
IODelay(kDataDelay);
|
|
inb(kDataPort);
|
|
IODelay(kDataDelay);
|
|
}
|
|
writeCommandPort(kCP_EnableMouseClock);
|
|
writeCommandPort(kCP_EnableKeyboardClock);
|
|
// Read current command
|
|
writeCommandPort(kCP_GetCommandByte);
|
|
commandByte = readDataPort(kDT_Keyboard);
|
|
DEBUG_LOG("%s: initial commandByte = %02x\n", getName(), commandByte);
|
|
// Issue Test Controller to try to reset device
|
|
writeCommandPort(kCP_TestController);
|
|
readDataPort(kDT_Keyboard);
|
|
readDataPort(kDT_Mouse);
|
|
// Issue Test Keyboard Port to try to reset device
|
|
writeCommandPort(kCP_TestKeyboardPort);
|
|
readDataPort(kDT_Keyboard);
|
|
// Issue Test Mouse Port to try to reset device
|
|
writeCommandPort(kCP_TestMousePort);
|
|
readDataPort(kDT_Mouse);
|
|
_suppressTimeout = false;
|
|
|
|
//
|
|
// Initialize the mouse and keyboard hardware to a known state -- the IRQs
|
|
// are disabled (don't want interrupts), the clock line is enabled (want to
|
|
// be able to send commands), and the device itself is disabled (don't want
|
|
// asynchronous data arrival for key/mouse events). We call the read/write
|
|
// port routines directly, since no other thread will conflict with us.
|
|
//
|
|
commandByte &= ~(kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ | kCB_DisableMouseClock | kCB_DisableMouseClock);
|
|
////commandByte |= kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ;
|
|
commandByte |= kCB_TranslateMode;
|
|
writeCommandPort(kCP_SetCommandByte);
|
|
writeDataPort(commandByte);
|
|
DEBUG_LOG("%s: new commandByte = %02x\n", getName(), commandByte);
|
|
|
|
writeDataPort(kDP_SetDefaultsAndDisable);
|
|
readDataPort(kDT_Keyboard); // (discard acknowledge; success irrelevant)
|
|
|
|
writeCommandPort(kCP_TransmitToMouse);
|
|
writeDataPort(kDP_SetDefaultsAndDisable);
|
|
readDataPort(kDT_Mouse); // (discard acknowledge; success irrelevant)
|
|
|
|
//
|
|
// Clear out garbage in the controller's input streams, before starting up
|
|
// the work loop.
|
|
//
|
|
|
|
while ( inb(kCommandPort) & kOutputReady )
|
|
{
|
|
IODelay(kDataDelay);
|
|
inb(kDataPort);
|
|
IODelay(kDataDelay);
|
|
}
|
|
}
|
|
|
|
// -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool ApplePS2Controller::start(IOService * provider)
|
|
{
|
|
DEBUG_LOG("ApplePS2Controller::start entered...\n");
|
|
|
|
//
|
|
// The driver has been instructed to start. Allocate all our resources.
|
|
//
|
|
if (!super::start(provider))
|
|
return false;
|
|
|
|
#if DEBUGGER_SUPPORT
|
|
// Enable special key sequence to enter debugger if debug boot-arg was set.
|
|
int debugFlag = 0;
|
|
#ifdef TIGER
|
|
PE_parse_boot_arg("debug", &debugFlag);
|
|
#else
|
|
PE_parse_boot_argn("debug", &debugFlag, sizeof(debugFlag));
|
|
#endif
|
|
if (debugFlag) _debuggingEnabled = true;
|
|
|
|
_keyboardQueueAlloc = new KeyboardQueueElement[kKeyboardQueueSize];
|
|
if (!_keyboardQueueAlloc) goto fail;
|
|
|
|
// Add the allocated keyboard queue entries to "unused" queue.
|
|
for (int index = 0; index < kKeyboardQueueSize; index++)
|
|
queue_enter(&_keyboardQueueUnused, &_keyboardQueueAlloc[index],
|
|
KeyboardQueueElement *, chain);
|
|
#endif //DEBUGGER_SUPPORT
|
|
// Note: I don't think this newIRQLayout thing is used at all
|
|
// -- our provider is PS2Nub and the PS2 nub we use does not set this flag
|
|
// -- in addition it only supports the LEGACY interrupt specifiers
|
|
// #ifdef to eliminate for now...
|
|
#ifdef NEWIRQ
|
|
if (provider->getProperty("newIRQLayout")) { // turbo
|
|
IOLog("Using new IRQ layout 0,1\n");
|
|
_newIRQLayout = true;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Reset and clean the 8042 keyboard/mouse controller.
|
|
//
|
|
|
|
resetController();
|
|
|
|
//
|
|
// Use a spin lock to protect the client async request queue.
|
|
//
|
|
|
|
_requestQueueLock = IOLockAlloc();
|
|
if (!_requestQueueLock) goto fail;
|
|
_cmdbyteLock = IOLockAlloc();
|
|
if (!_cmdbyteLock) goto fail;
|
|
|
|
//
|
|
// Initialize our work loop, our command gate, and our interrupt event
|
|
// sources. The work loop can accept requests after this step.
|
|
//
|
|
|
|
_workLoop = IOWorkLoop::workLoop();
|
|
#if HANDLE_INTERRUPT_DATA_LATER
|
|
_interruptSourceMouse = IOInterruptEventSource::interruptEventSource( this,
|
|
OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::interruptOccurred));
|
|
_interruptSourceKeyboard = IOInterruptEventSource::interruptEventSource( this,
|
|
OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::interruptOccurred));
|
|
#else
|
|
_interruptSourceMouse = IOInterruptEventSource::interruptEventSource( this,
|
|
OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::packetReadyMouse));
|
|
_interruptSourceKeyboard = IOInterruptEventSource::interruptEventSource( this,
|
|
OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::packetReadyKeyboard));
|
|
#endif
|
|
_interruptSourceQueue = IOInterruptEventSource::interruptEventSource( this,
|
|
OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::processRequestQueue));
|
|
_cmdGate = IOCommandGate::commandGate(this);
|
|
#if WATCHDOG_TIMER
|
|
_watchdogTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &ApplePS2Controller::onWatchdogTimer));
|
|
if (!_watchdogTimer)
|
|
goto fail;
|
|
#endif
|
|
|
|
if ( !_workLoop ||
|
|
!_interruptSourceMouse ||
|
|
!_interruptSourceKeyboard ||
|
|
!_interruptSourceQueue ||
|
|
!_cmdGate) goto fail;
|
|
|
|
if ( _workLoop->addEventSource(_interruptSourceQueue) != kIOReturnSuccess )
|
|
goto fail;
|
|
if ( _workLoop->addEventSource(_cmdGate) != kIOReturnSuccess )
|
|
goto fail;
|
|
|
|
#if WATCHDOG_TIMER
|
|
if ( _workLoop->addEventSource(_watchdogTimer) != kIOReturnSuccess )
|
|
goto fail;
|
|
_watchdogTimer->setTimeoutMS(kWatchdogTimerInterval);
|
|
#endif
|
|
_interruptSourceQueue->enable();
|
|
|
|
//
|
|
// Since there is a calling path from the PS/2 driver stack to power
|
|
// management for activity tickles. We must create a thread callout
|
|
// to handle power state changes from PM to avoid a deadlock.
|
|
//
|
|
|
|
_powerChangeThreadCall = thread_call_allocate(
|
|
(thread_call_func_t) setPowerStateCallout,
|
|
(thread_call_param_t) this );
|
|
if ( !_powerChangeThreadCall )
|
|
goto fail;
|
|
|
|
//
|
|
// Initialize our PM superclass variables and register as the power
|
|
// controlling driver.
|
|
//
|
|
|
|
PMinit();
|
|
|
|
registerPowerDriver( this, (IOPMPowerState *) PS2PowerStateArray,
|
|
kPS2PowerStateCount );
|
|
|
|
//
|
|
// Insert ourselves into the PM tree.
|
|
//
|
|
|
|
provider->joinPMtree(this);
|
|
|
|
//
|
|
// Create the keyboard nub and the mouse nub. The keyboard and mouse drivers
|
|
// will query these nubs to determine the existence of the keyboard or mouse,
|
|
// and should they exist, will attach themselves to the nub as clients.
|
|
//
|
|
|
|
_keyboardDevice = OSTypeAlloc(ApplePS2KeyboardDevice);
|
|
if ( !_keyboardDevice ||
|
|
!_keyboardDevice->init() ||
|
|
!_keyboardDevice->attach(this) )
|
|
{
|
|
OSSafeReleaseNULL(_keyboardDevice);
|
|
OSSafeReleaseNULL(_interruptSourceKeyboard);
|
|
}
|
|
|
|
_mouseDevice = OSTypeAlloc(ApplePS2MouseDevice);
|
|
if ( !_mouseDevice ||
|
|
!_mouseDevice->init() ||
|
|
!_mouseDevice->attach(this) )
|
|
{
|
|
OSSafeReleaseNULL(_mouseDevice);
|
|
OSSafeReleaseNULL(_interruptSourceMouse);
|
|
}
|
|
|
|
if (_keyboardDevice)
|
|
_keyboardDevice->registerService();
|
|
if (_mouseDevice)
|
|
_mouseDevice->registerService();
|
|
|
|
registerService();
|
|
|
|
DEBUG_LOG("ApplePS2Controller::start leaving.\n");
|
|
return true; // success
|
|
|
|
fail:
|
|
stop(provider);
|
|
|
|
DEBUG_LOG("ApplePS2Controller::start leaving(fail).\n");
|
|
return false;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::stop(IOService * provider)
|
|
{
|
|
//
|
|
// The driver has been instructed to stop. Note that we must break all
|
|
// connections to other service objects now (ie. no registered actions,
|
|
// no pointers and retains to objects, etc), if any.
|
|
//
|
|
|
|
// Ensure that the interrupt handlers have been uninstalled (ie. no clients).
|
|
assert(!_interruptInstalledKeyboard);
|
|
assert(!_interruptInstalledMouse);
|
|
assert(!_powerControlInstalledKeyboard);
|
|
assert(!_powerControlInstalledMouse);
|
|
|
|
// Free the nubs we created.
|
|
OSSafeReleaseNULL(_keyboardDevice);
|
|
OSSafeReleaseNULL(_mouseDevice);
|
|
|
|
// Free the event/interrupt sources.
|
|
OSSafeReleaseNULL(_interruptSourceKeyboard);
|
|
OSSafeReleaseNULL(_interruptSourceMouse);
|
|
OSSafeReleaseNULL(_interruptSourceQueue);
|
|
OSSafeReleaseNULL(_cmdGate);
|
|
#if WATCHDOG_TIMER
|
|
OSSafeReleaseNULL(_watchdogTimer);
|
|
#endif
|
|
|
|
// Free the work loop.
|
|
OSSafeReleaseNULL(_workLoop);
|
|
|
|
// Free the request queue lock and empty out the request queue.
|
|
if (_requestQueueLock)
|
|
{
|
|
_hardwareOffline = true;
|
|
processRequestQueue(0, 0);
|
|
IOLockFree(_requestQueueLock);
|
|
_requestQueueLock = 0;
|
|
}
|
|
if (_cmdbyteLock)
|
|
{
|
|
IOLockFree(_cmdbyteLock);
|
|
_cmdbyteLock = 0;
|
|
}
|
|
|
|
// Free the power management thread call.
|
|
if (_powerChangeThreadCall)
|
|
{
|
|
thread_call_free(_powerChangeThreadCall);
|
|
_powerChangeThreadCall = 0;
|
|
}
|
|
|
|
// Detach from power management plane.
|
|
PMstop();
|
|
|
|
#if DEBUGGER_SUPPORT
|
|
// Free the keyboard queue allocation space (after disabling interrupt).
|
|
if (_keyboardQueueAlloc)
|
|
{
|
|
delete[] _keyboardQueueAlloc;
|
|
_keyboardQueueAlloc = 0;
|
|
}
|
|
#endif //DEBUGGER_SUPPORT
|
|
|
|
super::stop(provider);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
IOWorkLoop * ApplePS2Controller::getWorkLoop() const
|
|
{
|
|
return _workLoop;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::installInterruptAction(PS2DeviceType deviceType,
|
|
OSObject * target,
|
|
PS2InterruptAction interruptAction,
|
|
PS2PacketAction packetAction)
|
|
{
|
|
//
|
|
// Install the keyboard or mouse interrupt handler.
|
|
//
|
|
// This method assumes only one possible mouse and only one possible
|
|
// keyboard client (ie. callers), and assumes two distinct interrupt
|
|
// handlers for each, hence needs no protection against races.
|
|
//
|
|
|
|
// Is it the keyboard or the mouse interrupt handler that was requested?
|
|
// We only install it if it is currently uninstalled.
|
|
|
|
if (deviceType == kDT_Keyboard && !_interruptInstalledKeyboard && _interruptSourceKeyboard)
|
|
{
|
|
target->retain();
|
|
_interruptTargetKeyboard = target;
|
|
_interruptActionKeyboard = interruptAction;
|
|
_packetActionKeyboard = packetAction;
|
|
_workLoop->addEventSource(_interruptSourceKeyboard);
|
|
DEBUG_LOG("%s: setCommandByte for keyboard interrupt install\n", getName());
|
|
setCommandByte(kCB_EnableKeyboardIRQ, 0);
|
|
#ifdef NEWIRQ
|
|
if (_newIRQLayout)
|
|
{ // turbo
|
|
getProvider()->registerInterrupt(0,0, interruptHandlerKeyboard, this);
|
|
getProvider()->enableInterrupt(0);
|
|
} else
|
|
#endif
|
|
{
|
|
getProvider()->registerInterrupt(kIRQ_Keyboard,0, interruptHandlerKeyboard, this);
|
|
getProvider()->enableInterrupt(kIRQ_Keyboard);
|
|
}
|
|
|
|
_interruptInstalledKeyboard = true;
|
|
}
|
|
else if (deviceType == kDT_Mouse && !_interruptInstalledMouse && _interruptSourceMouse)
|
|
{
|
|
target->retain();
|
|
_interruptTargetMouse = target;
|
|
_interruptActionMouse = interruptAction;
|
|
_packetActionMouse = packetAction;
|
|
_workLoop->addEventSource(_interruptSourceMouse);
|
|
DEBUG_LOG("%s: setCommandByte for mouse interrupt install\n", getName());
|
|
setCommandByte(kCB_EnableMouseIRQ, 0);
|
|
#ifdef NEWIRQ
|
|
if (_newIRQLayout)
|
|
{ // turbo
|
|
getProvider()->registerInterrupt(1, 0, interruptHandlerMouse, this);
|
|
getProvider()->enableInterrupt(1);
|
|
} else
|
|
#endif
|
|
{
|
|
getProvider()->registerInterrupt(kIRQ_Mouse, 0, interruptHandlerMouse, this);
|
|
getProvider()->enableInterrupt(kIRQ_Mouse);
|
|
}
|
|
|
|
_interruptInstalledMouse = true;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::uninstallInterruptAction(PS2DeviceType deviceType)
|
|
{
|
|
//
|
|
// Uninstall the keyboard or mouse interrupt handler.
|
|
//
|
|
// This method assumes only one possible mouse and only one possible
|
|
// keyboard client (ie. callers), and assumes two distinct interrupt
|
|
// handlers for each, hence needs no protection against races.
|
|
//
|
|
|
|
// Is it the keyboard or the mouse interrupt handler that was requested?
|
|
// We only install it if it is currently uninstalled.
|
|
|
|
if (deviceType == kDT_Keyboard && _interruptInstalledKeyboard)
|
|
{
|
|
setCommandByte(0, kCB_EnableKeyboardIRQ);
|
|
#ifdef NEWIRQ
|
|
getProvider()->disableInterrupt(0);
|
|
getProvider()->unregisterInterrupt(0);
|
|
#else
|
|
getProvider()->disableInterrupt(kIRQ_Keyboard);
|
|
getProvider()->unregisterInterrupt(kIRQ_Keyboard);
|
|
#endif
|
|
_workLoop->removeEventSource(_interruptSourceKeyboard);
|
|
_interruptInstalledKeyboard = false;
|
|
_interruptActionKeyboard = NULL;
|
|
_packetActionKeyboard = NULL;
|
|
_interruptTargetKeyboard->release();
|
|
_interruptTargetKeyboard = 0;
|
|
}
|
|
|
|
else if (deviceType == kDT_Mouse && _interruptInstalledMouse)
|
|
{
|
|
setCommandByte(0, kCB_EnableMouseIRQ);
|
|
#ifdef NEWIRQ
|
|
getProvider()->disableInterrupt(1);
|
|
getProvider()->unregisterInterrupt(1);
|
|
#else
|
|
getProvider()->disableInterrupt(kIRQ_Mouse);
|
|
getProvider()->unregisterInterrupt(kIRQ_Mouse);
|
|
#endif
|
|
_workLoop->removeEventSource(_interruptSourceMouse);
|
|
_interruptInstalledMouse = false;
|
|
_interruptActionMouse = NULL;
|
|
_packetActionMouse = NULL;
|
|
_interruptTargetMouse->release();
|
|
_interruptTargetMouse = 0;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
PS2Request * ApplePS2Controller::allocateRequest(int max)
|
|
{
|
|
//
|
|
// Allocate a request structure. Blocks until successful.
|
|
// Most of request structure is guaranteed to be zeroed.
|
|
//
|
|
|
|
assert(max > 0);
|
|
|
|
return new(max) PS2Request;
|
|
}
|
|
|
|
EXPORT PS2Request::PS2Request()
|
|
{
|
|
commandsCount = 0;
|
|
completionTarget = 0;
|
|
completionAction = 0;
|
|
completionParam = 0;
|
|
|
|
#ifdef DEBUG
|
|
// These items do not need to be initialized, but it might make it easier to
|
|
// debug if they start at zero.
|
|
chain.prev = 0;
|
|
chain.next = 0;
|
|
#endif
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::freeRequest(PS2Request * request)
|
|
{
|
|
//
|
|
// Deallocate a request structure.
|
|
//
|
|
|
|
delete request;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
UInt8 ApplePS2Controller::setCommandByte(UInt8 setBits, UInt8 clearBits)
|
|
{
|
|
TPS2Request<1> request;
|
|
request.commands[0].command = kPS2C_ModifyCommandByte;
|
|
request.commands[0].setBits = setBits;
|
|
request.commands[0].clearBits = clearBits;
|
|
request.commandsCount = 1;
|
|
assert(request.commandsCount <= countof(request.commands));
|
|
_cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Controller::setCommandByteGated), &request);
|
|
return request.commands[0].oldBits;
|
|
}
|
|
|
|
void ApplePS2Controller::setCommandByteGated(PS2Request* request)
|
|
{
|
|
UInt8 setBits = request->commands[0].setBits;
|
|
UInt8 clearBits = request->commands[0].clearBits;
|
|
++_ignoreInterrupts;
|
|
writeCommandPort(kCP_GetCommandByte);
|
|
UInt8 oldCommandByte = readDataPort(kDT_Keyboard);
|
|
--_ignoreInterrupts;
|
|
DEBUG_LOG("%s: oldCommandByte = %02x\n", getName(), oldCommandByte);
|
|
UInt8 newCommandByte = (oldCommandByte | setBits) & ~clearBits;
|
|
if (oldCommandByte != newCommandByte)
|
|
{
|
|
DEBUG_LOG("%s: newCommandByte = %02x\n", getName(), newCommandByte);
|
|
writeCommandPort(kCP_SetCommandByte);
|
|
writeDataPort(newCommandByte);
|
|
}
|
|
request->commands[0].oldBits = oldCommandByte;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool ApplePS2Controller::submitRequest(PS2Request * request)
|
|
{
|
|
//
|
|
// Submit the request to the controller for processing, asynchronously.
|
|
//
|
|
|
|
IOLockLock(_requestQueueLock);
|
|
queue_enter(&_requestQueue, request, PS2Request *, chain);
|
|
IOLockUnlock(_requestQueueLock);
|
|
|
|
_interruptSourceQueue->interruptOccurred(0, 0, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::submitRequestAndBlock(PS2Request * request)
|
|
{
|
|
_cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Controller::submitRequestAndBlockGated), request);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::submitRequestAndBlockGated(PS2Request* request)
|
|
{
|
|
processRequestQueue(0, 0);
|
|
processRequest(request);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
#if HANDLE_INTERRUPT_DATA_LATER
|
|
void ApplePS2Controller::interruptOccurred(IOInterruptEventSource* source, int)
|
|
{ // IOInterruptEventAction
|
|
//
|
|
// Our work loop has informed us of an interrupt, that is, asynchronous
|
|
// data has arrived on our input stream. Read the data and dispatch it
|
|
// to the appropriate driver.
|
|
//
|
|
// This method should only be called from our single-threaded work loop.
|
|
//
|
|
|
|
if (_hardwareOffline)
|
|
{
|
|
// Toss any asynchronous data received. The interrupt event source may
|
|
// have been signalled before the PS/2 port was offline.
|
|
return;
|
|
}
|
|
|
|
#if DEBUGGER_SUPPORT
|
|
UInt8 status;
|
|
int state;
|
|
lockController(&state); // (lock out interrupt + access to queue)
|
|
while (1)
|
|
{
|
|
// See if data is available on the keyboard input stream (off queue);
|
|
// we do not read keyboard data from the real data port if it should
|
|
// be available.
|
|
|
|
if (dequeueKeyboardData(&status))
|
|
{
|
|
unlockController(state);
|
|
dispatchDriverInterrupt(kDT_Keyboard, status);
|
|
lockController(&state);
|
|
}
|
|
|
|
// See if data is available on the mouse input stream (off real port).
|
|
|
|
else if ( (inb(kCommandPort) & (kOutputReady | kMouseData)) ==
|
|
(kOutputReady | kMouseData))
|
|
{
|
|
unlockController(state);
|
|
IODelay(kDataDelay);
|
|
dispatchDriverInterrupt(kDT_Mouse, inb(kDataPort));
|
|
lockController(&state);
|
|
}
|
|
else break; // out of loop
|
|
}
|
|
unlockController(state); // (release interrupt lockout + access to queue)
|
|
#else
|
|
handleInterrupt(source == _interruptSourceKeyboard ? kDT_Keyboard : kDT_Mouse);
|
|
#endif // DEBUGGER_SUPPORT
|
|
}
|
|
#endif // HANDLE_INTERRUPT_DATA_LATER
|
|
|
|
#if !HANDLE_INTERRUPT_DATA_LATER
|
|
void ApplePS2Controller::packetReadyKeyboard(IOInterruptEventSource *, int)
|
|
{
|
|
// a complete packet has arrived for the keyboard and has signaled the workloop
|
|
// -- dispatch it to the installed keyboard packet handler
|
|
if (_interruptInstalledKeyboard)
|
|
(*_packetActionKeyboard)(_interruptTargetKeyboard);
|
|
}
|
|
|
|
void ApplePS2Controller::packetReadyMouse(IOInterruptEventSource *, int)
|
|
{
|
|
// a complete packet has arrived for the mouse and has signaled the workloop
|
|
// -- dispatch it to the installed mouse packet handler
|
|
if (_interruptInstalledMouse)
|
|
(*_packetActionMouse)(_interruptTargetMouse);
|
|
}
|
|
#endif // !HANDLE_INTERRUPT_DATA_LATER
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
PS2InterruptResult ApplePS2Controller::_dispatchDriverInterrupt(PS2DeviceType deviceType, UInt8 data)
|
|
{
|
|
PS2InterruptResult result = kPS2IR_packetBuffering;
|
|
if (kDT_Mouse == deviceType && _interruptInstalledMouse)
|
|
{
|
|
// Dispatch the data to the mouse driver.
|
|
result = (*_interruptActionMouse)(_interruptTargetMouse, data);
|
|
}
|
|
else if (kDT_Keyboard == deviceType && _interruptInstalledKeyboard)
|
|
{
|
|
// Dispatch the data to the keyboard driver.
|
|
result = (*_interruptActionKeyboard)(_interruptTargetKeyboard, data);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ApplePS2Controller::dispatchDriverInterrupt(PS2DeviceType deviceType, UInt8 data)
|
|
{
|
|
PS2InterruptResult result = _dispatchDriverInterrupt(deviceType, data);
|
|
if (kPS2IR_packetReady == result)
|
|
{
|
|
#if HANDLE_INTERRUPT_DATA_LATER
|
|
if (kDT_Mouse == deviceType)
|
|
(*_packetActionMouse)(_interruptTargetMouse);
|
|
else if (kDT_Keyboard == deviceType)
|
|
(*_packetActionKeyboard)(_interruptTargetKeyboard);
|
|
#else
|
|
if (kDT_Mouse == deviceType)
|
|
_interruptSourceMouse->interruptOccurred(0, 0, 0);
|
|
else if (kDT_Keyboard == deviceType)
|
|
_interruptSourceKeyboard->interruptOccurred(0, 0, 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::processRequest(PS2Request * request)
|
|
{
|
|
//
|
|
// Our work loop has informed us of a request submission. Process
|
|
// the request. Note that this code "figures out" when the mouse
|
|
// input stream should be read over the keyboard input stream.
|
|
//
|
|
// This method should only be called from our single-threaded work loop.
|
|
//
|
|
|
|
UInt8 byte;
|
|
PS2DeviceType deviceMode = kDT_Keyboard;
|
|
bool failed = false;
|
|
bool transmitToMouse = false;
|
|
unsigned index;
|
|
|
|
if (_hardwareOffline)
|
|
{
|
|
failed = true;
|
|
index = 0;
|
|
goto hardware_offline;
|
|
}
|
|
|
|
// Don't handle interrupts during this process. We want to read the
|
|
// data by polling for it here.
|
|
|
|
++_ignoreInterrupts;
|
|
|
|
// Process each of the commands in the list.
|
|
|
|
for (index = 0; index < request->commandsCount; index++)
|
|
{
|
|
switch (request->commands[index].command)
|
|
{
|
|
case kPS2C_ReadDataPort:
|
|
request->commands[index].inOrOut = readDataPort(deviceMode);
|
|
break;
|
|
|
|
case kPS2C_ReadDataPortAndCompare:
|
|
#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE
|
|
byte = readDataPort(deviceMode, request->commands[index].inOrOut);
|
|
#else
|
|
byte = readDataPort(deviceMode);
|
|
#endif
|
|
failed = (byte != request->commands[index].inOrOut);
|
|
request->commands[index].inOrOut = byte;
|
|
break;
|
|
|
|
case kPS2C_WriteDataPort:
|
|
writeDataPort(request->commands[index].inOrOut);
|
|
if (transmitToMouse) // next reads from mouse input stream
|
|
{
|
|
deviceMode = kDT_Mouse;
|
|
transmitToMouse = false;
|
|
}
|
|
else
|
|
{
|
|
deviceMode = kDT_Keyboard;
|
|
}
|
|
break;
|
|
|
|
case kPS2C_WriteCommandPort:
|
|
writeCommandPort(request->commands[index].inOrOut);
|
|
if (request->commands[index].inOrOut == kCP_TransmitToMouse)
|
|
transmitToMouse = true; // preparing to transmit data to mouse
|
|
break;
|
|
|
|
//
|
|
// Send a composite mouse command that is equivalent to the following
|
|
// (frequently used) command sequence:
|
|
//
|
|
// 1. kPS2C_WriteCommandPort( kCP_TransmitToMouse )
|
|
// 2. kPS2C_WriteDataPort( command )
|
|
// 3. kPS2C_ReadDataPortAndCompare( kSC_Acknowledge )
|
|
//
|
|
|
|
case kPS2C_SendMouseCommandAndCompareAck:
|
|
writeCommandPort(kCP_TransmitToMouse);
|
|
writeDataPort(request->commands[index].inOrOut);
|
|
deviceMode = kDT_Mouse;
|
|
#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE
|
|
byte = readDataPort(kDT_Mouse, kSC_Acknowledge);
|
|
#else
|
|
byte = readDataPort(kDT_Mouse);
|
|
#endif
|
|
failed = (byte != kSC_Acknowledge);
|
|
break;
|
|
|
|
case kPS2C_ReadMouseDataPort:
|
|
deviceMode= kDT_Mouse;
|
|
request->commands[index].inOrOut = readDataPort(deviceMode);
|
|
break;
|
|
|
|
case kPS2C_ReadMouseDataPortAndCompare:
|
|
deviceMode= kDT_Mouse;
|
|
#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE
|
|
byte = readDataPort(deviceMode, request->commands[index].inOrOut);
|
|
#else
|
|
byte = readDataPort(deviceMode);
|
|
#endif
|
|
failed = (byte != request->commands[index].inOrOut);
|
|
break;
|
|
|
|
case kPS2C_FlushDataPort:
|
|
request->commands[index].inOrOut32 = 0;
|
|
while ( inb(kCommandPort) & kOutputReady )
|
|
{
|
|
++request->commands[index].inOrOut32;
|
|
IODelay(kDataDelay);
|
|
inb(kDataPort);
|
|
IODelay(kDataDelay);
|
|
}
|
|
break;
|
|
|
|
case kPS2C_SleepMS:
|
|
IOSleep(request->commands[index].inOrOut32);
|
|
break;
|
|
|
|
case kPS2C_ModifyCommandByte:
|
|
writeCommandPort(kCP_GetCommandByte);
|
|
UInt8 commandByte = readDataPort(kDT_Keyboard);
|
|
writeCommandPort(kCP_SetCommandByte);
|
|
writeDataPort((commandByte | request->commands[index].setBits) & ~request->commands[index].clearBits);
|
|
request->commands[index].oldBits = commandByte;
|
|
break;
|
|
}
|
|
|
|
if (failed) break;
|
|
}
|
|
|
|
// Now it is ok to process interrupts normally.
|
|
|
|
--_ignoreInterrupts;
|
|
|
|
hardware_offline:
|
|
|
|
// If a command failed and stopped the request processing, store its
|
|
// index into the commandsCount field.
|
|
|
|
if (failed) request->commandsCount = index;
|
|
|
|
// Invoke the completion routine, if one was supplied.
|
|
|
|
if (request->completionTarget && request->completionAction)
|
|
{
|
|
(*request->completionAction)(request->completionTarget,
|
|
request->completionParam);
|
|
}
|
|
else
|
|
{
|
|
freeRequest(request);
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::processRequestQueue(IOInterruptEventSource *, int)
|
|
{
|
|
queue_head_t localQueue;
|
|
|
|
// Transfer queued (async) requests to a local queue.
|
|
|
|
IOLockLock(_requestQueueLock);
|
|
|
|
if (!queue_empty(&_requestQueue))
|
|
{
|
|
queue_assign(&localQueue, &_requestQueue, PS2Request *, chain);
|
|
queue_init(&_requestQueue);
|
|
}
|
|
else queue_init(&localQueue);
|
|
|
|
IOLockUnlock(_requestQueueLock);
|
|
|
|
// Process each request in order.
|
|
|
|
while (!queue_empty(&localQueue))
|
|
{
|
|
PS2Request * request;
|
|
queue_remove_first(&localQueue, request, PS2Request *, chain);
|
|
processRequest(request);
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType)
|
|
{
|
|
//
|
|
// Blocks until keyboard or mouse data is available from the controller
|
|
// and returns that data. Note, if mouse data is requested but keyboard
|
|
// data is what is available, the data is delivered to the appropriate
|
|
// driver interrupt routine immediately (effectively, the request is
|
|
// "preempted" temporarily).
|
|
//
|
|
// There is a built-in timeout for this command of (timeoutCounter X
|
|
// kDataDelay) microseconds, approximately.
|
|
//
|
|
// This method should only be called from our single-threaded work loop.
|
|
//
|
|
|
|
UInt8 readByte;
|
|
UInt8 status;
|
|
UInt32 timeoutCounter = 10000; // (timeoutCounter * kDataDelay = 70 ms)
|
|
|
|
while (1)
|
|
{
|
|
#if DEBUGGER_SUPPORT
|
|
int state;
|
|
lockController(&state); // (lock out interrupt + access to queue)
|
|
if (deviceType == kDT_Keyboard && dequeueKeyboardData(&readByte))
|
|
{
|
|
unlockController(state);
|
|
return readByte;
|
|
}
|
|
#endif //DEBUGGER_SUPPORT
|
|
|
|
//
|
|
// Wait for the controller's output buffer to become ready.
|
|
//
|
|
|
|
while (timeoutCounter && !((status = inb(kCommandPort)) & kOutputReady))
|
|
{
|
|
timeoutCounter--;
|
|
IODelay(kDataDelay);
|
|
}
|
|
|
|
//
|
|
// If we timed out, something went awfully wrong; return a fake value.
|
|
//
|
|
|
|
if (timeoutCounter == 0)
|
|
{
|
|
#if DEBUGGER_SUPPORT
|
|
unlockController(state); // (release interrupt lockout + access to queue)
|
|
#endif //DEBUGGER_SUPPORT
|
|
|
|
if (!_suppressTimeout)
|
|
IOLog("%s: Timed out on %s input stream.\n", getName(),
|
|
(deviceType == kDT_Keyboard) ? "keyboard" : "mouse");
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// For older machines, it is necessary to wait a while after the controller
|
|
// has asserted the output buffer bit before reading the data port. No more
|
|
// data will be available if this wait is not performed.
|
|
//
|
|
|
|
IODelay(kDataDelay);
|
|
|
|
//
|
|
// Read in the data. We return the data, however, only if it arrived on
|
|
// the requested input stream.
|
|
//
|
|
|
|
readByte = inb(kDataPort);
|
|
|
|
#if DEBUGGER_SUPPORT
|
|
unlockController(state); // (release interrupt lockout + access to queue)
|
|
#endif //DEBUGGER_SUPPORT
|
|
|
|
if (_suppressTimeout) // startup mode w/o interrupts
|
|
return readByte;
|
|
|
|
if ( (status & kMouseData) )
|
|
{
|
|
if (deviceType == kDT_Mouse) return readByte;
|
|
}
|
|
else
|
|
{
|
|
if (deviceType == kDT_Keyboard) return readByte;
|
|
}
|
|
|
|
//
|
|
// The data we just received is for the other input stream, not the one
|
|
// that was requested, so dispatch other device's interrupt handler.
|
|
//
|
|
|
|
dispatchDriverInterrupt((deviceType==kDT_Keyboard)?kDT_Mouse:kDT_Keyboard,
|
|
readByte);
|
|
} // while (forever)
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE
|
|
|
|
UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType,
|
|
UInt8 expectedByte)
|
|
{
|
|
//
|
|
// Blocks until keyboard or mouse data is available from the controller
|
|
// and returns that data. Note, if mouse data is requested but keyboard
|
|
// data is what is available, the data is delivered to the appropriate
|
|
// driver interrupt routine immediately (effectively, the request is
|
|
// "preempted" temporarily).
|
|
//
|
|
// There is a built-in timeout for this command of (timeoutCounter X
|
|
// kDataDelay) microseconds, approximately.
|
|
//
|
|
// This method should only be called from our single-threaded work loop.
|
|
//
|
|
// This version of readDataPort does exactly the same as the original,
|
|
// except that if the value that should be read from the (appropriate)
|
|
// input stream is not what is expected, we make these assumptions:
|
|
//
|
|
// (a) the data byte we did get was "asynchronous" data being sent by
|
|
// the device, which has not figured out that it has to respond to
|
|
// the command we just sent to it.
|
|
// (b) that the real "expected" response will be the next byte in the
|
|
// stream; so what we do is put aside the first byte we read and
|
|
// wait for the next byte; if it's the expected value, we dispatch
|
|
// the first byte we read to the driver's interrupt handler, then
|
|
// return the expected byte. The caller will have never known that
|
|
// asynchronous data arrived at a very bad time.
|
|
// (c) that the real "expected" response will arrive within (kDataDelay
|
|
// X timeoutCounter) microseconds from the time the call is made.
|
|
//
|
|
|
|
UInt8 firstByte = 0;
|
|
bool firstByteHeld = false;
|
|
UInt8 readByte;
|
|
bool requestedStream;
|
|
UInt8 status;
|
|
UInt32 timeoutCounter = 10000; // (timeoutCounter * kDataDelay = 70 ms)
|
|
|
|
while (1)
|
|
{
|
|
#if DEBUGGER_SUPPORT
|
|
int state;
|
|
lockController(&state); // (lock out interrupt + access to queue)
|
|
if (deviceType == kDT_Keyboard && dequeueKeyboardData(&readByte))
|
|
{
|
|
requestedStream = true;
|
|
goto skipForwardToY;
|
|
}
|
|
#endif //DEBUGGER_SUPPORT
|
|
|
|
//
|
|
// Wait for the controller's output buffer to become ready.
|
|
//
|
|
|
|
while (timeoutCounter && !((status = inb(kCommandPort)) & kOutputReady))
|
|
{
|
|
timeoutCounter--;
|
|
IODelay(kDataDelay);
|
|
}
|
|
|
|
//
|
|
// If we timed out, we return the first byte we read, unless THIS IS the
|
|
// first byte we are trying to read, then something went awfully wrong
|
|
// and we return a fake value rather than lock up the controller longer.
|
|
//
|
|
|
|
if (timeoutCounter == 0)
|
|
{
|
|
#if DEBUGGER_SUPPORT
|
|
unlockController(state); // (release interrupt lockout + access to queue)
|
|
#endif //DEBUGGER_SUPPORT
|
|
|
|
if (firstByteHeld) return firstByte;
|
|
|
|
IOLog("%s: Timed out on %s input stream.\n", getName(),
|
|
(deviceType == kDT_Keyboard) ? "keyboard" : "mouse");
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// For older machines, it is necessary to wait a while after the controller
|
|
// has asserted the output buffer bit before reading the data port. No more
|
|
// data will be available if this wait is not performed.
|
|
//
|
|
|
|
IODelay(kDataDelay);
|
|
|
|
//
|
|
// Read in the data. We process the data, however, only if it arrived on
|
|
// the requested input stream.
|
|
//
|
|
|
|
readByte = inb(kDataPort);
|
|
requestedStream = false;
|
|
|
|
if ( (status & kMouseData) )
|
|
{
|
|
if (deviceType == kDT_Mouse) requestedStream = true;
|
|
}
|
|
else
|
|
{
|
|
if (deviceType == kDT_Keyboard) requestedStream = true;
|
|
}
|
|
|
|
#if DEBUGGER_SUPPORT
|
|
skipForwardToY:
|
|
unlockController(state); // (release interrupt lockout + access to queue)
|
|
#endif //DEBUGGER_SUPPORT
|
|
|
|
if (requestedStream)
|
|
{
|
|
if (readByte == expectedByte)
|
|
{
|
|
if (firstByteHeld == false)
|
|
{
|
|
//
|
|
// Normal case. Return first byte received.
|
|
//
|
|
|
|
return readByte;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Our assumption was correct. The second byte matched. Dispatch
|
|
// the first byte to the interrupt handler, and return the second.
|
|
//
|
|
|
|
if (!_ignoreOutOfOrder)
|
|
dispatchDriverInterrupt(deviceType, firstByte);
|
|
return readByte;
|
|
}
|
|
}
|
|
else // (readByte does not match expectedByte)
|
|
{
|
|
if (firstByteHeld == false)
|
|
{
|
|
//
|
|
// The first byte was received, and does not match the byte we are
|
|
// expecting. Put it aside for the moment.
|
|
//
|
|
|
|
firstByteHeld = true;
|
|
firstByte = readByte;
|
|
}
|
|
else if (readByte != expectedByte)
|
|
{
|
|
//
|
|
// The second byte mismatched as well. I have yet to see this case
|
|
// occur [Dan], however I do think it's plausible. No error logged.
|
|
//
|
|
|
|
if (!_ignoreOutOfOrder)
|
|
dispatchDriverInterrupt(deviceType, readByte);
|
|
return firstByte;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The data we just received is for the other input stream, not ours,
|
|
// so dispatch appropriate interrupt handler.
|
|
//
|
|
|
|
if (!_ignoreOutOfOrder)
|
|
dispatchDriverInterrupt(deviceType == kDT_Keyboard ? kDT_Mouse : kDT_Keyboard, readByte);
|
|
}
|
|
} // while (forever)
|
|
}
|
|
|
|
#endif
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::writeDataPort(UInt8 byte)
|
|
{
|
|
//
|
|
// Block until room in the controller's input buffer is available, then
|
|
// write the given byte to the Data Port.
|
|
//
|
|
// This method should only be dispatched from our single-threaded work loop.
|
|
//
|
|
|
|
while (inb(kCommandPort) & kInputBusy)
|
|
IODelay(kDataDelay);
|
|
IODelay(kDataDelay);
|
|
outb(kDataPort, byte);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::writeCommandPort(UInt8 byte)
|
|
{
|
|
//
|
|
// Block until room in the controller's input buffer is available, then
|
|
// write the given byte to the Command Port.
|
|
//
|
|
// This method should only be dispatched from our single-threaded work loop.
|
|
//
|
|
|
|
while (inb(kCommandPort) & kInputBusy)
|
|
IODelay(kDataDelay);
|
|
IODelay(kDataDelay);
|
|
outb(kCommandPort, byte);
|
|
}
|
|
|
|
// =============================================================================
|
|
// Escape-Key Processing Stuff Localized Here (eg. Mini-Monitor)
|
|
//
|
|
|
|
#if DEBUGGER_SUPPORT
|
|
|
|
#define kModifierShiftLeft 0x01
|
|
#define kModifierShiftRight 0x02
|
|
#define kModifierCtrlLeft 0x04
|
|
#define kModifierCtrlRight 0x08
|
|
#define kModifierAltLeft 0x10
|
|
#define kModifierAltRight 0x20
|
|
#define kModifierWindowsLeft 0x40
|
|
#define kModifierWindowsRight 0x80
|
|
|
|
#define kModifierShiftMask (kModifierShiftLeft | kModifierShiftRight )
|
|
#define kModifierCtrlMask (kModifierCtrlLeft | kModifierCtrlRight )
|
|
#define kModifierAltMask (kModifierAltLeft | kModifierAltRight )
|
|
#define kModifierWindowsMask (kModifierWindowsLeft | kModifierWindowsRight)
|
|
|
|
bool ApplePS2Controller::doEscape(UInt8 scancode)
|
|
{
|
|
static struct
|
|
{
|
|
UInt8 scancode;
|
|
UInt8 extended;
|
|
UInt16 modifier;
|
|
} modifierTable[] = { { kSC_Alt, false, kModifierAltLeft },
|
|
{ kSC_Alt, true, kModifierAltRight },
|
|
{ kSC_Ctrl, false, kModifierCtrlLeft },
|
|
{ kSC_Ctrl, true, kModifierCtrlRight },
|
|
{ kSC_ShiftLeft, false, kModifierShiftLeft },
|
|
{ kSC_ShiftRight, false, kModifierShiftRight },
|
|
{ kSC_WindowsLeft, true, kModifierWindowsLeft },
|
|
{ kSC_WindowsRight, true, kModifierWindowsRight },
|
|
{ 0, 0, 0 } };
|
|
|
|
UInt32 index;
|
|
bool releaseModifiers = false;
|
|
bool upBit = (scancode & kSC_UpBit) ? true : false;
|
|
|
|
//
|
|
// See if this is an extened scancode sequence.
|
|
//
|
|
|
|
if (scancode == kSC_Extend)
|
|
{
|
|
_extendedState = true;
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Update the modifier state, if applicable.
|
|
//
|
|
|
|
scancode &= ~kSC_UpBit;
|
|
|
|
for (index = 0; modifierTable[index].scancode; index++)
|
|
{
|
|
if ( modifierTable[index].scancode == scancode &&
|
|
modifierTable[index].extended == _extendedState )
|
|
{
|
|
if (upBit) _modifierState &= ~modifierTable[index].modifier;
|
|
else _modifierState |= modifierTable[index].modifier;
|
|
|
|
_extendedState = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call the debugger function, if applicable.
|
|
//
|
|
|
|
if (scancode == kSC_Delete) // (both extended and non-extended scancodes)
|
|
{
|
|
if ( _modifierState == (kModifierAltLeft | kModifierAltRight) )
|
|
{
|
|
// Disable the mouse by forcing the clock line low.
|
|
|
|
while (inb(kCommandPort) & kInputBusy)
|
|
IODelay(kDataDelay);
|
|
IODelay(kDataDelay);
|
|
outb(kCommandPort, kCP_DisableMouseClock);
|
|
|
|
// Call the debugger function.
|
|
|
|
Debugger("Programmer Key");
|
|
|
|
// Re-enable the mouse by making the clock line active.
|
|
|
|
while (inb(kCommandPort) & kInputBusy)
|
|
IODelay(kDataDelay);
|
|
IODelay(kDataDelay);
|
|
outb(kCommandPort, kCP_EnableMouseClock);
|
|
|
|
releaseModifiers = true;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release all the modifier keys that were down before the debugger
|
|
// function was called (assumption is that they are no longer held
|
|
// down after the debugger function returns).
|
|
//
|
|
|
|
if (releaseModifiers)
|
|
{
|
|
for (index = 0; modifierTable[index].scancode; index++)
|
|
{
|
|
if ( _modifierState & modifierTable[index].modifier )
|
|
{
|
|
if (modifierTable[index].extended) enqueueKeyboardData(kSC_Extend);
|
|
enqueueKeyboardData(modifierTable[index].scancode | kSC_UpBit);
|
|
}
|
|
}
|
|
_modifierState = 0x00;
|
|
}
|
|
|
|
//
|
|
// Update all other state and return status.
|
|
//
|
|
|
|
_extendedState = false;
|
|
return (releaseModifiers);
|
|
}
|
|
|
|
void ApplePS2Controller::enqueueKeyboardData(UInt8 key)
|
|
{
|
|
//
|
|
// Enqueue the supplied keyboard data onto our internal queues. The
|
|
// controller must already be locked.
|
|
//
|
|
|
|
KeyboardQueueElement * element;
|
|
|
|
// Obtain an unused keyboard data element.
|
|
if (!queue_empty(&_keyboardQueueUnused))
|
|
{
|
|
queue_remove_first(&_keyboardQueueUnused,
|
|
element, KeyboardQueueElement *, chain);
|
|
|
|
// Store the new keyboard data element on the queue.
|
|
element->data = key;
|
|
queue_enter(&_keyboardQueue, element, KeyboardQueueElement *, chain);
|
|
}
|
|
}
|
|
|
|
bool ApplePS2Controller::dequeueKeyboardData(UInt8 * key)
|
|
{
|
|
//
|
|
// Dequeue keyboard data from our internal queues, if the queue is not
|
|
// empty. Should the queue be empty, false is returned. The controller
|
|
// must already be locked.
|
|
//
|
|
|
|
KeyboardQueueElement * element;
|
|
|
|
// Obtain an unused keyboard data element.
|
|
if (!queue_empty(&_keyboardQueue))
|
|
{
|
|
queue_remove_first(&_keyboardQueue, element, KeyboardQueueElement *, chain);
|
|
*key = element->data;
|
|
|
|
// Place the unused keyboard data element onto the unused queue.
|
|
queue_enter(&_keyboardQueueUnused, element, KeyboardQueueElement *, chain);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ApplePS2Controller::unlockController(int state)
|
|
{
|
|
IOSimpleLockUnlockEnableInterrupt(_controllerLock, state);
|
|
}
|
|
|
|
void ApplePS2Controller::lockController(int * state)
|
|
{
|
|
*state = IOSimpleLockLockDisableInterrupt(_controllerLock);
|
|
}
|
|
|
|
#endif //DEBUGGER_SUPPORT
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
//
|
|
// Power Management support.
|
|
//
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
IOReturn ApplePS2Controller::setPowerState( unsigned long powerStateOrdinal,
|
|
IOService * policyMaker)
|
|
{
|
|
IOReturn result = IOPMAckImplied;
|
|
|
|
//
|
|
// Prevent the object from being freed while a call is pending.
|
|
// If thread_call_enter() returns TRUE, indicating that a call
|
|
// is already pending, then the extra retain is dropped.
|
|
//
|
|
|
|
retain();
|
|
if ( thread_call_enter1( _powerChangeThreadCall,
|
|
(void *) powerStateOrdinal ) == TRUE )
|
|
{
|
|
release();
|
|
}
|
|
result = 5000000; // 5 seconds before acknowledgement timeout
|
|
|
|
return result;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::setPowerStateCallout( thread_call_param_t param0,
|
|
thread_call_param_t param1 )
|
|
{
|
|
ApplePS2Controller * me = (ApplePS2Controller *) param0;
|
|
assert(me);
|
|
|
|
if (me->_workLoop)
|
|
{
|
|
me->_workLoop->runAction( /* Action */ setPowerStateAction,
|
|
/* target */ me,
|
|
/* arg0 */ param1 );
|
|
}
|
|
|
|
me->release(); // drop the retain from setPowerState()
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
IOReturn ApplePS2Controller::setPowerStateAction( OSObject * target,
|
|
void * arg0, void * arg1,
|
|
void * arg2, void * arg3 )
|
|
{
|
|
ApplePS2Controller * me = (ApplePS2Controller *) target;
|
|
|
|
#ifdef __LP64__
|
|
UInt32 powerState = (UInt32)(UInt64)arg0;
|
|
#else
|
|
UInt32 powerState = (UInt32) arg0;
|
|
#endif
|
|
|
|
me->setPowerStateGated( powerState );
|
|
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::setPowerStateGated( UInt32 powerState )
|
|
{
|
|
if ( _currentPowerState != powerState )
|
|
{
|
|
switch ( powerState )
|
|
{
|
|
case kPS2PowerStateSleep:
|
|
|
|
//
|
|
// 1. Make sure clocks are enabled, but IRQ lines held low.
|
|
//
|
|
|
|
++_ignoreInterrupts;
|
|
DEBUG_LOG("%s: setCommandByte for sleep 1\n", getName());
|
|
setCommandByte(0, kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ);
|
|
|
|
// 2. Notify clients about the state change. Clients can issue
|
|
// synchronous requests thanks to the recursive lock.
|
|
// First Mouse, then Keyboard.
|
|
|
|
dispatchDriverPowerControl( kPS2C_DisableDevice, kDT_Mouse );
|
|
dispatchDriverPowerControl( kPS2C_DisableDevice, kDT_Keyboard );
|
|
|
|
// 3. Freeze the request queue and drop all data received over
|
|
// the PS/2 port.
|
|
|
|
_hardwareOffline = true;
|
|
|
|
// 4. Disable the PS/2 port.
|
|
|
|
#if DISABLE_CLOCKS_IRQS_BEFORE_SLEEP
|
|
// This will cause some machines to turn on the LCD after the
|
|
// ACPI display driver has turned it off. With a real display
|
|
// driver present, this block of code can be uncommented (?).
|
|
|
|
DEBUG_LOG("%s: setCommandByte for sleep 2\n", getName());
|
|
setCommandByte(kCB_DisableKeyboardClock | kCB_DisableMouseClock, 0);
|
|
#endif // DISABLE_CLOCKS_IRQS_BEFORE_SLEEP
|
|
break;
|
|
|
|
case kPS2PowerStateDoze:
|
|
case kPS2PowerStateNormal:
|
|
|
|
if ( _currentPowerState != kPS2PowerStateSleep )
|
|
{
|
|
// Transitions between doze and normal power states
|
|
// require no action, since both are working states.
|
|
break;
|
|
}
|
|
|
|
if (_wakedelay)
|
|
IOSleep(_wakedelay);
|
|
|
|
#if FULL_INIT_AFTER_WAKE
|
|
//
|
|
// Reset and clean the 8042 keyboard/mouse controller.
|
|
//
|
|
|
|
resetController();
|
|
|
|
#endif // FULL_INIT_AFTER_WAKE
|
|
|
|
|
|
//
|
|
// Transition from Sleep state to Working state in 4 stages.
|
|
//
|
|
|
|
// 1. Enable the PS/2 port -- but just the clocks
|
|
|
|
DEBUG_LOG("%s: setCommandByte for wake 1\n", getName());
|
|
setCommandByte(0, kCB_DisableKeyboardClock | kCB_DisableMouseClock | kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ);
|
|
|
|
// 2. Unblock the request queue and wake up all driver threads
|
|
// that were blocked by submitRequest().
|
|
|
|
_hardwareOffline = false;
|
|
|
|
// 3. Notify clients about the state change: Keyboard, then Mouse.
|
|
// (This ordering is also part of the fix for ProBook 4x40s trackpad wake issue)
|
|
|
|
dispatchDriverPowerControl( kPS2C_EnableDevice, kDT_Keyboard );
|
|
dispatchDriverPowerControl( kPS2C_EnableDevice, kDT_Mouse );
|
|
|
|
// 4. Now safe to enable the IRQs...
|
|
|
|
DEBUG_LOG("%s: setCommandByte for wake 2\n", getName());
|
|
setCommandByte(kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ | kCB_SystemFlag, 0);
|
|
--_ignoreInterrupts;
|
|
break;
|
|
|
|
default:
|
|
IOLog("%s: bad power state %ld\n", getName(), (long)powerState);
|
|
break;
|
|
}
|
|
|
|
_currentPowerState = powerState;
|
|
}
|
|
|
|
//
|
|
// Acknowledge the power change before the power management timeout
|
|
// expires.
|
|
//
|
|
|
|
acknowledgeSetPowerState();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::dispatchDriverPowerControl( UInt32 whatToDo, PS2DeviceType deviceType )
|
|
{
|
|
if (kDT_Mouse == deviceType && _powerControlInstalledMouse)
|
|
(*_powerControlActionMouse)(_powerControlTargetMouse, whatToDo);
|
|
|
|
if (kDT_Keyboard == deviceType && _powerControlInstalledKeyboard)
|
|
(*_powerControlActionKeyboard)(_powerControlTargetKeyboard, whatToDo);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::installPowerControlAction(
|
|
PS2DeviceType deviceType,
|
|
OSObject * target,
|
|
PS2PowerControlAction action )
|
|
{
|
|
if ( deviceType == kDT_Keyboard && _powerControlInstalledKeyboard == false )
|
|
{
|
|
target->retain();
|
|
_powerControlTargetKeyboard = target;
|
|
_powerControlActionKeyboard = action;
|
|
_powerControlInstalledKeyboard = true;
|
|
}
|
|
else if ( deviceType == kDT_Mouse && _powerControlInstalledMouse == false )
|
|
{
|
|
target->retain();
|
|
_powerControlTargetMouse = target;
|
|
_powerControlActionMouse = action;
|
|
_powerControlInstalledMouse = true;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::uninstallPowerControlAction( PS2DeviceType deviceType )
|
|
{
|
|
if ( deviceType == kDT_Keyboard && _powerControlInstalledKeyboard == true )
|
|
{
|
|
_powerControlInstalledKeyboard = false;
|
|
_powerControlActionKeyboard = NULL;
|
|
_powerControlTargetKeyboard->release();
|
|
_powerControlTargetKeyboard = 0;
|
|
}
|
|
else if ( deviceType == kDT_Mouse && _powerControlInstalledMouse == true )
|
|
{
|
|
_powerControlInstalledMouse = false;
|
|
_powerControlActionMouse = NULL;
|
|
_powerControlTargetMouse->release();
|
|
_powerControlTargetMouse = 0;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::installMessageAction(
|
|
PS2DeviceType deviceType,
|
|
OSObject *target, PS2MessageAction action)
|
|
{
|
|
if (deviceType == kDT_Keyboard && !_messageInstalledKeyboard)
|
|
{
|
|
target->retain();
|
|
_messageTargetKeyboard = target;
|
|
_messageActionKeyboard = action;
|
|
_messageInstalledKeyboard = true;
|
|
}
|
|
else if (deviceType == kDT_Mouse && !_messageInstalledMouse)
|
|
{
|
|
target->retain();
|
|
_messageTargetMouse = target;
|
|
_messageActionMouse = action;
|
|
_messageInstalledMouse = true;
|
|
}
|
|
}
|
|
|
|
void ApplePS2Controller::uninstallMessageAction(PS2DeviceType deviceType)
|
|
{
|
|
if (deviceType == kDT_Keyboard && _messageInstalledKeyboard)
|
|
{
|
|
_messageInstalledKeyboard = false;
|
|
_messageActionKeyboard = NULL;
|
|
_messageTargetKeyboard->release();
|
|
_messageTargetKeyboard = NULL;
|
|
}
|
|
else if (deviceType == kDT_Mouse && _messageInstalledMouse)
|
|
{
|
|
_messageInstalledMouse = false;
|
|
_messageActionMouse = NULL;
|
|
_messageTargetMouse->release();
|
|
_messageTargetMouse = NULL;
|
|
}
|
|
}
|
|
|
|
void ApplePS2Controller::dispatchMessage(PS2DeviceType deviceType, int message, void* data)
|
|
{
|
|
if (deviceType == kDT_Keyboard && _messageInstalledKeyboard)
|
|
{
|
|
(*_messageActionKeyboard)(_messageTargetKeyboard, message, data);
|
|
}
|
|
else if (deviceType == kDT_Mouse && _messageInstalledMouse)
|
|
{
|
|
(*_messageActionMouse)(_messageTargetMouse, message, data);
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void ApplePS2Controller::lock()
|
|
{
|
|
assert(_cmdbyteLock);
|
|
IOLockLock(_cmdbyteLock);
|
|
}
|
|
|
|
void ApplePS2Controller::unlock()
|
|
{
|
|
assert(_cmdbyteLock);
|
|
IOLockUnlock(_cmdbyteLock);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
#define kDefault "Default"
|
|
|
|
struct DSDT_HEADER
|
|
{
|
|
uint32_t tableSignature;
|
|
uint32_t tableLength;
|
|
uint8_t specCompliance;
|
|
uint8_t checkSum;
|
|
char oemID[6];
|
|
char oemTableID[8];
|
|
uint32_t oemRevision;
|
|
uint32_t creatorID;
|
|
uint32_t creatorRevision;
|
|
};
|
|
|
|
#define DSDT_SIGNATURE ('D' | 'S'<<8 | 'D'<<16 | 'T'<<24)
|
|
|
|
static const DSDT_HEADER* getDSDT()
|
|
{
|
|
IORegistryEntry* reg = IORegistryEntry::fromPath("IOService:/AppleACPIPlatformExpert");
|
|
if (!reg)
|
|
return NULL;
|
|
OSDictionary* dict = OSDynamicCast(OSDictionary, reg->getProperty("ACPI Tables"));
|
|
reg->release();
|
|
if (!dict)
|
|
return NULL;
|
|
OSData* data = OSDynamicCast(OSData, dict->getObject("DSDT"));
|
|
if (!data || data->getLength() < sizeof(DSDT_HEADER))
|
|
return NULL;
|
|
const DSDT_HEADER* pDSDT = (const DSDT_HEADER*)data->getBytesNoCopy();
|
|
if (!pDSDT || data->getLength() < sizeof(DSDT_HEADER) || pDSDT->tableSignature != DSDT_SIGNATURE)
|
|
return NULL;
|
|
return pDSDT;
|
|
}
|
|
|
|
static void stripTrailingSpaces(char* str)
|
|
{
|
|
char* p = str;
|
|
for (; *p; p++)
|
|
;
|
|
for (--p; p >= str && *p == ' '; --p)
|
|
*p = 0;
|
|
}
|
|
|
|
static OSString* getPlatformManufacturer()
|
|
{
|
|
// allow override in PS2K ACPI device
|
|
IORegistryEntry* reg = IORegistryEntry::fromPath("IOService:/AppleACPIPlatformExpert/PS2K");
|
|
if (reg) {
|
|
OSString* id = OSDynamicCast(OSString, reg->getProperty("RM,oem-id"));
|
|
reg->release();
|
|
if (id)
|
|
return id;
|
|
}
|
|
// otherwise use DSDT header
|
|
const DSDT_HEADER* pDSDT = getDSDT();
|
|
if (!pDSDT)
|
|
return NULL;
|
|
// copy to static data, NUL terminate, strip trailing spaces, and return
|
|
static char oemID[sizeof(pDSDT->oemID)+1];
|
|
bcopy(pDSDT->oemID, oemID, sizeof(pDSDT->oemID));
|
|
oemID[sizeof(oemID)-1] = 0;
|
|
stripTrailingSpaces(oemID);
|
|
return OSString::withCStringNoCopy(oemID);
|
|
}
|
|
|
|
static OSString* getPlatformProduct()
|
|
{
|
|
// allow override in PS2K ACPI device
|
|
IORegistryEntry* reg = IORegistryEntry::fromPath("IOService:/AppleACPIPlatformExpert/PS2K");
|
|
if (reg) {
|
|
OSString* id = OSDynamicCast(OSString, reg->getProperty("RM,oem-table-id"));
|
|
reg->release();
|
|
if (id)
|
|
return id;
|
|
}
|
|
const DSDT_HEADER* pDSDT = getDSDT();
|
|
if (!pDSDT)
|
|
return NULL;
|
|
// copy to static data, NUL terminate, strip trailing spaces, and return
|
|
static char oemTableID[sizeof(pDSDT->oemTableID)+1];
|
|
bcopy(pDSDT->oemTableID, oemTableID, sizeof(pDSDT->oemTableID));
|
|
oemTableID[sizeof(oemTableID)-1] = 0;
|
|
stripTrailingSpaces(oemTableID);
|
|
return OSString::withCStringNoCopy(oemTableID);
|
|
}
|
|
|
|
static OSDictionary* _getConfigurationNode(OSDictionary *root, const char *name);
|
|
|
|
static OSDictionary* _getConfigurationNode(OSDictionary *root, OSString *name)
|
|
{
|
|
OSDictionary *configuration = NULL;
|
|
|
|
if (root && name) {
|
|
if (!(configuration = OSDynamicCast(OSDictionary, root->getObject(name)))) {
|
|
if (OSString *link = OSDynamicCast(OSString, root->getObject(name))) {
|
|
const char* p1 = link->getCStringNoCopy();
|
|
const char* p2 = p1;
|
|
for (; *p2 && *p2 != ';'; ++p2);
|
|
if (*p2 != ';') {
|
|
configuration = _getConfigurationNode(root, link);
|
|
}
|
|
else {
|
|
if (OSString* strip = OSString::withString(link)) {
|
|
strip->setChar(0, (unsigned)(p2 - p1));
|
|
configuration = _getConfigurationNode(root, strip);
|
|
strip->release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return configuration;
|
|
}
|
|
|
|
static OSDictionary* _getConfigurationNode(OSDictionary *root, const char *name)
|
|
{
|
|
OSDictionary *configuration = NULL;
|
|
|
|
if (root && name) {
|
|
OSString *nameNode = OSString::withCStringNoCopy(name);
|
|
|
|
configuration = _getConfigurationNode(root, nameNode);
|
|
|
|
OSSafeRelease(nameNode);
|
|
}
|
|
|
|
return configuration;
|
|
}
|
|
|
|
OSDictionary* ApplePS2Controller::getConfigurationNode(OSDictionary* list, OSString *model)
|
|
{
|
|
OSDictionary *configuration = NULL;
|
|
|
|
if (OSString *manufacturer = getPlatformManufacturer())
|
|
if (OSDictionary *manufacturerNode = OSDynamicCast(OSDictionary, list->getObject(manufacturer)))
|
|
if (!(configuration = _getConfigurationNode(manufacturerNode, getPlatformProduct())))
|
|
if (!(configuration = _getConfigurationNode(manufacturerNode, model)))
|
|
configuration = _getConfigurationNode(manufacturerNode, kDefault);
|
|
|
|
if (!configuration && !(configuration = _getConfigurationNode(list, model)))
|
|
configuration = _getConfigurationNode(list, kDefault);
|
|
|
|
return configuration;
|
|
}
|
|
|
|
EXPORT OSDictionary* ApplePS2Controller::makeConfigurationNode(OSDictionary* list, OSString* model)
|
|
{
|
|
if (!list)
|
|
return NULL;
|
|
|
|
OSDictionary* result = 0;
|
|
OSDictionary* defaultNode = _getConfigurationNode(list, kDefault);
|
|
OSDictionary* platformNode = getConfigurationNode(list, model);
|
|
if (defaultNode)
|
|
{
|
|
// have default node, result is merge with platform node
|
|
result = OSDictionary::withDictionary(defaultNode);
|
|
if (result && platformNode)
|
|
result->merge(platformNode);
|
|
}
|
|
else if (platformNode)
|
|
{
|
|
// no default node, try to use just platform node
|
|
result = OSDictionary::withDictionary(platformNode);
|
|
}
|
|
return result;
|
|
}
|