iT邦幫忙

2023 iThome 鐵人賽

DAY 11
0

介紹完如何編譯和使用偵錯工具後,就可以開始著手實現我們需要的功能。我們的目標是使用I/O去存取環控晶片,為了後續測試的方便,會暫時將函式實作在Non-Pnp驅動程式範例中。

  • nonpnp.h
    • 在標頭檔宣告讀寫I/O的函式。
      TSTATUS
      eadIoPort (
      IN WDFREQUEST Request,
      IN ULONG IoControlCode
      );
      
      TSTATUS
      riteIoPort (
      IN WDFREQUEST Request,
      IN ULONG IoControlCode
      );
      ``
      
      
  • public.h
    • 宣告環控晶片常用的I/O埠號,方便後續ReadIoPortWriteIoPort判斷傳入的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
      ``
      
      
    • 宣告ReadIoPortWriteIoPort所對應的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;
      ``
      
      

接著就是ReadIoPortWriteIoPort的實作。

  • 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
      • 負責讀寫I/O的函式完成後,需要到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


上一篇
Day10 如何偵錯驅動程式-2
下一篇
Day12 使用I/O存取環控晶片-2
系列文
Windows Driver + Electron 學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言