has connected to the interrupt.
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.
The three time-out variables are used to detect time-outs, as described later. If
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
Listing 17.2 shows how
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
Listing 17.2 also shows the code in
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;