Source to bsd/dev/ppc/kmDevice.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. 
 *
 * kmDevice.m - kernel keyboard/monitor module, ObjC implementation.
 *
 * HISTORY
 * 21 Sep 92	Joe Pasqua
 *	Created i386 version based on m88k version.
 */
 
#import <driverkit/generalFuncs.h>
#import <machkit/NXLock.h>
#import <driverkit/ppc/driverTypesPrivate.h>
#import <driverkit/kernelDriver.h>
#import <bsd/sys/types.h>
#import <bsd/sys/tty.h>
#import <bsd/dev/ppc/cons.h>
#import <bsd/sys/conf.h>
#import <bsd/sys/fcntl.h>
#import <bsd/sys/errno.h>
#import <bsd/sys/msgbuf.h>
#import <bsd/dev/ppc/FBConsole.h>
#import <bsd/dev/ppc/SerialConsole.h>
#import <bsd/dev/ppc/kmDevice.h>
#import <bsd/dev/kmreg_com.h>
#import <bsd/sys/reboot.h>
#import <kernserv/prototypes.h>
#import <driverkit/IODisplay.h>
#import <driverkit/IOFrameBufferDisplay.h>
#import <driverkit/IODisplayPrivate.h>
#import <driverkit/IOConfigTable.h>
#import <bsd/dev/ppc/kmWaitCursor.h>
#import <bsd/sys/proc.h>
#import <bsd/sys/systm.h>

#import <kern/assert.h>
#import <machdep/ppc/boot.h>

#import "PCKeyboardDefs.h"
#import "PPCKeyboardPriv.h"
#import "ConsoleSupport.h"
#import "km.h"

IODisplayInfo	bootDisplayInfo;

static IOConsoleInfo *serialConsole;
static int bootGraphicsMode = NO;
static short prettyShutdown = 0;

// Title strings for the various windows
const char *mach_title = "Mac OS X Server Operating System"; // Used by km.m also
static const char *alert_title  = "Alert";


#if	KERNOBJC
// Module Globals
static int kmUnit;	// Incremented to keep track of what unit we're on
static id display;
	// display == nil means that we do not have a display object around
	// yet. In this case we use the basicConsole. When display != nil
	// we can use it's console support mechanism to do our displaying.

// The following function pointers are used by DoAlert and DoRestore. They
// get called at interrupt level and can't execute ObjC code. To avoid doing
// so we bind the lock and unlock methods that are used and call them thru
// these pointers.
static id (*lockProc)(), (*unlockProc)();

#define	AllocConsole()		\
		AllocDisplayConsole(display) 

LANG_TYPE glLanguage;

#else

#define	AllocConsole()		basicConsole

#endif KERNOBJC


static IOConsoleInfo * 
AllocDisplayConsole( id display )
{
IOConsoleInfo * consoleInfo = NULL;

    if( display)
	consoleInfo = [display allocateConsoleInfo];
    if( NULL == consoleInfo)
	consoleInfo = basicConsole;
    return( consoleInfo);
}


// Used for putting up alert panels very early, before kmId is valid.
IOConsoleInfo *kmAlertConsole;

/*
 * Calculate incremented index for inBuf.
 */
static inline int
inbuf_incr(int x)
{
	if(++x == INBUF_SIZE) {
		return 0;
	}
	else {
		return x;
	}
}

/*
 * Initialize the console enough for printf's to work via kmputc. No 
 * text window will be created if booting in icon mode. Keyboard is 
 * not enabled. 
 */

void
initialize_screen(void * args)
{
    extern int initialized;
    Boot_Video * framebufferArgs = (Boot_Video *) args;

    cons.t_dev = makedev(12, 0); /* point the cons device to cdevsw's km dev */

    serialConsole = SerialAllocateConsole();

    if( framebufferArgs->v_baseAddr) {
       /*
	* Initialize the OpenFirmware based framebuffer console.
	*/
	static char * ofPixelEncoding = "PPPPPPPP";

	bzero(&bootDisplayInfo, sizeof(bootDisplayInfo));
	bootDisplayInfo.width			= framebufferArgs->v_width;
	bootDisplayInfo.height			= framebufferArgs->v_height;
	bootDisplayInfo.totalWidth		= framebufferArgs->v_rowBytes;
	bootDisplayInfo.rowBytes		= framebufferArgs->v_rowBytes;
	bootDisplayInfo.refreshRate		= 75;		// very approx
	bootDisplayInfo.frameBuffer		= (void *)framebufferArgs->v_baseAddr;
	bootDisplayInfo.bitsPerPixel		= IO_8BitsPerPixel;
	bootDisplayInfo.colorSpace		= IO_RGBColorSpace;
	strcpy( bootDisplayInfo.pixelEncoding, ofPixelEncoding);
	basicConsole = BasicAllocateConsole(&bootDisplayInfo);
    }
    if( basicConsole == NULL)
	basicConsole = serialConsole;

    if( framebufferArgs->v_display & 1)
	bootGraphicsMode = YES;

    basicConsoleMode = bootGraphicsMode ? SCM_GRAPHIC : SCM_TEXT;
    (*basicConsole->Init)(
	basicConsole, basicConsoleMode, (bootGraphicsMode == NO), (bootGraphicsMode == NO), mach_title);

    initialized = TRUE;
}

#if	KERNOBJC

@implementation kmDevice

- (void)registerDisplay:newDisplay
{
    IOConsoleInfo *	newConsole;

    if (display == nil) {
	display = newDisplay;

	if( NO == probed) {
	    newConsole = AllocDisplayConsole( newDisplay);
	    if( newConsole) {
    		(*newConsole->Init)(
		    newConsole, fbMode, YES, fbMode == SCM_TEXT, mach_title);
                fbp[FB_DEV_NORMAL] = newConsole;
		if( fbMode == SCM_TEXT)
		    kmdumplog();
		else {
                    kmDrawString( newConsole, "Starting Mac OS X Server");
                    [self animationCtl: KM_ANIM_RESUME];
		}
	    }
            newConsole = AllocDisplayConsole( newDisplay);
	    if( newConsole)
                fbp[FB_DEV_ALERT] = newConsole;

	} else {
	    // somebody like the window server has changed modes,
	    // but we still own the display!
	    fbMode = SCM_OTHER;
            [self animationCtl: KM_ANIM_STOP];

            fbp[FB_DEV_NORMAL] = AllocDisplayConsole( newDisplay);
            fbp[FB_DEV_ALERT] = AllocDisplayConsole( newDisplay);
	}
    }
}

- (void)unregisterDisplay:oldDisplay
{
    if (display == oldDisplay) {
        [self animationCtl: KM_ANIM_SUSPEND];
	basicConsole = serialConsole;
        display = nil;

        if( fbp[FB_DEV_NORMAL])
            (*fbp[FB_DEV_NORMAL]->Free) (fbp[FB_DEV_NORMAL]);
        if( fbp[FB_DEV_ALERT])
            (*fbp[FB_DEV_ALERT]->Free) (fbp[FB_DEV_ALERT]);

        fbp[FB_DEV_NORMAL] = serialConsole;
        fbp[FB_DEV_ALERT] = serialConsole;
    }
}


/* Probe routine for a pseudo-device driver. */
+ (BOOL)probe : deviceDescription
{
    // Proceed with keyboard initialization.

    if ([kmId init:YES fb_mode: 
	    bootGraphicsMode ? SCM_GRAPHIC : SCM_TEXT] == nil)
    {
	IOLog("kmDevice: continuing with bad kmDevice instance\n");
	[kmId free];
	kmId = nil;
	return NO;
    } else
	kmId->probed = YES;

    return YES;
}

+ (IODeviceStyle)deviceStyle
{
    return IO_PseudoDevice;
}

/* 
 * Can't put this in the kmDevice object because we might need it
 * before kmId is initialized.
 */
static int kmDeviceAnimationState = -1;
static simple_lock_data_t kmDeviceAnimationLock;

/*
 * Create a kmDevice. This should only be called once in the lifetime of 
 * the system. It can be called from kmDevice's probe:, or from km.m's
 * alert() (if alert is called before IOTask autoconf).
 */
+ new
{
	char 		name[20];
	kmDevice	*km_id;
	int i;
	
        simple_lock_init(&kmDeviceAnimationLock);

	km_id = [self alloc];
	km_id->kmOpenLock = [NXLock new];
	(IMP)lockProc = [km_id->kmOpenLock methodFor:@selector(lock)];
	(IMP)unlockProc = [km_id->kmOpenLock methodFor:@selector(unlock)];
	km_id->kbId = nil;
	
	for(i=0; i<NUM_FB_DEVS; i++) {
		km_id->fbp[i] = NULL;
	}
	
	/*
	 * Global kmId is id of first kmDevice created.
	 */
	if(kmUnit == 0) {
		ASSERT(kmId == nil);
		kmId = km_id;
	}
	
	/*
	 * Special case for this one, it's hard coded - there is always 
	 * at least one of these, and it's created before autoconf.
	 */
        km_id->fbp[FB_DEV_NORMAL] = basicConsole;
        km_id->fbp[FB_DEV_ALERT] = basicConsole;
	[kmId setUnit:kmUnit];
	sprintf(name, "kmDevice%d", kmUnit++);	
	[kmId setName:name];
	[kmId setDeviceKind:"kmDevice"];
	[kmId setLocation:NULL];
	return km_id;
}

/*
 * Reusable initialization method. kmOpenLock must already have been 
 * initialized. initKb enables initialiation of keyboard.
 */
- init : (BOOL)initKb
	 fb_mode : (ScreenMode)fb_mode
{
	id		rtn = self;
	
	[kmOpenLock lock];
	
	/*
	 * Init local instance variables.
	 */
	inDex = outDex 	= 0;
	blockIn 	= 0;
	alertRefCount 	= 0;
	fbMode          = fb_mode;
	simple_lock_init(&inBufLock);
	
	if(initKb) {
		/*
		 * connect to Keyboard.
		 */
		if([self initKb] == nil) {
			IOLog("kmDevice: No Keyboard Found\n");
			/* but continue anyway... */
		}
	}
	[super init];
	if( (!hasRegistered) && initKb) {
		[self registerDevice];
		hasRegistered = YES;
	}

	[kmOpenLock unlock];
	return rtn;
}

- initKb
// Description:	Initialize connection with PCKeyboard object
{
	IOReturn drtn;
	
	drtn = IOGetObjectForDeviceName("PPCKeyboard0", &kbId);
	if(drtn) {
		IOLog("km init: Can't find PPCKeyboard0 (%s)\n",
			[self stringFromReturn:drtn]);
		return nil;
	}
	
	drtn = [kbId becomeOwner:self];
	if(drtn) {
		IOLog("km init: becomeOwner failed (%s)\n",
			[self stringFromReturn:drtn]);
		return nil;
	}
	
	/*
	 * Also register to be notified if anyone else relinquishes
	 * ownership after we give it up. This allows us to take over
	 * the frame buffer and keyboard when the window server exits.
	 */
	drtn = [kbId desireOwnership:self];
	if(drtn) {
		IOLog("km init: desireOwnership failed (%s)\n",
			[self stringFromReturn:drtn]);
		return nil;
	}
	return self;
}

/*
 * Interface to bsd code.
 */
- (int)kmOpen : (int)flag
{
	int rtn = 0;

	[kmOpenLock lock];
	if(flag & ( O_POPUP | O_ALERT)) {
		switch(fbMode) {
		    case SCM_TEXT:
		    	/*
			 * Easy case, we already have a place to do putc()'s. 
			 * Note for SCM_TEXT case, alert text just goes to 
			 * the normal console.
			 */
			break;
		    case SCM_ALERT:
			alertRefCount++;
			break;

		    default:
			/*
			 * Create an alert window.
			 *
			 * We only want to allow this for
			 * programs during system startup (eg.
			 * they must be run as root, and PostScript
			 * must not be running.
			 */
			if(suser(current_proc()->p_ucred,
						&current_proc()->p_acflag)) {
				rtn = EACCES;
				goto out;
			}
			if(fbMode == SCM_OTHER) {
				rtn = EBUSY;
				goto out;
			}

			/*
			 * Dropping to single user mode => O_POPUP,
			 * so make the console text mode.
			 */
			if( flag & O_POPUP) {
			    fbMode = SCM_TEXT;
			    (*fbp[FB_DEV_NORMAL]->Init)(
				    fbp[FB_DEV_NORMAL], SCM_TEXT, TRUE, TRUE, mach_title );

			} else {
			   /*
			    * Jump into "alert mode". Note that alertRefCount
			    * doesn't get decremented in close; the user has
			    * to do an explicit KMIOCRESTORE.
			    */
			    alertRefCount++;
			    savedFbMode = fbMode;
			    fbMode = SCM_ALERT;
			    (*fbp[FB_DEV_ALERT]->Init)(
				    fbp[FB_DEV_ALERT], fbMode, TRUE, TRUE, alert_title );
			}
		}
	}
	
	/*
	 * km.m takes care of Unix-y tty stuff.
	 */
out:
	[kmOpenLock unlock];
	return rtn;
}

- (int)kmPutc : (int)c
{
	switch(fbMode) {
	    case SCM_TEXT:
		(*fbp[FB_DEV_NORMAL]->PutC)(fbp[FB_DEV_NORMAL], c);
		return 0;
		
	    case SCM_ALERT:
	    	ASSERT(fbp[FB_DEV_ALERT] != NULL);
		(*fbp[FB_DEV_ALERT]->PutC)(fbp[FB_DEV_ALERT], c);
		return 0;

	    default:
	    	/*
		 * No place to put this text. This is not an error.
		 */
		return 0;
	}
}

/*
 * Blocking read routine. This assumes that interrupts and threads are 
 * working, since keyboard driver will need that functionality...OK?
 */

- (int)kmGetc
{
	int c;
	
	simple_lock(&inBufLock);
	blockIn = 1;
	while(inDex == outDex) {
		/*
		 * Just wait for something to show up...
		 */
		thread_sleep((void *)inBuf, &inBufLock, TRUE);
		simple_lock(&inBufLock);
	} 
	c = inBuf[outDex];
	outDex = inbuf_incr(outDex);
	blockIn = 0;
	simple_unlock(&inBufLock);
	return c;
}

#endif	//KERNOBJC

/*
 * ioctl equivalents. All return an errno.
 */
 
/*
 * Client finished with alert panel. Called from ioctl and from inside the 
 * kernel via alert_done().
 */
int DoRestore(void)
{
	int rtn = 0;

#if	KERNOBJC
	
	if (kmAlertConsole) {	// No kmDevice yet!
	    (*kmAlertConsole->Restore)(kmAlertConsole);
	    (*kmAlertConsole->Free)(kmAlertConsole);
	    kmAlertConsole = NULL;
	    return 0;
	}
	
	if (kmId == nil)
	    return 0;


	/*
	 * Have to protect with kmOpenLock since we may alter frame buffer
	 * context.
	 */
	(*lockProc)(kmId->kmOpenLock, @selector(lock));
	if(kmId->fbMode == SCM_ALERT) {
		if(kmId->alertRefCount == 0) {
			/*
			 * No can do. I'm not sure when this could 
			 * ever happen...
			 */
			rtn = EBUSY;
			goto out;
		}
		if(--kmId->alertRefCount != 0) {
		
			/*
			 * Not out of the woods yet, other user(s) of alert
			 * panel still active. 
	    		 */
			goto out;
		}
		
		/*
		 * Last user of alert/popup window.
		 */
		kmId->fbMode = kmId->savedFbMode;
		if(kmId->fbMode != SCM_ALERT) {
			rtn = (*kmId->fbp[FB_DEV_ALERT]->Restore)(
			    kmId->fbp[FB_DEV_ALERT]);
		}
		else {
			IOLog("kmDevice: Recursive SCM_ALERT in restore!\n");
		}
		goto out;
	} else {
		/*
		 * Not in alert mode, this is a nop (and it's also somewhat
		 * unexpected...).
		 */
		rtn = EINVAL;
	}
out:
	(*unlockProc)(kmId->kmOpenLock, @selector(unlock));

#endif	// KERNOBJC

	return rtn;
}

#if	KERNOBJC

- (int)restore
{
    return DoRestore();
}

- (int)drawRect : (const struct km_drawrect *)kmRect
{
	struct km_drawrect rect = *kmRect;
	int size, ret;
	
	if(fbMode != SCM_GRAPHIC) {
	    	/*
		 * No can do.
		 */
		return EBUSY;
	}
	/*
	 * Copy in user data so the console DrawRect routine
	 * doesn't have to.
	 */

        if( (rect.x & 3) == 3) {
            size = rect.width * rect.height;		// size in bytes, 8bit src

	} else {
	rect.width = (rect.width + 3) & ~3;	// Round up to 4 pixel boundary
	size = (rect.width >> 2) * rect.height;	// size in bytes
	}
	rect.data.bits = (unsigned char *)kalloc( size );
	if (copyin(kmRect->data.bits, rect.data.bits, size)) {
	    ret = -1;
	    goto out;
	}

	ret = (*fbp[FB_DEV_NORMAL]->DrawRect)(fbp[FB_DEV_NORMAL], &rect);

out:
	kfree( rect.data.bits, size );
	return ret;
}

- (int)eraseRect : (const struct km_drawrect *)kmRect
{
        if(fbMode != SCM_GRAPHIC) {
	    	/*
		 * No can do.
		 */
		return EBUSY;
	}
	return (*fbp[FB_DEV_NORMAL]->EraseRect)(fbp[FB_DEV_NORMAL], kmRect);
}

- (int)disableCons
{
	/*
	 * This means that tty code has detected a "console 
	 * now invalid" situation.
	 */
#if 0
	if(fbMode == SCM_TEXT) {
		fbMode = SCM_OTHER;
	}
#endif
	return 0;
}

#endif	// KERNOBJC

static void
kmDeviceDrawCursor()
{
    IOConsoleInfo *device;
    
    simple_lock(&kmDeviceAnimationLock);

    if (kmDeviceAnimationState > 0) {
	device = kmId->fbp[FB_DEV_NORMAL];
	if (device && (kmId->fbMode == SCM_GRAPHIC)) {
	    (void)(*device->DrawRect)(device,
		&kmDeviceWaitCursor[kmDeviceAnimationState - 1]);
	}
	if (++kmDeviceAnimationState > KM_DEVICE_WAITCURSOR_NSTATES)
	    kmDeviceAnimationState = 1;
	ns_timeout((func)kmDeviceDrawCursor, 0, KM_DEVICE_WAITCURSOR_DELAY, 
			CALLOUT_PRI_THREAD);
    }
    simple_unlock(&kmDeviceAnimationLock);
}

/* Called once and only once from main(),
 */
void
kmEnableAnimation(void)
{
    if( kmId == nil) {
	kmId = [kmDevice new];
	[kmId init:NO fb_mode: 
		bootGraphicsMode ? SCM_GRAPHIC : SCM_TEXT];
    }
    [kmId animationCtl:KM_ANIM_RESUME];
}

void
kmDisableAnimation(void)
{
    if (kmId != nil)
	[kmId animationCtl:KM_ANIM_STOP];
}

#if	KERNOBJC

- (int)animationCtl : (km_anim_ctl_t)ctl
{
	int doAnimation = 0;
	int ret = 0;
	
	if (fbMode != SCM_GRAPHIC)
	    return 0;
	    
	simple_lock(&kmDeviceAnimationLock);
	switch(ctl) {
	    case KM_ANIM_STOP:
	    case KM_ANIM_SUSPEND:
		if (kmDeviceAnimationState > 0) {
		    kmDeviceAnimationState = -kmDeviceAnimationState;
		    (void)(*fbp[FB_DEV_NORMAL]->DrawRect)
			(fbp[FB_DEV_NORMAL], &kmDeviceBlankCursor);
		}
                ns_untimeout((func)kmDeviceDrawCursor, 0);
                if( ctl == KM_ANIM_STOP)
                    kmDeviceAnimationState = 0;
		break;
	    case KM_ANIM_RESUME:
		if (kmDeviceAnimationState < 0) {
		    kmDeviceAnimationState = -kmDeviceAnimationState;
		    doAnimation = 1;
		}
		break;
	    default:
		ret = EINVAL;
		break;
	}
	simple_unlock(&kmDeviceAnimationLock);
	if (doAnimation)
	    kmDeviceDrawCursor();
	return ret;
}

- (int)getStatus : (unsigned *)statusp
{
	unsigned status = KMS_SEE_MSGS;
	
	if(fbMode == SCM_GRAPHIC) {
	    status = 0;
	}
	*statusp = status;
	return 0;
}

- (int)getScreenSize : (struct ConsoleSize *)size
{
	if (fbp[FB_DEV_NORMAL] == 0)
	    return EINVAL;
	
	(*fbp[FB_DEV_NORMAL]->GetSize)(fbp[FB_DEV_NORMAL], size);
	return 0;
}

#endif	KERNOBJC

#import <bsd/dev/ppc/keycodes.h>

int ProcessKbdEvent(PCKeyboardEvent *ke)
{
    // State of the modifier keys:
    static boolean_t	control_left, control_right,
			shift_left, shift_right, caps_lock,
			alt_left, alt_right,
			cmd_left, cmd_right;
    boolean_t valid, shift, control, cmd, c;

    valid = FALSE;
    switch (ke->keyCode) {
	case ADBK_CONTROL:
	    control_left = ke->goingDown;
	    break;

	case ADBK_CONTROL_R:
	    control_right = ke->goingDown;
	    break;
	    
	case ADBK_SHIFT:
	    shift_left = ke->goingDown;
	    break;

	case ADBK_SHIFT_R:
	    shift_right = ke->goingDown;
	    break;

	case ADBK_CAPSLOCK:
	    caps_lock = ke->goingDown;
	    break;
	
	case ADBK_OPTION:
	    alt_left = ke->goingDown;
	    break;
	    
	case ADBK_OPTION_R:
	    alt_right = ke->goingDown;
	    break;

	case ADBK_FLOWER:
	    cmd_left = ke->goingDown;
	    break;

	default:
	    valid = TRUE;
    }

    if (!valid)
        return inv;

    // caps lock is just a pain at this point
    shift = shift_right || shift_left;  // || caps_lock;

    control = (control_left || control_right) ? (_N_KEYCODES * 2) : 0;
    c = keycodeToAscii[((ke->keyCode << 1) | shift) + control];

   cmd = (cmd_right || cmd_left) ? 0x80 : 0;

    switch (c) {
	case dim:
	case bright:
	case loud:
	case quiet:
	case up:
	case down:
	    break;
    
	default:
	    c |= cmd;
	    break;
    }

//printf("{raw:%lx, ascii:%lx}",ke->keyCode,c);

    if (!ke->goingDown)	// Ignore keys coming up
	return(inv);
    return(c);
}

#if	KERNOBJC

- (void)dispatchKeyboardEvent:(PCKeyboardEvent *)event;
// Description:	Called when the keyboard object receives a new keyboard
//		Event. Don't free the event object.
{
    register int c;

    if ((c = ProcessKbdEvent(event)) == inv)
	return;

    // Normal ASCII data. pass it up.
    if(blockIn) {
	    //
	    // We're in 'blocking input' mode; add this to 
	    // the input buffer if there is room.
	    //
	    simple_lock(&inBufLock);
	    if(inbuf_incr(inDex) == outDex) {
		    //
		    // Overflow. Oh well.
		    //
		    simple_unlock(&inBufLock);
		    return;
	    }
	    inBuf[inDex] = c;
	    inDex = inbuf_incr(inDex);
	    thread_wakeup((void *)inBuf);
	    simple_unlock(&inBufLock);
    }
    else {
	    //
	    // Normal console input.
	    //
	    struct tty *tp = &cons;

	    (*linesw[tp->t_line].l_rint) (c, tp);
    }
    return;
}

#else

void
cDispatchKeyboardEvent(PCKeyboardEvent *event)
{
    register int c;
    struct tty *tp = &cons;

    if ((c = ProcessKbdEvent(event)) == inv)
	return;
#if 0
    cnputc(c);
#else
    (*linesw[tp->t_line].l_rint) (c, tp);
#endif

    return;
}

#endif	KERNOBJC

#if	KERNOBJC

/*
 * ev driver (or DPS or ...) is taking over the screen and keyboard. 
 * This should always succeed.
 */
- (IOReturn)relinquishOwnershipRequest : device
{
        [self animationCtl: KM_ANIM_STOP];

	fbMode = SCM_OTHER;
	/*
	 * FIXME - notify parent of SCM_UNINIT.
	 */
	return IO_R_SUCCESS;
}

/*
 * Called when ev/DPS, which has been the owner of Keyboard, relinquishes 
 * ownership. I think this means that we unconditionally go to SCM_TEXT 
 * mode...
 */

static char *k_languages[L_NUM_LANGUAGE] = {
	"English",
	"French",
	"German",
	"Spanish",
	"Italian",
	"Swedish",
	"Japanese", 
	};


- (IOReturn)canBecomeOwner : device
{
	IOReturn drtn;
	static short checkedSystemConfig = 0;
	static short preventPrettyShutdown = 0;
	
	/*
	 * Regain control of keyboard.
	 */
	if((drtn = [kbId becomeOwner:self])) {
	    IOLog("km canBecomeOwner: becomeOwner failed (%s)\n",
		    [self stringFromReturn:drtn]);
	    /*
	     * But continue, although we have no keyboard...
	     */
	}

	if (!checkedSystemConfig) {
	    IOConfigTable	*configTable;
	    const char		*val;
	    
	    checkedSystemConfig = 1;
	    configTable = [IOConfigTable newFromSystemConfig];
	    val = [configTable valueForStringKey:"Shutdown Graphics"];
	    if (val) {
		if (!strcmp(val,"No"))
		    preventPrettyShutdown = 1;
		[IOConfigTable freeString:val];
	    }

	    val = [configTable valueForStringKey:"Language"];
	    if (val) {
		int 		i;
		
		for (i=0; i<L_NUM_LANGUAGE; i++) {
		    if (!strcmp(val, k_languages[i])) {
			glLanguage = i;
			break;
		    }
		}
		[IOConfigTable freeString:val];
	    }

	    [configTable free];
	}

	if (preventPrettyShutdown)
	    prettyShutdown = 0;
	
	if (prettyShutdown)
	    fbMode = SCM_GRAPHIC;
	else
	    fbMode = SCM_TEXT;
	    
	(*fbp[FB_DEV_NORMAL]->Init)(
	    fbp[FB_DEV_NORMAL], fbMode, TRUE, TRUE, mach_title);

        if (prettyShutdown) {
            [self drawGraphicPanel: KM_PANEL_NS];

            switch(prettyShutdown) {
            case 1:
                [self graphicPanelString: "Restarting the computer...\n"];
                break;
            case 2:
                [self graphicPanelString: "Shutting down the computer...\n"];
                break;
            default:
                [self graphicPanelString: "Please wait..."];
	    }

            kmDeviceAnimationState = -1;
            [self animationCtl: KM_ANIM_RESUME];
	}

	return IO_R_SUCCESS;
}

#endif	// KERNOBJC

/*
 * Kernel internal alert panel support.
 */

void DoSafeAlert(const char *windowTitle, const char *msg, boolean_t saveUnder )
{
#if	KERNOBJC
    if (kmId == nil)
#endif
    {
	// We haven't created a kmDevice yet! We can't put up a panel or
	// call ObjC stuff.  Create an alert console.
#if	KERNOBJC
	if (kmAlertConsole)
	    return;
#endif
	// ...unless we already have basicConsole in a text mode.
	if (basicConsoleMode == SCM_ALERT ||
	    basicConsoleMode == SCM_TEXT) {
		if (basicConsole)
		    while (*msg)
			(basicConsole->PutC)(basicConsole, *msg++);
		return;
	}
	kmAlertConsole = basicConsole;
	if (kmAlertConsole) {
	    (*kmAlertConsole->Init)(
		kmAlertConsole, SCM_ALERT, FALSE, TRUE, windowTitle);
	    while(*msg)
		(kmAlertConsole->PutC)(kmAlertConsole, *msg++);
	}
	return;
    }

#if	KERNOBJC
    /* 
     * Create alert panel if necessary
     */
    (*lockProc)(kmId->kmOpenLock, @selector(lock));
    switch(kmId->fbMode) {
	case SCM_TEXT:
	    /*
	     * Easy case, we already have a place to do putc()'s. 
	     * Note for SCM_TEXT case, alert text just goes to 
	     * the normal console.
	     */
	    break;

	/*
	 * FIXME - maybe do a case for SCM_UNINIT, do allow printfs
	 * via alert() very early in boot.
	 */		
	default:
	    /*
	     * Jump into "alert mode". 
	     */

            kmId->savedFbMode = kmId->fbMode;
            kmId->fbMode = SCM_ALERT;

            /*
            * Create an alert window.
            */
            (*kmId->fbp[FB_DEV_ALERT]->Init)(
                kmId->fbp[FB_DEV_ALERT], SCM_ALERT, saveUnder, TRUE, windowTitle);

	    /* fall through */

	case SCM_ALERT:
	    kmId->alertRefCount++;
	    break;
    }
    (*unlockProc)(kmId->kmOpenLock, @selector(unlock));
    
    /*
     * Print alert message.
     */
    {
    IOConsoleInfo *cons;
    
	if (kmId->fbMode == SCM_ALERT)
	    cons = kmId->fbp[FB_DEV_ALERT];
	else
	    cons = kmId->fbp[FB_DEV_NORMAL];
	while(*msg)
	    (*cons->PutC)(cons, *msg++);
    }
#endif	// KERNOBJC

    return;
}

extern int scc_getc(int unit, int line, boolean_t wait, boolean_t raw);
extern PCKeyboardEvent * PPCStealKeyEvent();

// This gets called from interrupt level by kdp

int kmtrygetc()
{
    int c = -1;
    PCKeyboardEvent * event;

    event = PPCStealKeyEvent();
    if(event)
	c = ProcessKbdEvent(event);

    if( (c != -1) && ( c != inv)) {
	return(c);
    } else {
/* modem port is 1, printer port is 0 */
#define LINE	1
	return( scc_getc(0 /* ignored */, LINE, 0, 0));
    }
}

void DoAlert(const char *windowTitle, const char *msg)
{
    DoSafeAlert(windowTitle, msg, TRUE);
}

#if	KERNOBJC

- (void)doAlert	: (const char *)windowTitle
		   msg : (const char *)msg
{
	DoSafeAlert(windowTitle, msg, TRUE);
}


- (IOReturn)setIntValues		: (unsigned *)parameterArray
			   forParameter : (IOParameterName)parameterName
			          count : (unsigned)count
{
	if(!strcmp(parameterName, "prettyShutdown"))
	{
		prettyShutdown = (short)(*parameterArray);
		return IO_R_SUCCESS;
	}
	else
	{ 
	/* Pass parameters we don't recognize to our superclass. */
        return [super setIntValues:parameterArray
		forParameter:parameterName count:count];
	}
}

@end

#endif	// KERNOBJC

/* end of kmDevice.m */