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;

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×