Книга: Writing Windows WDM Device Drivers

WdmIo Reads and Writes

WdmIo Reads and Writes

Before I look at its interrupt handler, let's see how WdmIo starts and runs its interrupt-driven reads and writes.

Listing 17.1 shows the mass of extra fields that are needed in the device extension. The ConnectedToInterrupt field, as mentioned before, indicates whether WdmIo has connected to the interrupt. InterruptReg, InterruptRegMask, and InterruptRegValue are the values given by the controlling Win32 application in its PHDIO_IRCLCONNECT command.

Remember that the controlling application uses three IOCTLs, such as IOCTL_PHDIO_CMDS_FOR_WRITE, to store the commands that are used to process read and write transfers. WdmIoStartIo calls StoreCmds in each case to allocate some memory from the nonpaged pool to store a copy of the passed commands. The device extension WriteCmds, StartReadCmds, and ReadCmds fields store the pointers to this memory, with WriteCmdsLen, StartReadCmdsLen, and ReadCmdsLen storing the lengths. WdmIo frees the allocated memory when the device is removed.

The three time-out variables are used to detect time-outs, as described later. If Timeout is –1, no read or write is in progress. The final new group of variables are used to keep track of where WdmIo is in the read or write transfer. The next sections describe how these fields are used.

Listing 17.1 Interrupt handling fields in the device extension

// Interrupt handling support
bool ConnectedToInterrupt;
UCHAR InterruptReg;
UCHAR InterruptRegMask;
UCHAR InterruptRegValue;
ULONG CmdOutputCount; // Count of bytes output from commands
PUCHAR WriteCmds; // Stored commands for write IRP
ULONG WriteCmdsLen; // length
PUCHAR StartReadCmds; // Stored commands for start read IRP
ULONG StartReadCmdsLen; // length
PUCHAR ReadCmds; // Stored commands for read IRP
ULONG ReadCmdsLen; // length
UCHAR SetTimeout; // Timeout stored from script
int Timeout; // Seconds left to go. –1 if not in force
bool StopTimer; // Set to stop timer
ULONG TxTotal; // R/W total transfer size in bytes
ULONG TxLeft; // R/W bytes left to transfer
PUCHAR TxBuffer; // R/W buffer. Moves through current IRP SystemBuffer
bool TxIsWrite; // R/W direction
NTSTATUS TxStatus; // R/W status return
UCHAR TxResult[5]; // R/W output buffer (2 Failcode, 2 Offset, 1 user)
UCHAR TxLastIntReg; // R/W last interrupt register value
ULONG TxCmdOutputCount; // R/W Copy of last CmdOutputCount

Starting Requests

Listing 17.2 shows how WdmIoStartIo initiates a write request. The device extension TxIsWrite field is set true for writes, and false for reads. TxTotal contains the total number of bytes to transfer. TxLeft stores the number of bytes left to transfer, and so counts from TxTotal down to zero. TxBuffer points to the next byte to transfer in the IRP buffer, so it moves through the buffer as each byte is written. The IRP buffer is always accessible, as long as the IRP is being processed, so there is no need to make a copy of it. The TxStatus field contains the IRP's eventual completion status, which is initially assumed to be successful.

The TxResult array is used to contain the output from running the stored write commands. This 5-byte array is, therefore, zeroed before the write begins in earnest. TxLastIntReg stores the last value read by the interrupt handler from its status register. The contents of TxResult and TxLastIntReg can eventually be obtained using the IOCTL_WDMIO_GET_RW_ RESULTS call, as shown in Listing 15.4.

The "one-second interval" timer is now started to detect time-outs.

WdmIo is now finally ready to output the first data byte by running the stored write data commands. As interrupts have been enabled, they must be run in the context of a Critical Section routine to avoid being interrupted. Listing 17.2 shows how RunWriteCmdsSynch does this job, calling ProcessCmds to run the commands in dx->WriteCmds with the output going to dx->TxResult. If ProcessCmds fails or if there are bytes left to transfer, RunWriteCmdsSynch returns TRUE and WdmIoStartIo completes the Write IRP straight away with status STATUS_UNSUCCESSFUL.

Listing 17.2 also shows the code in ProcessCmds that handles the PHDIO_WRITE_NEXT command. Basically, it retrieves the next byte from TxBuffer and writes it to the Data register. It increments the TxBuffer pointer and decrements the count of bytes left to process, TxLeft.

Listing 17.2 How WdmIoStartIo starts write requests

case IRP_MJ_WRITE:
 if (dx->WriteCmds==NULL || !dx->ConnectedToInterrupt) {
  status = STATUS_INVALID_DEVICE_REQUEST;
  break;
 }
 // Store transfer details dx->TxIsWrite = true;
 dx->TxTotal = IrpStack->Parameters.Write.Length;
 dx->TxLeft = dx->TxTotal;
 dx->TxBuffer = (PUCHAR)Buffer;
 dx->TxStatus = STATUS_SUCCESS;
 RtlZeroMemory(dx->TxResult, sizeof(dx->TxResult));
 DebugPrint("WdmIoStartIo: Write %d bytes: %*s", dx->TxTotal, dx->TxTotal, dx->TxBuffer);
 // Start timeout timer
 dx->Timeout = dx->SetTimeout+1;
 IoStartTimer(fdo);
 // Send first value
 if (KeSynchronizeExecution(dx->InterruptObject, (PKSYNCHRONIZE_ROUTINE)RunWriteCmdsSynch, (PVOID)dx)) {
  status = STATUS_UNSUCCESSFUL;
  break;
 }
 return;
 // …
BOOLEAN RunWriteCmdsSynch(IN PWDMIO_DEVICE_EXTENSION dx) {
 if (dx->TxLeft==0) return TRUE;
 dx->CmdOutputCount = 0;
 BOOLEAN rv = ProcessCmds(dx, dx->WriteCmds, dx->WriteCmds_en, dx->TxResult, sizeof(dx->TxResult), false);
 dx->TxCmdOutputCount = dx->CmdOutputCount;
 if (!rv) {
  dx->TxStatus = STATUS_UNSUCCESSFUL;
  return TRUE;
 }
 return FALSE;
}
//In ProcessCmds…
case PHDIO_WRITE_NEXT:
 {
  if (dx->Timeout==-1) {
   FailCode = PHDIO_CANNOT_RW_NEXT;
   goto fail;
  }
  if (dx->TxLeft==0) {
   FailCode = PHDIO_NO_DATA_LEFT_TO_TRANSFER;
   goto fail;
  }
  GetUChar(reg);
  WriteByte(dx, reg, *dx->TxBuffer++);
  dx->TxLeft--;
  break;
 }

Read requests are processed in a very similar way. As Chapter 15 mentioned, one set of stored commands, in StartReadCmds, is used to start read requests. A different set, in ReadCmds, is used to process read interrupts. RunStartReadCmdsSynch and RunReadCmdsSynch are run as Critical Section routines to process these two sets of commands. The PHDIO_READ_NEXT command is run in a very similar way to PHDIO_WRITE_NEXT, except that it stores the byte that it reads in the next location in the IRP buffer.

Оглавление книги


Генерация: 1.126. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз