Source to bsd/dev/ppc/EventSrcPCKeyboard.m


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

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @[email protected]
 * 
 * "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."
 * 
 * @[email protected]
 */

/* 	Copyright (c) 1992 NeXT Computer, Inc.  All rights reserved. 
 *
 * EventSrcPCKeyoard.m - PC Keyboard EventSrc subclass implementation
 *
 * HISTORY
 * 10 Sep 1992    Joe Pasqua
 *      Created. 
 * 5 Nov, 1993
 *	Modified for Hppa
 * 15 Dec 1993
 *	Modified for 712
 * 23 Mar 1997
 *	Modified for PPC
 */

// TO DO:
//
// NOTES:
// * We override methods in the EventSrcExported protocol so that we can
//   intercept messages used to control who owns us. This gives us
//   an opportunity to acquire/release the PCKeyboard device we want to own.
// * In the current system the EventDriver instance is always the owner
//   of this EventSrc. In the future the km driver and the EventDriver might
//   both be potential owners.
// * The device dependent portion of the event flags are set by this module
//   before the flags are passed up to our owner. There are a couple of notes
//   on this. First, the PC keyboard has no key labelled "Command", therefore
//   the NX_NEXT[RL]CMDKEYMASK flag bits will never be set. Conversely, the
//   PC keyboard has both a left and a right Control key. When either key is
//   pressed we set the NX_NEXTCTLKEYMASK mask. The code processing events
//   must determine which one it was by looking at the code that is generated.
// * 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.
//

#import <driverkit/generalFuncs.h>
#import <machkit/NXLock.h>
#import <driverkit/KeyMap.h>
#import <bsd/dev/evsio.h>
#import <bsd/dev/ppc/event.h>
#import <bsd/dev/ppc/PPCKeymap.c>	// The default keymapping string
#import <bsd/dev/ppc/EventSrcPCKeyboard.h> 
#import <bsd/dev/ppc/PCKeyboardDefs.h>
#import <bsd/dev/ppc/PPCKeyboardPriv.h>

// Private variables that are global to the module:
static EventSrcPCKeyboard *instance = (EventSrcPCKeyboard *)0;

// Forward declarations
static void autoRepeatCallout( void *data );
unsigned int wserver_on=0;

//Make sure USB exists
id usb_UIM_object = 0;

// FIXME: Nasty hack to reinit mouse on server startup
#include "PPCMouse.h"
extern PPCMouse *_my_Mouse;

@implementation EventSrcPCKeyboard

//
// BEGIN:	Utility Procedures
//
static void nsecs_to_packed_ns(ns_time_t *nsecs, unsigned int *pnsecs)
{
    _NX_packed_time_t data;
    int i;

    data.tval = *nsecs;	// nsecs to ns_time_t
    for ( i = 0; i < EVS_PACKED_TIME_SIZE; ++i )
	pnsecs[i] = data.itval[i];
}

static void packed_nsecs_to_nsecs(unsigned int *pnsecs, ns_time_t *nsecs)
{
    _NX_packed_time_t data;
    int i;

    for ( i = 0; i < EVS_PACKED_TIME_SIZE; ++i )
	data.itval[i] = pnsecs[i];
    *nsecs = data.tval;
}
//
// END:	Utility Procedures
//


//
// BEGIN:	Implementation of Private EventSrcPCKeyoard methods
//
- resetKeyboard
// Description:	Reset the keymapping to the default value and reconfigure
//		the keyboards.
{
    [deviceLock lock];
    if ( keyMap != nil )
	[keyMap free];
    // Set up default keymapping.

    keyMap = [[KeyMap alloc] initFromKeyMapping:PPCDefaultKeymap
	length:sizeof PPCDefaultKeymap
	canFree:NO];

    [keyMap setDelegate:self];
    keyRepeat = EV_DEFAULTKEYREPEAT;
    initialKeyRepeat = EV_DEFAULTINITIALREPEAT;
    [deviceLock unlock];
    return self;
}

- scheduleAutoRepeat
// Description:	Schedule a procedure to be called when a timeout has expired
//		so that we can generate a repeated key.
// Preconditions:
// *	deviceLock should be held on entry
{
    if ( calloutPending == YES )
    {
	ns_untimeout( (func)autoRepeatCallout, (void *)self );
	calloutPending = NO;
    }
    if ( downRepeatTime > 0ULL )
    {
	ns_abstimeout(
	    (func)autoRepeatCallout,
	    (void *)self,
	    downRepeatTime,
	    CALLOUT_PRI_THREAD );
	calloutPending = YES;
    }
    return self;
}

- autoRepeat
// Description:	Repeat the currently pressed key and schedule ourselves
//		to be called again after another interval elapses. 
// Preconditions:
// *	Should only be executed on callout thread
// *	deviceLock should be unlocked on entry.
{
    ns_time_t ns;
    
    IOGetTimestamp(&ns);
    [deviceLock lock];
    if ( calloutPending == NO )
    {
	[deviceLock unlock];
	return self;
    }
    calloutPending = NO;
    isRepeat = YES;
    
    if ( downRepeatTime > 0ULL && downRepeatTime <= ns )
    {
	// Device is due to generate a repeat
	lastEventTime = ns;
	[keyMap doKeyboardEvent:codeToRepeat
	    direction:YES
	    keyBits:keyState];
	downRepeatTime += keyRepeat;
    }

    isRepeat = NO;
    [self scheduleAutoRepeat];
    [deviceLock unlock];
    return self;
}

static void autoRepeatCallout( void *data )
// Description:	Callout used to enter Obj-C autoRepeat code from NS timer
//		callout. This proc was set as a callback in scheduleAutoRepeat.
{
    EventSrcPCKeyboard *kbd = (EventSrcPCKeyboard *)data;
    [kbd autoRepeat];
}


- setRepeat:(unsigned)eventType forCode:(unsigned)keyCode
// Description:	Set up or tear dow key repeat operations. The method
//		that locks deviceLock is a bit higher on the call stack.
//		This method is invoked as a side effect of our own
//		invocation of [keyMap doKeyboardEvent].
// Preconditions:
// *	deviceLock should be held upon entry.
{
    if ( isRepeat == NO )  // make sure we're not already repeating
    {
	if (eventType == NX_KEYDOWN)	// Start repeat
	{
	    // Set this key to repeat (push out last key if present)
	    downRepeatTime = initialKeyRepeat + lastEventTime; 
	    codeToRepeat = keyCode;
	    // reschedule key repeat event here
	    [self scheduleAutoRepeat];
	}
	else if (eventType == NX_KEYUP)	// End repeat
	{
	    /* Remove from downKey */
	    if (codeToRepeat == keyCode)
	    {
		downRepeatTime = 0ULL;
		codeToRepeat = -1;
		[self scheduleAutoRepeat];
	    }
	}
    }
    return self;
}

- initKeyboard
// Description:	Perform setup work needed to find and configure our
//		PCKeyboard device. We have to tell it to send events to us.
{
    IOReturn drtn;
    

    drtn = IOGetObjectForDeviceName("PPCKeyboard0", &kbdDevice);
    if(drtn) {
	IOLog("initKeyboard: Can't find PPCKeyboard0 (%s)\n",
	    [self stringFromReturn:drtn]);
	return nil;
    }
    return self;
}
//
// END:	Implementation of Private EventSrcPCKeyoard methods
//


//
// BEGIN:	Implementation of the KeyMapDelegate protocol
//
- keyboardEvent:(int)eventType
    flags:(unsigned)flags
    keyCode:(unsigned)keyCode
    charCode:(unsigned)charCode
    charSet:(unsigned)charSet
    originalCharCode:(unsigned)origCharCode
    originalCharSet:(unsigned)origCharSet
// Description: We use this notification to set up our keyRepeat timer
//		and to pass along the event to our owner. In the current
//		implementation this is always the EventDriver. In the future,
//		the km driver may be another potential owner. This method
//		will be called while the KeyMap object is processing
//		the key code we've sent it using doKeyboardEvent.
{
    [[self owner] keyboardEvent	:eventType
	flags:(flags | deviceDependentFlags)
	keyCode:keyCode
	charCode:charCode
	charSet:charSet
	originalCharCode:origCharCode
	originalCharSet:origCharSet
	repeat:isRepeat
	atTime:lastEventTime];

    // Set up key repeat operations here.
    return [self setRepeat:eventType forCode:keyCode];
}

- keyboardSpecialEvent:(unsigned)eventType
	flags:(unsigned)flags
	keyCode:(unsigned)keyCode
	specialty:(unsigned)flavor
// Description: See the description for keyboardEvent.
{
    [[self owner] keyboardSpecialEvent:eventType
	flags:(flags | deviceDependentFlags)
	keyCode	:keyCode
	specialty:flavor
	atTime:lastEventTime];

    // Set up key repeat operations here.
    if (flavor != NX_KEYTYPE_CAPS_LOCK)
	return [self setRepeat:eventType forCode:keyCode];
}

- updateEventFlags:(unsigned)flags
// Description:	Process non-event-generating flag changes. Simply pass this
//		along to our owner.
{
    return [[self owner] updateEventFlags:flags];
}

- (unsigned)eventFlags
// Description:	Return global event flags In this world, there is only
//		one keyboard device so device flags == global flags.
{
    return eventFlags;
}

- (unsigned)deviceFlags
// Description: Return per-device event flags. In this world, there is only
//		one keyboard device so device flags == global flags.
{
    return eventFlags;
}

- setDeviceFlags:(unsigned)flags
// Description: Set device event flags. In this world, there is only
//		one keyboard device so device flags == global flags.
{
    eventFlags = flags;
    return self;
}

- (BOOL)alphaLock
// Description: Return current alpha-lock state. This is a state tracking
//		callback used by the KeyMap object.
{
    return alphaLock;
}

- setAlphaLock:(BOOL)val
// Description: Set current alpha-lock state This is a state tracking
//		callback used by the KeyMap object.
{
    alphaLock = val;
    [kbdDevice setAlphaLockFeedback:val];
    return self;
}

- (BOOL)charKeyActive
// Description: Return YES If a character generating key down This is a state
//		tracking callback used by the KeyMap object.
{
    return charKeyActive;
}

- setCharKeyActive:(BOOL)val
// Description: Note whether a char generating key is down. This is a state
//		tracking callback used by the KeyMap object.
{
    charKeyActive = val;
    return self;
}
//
// END:		Implementation of the KeyMapDelegate protocol
//


//
// BEGIN:	Implementation of PCKeyboardImported protocol
//
- (void)dispatchKeyboardEvent:(PCKeyboardEvent *)event
// Description:	This method is the heart of event dispatching. The underlying
//		PCKeyboard object invokes this method with each event. We then
//		get the event xlated and dispatched using a keyMap instance.
//		The event structure passed in by reference should not be freed.
{
    unsigned keyMask;

    lastEventTime = event->timeStamp;

    switch (event->keyCode) {
	case ADBK_SHIFT:     keyMask = NX_NEXTLSHIFTKEYMASK; break;
	case ADBK_SHIFT_R:   keyMask = NX_NEXTRSHIFTKEYMASK; break;
	case ADBK_OPTION:    keyMask = NX_NEXTLALTKEYMASK; break;
	case ADBK_OPTION_R:  keyMask = NX_NEXTRALTKEYMASK; break;
	case ADBK_CONTROL:   keyMask = NX_NEXTCTLKEYMASK; break;
	case ADBK_CONTROL_R: keyMask = NX_NEXTCTLKEYMASK; break;
	default: 	     keyMask = 0; break;
    }

    if (event->goingDown)
	deviceDependentFlags |= keyMask;
    else
	deviceDependentFlags &= ~keyMask;

    [deviceLock lock];

    if( (event->keyCode == ADBK_CAPSLOCK)) {			// Send a capslock toggle
	if( event->goingDown != alphaLock ) {
	    [keyMap doKeyboardEvent:ADBK_CAPSLOCK
		direction:YES
		keyBits:keyState];
	    [keyMap doKeyboardEvent:ADBK_CAPSLOCK
		direction:NO
		keyBits:keyState];
	}
    } else {
	[keyMap doKeyboardEvent:event->keyCode
	    direction:event->goingDown
	    keyBits:keyState];
    }
    [deviceLock unlock];
}

- (IOReturn)relinquishOwnershipRequest	: device
{
    IOReturn r;

    [[self ownerLock] lock];
    if ( [self owner] == nil )
    {
	ownDevice = NO;
	r = IO_R_SUCCESS;
    }
    else
	r = IO_R_BUSY;
    
    [[self ownerLock] unlock];
    return r; 
}

- (IOReturn)canBecomeOwner : device
{
    IOReturn drtn;
    drtn = [device becomeOwner: self];
    if(drtn)
    {
	IOLog("%s: becomeOwner of %s failed (%s)\n",
	    [self name],
	    [device name],
	    [self stringFromReturn:drtn]);
    }
    else
    {
	ownDevice = YES;
    }
    return( drtn);
}
//
// END:		Implementation of PCKeyboardImported protocol
//


//
// BEGIN:	Implementation of Exported EventSrcPCKeyoard methods
//
- init
// Description:	Initialize an EventSrcPCKeyboard
{
    [deviceLock lock];
    [super init];
    if ( keyMap != nil )
	[keyMap free];
    // Set up default keymapping

    keyMap = [[KeyMap alloc] initFromKeyMapping:PPCDefaultKeymap
	length:sizeof PPCDefaultKeymap
	canFree:NO];

    [keyMap setDelegate:self];
    [self initKeyboard];
    keyRepeat = EV_DEFAULTKEYREPEAT;
    initialKeyRepeat = EV_DEFAULTINITIALREPEAT;
    [deviceLock unlock];
    return self;
}

+ probe
// Description:	This is our factory method. It is the IODevice probe
//		routine for psuedo drivers.
{
    if ( instance != nil )
	return instance;

    instance = [self alloc];

    instance->deviceLock = [NXLock new];
    [instance setName:"EventSrcPCKeyboard0"];
    [instance setDeviceKind:"EventSrcPCKeyboard"];
    
    if ( [instance init] == nil )
	[instance free];	// No devices.  Go away
    return instance;
}

- free
// Description:	Go Away. Be careful when freeing the lock.
{
    id lock;
    
    lock = deviceLock;
    [lock lock];
    instance = nil;
    deviceLock = nil;
    if ( keyMap != nil )
	[keyMap free];
    // Release keyboard device, so we won't get any more events
    if ( kbdDevice != nil )
	[kbdDevice relinquishOwnership:self];
    [lock unlock];
    [lock free];
    return [super free];
}

- (IOReturn)getIntValues:(unsigned *)parameterArray
	forParameter:(IOParameterName)parameterName
	count:(unsigned int *)count
{
    IOReturn r = IO_R_INVALID_ARG;
    NXEventSystemDevice *dp;
    unsigned maxCount = *count;
    unsigned *returnedCount = count;

    if ( strcmp( parameterName, EVSIOCKR ) == 0 )	// Current Key Repeat
    {
	if ( maxCount >= EVSIOCKR_SIZE )
	{
	    *returnedCount = EVSIOCKR_SIZE;
	    [deviceLock lock];
	    nsecs_to_packed_ns(
	        &initialKeyRepeat, &parameterArray[EVSIOCKR_INITIAL]);
	    nsecs_to_packed_ns(
	        &keyRepeat, &parameterArray[EVSIOCKR_BETWEEN]);
	    [deviceLock unlock];
	    r = IO_R_SUCCESS;
	}
    }
    else if ( strcmp( parameterName, EVSIOCKML ) == 0 )	// Current Keymap len.
    {
	if ( maxCount >= EVSIOCKML_SIZE )
	{
	    *returnedCount = EVSIOCKML_SIZE;
	    [deviceLock lock];
	    if ( keyMap == nil )
		parameterArray[0] = 0;
	    else
		parameterArray[0] = [keyMap keyMappingLength];
	    [deviceLock unlock];
	    r = IO_R_SUCCESS;
	}
    }
    else if ( strcmp( parameterName, EVSIOINFO ) == 0 )	// Device info
    {	
	dp = (NXEventSystemDevice *)&parameterArray[0];
	*returnedCount = 0;
	// No need to lock device since we're not even reading the structure.
	dp->interface = [kbdDevice interfaceId]; 
	dp->dev_type = NX_EVS_DEVICE_TYPE_KEYBOARD;
	dp->interface_addr = 0;
	dp->id = [kbdDevice handlerId];  //Right now ID is for ADB keyboard
//This line shows up very late in bootup, around time LOGIN is happening on screen
//   I think this may be the code that maps international key maps.  A.W. 12/21/98
//   Now I will call USB method to retrieve USB keyboard attached, if any.  The
//   USB method will return the same 16 + ADB ID that is in the various .keyboard
//   files in /System/Library/Keyboards/*
	
	if (usb_UIM_object != 0)
	{	
		int	adb_id;

		adb_id = [usb_UIM_object    getADBKeyboardID];
		if (adb_id != 0)
		{
			dp->id = adb_id;	//converted from USB land
			//kprintf("ADB: keyboard converted from USB is %d decimal\n", adb_id);
		}
	}


	*returnedCount = sizeof(NXEventSystemDevice) / sizeof(int);
	r = IO_R_SUCCESS;
    }
    else if ( strcmp( parameterName, EVSIOGKEYS ) == 0 )	// Key state
    {
	if( maxCount == EVK_NUNITS) {
	    [deviceLock lock];
	    bcopy( &keyState, parameterArray, EVK_NUNITS * sizeof( int));
	    [deviceLock unlock];
	    r = IO_R_SUCCESS;
	}
    }
    else
    {
	r = [super getIntValues:parameterArray
			forParameter:parameterName
			count:count];
	if (r == IO_R_UNSUPPORTED)
	    r = IO_R_INVALID_ARG;
    }
    return r;
}

- (IOReturn)getCharValues:(unsigned char *)parameterArray
	forParameter:(IOParameterName)parameterName
	count:(unsigned int *)count
{
    IOReturn r = IO_R_INVALID_ARG;
    int cnt;
    unsigned maxCount = *count;
    unsigned *returnedCount = count;

    if ( strcmp( parameterName, EVSIOCKM ) == 0 )	// Current KeyMap
    {
	[deviceLock lock];
	if ( keyMap != nil )
	{
	    cnt = [keyMap keyMappingLength];
	    cnt = (cnt < maxCount) ? cnt : maxCount;
	    *returnedCount = cnt;
	    bcopy( [keyMap keyMapping:(int *)0], parameterArray, cnt );
	    r = IO_R_SUCCESS;
	}
	else
	    r = IO_R_NOT_ATTACHED;
	[deviceLock unlock];
    }
    else
    {
	r = [super getCharValues:parameterArray
			forParameter:parameterName
			count:count];
	if (r == IO_R_UNSUPPORTED)
	    r = IO_R_INVALID_ARG;
    }
    return r;
}
			
- (IOReturn)setIntValues:(unsigned *)parameterArray
	forParameter:(IOParameterName)parameterName
	count:(unsigned int)count
{
    IOReturn r = IO_R_INVALID_ARG;

    if ( strcmp( parameterName, EVSIOSKR ) == 0 )	// Set Key Repeat
    {
	if ( count == EVSIOSKR_SIZE )
	{
	    [deviceLock lock];
	    packed_nsecs_to_nsecs(parameterArray, &keyRepeat);
	    if ( keyRepeat < EV_MINKEYREPEAT )
		    keyRepeat = EV_MINKEYREPEAT;
	    [deviceLock unlock];
	    r = IO_R_SUCCESS;
	}
    }
    else if ( strcmp( parameterName, EVSIOSIKR ) == 0 )	// Set Init Key Repeat
    {
	if ( count == EVSIOSIKR_SIZE )
	{
	    [deviceLock lock];
	    packed_nsecs_to_nsecs(parameterArray, &initialKeyRepeat);
	    if ( initialKeyRepeat < EV_MINKEYREPEAT )
		    initialKeyRepeat = EV_MINKEYREPEAT;
	    [deviceLock unlock];
	    r = IO_R_SUCCESS;
	}
    }
    else if ( strcmp( parameterName, EVSIORKBD ) == 0 )	// Reset keyboard
    {
	[self resetKeyboard];
	r = IO_R_SUCCESS;
    }
    else
    {
	r = [super setIntValues:parameterArray
		   forParameter:parameterName
		   count : count];
	if (r == IO_R_UNSUPPORTED)
	    r = IO_R_INVALID_ARG;
    }
    return r;
}

- (IOReturn)setCharValues:(unsigned char *)parameterArray
	forParameter:(IOParameterName)parameterName
	count:(unsigned int)count
{
    IOReturn r = IO_R_INVALID_ARG;
    unsigned char *map;
    id oldMap;

    if ( strcmp( parameterName, EVSIOSKM ) == 0 )	// Set KeyMap
    {
	map = (unsigned char *)IOMalloc( count );
	bcopy( parameterArray, map, count );
	[deviceLock lock];
	oldMap = keyMap;
	keyMap = [[KeyMap alloc] initFromKeyMapping:map
	    length:count
	    canFree:YES];
	if ( keyMap != nil )
	{
	    if ( oldMap != nil )
		[oldMap free];
	    [keyMap setDelegate:self];
	    r = IO_R_SUCCESS;
	}
	else
	{
	    keyMap = oldMap;
	    r = IO_R_INVALID_ARG;
	} 
	[deviceLock unlock];
    }
    else
    {
	r = [super setCharValues:parameterArray
		   forParameter:parameterName
		   count : count];
	if (r == IO_R_UNSUPPORTED)
	    r = IO_R_INVALID_ARG;
    }
    return r;
}
//
// END:		Implementation of Exported EventSrcPCKeyoard methods
//



//
// BEGIN:	Implementation of EventSrcExported protocol
//
//
- (IOReturn)relinquishOwnership:(id)client
{
	IOReturn r;

	// kill autorepeat task
	downRepeatTime = 0ULL;
	codeToRepeat = -1;
	[self scheduleAutoRepeat];
	// clear modifiers to avoid stuck keys
	[self setAlphaLock:NO];
	[[self owner] updateEventFlags:0];
	deviceDependentFlags = 0;
	eventFlags = 0;
	bzero( keyState, sizeof( kbdBitVector));

	r = [super relinquishOwnership:client];
	[[self ownerLock] lock];
	// If nobody took over this EventSrc, release attached keyboards
	wserver_on = 0;
	if ( r == IO_R_SUCCESS && [self owner] == nil )
	{
	    if ( [kbdDevice relinquishOwnership:self] == IO_R_SUCCESS )
		ownDevice = NO;
	}
	[[self ownerLock] unlock];
	return r;
}

- (IOReturn) becomeOwner:(id)client
{
	IOReturn r;

	r = [super becomeOwner:client];
	[[self ownerLock] lock];
	if ( r == IO_R_SUCCESS && ownDevice == NO )
	{
	    if ( [kbdDevice becomeOwner:self] == IO_R_SUCCESS )
	    {
		ownDevice = YES;
		wserver_on = 1;
// FIXME: Nasty hack to reinit mouse on server startup
		[_my_Mouse mouseInit];
	    }
	    else if ( [kbdDevice desireOwnership:self] != IO_R_SUCCESS )
	    {
		[super relinquishOwnership:client];
		r = IO_R_BUSY;
	    }
	}
	[[self ownerLock] unlock];
	return r;
}
//
// END:		Implementation of EventSrcExported protocol
//

@end