User Mode Driver Management 介绍(二)

时间:2021-10-28 16:30:04

(接上一篇)

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

 

BOOL CeFsIoControl(

  LPCWSTR pszDir,

  DWORD dwIoControlCode,

  LPVOID lpInBuffer,

  DWORD nInBufferSize,

  LPVOID lpOutBuffer,

  DWORD nOutBufferSize,

  LPDWORD lpBytesReturned,

  LPOVERLAPPED lpOverlapped

);

Parameters

pszDir

[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 Value

TRUE 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向系统注册APIHandle值填充到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;

}

       至此,分析完毕。

2User 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: 传入的就是activekeypath

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 Servicedevice.dll初始化时向系统注册的的API

                   // 这些API包括REFL_DevDeviceIoControlREFL_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);

 

                       // 将上面dumplicateHandle,也即Reflector Service的操作Handle保存到注册表项"ReflectorHandle"

                       // CEDDKBUS Driver中可以通过该handle来操作REFL_DevDeviceIoControlREFL_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.

              // 调用CEDDKAPI来创建一个访问Bus DriverAPI

              HANDLE hParentBus = CreateBusAccessHandle((LPCTSTR)dwInfo);

 

              // 获取memory windowio 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_DevDeviceIoControlREFL_DevCloseFileHandle

HANDLE REFL_CreateDeviceServiceHandle(CReflector * pReflect)

{

     HANDLE hReturn = NULL;

     if (g_pServiceContainer) {      

         UserDriverService * pNewService = new UserDriverService(pReflect);

         if (pNewService && pNewService->Init()) {

              // 获取m_hDevFileApiHandleUserDriverService关联起来的handle

              // 并将其返回给调用者,方便后续对reflserv api的调用

              // 实际上在CReflector::InitEx()中会调用该函数,并把上述的handle存放到注册表键ReflectorHandle

              hReturn = pNewService->CreateAPIHandle(g_pServiceContainer->GetApiSetHandle());

 

              // 注意这里的插入变量pNewService,其指向了类UserDriverService的实例,

              // 每一个类UserDriverService的实例在构造函数中和CReflector * pReflect进行关联

              // 后续调用REFL_DevDeviceIoControl的时候,实际上最终会执行CReflector * pReflectReflService函数

              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,用来和它联系到一块的handlehAPISetHandle,创建的结果就是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.

     // 调用CEDDKAPI来创建一个访问Bus DriverAPI

     HANDLE hParentBus = CreateBusAccessHandle((LPCTSTR)dwInfo);

 

     // 获取memory windowio 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 windowio window创建多个CPhysMemoryWindow实例

// memory windowsio 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就是这些apihandle

         // 有关这一部分内容,可以参照m_hUDriver的定义和初始化[CReflector的构造函数中定义]

         // 其实,这里就是CReflectoruser 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_DevDeviceIoControlDEVFS_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);

    // 这个dwContentDriver Load到内存的时候调用DEVFS_IoControl(IOCTL_USERDRIVER_LOAD)创建

    // 它就是UserDriver实例,用作udevice.exeuser 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 {

              // 终于找到DriverInit()函数了,开心

              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设备的句柄。lpActiveRegPathBus设备的注册表路径,返回值为句柄。

       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为内存空间,0x1IO空间,对于非X86体系的CPU来说,不支持IO空间,也即AddressSpace必须设置为0

    TranslatedAddress      转换后的系统物理地址;

       该函数将一个总线设备上的设备物理地址转换为总线的系统物理地址,会根据Interface_type的类型进行相应的转换,一般用于PCI或者ISA总线。

       对于User Mode Driver来说,如果映射的物理地址空间不在注册表中声明的话,则会映射失败。

/*

其实下面的这个代码是.0上的代码,其中调用的一些API6.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为内存空间,0x1IO空间

    TranslatedAddress      映射后的总线的系统虚拟地址

       该函数将一个总线上的设备物理地址转换为总线的系统虚拟地址,实际上是先调用了TranslateBusAddr函数获得总线的系统物理地址,再调用MmMapIoSpace函数(该函数可以在User Mode下调用)进行虚拟地址映射。

3> 关闭Bus操作Handle

       直接调用函数CloseBusAccessHandle()关闭Handle就行了。

VOID CloseBusAccessHandle(HANDLE hBusAccess)

       该函数用于关闭所访问的总线设备,客户端驱动(Client Driver)会在它的XXX_Deinit函数中调用该函数,hBusAccess是由CreateBusAccessHandle创建的句柄。

 

附:

1.这里所说的User Mode Driver ReflectorReflector Service是同一个概念。

2User Mode DriverHost的典型注册表配置

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,或在此留言。