A.1.4.5 SCSI Class Driver’s SplitTransferRequest Routine
The IO_SCSI_CAPABILITIES-type data returned to the DriverEntry routine indicates the transfer capabilities of a given HBA to the class driver. In particular, this data indicates the HBA’s MaximumTransferLength in bytes and the HBA’s MaximumPhysicalPages: that is, how many discontiguites in the physical memory backing a system buffer the HBA can manage.
Most class drivers store a pointer to this configuration data in the device extension of each device object because SCSI class drivers are responsible for splitting up all transfer requests that exceed the HBA’s capability to transfer data. In other words, a class driver’s DispatchReadWrite routine must determine whether each IRP requests a transfer that is more than the HBA can handle in a single transfer operation.
For example, such a DispatchReadWrite routine could have code similar to the following:
maxTransferLength =
deviceExtension->PortCapabilities->MaximumTransferLength;
maxPhysicalPages =
deviceExtension->PortCapabilities->MaximumPhysicalPages;
currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
transferLengthRequested = currentIrpStack->Parameters.Read.Length;
: :
//
// Calculate number of pages in this transfer
//
transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
MmGetMdlVirtualAddress(Irp->MdlAddress),
transferLengthRequested);
//
// Check whether requested length is greater than maximum
// HBA can transfer in a single operation
//
if (transferLengthRequested > maxTransferLength ||
transferPages > maxPhysicalPages) {
transferPages = maxPhysicalPages - 1;
if (maxTransferLength > transferPages << PAGE_SHIFT) {
maxTransferLength = transferPages << PAGE_SHIFT;
}
IoMarkIrpPending(Irp);
SplitTransferRequest(DeviceObject,
Irp,
maxTransferLength);
return STATUS_PENDING;
}
: :
Note that such a driver’s DispatchReadWrite routine calls IoMarkIrpPending and returns STATUS_PENDING immediately after a call to its SplitTransferRequest routine with the original IRP.
To carry out the original transfer request, the driver’s SplitTransferRequest routine creates one or more IRPs to handle sub-buffers that are sized to suit the HBA’s capabilities. For each such IRP, the SplitTransferRequest routine sets up an SRB, usually by calling a BuildSrb routine (see Section A.1.4.4), and sets up its IoCompletion routine before sending the IRP on to the port driver with IoCallDriver.
To track each piece of the transfer, SplitTransferRequest registers an IoCompletion routine for each driver-allocated IRP it sends down to the port driver. The IoCompletion routine maintains a count of completed partial transfer requests in the original IRP and protects the count with a spin lock, as already mentioned in Section A.1.3.4.
Such an IoCompletion routine must free any IRPs and/or SRBs the driver has allocated and must complete the original IRP when all requested data has been transferred or when the class driver has exhausted retries of the IRP and must fail it due device transfer errors.