34、I/O端口操作

    I/O端口操作在Windows操作系统中属于特权命令,必须在内核模式下运行。在DOS中,I/O端口操作主要通过IN/OUT指令来进行。 

一、I/O端口操作实现 

1DDK实现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.VXD9598等相关。 

代码
#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 驱动程序开发技术详解,张帆

 

posted @ 2010-12-06 00:22  浪里飞  阅读(1953)  评论(0编辑  收藏  举报