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

#include
#include

#include

#include


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);
}

BOOL
PBT_Close(DWORD Handle)
{
return (TRUE);
}

//
// Device deinit - devices are expected to close down.
// The device manager does not check the return code.
//
BOOL
PBT_Deinit(DWORD dwContext)
{
// We can even Release a SYSINTR/IRQ mapping from the OAL.
return (TRUE);
}

//
// Returns handle value for the open instance.
//
DWORD
PBT_Open(DWORD dwData, DWORD dwAccess, DWORD dwShareMode)
{
return 0;
}

//
// I/O Control function - None implemented in this driver.
//
BOOL
PBT_IOControl(
DWORD Handle,
DWORD dwIoControlCode,
PBYTE pInBuf,
DWORD nInBufSize,
PBYTE pOutBuf,
DWORD nOutBufSize,
PDWORD pBytesReturned
)
{
//Implement any other Functionality need to be implemented
// We can also Implement the Power IOCTL to make the driver
// Capable of Handling Power States and request.
return FALSE;
}

DWORD
PBT_Read(DWORD Handle, LPVOID pBuffer, DWORD dwNumBytes)
{
return (0);
}

DWORD
PBT_Write(DWORD Handle, LPCVOID pBuffer, DWORD dwNumBytes)
{
return (0);
}

DWORD
PBT_Seek(DWORD Handle, LONG lDistance, DWORD dwMoveMethod)
{
return (0);
}

VOID
PBT_PowerUp(VOID)
{
return;
}

VOID
PBT_PowerDown(VOID)
{
return;
}



2 comments:

Vaisakh P S said...

nice blog mukku..
but can u put only the relevant portions of code..
:)
remove the rest of entry points. i.e. power up and power down :)

Vaisakh P S said...

or what you can do is like upload the full source code in some public server and and put a link to that... here
here only mention the relvant portions