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 < 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个端口操作函数
// 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 驱动程序开发技术详解,张帆