34、I/O端口操作
I/O端口操作在Windows操作系统中属于特权命令,必须在内核模式下运行。在DOS中,I/O端口操作主要通过IN/OUT指令来进行。
一、I/O端口操作实现
1、DDK实现I/O端口操作
READ_PORT_UCHAR
The READ_PORT_UCHAR macro reads a byte from the specified port address。
http://msdn.microsoft.com/en-us/library/ff560797%28VS.85%29.aspx
2、工具软件WinIO
第三方库。5个文件。
WinIO.dll 封装了驱动程序调用函数。
WinIO.lib 用来与应用程序链接编译。
WinIO.h 提供了封装函数的声明。
使用时必须把WinIO.sys和应用程序放在同一个目录。
WinIO.VXD与95,98等相关。
#include <Windows.h>
#include <stdio.h>
#include ".\winiolib\WinIo.h"
int main()
{
//打开WinIO驱动
bool bRet = InitializeWinIo();
if (bRet)
{
printf("Load Dirver successfully!\n");
//对0x378端口进行输出操作,8位操作
SetPortVal(0x378,0,1);
//关闭WinIO驱动
ShutdownWinIo();
}
return 0;
}
示例代码 P391
3、端口操作实现
法一:利用DDK提供的6个端口操作函数
// IOCTLS.H -- IOCTL code definitions for fileio driver
// Copyright (C) 1999 by Walter Oney
// All rights reserved
#ifndef IOCTLS_H
#define IOCTLS_H
#ifndef CTL_CODE
#pragma message("CTL_CODE undefined. Include winioctl.h or wdm.h")
#endif
#define READ_PORT CTL_CODE(\
FILE_DEVICE_UNKNOWN, \
0x800, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
#define WRITE_PORT CTL_CODE(\
FILE_DEVICE_UNKNOWN, \
0x801, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
#endif
//.h
typedef struct _DEVICE_EXTENSION {
PDEVICE_OBJECT pDevice;
UNICODE_STRING ustrDeviceName; //设备名称
UNICODE_STRING ustrSymLinkName; //符号链接名
PUCHAR buffer;//缓冲区
ULONG file_length;//模拟的文件长度,必须小于MAX_FILE_LENGTH
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
// function.cpp
#include <windows.h>
#include <stdio.h>
#include "function.h"
// DWORD ReadPort(HANDLE hDevice,DWORD port)
// {
// DWORD dwOutput;
// DWORD dwRead;
// DeviceIoControl(hDevice, IOCTL_READ_PORT_ULONG, &port, 4, &dwOutput, 4, &dwRead, NULL);
// return dwOutput;
// }
// VOID WritePort(HANDLE hDevice,DWORD port,DWORD value)
// {
// PVOID buffer[2];
// buffer[0] = (PVOID)port;
// buffer[1] = (PVOID)value;
// DWORD dwWrite;
// DeviceIoControl(hDevice, IOCTL_WRITE_PORT_ULONG, &port, 8, NULL, 0, &dwWrite, NULL);
// }
// VOID TestDriver(HANDLE hDevice)
// {
// DWORD dwOutput;
// DeviceIoControl(hDevice, IOCTL_TEST, NULL, 0, NULL, 0, &dwOutput, NULL);
// }
//driver.cpp
#pragma PAGEDCODE
NTSTATUS HelloDDKDeviceIOControl(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
KdPrint(("Enter HelloDDKDeviceIOControl\n"));
//得到当前堆栈
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
//得到输入缓冲区大小
ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
//得到输出缓冲区大小
ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
//得到IOCTL码
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
ULONG info = 0;
switch (code)
{ // process request
case READ_PORT:
{
KdPrint(("READ_PORT\n"));
//缓冲区方式IOCTL
//显示输入缓冲区数据
PULONG InputBuffer = (PULONG)pIrp->AssociatedIrp.SystemBuffer;
ULONG port = (ULONG)(*InputBuffer);
InputBuffer++;
UCHAR method = (UCHAR)(*InputBuffer);
//操作输出缓冲区
PULONG OutputBuffer = (PULONG)pIrp->AssociatedIrp.SystemBuffer;
if (method==1)//8位操作
{
*OutputBuffer = READ_PORT_UCHAR((PUCHAR)port);
}else if(method==2)//16位操作
{
*OutputBuffer = READ_PORT_USHORT((PUSHORT)port);
}else if(method==4)//32位操作
{
*OutputBuffer = READ_PORT_ULONG((PULONG)port);
}
//设置实际操作输出缓冲区长度
info = 4;
break;
}
case WRITE_PORT:
{
KdPrint(("WRITE_PORT\n"));
//缓冲区方式IOCTL
//显示输入缓冲区数据
PULONG InputBuffer = (PULONG)pIrp->AssociatedIrp.SystemBuffer;
ULONG port = (ULONG)(*InputBuffer);
InputBuffer++;
UCHAR method = (UCHAR)(*InputBuffer);
InputBuffer++;
ULONG value = (ULONG)(*InputBuffer);
//操作输出缓冲区
PULONG OutputBuffer = (PULONG)pIrp->AssociatedIrp.SystemBuffer;
if (method==1)//8位操作
{
WRITE_PORT_UCHAR((PUCHAR)port,(UCHAR)value);
}else if(method==2)//16位操作
{
WRITE_PORT_USHORT((PUSHORT)port,(USHORT)value);
}else if(method==4)//32位操作
{
WRITE_PORT_ULONG((PULONG)port,(ULONG)value);
}
//设置实际操作输出缓冲区长度
info = 0;
break;
}
default:
status = STATUS_INVALID_VARIANT;
}
// 完成IRP
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = info; // bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("Leave HelloDDKDeviceIOControl\n"));
return status;
}
//main.cpp
#include <windows.h>
#include <stdio.h>
//使用CTL_CODE必须加入winioctl.h
#include <winioctl.h>
#include "..\NT_Driver\Ioctls.h"
int main()
{
HANDLE hDevice =
CreateFile("\\\\.\\HelloDDK",
GENERIC_READ | GENERIC_WRITE,
0, // share mode none
NULL, // no security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL ); // no template
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("Failed to obtain file handle to device: "
"%s with Win32 error code: %d\n",
"MyWDMDevice", GetLastError() );
return 1;
}
DWORD dwOutput ;
DWORD inputBuffer[3] =
{
0x378,//对0x378进行操作
1,//1代表8位操作,2代表16位操作,4代表32位操作
0//输出字节0
};
//类似于Out_8((PUCHAR)0x378,0);
DeviceIoControl(hDevice, WRITE_PORT, inputBuffer, sizeof(inputBuffer), NULL, 0, &dwOutput, NULL);
CloseHandle(hDevice);
return 0;
}
示例代码 P394
[1] 中还提供了一些其它方法
提升用户模式的方法是将应用程序的一个函数指针传递给驱动程序,在驱动程序接收到这个函数指针后,在内核模式下执行此函数。
I/O 允许位图设置可以让不具备足够权限的程序存取I/O端口。I/O允许位图设置是利用一个位代表每个I/O地址。
每个PC系统至少包含一个8253可编程时钟或等价芯片,这个时钟包含三个独立的16位时钟。时钟0用于基本系统时钟,时钟1用于PC系统上的DRAM刷新,时钟2用于一般的应用程序,如扬声器音调控制。
并口设备的操作
操作PC上的并口设备,主要也是设置和读取并口设备对应的I/O端口。数据、状态、控制寄存器。进一步的介绍可以参见[1]。
参考:
[1] Windows 驱动程序开发技术详解,张帆