付笑宇 张线媚
摘 要:WinIo 3.0软件包只提供了在C#使用WinIo的用例,且没有提供WinIo.lib文件,使得VC 2010下无法直接使用WinIo。在VC 2010下对WinIo 3.0的源码重新进行编译生成WinIo.lib,修改WinIo.h文件后VC 2010下即可直接使用WinIo。给出了一个应用样例,实验结果表明该方法安全、简洁、高效。
关键词:内核态 WDF 驱动程序 I/O端口
中图分类号:TP311 文献标志码:A 文章编号:1672-3791(2015)05(a)-0020-02
在电网实时测控系统研发过程中,需要使用in和out指令对测控单元的端口直接进行读写,但X86CPU的硬件保护机制将I/O指令指定为特权指令,禁止用户态的应用程序访问I/O端口。用户态的应用程序只能通过运行于核心态的设备驱动程序提供的API间接地访问I/O端口,实时性较差。
Windows操作系统为用户提供了WDM (Windows Driver Model)[1]架构,用于编写设备驱动程序。WDM架构异常复杂,要求开发人员对Windows系统内部的管理机制非常熟悉,有丰富的系统级应用开发的经验,驱动程序的调试相对困难,其安全性与效率无法保障。
Yariv Kaplan在2010年发布开源软件WinIo 3.0[2],它采用了非文档化的Windows API(native API)和其它一些底层编程技巧绕过Windows安全保护机制,Windows下的应用程序程序可直接对I/O端口进行操作、
1 WinIo 3.0简介
WinIo 3.0支持32位和64位版的Windows 7/8,它提供了10个库函数,用于对I/O端口和内存物理存储单元的直接读写。WinIo软件包由WinIo.sys、winio_nt.h、Winio.h、WinIo.dll、WinIo.lib(未提供,需要用户在VC2010下生成)等5个核心文件组成。与I/O端口操作相关的4个函数如下:
(1)WinIo库函数初始化。
WINIO_API bool -stdcall InitializeWinIo();
(2)卸载WinIo库函数。
WINIO_API void -stdcall ShutdownWinIo();
(3)I/O端口读操作函数。
WINIO_API bool _stdcall GetPortVal(WORD wPortAddr,PDWORD pdwPortVal,BYTE bSize);
参数wPortAddr为端口地址,16位整型变量或常量;参数pdwPortVal为指向一存放读取端口内容的32位整型变量指针;参数bSize为实际读取的字节数,8位整型变量或常量,取值范围为1、2或4,表示读操作的对象为8位、16位或32位端口。
(4)I/O端口写操作函数。
WINIO_API bool _stdcall SetPortVal(WORD wPortAddr,DWORD dwPortVal, BYTE bSize);
参数wPortAddr为端口地址,16位位整型变量或常量;参数dwPortVal为要写入端口的32位整型变量或常量;参数bSize为实际写出的字节数,取值范围为1、2或4,表示写操作的对象为8位、16位或32位端口。
2 VC 2010下WinIo 3.0的应用
2.1 VC 2010下WinIo.lib的生成
WinIo 3.0软件包的Source文件夹下包含了WinIo的源文件及其所需的资源,其下的DLL文件夹为VC 2010的WinIo项目文件夹。生成WinIo.lib文件的步骤如下。
(1)双击“WinIo.sln”启动WinIo项目。
(2)在VC 2010 IDE下打开“生成”菜单,单击“生成WinIo”。
生成的WinIo.lib文件它存放于“.\Suorce\DLL\Debug”文件夹。注意:VC 2010学习版不能生成WinIo.lib文件。
2.2 VC 2010下WinIo应用环境配置
(1)创建一个Win32控制台应用项目。
启动VC 2010,创建一个“Win32控制台应用程序”的WinIoDemo项目,在VC2010 IDE下打开“生成”菜单,单击“生成WinIoDemo”。
(2)复制WinIo核心文件到制定的文件夹。
将WinIo 3.0软件包的Source文件夹下的winio_nt.h、Winio.h与WinIo.lib复制到WinIoDemo项目文件夹下,WinIo.sys与WinIo.dll复制到WinIoDemo项目文件夹下的Debug文件夹。
(3)修改Winio.h文件。
将Winio.h中的“#include"..\drv\winio_nt.h"”,修改为“#include "winio_nt.h"”;
(4)配置链接器附加依赖项。
在VC 2010 IDE下打开“项目”菜单,单击“属性”,打开项目属性页对话框,打开“配置属性”目录树,选择“链接器”下的“输入”子项,在“附加依赖项”中添加“WinIo.lib”,如图1所示。WinIo应用环境配置完成,用户可以编写自己的源代码进行调试。
2.3 CMOS RAM读写实例
PC机的主板上集成有CMOS实时时钟,为操作系统提供时间信息:年、月、日与时、分、秒。它附有128或256个字节的RAM,用于存放时间与硬盘、内存、显示卡等最基本的硬件配置信息。操作系统启动时,从CMOS实时时钟中读取时间信息作为系统的基准时间。系统断电后由后备的锂电池供电,以保障信息不丢失。
PC机为CMOS实时时钟分配了70H和71H两个8端口,用于访问CMOS RAM,在进行读写操作时,需要先向70H端口写入CMOS RAM单元的地址,然后通过71H端口进行读写操作。
CMOS RAM的0、2、4单元分别存放秒、分、小时信息,7、8、9单元依次存放日、月、年信息,这些数据均采用压缩的BCD码表示。下面的代码是一个完整的利用WinIo实现对I/O端口直接进行读写操作的样例,它先读取CMOS RAM的9号单元的年份信息并显示,然后再向9号单元写入新的年份信息。重新启动PC后,Windows系统则采用新的年份。该样例源代码在32位版的Windows 7/8操作系统平台上,使用VC 2010调试通过。
#include "stdafx.h"
#include "stdio.h"
#include "conio.h"
#include
#include "winio.h"
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwPortVal;
bool bResult;
bResult = InitializeWinIo();
if (bResult)
{
// write CMOS ram addr=9
SetPortVal(0x70, 9, 1);
// read CMOS ram addr=9,year,BCD code
GetPortVal(0x71, &dwPortVal, 1);
printf("Current Year:%4x\n",dwPortVal);
printf("Enter new year:");
scanf("%x",&dwPortVal);
SetPortVal(0x70, 9, 1);
SetPortVal(0x71,dwPortVal , 1); //write new year
SetPortVal(0x70, 9, 1);
GetPortVal(0x71, &dwPortVal, 1); //read new year
printf(" new Year:%4x\n",dwPortVal);
// When you're done using WinIo, call ShutdownWinIo
ShutdownWinIo();
}
else
{
printf("Error during initialization of WinIo.\n");
exit(1);
}
printf("\nPress anyke to continue …");
_getch();
return 0;
}
3 结语
该文给出了一种在VC 2010下使用WinIo 3.0的简洁方法,已有的采用使用WinIo 2.0开发的应用系统,无需修改源代码,在VC 2010下重新编译后即可迁移到安全性更好的WinIo 3.0平台。在实时测控系统的开发中,使用WinIo 3.0,省去了设备驱动程序的开发环节,可以极大的缩短系统的开发周期,降低开发成本,系统的安全性和可靠性也得到了保障。
参考文献
[1] 邰铭 ,武安河,于洪涛.Windows 2000/XP WDM设备驱动程序开发[M].北京:电子工业出版社,2003.
[2] Yariv Kaplan.WinIo[DB/OL].http://www.internals.com,2014-11-02.
[3] 孙颖.随机波浪数据采集与处理技术的研究[D].天津:天津理工大学,2014.
[4] 万振凯,郭建民.大型自动化三维编织复合材料编织机的开发[J].纺织导报,2013(9):64-66,68.
[5] 袁军,谭永东,任俊.利用WinIo实现并口数据通信[J].计算机与现代化,2009(8):49-53.
[6] 冯晓伟,李正生,吴宁.XP系统下CB对ISA总线的读写方法及应用研究[J].自动化与仪器仪表,2011(5):17-19.
[7] 王元强,朱为.一种通过PCI总线配置FPGA的设计方法[J].现代电子技术,2010(2):90-92,95.