This topic describes how to implement an interrupt driven keyboard driver.
The steps are:
Implement the driver entry point function and initialisation code. This function is called when the extension is loaded.
The initialisation code binds the hardware interrupt to the Interrupt Service Routine (ISR) and enables the interrupt.
Implement an ISR to handle key events. The ISR queues a keyboard DFC.
Implement a keyboard DFC. This function interrogates the keyboard, converts the scancode to a keypress event, and places it onto the kernel's event queue.
In the template reference board port, the .mmp file for the keyboard driver is ...\template_variant\exkey_inttemplate.mmp. This is one of the PRJ_MMPFILES referenced in the template variant's bld.inf file in the ...\template_variant\... directory, and means that the keyboard driver is built as part of the Variant.
The source for the driver is contained entirely within ...\template_variant\specific\keyboard_interrupt.cpp.
The driver is defined as a kernel extension and is loaded early in the boot sequence.
The driver functionality is encapsulated by the DKeyboardTemplate class, and an instance of this is created when the extension is loaded.
As the driver is a kernel extension, it must have a DECLARE_STANDARD_EXTENSION() statement. In the template port, this is implemented:
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; }
It simply creates an instance of the DKeyboardTemplate class and then performs a second-phase initialisation, which is a common pattern in Symbian platform and third party applications.
Initialisation on construction
The constructor of the DKeyboardTemplate class has the following signature:
DKeyboardTemplate::DKeyboardTemplate() : DPowerHandler(KLitKeyboard), iMsgQ(rxMsg,this,NULL,1), iPowerUpDfc(PowerUpDfcFn,this,6), iPowerDownDfc(PowerDownDfcFn,this,7), iEventDfc(EventDfcFn,this,1) { }
See also Interrupt Dispatcher Tutorial.
The ISR (Interrupt Service Routine) just schedules the DFC that handles the keypress. It can also optionally contribute the timing of key interrupts as a source of random data for the Random Number Generator (see CSPRNG Implementation in Kernel). On the template reference board, this is implemented as:
void DKeyboardTemplate::Isr(TAny* aPtr) { Interrupt::Disable(KIntIdKeyboard); // Add the timing of key interrupts as entropy data for the RNG Interrupt::AddTimingEntropy(); k.iEventDfc.Add(); }
The ISR disables the keyboard interrupt, as repeated ISRs are not allowed to queue further DFC routines.
The DFC is the function DKeyboardTemplate::EventDfcFn which is implemented as a call to DKeyboardTemplate::EventDfc().
void DKeyboardTemplate::EventDfcFn(TAny* aPtr) { ((DKeyboardTemplate*)aPtr)->EventDfc(); } void DKeyboardTemplate::EventDfc() { __KTRACE_OPT(KHARDWARE,Kern::Printf("DKeyboardTemplate::EventDfc")); TInt irq=NKern::DisableAllInterrupts(); while (IsKeyReady()) // while there are keys in the controller's output buffer { NKern::RestoreInterrupts(irq); TRawEvent e; TUint keyCode=GetKeyCode(); // Read keycodes from controller __KTRACE_OPT(KHARDWARE,Kern::Printf("#%02x",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: // TUint bareCode=keyCode&~KFlagKeyPressed; TUint8 stdKey=convertCode[bareCode]; if (keyCode&KFlagKeyPressed) e.Set(TRawEvent::EKeyUp,stdKey,0); else e.Set(TRawEvent::EKeyDown,stdKey,0); Kern::AddEvent(e); NKern::Sleep(1); // pause before reading more keycodes irq=NKern::DisableAllInterrupts(); } Interrupt::Enable(KIntIdKeyboard); NKern::RestoreInterrupts(irq); }
This:
Copyright ©2010 Nokia Corporation and/or its subsidiary(-ies).
All rights
reserved. Unless otherwise stated, these materials are provided under the terms of the Eclipse Public License
v1.0.