bsptemplate/asspandvariant/template_variant/specific/keyboard.cpp
author Slion
Tue, 08 Dec 2009 08:11:42 +0100
branchanywhere
changeset 19 f6d3d9676ee4
parent 0 a41df078684a
permissions -rw-r--r--
Trying to figure out how to implement my WINC like compatibility layer. Going the emulation way is probably not so smart. We should not use the kernel but rather hook native functions in the Exec calls.

// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// template\template_variant\specific\keyboard.cpp
// Access to Template polled keyboard
// The code here implements a simple polled keyboard driver.
// This is an alternative to the interrupt-driven driver in keyboard_interrupt.cpp.
// This example assumes that we have a non-intelligent keyboard
// consisting of a number of i/o lines arranged in a grid.
// You can use this code as a starting point and modify it to suit
// your hardware.
// 
//

#include <template_assp.h>
#include "platform.h"
#include <kernel/kpower.h>
#include <e32keys.h>



// The TKeyboardState class is used to encapsulate the state of 
// the keyboard. i.e which keys are currently being pressed.
// To determine which keys are being pressed, typically a voltage
// is applied to each row in turn (or column, depending on the hardware) 
// and the output is read resulting in a bitmask for each row.
//
// For example, the keys could be arranged as follows (where a '1' indicates
// that a key is currently being pressed :
// EXAMPLE ONLY
//
//																						Translated
//				Column#	0	1	2	3	4	5	6	7	8	9	A	B	C	D	E	F	KeyCode
//			Row#	
//			6			0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	60	to	6F
//			5			0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	50	to	5F
//			4			0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	40	to	4F
//			3			0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	30	to	3F
//	Input->	2			0	0	0	1	0	0	0	0	1	0	0	0	0	0	0	0	20	to	2F
//			1			0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	10	to	1F
//			0			0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	00	to	0F	
//	
//	output->			0	0	0	1	0	0	0	0	1	0	0	0	0	0	0	0	
//
// TO DO: (mandadory)
// Modify TKeyboardState (or provide an alternative) to model the 
// real keyboard state
//
// EXAMPLE ONLY
class TKeyboardState
	{
public:

	enum TDimensions
	{
	KRows = 7,
	KColumns = 16
	};

public:
	TKeyboardState();
	void Clear();
	TBool IsKeyReady();
	TUint32 GetKeyCode();
	TKeyboardState operator&(const TKeyboardState& aState);
	TKeyboardState operator|(const TKeyboardState& aState);
	TKeyboardState operator~();

public:
	TUint32 iKeyBitMask[KRows];
	};

/**
Constructor
*/
TKeyboardState::TKeyboardState()
	{
	Clear();
	}

/**
Clears the array of bitmasks 
*/
void TKeyboardState::Clear()
	{
	for (TInt row=0; row<KRows; row++)
		iKeyBitMask[row] = 0;
	}

/**
Determines whether any keys are being pressed by examining the 
array of bitmasks to determine whether any bits are set

@return	ETrue if one or more keys are being pressed
*/
TBool TKeyboardState::IsKeyReady()
	{
	for (TInt row=0; row<KRows; row++)
		{
		if (iKeyBitMask[row] != 0)
			return ETrue;
		}

	return EFalse;
	}

/**
Scans the array of bitmasks and returns a keycode representing
the first bit that it finds that is on.
E.g. :
if the first bit on the first row is set, then 1 is returned,
if the third bit on the first row is set, then 3 is returned. etc.

Once a bit is found it is cleared to avoid reading it again.

NB Before calling this function, IsKeyReady() should be called 
to determine whether a key code is available.

@return	a 32-bit keycode representing a key that is currently pressed
*/

TUint32 TKeyboardState::GetKeyCode()
	{
	TInt keyNum = 0;
	for (TInt row=0; row<KRows; row++)
		{
		TUint32 bitMask = 1;
		for (TInt col=0; col<KColumns; col++)
			{
			if (iKeyBitMask[row] & bitMask)
				{
				iKeyBitMask[row] &= ~bitMask;
				return keyNum;
				}
			bitMask<<= 1;
			keyNum++;
			}
		}
	return 0;
	}

/**
Perform a bitwise AND between two TKeyboardState objects
by AND-ing together all the 32-bit integers

@return	a new instance of a TKeyboardState object containing the result
*/
TKeyboardState TKeyboardState::operator&(const TKeyboardState& aState)
	{
	TKeyboardState state = *this;

	for (TInt row=0; row<KRows; row++)
		state.iKeyBitMask[row]&= aState.iKeyBitMask[row];;

	return state;
	}

/**
Perform a bitwise OR between two TKeyboardState objects
by OR-ing together all the 32-bit integers

@return	a new instance of a TKeyboardState object containing the result
*/
TKeyboardState TKeyboardState::operator|(const TKeyboardState& aState)
	{
	TKeyboardState state = *this;

	for (TInt row=0; row<KRows; row++)
		state.iKeyBitMask[row]|= aState.iKeyBitMask[row];;

	return state;
	}

/**
Perform a bitwise NOT (one's complement) of a KeyboardState object
by NOT-ing all the 32-bit integers

@return	a new instance of a TKeyboardState object containing the result
*/
TKeyboardState TKeyboardState::operator~()
	{
	TKeyboardState state = *this;

	for (TInt row=0; row<KRows; row++)
		state.iKeyBitMask[row] = ~state.iKeyBitMask[row];

	return state;
	}

//
//
// TO DO: (optional)
//
// Modify this conversion table to suit your keyboard layout
// EXAMPLE ONLY
//

const TUint8 convertCode[] =
	{
//Row 0 (bottom row)
	EStdKeyLeftAlt		,	EStdKeyHash			,	EStdKeyNull			,	EStdKeyLeftCtrl				,
	EStdKeyLeftFunc		,	EStdKeyEscape		,	'1'					,	'2'							,
	'9'					,	'0'					,	EStdKeyMinus		,	EStdKeyEquals				,
	EStdKeyNull			,	EStdKeyBackspace	,	EStdKeyNull			,	EStdKeyNull					,
//Row 1
	EStdKeyNull			,	EStdKeyBackSlash	,	EStdKeyLeftShift	,	EStdKeyNull					,
	EStdKeyNull			,	EStdKeyDelete		,	EStdKeyNull			,	'T'							,
	'Y'					,	'U'					,	'I'					,	 EStdKeyEnter				,
	EStdKeyRightShift	,	EStdKeyDownArrow	,	EStdKeyNull			,	EStdKeyNull					,
//Row 2
	EStdKeyNull			,	EStdKeyTab			,	EStdKeyNull			,	 EStdKeyNull				,
	EStdKeyNull			,	'Q'					,	'W'					,	'E'							,
	'R'					,	'O'					,	'P'					,	EStdKeySquareBracketLeft	,
	EStdKeyNull			,	EStdKeySquareBracketRight,EStdKeyNull		,	EStdKeyNull					,
//Row 3
	EStdKeyNull			,	'Z'					,	EStdKeyNull			,	EStdKeyNull					,
	EStdKeyNull			,	EStdKeyCapsLock		,	EStdKeyNull			,	EStdKeyNull					,
	'K'					,	'L'					,	EStdKeySemiColon	,	EStdKeySingleQuote			,
	EStdKeyNull			,	EStdKeyUpArrow		,	EStdKeyNull			,	EStdKeyNull					,
//Row 4
	EStdKeyNull			,	EStdKeyTab			,	EStdKeyNull			,	EStdKeyNull,
	EStdKeyNull			,	'Q'					,	'W'					,	'E'							,
	'R'					,	'O'					,	'P'					,	EStdKeySquareBracketLeft	,
	EStdKeyNull			,	EStdKeySquareBracketRight,	EStdKeyNull		,	EStdKeyNull					,
//Row 5
	EStdKeyNull			,	'X'					,	EStdKeyNull			,	EStdKeyNull					,
	EStdKeyNull			,	'C'					,	'V'					,	'B'							,
	'N'					,	'M'					,	EStdKeyComma		,	EStdKeyFullStop				,
	EStdKeyNull			,	EStdKeySpace		,	EStdKeyNull			,	EStdKeyNull					,
//Row 6
	EStdKeyNull			,	EStdKeyNull			,	EStdKeyNull			,	EStdKeyNull					,
	EStdKeyNull			,	'3'					,	'4'					,	'5'							,
	'6'					,	'7'					,	'8'					,	EStdKeyMenu					,
	EStdKeyNull			,	EStdKeyRightArrow	,	EStdKeyNull			,	EStdKeyNull					
	};




// EXAMPLE ONLY
const TKeyboard	KConfigKeyboardType = EKeyboard_Full;
const TInt KConfigKeyboardDeviceKeys = 0;
const TInt KConfigKeyboardAppsKeys = 0;


//
// TO DO: (optional)
//
// Set the keyboard scan rate in milliseconds
//

// EXAMPLE ONLY
const TInt KScanRate = 50;	// poll every 1/20 of a second (i.e. every 50 milliseconds)


_LIT(KLitKeyboard,"Keyboard");


//
// TO DO: (optional)
//
// Add any private functions and data you require
//
NONSHARABLE_CLASS(DKeyboardTemplate) : public DPowerHandler
	{
public:
	DKeyboardTemplate();
	TInt Create();
	
	// from DPowerHandler
	void PowerUp();
	void PowerDown(TPowerState);

private:
	static void HandleMessage(TAny* aPtr);
	void HandleMsg(TMessageBase* aMsg);
	
	static TInt HalFunction(TAny* aPtr, TInt aFunction, TAny* a1, TAny* a2);
	TInt HalFunction(TInt aFunction, TAny* a1, TAny* a2);
	
	static void PowerUpDfcFn(TAny* aPtr);
	void PowerUpDfc();
	
	static void PowerDownDfcFn(TAny* aPtr);
	void PowerDownDfc();

	static void TimerCallback(TAny* aDriver);
	static void TimerDfcFn(TAny* aDriver);
	void Poll();

	void KeyboardInfo(TKeyboardInfoV01& aInfo);

	void KeyboardOn();
	void KeyboardOff();
	void KeyboardPowerUp();

private:
	TDfcQue* iDfcQ;
	TMessageQue iMsgQ;	
	TDfc iPowerUpDfc;
	TDfc iPowerDownDfc;	
	TBool iKeyboardOn;
	NTimer iTimer;
	TInt iTimerTicks;
	TDfc iTimerDfc;

	// a bitmask indicating which keys were pressed down on the last timer tick
	TKeyboardState iKeyStateLast;

	// a bitmask indicating the set of keys for which we have sent an EKeyDown event
	TKeyboardState iKeysDown;			
	};

/**
constructor
*/
DKeyboardTemplate::DKeyboardTemplate()
	:	DPowerHandler(KLitKeyboard), 
		iMsgQ(HandleMessage, this, NULL, 1),
		iPowerUpDfc(PowerUpDfcFn, this, 6),
		iPowerDownDfc(PowerDownDfcFn, this, 7),
		iTimer(&DKeyboardTemplate::TimerCallback, (TAny*) this),
		iTimerDfc(TimerDfcFn, this, 1)
	{
	// Convert the scan rate from milliseconds to nanokernel ticks (normally 1/64 of a second)
	iTimerTicks = NKern::TimerTicks(KScanRate);
	}

/**
Second-phase constructor 
Assigns queues for all the DFCs and starts the keyboard-polling timer

Called by factory function at ordinal 0
*/
TInt DKeyboardTemplate::Create()
	{
	iDfcQ=Kern::DfcQue0();

	iKeyboardOn = EFalse;	

	// install the HAL function
	TInt r = Kern::AddHalEntry(EHalGroupKeyboard, DKeyboardTemplate::HalFunction, this);
	if (r != KErrNone)
		return r;

	iTimerDfc.SetDfcQ(iDfcQ);

	iPowerUpDfc.SetDfcQ(iDfcQ);
	iPowerDownDfc.SetDfcQ(iDfcQ);
	iMsgQ.SetDfcQ(iDfcQ);
	iMsgQ.Receive();

	// install the power handler
	Add();

	// Power up the device and start the timer
	KeyboardPowerUp();

	return r;
	}

/**
Calback for the keyboard-polling timer
Called in the context of an ISR

@param	aPtr A pointer to an instance of DKeyboardTemplate
*/
void DKeyboardTemplate::TimerCallback(TAny *aPtr)
	{
	// schedule a DFC
	DKeyboardTemplate& k=*(DKeyboardTemplate*)aPtr;
	k.iTimerDfc.Add();
	}


/**
DFC scheduled by the keyboard-polling timer when it expires

@param	aPtr A pointer to an instance of DKeyboardTemplate
*/
void DKeyboardTemplate::TimerDfcFn(TAny* aPtr)
	{
	((DKeyboardTemplate*)aPtr)->Poll();
	}


/**
Reads scan codes from the keyboard until there are none left
Called from the keyboard-polling timer's DFC
*/
void DKeyboardTemplate::Poll()
	{
	__KTRACE_OPT(KHARDWARE,Kern::Printf("DKeyboardTemplate::EventDfc"));

	
	TKeyboardState keyState;

	//
	// TO DO: (mandatory)
	// Read new key state into the array of bitmasks in keyState
	// This typically involves applying a voltage to each row from 0 to KRows-1, 
	// reading the output state of the i/o lines at every step 
	// - this represents the keys that are pressed on each row -
	// and storing the output of each row as a bitmask into keyState.iKeyBitMask[n], 
	// where n = the row being accessed
	//

	// To enable a simple de-bouncing algorithm, 
	// work out which keys have been pressed down for at least two timer 
	// ticks by AND-ing together the last bitmask with the current bitmask
	TKeyboardState keysStillDown =  keyState & iKeyStateLast;
	

	// Similarly, work out which keys have been "un-pressed" for at least two timer 
	// ticks by AND-ing together the one's complement of the last bitmask with the 
	// one's complement of the current bitmask and 
	// then AND-ing this with the set of keys for which we have sent an EKeyDown 
	// event to give the set of keys for which we need to send an EKeyUp event
	TKeyboardState keysStillUp =  (~keyState & ~iKeyStateLast) & iKeysDown;

	// save the current state for next time
	iKeyStateLast = keyState;

	// update the set of keys for which we have sent an EKeyDown event
	iKeysDown = iKeysDown | keysStillDown;
	iKeysDown = iKeysDown & ~keysStillUp;

	// process all the key-down events
	while (keysStillDown.IsKeyReady())						// while there are keys we haven't processed
		{
		TRawEvent e;
		TUint keyCode = keysStillDown.GetKeyCode();			// Read keycodes from bitmask 

		__KTRACE_OPT(KHARDWARE,Kern::Printf("EKeyDown: #%02x\n",keyCode));

		//
		// TO DO: (mandatory)
		//
		// Convert from hardware scancode to EPOC scancode and send the scancode as an event (key pressed or released)
		// as per below EXAMPLE ONLY:
		//
		__ASSERT_DEBUG(keyCode < (sizeof(convertCode) / sizeof(TUint8)), Kern::Fault("Keyboard", __LINE__));
		TUint8 stdKey = convertCode[keyCode];
		
		e.Set(TRawEvent::EKeyDown, stdKey, 0);
		Kern::AddEvent(e);
		}

	// process all the key-up events
	while (keysStillUp.IsKeyReady())						// while there are keys we haven't processed
		{
		TRawEvent e;
		TUint keyCode = keysStillUp.GetKeyCode();			// Read keycodes from bitmask 

		__KTRACE_OPT(KHARDWARE,Kern::Printf("EKeyUp: #%02x\n",keyCode));

		//
		// TO DO: (mandatory)
		//
		// Convert from hardware scancode to EPOC scancode and send the scancode as an event (key pressed or released)
		// as per below EXAMPLE ONLY:
		//
		__ASSERT_DEBUG(keyCode < (sizeof(convertCode) / sizeof(TUint8)), Kern::Fault("Keyboard", __LINE__));
		TUint8 stdKey = convertCode[keyCode];

		e.Set(TRawEvent::EKeyUp, stdKey, 0);
		Kern::AddEvent(e);
		}

	// start the timer again
	iTimer.OneShot(iTimerTicks);
	}



/**
Notifies the peripheral of system power up.
Called by the power manager during a transition from standby.
Schedules a DFC to handle the power up.
*/
void DKeyboardTemplate::PowerUp()
	{
	iPowerUpDfc.Enque();
	}


/**
static DFC to handle powering up the keyboard

@param	aPtr A pointer to an instance of DKeyboardTemplate
*/
void DKeyboardTemplate::PowerUpDfcFn(TAny* aPtr)
	{
	((DKeyboardTemplate*)aPtr)->PowerUpDfc();
	}


/**
DFC to handle powering up the keyboard
*/
void DKeyboardTemplate::PowerUpDfc()
	{
	__KTRACE_OPT(KPOWER, Kern::Printf("DKeyboardTemplate::PowerUpDfc()"));
	KeyboardOn();

	// Indicate to power handle that powered up is complete
	PowerUpDone();
	}

/**
Powers up the keyboard
May be called as a result of a power transition or from the HAL
*/
void DKeyboardTemplate::KeyboardOn()
	{
	__KTRACE_OPT(KPOWER,Kern::Printf("DKeyboardTemplate::KeyboardOn() iKeyboardOn=%d", iKeyboardOn));

	if (!iKeyboardOn)	// make sure we don't initialize more than once
		KeyboardPowerUp();
	}

/**
Powers up the keyboard
Assumes that the keyboard is currently powered off
*/
void DKeyboardTemplate::KeyboardPowerUp()
	{
	__KTRACE_OPT(KPOWER,Kern::Printf("DKeyboardTemplate::KeyboardPowerUp()"));

	iKeyboardOn = ETrue;

	iKeyStateLast.Clear();
	iKeysDown.Clear();

	// Send key up events for EStdKeyOff (Fn+Esc) event 
	TRawEvent e;
	e.Set(TRawEvent::EKeyUp,EStdKeyEscape,0);
	Kern::AddEvent(e);
	e.Set(TRawEvent::EKeyUp,EStdKeyLeftFunc,0);
	Kern::AddEvent(e);

	// Start the periodic tick for the selected rate.
	// This will call TimerCallback() in the context of an ISR
	iTimer.OneShot(iTimerTicks);
	}


/**
Requests keyboard to power down.
Called by the power manager during a transition to standby or power off
Schedules a DFC to handle the power up.

@param aPowerState the current power state
*/
void DKeyboardTemplate::PowerDown(TPowerState)
	{
	iPowerDownDfc.Enque();
	}

/**
static DFC to handle powering down the keyboard

@param	aPtr A pointer to an instance of DKeyboardTemplate
*/
void DKeyboardTemplate::PowerDownDfcFn(TAny* aPtr)
	{
	((DKeyboardTemplate*)aPtr)->PowerDownDfc();
	}

/**
DFC to handle powering down the keyboard
*/
void DKeyboardTemplate::PowerDownDfc()
	{
	__KTRACE_OPT(KPOWER, Kern::Printf("DKeyboardTemplate::PowerDownDfc()"));
	KeyboardOff();
	PowerDownDone();
	}

/**
Powers down the keyboard
May be called as a result of a power transition or from the HAL
*/
void DKeyboardTemplate::KeyboardOff()
	{
	__KTRACE_OPT(KPOWER,Kern::Printf("DKeyboardTemplate::KeyboardOff() iKeyboardOn=%d", iKeyboardOn));

	// cancel the keyboard-polling timer
	iTimerDfc.Cancel();
	iTimer.Cancel();

	iKeyboardOn = EFalse;
	}


/**
static message handler for processing power up/down messages 
posted internally from HalFunction()

@param	aPtr A pointer to an instance of DKeyboardTemplate
*/
void DKeyboardTemplate::HandleMessage(TAny* aPtr)
	{
	DKeyboardTemplate& h=*(DKeyboardTemplate*)aPtr;
	TMessageBase* pM=h.iMsgQ.iMessage;
	if (pM)
		h.HandleMsg(pM);
	}

/**
Message handler for processing power up/down messages 
posted internally from HalFunction()

param	aMsg A message indicating whether to power the keyboard on or off
*/
void DKeyboardTemplate::HandleMsg(TMessageBase* aMsg)
	{
	if (aMsg->iValue)
		KeyboardOn();
	else
		KeyboardOff();
	aMsg->Complete(KErrNone,ETrue);
	}


/**
Retrieves information about the keyboard
Called from HalFunction()

@param	aInfo a caller-supplied class which on return contains information about the keyboard
*/
void DKeyboardTemplate::KeyboardInfo(TKeyboardInfoV01& aInfo)
	{
	__KTRACE_OPT(KEXTENSION,Kern::Printf("DKeyboardTemplate::KeyboardInfo"));
	aInfo.iKeyboardType=KConfigKeyboardType;
	aInfo.iDeviceKeys=KConfigKeyboardDeviceKeys;
	aInfo.iAppsKeys=KConfigKeyboardAppsKeys;
	}


/**
HAL handler function

@param	aPtr a pointer to an instance of DLcdPowerHandler
@param	aFunction the function number
@param	a1 an arbitrary parameter
@param	a2 an arbitrary parameter
*/
TInt DKeyboardTemplate::HalFunction(TAny* aPtr, TInt aFunction, TAny* a1, TAny* a2)
	{
	DKeyboardTemplate* pH=(DKeyboardTemplate*)aPtr;
	return pH->HalFunction(aFunction,a1,a2);
	}


/**
a HAL entry handling function for HAL group attribute EHalGroupKeyboard

@param	a1 an arbitrary argument
@param	a2 an arbitrary argument
@return	KErrNone if successful
*/
TInt DKeyboardTemplate::HalFunction(TInt aFunction, TAny* a1, TAny* a2)
	{
	TInt r=KErrNone;

	__KTRACE_OPT(KEXTENSION,Kern::Printf("DKeyboardTemplate::HalFunction %d", aFunction));
	
	switch(aFunction)
		{
		case EKeyboardHalKeyboardInfo:
			{
			TPckgBuf<TKeyboardInfoV01> kPckg;
			KeyboardInfo(kPckg());
			Kern::InfoCopy(*(TDes8*)a1,kPckg);
			break;
			}

		case EKeyboardHalSetKeyboardState:
			{
			if(!Kern::CurrentThreadHasCapability(ECapabilityPowerMgmt,__PLATSEC_DIAGNOSTIC_STRING("Checked by Hal function EKeyboardHalSetKeyboardState")))
				return KErrPermissionDenied;
			if ((TBool)a1)
				{
				TThreadMessage& m=Kern::Message();
				m.iValue = ETrue;
				m.SendReceive(&iMsgQ);		// send a message and block Client thread until keyboard has been powered up
				}
			else
				{
				TThreadMessage& m=Kern::Message();
				m.iValue = EFalse;
				m.SendReceive(&iMsgQ);		// send a message and block Client thread until keyboard has been powered down
				}
			}
			break;

		case EKeyboardHalKeyboardState:
			kumemput32(a1, &iKeyboardOn, sizeof(TBool));
			break;
		
		default:
			r=KErrNotSupported;
			break;
		}
	return r;
	}



DECLARE_STANDARD_EXTENSION()
	{
	__KTRACE_OPT(KEXTENSION,Kern::Printf("Starting keyboard driver"));

	// create keyboard driver
	TInt r=KErrNoMemory;
	DKeyboardTemplate* pK=new DKeyboardTemplate;
	if (pK)
		r=pK->Create();

	__KTRACE_OPT(KEXTENSION,Kern::Printf("Returns %d",r));
	return r;
	}