介紹完如何編譯和使用偵錯工具後,就可以開始著手實現我們需要的功能。我們的目標是使用I/O去存取環控晶片,為了後續測試的方便,會暫時將函式實作在Non-Pnp驅動程式範例中。
nonpnp.h
TSTATUS
eadIoPort (
IN WDFREQUEST Request,
IN ULONG IoControlCode
);
TSTATUS
riteIoPort (
IN WDFREQUEST Request,
IN ULONG IoControlCode
);
``
public.h
ReadIoPort
和WriteIoPort
判斷傳入的I/O埠號是否合法。
define SIO_INDEX_PORT 0x2E
define SIO_DATA_PORT 0x2F
define SEC_SIO_INDEX_PORT 0x4E
define SEC_SIO_DATA_PORT 0x4F
define SIO_HWM_PORT_0 0x290
define SIO_HWM_PORT_1 0x2A0
``
ReadIoPort
和WriteIoPort
所對應的IO control code,供FileEvtIoDeviceControl
使用。
define IOCTL_CUSTOM_READ_IO_COMMAND CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
define IOCTL_CUSTOM_WRITE_IO_COMMAND CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
``
IO_PORT_INPUT
來傳遞I/O埠號及寫入資料。
ypedef struct _IO_PORT_INPUT {
ULONG PortNumber;
union _IO_PORT_DATA {
ULONG LongData;
USHORT ShortData;
UCHAR CharData;
} IoPortData;
IO_PORT_INPUT, *PIO_PORT_INPUT;
``
接著就是ReadIoPort
和WriteIoPort
的實作。
nonpnp.c
ReadIoPort
TSTATUS
eadIoPort (
IN WDFREQUEST Request,
IN ULONG IoControlCode
)
NTSTATUS Status;
UCHAR Data;
USHORT PortNumber;
PIO_PORT_INPUT InputData = NULL;
size_t InDataSize;
PVOID OutBuf = NULL;
size_t OutBufSize = 0;
UNREFERENCED_PARAMETER(IoControlCode);
Status = WdfRequestRetrieveInputBuffer(Request, sizeof(IO_PORT_INPUT), (PVOID*)&InputData, &InDataSize);
if (!NT_SUCCESS(Status)) {
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = WdfRequestRetrieveOutputBuffer(Request, 0, &OutBuf, &OutBufSize);
if (!NT_SUCCESS(Status)) {
return STATUS_INSUFFICIENT_RESOURCES;
}
if ((OutBuf == NULL) || (OutBufSize == 0)) {
return STATUS_BUFFER_TOO_SMALL;
}
PortNumber = (USHORT)InputData->PortNumber;
KdPrint(("PortNumber %x\n", PortNumber));
if ((PortNumber != SIO_INDEX_PORT) && (PortNumber != SIO_DATA_PORT) &&
(PortNumber != SEC_SIO_INDEX_PORT) && (PortNumber != SEC_SIO_DATA_PORT) &&
(PortNumber != (SIO_HWM_PORT_0 + 0x05)) && (PortNumber != (SIO_HWM_PORT_0 + 0x06)) &&
(PortNumber != (SIO_HWM_PORT_1 + 0x05)) && (PortNumber != (SIO_HWM_PORT_1 + 0x06)))
{
return STATUS_INVALID_PARAMETER;
}
Data = __inbyte(PortNumber);
KdPrint(("Data %x\n", Data));
RtlCopyMemory(OutBuf, &Data, sizeof(UCHAR));
WdfRequestSetInformation(Request, sizeof(UCHAR));
return STATUS_SUCCESS;
``
首先使用`WdfRequestRetrieveInputBuffer`來獲取input buffer中的資料,使用先前自行定義的`PIO_PORT_INPUT`來存放。
接著`WdfRequestRetrieveOutputBuffer`來獲取output buffer的指標。
再來判斷輸入的I/O埠號是否合法,I/O埠號**必須是之前所宣告的環控晶片常用I/O埠**。
使用編譯器內建的`__inbyte`,**指定I/O埠號並讀取該埠號的值**。
將讀取到的值使用`RtlCopyMemory`複製到output buffer。
最後用`WdfRequestSetInformation`設定完成狀態,這裡回傳讀取到的長度,固定為一個byte。
WriteIoPort
TSTATUS
riteIoPort (
IN WDFREQUEST Request,
IN ULONG IoControlCode
)
NTSTATUS Status;
USHORT PortNumber;
PIO_PORT_INPUT InputData = NULL;
size_t InDataSize;
UNREFERENCED_PARAMETER(IoControlCode);
Status = WdfRequestRetrieveInputBuffer(Request, sizeof(IO_PORT_INPUT), (PVOID*)&InputData, &InDataSize);
if (!NT_SUCCESS(Status)) {
return STATUS_INSUFFICIENT_RESOURCES;
}
PortNumber = (USHORT)InputData->PortNumber;
KdPrint(("PortNumber %x, WriteData %x\n", PortNumber, InputData->IoPortData.CharData));
if ((PortNumber != SIO_INDEX_PORT) && (PortNumber != SIO_DATA_PORT) &&
(PortNumber != SEC_SIO_INDEX_PORT) && (PortNumber != SEC_SIO_DATA_PORT) &&
(PortNumber != (SIO_HWM_PORT_0 + 0x05)) && (PortNumber != (SIO_HWM_PORT_0 + 0x06)) &&
(PortNumber != (SIO_HWM_PORT_1 + 0x05)) && (PortNumber != (SIO_HWM_PORT_1 + 0x06)))
{
return STATUS_INVALID_PARAMETER;
}
__outbyte(PortNumber, InputData->IoPortData.CharData);
WdfRequestSetInformation(Request, sizeof(UCHAR));
return STATUS_SUCCESS;
``
首先使用`WdfRequestRetrieveInputBuffer`來獲取input buffer中的資料,一樣使用`PIO_PORT_INPUT`來存放。
一樣需要判斷輸入的I/O埠號是否合法,I/O埠號**必須是之前所宣告的環控晶片常用I/O埠**。
使用編譯器內建的`__outbyte`,**指定I/O埠號並傳送指定的值到該I/O埠**。
最後用`WdfRequestSetInformation`設定完成狀態,這裡回傳寫入到I/O埠的長度,固定為一個byte。
FileEvtIoDeviceControl
FileEvtIoDeviceControl
中添加對應讀寫I/O函式的IO contorl code到switch case中:
case IOCTL_CUSTOM_READ_IO_COMMAND:
KdPrint(("IOCTL_CUSTOM_READ_IO_COMMAND\n"));
status = ReadIoPort(Request, IoControlCode);
break;
case IOCTL_CUSTOM_WRITE_IO_COMMAND:
KdPrint(("IOCTL_CUSTOM_WRITE_IO_COMMAND\n"));
status = WriteIoPort(Request, IoControlCode);
break;
``
如此一來在Non-PnP驅動程式端使用I/O去存取環控晶片的實作就完成了。
LibreHardwareMonitor
Non-PnP Driver Sample