物理中断请求(Interrupt ReQuest,IRQ)
↓
OAL把物理中断信号映射成OEM定义的逻辑中断信号,如SYSINTR_KEYBOARD;
(硬件平台上产生的IRQ可能会不同,但是当键盘中断产生式,这些IRQ被统一转换成SYSINTR_KEYBOARD)
↓
内核模式的中断服务例程(ISR,Interrupt Service Routine)
ISR实际上是处理IRQ中断,然后返回相应的系统中断SYSINTR给内核
↓
用户模式的中断服务线程(Interrupt Service Thread,IST)
内核会根据相应的SYSINTR触发相应的IST来完成中断处理。
1.Windows CE下中断的基本概
大多数外部设备都会产生中断来请求操作系统服务。一般情况下,外部设备并不占用CPU,只有当外设工作时,才会通过中断来打断CPU的当前执行,请求CPU处理外部设备的数据。这时CPU会暂停当前的工作,来处理外部设备提出的请求。例如,用户按下键盘上的箭会产生键盘中断。因为外设会产生中断,所以,外设相对应的驱动程序就要处理这些中断。
在Windows CE中使用逻辑中断(SYSINTR)的概念,当中断发生时,OAL(功能上看,OAL颇似桌面机上的BIOS,具有初始化设备、引导操作系统以及抽象硬件功能等作用。与B10S不同的是,0AL隶属于操作系统)
需要把物理的中断信号映射成OEM定义的逻辑的中断信号。然后供操作系统和驱动程序调用,逻辑中断是对硬件物理中断的很好的抽象。
Windows CE下的中断处理分为两个阶段,
处于内核模式的中断服务例程(ISR,Interrupt Service Routine)
和
处于用户模式的中断服务线程(IST,Interrupt Service Thread)。
ISR与IRQ是一对N的关系,也就是说每个IRQ都对应一个ISR,但是一个ISR可以处理多个IRQ。大致来讲,ISR负责把IRQ转化成逻辑中断并返回给内核,IST则负责中断的逻辑处理。
这里要提到两个概念:IRQ和SYSINTR。IRQ是指物理中断或者叫硬件中断,而SYSINTR指的是系统中断(虚拟中断或者逻辑中断)每一个IRQ会和一个系统中断SYSINTR相对应,当硬件中断产生时,ISR实际上是处理IRQ中断,然后返回相应的系统中断SYSINTR给内核,内核会根据相应的SYSINTR触发相应的IST来完成中断处理。
IRQ和SYSINTR之间的对应关系称为映射,分为静态映射和动态映射。静态映射是指在系统编译时IRQ已经和SYSINTR相对应,一般通过OALIntrStaticTranslate函数来实现。而动态映射是指WinCE系统启动后,动态关联IRQ与SYSINTR,一般通过KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR…)来实现。
SYSINTR的类型在nkintr.h中定义,OEMInterruptHandler函数在处理完中断以后,会返回不同类型的SYSINTR给内核,内核会根据返回值进行下一步操作,分为以下几种类型:
SYSINTR_NOP:表示不需要进行任何处理
SYSINTR_RESCHED:表示要进行一次系统调度
SYSINTR_CHAIN:表示不是该中断源产生,在中断链中寻找下一个中断
SYSINTR_RTC_ALARM:表示RTC报警产生
SYSINTR_TIMING:用于ILTiming测试
SYSINTR_PROFILE:用于系统的profile
SYSINTR_FIRMWARE:用于用户自定义系统中断号,所有自定义的系统中断号都应该基于该值进行累加加1,这些自定义的系统中断号用于和IRQ一一对应
Windows CE中断处理模型
![【Win <wbr>CE中断】Windows <wbr>CE中断处理机制 【Win <wbr>CE中断】Windows <wbr>CE中断处理机制](https://i-blog.csdnimg.cn/blog_migrate/d340ae2dbb14124be1a2759d81af0e8e.gif)
Windows CE中断体系结构
ISR的返回值
SYSINTR_NOP:中断不与设备的任何已注册 ISR 关联。内核启用所有其他中断。
SYSINTR_RESCHED:OS重新调度的计时器到期,OS进行重新调度。
SYSINTR_XXX,逻辑中断号:中断与已知的已注册ISR和设备关联。
2.中断服务例程ISR
ISR是运行在内核中的一段代码。通常是由OEM(OAL,OEM Adaption Layer)实现的。ISR会检查中断并决定如何处理该中断。如果中断需要被内核和IST进一步处理,ISR会返回一个逻辑的中断号SYSINTR_XXX。如果该中断不需要进一步的处理了,ISR只需要返回SYSINTR_NOP给内核。ISR必须非常高效,以避免对设备操作的滞后和对所有低优先级ISR处理的滞后。
从Windows CE 4.0开始,引入了可安装的ISR(IISR,Installable Interrupt Service Routine)。在没有可安装的ISR之前,如果应用程序开发商在通用总线上加载了自己的外设,并且需要处理该外设的中断,通常应用开发商需要找到硬件OEM上请求更改OAL的源代码以添加对词外设的逻辑中断支持。有了IISR之后,应用程序开发商只需要安装自己的ISR就完全可以处理这一切了。同样,有了IISR,多个设备可以共享同一个硬件IRQ了。要将ISR安装到平台中,需要完成两个步骤:
1.调用LoadIntChainHandler函数以加载包含ISR代码的DLL。
2.该ISR需要把IRQ转化为某个SYSINTR_XXX并返回。
LoadIntChainHandler函数将包含IISR代码的动态链接库加载到内核的地址空间中。这意味着代码不能调用任何非内核函数,包括任何C语言运行时库函数。我们必须检查所有代码以确保不需要任何外部库(即使这些库是由编译器自动产生的)。
3.中断服务线程IST
IST线程大多数时候是空闲的,只有操作系统通知IST有中断发生的时候IST才开始工作。这是通过把一个逻辑中断号与一个Win32的同步对象事件相关联实现的,当有中断发生时,操作系统会引发与该逻辑中断相关联的事件。
在IST中,通常会使用到
InterruptInitialize(),
WaitForSingleObject()
与InterruptDone(),
InterruptDisable(),
eSetThreadPriority等几个函数。
InterruptInitialize()函数负责把某个逻辑中断号与一个Event内核对象关联起来,当中断发生时,操作系统会负责引发这个事件。
WaitForSingleObject()函数会阻塞当前的线程,等待某个Event内核对象标识的事件发生。
InterruptDone()函数用来告诉操作系统对该中断的处理已经完成,操作系统可以重新开启该中断。调用此函数标志着一次中断处理结束。
当设备卸载的时候,需要对IST进行清理工作,例如调用InterruptDisable()函数,关闭Event句柄等等。
通常,IST线程运行在普通优先级之上。因此,在IST开始时,通常我们可以用CeSetThreadPriority函数为IST线程设置合适的优先级。
![【Win <wbr>CE中断】Windows <wbr>CE中断处理机制 【Win <wbr>CE中断】Windows <wbr>CE中断处理机制](https://i-blog.csdnimg.cn/blog_migrate/b92577b0db614f91a19075da5a6b8edc.gif)
典型的IST流程
3.中断延迟及实时性
对中断处理的效率是衡量一个嵌入式操作系统实时性优劣的重要标准。在Windows CE的中断处理中,主要存在两类延迟,ISR延迟和IST延迟。它们的定义同样可以在中断体系结构的图中看到。
导致ISR延迟的因素包括:
1. 中断被关闭的时间
2. 总线指令锁定处理器的时间
3. 内核ISR的执行时间加上导向ISR的时间
导致IST延迟的因素包括:
1. ISR延迟时间
2. ISR执行时间
3. OS执行系统调用的时间
4. 调度IST时间
4.中断实现
当驱动程序调用InterruptInitialize()、InterruptDisable()和InterruptDone()函数的时候,操作系统只不过做了个简单的转接,实际真正被调用到的函数是OEMInterruptEnable()、OEMInterruptDisable()和OEMInterruptDone()。这三个函数负责开关中断和通知中断完成。此外,对于ARM平台,由于其CPU处理中断的特殊性,OEMInterruptHandler()和OEMInterruptHandlerFIQ()两个函数就充当了ISR的角色。
OEMInterruptEnable:Enables an interrupt with a specified identifier. This function is called from the InterruptInitialize and InterruptMask functions.
OEMInterruptDisable:Disables an interrupt with a specified identifier. This function is called from the InterruptDisable and InterruptMask functions
OEMInterruptDone:Processes the announcement stating that interrupt processing is done. This function is called from the InterruptDone function.
OEMInterruptHandler:Applies only to the ARM architecture. This is an interrupt-handing function that is called with any interrupt on the ARM platform; it returns a SYSINTR identifier and therefore ISR is not registered in OEMInit on the ARM platform. The role of this function is to determine a corresponding source of the interrupt.
OEMInterruptHandlerFIQ:Applies only to the ARM architecture. Support for Fast Interrupt Query has limitations. It is not used in the BSPs included with Windows Embedded CE.
自从WinCE5.0以后,微软提出了PQOAL架构,OEMXXX的中的中断函数调用如下函数。
1. BOOL OALIntrInit()
该函数为中断初始化函数,会在OEMInit中被调用,用于初始化系统的中断,以及完成一些中断的静态映射。
2. BOOL OALIntrRequestIrqs(DEVICE_LOCATION *pDevLoc, UINT32 *pCount, UINT32 *pIrqs)
pDevLoc:一个DEVICE_LOCATION结构指针,包含设备信息
pCount:作为输入表示最大的IRQ数,作为输出表示实际获得的IRQ数
pIrqs:指向一个实际获得的IRQ数组
该函数用于通过设备的物理地址来得到IRQ信息,一般用于总线设备。
3. BOOL OALIntrEnableIrqs(UINT32 count, const UINT32 *pIrqs)
count:要使能多少个IRQ
pIrqs:要被使能的IRQ数组
该函数用于使能IRQ中断,该函数会被OEMInterruptEnable函数调用。
4. VOID OALIntrDisableIrqs(UINT32 count, const UINT32 *pIrqs)
count:要禁用多少个IRQ
pIrqs:要被禁用的IRQ数组
该函数用于禁用IRQ中断,该函数会被OEMInterruptDisable函数调用。
5. VOID OALIntrDoneIrqs(UINT32 count, const UINT32 *pIrqs)
count:要重新使能多少个IRQ
pIrqs:要被重新使能的IRQ数组
该函数用于重新使能IRQ中断,会被OEMInterruptDone函数调用。
最后还有几个函数要说一下,如下:
BSPIntrInit:被OALIntrInit函数调用
BSPIntrEnableIrq:被OALIntrEnableIrqs函数调用
BSPIntrDisableIrq:被OALIntrDisableIrqs函数调用
BSPIntrDoneIrq:被OALIntrDoneIrqs函数调用
BSPIntrRequestIrqs:被OALIntrRequestIrqs函数调用
实例巩固:
Windows CE6.0中断实验过程
----利用键盘控制LED的驱动程序
1.实验目的:通过本次试验学习Windows CE6.0的中断处理的过程以及熟悉在驱动程序中运行中断的编程。
2.我对Windows CE6.0中断的理解:
Windows® CE将中断处理分成两个步骤:中断服务程序ISR和中断服务线程IST。如果中断被使能,则当中断产生时,内核将调用该中断注册的ISR,ISR执行完后将返回系统中断号,内核检查系统中断号并且设置相关的事件,内核设置相关事件后,相应的IST将开始执行。
3.Windows® CE的处理流程:
(1)如果一个驱动程序要处理一个中断,那么驱动程序首先要建立一个事件(CreateEvent),然后调用InterruptInitialize将该事件与中断号绑定,这一步会使能该中断,OAL中的OEMInerrupteEnable会被调用,如果该函数不返回true的话,InterruptInitialize就会失败。然后驱动程序中的IST就可以使用WaitForSingleObject函数来等待中断的发生。
(2)当一个硬件中断发生之后,操作系统陷入异常,中断向量指示进入CE的异常处理程序,该异常处理程序然后调用OAL的OEMInterruptHandler函数,该函数检测硬件之后,将硬件中断转换为软件的中断号,返回给系统。该中断号就是上面提到的InterruptInitialize中使用的那个中断号。系统得到该中断号之后,就会找到该中断号对应的事件,并唤醒等待相应事件的线程(IST),然后IST就可以在用户态进行中断处理。处理完成之后,IST需要调用InterruptDone来告诉操作系统中断处理结束,操作系统调用OAL中的OEMInterruptDone函数,最后完成中断的处理。
4.在驱动中安装中断的方法:
首先, 在驱动中通过 CreateEvent()函数创建一个 Event 内核对象, 然后通过 InterruptInitialize()
函数负责把某个逻辑中断号与这个 Event 内核对象关联起来。当中断发生时,操作系统负责引发此
Event 事件,函数的原型如下:
InterruptInitialize(DWORD idInt, // SYSINTR中断号
HANDLE hEvent , // 与该中断相关联的事件句柄
LPVOID pvData, // 传给OEMInterruptEnable缓冲指针
DWORD cbData, // 缓冲区的大小
)
然后通过 CreatThread()函数来来创建一个线程,在线程函数中用 WaitForSingleObject()来阻塞
当前的线程,等待某个 Event 内核对象标识的事件发生。当中断发生后,OAL层就会返回逻辑中断,
与逻辑中断相关联的 Event 事件就会被触发,被阻塞的中断线程函数就会就绪开始工作。
InterruptDonce()函数用来告诉操作系统, 对该中断的处理已完成, 操作系统可重新开启该中断。
5.步骤:
1.在vs2005里面新建一个DLL的子项目MyKey,在F:/WINCE600/PLATFORM/SMDK6410/SRC/DRIVERS/目录下
2.添加MyKey.c和MyKey.h文件,编辑源程序,如下:
MyKey.h:
#ifndef _MYKEY_H
#define _MYKEY_H
#ifdef __cplusplus
Extern "C" {
#endif
typedef struct {
volatile S3C6410_GPIO_REG *pGPIOregs;
BOOL FlagExitThrd;
} KEY_PUBLIC_CONTEXT, *PKEY_PUBLIC_CONTEXT;
#ifdef __cplusplus
}
#endif
#endif
MyKey.c:
#include <windows.h>
#include <nkintr.h>
#include <pm.h>
#include <nkintr.h>
#include <bsp.h>
#include <s3c6410.h>
#include <s3c6410_vintr.h>
#include <DrvLib.h>
#include "MyKey.h"
#define Led1Mask 0x01
#define Led2Mask 0x02
#define Led3Mask 0x04
#define Led4Mask 0x08
#define LedAllMask 0x0F
#define Butt1Mask 0x01
#define Butt2Mask 0x02
#define Butt3Mask 0x04
#define Butt4Mask 0x08
#define Butt5Mask 0x10
#define Butt6Mask 0x20
static KEY_PUBLIC_CONTEXT *pPublicKey = NULL;
static volatile UINT32 dwLedFlag = 1;
UINT32 g_SysIntr1 = 0;
UINT32 g_SysIntr2 = 0;
HANDLE g_hEvent1 = NULL;
HANDLE g_hThread1 = NULL;
HANDLE g_hEvent2 = NULL;
HANDLE g_hThread2 = NULL;
INT WINAPI Button1Thread(void)
{
RETAILMSG(1, (TEXT("Button1 Thread Entered ! /r/n")));
while(!pPublicKey->FlagExitThrd)
{
UINT16 ch;
RETAILMSG(1, (TEXT("Button1 KEY_Read: KEY Device Read Successfully./r/n")));
ch = (UINT16)pPublicKey->pGPIOregs->GPNDAT;
RETAILMSG(1,(TEXT("Button1 ReadValue: 0x%x /n"), ch));
RETAILMSG(1, (TEXT("Button1 Thread ! /r/n")));
WaitForSingleObject(g_hEvent1, INFINITE);
if(pPublicKey->FlagExitThrd)
break;
RETAILMSG(1, (TEXT("Button1 Thread Start Running ! /r/n")));
if( dwLedFlag == 1 )
{
dwLedFlag = 0;
pPublicKey->pGPIOregs->GPMDAT |= LedAllMask;
RETAILMSG(1, (TEXT("Button1 pressed---Led ALL On:/r/n")));
}
else
{
dwLedFlag = 1;
pPublicKey->pGPIOregs->GPMDAT &= ~LedAllMask;
RETAILMSG(1, (TEXT("Button1 pressed---Led ALL Off:/r/n")));
}
InterruptDone(g_SysIntr1);
}
RETAILMSG(1, (TEXT("KEY: KEY Button1Thread Exiting/r/n")));
return 0;
} // Key_Button1Thread()
INT WINAPI Button2Thread(void)
{
DWORD LedNum = 1;
RETAILMSG(1, (TEXT("Button2 Thread Entered ! /r/n")));
while(!pPublicKey->FlagExitThrd)
{
UINT16 ch;
RETAILMSG(1, (TEXT("Button2 KEY_Read: KEY Device Read Successfully./r/n")));
ch = (UINT16)pPublicKey->pGPIOregs->GPNDAT;
RETAILMSG(1,(TEXT("Button2 ReadValue: 0x%x /n"), ch));
RETAILMSG(1, (TEXT("Button2 Thread ! /r/n")));
WaitForSingleObject(g_hEvent2, INFINITE);
if(pPublicKey->FlagExitThrd)
break;
RETAILMSG(1, (TEXT("Button2 Thread Start Running ! /r/n")));
if( LedNum == 1 )
{
LedNum = 2;
pPublicKey->pGPIOregs->GPMDAT |= Led1Mask;
RETAILMSG(1, (TEXT("Button2 pressed---Led 1 on:/r/n")));
}
else if ( LedNum == 2 )
{
LedNum = 3;
pPublicKey->pGPIOregs->GPMDAT |= Led2Mask;;
RETAILMSG(1, (TEXT("Button2 pressed---Led 2 On:/r/n")));
}
else if ( LedNum == 3 )
{
LedNum = 4;
pPublicKey->pGPIOregs->GPMDAT |= Led3Mask;;
RETAILMSG(1, (TEXT("Button2 pressed---Led 3 On:/r/n")));
}
else if ( LedNum == 4 )
{
LedNum = 0;
pPublicKey->pGPIOregs->GPMDAT |= Led4Mask;;
RETAILMSG(1, (TEXT("Button2 pressed---Led 4 On:/r/n")));
}
else
{
LedNum = 1;
pPublicKey->pGPIOregs->GPMDAT &= ~LedAllMask;;
RETAILMSG(1, (TEXT("Button2 pressed---Led ALL off:/r/n")));
}
InterruptDone(g_SysIntr2);
}
RETAILMSG(1, (TEXT("KEY: KEY Button2Thread Exiting/r/n")));
return 0;
} // Key_Button2Thread()
BOOL KEY_Deinit(DWORD dwContext)
{
RETAILMSG(1, (TEXT("KEY_DeInit: dwContext = 0x%x/r/n/n"), dwContext));
// inform IST exit status
pPublicKey->FlagExitThrd = TRUE;
// free virtual memory
if(pPublicKey->pGPIOregs )
{
DrvLib_UnmapIoSpace((PVOID)pPublicKey->pGPIOregs);
pPublicKey->pGPIOregs = NULL;
}
if(g_hEvent1)
{
SetEvent(g_hEvent1);
InterruptDisable(g_SysIntr1);
CloseHandle(g_hEvent1);
}
if(g_hEvent2)
{
SetEvent(g_hEvent2);
InterruptDisable(g_SysIntr2);
CloseHandle(g_hEvent2);
}
// Wait for threads to finish
WaitForSingleObject(g_hThread1, INFINITE);
if(g_hThread1)
CloseHandle(g_hThread1);
WaitForSingleObject(g_hThread2, INFINITE);
if(g_hThread2)
CloseHandle(g_hThread2);
LocalFree(pPublicKey);
return (TRUE);
}
PKEY_PUBLIC_CONTEXT KEY_Init(DWORD dwContext)
{
LPTSTR ActivePath = (LPTSTR) dwContext; // HKLM/Drivers/Active/xx
BOOL bResult = TRUE;
DWORD dwHwIntr = 0;
RETAILMSG(1, (TEXT("KEY_Init:dwContext = 0x%x/r/n"), dwContext));
RETAILMSG(1,(TEXT("[KEY] Active Path : %s/n"), ActivePath));
if ( !(pPublicKey = (PKEY_PUBLIC_CONTEXT)LocalAlloc( LPTR, sizeof(KEY_PUBLIC_CONTEXT) )) )
{
RETAILMSG(1,(TEXT("[KEY] Can't not allocate for KEY Context/n")));
return NULL;
}
// GPIO Virtual alloc
pPublicKey->pGPIOregs = (volatile S3C6410_GPIO_REG *)DrvLib_MapIoSpace(S3C6410_BASE_REG_PA_GPIO, sizeof(S3C6410_GPIO_REG), FALSE);
if (pPublicKey->pGPIOregs == NULL)
{
RETAILMSG(1,(TEXT("[KEY] For pGPIOregs: DrvLib_MapIoSpace failed!/r/n")));
bResult = FALSE;
goto CleanUp;
}
//禁止上下拉
pPublicKey->pGPIOregs->GPMPUD = 0x00000000;
//GPM0-GPM3设置成输出
pPublicKey->pGPIOregs->GPMCON = 0x00001111;
//LED0-3熄灭
pPublicKey->pGPIOregs->GPMDAT = 0x0;
//设置EINT0中断
//禁止GPN0和GPN1上下拉
pPublicKey->pGPIOregs->GPNPUD &= ~0x0F;
//GPN0-1设置成外中断
pPublicKey->pGPIOregs->GPNCON |= 0x0a;
//使能外中断EINT0-EINT1
pPublicKey->pGPIOregs->EINT0MASK &= ~0x3;
//EINT0-1下降沿触发中断
pPublicKey->pGPIOregs->EINT0CON0 |= 0x2;
do
{
//Button1 Thread
pPublicKey->FlagExitThrd = FALSE;
g_hEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
g_SysIntr1 = SYSINTR_UNDEFINED;
dwHwIntr = IRQ_EINT0;
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &dwHwIntr, sizeof(DWORD), &g_SysIntr1, sizeof(DWORD), NULL))
{
RETAILMSG(1,(TEXT("[KEY] Failed to request the KEY sysintr./n")));
g_SysIntr1 = SYSINTR_UNDEFINED;
bResult = FALSE;
break;
}
if (!InterruptInitialize(g_SysIntr1, g_hEvent1, NULL, 0))
{
RETAILMSG(1,(TEXT("[KEY] KEY Interrupt Initialization failed!!!/n")));
bResult = FALSE;
break;
}
g_hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Button1Thread, NULL, 0, NULL);
if (g_hThread1 == NULL)
{
RETAILMSG(1,(TEXT("[KEY] Key Button Thread creation error!!!/n")));
bResult = FALSE;
break;
}
//Button2 Thread
g_hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
g_SysIntr2 = SYSINTR_UNDEFINED;
dwHwIntr = IRQ_EINT1;
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &dwHwIntr, sizeof(DWORD), &g_SysIntr2, sizeof(DWORD), NULL))
{
RETAILMSG(1,(TEXT("[KEY] Failed to request the KEY sysintr./n")));
g_SysIntr2 = SYSINTR_UNDEFINED;
bResult = FALSE;
break;
}
if (!InterruptInitialize(g_SysIntr2, g_hEvent2, NULL, 0))
{
RETAILMSG(1,(TEXT("[KEY] KEY Interrupt Initialization failed!!!/n")));
bResult = FALSE;
break;
}
g_hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Button2Thread, NULL, 0, NULL);
if (g_hThread2 == NULL)
{
RETAILMSG(1,(TEXT("[KEY] Key Button2 Thread creation error!!!/n")));
bResult = FALSE;
break;
}
} while (0);
RETAILMSG(1,(TEXT("--[KEY] KEY_Init Function/r/n")));
if(bResult)
{
return pPublicKey;
}
else
{
return NULL;
}
CleanUp:
if (!bResult)
{
if (pPublicKey->pGPIOregs)
{
DrvLib_UnmapIoSpace((PVOID)pPublicKey->pGPIOregs);
pPublicKey->pGPIOregs = NULL;
}
return NULL;
}
}
DWORD KEY_Open(DWORD dwData, DWORD dwAccess, DWORD dwShareMode)
{
RETAILMSG(1, (TEXT("KEY_Open: KEY Device Open Successfully./r/n")));
return(dwData);
}
BOOL KEY_Close(DWORD Handle)
{
RETAILMSG(1, (TEXT("KEY_Close: KEY Device Close Successfully./r/n")));
return (TRUE);
}
DWORD KEY_Read(DWORD Handle, LPVOID pBuffer, DWORD dwNumBytes)
{
UINT16 ch;
DWORD result;
RETAILMSG(1, (TEXT("KEY_Read: KEY Device Read Successfully./r/n")));
ch = (UINT16)pPublicKey->pGPIOregs->GPNDAT;
if( (ch & Butt1Mask) != 0)
{
result = 1;
RETAILMSG(1, (TEXT("Button 1 pressed:/r/n")));
}
else if( (ch & Butt2Mask) != 0)
{
result = 2;
RETAILMSG(1, (TEXT("Button 2 pressed:/r/n")));
}
else
{
result = -1;
RETAILMSG(1, (TEXT("No Button pressed:/r/n")));
}
dwNumBytes = result;
return (dwNumBytes);
}
DWORD KEY_Write(DWORD Handle, LPCVOID pBuffer, DWORD dwNumBytes)
{
RETAILMSG(1, (TEXT("BTN_Write:KEY Device Write Successfully./r/n")));
return(TRUE);
}
DWORD KEY_Seek(DWORD Handle, long lDistance, DWORD dwMoveMethod)
{
return (DWORD)-1;
}
void KEY_PowerDown(void)
{
return;
}
void KEY_PowerUp(void)
{
return;
}
BOOL KEY_IOControl(DWORD hOpenContext,DWORD dwCode,PBYTE pBufIn,DWORD dwLenIn,PBYTE pBufOut,DWORD dwLenOut,PDWORD pdwActualOut)
{
RETAILMSG(1, (TEXT("BTN_IOControl:/r/n")));
return(TRUE);
}
BOOL
DllEntry(
HINSTANCE hinstDll,
DWORD dwReason,
LPVOID lpReserved
)
{
if ( dwReason == DLL_PROCESS_ATTACH )
{
DEBUGMSG (1, (TEXT("[MyKey] Process Attach/r/n")));
}
if ( dwReason == DLL_PROCESS_DETACH )
{
DEBUGMSG (1, (TEXT("[MyKey] Process Detach/r/n")));
}
return(TRUE);
}
3.编写配置文件,包括如下文件sources,MyKey.def,makefile如下:
技巧:可以到其他驱动程序目录下拷贝在加以修改
Sources:
RELEASETYPE=PLATFORM
PREPROCESSDEFFILE=1
TARGETNAME=MyKey
TARGETTYPE=DYNLINK
DEFFILE=MyKey.def
DLLENTRY=DllEntry
TARGETLIBS= /
$(_PROJECTROOT)/cesysgen/sdk/lib/$(_CPUINDPATH)/coredll.lib /
$(_TARGETPLATROOT)/lib/$(_CPUINDPATH)/DriverLib.lib
SOURCES= /
MyKey.c
MyKey.def:
LIBRARY MyKey
EXPORTS
KEY_Init
KEY_Deinit
KEY_Open
KEY_Close
KEY_Read
KEY_Write
KEY_Seek
KEY_IOControl
KEY_PowerDown
KEY_PowerUp
Makefile:
!INCLUDE $(_MAKEENVROOT)/makefile.def
4.修改整体项目的配置文件,以便加入我们的驱动到系统中
(1)。platform.bib文件:添加如下一句到文件的合适位置
MyKey.dll $(_FLATRELEASEDIR)/MyKey.dll NK SHK
(2)。Platform.reg注册表文件:添加如下
[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/MyKey]
"Prefix"="KEY"
"DLL"="MyKey.dll"
"Order"=dword:4
"Index"=dword:1
(3)。修改上层目录的Dirs文件,根据需要添加如下一句:
DIRS=/
DrvLib/
. . .
WIFI_SDIO/
MyKey 注:这是添加到此文件的内容。
5.编译建立此驱动。注意:这里只需要编译驱动目录就可以了,不需要重新编译整个系统。
:
Windows CE下驱动程序的中断处理 先介绍一下WINCE常用的几个概念: 1.IRQ(Interrupt ReQuest)、物理中断请求 2.SYSINTR、逻辑中断 3.ISR(Interrupt Service Routine)、中断服务例程 4.IST(Interrupt Service Thread)、中断服务线程 中断处理 一般中断处理流程: 1.一个中断发生时,微处理器跳转到系统的中断处理模块来处理; 2.中断处理模块禁止微处理器中的所有其他同优先级和较低优先级的中断,然后再调用适当的ISR来处理这一物理中断请求(IRQ); 3.以中断标识位的形式ISR向中断处理模块返回一个逻辑中断,而且通常屏蔽了板级设备驱动中断; 4.当前正在执行的中断一直被硬件所屏蔽,这样可以等待适当的信号的到来,触发IST事件执行。除此之外,中断处理程序重新打开所有微处理器的中断; 5.IST对应处理流程应该被提前设置好,按照流程向硬件提供服务,然后完成中断处理; 6.中断事件处理结束前IST调用interruptdone()函数,这个函数的作用是告诉操作系统对该中断的处理已经完成,不用再屏蔽此中断了。在系统内部,这个函数执行后,还会调用OAL中的oeminterrupdone()函数来宣告中断处理已经完成。 中断服务 中断服务主要有两部分:内核模式中断服务例程(ISR)和用户模式中断服务线程(IST)。在WINCE中,ISR负责寄存器的保护和恢复,所以用C代码就可以实现一个ISR。 一般ISR只要完成下面五项就足够了,不应该在ISR上实现太复杂,太多的功能; 1.在数据丢失或者被另一中断覆盖的情况下,ISR能把数据从设备读取到软件的缓冲区中; 2.ISR清除设备中的中断条件; 3.ISR向内核返回一个逻辑中断号SYSINTR; 4.内核设置一个中断事件来解除对ISR的响应IST的请求; 5.调度器调度响应的IST完成相应的中断处理。 中断屏蔽 中断屏蔽可以使某一个中断执行过程中的所有操作无效,这会导致操作系统对此中断信号将不予理睬。 嵌套中断处理 为了防止高优先级中断的失效和延迟,WIN CE 内核引入了嵌套中断的机制。 共享中断处理 WIN CE 处理共享中断的步骤: 1.在OEMInit中与ISR程序建立关系的一个中断必须调用NKCallIntChain()内核函数。这个函数用于检查已安装ISR的列表,并发出中断信号。 2.如果第一个ISR决定它的关联设备宣告了一个中断,那么它将执行必要的准备工作,处理结束后,这个ISR将为这个设备返回一个逻辑中断号。如果这个ISR没有决定下一步的操作,那么ISR将会返回SYSINTR_NOP。 3.如果ISR判断出它相关的设备没有宣告中断,它回到SYSINTR_CHAIN,将导致NKCallIntChain访问链中下一个ISR的发生,安装ISR的顺序是很重要的,因为它涉及到隐含优先权的问题。 中断处理过程分析 一个完整的中断处理大致需要一下8个步骤: 1.如果内核的捕获异常代码接收一个硬件中断,那么内核接着就会识别一个异常,并操作相应的硬件中断。否则,跳到第二步执行。 2.中断管理器通知ISR禁用当前指定的中断,直到这个中断处理完成后,再启用这个中断。不过在此过程中其它的中断仍然可用。 3.异常管理器调用ISR来决定如何处理这个中断。 4.内核接收ISR的返回值后,就会知道该中断在做什么。 返回值 描述 SYSINTR_NOP 内核不做任何响应 SYSINT_RESCHED 操作系统定时超时,内核重新调度IST SYSINTR_XXX (逻辑中断号) 内核触发中断源ISR后,IST将被唤醒开始工作,接着IST创建一个事件等待中断信号到来。 5.当IST被唤醒后,它做中断处理要做的所有工作,如将数据移到一个缓冲区里或用一种可行的方法来解决此数据。 6.必要时,IST调用各种I/O函数访问硬件来完成对它的相关操作。 7.当IST完成中断处理工作时,它将调用InterruptDone()函数来通知内核。 8.内核调用OAL中的函数OEMInterruptDone()来宣告所有中断处理工作已经完成。OAL通知硬件重新打开该中断。 配置、注册、并撤销一个中断处理程序 当一个设备驱动程序加载时必须要遵循以下步骤执行相关的动作。 1.在内核中注册ISR 2.将物理中断映(IRQ)射为逻辑中断 中断服务例程 中断服务线程 中断通知 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/guly699/archive/2009/10/19/4696722.aspx |