硬件中断线总是有限的,我们可能需要在已有的系统上做一些扩展,比如将串口扩展成好几个,有些硬件本身就设计成多个设备共享一条中断线,比如我的系统中两个串口就共享同一个CPU中断,任何一个串口发生中断以后都会触发CPU的同一条中断线,需要判断别的寄存器来确定是哪个串口发生了什么中断。
我们可以在OAL中分析各个中断源,然后返回不同的SYSINTR值,但这种做法扩展性不好。例如,OAL中设值某个中断源最多会产生三个SYSINTR,但以后扩展成了四个设备,有一个设备就无法正常工作了。
WINCE引入了可装载中断处理例程的概念。即在需要与别的设备共享中断的驱动程序中加载一个ISR,一般使用WINCE提供的GIISR即成满足需求。将其安装到内核。OAL中发生中断时调用NKCallIntChain来得到SYSINTR,这个函数会引起系统逐个调用在该IRQ上加载的所有可装载的ISR,当某个ISR认为这个中断是由它引发的时就返回其SYSINTR,否则就返回SYSINTR_CHAIN,系统就会接着调用其它的ISR,甚至所有的ISR都被调用或者有一个ISR返回了正确的SYSINTR。
驱动程序中的调用办法如下(CE帮助文档):
if (InstallIsr) {
// Install ISR handler
g_IsrHandle = LoadIntChainHandler(IsrDll, IsrHandler, (BYTE)Irq);
if (!g_IsrHandle) {
DEBUGMSG(ZONE_ERROR, (L"WAVEDEV: Couldn't install ISR handler\r\n"));
} else {
GIISR_INFO Info;
PVOID PhysAddr;
DWORD inIoSpace = 1; // io space
PHYSICAL_ADDRESS PortAddress = {ulIoBase, 0};
if (!TransBusAddrToStatic(PCIBus, 0, PortAddress, ulIoLen, &inIoSpace, &PhysAddr)) {
DEBUGMSG(ZONE_ERROR, (L"WAVEDEV: Failed TransBusAddrToStatic\r\n"));
return FALSE;
}
DEBUGMSG(ZONE_PDD, (L"WAVEDEV: Installed ISR handler, Dll = '%s', Handler = '%s', Irq = %d, PhysAddr = 0x%x\r\n", IsrDll, IsrHandler, Irq, PhysAddr));
// Set up ISR handler
Info.SysIntr = ulSysIntr;
Info.CheckPort = TRUE;
Info.PortIsIO = TRUE;
Info.UseMaskReg = FALSE;
Info.PortAddr = (DWORD)PhysAddr + ES1371_dSTATUS_OFF;
Info.PortSize = sizeof(DWORD);
Info.Mask = ES1371_INTSTAT_PENDING;
if (!KernelLibIoControl(g_IsrHandle, IOCTL_GIISR_INFO, &Info, sizeof(Info), NULL, 0, NULL)) {
DEBUGMSG(ZONE_ERROR, (L"WAVEDEV: KernelLibIoControl call failed.\r\n"));
}
}
}
这里需要注意一下,因为ISR在内核态运行,Info.PortAddr必须是系统最原始的虚拟地址,即没有用VirtualCopy映射过的,从OEMAddressTable中计算出来的虚拟地址。在这个例子中用TransBusAddrToStatic函数可以直接把物理地址转换成这种地址。而MmMapIoSpace得到是在当前程序空间中的地址,不能使用。而且GIIR要被加载到内核空间,所以在加入到OS包中时需要加上K标志,否则LoadIntChainHandler函数会失败。
本文来自CSDN博客,转载请标明出处: http://blog.csdn.net/Vichard/archive/2009/08/29/4497641.aspx