Windows CE

Showing posts with label Driver. Show all posts
Showing posts with label Driver. Show all posts

Sunday, May 10, 2009

Multiple Key detection + PXA27x + Wince 6.0

Recently i had a chance to look into a keypad problem.We are using a development board having PXA270 processor.For a single key press we were getting the proper scan code, but when multiple key were pressed, we were still getting the 1st pressed key scan code.Second Key was getting ignored.

So,I checked the xllp code for keypad.

\WINCE600\PLATFORM\COMMON\SRC\SOC\PXA27X_MS_V1\XLLP\SOURCE\xllp_keypad.c

There i found that in function XllpKeyPadConfigure , IGNORE_MULTIPLE_KEY_PRESS bit was set in keypad control register. I cleared that bit and checked the scan code.Now i was able to get some scan code when the second key was pressed,without lifting the 1st key.It means Multiple key press was not ignored now.

But still the second key scan code was not proper.The scan code we were getting was not what we were expecting.So i checked function ReadScanCodeAutomatically.

There i found a comment for the part of the code when numOfKeysPressed > 1

// these keys are the "minor keys", the ones that needs top right and bottom left of the
// cooresponding 4 keys surrounding them to trigger the correct key. Doing a binary search
// there are 5 keys, the middle key reads 0x8, the first key reads 0x2 and the last reads 0x20.
// this needs to be done for each row.  This will be encorporated into a routine for the next
// upgrade of keypad.

I couldn't understand this what does this mean.But one thing was sure that the logic for determining the scan code for multikey press was not according to the PXA27x.

I rewrote the function as follows :

   1: XLLP_BOOL_T ReadScanCodeAutomatically(XLLP_KEYPAD_REGS *v_pKeyPadRegs,XLLP_UINT8_T *key)
   2: {
   3:    
   4:         /**
   5:             Check for Single Key press
   6:         **/
   7: if(numOfKeysPressed > 1)
   8:         {
   9:             c0 = v_pKeyPadRegs->kpAutoScanMultiKeyPress0 & 0xFF;
  10:             c1 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress0 >> 16) & 0xFF);
  11:             c2 = v_pKeyPadRegs->kpAutoScanMultiKeyPress1 & 0xFF;
  12:             c3 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress1 >> 16) & 0xFF);
  13:             c4 = v_pKeyPadRegs->kpAutoScanMultiKeyPress2 & 0xFF;
  14:             c5 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress2 >> 16) & 0xFF);
  15:             c6 = v_pKeyPadRegs->kpAutoScanMultiKeyPress3 & 0xFF;
  16:             c7 = ((v_pKeyPadRegs->kpAutoScanMultiKeyPress3 >> 16) & 0xFF);
  17:  
  18:             
  19:         if(c0!=0)
  20:         {
  21:             ReadMultipleKeyScanCode(c0,0,RowBit,Col);
  22:         }
  23:         if(c1!=0)
  24:         {
  25:             ReadMultipleKeyScanCode(c1,1,RowBit,Col);
  26:         }
  27:         if(c2!=0)
  28:         {
  29:             ReadMultipleKeyScanCode(c2,2,RowBit,Col);
  30:         }
  31:         if(c3!=0)
  32:         {
  33:             ReadMultipleKeyScanCode(c3,3,RowBit,Col);
  34:         }
  35:         if(c4!=0)
  36:         {
  37:             ReadMultipleKeyScanCode(c4,4,RowBit,Col);
  38:         }
  39:         if(c5!=0)
  40:         {
  41:             ReadMultipleKeyScanCode(c5,5,RowBit,Col);
  42:         }
  43:         if(c6!=0)
  44:         {
  45:             ReadMultipleKeyScanCode(c6,6,RowBit,Col);
  46:         }
  47:         if(c7!=0)
  48:         {
  49:             ReadMultipleKeyScanCode(c7,7,RowBit,Col);
  50:         }
  51:  
  52:         key1 = (unsigned char) (( RowBit[0] <<>
  53:         key2 = (unsigned char) (( RowBit[1] <<>
  54:        
  55:         if(key1 == PrevKey)
  56:         {
  57:             *key = key2 ;
  58:         }
  59:         else
  60:             *key = key1 ;
  61:  
  62:     }
  63:         else
  64:             *key = NO_KEY;
  65:  
  66:       retval = XLLP_TRUE;
  67:     }
  68:     NKDbgPrintfW(L"ReadScanCodeAutomatically<\r\n");
  69:     PrevKey = *key ;
  70:     return(retval);
  71: }
  72:  
  73: //---------------------------------------------------------------------------------------------------------------
  74: // Function: ReadMultipleKeyScanCode
  75: // Purpose:  This functions reads the Multiple key pressed scan code from the KeyPad controller triggered by setting of the ASACT bit
  76: //           in the KeyPad Control register. If there is a valid key detected then it returns the scan code.
  77: // Returns:  success/failure.
  78: //---------------------------------------------------------------------------------------------------------------
  79: XLLP_BOOL_T ReadMultipleKeyScanCode(XLLP_UINT32_T KPASMKPRegister,XLLP_UINT32_T col, XLLP_UINT32_T RowBit[] ,XLLP_UINT32_T Col[])
  80: {
  81:     int i = 0;
  82:     while (i <>
  83:     {
  84:         if((KPASMKPRegister & 0x01) == 0x01 )
  85:         {
  86:             if(RowBit[0] == 0)
  87:             {
  88:                 RowBit[0] = i ;
  89:                 Col[0]  = col ;
  90:             }
  91:             else
  92:             {
  93:                 RowBit[1] = i ;
  94:                 Col[1]  = col ;
  95:             }
  96:             NKDbgPrintfW(L"XLLP_NEW:ReadMultipleKeyScanCode bit no  %d>\r\n",i);
  97:         }
  98:  
  99:         KPASMKPRegister = KPASMKPRegister >> 1 ;
 100:         i++ ;
 101:  
 102:     }
 103:      return(XLLP_TRUE);
 104: }
 105:  

And it worked fine. I was getting the second key scan code properly.

Note : There is a limitation in above code.It can only read two key press properly.More than 2 keys pressed simultaneously cant be recognized, for example Ctrl+Alt+Del   ;) But this is not required for our project..

Monday, September 1, 2008

Implementing a Dynamic Interrupt Mapping in device driver

Hi.

I will start with a driver which can do a little more than just exporting its entry points.

Here i will explain How to Implement an Interrupt Mechanism in device driver with Dynamic Interrupt Mappings.

Interrupts are notifications generated either in hardware or software to inform the CPU that an event has occurred that requires immediate attention, such as keyboard press events or touch event. In response to an interrupt, the CPU stops executing the current thread, jumps to a trap handler in the kernel to respond to the event, and then resumes executing the original thread after the interrupt is handled. In this way, integrated and peripheral hardware components, such as keyboards, mouse, touchscreen, can get the attention of the CPU and have the kernel exception handler run appropriate code in interrupt service routines (ISRs) within the kernel or in associated device drivers.

Each hardware interrupt line corresponds to an IRQ value in the interrupt controller registers. Each IRQ value can be associated with only one ISR, but an ISR can map to multiple IRQs. The kernel just determines and signals events associated with the SYSINTR values returned from the ISR in response to the IRQ.

For the ISR to determine a correct SYSINTR return value, there must be a mapping between the IRQ and the SYSINTR, which can be hardcoded into the OAL. But its not always the case. A developer may need to register IRQ/SYSINTR mappings without modifying the OAL code. This can be done if you call KernelIoControl in your device drivers with an IO control code of IOCTL_HAL_REQUEST_SYSINTR which registers the IRQ and SYSINTR mappings in the kernel’s interrupt mapping arrays.

When calling KernelIoControl with IOCTL_HAL_REQUEST_SYSINTR, you establish a 1:1 relationship between IRQ and SYSINTR.To remove an entry from the interrupt mapping tables, such as when unloading a driver, call KernelIoControl with an IO control code of IOCTL_HAL_REQUEST_SYSINTR. IOCTL_HAL_RELEASE_SYSINTR dissociates the IRQ from the SYSINTR value.

The following is a sample powerbutton driver for Device Emulator BSP.

Here power button is used to trigger IRQ0 which is mapped dynamically to SYSINTR_POWERBTN.

The event associated with the SYSINTR, Suspends the system when Signalled.

///PowerButton.c

static HANDLE PwrButtonIntrEvent;
static HANDLE PowerButtonIntrThreadHandle;

static DWORD PwrButtonIrq = IRQ0; // Determined by the board layout.
static DWORD PwrButtonSysIntr = SYSINTR_POWERBTN;

PHYSICAL_ADDRESS PA;
static volatile XLLP_GPIO_T *v_pGPIO;

static VOID
EnablePowerButtonInterrupt(VOID)
{
//Set GPIO as Interrupt
//Configure INTERUPT as falling or Rising Edge
}

static BOOL
PowerButtonIsPushed(VOID)
{
// Check for the Button pressed status
}

static BOOL
InitializeAddresses(VOID)
{
BOOL RetValue = FALSE;
PA.LowPart=BULVERDE_BASE_REG_PA_GPIO;
v_pGPIO = (XLLP_GPIO_T *)MmMapIoSpace(PA,sizeof(XLLP_GPIO_T),FALSE);
//or
/***
* Alternate Way for Register Allocation
*
* v_pGPIO = (volatile XLLP_GPIO_T *)VirtualAlloc(0, sizeof *v_pGPIO, MEM_RESERVE, PAGE_NOACCESS);
* if (v_pGPIO == NULL)
* {
* ERRORMSG(1,(TEXT("PBT: VirtualAlloc failed!\r\n")));
* } else {
* if (!VirtualCopy((PVOID)v_pGPIO, (PVOID)(BULVERDE_BASE_REG_PA_GPIO >> 8), sizeof *v_pGPIO, PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE))
* {
* ERRORMSG(1,(TEXT("PBT: VirtualCopy failed!\r\n")));
* VirtualFree((PVOID)v_pGPIO, 0, MEM_RELEASE);
* v_pGPIO = NULL;
* }
* }
*
***/
if(v_pIOPregs)
{
   RetValue = TRUE;
}
return (RetValue);
}

static DWORD
PowerButtonIntrThread(PVOID pArg)
{
// Configure Interrupt When required
EnablePowerButtonInterrupt();
// Create the Event to be mapped with SYSINTR
PwrButtonIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
//
// Request a SYSINTR/IRQ mapping from the OAL.
//
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &PwrButtonIrq, sizeof PwrButtonIrq, &PwrButtonSysIntr, sizeof PwrButtonSysIntr, NULL))
{
     RETAILMSG(1, (TEXT("PBT: Error! Failed to Map sysintr value for power button interrupt.\r\n")));
     return(0);
}
if (!(InterruptInitialize(PwrButtonSysIntr, PwrButtonIntrEvent, 0, 0)))
{
    RETAILMSG(1, (TEXT("ERROR: PwrButton: Interrupt initialization failed.\r\n")));
}
// Handle power button presses.
for (;;)
{
    WaitForSingleObject(PwrButtonIntrEvent, INFINITE);
     if (PowerButtonIsPushed()) // Guard against noise triggering
    {
        Sleep(200); // Check again in 200 Ms. to fend off a continuous press.
        if (!PowerButtonIsPushed()) // Must be held+released in less than .2 seconds.
        {
            //
            // Set the Power state to suspend Mode
            //
             RETAILMSG(1,(TEXT("PBT: Requesting power manager to suspend...\r\n")));
             SetSystemPowerState(NULL, POWER_STATE_SUSPEND, POWER_FORCE);
        }
        else
            RETAILMSG(1,(TEXT("PBT: Button held too long \r\n")));
    }
    else
        RETAILMSG(1,(TEXT("PBT:noise triggered it\r\n")));
     InterruptDone(PwrButtonSysIntr);
}
}

DWORD
PBT_Init(DWORD dwContext)
{
DWORD IDPowerButtonThread;
// Initialize All the addresses now to avoid the race conditions
InitializeAddresses();
PowerButtonIntrThreadHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) PowerButtonIntrThread, 0, 0, &IDPowerButtonThread);
if (PowerButtonIntrThreadHandle == 0)
{
     RETAILMSG(1, (TEXT("PBT: CreateThread() Fail\r\n")));
}

return (dwContext);
}

BOOL WINAPI
DllEntry(HANDLE hInstDll, DWORD dwReason, LPVOID lpvReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
     //Disable all the calls to Thread attach an Dettach.
     DisableThreadLibraryCalls((HMODULE) hInstDll);
     break;
}
return (TRUE);
}