Windows CE 流驱动实例

    技术2022-05-20  38

    先描述一下流式接口驱动的基本概念:

    流式接口驱动:任何暴露流式接口函数的驱动程序都可以被称作流式接口驱动程序,也就是在驱动程序的DLL中把这些函数作为DLL的导出函数。在流式接口驱动程序中,驱动程序负责把外设抽象成一个文件,而应用程序则使用操作系统提供的文件API对外设进行访问。

     

    流式接口函数:

    XXX_Init()

    XXX_Deinit()

    XXX_Open()

    XXX_Close()

    XXX_IOControl()

    XXX_PowerUp()

    XXX_PowerDown()

    XXX_Read()

    XXX_Write()

    XXX_Seek()

    其中XXX是驱动程序的设备名称。例如串口驱动程序,其名称是COM。因此,XXX_Open在串口当中就被替换为COM_Open

     

    流式驱动程序工作原理:

    第一步:加载驱动程序。加载驱动程序有两种方式:第一种,当系统启动的时候,设备管理器会搜寻注册表的HKEY_LOCAL_MACHINE/Drivers/BuiltIn键下面的子键,并逐一加载子键下的每一个驱动,这一过程称之为BusEnum。第二种,应用程序可以调用ActivateDeviceEx()函数动态加载驱动程序。

    第二步:设备管理器会从注册表的dll键值中获取驱动程序所在的DLL文件名。

    第三步:设备管理器会调用LoadDriver函数把.dll加载到自己(也就是Device.exe)的虚拟地址空间内。

    第四步:设备管理器会在注册表的HKEY_LOCAL_MACHINE/Drivers/Active下面记录所有已经加载的驱动程序记录,通常会包含设备的名称等等,ActivateDeviceEx()的第二个参数就是要在Active键下增加的内容。

    第五步:设备管理器会调用驱动程序中的XXX_Init函数,并把上一步中添加的注册表

    项的完整路径作为XXX_Init函数的第一个参数传入驱动程序内。

    第六步:在XXX_Init中,通常需要对硬件进行一些最基本的初始化操作,例如打开硬件设备、影射硬件的I/O端口或缓存等。

    通过前面六步,流式接口驱动程序已经被成功加载。下面是对驱动程序的操作。

    第七步:应用程序需要使用该设备。首先,它会调用CreateFile(TEXT(XXX1), )来打开设备。CreateFile()函数是在FileSys.exe中实现的,但是FileSys.exe只作简单的判断,如果发现打开的是设备驱动程序而不是一个文件,就会重新把主动权交还给设备管理器。

    第八步:设备管理器会调用驱动程序中的XXX_Open函数来打开设备。在XXX_Open中,驱动程序可能会对硬件进行一些额外的初始化工作,使硬件进入工作状态。

    第九步:XXX_Open函数会把打开设备的结果返回给设备管理器。

    第十步:设备管理器会把XXX_Open返回的结果再返回给应用程序中的CreateFile()函数调用。

    通过第七到第十步,设备已经被成功的打开,接下来已经可以对设备进行读、写和控制操作了。我们以从设备中读取数据为例,进一步说明流式接口驱动的工作原理。

    第十一步:应用程序使用第七步CreateFile调用返回的句柄作为ReadFile的第一个参数,来向设备发送读请求,同样ReadFile要经过FileSys.exe转发给设备管理器。

    第十二步:设备管理器调用驱动程序中的XXX_Read函数,来读取设备的数据信息。

    第十三步:在流式驱动程序中,XXX_Read函数可以与硬件交互,从硬件中读取必要的信息。然后返回给设备管理器,再返回给应用程序。

    当应用程序不再使用该设备的时候,它可以调用CloseHandle()把设备关闭。

    当系统不再使用该设备的时候,应用程序可以调用DeactivateDevice()函数把该驱动程序卸载。这时,设备管理器会负责把.dlldevice.exe的虚拟地址空间中移除,并且会从HKEY_LOCAL_MACHINE/Drivers/Active键下移除对该设备驱动的记录。这样,流式接口驱动程序的完整生命周期就结束了。

     

    实现流式接口驱动程序通常只需四个步骤:

    1. 为流式接口驱动程序选择一个前缀。

    2. 实现流式接口驱动DLL所必需的接口函数。

    3. 编写DLL的导出函数定义文件.DEF

    4. 为驱动程序配置注册表。

     

     

     

    好,下面开始进入正题,如何创建一个流式驱动程序及测试,该驱动程序并不操作实际的硬件,只是作为一个示例。

    1.       先用PB创建一个基于模拟器平台的OS镜像,并生成OS镜像文件。

    2.       PB创建一个新动态库工程MyDriver,选择A simple Windows CE DLL project

    3.       MyDriver.def中加入如下内容:

    LIBRARY MyDriver

     

    EXPORTS

       DEM_Init

       DEM_Deinit

       DEM_Open

       DEM_Close

       DEM_IOControl

       DEM_PowerUp

       DEM_PowerDown

       DEM_Read

       DEM_Write

       DEM_Seek

     

    4.        MyDriver.cpp中加入如下内容:

     

    // MyDriver.cpp : Defines the entry point for the DLL application.

    //

     

    #include "stdafx.h"

     

    DWORD DEM_Init(LPCTSTR pContext, LPCVOID lpvBusContext);

    BOOL DEM_Deinit( DWORD hDeviceContext );

    DWORD DEM_Open( DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode );

    BOOL DEM_Close( DWORD hOpenContext );

    BOOL DEM_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut );

    void DEM_PowerUp( DWORD hDeviceContext );

    void DEM_PowerDown( DWORD hDeviceContext );

    DWORD DEM_Read( DWORD hOpenContext, LPVOID pBuffer, DWORD Count );

    DWORD DEM_Write( DWORD hOpenContext, LPCVOID pBuffer, DWORD Count );

    DWORD DEM_Seek( DWORD hOpenContext, long Amount, WORD Type );

     

    #define IOCTL_DRIVER_DEMO   42

    // Not exposed by the Device Driver

    void DBGOut(DWORD dwValue);

     

    HANDLE hMem=NULL;

    DWORD dwCount;

    BOOL APIENTRY DllMain( HANDLE hModule,

                           DWORD  ul_reason_for_call,

                           LPVOID lpReserved

                                        )

    {

           switch ( ul_reason_for_call )

           {

           case DLL_PROCESS_ATTACH:

                  OutputDebugString(L"MyDriver - DLL_PROCESS_ATTACH/n");

                  break;

           case DLL_PROCESS_DETACH:

                  OutputDebugString(L"MyDriver - DLL_PROCESS_DETACH/n");

                  break;

           case DLL_THREAD_ATTACH:

                  OutputDebugString(L"MyDriver - DLL_THREAD_ATTACH/n");

                  break;

           case DLL_THREAD_DETACH:

                  OutputDebugString(L"MyDriver - DLL_THREAD_DETACH/n");

                  break;  

           }

        return TRUE;

    }

     

    DWORD DEM_Init( LPCTSTR pContext, LPCVOID lpvBusContext)

    {

           OutputDebugString(L"MyDriver - DEM_Init - Context: ");

           OutputDebugString(pContext);

           OutputDebugString(L"/n");

          

           OutputDebugString(L"MyDriver - ~ DEM_Init/n");

           return 0x1234;

    }

     

    BOOL DEM_Deinit( DWORD hDeviceContext )

    {

           OutputDebugString(L"MyDriver - DEM_Deinit/n");

          

           OutputDebugString(L"MyDriver - ~ DEM_Deinit/n");

           return TRUE;

    }

     

    DWORD DEM_Open( DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode )

    {

           OutputDebugString(L"MyDriver - DEM_Open/n");

           OutputDebugString(L"hDeviceContext - ");

           DBGOut(hDeviceContext);

           OutputDebugString(L"/n");

          

           OutputDebugString(L"MyDriver - ~ DEM_Open/n");

           return 0x5678;

    }

     

    BOOL DEM_Close( DWORD hOpenContext )

    {

           OutputDebugString(L"MyDriver - DEM_Close/n");

           OutputDebugString(L"hOpenContext - ");

           DBGOut(hOpenContext);

           OutputDebugString(L"/n");

          

           OutputDebugString(L"MyDriver - ~ DEM_Close/n");

          

           return TRUE;

    }

     

    BOOL DEM_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut )

    {

           OutputDebugString(L"MyDriver - DEM_IOControl/n");

           OutputDebugString(L"hOpenContext - ");

           DBGOut(hOpenContext);

           OutputDebugString(L"/n");

          

           switch (dwCode) {

           case IOCTL_DRIVER_DEMO:

                  {

                         OutputDebugString(L"DRIVER DEMO IOCTL.../n");

                         // reverse the string...

                         HANDLE hTemp=LocalAlloc(LPTR,dwLenIn+1);

                         memset(hTemp,0x00,dwLenIn+1);

                         TCHAR *tcOut=(TCHAR*)hTemp;

                         TCHAR *tcIn=(TCHAR*)pBufIn;

                         DWORD dwChars=dwLenIn/2;

                         for (DWORD x=0;x < dwChars;x++) {

                                tcOut[x]=tcIn[dwChars-x-1];

                         }

                         memcpy(pBufOut,hTemp,dwLenIn);

                         LocalFree(hTemp);

                         *pdwActualOut=dwLenIn;

                  }

                  break;

           default:

                  OutputDebugString(L"Unknown IOCTL/n");

                  break;

           }

          

           OutputDebugString(L"MyDriver - ~ DEM_IOControl/n");

           return TRUE;

    }

     

    void DEM_PowerUp( DWORD hDeviceContext )

    {

           OutputDebugString(L"MyDriver - DEM_PowerUp/n");

           OutputDebugString(L"hDeviceContext - ");

           DBGOut(hDeviceContext);

           OutputDebugString(L"/n");

          

           OutputDebugString(L"MyDriver - ~ DEM_PowerUp/n");

    }

     

    void DEM_PowerDown( DWORD hDeviceContext )

    {

           OutputDebugString(L"MyDriver - DEM_PowerDown/n");

           OutputDebugString(L"hDeviceContext - ");

           DBGOut(hDeviceContext);

           OutputDebugString(L"/n");

          

           OutputDebugString(L"MyDriver - ~ DEM_PowerDown/n");

    }

     

    DWORD DEM_Read( DWORD hOpenContext, LPVOID pBuffer, DWORD Count )

    {

           DWORD dwRetCount=0xffff;      // default to error

           OutputDebugString(L"MyDriver - DEM_Read/n");

           OutputDebugString(L"hOpenContext - ");

           DBGOut(hOpenContext);

           OutputDebugString(L"/n");

           if (NULL != hMem) {

                  dwRetCount=dwCount;

                  memcpy(pBuffer,hMem,dwCount);

           }

           OutputDebugString(L"MyDriver - ~ DEM_Read/n");

          

           return dwRetCount;

    }

     

    DWORD DEM_Write( DWORD hOpenContext, LPCVOID pBuffer, DWORD Count )

    {

           OutputDebugString(L"MyDriver - DEM_Write/n");

           OutputDebugString(L"hOpenContext - ");

           DBGOut(hOpenContext);

           OutputDebugString(L"/n");

          

           if (NULL != hMem) {

                  LocalFree(hMem);

           }

          

           hMem=LocalAlloc(LPTR,Count);

           memcpy(hMem,pBuffer,Count);

           dwCount=Count;

          

           OutputDebugString(L"MyDriver - ~ DEM_Write/n");

          

           return Count;

    }

     

    DWORD DEM_Seek( DWORD hOpenContext, long Amount, WORD Type )

    {

           OutputDebugString(L"MyDriver - DEM_Seek/n");

           OutputDebugString(L"hOpenContext - ");

           DBGOut(hOpenContext);

           OutputDebugString(L"/n");

          

           OutputDebugString(L"MyDriver - ~ DEM_Seek/n");

          

           return 0;

    }

     

     

    void DBGOut(DWORD dwValue)

    {

           TCHAR tcTemp[10];

           wsprintf(tcTemp,L"%ld",dwValue);

           OutputDebugString(tcTemp);

    }

     

    5.        project.reg文件中加入如下内容:

    [HKEY_LOCAL_MACHINE/Drivers/BuiltIn/Sample]

        "Dll" = "mydriver.Dll"

        "Prefix" = "DEM"

        "Index" = dword:1

        "Order" = dword:0

        "FriendlyName" = "Demo Driver"

        "Ioctl" = dword:0

     

    流式驱动程序算是完成了,编译该工程,生成系统镜像,启动模拟器平台从输出信息里就可以看到驱动程序被加载了。

    780 PID:43f8f22a TID:43f9a002 MyDriver - DLL_PROCESS_ATTACH

        780 PID:43f8f22a TID:43f9a002 MyDriver - DEM_Init - Context:

        780 PID:43f8f22a TID:43f9a002 Drivers/Active/03

        780 PID:43f8f22a TID:43f9a002

        780 PID:43f8f22a TID:43f9a002 MyDriver - ~ DEM_Init

        790 PID:43f8f22a TID:43f9a002 MyDriver - DEM_Open

        790 PID:43f8f22a TID:43f9a002 hDeviceContext -

        790 PID:43f8f22a TID:43f9a002 4660

        790 PID:43f8f22a TID:43f9a002

        790 PID:43f8f22a TID:43f9a002 MyDriver - ~ DEM_Open

        790 PID:43f8f22a TID:43f9a002 MyDriver - DEM_IOControl

        790 PID:43f8f22a TID:43f9a002 hOpenContext -

        790 PID:43f8f22a TID:43f9a002 22136

        790 PID:43f8f22a TID:43f9a002

        790 PID:43f8f22a TID:43f9a002 Unknown IOCTL

        790 PID:43f8f22a TID:43f9a002 MyDriver - ~ DEM_IOControl

        790 PID:43f8f22a TID:43f9a002 MyDriver - DEM_Close

        790 PID:43f8f22a TID:43f9a002 hOpenContext -

        790 PID:43f8f22a TID:43f9a002 22136

        800 PID:43f8f22a TID:43f9a002

        800 PID:43f8f22a TID:43f9a002 MyDriver - ~ DEM_Close

        920 PID:43f8f22a TID:e3f24002 MyDriver - DLL_THREAD_ATTACH

        960 PID:43f8f22a TID:83f1f7aa MyDriver - DLL_THREAD_ATTACH

        960 PID:43f8f22a TID:63f1ffc6 MyDriver - DLL_THREAD_ATTACH

        960 PID:43f8f22a TID:63f1ffc6 MyDriver - DLL_THREAD_DETACH

        980 PID:43f8f22a TID:83f1f7aa MyDriver - DLL_THREAD_DETACH

     

    下面再建一个测试工程。

    1.       PB创建一个WCE Application工程MyApp,选择A typical Hello World! application

    2.       为该应用程序添加菜单资源:

    IDR_MENU MENU DISCARDABLE

    BEGIN

        POPUP "File"

        BEGIN

            MENUITEM "Exit",                        ID_FILE_EXIT

            MENUITEM "Write",                       ID_DRIVER_WRITE

            MENUITEM "Read",                        ID_DRIVER_READ

            MENUITEM "Io Control",                  ID_DRIVER_IOCTL

        END

    END

    3.       MyApp.cpp的内容如下:

    // MyApp.cpp : Defines the entry point for the application.

    //

     

    #include "stdafx.h"

    #include "resource.h"

    #include <commctrl.h>

     

    #pragma comment(lib, "commctrl")

    #define MAX_LOADSTRING 100

    #define IDC_CMDBAR 0x101

    HWND hWndCmdBar;

     

    void WriteToDriver( );

    void ReadFromDriver( );

    void HandleIOCTL( );

     

    #define IOCTL_DRIVER_DEMO   42

     

    // Global Variables:

    HINSTANCE hInst;                                                        // current instance

    TCHAR szTitle[MAX_LOADSTRING];                                                        // The title bar text

    TCHAR szWindowClass[MAX_LOADSTRING];                                                  // The title bar text

     

    // Forward declarations of functions included in this code module:

    ATOM                          MyRegisterClass(HINSTANCE hInstance);

    BOOL                          InitInstance(HINSTANCE, int);

    LRESULT CALLBACK  WndProc(HWND, UINT, WPARAM, LPARAM);

    void WriteToDriver( )

    {

           DWORD dwWritten;

           TCHAR *tcString=L"Demo String...";

           HANDLE hDrv=CreateFile(L"DEM1:",GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

           if (INVALID_HANDLE_VALUE == hDrv) {

                  OutputDebugString(L"Failed to open Driver.../n");

           } else {

                  WriteFile(hDrv,(LPVOID)tcString,lstrlen(tcString)*sizeof(TCHAR),&dwWritten,NULL);

           }

           CloseHandle(hDrv);

    }

     

    void ReadFromDriver( )

    {

           DWORD dwRead;

           TCHAR tcTemp[30];

           HANDLE hDrv=CreateFile(L"DEM1:",GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

           if (INVALID_HANDLE_VALUE == hDrv) {

                  OutputDebugString(L"Failed to open Driver.../n");

           } else {

                  memset(tcTemp,0x00,30*sizeof(TCHAR));

                  ReadFile(hDrv,tcTemp,30,&dwRead,NULL);

                  MessageBox(NULL,tcTemp,L"Demo Data",MB_OK);

           }

           CloseHandle(hDrv);

    }

     

    void HandleIOCTL( )

    {

           HANDLE hDrv=CreateFile(L"DEM1:",GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

           TCHAR tcBuffer[10];

           DWORD dwBytesReturned;

          

           lstrcpy(tcBuffer,L"Hello");

          

           BOOL bRet=DeviceIoControl(

                  hDrv,

                  IOCTL_DRIVER_DEMO,

                  tcBuffer,

                  lstrlen(tcBuffer)*sizeof(TCHAR),

                  tcBuffer,

                  lstrlen(tcBuffer)*sizeof(TCHAR),

                  &dwBytesReturned,

                  NULL);

          

           MessageBox(NULL,tcBuffer,L"IOCTL Test",MB_OK);

           CloseHandle(hDrv);

          

    }

     

    int WINAPI WinMain(HINSTANCE hInstance,

                         HINSTANCE hPrevInstance,

                         LPTSTR     lpCmdLine,

                         int       nCmdShow)

    {

          // TODO: Place code here.

           MSG msg;

           HACCEL hAccelTable;

     

           // Initialize global strings

           LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);

           LoadString(hInstance, IDC_MyApp, szWindowClass, MAX_LOADSTRING);

           MyRegisterClass(hInstance);

     

           // Perform application initialization:

           if (!InitInstance (hInstance, nCmdShow))

           {

                  return FALSE;

           }

     

           hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_MyApp);

     

           // Main message loop:

           while (GetMessage(&msg, NULL, 0, 0))

           {

                  if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

                  {

                         TranslateMessage(&msg);

                         DispatchMessage(&msg);

                  }

           }

     

           return msg.wParam;

    }

     

     

     

    //

    //  FUNCTION: MyRegisterClass()

    //

    //  PURPOSE: Registers the window class.

    //

    //  COMMENTS:

    //

    //    This function and its usage is only necessary if you want this code

    //    to be compatible with Win32 systems prior to the 'RegisterClassEx'

    //    function that was added to Windows 95. It is important to call this function

    //    so that the application will get 'well formed' small icons associated

    //    with it.

    //

    ATOM MyRegisterClass(HINSTANCE hInstance)

    {

           WNDCLASS wc;

     

        wc.style = CS_HREDRAW | CS_VREDRAW;

        wc.lpfnWndProc = (WNDPROC) WndProc;

        wc.cbClsExtra = 0;

        wc.cbWndExtra = 0;

        wc.hInstance = hInstance;

        wc.hIcon = 0;

        wc.hCursor = 0;

        wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);

        wc.lpszMenuName = 0;

        wc.lpszClassName = szWindowClass;

     

           return RegisterClass(&wc);

    }

     

    //

    //   FUNCTION: InitInstance(HANDLE, int)

    //

    //   PURPOSE: Saves instance handle and creates main window

    //

    //   COMMENTS:

    //

    //        In this function, we save the instance handle in a global variable and

    //        create and display the main program window.

    //

    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

    {

       HWND hWnd;

     

       hInst = hInstance; // Store instance handle in our global variable

     

       hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,

          0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

     

       if (!hWnd)

       {

          return FALSE;

       }

     

       ShowWindow(hWnd, nCmdShow);

       UpdateWindow(hWnd);

     

       return TRUE;

    }

     

    //

    //  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)

    //

    //  PURPOSE:  Processes messages for the main window.

    //

    //  WM_COMMAND    - process the application menu

    //  WM_PAINT     - Paint the main window

    //  WM_DESTROY      - post a quit message and return

    //

    //

    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

    {

           PAINTSTRUCT ps;

           HDC hdc;

           TCHAR szHello[MAX_LOADSTRING];

           LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

     

           switch (message)

           {

           case WM_COMMAND:

                  switch(LOWORD(wParam)) {

                  case ID_FILE_EXIT:

                         PostQuitMessage(0);

                break;

                  case ID_DRIVER_WRITE:

                         WriteToDriver( );

                break;

                  case ID_DRIVER_READ:

                         ReadFromDriver( );

                break;

                  case ID_DRIVER_IOCTL:

                         HandleIOCTL( );

                break;

                  }           

                  break;

                  case WM_CREATE:

                         hWndCmdBar=CommandBar_Create(hInst,hWnd,IDC_CMDBAR);

                         CommandBar_InsertMenubar(hWndCmdBar,hInst,IDR_MENU,0);

                         break;

                  case WM_PAINT:

                         hdc = BeginPaint(hWnd, &ps);

                         // TODO: Add any drawing code here...

                         RECT rt;

                         GetClientRect(hWnd, &rt);

                         DrawText(hdc, szHello, _tcslen(szHello), &rt, DT_CENTER);

                         EndPaint(hWnd, &ps);

                         break;

                  case WM_DESTROY:

                         PostQuitMessage(0);

                         break;

                  default:

                         return DefWindowProc(hWnd, message, wParam, lParam);

       }

       return 0;

    }

     

    4.       上面应用程序的详细步骤没有写,这个不会的话还没法学驱动,只能先去学习Windows程序设计了,主要就是实现几个菜单,然后菜单函数如上述3代码中所示就OK

    5.       MyApp.bib文件中内容该为如下(否则操作系统windows目录下看不到MyApp.exe):

    FILES

    MyApp.exe  $(_FLATRELEASEDIR)/MyApp.exe               NK     U

     

    OK,编译应用工程,生成系统镜像。

     


    最新回复(0)