Board logo

标题: Windows CE下驱动开发基础 03 [打印本页]

作者: samwalton    时间: 2014-6-24 14:50     标题: Windows CE下驱动开发基础 03

如果一个硬件驱动程序需要可安装ISR而开发者又不想自己写一个,那么可以利用giisr.dll来实现。除了在注册表中添加如上所示外,还要在驱动程序中调用相关函数注册可安装ISR。伪代码如下:
g_IsrHandle = LoadIntChainHandler(IsrDll, IsrHandler, (BYTE)Irq);GIISR_INFO InfoHYSICAL_ADDRESS PortAddress = {PhysAddr, 0};TransBusAddrToStatic(BusType, dwBusNumber, PortAddress, dwAddrLen, &dwIOSpace, &(PVOID)PhysAddr)Info.SysIntr = dwSysIntr;Info.CheckPort = TRUE;Info.PortIsIO = (dwIOSpace) ? TRUE : FALSE;Info.UseMaskReg = TRUE;Info.PortAddr = PhysAddr + 0x0C;Info.PortSize = sizeof(DWORD);Info.MaskAddr = PhysAddr + 0x10;KernelLibIoControl(g_IsrHandle, IOCTL_GIISR_INFO, &Info, sizeof(Info), NULL, 0, NULL);
LoadIntChainHandler函数负责注册可安装ISR,参数1为DLL名称,参数2为ISR函数名称,参数3为IRQ。TransBusAddrToStatic函数在后面讲。如果要利用giisr.dll作为可安装ISR,必须先填充GIISR_INFO结构体,CheckPort=TRUE表示giisr要检测指定的寄存器来确定当前发出中断的是否是这个设备。PortIsIO表示寄存器地址属于哪个地址空间,FALSE表示是内定空间,TRUE表示IO空间。UseMaskReg=TRUE表示设备有一个掩码寄存器,专用于指定当前设备是否是中断源,也就是发出中断,而MaskAddr表示掩码寄存器的地址。如果对Info.Mask赋值,那么PortAddr表示一个特殊的寄存器地址,这个寄存器的值与Mask的值&运算的结果如果为真,则证明当前设备是中断源,否则返回SYSINTR_CHAIN(表示当前ISR没有处理中断,内核将调用ISR链中下一个ISR),如果UseMaskReg=TRUE,那么MaskReg寄存器的值与PortAddr指定的寄存器的值&运算的结果如果为真,则证明当前设备是中断源。


函数SerInit接着调用函数Ser_InternalMapRegisterAddresses转换IO地址并且映射地址,Ser_InternalMapRegisterAddresses在内部调用系统提供的HalTranslateBusAddress(Isa, 0, ioPhysicalBase, &inIoSpace, &ioPhysicalBase)函数将与总线相关的地址转换为系统地址,参数1为总线类型,参数2为总线号,参数3为要转换的地址(PHYSICAL_ADDRESS类型,实际是LARGE_INTEGER型),参数4指定寄存器地址属于IO地址空间还是物理地址空间,参数5返回转换后的物理地址。观察HalTranslateBusAddress的源码得知如果是在x86平台,这个函数除了把参数3赋给了参数5其余什么都没有做,而非x86平台将inIoSpace的值置为0,表示一定是物理地址。在调用HalTranslateBusAddress前要确定从注册表中得到的寄存器地址到底是属于哪个地址空间的,例如:
ULONG inIoSpace = 1; ///1表示是IO空间PHYSICAL_ADDRESS ioPhysicalBase = {iobase, 0}; ///相当于ioPhysicalBase.LowPart = iobase
在地址转换后就要将转换后的地址映射到驱动程序(一般IST和应用程序一样运行在用户模式)能够访问的虚拟地址空间(0x80000000以下)和ISR能够访问的静态虚拟地址空间中(0x80000000以上)。例如:
////如果地址属于物理地址空间ioPortBase = (PUCHAR)MmMapIoSpace(ioPhysicalBase, Size, FALSE);TransBusAddrToStatic(Isa, 0, ioPhysicalBase, Size, &inIoSpace, ppStaticAddress);
MmMapIoSpace函数负责将物理地址映射到驱动程序能够访问的虚拟地址空间中,通过源码分析MmMapIoSpace在内部分别调用:
pVirtualAddress =VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS);VirtualCopy(pVirtualAddress, (PVOID)(SourcePhys >> 8), SourceSize, PAGE_PHYSICAL | PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE));
VirtualAlloc分配一块和MemLen一样大小的虚拟地址空间,因为参数1为0,所以内核自动分配。一般MemLen小于2MB,所以会在应用程序的地址空间中分配。VirtualCopy负责将硬件设备寄存器的物理地址与VirtualAlloc分配的虚拟地址做一个映射关系,这样驱动程序访问PvirtualAddress实际上就是访问第一个寄存器。因为硬件设备寄存器的物理地址一定是在512MB(CE支持RAM的最大值)以上,所以除了最后的参数要加PAGE_PHYSICAL外,第二个参数物理地址也要右移8位(或者除以256)。映射硬件寄存器当然PAGE_NOCACHE是必须加的。TransBusAddrToStatic函数负责将物理地址映射到ISR能够访问的静态虚拟地址空间中,当出现中断共享时,ISR要负责访问硬件设备的某一个寄存器来判断中断源,所以将寄存器的物理地址映射到静态虚拟地址空间中是必要的(ISR只能访问静态的虚拟地址空间)。所谓静态虚拟地址空间是指在OEMAddressTable中定义的虚拟地址空间(当然是0x80000000以上)。在x86平台一般这个表只定义RAM的物理地址与虚拟地址对应关系,而硬件设备的寄存器地址并不在该表中定义,所以如果要创建一块静态的虚拟地址空间供ISR访问,必须在此之前调用CreateStaticMapping函数在0xC4000000到0xE0000000虚拟地址空间中分配。TransBusAddrToStatic函数在内部就是调用了CreateStaticMapping函数。注:硬件设备的寄存器地址也可以在OEMAddressTable中定义。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0