Source to bsd/dev/i386/PS2Keyboard.m


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (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.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/* 	Copyright (c) 1992 NeXT Computer, Inc.  All rights reserved. 
 *
 * PS2Keyboard.m - Keyboard driver.
 * 
 *
 * HISTORY
 * 11-Aug-92    Joe Pasqua at NeXT
 *      Created. 
 */
 
// TO DO:
// * We still have kdreboot in here. Make a real public method and get the
//   rest of the system to use it.
// Notes:
// * To find things that need to be fixed, search for FIX, to find questions
//   to be resolved, search for ASK, to find stuff that still needs to be
//   done, search for TO DO.

#define MACH_USER_API	1
#undef	KERNEL_PRIVATE

#import <machkit/NXLock.h>
#import <driverkit/driverServer.h>
#import <driverkit/generalFuncs.h>
#import <driverkit/interruptMsg.h>
#import <bsd/dev/i386/PS2KeyboardPriv.h>
#import <bsd/dev/i386/PS2Keyboard.h>
#import <bsd/dev/i386/PS2Mouse.h>
#import <bsd/dev/i386/PCKeyboardDefs.h>
#import <driverkit/i386/directDevice.h>
#import <driverkit/i386/driverTypes.h>
#import <bsd/dev/ev_types.h>

#define	KeyboardIPL	3
#define	SAFE_IPL	(KeyboardIPL + 1)
#define	MAX_PENDING_EVENTS	5
#define	KEYCODE_NUMLOCK		0x45
#define	KEYCODE_LALT		0x38
#define	KEYCODE_RALT		0x61
#define	KEYCODE_PAUSE		0x6F

static PS2Keyboard *_kbdObj;
static PCKeyboardEvent _pendingEvents[MAX_PENDING_EVENTS];
static int _eventIndex;
static BOOL _manualDataHandling;

//
// BEGIN:	Definitions used to keep track of key state
//
// NOTES:	Key up/down state is tracked in a bit list.  Bits are set
//		for key-down, and cleared for key-up.  The bit vector and
//		macros for it's manipulation are defined here.
#define KBV_BITS_PER_UNIT	32
#define KBV_BITS_MASK		31
#define KBV_BITS_SHIFT		5	// 1<<5 == 32, for cheap divide
#define KBV_NUNITS ((128 + (KBV_BITS_PER_UNIT-1))/KBV_BITS_PER_UNIT)

#define KBV_KEYDOWN(n, bits) \
	(bits)[((n)>>KBV_BITS_SHIFT)] |= (1 << ((n) & KBV_BITS_MASK))

#define KBV_KEYUP(n, bits) \
	(bits)[((n)>>KBV_BITS_SHIFT)] &= ~(1 << ((n) & KBV_BITS_MASK))

#define KBV_IS_KEYDOWN(n, bits) \
	(((bits)[((n)>>KBV_BITS_SHIFT)] & (1 << ((n) & KBV_BITS_MASK))) != 0)

static unsigned long	_kbdBitVector[KBV_NUNITS];
//
// END:		Definitions used to keep track of key state
//


@implementation PS2Keyboard

//
// BEGIN:	Impl. of internal methods for dealing w/ kbd controller
// NOTE:	The following are utility procedures for dealing with
//		The keyboard controller. They are called from the low
//		level interrupt handler. Because of this, they can not be
//		ObjC methods.
//
static void SendCmd(unsigned char cmd)
// Description:	Sends a command to the keyboard controller
{
    // Wait for room in the buffer
    while (inb(K_STATUS) & K_IBUF_FUL)
	continue;
    outb(K_CMD, cmd);	// Send the command
    return;
}

static void SendData(unsigned char data)
// Description:	Sends data to the keyboard itself
{
    // Wait for room in the keyboard's input buffer
    while (inb(K_STATUS) & K_IBUF_FUL)
	continue;
    outb(K_RDWR, data);	// Send the data
    _kbdObj->lastSent = data;
}

static void DoResend()
// Description:	Resends the last data sent to the keyboard
{
    if (_kbdObj->pendingAck == NOT_WAITING) 
	IOLog("PS2Keyboard/DoResend: Unexpected RESEND from keyboard\n");
    else
        SendData(_kbdObj->lastSent);
}

static unsigned char GetData()
// Description:	Read data from the keyboard
{
    // Spin until there is something ready to read
    while ((inb(K_STATUS) & (K_OBUF_FUL | M_OBUF_FUL)) == 0);
    IODelay(K_DATA_DELAY);
    return(inb(K_RDWR));	// Read the data
}

static void BeginSettingLEDs(unsigned char val)
// Description:	Begin the process of setting the keyboard LEDs. First
//		you tell the keyboard that you want to set the LEDs. It
//		will respond with an ACK. When we get the ACK, HandleAck
//		will call CompleteSettingLEDs which sends the LED values
//		to the keyboard.
{
    if (_kbdObj->pendingAck != NOT_WAITING) {
	IOLog("PS2Keyboard/BeginSettingLEDs: Currently awaiting an ACK\n");
	return;
    }

    _kbdObj->pendingAck = SET_LEDS;
    _kbdObj->pendingLEDVal = val;
    SendData(K_CMD_LEDS);
}

static void CompleteSettingLEDs()
// Description:	See BeginSettingLEDs().
{
    SendData(_kbdObj->pendingLEDVal);	// Send the actual value
}

static void HandleAck()
// Description:	Respond to an ACK from the keyboard based on our state.
{
    switch (_kbdObj->pendingAck) {
	case SET_LEDS:
	    CompleteSettingLEDs();
	    _kbdObj->pendingAck = DATA_ACK;
	    break;
	case DATA_ACK:
	    _kbdObj->pendingAck = NOT_WAITING;
	    break;
	case NOT_WAITING:
	    IOLog("PS2Keyboard/HandleAck: Unexpected ACK from keyboard\n");
	    break;
	default:
	    IOLog("PS2Keyboard/HandleAck: pendingAck has impossible value\n");
	    break;
    }
}

static void InitKbdController()
// Description: Prepare the keyboard controller for use. This involves
//		setting certain enable bits in the controller.
{
    unsigned char cmdByte;		// keyboard command byte

    _manualDataHandling = YES;	// We'll be reading data manually

    /* get rid of any garbage in output buffer */
    if (inb(K_STATUS) & (K_OBUF_FUL | M_OBUF_FUL)) {
	    IODelay(K_DATA_DELAY);
	    (void)inb(K_RDWR);
    }
    
    SendCmd(KC_CMD_READ);	// ask for the ctlr command byte
    cmdByte = GetData();
    cmdByte |= K_CB_TRANSLATE;	// translate incoming scan codes to scan code 1
    cmdByte &= ~K_CB_DISBLE;	// Enable kbd dev
    cmdByte |= K_CB_ENBLIRQ;	// Enable kbd IRQ
    cmdByte |= M_CB_DISBLE;	// Disable mouse dev
    cmdByte &= ~M_CB_ENBLIRQ;	// Disable mouse IRQ
    SendCmd(KC_CMD_WRITE);	// Write new ctlr command byte
    SendData(cmdByte);
    _manualDataHandling = NO;	// We're done reading data manually
}
//
// END:		Impl. of internal methods for dealing w/ kbd controller
//


//
// BEGIN:	Implementation of internal functions and methods
//
static void EnqueueAltUp(int keyCode, ns_time_t stamp)
// Description:	Enqueue an event that corresponds to an ALT key coming up
{
    PCKeyboardEvent event;
    
    if (_eventIndex != MAX_PENDING_EVENTS)
    {
	event.keyCode = keyCode;
	event.goingDown = 0;
	event.timeStamp = stamp;
	_pendingEvents[_eventIndex++] = event;
    }
}

static BOOL SpecialKeys(PCKeyboardEvent *event, void *state)
// Description:	Test whether we have encountered a special key sequence.
//		If we have a special sequence, then we invoke mini_mon
//		appropriately and return YES. Otherwise we return NO.
{
    if (event->keyCode == KEYCODE_NUMLOCK && event->goingDown)
    {
	if (KBV_IS_KEYDOWN(KEYCODE_RALT, _kbdBitVector))
	{
	    if (KBV_IS_KEYDOWN(KEYCODE_LALT, _kbdBitVector))
	    {
		mini_mon("", "Mini-Monitor", state);
		EnqueueAltUp(KEYCODE_LALT, event->timeStamp);
	    }
	    else
		mini_mon("restart", "Restart", state);
	    EnqueueAltUp(KEYCODE_RALT , event->timeStamp);
	    return YES;
	}
    }
    
    return NO;
}

static inline
PCKeyboardEvent *ScancodeToKeyEvent(
    unsigned char key, unsigned char *extendCount)
// Description: Take a scan code from the keyboard and package it up into
//		a PCKeyboard event. If this is the first part of an extended
//		sequence then return a NIL pointer.
{
    static PCKeyboardEvent event;

    if (key == K_EXTEND) {
	// This introduces an extended key sequence. Note that fact
	// and then return. Next time we get a key we'll finish the
	// sequence.
	*extendCount = 1;
	return ((PCKeyboardEvent *) 0);
    }

    if (key == K_PAUSE) {
	// This introduces an extended key sequence for the pause key.
	// Note that fact and then return. We'll eat the rest of the
	// key sequence and return a non-zero keycode at the end.
	// The sequence is K_PAUSE 1D 5 K_PAUSE 9D C5. Note the 2nd
	// occurance of the K_PAUSE keycode.
	if (*extendCount == 0)
	{
	    *extendCount = 5;
	    return ((PCKeyboardEvent *) 0);
	}
	// Else this is the 2nd K_PAUSE in the sequence, chuck it...
    }


    if (*extendCount == 0) 
	event.keyCode = key & ~K_UP;
    else
    {
        (*extendCount)--;
	if (*extendCount != 0)
	    return ((PCKeyboardEvent *) 0);
	// Convert certain extended codes on the PC keyboard
	// into single scancodes
	switch (key & ~K_UP) {
	    case 0x1D: event.keyCode = 0x60; break;     // ctrl
	    case 0x38: event.keyCode = 0x61; break;	// alt
            case 0x1C: event.keyCode = 0x62; break;     // enter
            case 0x35: event.keyCode = 0x63; break;     // /
            case 0x48: event.keyCode = 0x64; break;     // up arrow
            case 0x50: event.keyCode = 0x65; break;     // down arrow
            case 0x4B: event.keyCode = 0x66; break;     // left arrow
            case 0x4D: event.keyCode = 0x67; break;     // right arrow
            case 0x52: event.keyCode = 0x68; break;     // insert
            case 0x53: event.keyCode = 0x69; break;     // delete
            case 0x49: event.keyCode = 0x6A; break;     // page up
            case 0x51: event.keyCode = 0x6B; break;     // page down
            case 0x47: event.keyCode = 0x6C; break;     // home
            case 0x4F: event.keyCode = 0x6D; break;     // end
	    case 0x37: event.keyCode = 0x6E; break;	// PrintScreen
	    case 0x45: event.keyCode = KEYCODE_PAUSE; break;	// Pause
	    case 0x2A: // This is a header or trailer for PrintScreen
	    default: return ((PCKeyboardEvent *) 0);
        }
    }

    if (event.keyCode == 0)
	return (PCKeyboardEvent *) 0;

    IOGetTimestamp(&event.timeStamp);
    event.goingDown = !(key & K_UP);
    
    // Deal with the Pause key in a special way. It only generates a
    // down code and it does not repeat. It always looks like a key up
    // to this code.
    if (event.keyCode == KEYCODE_PAUSE)
        event.goingDown = !(KBV_IS_KEYDOWN(event.keyCode, _kbdBitVector));
    
    if (event.goingDown)
    {
        if (KBV_IS_KEYDOWN(event.keyCode, _kbdBitVector))
            return (PCKeyboardEvent *)0;
	else
	    KBV_KEYDOWN(event.keyCode, _kbdBitVector);
    }
    else
        KBV_KEYUP(event.keyCode, _kbdBitVector);
    
    return (&event);
}

static void KbdIntHandler(void *identity, void *state, unsigned int arg)
// Description:	This is the low level interupt handler that gets called when
//		a keyboard interrupt occurs. It reads the key from the
//		controller, turns it into an event and adds it to a queue
//		of events for the high level interrupt handler to process.
//		It then causes an interrupt message to be sent. The
//		interruptHandler method will eventually receive that message
//		and send all queued events to the higher levels.
// NOTE:	This code also watches for "special" key sequences which are
//		meant to allow reboots and to invoke the debugger. This must
//		be done at interrupt level so that we avoid having a huge
//		amount of machinery between the request for a reboot/debugger
//		and the completion of that request. Furthermore, we need to
//		have a thread_saved_state_t in order to invoke the debugger.
{
    unsigned char key;
    PCKeyboardEvent *event;

    // Sometimes when tweaking the controller the code wants to poll
    // for data coming back from the controller. In these cases we just
    // ignore the interrupt telling us that there is data available. Someone
    // else will poll and read the data.  
    if (_manualDataHandling == YES)
        return;

    // If there's nothing there, ignore the interrupt
    if (!((unsigned char)inb(K_STATUS) & K_OBUF_FUL))
	return;

    IODelay(K_DATA_DELAY);    
    key = (unsigned char)inb(K_RDWR);	// Read data from keyboard

    if (key == K_ACKSC) {
	HandleAck();	// Respond to ack of previous request
	return;
    }
    else if (key == K_RESEND) {
	DoResend();	// Resend previous data
	return;
    }

    if ( (event = ScancodeToKeyEvent(key, &_kbdObj->extendCount)) )
    {
        if (SpecialKeys(event, state) == NO)
	{
	    // Add this event to the queue. If there is no room in the queue,
	    // we toss the event. TO DO: when we fill the last entry in the
	    // queue we should disable keyboard interrupts until the queue gets
	    // some free space.
	    if (_eventIndex == MAX_PENDING_EVENTS)
		return;
	    _pendingEvents[_eventIndex++] = *event;
	}
	
        // This call causes an interrupt message to be sent. Note that
	// if a previous message is outstanding, this call does nothing.
	// That is, no new message is queued.
    IOSendInterrupt(identity, state, IO_DEVICE_INTERRUPT_MSG);
    }
}

- (void)interruptHandler
// Description:	This method is invoked by the I/O Thread when a keyboard
//		interrupt message has been recieved. This method reads
//		the keycode, processes it into a PCKeyboardEvent and
//		sends it to our owner.
{
    int oldIPL, nEvents, i;
    PCKeyboardEvent events[MAX_PENDING_EVENTS];

    if (_owner == nil)
        return;		// After all that, no one is interested!

    // Safely copy the events from the pending queue
    oldIPL = splx(SAFE_IPL);
    if (_eventIndex == 1)
        events[0] = _pendingEvents[0];
    else
	bcopy(_pendingEvents, events, sizeof(PCKeyboardEvent) * _eventIndex);
    nEvents = _eventIndex;
    _eventIndex = 0;
    (void)splx(oldIPL);
    
    for (i = 0; i < nEvents; i++)
    {
	[_owner dispatchKeyboardEvent:&events[i]];
    }
}


static volatile void kbdThread(PS2Keyboard *_kbdObject)
// Description: This is the function which is executed as the I/O Thread.
//		It waits for messages from driverkit and dispatches them
//		as appropriate.
{
    kern_return_t	result;
    msg_header_t	msg, *msgPtr = &msg;
    port_t		kbdPort = [_kbdObject interruptPort];
    
    [_kbdObject setAlphaLockFeedback:FALSE];	// Set state of keys

    //
    // Main loop. Wait for incoming messages, dispatch as appropriate.
    //
    while (TRUE) {
	msgPtr->msg_size = sizeof(msg);
	msgPtr->msg_local_port = _kbdObject->portSet;
	
	result = msg_receive(msgPtr, MSG_OPTION_NONE, 0);
	if (result != RCV_SUCCESS) {
	    IOLog("kbdThread: msg_receive() returned %d\n", result);
	    continue;
	}
        if (msgPtr->msg_id == IO_DEVICE_INTERRUPT_MSG)
	{
	    if (msgPtr->msg_local_port ==  kbdPort)
	        [_kbdObject interruptHandler];   
	    else if (msgPtr->msg_local_port ==  _kbdObject->mouseIntPort)
	        [_kbdObject->mouseObject interruptHandler];   
	    else
		IOLog("kbdThread: Bogus msg_local_port\n");
	}
    }
}

- (BOOL)kbdInit:deviceDescription
// Description:	Initialize the keyboard object. Returns NO on error.
{
    kern_return_t krtn;
    IOReturn drtn;
    IOThread thread;
    id configTable;
    char *keyStr;

    pendingAck = NOT_WAITING;	// Set up initial state
    lastSent = 0;
    pendingLEDVal = 0;
    extendCount = 0;
    mouseObject = 0;
    _owner = nil;
    _desiredOwner = nil;
    _ownerLock = [NXLock new];

    // Obtain keyboard interface and hander IDs from config table.
    configTable = [[self deviceDescription] configTable];
    if (configTable == nil) {
	IOLog("PS2Keyboard kbdInit: no configuration table\n");
	return NO;
    }
    keyStr = (char *)[configTable valueForStringKey:"Interface"];
    if (keyStr == NULL) {
	IOLog("PS2Keyboard kbdInit: no Interface ID; use default\n");
	interfaceId = NX_EVS_DEVICE_INTERFACE_ACE;
    } else
	interfaceId = PCPatoi(keyStr);
    keyStr = (char *)[configTable valueForStringKey:"Handler ID"];
    if (keyStr == NULL) {
	IOLog("PS2Keyboard kbdInit: no Handler ID; use default\n");
	handlerId = 0;
    } else
	handlerId = PCPatoi(keyStr);

    // Initialize the controller itself.
    InitKbdController();

    // We create this portSet to contain the interrupt port for
    // the keyboard and the interrupt port for the mouse. When the
    // mouse object is created, it will call us and provide it's
    // interruptPort to add to the portSet.
    [self enableAllInterrupts];
    krtn = port_set_allocate(task_self(), &portSet);
    if(krtn) {
	IOLog("kbdInit: port_set_allocate returned %d\n", krtn);
	return NO;
    }
    krtn = port_set_add(task_self(), portSet, [self interruptPort]);
    if(krtn) {
	IOLog("kbdInit: port_set_add returned %d\n", krtn);
	return(-1);
    }

    thread = IOForkThread((IOThreadFunc)kbdThread, self);
    (void) IOSetThreadPolicy(thread, POLICY_FIXEDPRI);
    (void) IOSetThreadPriority(thread, 28);	/* XXX */
    
    return YES;	
}
//
// END:		Implementation of internal functions and methods
//


//
// BEGIN:	EXPORTED PS2Keyboard methods
//
+ (BOOL)probe:deviceDescription
// Description:	Bring a new instance into existance.
{
    PS2Keyboard *inst;
    
    //
    // Create an instance and initialize some basic instance variables.
    //
    inst = [[self alloc] initFromDeviceDescription:deviceDescription];

    [inst setUnit: 0];
    [inst setName:"PCKeyboard0"];
    [inst setDeviceKind:"PS2Keyboard"];

    //
    // Proceed with initialization.
    //
    if ([inst kbdInit:deviceDescription] == NO) {
	IOLog("PS2Keyboard probe: kdbInit failed\n");
	[inst free];
	return NO;
    }
    else
	[inst registerDevice];

    
    _kbdObj = inst;
    return YES;
}

- (BOOL) getHandler:(IOEISAInterruptHandler *)handler
              level:(unsigned int *) ipl
	   argument:(unsigned int *) arg
       forInterrupt:(unsigned int) localInterrupt
{
    *handler = KbdIntHandler;
    *ipl = 3;
    *arg = 0xdeadbeef;
    return YES;
}

- free
// Description:	Frees an instance.
{
    [_ownerLock free];
    port_set_deallocate(task_self(), portSet);
    return [super free];
}

- (int)attachMouse:mouseObj withPort:(port_t)mousePort
// Description:	Add the given port to the port set that we use to listen
//		for interrupt messages. When we get a message on the mousePort,
//		we dispatch it to mouseObj. This routine also initializes the
//		mouse related parts of the controller.
{
    unsigned char cmdByte;		// keyboard command byte
    kern_return_t krtn;
    unsigned char i;
    
    mouseObject = mouseObj;
    mouseIntPort = mousePort;
    krtn = port_set_add(task_self(), portSet, mouseIntPort);
    if (krtn) {
	IOLog("attachMouse: port_set_add returned %d\n", krtn);
	return(-1);
    }

    _manualDataHandling = YES;	// We'll be reading data manually
    /* get rid of any garbage in output buffer */
    if (inb(K_STATUS) & (K_OBUF_FUL | M_OBUF_FUL)) {
	IODelay(K_DATA_DELAY);
	(void)inb(K_RDWR);
    }
    
    SendCmd(KC_CMD_READ);	// ask for the ctlr command byte
    cmdByte = GetData();
    cmdByte &= ~(K_CB_DISBLE | M_CB_DISBLE);  // enable mouse & kbd devs
    cmdByte |= (K_CB_ENBLIRQ | M_CB_ENBLIRQ); // enable mouse & kbd IRQs
    SendCmd(KC_CMD_WRITE);	// Write new ctlr command byte
    SendData(cmdByte);

    SendCmd(KC_CMD_MOUSE);	// Send next data to mouse
    SendData(M_CMD_SAMPLING);	// Set mouse sampling rate
    SendCmd(KC_CMD_MOUSE);	// Send next data to mouse
    SendData(5);		// Set sampling index to 100 reports/second
    (void)GetData();		// Eat the ack

    SendCmd(KC_CMD_MOUSE);	// Send next data to mouse
    SendData(M_CMD_SETRES);	// Set mouse resolution
    SendCmd(KC_CMD_MOUSE);	// Send next data to mouse
    SendData(0x1);		// Set resolution index to 2 counts/mm
    (void)GetData();		// Eat the ack

    SendCmd(KC_CMD_MOUSE);	// Send next data to mouse
    SendData(M_CMD_POLL);	// Enable mouse data transmission
    (void)GetData();			// Eat the ack
    
    _manualDataHandling = NO;	// We're done reading data manually

    return(0);
}

- (void)detachMouse
// Description:	Don't listen on the mouse port any longer.
{
	if (mouseObject == 0)
		return;
	// TO DO: delete the mouseIntPort from the portSet;
	return;
}

- (int)interfaceId
// Description:       return keyboard interface ID.
{
    return interfaceId;
}

- (int)handlerId
// Description:       return keyboard handler ID.
{
    return handlerId;
}

- (void)setAlphaLockFeedback:(BOOL)locked
// Description:	Set the keyboard LEDs to indicate the state of alpha lock
{
	BeginSettingLEDs(locked ? K_LED_CAPSLK : 0);
}

PCKeyboardEvent *StealKeyEvent()
// Description:	Call this routine to steal a keyboard event directly
//		from the hardware. Must be called from interrupt level.
{
    unsigned char key;
    PCKeyboardEvent *event;

    // Toss mouse events.
    // FIX: This is going to confuse the mouse driver no end!!!
    if ((unsigned char)inb(K_STATUS) & M_OBUF_FUL) {
	IODelay(K_DATA_DELAY);
	(void)inb(K_RDWR);
    }

    // If there's nothing there, just return
    if (!((unsigned char)inb(K_STATUS) & K_OBUF_FUL))
	return (PCKeyboardEvent *)0;
    
    IODelay(K_DATA_DELAY);
    key = (unsigned char)inb(K_RDWR);	// Read data from keyboard

    if (key == K_ACKSC) {
	HandleAck();	// Respond to ack of previous request
	return (PCKeyboardEvent *)0;
    }
    else if (key == K_RESEND) {
	DoResend();	// Resend previous data
	return (PCKeyboardEvent *)0;
    }

    event = ScancodeToKeyEvent(key, &_kbdObj->extendCount);

    return event;
}
//
// END:		EXPORTED PS2Keyboard methods
//

//
// BEGIN:	Implementation of the PCKeyboardExported protocol
//
- (IOReturn)becomeOwner		: client;
// Description:	Register for event dispatch via device-specific protocol.
//		(See the dispatchKeyboardEvent method)
//		Returns IO_R_SUCCESS if successful, else IO_R_BUSY. The
//		relinquishOwnershipRequest: method may be called on another
//		client during the execution of this method.
{
    IOReturn rtn;
    
    [_ownerLock lock];
    if(_owner != nil) {
	if([_owner respondsTo:@selector(relinquishOwnershipRequest:)])
	{
	    rtn = [_owner relinquishOwnershipRequest:self];
	}
	else {
	    IOLog("%s: owner %s does not respond to "
		    "relinquishOwnershipRequest:\n",
		    [self name], [_owner name]);
	    rtn = IO_R_BUSY;
	}

	if(rtn == IO_R_SUCCESS) {
	    _owner = client;
	}
	else {
	    // NEGOTIATION FAILED
	}
    }
    else {
	_owner = client;
	rtn = IO_R_SUCCESS;
    }
    [_ownerLock unlock];
    return rtn;
}

- (IOReturn)relinquishOwnership	: client;
// Description:	Relinquish ownership. Returns IO_R_BUSY if caller is not
//		current owner.
{
    IOReturn rtn;
    
    [_ownerLock lock];
    if(_owner == client) {
	rtn = IO_R_SUCCESS;
	_owner = nil;
    }
    else {
	rtn = IO_R_BUSY;
    }
    [_ownerLock unlock];
    if((rtn == IO_R_SUCCESS) && 
	_desiredOwner &&
	(_desiredOwner != client)) {
	    /*
	     * Notify this potential client that it can take over.
	     * We'll most likely be called back during this method,
	     * which is why we released _ownerLock.
	     */
	    if([_desiredOwner respondsTo:@selector(canBecomeOwner:)]) {
		[_desiredOwner canBecomeOwner:self];
	    }
	    else {
		IOLog("%s: desiredOwner does not respond to "
			"canBecomeOwner:\n", [self name]);
	    }
    }
    return rtn;
}


- (IOReturn)desireOwnership	: client;
// Description:	Request notification (via canBecomeOwner:) when
//		relinquishOwnership: is called. This allows one potential
//		client to place itself "next in line" for ownership. The
//		queue is only one deep.
{
    IOReturn rtn;
    
    [_ownerLock lock];
    if(_desiredOwner && (_desiredOwner != client)) {
	rtn = IO_R_BUSY;
    }
    else {
	_desiredOwner = client;
	rtn = IO_R_SUCCESS;
    }
    [_ownerLock unlock];
    return rtn;	
}
//
// END:		Implementation of the PCKeyboardExported protocol
//
@end

kdreboot()
// Description:	Sends a "magic" sequence to the keyboard controller
//		which causes it to send a signal back to the system which
//		causes the system to reboot.
// TO DO:	Export this in the interface
{
    // Wait for room in the buffer
    while (inb(K_STATUS) & K_IBUF_FUL)
	continue;
    outb(K_CMD, KC_REBOOT);	// Send the command
    return;
}