/*++ Copyright (C) Microsoft Corporation, 1990 - 1999 Module Name: passthru.c Abstract: This is the storage port driver library. This file contains code that implements SCSI passthrough. Authors: John Strange (JohnStra) Environment: kernel mode only Notes: This module implements SCSI passthru for storage port drivers. Revision History: --*/ #include "precomp.h" // // Constants and macros to enforce good use of Ex[Allocate|Free]PoolWithTag. // Remeber that all pool tags will display in the debugger in reverse order // #if USE_EXFREEPOOLWITHTAG_ONLY #define TAG(x) (x | 0x80000000) #else #define TAG(x) (x) #endif #define PORT_TAG_SENSE_BUFFER TAG('iPlP') // Sense info #define PORT_IS_COPY(Srb) \ ((Srb)->Cdb[0] == SCSIOP_COPY) #define PORT_IS_COMPARE(Srb) \ ((Srb)->Cdb[0] == SCSIOP_COMPARE) #define PORT_IS_COPY_COMPARE(Srb) \ ((Srb)->Cdb[0] == SCSIOP_COPY_COMPARE) #define PORT_IS_ILLEGAL_PASSTHROUGH_COMMAND(Srb) \ (PORT_IS_COPY((Srb)) || \ PORT_IS_COMPARE((Srb)) || \ PORT_IS_COPY_COMPARE((Srb))) #if defined (_WIN64) NTSTATUS PortpTranslatePassThrough32To64( IN PSCSI_PASS_THROUGH32 SrbControl32, IN OUT PSCSI_PASS_THROUGH SrbControl64 ); VOID PortpTranslatePassThrough64To32( IN PSCSI_PASS_THROUGH SrbControl64, IN OUT PSCSI_PASS_THROUGH32 SrbControl32 ); #endif NTSTATUS PortpSendValidPassThrough( IN PPORT_PASSTHROUGH_INFO PassThroughInfo, IN PIRP RequestIrp, IN ULONG SrbFlags, IN BOOLEAN Direct ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, PortGetPassThrough) #pragma alloc_text(PAGE, PortPassThroughInitialize) #pragma alloc_text(PAGE, PortPassThroughInitializeSrb) #pragma alloc_text(PAGE, PortSendPassThrough) #pragma alloc_text(PAGE, PortpSendValidPassThrough) #pragma alloc_text(PAGE, PortPassThroughCleanup) #pragma alloc_text(PAGE, PortGetPassThroughAddress) #pragma alloc_text(PAGE, PortSetPassThroughAddress) #pragma alloc_text(PAGE, PortPassThroughMarshalResults) #if defined (_WIN64) #pragma alloc_text(PAGE, PortpTranslatePassThrough32To64) #pragma alloc_text(PAGE, PortpTranslatePassThrough64To32) #endif #endif NTSTATUS PortGetPassThroughAddress( IN PIRP Irp, OUT PUCHAR PathId, OUT PUCHAR TargetId, OUT PUCHAR Lun ) /*++ Routine Description: This routine retrieves the address of the device to witch the passthrough request is to be sent. Arguments: Irp - Supplies a pointer to the IRP that contains the SCSI_PASS_THROUGH structure. PathId - Pointer to the PathId of the addressed device. TargetId - Pointer to the TargetId of the addressed device. Lun - Pointer to the logical unit number of the addressed device. Return Value: Returns a status indicating the success or failure of the operation. --*/ { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PSCSI_PASS_THROUGH srbControl = Irp->AssociatedIrp.SystemBuffer; ULONG requiredSize; NTSTATUS status; PAGED_CODE(); #if defined(_WIN64) if (IoIs32bitProcess(Irp)) { requiredSize = sizeof(SCSI_PASS_THROUGH32); } else { requiredSize = sizeof(SCSI_PASS_THROUGH); } #else requiredSize = sizeof(SCSI_PASS_THROUGH); #endif if (irpStack->Parameters.DeviceIoControl.InputBufferLength < requiredSize) { status = STATUS_BUFFER_TOO_SMALL; } else { *PathId = srbControl->PathId; *TargetId = srbControl->TargetId; *Lun = srbControl->Lun; status = STATUS_SUCCESS; } return status; } NTSTATUS PortSetPassThroughAddress( IN PIRP Irp, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun ) /*++ Routine Description: This routine initializes the address of the SCSI_PASS_THROUGH structure embedded in the supplied IRP. Arguments: Irp - Supplies a pointer to the IRP that contains the SCSI_PASS_THROUGH structure. PathId - The PathId of the addressed device. TargetId - The TargetId of the addressed device. Lun - The logical unit number of the addressed device. Return Value: Returns a status indicating the success or failure of the operation. --*/ { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PSCSI_PASS_THROUGH srbControl = Irp->AssociatedIrp.SystemBuffer; ULONG requiredSize; NTSTATUS status; PAGED_CODE(); #if defined(_WIN64) if (IoIs32bitProcess(Irp)) { requiredSize = sizeof(SCSI_PASS_THROUGH32); } else { requiredSize = sizeof(SCSI_PASS_THROUGH); } #else requiredSize = sizeof(SCSI_PASS_THROUGH); #endif if (irpStack->Parameters.DeviceIoControl.InputBufferLength < requiredSize) { status = STATUS_BUFFER_TOO_SMALL; } else { srbControl->PathId = PathId; srbControl->TargetId = TargetId; srbControl->Lun = Lun; status = STATUS_SUCCESS; } return status; } NTSTATUS PortSendPassThrough ( IN PDEVICE_OBJECT Pdo, IN PIRP RequestIrp, IN BOOLEAN Direct, IN ULONG SrbFlags, IN PIO_SCSI_CAPABILITIES Capabilities ) /*++ Routine Description: This function sends a user specified SCSI CDB to the device identified in the supplied SCSI_PASS_THROUGH structure. It creates an srb which is processed normally by the port driver. This call is synchornous. Arguments: Pdo - Supplies a pointer to the PDO to which the passthrough command will be dispatched. RequestIrp - Supplies a pointer to the IRP that made the original request. Direct - Boolean indicating whether this is a direct passthrough. SrbFlags - The flags to copy into the SRB used for this command. Capabilities - Supplies a pointer to the IO_SCSI_CAPABILITIES structure describing the storage adapter. Return Value: Returns a status indicating the success or failure of the operation. --*/ { NTSTATUS status; PORT_PASSTHROUGH_INFO passThroughInfo; PAGED_CODE(); RtlZeroMemory(&passThroughInfo, sizeof(PORT_PASSTHROUGH_INFO)); // // Try to init a pointer to the passthrough structure in the IRP. // status = PortGetPassThrough( &passThroughInfo, RequestIrp, Direct ); if (status == STATUS_SUCCESS) { // // Perform parameter checking and to setup the PORT_PASSTHROUGH_INFO // structure. // status = PortPassThroughInitialize( &passThroughInfo, RequestIrp, Capabilities, Pdo, Direct ); if (status == STATUS_SUCCESS) { // // Call helper routine to finish processing the passthrough request. // status = PortpSendValidPassThrough( &passThroughInfo, RequestIrp, SrbFlags, Direct ); } PortPassThroughCleanup(&passThroughInfo); } return status; } NTSTATUS PortGetPassThrough( IN OUT PPORT_PASSTHROUGH_INFO PassThroughInfo, IN PIRP Irp, IN BOOLEAN Direct ) /*++ Routine Description: This routine returns a pointer to the user supplied SCSI_PASS_THROUGH structure. Arguments: PassThroughInfo - Supplies a pointer to a SCSI_PASSTHROUGH_INFO structure. Irp - Supplies a pointer to the IRP. Direct - Supplies a boolean that indicates whether this is a SCSI_PASS_THROUGH_DIRECT request. Return Value: Returns a status indicating the success or failure of the operation. --*/ { NTSTATUS status; PIO_STACK_LOCATION irpStack; ULONG inputLength; PAGED_CODE(); // // Get a pointer to the pass through structure. // irpStack = IoGetCurrentIrpStackLocation(Irp); PassThroughInfo->SrbControl = Irp->AssociatedIrp.SystemBuffer; // // BUGBUG: Why do we need to save this pointer to the beginning of // the SCSI_PASS_THROUGH structure. // PassThroughInfo->SrbBuffer = (PVOID) PassThroughInfo->SrbControl; // // Initialize a stack variable to hold the size of the input buffer. // inputLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; // // For WIN64, a passthrough request from a 32-bit application requires // us to perform a translation on the supplied SCSI_PASS_THROUGH structure. // This is required because the layout of the 32-bit structure does not // match that of a 64-bit structure. In this case, we translate the // supplied 32-bit structure into a stack-allocated 64-bit structure which // will be used to process the pass through request. // #if defined (_WIN64) if (IoIs32bitProcess(Irp)) { if (inputLength < sizeof(SCSI_PASS_THROUGH32)){ return STATUS_INVALID_PARAMETER; } PassThroughInfo->SrbControl32 = (PSCSI_PASS_THROUGH32)(Irp->AssociatedIrp.SystemBuffer); if (Direct == FALSE) { if (PassThroughInfo->SrbControl32->Length != sizeof(SCSI_PASS_THROUGH32)) { return STATUS_REVISION_MISMATCH; } } else { if (PassThroughInfo->SrbControl32->Length != sizeof(SCSI_PASS_THROUGH_DIRECT32)) { return STATUS_REVISION_MISMATCH; } } status = PortpTranslatePassThrough32To64( PassThroughInfo->SrbControl32, &PassThroughInfo->SrbControl64); if (!NT_SUCCESS(status)) { return status; } PassThroughInfo->SrbControl = &PassThroughInfo->SrbControl64; } else { #endif if (inputLength < sizeof(SCSI_PASS_THROUGH)){ return(STATUS_INVALID_PARAMETER); } if (Direct == FALSE) { if (PassThroughInfo->SrbControl->Length != sizeof(SCSI_PASS_THROUGH)) { return STATUS_REVISION_MISMATCH; } } else { if (PassThroughInfo->SrbControl->Length != sizeof(SCSI_PASS_THROUGH_DIRECT)) { return STATUS_REVISION_MISMATCH; } } #if defined (_WIN64) } #endif return STATUS_SUCCESS; } NTSTATUS PortPassThroughInitialize( IN OUT PPORT_PASSTHROUGH_INFO PassThroughInfo, IN PIRP Irp, IN PIO_SCSI_CAPABILITIES Capabilities, IN PDEVICE_OBJECT Pdo, IN BOOLEAN Direct ) /*++ Routine Description: This routine validates the caller-supplied data and initializes the PORT_PASSTHROUGH_INFO structure. Arguments: PassThroughInfo - Supplies a pointer to a SCSI_PASSTHROUGH_INFO structure. Irp - Supplies a pointer to the IRP. Direct - Supplies a boolean that indicates whether this is a SCSI_PASS_THROUGH_DIRECT request. Return Value: Returns a status indicating the success or failure of the operation. --*/ { NTSTATUS status; ULONG outputLength; ULONG inputLength; PIO_STACK_LOCATION irpStack; PSCSI_PASS_THROUGH srbControl; ULONG dataTransferLength; ULONG pages; PAGED_CODE(); irpStack = IoGetCurrentIrpStackLocation(Irp); srbControl = PassThroughInfo->SrbControl; outputLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; inputLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; PassThroughInfo->Pdo = Pdo; // // Verify that the CDB is no greater than 16 bytes. // if (srbControl->CdbLength > 16) { return STATUS_INVALID_PARAMETER; } // // If there's a sense buffer then its offset cannot be shorter than the // length of the srbControl block, nor can it be located after the data // buffer (if any). // if (srbControl->SenseInfoLength != 0) { // // Sense info offset should not be smaller than the size of the // pass through structure. // if (srbControl->Length > srbControl->SenseInfoOffset) { return STATUS_INVALID_PARAMETER; } if (!Direct) { // // Sense info buffer should precede the data buffer offset. // if ((srbControl->SenseInfoOffset >= srbControl->DataBufferOffset) || ((srbControl->SenseInfoOffset + srbControl->SenseInfoLength) > srbControl->DataBufferOffset)) { return STATUS_INVALID_PARAMETER; } // // Sense info buffer should be within the output buffer. // if ((srbControl->SenseInfoOffset > outputLength) || (srbControl->SenseInfoOffset + srbControl->SenseInfoLength > outputLength)) { return STATUS_INVALID_PARAMETER; } } else { // // Sense info buffer should be within the output buffer. // if ((srbControl->SenseInfoOffset > outputLength) || (srbControl->SenseInfoOffset + srbControl->SenseInfoLength > outputLength)) { return STATUS_INVALID_PARAMETER; } } } if (!Direct) { // // Data buffer offset should be greater than the size of the pass // through structure. // if (srbControl->Length > srbControl->DataBufferOffset && srbControl->DataTransferLength != 0) { return STATUS_INVALID_PARAMETER; } // // If this command is sending data to the device. Make sure the data // buffer lies entirely within the supplied input buffer. // if (srbControl->DataIn != SCSI_IOCTL_DATA_IN) { if ((srbControl->DataBufferOffset > inputLength) || ((srbControl->DataBufferOffset + srbControl->DataTransferLength) > inputLength)) { return STATUS_INVALID_PARAMETER; } } // // If this command is retrieving data from the device, make sure the // data buffer lies entirely within the supplied output buffer. // if (srbControl->DataIn) { if ((srbControl->DataBufferOffset > outputLength) || ((srbControl->DataBufferOffset + srbControl->DataTransferLength) > outputLength)) { return STATUS_INVALID_PARAMETER; } } } // // Validate the specified timeout value. // if (srbControl->TimeOutValue == 0 || srbControl->TimeOutValue > 30 * 60 * 60) { return STATUS_INVALID_PARAMETER; } // // Check for illegal command codes. // if (PORT_IS_ILLEGAL_PASSTHROUGH_COMMAND(srbControl)) { return STATUS_INVALID_DEVICE_REQUEST; } if (srbControl->DataTransferLength == 0) { PassThroughInfo->Length = 0; PassThroughInfo->Buffer = NULL; PassThroughInfo->BufferOffset = 0; PassThroughInfo->MajorCode = IRP_MJ_FLUSH_BUFFERS; } else if (Direct == TRUE) { PassThroughInfo->Length = (ULONG) srbControl->DataTransferLength; PassThroughInfo->Buffer = (PUCHAR) srbControl->DataBufferOffset; PassThroughInfo->BufferOffset = 0; PassThroughInfo->MajorCode = !srbControl->DataIn ? IRP_MJ_WRITE : IRP_MJ_READ; } else { PassThroughInfo->Length = (ULONG) srbControl->DataBufferOffset + srbControl->DataTransferLength; PassThroughInfo->Buffer = (PUCHAR) PassThroughInfo->SrbBuffer; PassThroughInfo->BufferOffset = (ULONG)srbControl->DataBufferOffset; PassThroughInfo->MajorCode = !srbControl->DataIn ? IRP_MJ_WRITE : IRP_MJ_READ; } // // Make sure the buffer is properly aligned. // if (Direct == TRUE) { // // Make sure the user buffer is valid. IoBuildSynchronouseFsdRequest // calls MmProbeAndLock with AccessMode == KernelMode. This check is // the extra stuff that MM would do if it were called with // AccessMode == UserMode. // // ISSUE: We should probably do an MmProbeAndLock here. // if (Irp->RequestorMode != KernelMode) { PVOID endByte; if (PassThroughInfo->Length) { endByte = (PVOID)((PCHAR)PassThroughInfo->Buffer + PassThroughInfo->Length - 1); if ((endByte > (PVOID)MM_HIGHEST_USER_ADDRESS) || (PassThroughInfo->Buffer >= endByte)) { return STATUS_INVALID_USER_BUFFER; } } } if (srbControl->DataBufferOffset & PassThroughInfo->Pdo->AlignmentRequirement) { return STATUS_INVALID_PARAMETER; } } // // Check if the request is too big for the adapter. // dataTransferLength = PassThroughInfo->SrbControl->DataTransferLength; pages = ADDRESS_AND_SIZE_TO_SPAN_PAGES( (PUCHAR)PassThroughInfo->Buffer + PassThroughInfo->BufferOffset, dataTransferLength); if ((dataTransferLength != 0) && (pages > Capabilities->MaximumPhysicalPages || dataTransferLength > Capabilities->MaximumTransferLength)) { return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } VOID PortPassThroughCleanup( IN PPORT_PASSTHROUGH_INFO PassThroughInfo ) /*++ Routine Description: This routine performs any cleanup required after processing a SCSI passthrough request. Arguments: PassThroughInfo - Supplies a pointer to a SCSI_PASSTHROUGH_INFO structure. Return Value: VOID --*/ { PAGED_CODE(); #if defined (_WIN64) if (PassThroughInfo->SrbControl32 != NULL) { PortpTranslatePassThrough64To32( PassThroughInfo->SrbControl, PassThroughInfo->SrbControl32); } #endif } NTSTATUS PortPassThroughInitializeSrb( IN PPORT_PASSTHROUGH_INFO PassThroughInfo, IN PSCSI_REQUEST_BLOCK Srb, IN PIRP Irp, IN ULONG SrbFlags, IN PVOID SenseBuffer ) /*++ Routine Description: This routine initializes the supplied SRB to send a passthrough request. Arguments: PassThroughInfo - Supplies a pointer to a SCSI_PASSTHROUGH_INFO structure. Srb - Supplies a pointer to the SRB to initialize. Irp - Supplies a pointer to the IRP. SrbFlags - Supplies the appropriate SRB flags for the request. SenseBuffer - Supplies a pointer to request sense buffer to put in the SRB. Return Value: Returns a status indicating the success or failure of the operation. --*/ { PSCSI_PASS_THROUGH srbControl; NTSTATUS status; PAGED_CODE(); // // Zero out the srb. // RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK)); // // Fill in the srb. // srbControl = PassThroughInfo->SrbControl; Srb->Length = SCSI_REQUEST_BLOCK_SIZE; Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; Srb->SrbStatus = SRB_STATUS_PENDING; Srb->PathId = srbControl->PathId; Srb->TargetId = srbControl->TargetId; Srb->Lun = srbControl->Lun; Srb->CdbLength = srbControl->CdbLength; Srb->SenseInfoBufferLength = srbControl->SenseInfoLength; switch (srbControl->DataIn) { case SCSI_IOCTL_DATA_OUT: if (srbControl->DataTransferLength) { Srb->SrbFlags = SRB_FLAGS_DATA_OUT; } break; case SCSI_IOCTL_DATA_IN: if (srbControl->DataTransferLength) { Srb->SrbFlags = SRB_FLAGS_DATA_IN; } break; default: Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT; break; } if (srbControl->DataTransferLength == 0) { Srb->SrbFlags = 0; } else { // // Flush the data buffer for output. This will insure that the data is // written back to memory. // if (Irp != NULL) { KeFlushIoBuffers(Irp->MdlAddress, FALSE, TRUE); } } Srb->SrbFlags |= (SrbFlags | SRB_FLAGS_NO_QUEUE_FREEZE); Srb->DataTransferLength = srbControl->DataTransferLength; Srb->TimeOutValue = srbControl->TimeOutValue; Srb->DataBuffer = (PCHAR) PassThroughInfo->Buffer + PassThroughInfo->BufferOffset; Srb->SenseInfoBuffer = SenseBuffer; Srb->OriginalRequest = Irp; RtlCopyMemory(Srb->Cdb, srbControl->Cdb, srbControl->CdbLength); // // Disable autosense if there's no sense buffer to put the data in. // if (SenseBuffer == NULL) { Srb->SrbFlags |= SRB_FLAGS_DISABLE_AUTOSENSE; } return STATUS_SUCCESS; } #if defined (_WIN64) NTSTATUS PortpTranslatePassThrough32To64( IN PSCSI_PASS_THROUGH32 SrbControl32, IN OUT PSCSI_PASS_THROUGH SrbControl64 ) /*++ Routine Description: On WIN64, a SCSI_PASS_THROUGH structure sent down by a 32-bit application must be marshaled into a 64-bit version of the structure. This function performs that marshaling. Arguments: SrbControl32 - Supplies a pointer to a 32-bit SCSI_PASS_THROUGH struct. SrbControl64 - Supplies the address of a pointer to a 64-bit SCSI_PASS_THROUGH structure, into which we'll copy the marshaled 32-bit data. Return Value: Returns a status indicating the success or failure of the operation. --*/ { PAGED_CODE(); // // Copy the first set of fields out of the 32-bit structure. These // fields all line up between the 32 & 64 bit versions. // // Note that we do NOT adjust the length in the srbControl. This is to // allow the calling routine to compare the length of the actual // control area against the offsets embedded within. If we adjusted the // length then requests with the sense area backed against the control // area would be rejected because the 64-bit control area is 4 bytes // longer. // RtlCopyMemory(SrbControl64, SrbControl32, FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset) ); // // Copy over the CDB. // RtlCopyMemory(SrbControl64->Cdb, SrbControl32->Cdb, 16 * sizeof(UCHAR) ); // // Copy the fields that follow the ULONG_PTR. // SrbControl64->DataBufferOffset = (ULONG_PTR)SrbControl32->DataBufferOffset; SrbControl64->SenseInfoOffset = SrbControl32->SenseInfoOffset; return STATUS_SUCCESS; } VOID PortpTranslatePassThrough64To32( IN PSCSI_PASS_THROUGH SrbControl64, IN OUT PSCSI_PASS_THROUGH32 SrbControl32 ) /*++ Routine Description: On WIN64, a SCSI_PASS_THROUGH structure sent down by a 32-bit application must be marshaled into a 64-bit version of the structure. This function marshals a 64-bit version of the structure back into a 32-bit version. Arguments: SrbControl64 - Supplies a pointer to a 64-bit SCSI_PASS_THROUGH struct. SrbControl32 - Supplies the address of a pointer to a 32-bit SCSI_PASS_THROUGH structure, into which we'll copy the marshaled 64-bit data. Return Value: Returns a status indicating the success or failure of the operation. --*/ { PAGED_CODE(); // // Copy back the fields through the data offsets. // RtlCopyMemory(SrbControl32, SrbControl64, FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset)); return; } #endif NTSTATUS PortpSendValidPassThrough( IN PPORT_PASSTHROUGH_INFO PassThroughInfo, IN PIRP RequestIrp, IN ULONG SrbFlags, IN BOOLEAN Direct ) /*++ Routine Description: This routine sends the SCSI passthrough request described by the supplied PORT_PASSTHROUGH_INFO. Arguments: PassThroughInfo - Supplies a pointer to a SCSI_PASSTHROUGH_INFO structure. RequestIrp - Supplies a pointer to the IRP. SrbFlags - Supplies the appropriate SRB flags for the request. Direct - Indicates whether this is a SCSO_PASS_THROUGH_DIRECT. Return Value: Returns a status indicating the success or failure of the operation. --*/ { PIO_STACK_LOCATION irpStack; NTSTATUS status; PIRP irp; PVOID senseBuffer; KEVENT event; LARGE_INTEGER startingOffset; IO_STATUS_BLOCK ioStatusBlock; SCSI_REQUEST_BLOCK srb; PAGED_CODE(); // // Allocate an aligned sense buffer if one is required. // if (PassThroughInfo->SrbControl->SenseInfoLength != 0) { senseBuffer = ExAllocatePoolWithTag( NonPagedPoolCacheAligned, PassThroughInfo->SrbControl->SenseInfoLength, PORT_TAG_SENSE_BUFFER ); if (senseBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } } else { senseBuffer = NULL; } // // Must be at PASSIVE_LEVEL to use synchronous FSD. // ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); // // Initialize the notification event. // KeInitializeEvent(&event, NotificationEvent, FALSE); // // Build IRP for this request. // Note we do this synchronously for two reasons. If it was done // asynchonously then the completion code would have to make a special // check to deallocate the buffer. Second if a completion routine were // used then an addation stack locate would be needed. // startingOffset.QuadPart = (LONGLONG) 1; irp = IoBuildSynchronousFsdRequest( PassThroughInfo->MajorCode, PassThroughInfo->Pdo, PassThroughInfo->Buffer, PassThroughInfo->Length, &startingOffset, &event, &ioStatusBlock); if (irp == NULL) { if (senseBuffer != NULL) { ExFreePool(senseBuffer); } return STATUS_INSUFFICIENT_RESOURCES; } // // Set major code. // irpStack = IoGetNextIrpStackLocation(irp); irpStack->MajorFunction = IRP_MJ_SCSI; irpStack->MinorFunction = 1; // // Initialize the IRP stack location to point to the SRB. // irpStack->Parameters.Others.Argument1 = &srb; // // Have the port library initialize the SRB for us. // status = PortPassThroughInitializeSrb( PassThroughInfo, &srb, irp, SrbFlags, senseBuffer); // // Call port driver to handle this request and wait for the request to // complete. // status = IoCallDriver(PassThroughInfo->Pdo, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); } else { ioStatusBlock.Status = status; } PortPassThroughMarshalResults(PassThroughInfo, &srb, RequestIrp, &ioStatusBlock, Direct); // // Free the sense buffer. // if (senseBuffer != NULL) { ExFreePool(senseBuffer); } return ioStatusBlock.Status; } VOID PortPassThroughMarshalResults( IN PPORT_PASSTHROUGH_INFO PassThroughInfo, IN PSCSI_REQUEST_BLOCK Srb, IN PIRP RequestIrp, IN PIO_STATUS_BLOCK IoStatusBlock, IN BOOLEAN Direct ) { PAGED_CODE(); // // Copy the returned values from the srb to the control structure. // PassThroughInfo->SrbControl->ScsiStatus = Srb->ScsiStatus; if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { // // Set the status to success so that the data is returned. // IoStatusBlock->Status = STATUS_SUCCESS; PassThroughInfo->SrbControl->SenseInfoLength = Srb->SenseInfoBufferLength; // // Copy the sense data to the system buffer. // RtlCopyMemory( (PUCHAR)PassThroughInfo->SrbBuffer + PassThroughInfo->SrbControl->SenseInfoOffset, Srb->SenseInfoBuffer, Srb->SenseInfoBufferLength); } else { PassThroughInfo->SrbControl->SenseInfoLength = 0; } // // If the srb status is buffer underrun then set the status to success. // This insures that the data will be returned to the caller. // if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) { IoStatusBlock->Status = STATUS_SUCCESS; } // // Set the information length // PassThroughInfo->SrbControl->DataTransferLength = Srb->DataTransferLength; if (Direct == TRUE) { RequestIrp->IoStatus.Information = PassThroughInfo->SrbControl->SenseInfoOffset + PassThroughInfo->SrbControl->SenseInfoLength; } else { if (!PassThroughInfo->SrbControl->DataIn || PassThroughInfo->BufferOffset == 0) { RequestIrp->IoStatus.Information = PassThroughInfo->SrbControl->SenseInfoOffset + PassThroughInfo->SrbControl->SenseInfoLength; } else { RequestIrp->IoStatus.Information = PassThroughInfo->SrbControl->DataBufferOffset + PassThroughInfo->SrbControl->DataTransferLength; } } ASSERT((Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) == 0); RequestIrp->IoStatus.Status = IoStatusBlock->Status; return; }