630 likes | 756 Views
一个端口访问器的编写. — Windows XP 驱动程序举例. VS.NET+WIN XP DDK+DriverStudio3.2 开发环境版. 东南大学计算机科学与工程学院 杨全胜. 本讲义假设阅读者已经熟悉 VC++.NET 和 VS.NET(2002) 的使用。如果对 Window XP 驱动程序的编写尚不熟悉,请参阅本人所编写的 《Windows XP 驱动程序编写方法 ——Step by Step》 电子讲义。.
E N D
一个端口访问器的编写 — Windows XP驱动程序举例 VS.NET+WIN XP DDK+DriverStudio3.2开发环境版 东南大学计算机科学与工程学院 杨全胜
本讲义假设阅读者已经熟悉VC++.NET和VS.NET(2002)的使用。如果对Window XP驱动程序的编写尚不熟悉,请参阅本人所编写的《Windows XP驱动程序编写方法——Step by Step》电子讲义。 注意:程序中暗红色显示的部分是我们添加或修改过的语句,其他是DriverWorks自动生成的。蓝色显示的部分是要删除的语句。省略号的部分是不变的。语句中T.Trace(TraceInfo, __FUNCTION__“xxxx”)这样的语句是向调试软件输出信息,该信息可在DriverMonitor或其他调试监视器中看到。
由于一个可能是DriverStudio 3.2中的BUG,所以及时生成的一个空工程项目也无法编译通过,需要对生成的工程文件做以下手工修改: 把MyIOPort项目中的sources文件中的:TARGETLIBS=$ (DDK_LIB_PATH)\ntstrsafe.lib $ (DDK_LIB_PATH)\csq.lib这一行去掉就可以编译通过了
在“MyIOPortDevice.h”文件的 class MyIOPortDevice : public KPnpDevice定义中添加下面的变量定义。 protected: // Member data KIoRange m_ParPortIos; 注意:程序中暗红色显示的部分是我们添加或修改过的语句,其他是DriverWorks自动生成的。蓝色显示的部分是要删除的语句。省略号的部分是不变的。语句中T.Trace(TraceInfo, __FUNCTION__“xxxx”)这样的语句是向调试软件输出信息,该信息可在DriverMonitor或其他调试监视器中看到。
修改下面函数代码: NTSTATUS MyIOPortDevice::OnStartDevice(KIrp I) { T.Trace(TraceInfo, __FUNCTION__"++. IRP %p\n", I); NTSTATUS status = STATUS_SUCCESS; I.Information() = 0; // Get the list of raw resources from the IRP PCM_RESOURCE_LIST pResListRaw = I.AllocatedResources(); // Get the list of translated resources from the IRP PCM_RESOURCE_LIST pResListTranslated = I.TranslatedResources(); // TODO: Add device-specific code to initialize/start your hardware device. // The base class will handle completion of the IRP status = m_ParPortIos.Initialize( 0x00,// PC机I/O地址空间的首地址是00H TRUE,// 在CPU I/O空间内 8,// 设备读写数据的字节宽度 TRUE// 映射到系统空间 ); T.Trace(TraceInfo, __FUNCTION__"--. IRP %p, STATUS %x\n", I, status); return status; }
下面的函数保留和添加下述语句,其他全部删除下面的函数保留和添加下述语句,其他全部删除 NTSTATUS MyIOPortDevice::MYIOPORT_IOCTL_Read_Handler(KIrp I) { T.Trace(TraceInfo, __FUNCTION__"++. IRP %p\n", I); NTSTATUS status = STATUS_SUCCESS; ULONG outputSize = I.IoctlOutputBufferSize(); char buff1[50],buff2[50]; struct ioport { int port; UCHAR data; } *iopt; ULONG fwLength=0; iopt=(ioport *)I.IoctlBuffer();// 指针直接指向IRP的BUFF区域这里进 //来的时候有用户程序的信息,出去的时候放返回信息 //显示从应用程序得到的要读的端口号。 T.Trace(TraceInfo, __FUNCTION__"Read port is 0x%d\n", iopt->port );
// 从端口读一个字节的数据 iopt->data= (UCHAR)m_ParPortIos.inb(iopt->port); fwLength = 8; if (outputSize >= fwLength) // 如果读入缓冲够长 { I.Information() = fwLength;// 返回信息长度 T.Trace(TraceInfo, __FUNCTION__"Read Data is 0x%d\n", iopt->data);// 显示从应用程序得到的命令串。 } else { I.Information() = 0;// 否则信息长度为0 T.Trace(TraceInfo, __FUNCTION__"buff size too small\n"); } T.Trace(NT_SUCCESS(status)?TraceInfo:TraceWarning, __FUNCTION__"--. IRP %p, STATUS %x\n", I, status); return status; }
NTSTATUS MyIOPortDevice::MYIOPORT_IOCTL_Write_Handler(KIrp I) { T.Trace(TraceInfo, __FUNCTION__"++. IRP %p\n", I); NTSTATUS status = STATUS_SUCCESS; char buff1[50],buff2[50]; struct ioport { int port; UCHAR data; } *iopt; ULONG fwLength=0; iopt=(ioport *)I.IoctlBuffer();// 指针直接指向IRP的BUFF区域这里进来的时候有用户程序的信息,出去的时候放返回信息 T.Trace(TraceInfo, __FUNCTION__"Write port is 0x%d, Write data is 0x%d\n", iopt->port , iopt->data ); // 显示从应用程序得到的命令串。 m_ParPortIos.outb(iopt->port,iopt->data);// 向端口写一个字节的数据 I.Information() = 0; T.Trace(NT_SUCCESS(status)?TraceInfo:TraceWarning, __FUNCTION__"--. IRP %p, STATUS %x\n", I, status); return status; }
下面我们来修改应用程序,该程序访问硬件端口来获得CMOS中的数据以及让主板小喇叭发声。首先要修改一下应用程序项目的属性中的字符集。缺省的字符集是“使用 Unicode 字符集”,把它改成“未设置”。 右键点击
接下来我们修改资源文件。下图是系统自动生成的应用程序界面,这并不适合我们的需要。删除这些控件,换上下页显示的控件。接下来我们修改资源文件。下图是系统自动生成的应用程序界面,这并不适合我们的需要。删除这些控件,换上下页显示的控件。 全部删除
List Control Static Text Button 在对话框中分别建立如图的三个按钮,一个列表控件(List Control)和一个静态文本框。其中,列表控件和按钮的属性如下页的图设置。
在“MyIOPortApp.h”文件中增加下列函数声明 UCHAR ReadOneByte(int port); // 从port读一个字节 void WriteOneByte(int port, UCHAR value); // 向端口port写一个字节 BOOL OpenMyDevice(); // 打开设备 void Silence( void ); // 静音 void Sound(DWORD freq ); // 发频率为freq的声音 void OnReadcmos(HWND hDlg); // 读CMOS
在“MyIOPortApp.cpp”文件中增加下列函数: UCHAR ReadOneByte(int port) { char buff[200]; ULONG nOutput; // Count written to bufOutput struct ioport { int port; UCHAR data; } iopt,iopt2; iopt.port=port; if (!DeviceIoControl(g_hDevice, MYIOPORT_IOCTL_Read, &iopt, //输出到驱动程序 sizeof(iopt), //IOCTL_INBUF_SIZE, &iopt2, // 从驱动程序得到返回值 8, &nOutput, NULL) )
{ sprintf(buff,"ERROR: DeviceIoControl returns %0x.", GetLastError()); ::MessageBox(NULL,buff,"错误", MB_OK|MB_ICONSTOP); return 0; } else { return iopt2.data; } }
void WriteOneByte(int port, UCHAR value) { char buff[200]; ULONG nOutput; struct ioport { int port; UCHAR data; } iopt,iopt2; iopt.port=port; iopt.data=value; if (!DeviceIoControl(g_hDevice, MYIOPORT_IOCTL_Write, &iopt, //输出到驱动程序 sizeof(iopt), //IOCTL_INBUF_SIZE, &iopt2, // 从驱动程序得到返回值 8, &nOutput, NULL) )
{ sprintf(buff,"ERROR: DeviceIoControl returns %0x.", GetLastError()); ::MessageBox(NULL,buff,"错误", MB_OK|MB_ICONSTOP); return ; } }
BOOL OpenMyDevice() { DWORD lastError; HDEVINFO hDeviceInfo; DWORD bufferSize; SP_DEVICE_INTERFACE_DATA interfaceData; PSP_DEVICE_INTERFACE_DETAIL_DATA deviceDetail; // Find devices that have our interface hDeviceInfo = SetupDiGetClassDevs( (LPGUID)&GUID_DEVINTERFACE_MYIOPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
if (hDeviceInfo == INVALID_HANDLE_VALUE) { lastError = GetLastError(); MyIOPortOutputText(_T("SetupDiGetClassDevs failed, GetLastError() = %d"), lastError); return FALSE; } // Setup the interface data struct interfaceData.cbSize = sizeof(interfaceData); if(SetupDiEnumDeviceInterfaces(hDeviceInfo,NULL, (LPGUID)&GUID_DEVINTERFACE_MYIOPORT, 0,&interfaceData)) { if (!SetupDiGetDeviceInterfaceDetail( hDeviceInfo,&interfaceData, NULL,0,&bufferSize,NULL)) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { MyIOPortOutputText(_T("Error: couldn't get interface detail, (%d)"), GetLastError()); return FALSE; } } // Allocate a big enough buffer to get detail data deviceDetail= (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(bufferSize); if (deviceDetail == NULL) { MyIOPortOutputText(_T("Error: Buffer allocation failed")); return FALSE; } // Setup the device interface struct deviceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
// Try again to get the device interface detail info if (!SetupDiGetDeviceInterfaceDetail( hDeviceInfo, &interfaceData, deviceDetail, bufferSize, NULL, NULL)) { MyIOPortOutputText(_T("Error: SetupDiGetDeviceInterfaceDetail failed (%d)"), GetLastError()); free(deviceDetail); return FALSE; } SetupDiDestroyDeviceInfoList(hDeviceInfo);
g_hDevice = CreateFile( (LPCTSTR)deviceDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,0); if (g_hDevice == INVALID_HANDLE_VALUE) { MyIOPortOutputText(_T("Error: CreateFile failed for device %s (%d)\n"), deviceDetail->DevicePath, GetLastError()); return FALSE; } MyIOPortOutputText(_T("Opened device %s"), deviceDetail->DevicePath); return TRUE; } MyIOPortOutputText(_T("No devices found")); return FALSE; }
void Sound(DWORD freq ) { UCHAR data; if(freq>=20 && freq<=20000) { freq = 1193181 / freq; data = ReadOneByte(0x61); if((data & 3) == 0) { WriteOneByte(0x61, data | 3); WriteOneByte(0x43, 0xb6); } WriteOneByte(0x42, (UCHAR)(freq%256)); WriteOneByte(0x42, (UCHAR)(freq/256)); } }
void Silence( void ) { UCHAR data; data = ReadOneByte(0x61); WriteOneByte(0x61, data & 0xfc); }
void OnReadcmos(HWND hDlg) { // TODO: Add your control notification handler code here int i,it; char buff[23]; char cmosram[128][200]={ // CMOS每个字节的含义 "目前系统时间的秒", "报警时间秒值", "目前系统时间的分", "报警时间分值", "目前系统时间的小时", "报警时间小时值", "目前星期几", "目前系统日期", "目前系统月份", "目前系统年的后两位",
"状态寄存器A", "状态寄存器B", "状态寄存器C", "状态寄存器D", "诊断状态记录值", "当机复位指示字节", "磁盘驱动器类型:xxxx.... 软驱0类型 0001=360K 0010=1.2M ....xxxx 软驱1类型 0011=720K 0100=1.44M 0110=2.88M", "(海洋板)..x..... 硬盘0Translate 1=Yes 0=No ...x.... 硬盘1Translate 1=Yes 0=No .....x.. 1=Step rate fast 0=Step rate slow ......xx 软驱个数00=1个 01=2个 10=三个 11=四个 ", "硬盘类型:xxxx.... 硬盘驱动器0的类型 1111=使用19h单元 ....xxxx 硬盘驱动器1的类型 1111=使用1Ah单元", "字节 x....... 1=Anti-Virus 硬盘Boot区写保护 0=disable .xxx.... 软驱2类型 ....xxxx 软驱3类型",
"所安装设备的类型:xx...... 00=1个软驱,01=2个软驱,..xx.... 00=单显 01=CGA 10=CGA 11=VGA/EGA,后四位高到低是显示、键盘、协处理器与软件机使能(=1)", "基本内存容量低字节,单位KB", "基本内存容量高字节,单位KB", "扩充内存容量低字节,单位KB", "扩充内存容量高字节,单位KB", "(海洋板)硬盘驱动器0的类型", "(海洋板)硬盘驱动器1的类型", "(海洋板)显示卡类型 VGA/monochrome", "(海洋板)....xxxx 启动顺序0=A:C: 1=C:A: 2=Screen prompt 3=Auto search 4=Network .x...... 486-CPU Cache 0=disable 1= enable ", "(海洋板)x....... 1=键盘使用缺省参数 0=使用本单元值 .xx..... 键盘延时00=0.25秒01=0.5秒10=0.75秒11=1秒 ...xxxxx 键盘重发速率,单位cps",
"(海洋板)硬盘1的柱面数", "(海洋板)硬盘1的柱面数", "(海洋板)硬盘1的磁头数", "(海洋板)硬盘1的扇区数", "(海洋板)硬盘0的柱面数", "(海洋板)硬盘0的柱面数", "(海洋板)硬盘0的磁头数", "(海洋板)硬盘0的扇区数", "(海洋板)AT-Bus clock 0=16.7Mhz 1=13.3Mhz 2=11.1Mhz 3=8.3Mhz 4=6.7Mhz 5=5.6Mhz 6=4.2Mhz", "(海洋板)memory type 00h=60nS 20h=70nS", "串口配置", "并口配置", "未使用", "未使用", "未使用", "未使用",
"标准CMOS校验和", "标准CMOS校验和", "扩充内存容量低字节,单位KB", "扩充内存容量低字节,单位KB", "BCD码的世纪值(年的高2位,如19,20等)", "信息标志", "xxxx.... Shadow of D000 0=Vacant ....xxxx Shadow of C000 0=ROM ", "xxxx.... Shadow of F000 0=ROM ....xxxx Shadow of E000 0=Vacant ", "xxxx.... Shadow of D000 1=WP 0=Read/Write ....xxxx Shadow of C000 1=WP 0=Read/Write ", "xxxx.... Shadow of F000 1=WP 0=Read/Write ....xxxx Shadow of E000 1=WP 0=Read/Write ", "内存大小,单位兆", "(内存大小有关=160/前一单元 )", "口令代码Security Code", "口令代码Security Code",
"xx...... 口令检测方式 0=Disable 1=Setup only 2=Powerup&Setup 3=Bootup&Setup ..xxxxxx Cold-Boot Delay 冷启动延时(单位秒)*2 ", "xxxx.... 4=Full test 5=Quick scan 7=Skip test ....xx.. Xfer-Mode of 硬盘0 0=Standard 1=Poll ......xx Xfer-Mode of 硬盘1 2=Block 3=32-Bit Block ", "10h-3Dh部分单元的按字节检查和,不包括17h,18h,19h,1Ah,26h, 27h,30h,31h,32h,38h,3Ah,3Ch,3Dh单元 ", "10h-3Dh部分单元的按字节检查和,不包括17h,18h,19h,1Ah,26h, 27h,30h,31h,32h,38h,3Ah,3Ch,3Dh单元 ", "保留", "保留", "保留", "保留", "保留", "保留", "保留",