先描述一下流式接口驱动的基本概念:
流式接口驱动:任何暴露流式接口函数的驱动程序都可以被称作流式接口驱动程序,也就是在驱动程序的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()函数把该驱动程序卸载。这时,设备管理器会负责把.dll从device.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,编译应用工程,生成系统镜像。