(接上一篇)
3> 调用User Mode Driver Host API来将Driver Load到内存
CeFsIoControl()实际上是一个对文件系统驱动FSD进行操作的函数,需要传入文件夹名字和IoControlCode。
帮助文档中对该函数的解释如下:
This function sends an I/O control to a file system driver (FSD). It may not be supported by all file system drivers, and not all implementations support all I/O controls. Syntax
ParameterspszDir [in] String representing the system mount point. Set to NULL to access the object store file system. dwIoControlCode [in] File system I/O control code. lpInBuffer [in] Long pointer to a buffer that contains the data required to perform the operation. Set to NULL if the dwIoControlCode parameter specifies an operation that does not require input data. nInBufferSize [in] Size, in bytes, of the buffer pointed to by lpInBuffer. lpOutBuffer [out] Long pointer to a buffer that receives the output data for the operation. Set to NULL if dwIoControlCode does not produce output data. nOutBufferSize [in] Size, in bytes, of the buffer pointed to by lpOutBuffer. lpBytesReturned [out] Long pointer to a variable that receives the size, in bytes, of the data stored in the buffer pointed to by lpOutBuffer. lpOverlapped [in] Ignored. Set to NULL. Return ValueTRUE indicates success. FALSE indicates failure. If this function is not supported by an FSD, it returns FALSE, and GetLastError returns ERROR_NOT_SUPPORTED. |
实际上被执行的DEVFS_IoControl(IOCTL_USERDRIVER_LOAD)代码如下:
// udevice-ioctonrol // 该api已经向系统注册 // 该函数向系统注册后,可以通过CeFsIoControl调用 extern "C" BOOL DEVFS_IoControl(DWORD dwContent, HANDLE hProc, DWORD dwIoControlCode, PVOID pInBuf, DWORD nInBufSize, PVOID pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned, OVERLAPPED *pOverlapped) { //hProc is only from Kernel. BOOL bRet = FALSE; DWORD dwOldLastError = GetLastError(); SetLastError (ERROR_INVALID_PARAMETER); // dwIoControlCode. switch (dwIoControlCode) { case IOCTL_USERDRIVER_LOAD: // 完成一些结构体的建立,并没有真正的开始初始化 // 具体完成的工作就是创建UserDriver对象并将其使用类UserDriverContainer进行维护 // 然后将其地址赋值给((PFNDRIVERLOAD_RETURN)pOutBuf)->dwDriverContext 并返回,同时返回的还有udevice.exe向系统注册api handle if (pInBuf && nInBufSize>=sizeof(FNDRIVERLOAD_PARAM) && pOutBuf && nOutBufSize>= sizeof(FNDRIVERLOAD_RETURN)) { UserDriver * pUserDriver;
// 创建UserDriver并将其加入到g_pUserDriverContainer指向的链表中 pUserDriver = CreateDriverObject(*(PFNDRIVERLOAD_PARAM)pInBuf);
// udevice.exe在下面将ghDevFileApiHandle返回给调用者,其实就是reflector,以方便其操作user mode driver host向系统注册的api if (pUserDriver) { // 这里将udevice.exe中创建的UserDriver实例地址填充到pOutBuf->dwDriverContext ((PFNDRIVERLOAD_RETURN)pOutBuf)->dwDriverContext = (DWORD) pUserDriver ; // We also create Handle base API set. HANDLE hDev = CreateAPIHandle(ghDevFileApiHandle, pUserDriver ); if (hDev!=NULL && hDev!=INVALID_HANDLE_VALUE) { pUserDriver->SetUDriverHandle(hDev); pUserDriver->AddRef(); } // 这里将udevice.exe向系统注册API的Handle值填充到pOutBuf-> hDriversAccessHandle ((PFNDRIVERLOAD_RETURN)pOutBuf)->hDriversAccessHandle = hDev; if (pBytesReturned) *pBytesReturned = sizeof(FNDRIVERLOAD_RETURN); } bRet = (pUserDriver!=NULL); } break; } } |
从上面的这段代码,也可以看到,User Mode Driver的实例UserDriver其实是由DEVFS_IoControl()àCreateDriverObject()创建,也即由udevice.exe创建,通过Reflector Service调用API的方式获取其实例,这也是为什么说User Mode Driver是由User Mode Driver Host直接进行管理的,也是有关描述User Mode Driver的功能框图中总是将User Mode Driver囊括在User Mode Driver Host内部的原因。
正如下面的图中所描述的:
CreateDriverObject()完成创建一个类UserDriver实例的任务,该函数实际上最终会调用到UserDriver::LoadDriver(),在这里将完成将User Mode Driver Load到内存并获取其导出流接口的任务,最终这些流接口的导出函数指针将会记录在UserDriver的成员m_fnInit/ m_fnPreDeinit/m_fnOpen/m_fnClose/m_fnControl函数指针中。
呵呵,分析了这么久,大家终于看到将Driver Load到内存的位置了吧,累死我了。
// 创建UserDriver对象,并将其插入到g_pUserDriverContainer指向的链表中,同时还完成了将driver load到内存中并提取导出函数指针的功能 // 该链表实际上的最小单元结点是类UserDriverContainer inline UserDriver * CreateDriverObject(FNDRIVERLOAD_PARAM& fnDriverLoadParam) { if (g_pUserDriverContainer==NULL) return NULL;
UserDriver* pReturnDriver = CreateUserModeDriver(fnDriverLoadParam) ; //new UserDriver(fnDriverLoadParam,NULL); if (pReturnDriver != NULL && !pReturnDriver->Init()) { delete pReturnDriver; pReturnDriver = NULL; }
if (pReturnDriver && !g_pUserDriverContainer->InsertNewDriverObject(pReturnDriver)) { delete pReturnDriver; pReturnDriver = NULL; } return pReturnDriver; } |
UserDriver * CreateUserModeDriver(FNDRIVERLOAD_PARAM& fnDriverLoadParam) { return new UserDriver(fnDriverLoadParam,NULL); } |
virtual BOOL UserDriver:: Init() { return LoadDriver(); }; |
// 之类完成load user mode driver的过程,但是因为执行init,所以并没有加载到系统中 BOOL UserDriver::LoadDriver() { DEBUGMSG(ZONE_ACTIVE, (_T("UDEVICE!CreateDevice: loading driver DLL '%s'/r/n"), m_fnDriverLoadParam.DriverName)); if (m_hLib == NULL ) { DWORD dwStatus = ERROR_SUCCESS;
// 这里根据注册表的配置决定加载驱动的方式,即LoadDriver() or Loadlibrary() m_hLib = (m_fnDriverLoadParam.dwFlags & DEVFLAGS_LOADLIBRARY) ? (::LoadLibrary(m_fnDriverLoadParam.DriverName)) : (::LoadDriver(m_fnDriverLoadParam.DriverName)); if (!m_hLib) { DEBUGMSG(ZONE_WARNING, (_T("UDEVICE!CreateDevice: couldn't load '%s' -- error %d/r/n"), m_fnDriverLoadParam.DriverName, GetLastError())); dwStatus = ERROR_FILE_NOT_FOUND; } else { LPCTSTR pEffType = m_fnDriverLoadParam.Prefix ; m_fnInit = (pInitFn)GetDMProcAddr(pEffType,L"Init",m_hLib); m_fnPreDeinit = (pDeinitFn)GetDMProcAddr(pEffType,L"PreDeinit",m_hLib); m_fnDeinit = (pDeinitFn)GetDMProcAddr(pEffType,L"Deinit",m_hLib); m_fnOpen = (pOpenFn)GetDMProcAddr(pEffType,L"Open",m_hLib); m_fnPreClose = (pCloseFn)GetDMProcAddr(pEffType,L"PreClose",m_hLib); m_fnClose = (pCloseFn)GetDMProcAddr(pEffType,L"Close",m_hLib); m_fnRead = (pReadFn)GetDMProcAddr(pEffType,L"Read",m_hLib); m_fnWrite = (pWriteFn)GetDMProcAddr(pEffType,L"Write",m_hLib); m_fnSeek = (pSeekFn)GetDMProcAddr(pEffType,L"Seek",m_hLib); m_fnControl = (pControlFn)GetDMProcAddr(pEffType,L"IOControl",m_hLib); m_fnPowerup = (pPowerupFn)GetDMProcAddr(pEffType,L"PowerUp",m_hLib); m_fnPowerdn = (pPowerupFn)GetDMProcAddr(pEffType,L"PowerDown",m_hLib);
// Make sure that the driver has an init and deinit routine. If it is named, // it must have open and close, plus at least one of the I/O routines (read, write // ioctl, and/or seek). If a named driver has a pre-close routine, it must also // have a pre-deinit routine. if (!(m_fnInit && m_fnDeinit) || (m_fnOpen && !m_fnClose) || (!m_fnRead && !m_fnWrite &&!m_fnSeek && !m_fnControl) || (m_fnPreClose && !m_fnPreDeinit)) { DEBUGMSG(ZONE_WARNING, (_T("UDEVICE!CreateDevice: illegal entry point combination in driver DLL '%s'/r/n"), m_fnDriverLoadParam.DriverName)); dwStatus = ERROR_INVALID_FUNCTION; } } DEBUGMSG(ZONE_ACTIVE,(L"UserDriver::LoadDriver: dwStatus = 0x%x",dwStatus)); return (dwStatus == ERROR_SUCCESS); } return FALSE; } |
至此,分析完毕。
2.User Mode Driver的初始化调用
1> 流程概述
User Mode Driver的初始化就是由Device Manager调用Reflector_InitEx(),进而调用到CReflector::InitEx()完成所有Driver初始化的过程。
初始化过分为三步:
第一步:
调用Reflector Service的导出函数REFL_CreateDeviceServiceHandle(CReflector * pReflect)来获取其向系统注册的API Handle,并记录到注册表中,供后续CEDDK Bus Driver使用。
第二步:
获取Driver的注册表信息,包括Memory Window/IO Window/ISR等信息,并去创建Memory Window,供后续Reflector Service检查物理内存访问权限的时候使用。
第三步:
调用CReflector::FnInit()来完成最终的Driver加载动作。
我画了一张流程图,如下图所示:
下面将对这三步操作分别进行描述和分析。
2> 获取Reflector Service向系统注册API Handle
Reflector Service的函数REFL_CreateDeviceServiceHandle(CReflector * pReflect)在Driver初始化的时候调用,用来创建一个类UserDriverService实例,并将其插入到类UDServiceContainer * g_pServiceContainer维护的链表中。然后将UserDriverService实例与Reflector Service向系统注册的API Handle关联起来(This function creates a handle and associates the handle to the specified handle object.)并返回给调用者。
接着,将Reflector Service的操作Handle保存到注册表项"ReflectorHandle"下。
// dwInfo: 传入的就是activekey的path BOOL CReflector::InitEx(DWORD dwInfo, LPVOID lpvParam) { BOOL bRet = FALSE; Lock(); if (m_pUDP) { FNINIT_PARAM fnInitParam; fnInitParam.dwCallerProcessId = GetCallerProcessId(); fnInitParam.dwDriverContent = m_dwData; // Findout the m_dwInfo is Activete Registry or not. CRegistryEdit activeReg(HKEY_LOCAL_MACHINE, (LPCTSTR)dwInfo); if (activeReg.IsKeyOpened() && (SUCCEEDED(StringCbCopy(fnInitParam.ActiveRegistry,sizeof(fnInitParam.ActiveRegistry),(LPCTSTR)dwInfo)))) {
// 实际上也就是Reflector Service的操作handle,可以用来操作Reflector Service在device.dll初始化时向系统注册的的API // 这些API包括REFL_DevDeviceIoControl和REFL_DevCloseFileHandle // REFL_DevDeviceIoControl完成的功能有所有User Mode下不能够完成的有关物理内存和中断函数的操作,实际执行的就是CReflector::ReflService HANDLE hServiceHandle = REFL_CreateDeviceServiceHandle(this); if (hServiceHandle !=NULL ) { HANDLE hClientHandle = NULL; // 下面dumplicate的目的就是hClientHandle = hServiceHandle 的内容,一旦一个内容发生了变化,另外一个也将会变化 BOOL fResult = DuplicateHandle( GetCurrentProcess(),hServiceHandle, (HANDLE)m_pUDP->GetUserDriverPorcessorInfo().dwProcessId,&hClientHandle, 0,FALSE,DUPLICATE_SAME_ACCESS); ASSERT(fResult);
// 将上面dumplicate的Handle,也即Reflector Service的操作Handle保存到注册表项"ReflectorHandle"下 // CEDDK的BUS Driver中可以通过该handle来操作REFL_DevDeviceIoControl和REFL_DevCloseFileHandle // if (fResult) { DWORD dwAccessKey = (DWORD)hClientHandle; BOOL fSuccess = activeReg.RegSetValueEx(DEVLOAD_UDRIVER_REF_HANDLE_VALNAME, DEVLOAD_UDRIVER_REF_HANDLE_VALTYPE, (PBYTE)&dwAccessKey,sizeof(dwAccessKey)); ASSERT(fSuccess); } CloseHandle(hServiceHandle); } // It is copies Registry Correctly. fnInitParam.dwInfo = NULL; } else { fnInitParam.dwInfo = dwInfo; } CRegistryEdit deviceReg((LPCTSTR)dwInfo); if (deviceReg.IsKeyOpened()) { deviceReg.GetIsrInfo(&m_DdkIsrInfo); DDKWINDOWINFO dwi;
// This function creates a handle that can be used for accessing a bus. // 调用CEDDK的API来创建一个访问Bus Driver的API HANDLE hParentBus = CreateBusAccessHandle((LPCTSTR)dwInfo);
// 获取memory window和io window deviceReg.GetWindowInfo(&dwi);
// 为每一个window创建一个类CPhysMemoryWindow对象 InitAccessWindow(dwi,hParentBus); if (hParentBus) CloseBusAccessHandle(hParentBus); } fnInitParam.lpvParam = lpvParam;
// 调用driver的初始化函数 bRet = FnInit(fnInitParam) ; } Unlock(); DEBUGMSG(ZONE_WARNING && !bRet,(L"CReflector::InitEx: return FALSE!")); return bRet; } |
// 这里创建一个handle,可以通过该handle调用REFL_DevDeviceIoControl和REFL_DevCloseFileHandle HANDLE REFL_CreateDeviceServiceHandle(CReflector * pReflect) { HANDLE hReturn = NULL; if (g_pServiceContainer) { UserDriverService * pNewService = new UserDriverService(pReflect); if (pNewService && pNewService->Init()) { // 获取m_hDevFileApiHandle和UserDriverService关联起来的handle // 并将其返回给调用者,方便后续对reflserv api的调用 // 实际上在CReflector::InitEx()中会调用该函数,并把上述的handle存放到注册表键ReflectorHandle下 hReturn = pNewService->CreateAPIHandle(g_pServiceContainer->GetApiSetHandle());
// 注意这里的插入变量pNewService,其指向了类UserDriverService的实例, // 每一个类UserDriverService的实例在构造函数中和CReflector * pReflect进行关联 // 后续调用REFL_DevDeviceIoControl的时候,实际上最终会执行CReflector * pReflect的ReflService函数 if (hReturn!=NULL && g_pServiceContainer->InsertObjectBy(pNewService)==NULL ) { // Fail to insert. CloseHandle(hReturn); hReturn = NULL; } } if (hReturn == NULL && pNewService!=NULL) { delete pNewService; } } ASSERT(hReturn!=NULL); return hReturn; } |
// 帮助文档中提到,该函数用来创建一个特殊的handle,该handle和一个特殊的handle object联系到了一块 // 这里特殊的handle object就是this,用来和它联系到一块的handle是hAPISetHandle,创建的结果就是CreateAPIHandle的返回值 HANDLE UserDriverService::CreateAPIHandle(HANDLE hAPISetHandle) { return( ::CreateAPIHandle(hAPISetHandle, this)); } |
3> 获取注册表信息并建立Memory Window
如上面代码中CReflector::InitEx(DWORD dwInfo, LPVOID lpvParam)实现过程,我将代码段粘贴如下:
CRegistryEdit deviceReg((LPCTSTR)dwInfo); if (deviceReg.IsKeyOpened()) { deviceReg.GetIsrInfo(&m_DdkIsrInfo); DDKWINDOWINFO dwi;
// This function creates a handle that can be used for accessing a bus. // 调用CEDDK的API来创建一个访问Bus Driver的API HANDLE hParentBus = CreateBusAccessHandle((LPCTSTR)dwInfo);
// 获取memory window和io window deviceReg.GetWindowInfo(&dwi);
// 为每一个window创建一个类CPhysMemoryWindow对象 InitAccessWindow(dwi,hParentBus); if (hParentBus) CloseBusAccessHandle(hParentBus); } |
首先调用GetIsrInfo()去获取ISR信息,然后调用GetWindowInfo()去查询Memory信息。并将前者的结果存放在类CReflector变量m_DdkIsrInfo中,后续Reflector Service调用的时候会使用到。
然后调用CEDDK Bus函数CreateBusAccessHandle(active register)来创建Bus操作Handle,然后利用GetWindowInfo()查询的结果去创建类CPhysMemoryWindow实例。
// 这里维护的是一张内存访问的window // 这里根据user传入的memory window和io window创建多个CPhysMemoryWindow实例 // memory windows和io windows都是怎么得到的呢 BOOL CReflector::InitAccessWindow( DDKWINDOWINFO& dwi, HANDLE hParentBus ) { Lock(); // 从这里可以看到,user可以建立多张内存访问的window // dwi.memWindows:Array of dwNumMemWindows DEVICEWINDOW structures, each of which describes a memory resource window for (DWORD dwIndex= 0; dwIndex < dwi.dwNumMemWindows && dwIndex < MAX_DEVICE_WINDOWS; dwIndex++) { PHYSICAL_ADDRESS PhysicalAddress = { dwi.memWindows[dwIndex].dwBase,0 }; CPhysMemoryWindow * pNewWindows = new CPhysMemoryWindow(PhysicalAddress,dwi.memWindows[dwIndex].dwLen, 0,(INTERFACE_TYPE)dwi.dwInterfaceType,dwi.dwBusNumber,hParentBus, m_pPhysicalMemoryWindowList); if (pNewWindows && !pNewWindows->Init()) { delete pNewWindows; pNewWindows = NULL; }; if (pNewWindows) m_pPhysicalMemoryWindowList = pNewWindows; } // dwi.ioWindows:Array of dwNumIoWindows DEVICEWINDOW structures, each of which describes an I/O resource window. for (dwIndex= 0; dwIndex < dwi.dwNumIoWindows&& dwIndex < MAX_DEVICE_WINDOWS; dwIndex++) { PHYSICAL_ADDRESS PhysicalAddress = { dwi.ioWindows[dwIndex].dwBase,0 }; CPhysMemoryWindow * pNewWindows = new CPhysMemoryWindow(PhysicalAddress,dwi.ioWindows[dwIndex].dwLen, 1,(INTERFACE_TYPE)dwi.dwInterfaceType,dwi.dwBusNumber,hParentBus, m_pPhysicalMemoryWindowList); if (pNewWindows && !pNewWindows->Init()) { delete pNewWindows; pNewWindows = NULL; }; if (pNewWindows) m_pPhysicalMemoryWindowList = pNewWindows; } Unlock(); return TRUE; } |
CPhysMemoryWindow::CPhysMemoryWindow( PHYSICAL_ADDRESS PhysicalAddress, ULONG NumberOfBytes,ULONG AddressSpace,INTERFACE_TYPE InterfaceType,ULONG BusNumber, HANDLE hParentBus,CPhysMemoryWindow *pNext) : m_pNextFileFolder(pNext) , m_PhBusAddress(PhysicalAddress) , m_dwSize(NumberOfBytes) , m_AddressSpace(AddressSpace) { m_PhSystemAddresss.QuadPart = 0 ; m_pStaticMappedUserPtr = NULL; m_dwStaticMappedLength = 0; // 该函数将一个总线设备上的设备物理地址转换为总线的系统物理地址,会根据Interface_type的类型进行相应的转换,一般用于PCI或者ISA总线 BOOL bRet= TranslateBusAddr(hParentBus, InterfaceType,BusNumber,m_PhBusAddress,&m_AddressSpace,&m_PhSystemAddresss); } |
需要指出的是,同一个Reflector中可能存在多个类CPhysMemoryWindow的实例,也即多个Memory Window,通过类Reflector:: m_pPhysicalMemoryWindowList可以对其进行遍历。
这些Window会在CEDDK Bus Driver通过DeviceIoControl()调用REFL_DevDeviceIoControl() àCReflector::ReflService()完成内存操作时候使用到。
4> 调用Driver的初始化函数完成加载
上面函数CReflector::InitEx()中调用CReflector::FnDriverLoad,进而调用User Mode Driver Host向系统注册的API来完成Driver的加载任务。
相关代码如下:
// 具体完成的工作就是创建UserDriver对象并将其使用类UserDriverContainer进行维护 // 然后将其地址赋值给((PFNDRIVERLOAD_RETURN)driversReturn)->dwDriverContext 并返回,同时返回的还有udevice.exe向系统注册api handle BOOL CReflector::FnDriverLoad(FNDRIVERLOAD_PARAM& DriverLoadParam, FNDRIVERLOAD_RETURN& driversReturn) { return SendIoControl(IOCTL_USERDRIVER_LOAD,&DriverLoadParam, sizeof(DriverLoadParam),&driversReturn, sizeof(FNDRIVERLOAD_RETURN) ,NULL); } |
// io control of creflector // 该函数究竟调用到哪里,和m_hUDriver密切相关 // 在m_hUDriver初始化之前,调用到DEVFS_IoControl // 初始化之后,调用到UD_DevDeviceIoControl // m_hUDriver的初始化在类CReflector的构造函数的后半部分中完成 BOOL CReflector::SendIoControl(DWORD dwIoControlCode,LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned) { PREFAST_ASSERT(m_pUDP); DWORD dwOldCaller = UTlsPtr()[ PRETLS_DRIVER_DIRECT_CALLER ] ; UTlsPtr()[ PRETLS_DRIVER_DIRECT_CALLER ] = GetCallerProcessId(); BOOL fReturn = FALSE; // m_hUDriver的初始化是在类CReflector的后半段完成的,所以前半段的时候还是会调用到m_pUDP->SendIoControl的 if (m_hUDriver != INVALID_HANDLE_VALUE) // 没错,这里就调用到了UD_DevDeviceIoControl,呵呵,因为m_hUDriver就是这些api的handle // 有关这一部分内容,可以参照m_hUDriver的定义和初始化[CReflector的构造函数中定义] // 其实,这里就是CReflector和user mode driver host进行交互的地方 fReturn = DeviceIoControl(m_hUDriver, dwIoControlCode,lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned,NULL); else // 实际上这里调用的就是DEVFS_IoControl,因为DEVFS_IoControl所在文件中,已经将 fReturn = m_pUDP->SendIoControl(dwIoControlCode,lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned); UTlsPtr()[ PRETLS_DRIVER_DIRECT_CALLER ] = dwOldCaller; return fReturn ; }; |
// 不知道UD_DevDeviceIoControl和DEVFS_IoControl有什么差别 // 前者用于udevice的管理,后者用于device fs的管理 extern "C" BOOL UD_DevDeviceIoControl(DWORD dwContent, DWORD dwIoControlCode, PVOID pInBuf, DWORD nInBufSize, PVOID pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned, OVERLAPPED *pOverlapped) { DWORD dwOldLastError = GetLastError(); SetLastError (ERROR_INVALID_PARAMETER); // 这个dwContent在Driver Load到内存的时候调用DEVFS_IoControl(IOCTL_USERDRIVER_LOAD)创建 // 它就是UserDriver实例,用作udevice.exe和user mode driver进行通信 UserDriver * pUserDriver = (UserDriver *)dwContent; if (!pUserDriver) { ASSERT(FALSE); return FALSE; } BOOL bRet = FALSE; switch (dwIoControlCode) { case IOCTL_USERDRIVER_INIT: // 这里会去执行user mode driver的初始化动作,也即加载动作 if (pInBuf!=NULL && nInBufSize>= sizeof(FNINIT_PARAM)) { bRet = pUserDriver->DriverInit((*(PFNINIT_PARAM) pInBuf)); } break; } } |
// 这里也会去调用user mode driver初始化函数,也就是说driver也可以在这里加载 // 实际上driver的加载就是调用这里完成的 BOOL UserDriver::DriverInit(FNINIT_PARAM& fnInitParam) { BOOL bRet = FALSE; if (m_fnInit && m_dwInitData == 0 ) { // 获取操作handle,这些handle相当的重要呀,呵呵 if (fnInitParam.dwInfo == 0 && m_hReflector == NULL) { // We have ActiveRegistry. DetermineReflectorHandle(fnInitParam.ActiveRegistry); } DWORD dwContent = (fnInitParam.dwInfo!=0 ?fnInitParam.dwInfo : (DWORD)fnInitParam.ActiveRegistry); DWORD dwOldCaller = UTlsPtr()[ PRETLS_DRIVER_DIRECT_CALLER ] ; UTlsPtr()[ PRETLS_DRIVER_DIRECT_CALLER ] = fnInitParam.dwCallerProcessId ; __try { // 终于找到Driver的Init()函数了,开心 m_dwInitData = m_fnInit(dwContent,fnInitParam.lpvParam); bRet = (m_dwInitData!=0); } __except(EXCEPTION_EXECUTE_HANDLER) { bRet = FALSE; } UTlsPtr()[ PRETLS_DRIVER_DIRECT_CALLER ] = dwOldCaller; if (!bRet && m_hReflector) { CloseHandle(m_hReflector); m_hReflector = NULL; } } ASSERT(bRet); return bRet; } |
四.User Mode Driver对物理内存和中断函数的访问
1.创建Bus访问Handle
函数:HANDLE CreateBusAccessHandle(LPCTSTR lpActiveRegPath)
该函数用于创建一个可以访问Bus设备驱动的句柄,一个客户端驱动(Client Driver)会在它的XXX_Init函数中调用该函数来获得Bus设备的句柄。lpActiveRegPath为Bus设备的注册表路径,返回值为句柄。
Sample Code:
CSerialPDD::CSerialPDD(LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj ) : CRegistryEdit(lpActivePath) , m_pMdd(pMdd) , m_pHwObj(pHwObj) { m_hParent = CreateBusAccessHandle(lpActivePath); m_PowerHelperHandle = INVALID_HANDLE_VALUE; m_hPowerLock = NULL; // Initial Open Count. m_lOpenCount = 0; m_ulCommErrors = 0; m_PowerCallbackThread = NULL; if (!GetRegValue(PC_REG_SERIALPRIORITY_VAL_NAME,(LPBYTE)&m_dwPriority256,sizeof(DWORD))) { m_dwPriority256 = DEFAULT_CE_THREAD_PRIORITY+55; } } |
2.物理内存的映射
BOOL TranslateBusAddr(HANDLE hBusAccess, INTERFACE_TYPE InterfaceType, ULONG BusNumber, PHYSICAL_ADDRESS BusAddress, PULONG AddressSpace, PPHYSICAL_ADDRESS TranslatedAddress)
hBusAccess: 总线设备的句柄;
interface_Type: 接口类型或总线类型;
BusNumber: 总线号,实际使用中,这个值为0,可供选择的值有:
typedef enum _INTERFACE_TYPE { InterfaceTypeUndefined = -1, Internal, Isa, Eisa, MicroChannel, TurboChannel, PCIBus, VMEBus, NuBus, PCMCIABus, CBus, MPIBus, MPSABus, ProcessorInternal, InternalPowerBus, PNPISABus, PNPBus, MaximumInterfaceType } INTERFACE_TYPE, *PINTERFACE_TYPE; |
BusAddress: 总线上的物理地址;
AddressSpace: 作为输入,0x0为内存空间,0x1为IO空间,对于非X86体系的CPU来说,不支持IO空间,也即AddressSpace必须设置为0;
TranslatedAddress: 转换后的系统物理地址;
该函数将一个总线设备上的设备物理地址转换为总线的系统物理地址,会根据Interface_type的类型进行相应的转换,一般用于PCI或者ISA总线。
对于User Mode Driver来说,如果映射的物理地址空间不在注册表中声明的话,则会映射失败。
/* 其实下面的这个代码是.0上的代码,其中调用的一些API在6.0上已经被废弃, 但是它的结构比较清晰,便于理解Device Physical Address/Bus Physical Address/Virtual Address之间的转换,所以粘贴出来。 6.0上的代码可以参照下一个函数 */ BOOL CPdd2443Uart::MapHardware() { if (m_pRegVirtualAddr !=NULL) return TRUE;
// Get IO Window From Registry DDKWINDOWINFO dwi; if ( GetWindowInfo( &dwi)!=ERROR_SUCCESS || dwi.dwNumMemWindows < 1 || dwi.memWindows[0].dwBase == 0 || dwi.memWindows[0].dwLen < 0x30) //0x2c) return FALSE; DWORD dwInterfaceType; if (m_ActiveReg.IsKeyOpened() && m_ActiveReg.GetRegValue( DEVLOAD_INTERFACETYPE_VALNAME, (PBYTE)&dwInterfaceType,sizeof(DWORD))) { dwi.dwInterfaceType = dwInterfaceType; }
// Translate to System Address. PHYSICAL_ADDRESS ioPhysicalBase = { dwi.memWindows[0].dwBase, 0}; ULONG inIoSpace = 0; if (TranslateBusAddr(m_hParent,(INTERFACE_TYPE)dwi.dwInterfaceType,dwi.dwBusNumber, ioPhysicalBase,&inIoSpace,&ioPhysicalBase)) { // Map it if it is Memeory Mapped IO. m_pRegVirtualAddr = MmMapIoSpace(ioPhysicalBase, dwi.memWindows[0].dwLen,FALSE); } ioPhysicalBase.LowPart = S3C2443_BASE_REG_PA_INTR ; ioPhysicalBase.HighPart = 0; inIoSpace = 0; if (TranslateBusAddr(m_hParent,(INTERFACE_TYPE)dwi.dwInterfaceType,dwi.dwBusNumber, ioPhysicalBase,&inIoSpace,&ioPhysicalBase)) { m_pINTregs = (S3C2443_INTR_REG *) MmMapIoSpace(ioPhysicalBase,sizeof(S3C2443_INTR_REG),FALSE); } return (m_pRegVirtualAddr!=NULL && m_pINTregs!=NULL); } |
/* 6.0上的代码可以参照下面的Sample Code */ BOOL CBulverdeOTG::MapHardware() { DDKWINDOWINFO dwi; if (GetWindowInfo(&dwi)==ERROR_SUCCESS && dwi.dwNumMemWindows!=0) { if (dwi.memWindows[0].dwBase && dwi.memWindows[0].dwLen>=sizeof(BULVERDE_USBD_REG)) { PHYSICAL_ADDRESS ioPhysicalBase = {dwi.memWindows[0].dwBase,0 }; ULONG AddressSpace =0 ; if (!BusTransBusAddrToVirtual( m_hParent,Internal,0, ioPhysicalBase, sizeof(BULVERDE_USBD_REG),&AddressSpace, (PPVOID)&m_pUSBDReg) || AddressSpace!=0) { m_pUSBDReg = NULL; } AddressSpace = 0 ; if (!BusTransBusAddrToStatic( m_hParent,Internal,0, ioPhysicalBase, sizeof(BULVERDE_USBD_REG),&AddressSpace, &m_pUSBDStaticAddr) || AddressSpace!=0) { m_pUSBDStaticAddr = NULL; } DEBUGMSG(ZONE_OTG_FUNCTION,(TEXT("CBulverdeOTG::MapHardware: m_pUSBDReg = 0x%x,m_pUSBDStaticAddr=0x%x/r/n"),m_pUSBDReg,m_pUSBDStaticAddr)); PHYSICAL_ADDRESS gpioPhysicalBase = {BULVERDE_BASE_REG_PA_GPIO,0 }; v_pGPIORegs = (P_XLLP_GPIO_T) MmMapIoSpace(gpioPhysicalBase, sizeof(XLLP_GPIO_T), FALSE); } } ASSERT(m_pUSBDReg!=NULL && m_pUSBDStaticAddr!=NULL && v_pGPIORegs!=NULL); return (m_pUSBDReg!=NULL && m_pUSBDStaticAddr!=NULL && v_pGPIORegs!=NULL); } |
其中,上面红色标记的函数GetWindowInfo(&dwi)是类CPdd2443UartBase Class CRegistryEdit的一个Method,必须在Class CRegistryEdit进行过初始化之后才能调用,最终调用的是DDKReg_GetWindowInfo()。有关Base Class CRegistryEdit的描述很简单,可以参照Public下的源代码或者网上朋友们的解释。
其实在不建议TranslateBusAddr()和MmMapIoSpace()配合来使用了,因为TransBusAddrToVirtual function instead of calling HalTranslateBusAddress and MmMapIoSpace。
在Windows CE 6.0中,函数TransBusAddrToVirtual ()已经被废弃掉,可以使用函数BusTransBusAddrToVirtual()来替代。
有关解释如下:
BOOL BusTransBusAddrToVirtual(IN HANDLE hBusAccess, IN INTERFACE_TYPE InterfaceType, IN ULONG BusNumber, IN PHYSICAL_ADDRESS BusAddress, IN ULONG Length, IN OUT PULONG AddressSpace, OUT PPVOID MappedAddress)
hBusAccess: 总线设备的句柄
interface_Type: 接口类型或总线类型
BusNumber: 总线号
BusAddress: 总线上的物理地址
Length: 被映射的地址空间的大小
AddressSpace: 0x0为内存空间,0x1为IO空间
TranslatedAddress: 映射后的总线的系统虚拟地址
该函数将一个总线上的设备物理地址转换为总线的系统虚拟地址,实际上是先调用了TranslateBusAddr函数获得总线的系统物理地址,再调用MmMapIoSpace函数(该函数可以在User Mode下调用)进行虚拟地址映射。
3> 关闭Bus操作Handle
直接调用函数CloseBusAccessHandle()关闭Handle就行了。
VOID CloseBusAccessHandle(HANDLE hBusAccess)
该函数用于关闭所访问的总线设备,客户端驱动(Client Driver)会在它的XXX_Deinit函数中调用该函数,hBusAccess是由CreateBusAccessHandle创建的句柄。
附:
1.这里所说的User Mode Driver Reflector和Reflector Service是同一个概念。
2.User Mode Driver及Host的典型注册表配置
User Mode Driver Registry [HKEY_LOCAL_MACHINE/Drivers/BuiltIn/Serial] "SysIntr"=dword:13 "IoBase"=dword:02F8 "IoLen"=dword:8 "DeviceArrayIndex"=dword:0 "Prefix"="COM" "Flags"=dword:10 ;"ProcGroup"=dword:2 "IClass"="{CC5195AC-BA49-48a0-BE17-DF6D1B0173DD}" "Dll"="Com16550.Dll" "Order"=dword:0 "Priority"=dword:0 ; Turn on follows for Installable ISR (isr16550 supporting SOFTWARE FIFO ;"Irq"=dword:3 ;"IsrDll"="isr16550.dll" ;"IsrHandler"="ISRHandler" User Mode Driver Host Registry [HKEY_LOCAL_MACHINE/Drivers/ProcGroup_0002] "ProcName"="udevice.exe" ; Dummy for Service.exe now. "ProcVolPrefix"="$services" "Privilege"=dword:xxxxxx ; Processor Privilege Bit setting. |
任何问题请发送mail到guopeixin@126.com,或在此留言。