Source to bsd/dev/ppc/drvUSBCMD/kbd/KBDHIDEmulation.c


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@
 */

/*
	File:		KBDHIDEmulation.c

	Contains:	Keyboard Emulation code

	Version:	xxx put version here xxx

	Copyright:	� 1998 by Apple Computer, Inc., all rights reserved.

	File Ownership:

		DRI:				Craig Keithley

		Other Contact:		xxx put other contact here xxx

		Technology:			USB

	Writers:

		(BWS)	Brent Schorsch
		(BG)	Bill Galcher
		(CJK)	Craig Keithley

	Change History (most recent first):

	 <USB32>	 6/20/98	CJK		Expand on Brent's comments... It wasn't a "fix" of the refcon,
									it was the addition of a new getinfo selector so that an
									InputSprocket can ask the Keyboard HID Module to pass back it's
									refcon to the caller. There was nothing "broken" in the original
									code.
	 <USB31>	 6/18/98	BWS		fix interrupt refcon support
	 <USB30>	 6/18/98	CJK		update to use modified HID dispatch table (passing in refcon)
	 <USB29>	  6/5/98	CJK		remove pragma unused from GetKeysPressed
	 <USB28>	 5/28/98	CJK		add HID GetInfo Selector to get notification proc address
	 <USB27>	 5/20/98	CJK		change driver name from USBKeyboardModule to
									USBHIDKeyboardModule
	 <USB26>	 5/20/98	CJK		add check for 'DDKBuild' define. If it's not defined then it's
									okay to include USBPriv.h and use the USL's DoneQueue API.
	 <USB25>	 5/19/98	CJK		remove include of USBPriv.h. This file should not be included,
									as the DDK will not supply it. If there are prototypes needed by
									this file, they should be moved to USB.h (usb.i).
	 <USB24>	 5/19/98	BG		Add includes of <USBPriv.h> and "UIMPriv.h" to bring in needed
									prototypes.
	 <USB23>	 5/18/98	CJK		remove the breaks...
	 <USB22>	 5/18/98	CJK		Add support for 'GetCurrentKeyboardState' getinfo selector
	 <USB21>	 4/30/98	CJK		fix break statements.
	 <USB20>	 4/30/98	CJK		handle keyreleasedflag slightly differently
	 <USB19>	 4/30/98	CJK		add a "send raw report" mode.  rework so that the old hid report
									and initialized flags are kept in the keyboard data structure
									(rather than in static vars.)
	 <USB18>	 4/27/98	CJK		correct zero'ing of non-relevant hid report bytes.
	 <USB17>	 4/27/98	CJK		Add handling of key rollover errors.  No longer sends already
									down keys when the rollover error is removed.
	 <USB16>	 4/26/98	CJK		update to match reworked keyboard interface driver.
									Restructured user notification routine.
	 <USB15>	 4/14/98	CJK		change call to USBIdleTask to UIMProcessDoneQueue
	 <USB14>	  4/9/98	CJK		remove include of USBDeviceDefines.h
		<13>	  4/8/98	CJK		add enable/disable of emulation code
		<12>	 3/26/98	CJK		remove debugstr
		<11>	 3/17/98	CJK		change }; to just }. MetroWerks has a problem with };.  Also
									added parens around pragma unused's variable name.
		<10>	 3/12/98	CJK		Don't worry about initializing the shim keyboard paramblock.
									That's done in the keyboard initialize routine.
		 <9>	  3/2/98	CJK		remove include of KBDHIDEmulation.h
		 <8>	  3/2/98	CJK		change to use USBHIDModule hid data structure (rather than the
									keyboard data structure).
		 <7>	 2/27/98	CJK		add check for keycount growing too large.  Add include of
									USBHIDModules.h.  Change LED control selector to be "by bit" (as
									opposed to by ID, which is probably how things will ultimately
									work).
		 <6>	 2/26/98	CJK		Add USBHIDControlDevice functionality. All it does (right now)
									is set the LEDs.
		 <5>	 2/17/98	CJK		Integrate Ferg's changes.
		<4*>	 2/16/98	DKF		Add keyposting if there is no shim registered
		 <4>	 2/16/98	CJK		remove write of $00 keycodes into the keycode array. Streamline
									old vs. new keycode detection (hopefully faster than before).
		 <3>	 2/16/98	CJK		Improve check of modifier key changes (shouldn't need to scan
									through each bit to determine if any modifiers changed.  Just check
									the changed bits.  If any changed, then look through each bit individually.
		 <2>	 2/10/98	CJK		Correct change history (to reflect the keyboard module changes)
		 <1>	 2/10/98	CJK		First time checkin.  Cloned from Mouse HID Module.
*/

//#include <Types.h>
//#include <Devices.h>
#include "../driverservices.h"
#include "../USB.h"
#ifndef DDKBuild
#include "../USBpriv.h"
#endif

#include "KeyboardModule.h"
//prototype from PPCKeyboard.m
void keyboard_adbhandler(int number, unsigned char *buffer, int count, void * ssp);


extern	usbKeyboardPBStruct myKeyboardPB;
extern	usbKeyboardPBStruct shimKeyboardPB;
extern  UInt8 usb_2_adb_keymap[];

void GetKeysPressed(USBHIDData * pKeysPressed);

OSStatus kbd_USBHIDInstallInterrupt(HIDInterruptProcPtr HIDInterruptFunction, UInt32 refcon)
{
	myKeyboardPB.interruptRefcon = refcon;
	myKeyboardPB.pSHIMInterruptRoutine = HIDInterruptFunction;
	return 0;
}

OSStatus kbd_USBHIDControlDevice(UInt32 theControlSelector, void * theControlData)
{
	switch (theControlSelector)
	{
		case kHIDSetLEDStateByBits:
			shimKeyboardPB.hidReport[0] = *(UInt8*)theControlData;

			shimKeyboardPB.retryCount = kKeyboardRetryCount;
			shimKeyboardPB.delayLevel = 0;							
			shimKeyboardPB.transDepth = 0;	
			
			shimKeyboardPB.pb.usbRefcon = kSetKeyboardLEDs;					/* Start with setting the interface protocol */
			KeyboardModuleInitiateTransaction(&shimKeyboardPB.pb);
			break;
			
		case kHIDRemoveInterruptHandler:
			myKeyboardPB.interruptRefcon = nil;
			myKeyboardPB.pSavedInterruptRoutine = nil;
			myKeyboardPB.pSHIMInterruptRoutine = nil;
			break;
			
		case kHIDEnableDemoMode:
			USBExpertStatus(myKeyboardPB.deviceRef, "USBHIDKeyboardModule: Demo Mode Enabled", myKeyboardPB.deviceRef);
			myKeyboardPB.pSavedInterruptRoutine = myKeyboardPB.pSHIMInterruptRoutine;
			myKeyboardPB.pSHIMInterruptRoutine = USBDemoKeyIn;
			break;

		case kHIDDisableDemoMode:
			USBExpertStatus(myKeyboardPB.deviceRef, "USBHIDKeyboardModule: Demo Mode Disabled", myKeyboardPB.deviceRef);
			myKeyboardPB.pSHIMInterruptRoutine = myKeyboardPB.pSavedInterruptRoutine;
			break;

		default:
			return paramErr;
	}
	return 0;
}

void GetKeysPressed(USBHIDDataPtr pKeysPressed)
{
UInt8	i,keycount;
	
	keycount = 0;
	for (i = 0; i < kKeyboardModifierBits; i++)
	{
		if (myKeyboardPB.oldHIDReport[0] & (1 << i))
		{
			pKeysPressed->kbd.usbkeycode[keycount++] = (0xe0 + i);
		}
	}
			
	if ((myKeyboardPB.oldHIDReport[kKeyboardOffsetToKeys] == 0) || (myKeyboardPB.oldHIDReport[kKeyboardOffsetToKeys] > 0x03))
	{
		for (i = kKeyboardOffsetToKeys; i < (kKeyboardOffsetToKeys + kKeyboardReportKeys); i++)
		{
			if (myKeyboardPB.oldHIDReport[i] > 0x03)
			{
				pKeysPressed->kbd.usbkeycode[keycount++] = myKeyboardPB.oldHIDReport[i];
			}
		}														
	}
	pKeysPressed->kbd.keycount = keycount;
}


OSStatus kbd_USBHIDGetDeviceInfo(UInt32 theInfoSelector, void * theInfo)
{
HIDInterruptProcPtr * pHIDIntProcPtr;
UInt32 * pInterruptRefcon;

	switch (theInfoSelector)
	{
		case kHIDGetCurrentKeys:
			GetKeysPressed((USBHIDDataPtr)theInfo);
			break;

		case kHIDGetInterruptHandler:
			pHIDIntProcPtr = (HIDInterruptProcPtr *)theInfo;  
			*pHIDIntProcPtr = myKeyboardPB.pSHIMInterruptRoutine;
			break;

		case kHIDGetInterruptRefcon:
			pInterruptRefcon = (UInt32 *)theInfo;
			*pInterruptRefcon = myKeyboardPB.interruptRefcon;
			break;
		
		default:
			return paramErr;
	}
	return 0;
}

OSStatus kbd_USBHIDPollDevice(void)
{
#ifndef DDKBuild
	USLPolledProcessDoneQueue();
#endif
	return kUSBNoErr;
}

OSStatus kbd_USBHIDEnterPolledMode(void)
{    
	return unimpErr;
}

OSStatus kbd_USBHIDExitPolledMode(void)
{
	return unimpErr;
}

void kbd_NotifyRegisteredHIDUser(UInt32 devicetype, UInt8 hidReport[])
{
#pragma unused (devicetype)

UInt8	i, j, newkey, oldkey, deltas;

UInt8		changedmodifiers, keycount;
USBHIDData	theKeyboardData;
Boolean		keypressedflag, keyreleasedflag;
char		adb_data[8];
	
	deltas = 0;
	if (myKeyboardPB.hidEmulationInit == false)
	{
		myKeyboardPB.hidEmulationInit = true;
		for (i = 0; i < kKeyboardReportSize; i++)
			myKeyboardPB.oldHIDReport[i] = 0;
	};
	
	myKeyboardPB.oldHIDReport[1] = 0x0;
	hidReport[1] = 0x0;
	
	for (i = 0; i < kKeyboardReportSize; i++)
	{
		if (hidReport[i] != myKeyboardPB.oldHIDReport[i])
		{
			deltas++;
		}
	}
	
	if ((myKeyboardPB.sendRawReportFlag) && deltas)
	{
		(*myKeyboardPB.pSHIMInterruptRoutine)(0xff, (void *)&hidReport[0]);
	}
	else
	{
		if (deltas)
		{
			keycount = 0;
			changedmodifiers = hidReport[0] ^  myKeyboardPB.oldHIDReport[0];
			
			if (changedmodifiers)
			{
				for (i = 0; i < kKeyboardModifierBits; i++)
				{
					if (changedmodifiers & (1 << i))
					{
						if (hidReport[0] & (1 << i))
						{
							theKeyboardData.kbd.usbkeycode[keycount++] = (0xe0 + i);
						}
						else
						{
							theKeyboardData.kbd.usbkeycode[keycount++] = (0x80e0 + i);
						}
					}
				}
				myKeyboardPB.oldHIDReport[0] = hidReport[0];
			}
			
			if ((hidReport[kKeyboardOffsetToKeys] == 0) || (hidReport[kKeyboardOffsetToKeys] > 0x03))
			{
				// While this double loop may look strange, I refer you to appendix C, of the HID Devices specification.
				// pp. 73 & 74 clearly state that report order is abitrary and does not reflect the order of events.
				// to quote: "The order of keycodes in array fields has no significance.  Order determination is done
				// by the host software comparing the contents of the previous report to the current report.  If two or 
				// more keys are pressed in one report, their order is indeterminate.  Keyboards may buffer events that
				// would have otherwise resulted in multiple events in a single report".
				
				// Because this specification (or lack thereof) states that the order is indeterminant, we have to 
				// check all the old keys against all the new keys...
				
				for (i = kKeyboardOffsetToKeys; i < (kKeyboardOffsetToKeys + kKeyboardReportKeys); i++)
				{
					keypressedflag = true;
					newkey = hidReport[i];
					
					keyreleasedflag = true;
					oldkey = myKeyboardPB.oldHIDReport[i];							
					for (j = kKeyboardOffsetToKeys; j < (kKeyboardOffsetToKeys + kKeyboardReportKeys); j++)								// then look through all the keys that were previously and are currently reported as pressed
					{									
						if (newkey == myKeyboardPB.oldHIDReport[j])		// was this new key already pressed?
						{								
							keypressedflag = false;						// if it was, then don't report it as being pressed.
						}
						
						if (oldkey == hidReport[j])						// Is the old key still pressed?
						{								
							keyreleasedflag = false;					// If yes, then don't report it as being released
						}
					}
					if ((newkey > 0x03) && keypressedflag)
					{
						theKeyboardData.kbd.usbkeycode[keycount++] = newkey;
					}
					
					if ((oldkey > 0x03) && keyreleasedflag)
					{
						theKeyboardData.kbd.usbkeycode[keycount++] = (oldkey + 0x8000);
					}
					
					if (keycount > 20)									// a worse case scenario is:
					{													// 4 modifier keys up
						break;											// the other 4 modifier keys down
					}													// 6 previously reported keys up
				}														// 6 newly reported keys down
																		// 6 + 6 + 4 + 4 = 20
				for (i = 0; i<kKeyboardReportSize; i++)
				{
					myKeyboardPB.oldHIDReport[i] = hidReport[i];
				};
			}
			
			//Now we map to ADB from USB 
			//Adam Wang 12/2/98
			adb_data[1] = 0xff;
			for(i=0;i<keycount;++i)
			{
				static char caps_lock_state = 1;
                                unsigned char newLEDState;
				//Special handling of USB caps-lock-that-doesn't-lock 12/8/98
				if ( theKeyboardData.kbd.usbkeycode[i] == 0x0039)  //00 = going down, 39 = caps lock                  
				{
					newLEDState = 2;
					//This call to set LED will hang on Yosemite 
					kbd_USBHIDControlDevice(1, &newLEDState);
				}
				if ( theKeyboardData.kbd.usbkeycode[i] == 0x8039)  //80 = going up, 39 = caps lock                  
				{ 
					caps_lock_state ^= 1; //toggle bit
					if (caps_lock_state == 0)     
						continue;   //naga changed break to continue
					//The logic is, ignore first caps-up on iMac keyboards        
					newLEDState = 0;
					kbd_USBHIDControlDevice(1, &newLEDState);
				}                

				adb_data[0] = usb_2_adb_keymap[(theKeyboardData.kbd.usbkeycode[i] & 0x00ff)];

				if ((theKeyboardData.kbd.usbkeycode[i] & 0x8000) == 0x8000) // key is on its way up
					adb_data[0] |= 0x80;		//Set ADB most significant bit
				
				//Special handling needed for ADB POWER key
				if (adb_data[0] == 0x7f)
				{
					adb_data[1] = 0x7f;  //ADB power scan code is 7f 7f
				}

				keyboard_adbhandler(2, adb_data, 2, (void *) 0);
			}


/*
			if ((myKeyboardPB.pSHIMInterruptRoutine) && keycount)
			{
				theKeyboardData.kbd.keycount = keycount;
				(*myKeyboardPB.pSHIMInterruptRoutine)(myKeyboardPB.interruptRefcon, (void *)&theKeyboardData);
			}
*/
		}
	}
}


USBHIDModuleDispatchTable kbd_TheHIDModuleDispatchTable =
{
	(UInt32)0,
	(USBHIDInstallInterruptProcPtr)kbd_USBHIDInstallInterrupt,
	(USBHIDPollDeviceProcPtr)kbd_USBHIDPollDevice,
	(USBHIDControlDeviceProcPtr)kbd_USBHIDControlDevice,
	(USBHIDGetDeviceInfoProcPtr)kbd_USBHIDGetDeviceInfo,
	(USBHIDEnterPolledModeProcPtr)kbd_USBHIDEnterPolledMode,
	(USBHIDExitPolledModeProcPtr)kbd_USBHIDExitPolledMode,
};