Source to iokit/Drivers/platform/drvApplePS2Controller/ApplePS2Controller.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@
*/
#include <IOKit/assert.h>
#include <IOKit/IOService.h>
#include <IOKit/IOSyncer.h>
#include <IOKit/IOCommandQueue.h>
#include <IOKit/ps2/ApplePS2KeyboardDevice.h>
#include <IOKit/ps2/ApplePS2MouseDevice.h>
#include "ApplePS2Controller.h"
extern "C"
{
#include <architecture/i386/pio.h>
#include <machine/machine_routines.h>
}
static ApplePS2Controller * gApplePS2Controller = 0; // global variable to self
// =============================================================================
// Interrupt-Time Support Functions
//
static void interruptHandlerMouse(OSObject *, void *, IOService *, int)
{
//
// 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.
//
gApplePS2Controller->_interruptSourceMouse->interruptOccurred(0, 0, 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void interruptHandlerKeyboard(OSObject *, void *, IOService *, int)
{
#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;
// Lock out the keyboard interrupt handler [redundant here] and claim
// exclusive access to the internal keyboard queue.
gApplePS2Controller->lockController();
// 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, 0, 0, 0);
}
else
{
// Retrieve the keyboard data on the controller's input port.
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.
if (gApplePS2Controller->doEscape(key) == false)
gApplePS2Controller->enqueueKeyboardData(key);
// In all cases, we wake up our workloop to service the interrupt data.
gApplePS2Controller->_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.
gApplePS2Controller->unlockController();
#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.
//
gApplePS2Controller->_interruptSourceKeyboard->interruptOccurred(0, 0, 0);
#endif DEBUGGER_SUPPORT
}
// =============================================================================
// ApplePS2Controller Class Implementation
//
#define super IOService
OSDefineMetaClassAndStructors(ApplePS2Controller, IOService);
bool ApplePS2Controller::init(OSDictionary * properties)
{
if (!super::init(properties)) return false;
//
// Initialize minimal state.
//
_commandQueue = 0;
_workLoop = 0;
_interruptSourceKeyboard = 0;
_interruptSourceMouse = 0;
_interruptTargetKeyboard = 0;
_interruptTargetMouse = 0;
_interruptActionKeyboard = NULL;
_interruptActionMouse = NULL;
_interruptInstalledKeyboard = false;
_interruptInstalledMouse = false;
_mouseDevice = 0;
_keyboardDevice = 0;
#if DEBUGGER_SUPPORT
_extendedState = false;
_modifierState = 0x00;
_keyboardQueueAlloc = NULL;
queue_init(&_keyboardQueue);
queue_init(&_keyboardQueueUnused);
_controllerLockOldSpl = 0;
usimple_lock_init(&_controllerLock, ETAP_NO_TRACE);
#endif DEBUGGER_SUPPORT
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool ApplePS2Controller::start(IOService * provider)
{
//
// The driver has been instructed to start. Allocate all our resources.
//
if (!super::start(provider)) return false;
#if DEBUGGER_SUPPORT
_keyboardQueueAlloc = (KeyboardQueueElement *)
IOMalloc(kKeyboardQueueSize*sizeof(KeyboardQueueElement));
if (!_keyboardQueueAlloc) return false;
// 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
//
// 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.
//
UInt8 commandByte;
writeCommandPort(kCP_GetCommandByte);
commandByte = readDataPort(kDT_Keyboard);
commandByte &= ~(kCB_EnableMouseIRQ | kCB_DisableMouseClock);
writeCommandPort(kCP_SetCommandByte);
writeDataPort(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 )
{
inb(kDataPort);
IODelay(kDataDelay);
}
//
// Initialize our work loop, our command queue, and our interrupt event
// sources. The work loop can accept requests after this step.
//
_workLoop = IOWorkLoop::workLoop();
_commandQueue = IOCommandQueue::commandQueue(
this, (IOCommandQueueAction) &ApplePS2Controller::processRequest);
_interruptSourceMouse = IOInterruptEventSource::interruptEventSource(
this, (IOInterruptEventAction) &ApplePS2Controller::interruptOccurred);
_interruptSourceKeyboard = IOInterruptEventSource::interruptEventSource(
this, (IOInterruptEventAction) &ApplePS2Controller::interruptOccurred);
if ( !_workLoop ||
!_commandQueue ||
!_interruptSourceMouse ||
!_interruptSourceKeyboard ) return false;
if ( _workLoop->addEventSource(_commandQueue) != kIOReturnSuccess )
return false;
//
// 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 = new ApplePS2KeyboardDevice;
if ( !_keyboardDevice ||
!_keyboardDevice->init() ||
!_keyboardDevice->attach(this) ) return false;
_mouseDevice = new ApplePS2MouseDevice;
if ( !_mouseDevice ||
!_mouseDevice->init() ||
!_mouseDevice->attach(this) ) return false;
gApplePS2Controller = this;
_keyboardDevice->registerService();
_mouseDevice->registerService();
return true; // success
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 == false);
assert(_interruptInstalledMouse == false);
// Free the nubs we created.
if (_keyboardDevice) _keyboardDevice->release();
if (_mouseDevice) _mouseDevice->release();
// Free the work loop.
if (_workLoop) _workLoop->release();
// Free the interrupt source and command queue.
if (_commandQueue) _commandQueue->release();
if (_interruptSourceKeyboard) _interruptSourceKeyboard->release();
if (_interruptSourceMouse) _interruptSourceMouse->release();
#if DEBUGGER_SUPPORT
// Free the keyboard queue allocation space (after disabling interrupt).
if (_keyboardQueueAlloc)
IOFree(_keyboardQueueAlloc,kKeyboardQueueSize*sizeof(KeyboardQueueElement));
#endif DEBUGGER_SUPPORT
gApplePS2Controller = 0;
super::stop(provider);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IOWorkLoop * ApplePS2Controller::getWorkLoop() const
{
return _workLoop;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ApplePS2Controller::installInterruptAction(PS2DeviceType deviceType,
OSObject * target,
PS2InterruptAction action)
{
//
// 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 == false)
{
target->retain();
_interruptTargetKeyboard = target;
_interruptActionKeyboard = action;
_workLoop->addEventSource(_interruptSourceKeyboard);
getProvider()->registerInterrupt(kIRQ_Keyboard,0, interruptHandlerKeyboard);
getProvider()->enableInterrupt(kIRQ_Keyboard);
_interruptInstalledKeyboard = true;
}
else if (deviceType == kDT_Mouse && _interruptInstalledMouse == false)
{
target->retain();
_interruptTargetMouse = target;
_interruptActionMouse = action;
_workLoop->addEventSource(_interruptSourceMouse);
getProvider()->registerInterrupt(kIRQ_Mouse, 0, interruptHandlerMouse);
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 == true)
{
getProvider()->disableInterrupt(kIRQ_Keyboard);
getProvider()->unregisterInterrupt(kIRQ_Keyboard);
_workLoop->removeEventSource(_interruptSourceMouse);
_interruptInstalledKeyboard = false;
_interruptActionKeyboard = NULL;
_interruptTargetKeyboard->release();
_interruptTargetKeyboard = 0;
}
else if (deviceType == kDT_Mouse && _interruptInstalledMouse == true)
{
getProvider()->disableInterrupt(kIRQ_Mouse);
getProvider()->unregisterInterrupt(kIRQ_Mouse);
_workLoop->removeEventSource(_interruptSourceMouse);
_interruptInstalledMouse = false;
_interruptActionMouse = NULL;
_interruptTargetMouse->release();
_interruptTargetMouse = 0;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PS2Request * ApplePS2Controller::allocateRequest()
{
//
// Allocate a request structure. Blocks until successful. Request structure
// is guaranteed to be zeroed.
//
PS2Request * request = (PS2Request *) IOMalloc(sizeof(PS2Request));
bzero(request, sizeof(PS2Request));
return request;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ApplePS2Controller::freeRequest(PS2Request * request)
{
//
// Deallocate a request structure.
//
IOFree(request, sizeof(PS2Request));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool ApplePS2Controller::submitRequest(PS2Request * request)
{
//
// Submit the request to the controller for processing, asynchronously.
//
return (_commandQueue->enqueueCommand(false, request) == KERN_SUCCESS);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ApplePS2Controller::submitRequestAndBlock(PS2Request * request)
{
//
// Submit the request to the controller for processing, synchronously.
//
IOSyncer * completionSyncer = IOSyncer::create();
assert(completionSyncer);
request->completionTarget = this;
request->completionAction = submitRequestAndBlockCompletion;
request->completionParam = completionSyncer;
_commandQueue->enqueueCommand(true, request);
completionSyncer->wait(); // wait 'till done
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ApplePS2Controller::submitRequestAndBlockCompletion(void *, void * param)
{ // PS2CompletionAction
IOSyncer * completionSyncer = (IOSyncer *) param;
completionSyncer->signal();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ApplePS2Controller::interruptOccurred(IOInterruptEventSource *, 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.
//
UInt8 status;
#if DEBUGGER_SUPPORT
lockController(); // (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();
dispatchDriverInterrupt(kDT_Keyboard, status);
lockController();
}
// See if data is available on the mouse input stream (off real port).
else if ( (inb(kCommandPort) & (kOutputReady | kMouseData)) ==
(kOutputReady | kMouseData))
{
unlockController();
dispatchDriverInterrupt(kDT_Mouse, inb(kDataPort));
lockController();
}
else break; // out of loop
}
unlockController(); // (release interrupt lockout + access to queue)
#else
// Loop only while there is data currently on the input stream.
while ( ((status = inb(kCommandPort)) & kOutputReady) )
{
// Read in and dispatch the data, but only if it isn't what is required
// by the active command.
dispatchDriverInterrupt((status&kMouseData)?kDT_Mouse:kDT_Keyboard,
inb(kDataPort));
}
#endif DEBUGGER_SUPPORT
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ApplePS2Controller::dispatchDriverInterrupt(PS2DeviceType deviceType,
UInt8 data)
{
//
// The supplied data is passed onto the interrupt handler in the appropriate
// driver, if one is registered, otherwise the data byte is thrown away.
//
// This method should only be called from our single-threaded work loop.
//
if ( deviceType == kDT_Mouse )
{
// Dispatch the data to the mouse driver.
if (_interruptInstalledMouse)
(*_interruptActionMouse)(_interruptTargetMouse, data);
}
else if ( deviceType == kDT_Keyboard )
{
// Dispatch the data to the keyboard driver.
if (_interruptInstalledKeyboard)
(*_interruptActionKeyboard)(_interruptTargetKeyboard, data);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ApplePS2Controller::processRequest(PS2Request * request,
void * /* field1 */,
void * /* field2 */,
void * /* field3 */)
// IOCommandQueueAction
{
//
// 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;
// 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);
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;
}
if (failed) break;
}
// 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);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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
lockController(); // (lock out interrupt + access to queue)
if (deviceType == kDT_Keyboard && dequeueKeyboardData(&readByte))
{
unlockController();
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(); // (release interrupt lockout + access to queue)
#endif DEBUGGER_SUPPORT
IOLog("%s: Timed out on %s input stream.\n", getName(),
(deviceType == kDT_Keyboard) ? "keyboard" : "mouse");
return 0;
}
//
// 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(); // (release interrupt lockout + access to queue)
#endif DEBUGGER_SUPPORT
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
lockController(); // (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(); // 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;
}
//
// 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(); // (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.
//
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.
//
dispatchDriverInterrupt(deviceType, readByte);
return firstByte;
}
}
}
else
{
//
// The data we just received is for the other input stream, not ours,
// so dispatch appropriate interrupt handler.
//
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);
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);
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 ||
_modifierState == kModifierAltRight )
{
// Disable the mouse by forcing the clock line low.
while (inb(kCommandPort) & kInputBusy) 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);
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(void)
{
usimple_unlock(&_controllerLock);
ml_set_interrupts_enabled(_controllerLockOldSpl);
}
void ApplePS2Controller::lockController(void)
{
int oldSpl = ml_set_interrupts_enabled(FALSE);
usimple_lock(&_controllerLock);
_controllerLockOldSpl = oldSpl;
}
#endif DEBUGGER_SUPPORT