Send SCSI command error

I’m writting a virtual miniport driver. When I need to handle Read10 and Write10 command, I build a new irp and send it to a physical scsi controller. Here is part of my code for Read10:
{
// Make srb

PSCSI_REQUEST_BLOCK newSrb = NULL;
PSENSE_DATA pSenseData = NULL;
PCDB pCdb10 = NULL;

newSrb = (PSCSI_REQUEST_BLOCK)ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK),0x12345678);
if (newSrb == NULL)
{
if (newSrb->SenseInfoBuffer && newSrb->SenseInfoBuffer != pSenseData)
{
ExFreePool(newSrb->SenseInfoBuffer);
}
if (newSrb != NULL)
{
ExFreePool(newSrb);
}
if (pSenseData != NULL)
{
ExFreePool(pSenseData);
}
SetSrbSenseCode(newSrb,NO_MEDIA_IN_DEVICE);
return TRUE;
}
pSenseData = (PSENSE_DATA)ExAllocatePoolWithTag(NonPagedPool, sizeof(SENSE_DATA),0x12345678);
if (pSenseData == NULL)
{
if (newSrb->SenseInfoBuffer && newSrb->SenseInfoBuffer != pSenseData)
{
ExFreePool(newSrb->SenseInfoBuffer);
}
if (newSrb != NULL)
{
ExFreePool(newSrb);
}
if (pSenseData != NULL)
{
ExFreePool(pSenseData);
}
SetSrbSenseCode(newSrb,NO_MEDIA_IN_DEVICE);
return TRUE;
}
RtlZeroMemory(newSrb, sizeof(SCSI_REQUEST_BLOCK));
RtlZeroMemory(pSenseData, sizeof(SENSE_DATA));
newSrb->Length = sizeof (SCSI_REQUEST_BLOCK);
newSrb->Function = SRB_FUNCTION_EXECUTE_SCSI;
newSrb->PathId = 0;
newSrb->TargetId = 2;
newSrb->Lun = 0;
newSrb->ScsiStatus = newSrb->SrbStatus = 0;
newSrb->NextSrb = NULL;
newSrb->LinkTimeoutValue = -1;
newSrb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
newSrb->SenseInfoBuffer = pSenseData;
newSrb->SenseInfoBufferLength = sizeof(SENSE_DATA);
newSrb->DataBuffer = (PVOID)pSrb->DataBuffer;
newSrb->DataTransferLength = ulLen*512;
newSrb->QueueSortKey = ulStart;
if (1)
{
// READ
newSrb->SrbFlags |= SRB_FLAGS_DATA_IN;
newSrb->SrbFlags |= SRB_FLAGS_ADAPTER_CACHE_ENABLE;
}
else
{
// WRITE
newSrb->SrbFlags |= SRB_FLAGS_DATA_OUT;
}
newSrb->SrbFlags |= SRB_FLAGS_DISABLE_AUTOSENSE;

newSrb->CdbLength = 0x0A; //CDB10
pCdb10 = (PCDB)newSrb->Cdb;
if (1)
{
pCdb10->CDB10.OperationCode = SCSIOP_READ;
}
else
{
pCdb10->CDB10.OperationCode = SCSIOP_WRITE;
}
pCdb10->CDB10.LogicalBlockByte0 = (UCHAR)(ulStart >> 0x18);
pCdb10->CDB10.LogicalBlockByte1 = (UCHAR)(ulStart >> 0x10);
pCdb10->CDB10.LogicalBlockByte2 = (UCHAR)(ulStart >> 0x8);
pCdb10->CDB10.LogicalBlockByte3 = (UCHAR)(ulStart);
pCdb10->CDB10.TransferBlocksLsb = (UCHAR)(ulLen >> 0x8);
pCdb10->CDB10.TransferBlocksMsb = (UCHAR)(ulLen);

#if 0
NTSTATUS status = STATUS_SUCCESS;
if (doOnceFlag == 0)
{
UNICODE_STRING mpUnicodeName;
RtlInitUnicodeString(&mpUnicodeName, L"\Device\Scsi\symmpi1Port2Path0Target2Lun0");

status = IoGetDeviceObjectPointer(
&mpUnicodeName,
GENERIC_READ|GENERIC_WRITE,
&SCSIFileObject,
&SCSIDeviceObject
);

if (NT_SUCCESS(status))
{
InterlockedIncrement(&doOnceFlag);

ObDereferenceObject(SCSIFileObject);
}
}
#endif
//if (NT_SUCCESS(status))
MakeAsynchronousRequest2((PDEVICE_OBJECT)g_pstrSCSIfdo,pSrb->DataBuffer,ulLen*512,1,newSrb);
}

NTSTATUS
MakeAsynchronousRequestCompletion2(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PMDL mdl, nextMdl;

*(Irp->UserIosb) = Irp->IoStatus;

if (Irp->MdlAddress != NULL) {
for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) {
nextMdl = mdl->Next;
MmUnlockPages( mdl ); IoFreeMdl( mdl ); // This function will also unmap pages.
}
Irp->MdlAddress = NULL;
}

KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);

IoFreeIrp(Irp);

return STATUS_MORE_PROCESSING_REQUIRED;
}

NTSTATUS
MakeAsynchronousRequest2(
PDEVICE_OBJECT TopOfDeviceStack,
PVOID WriteBuffer,
ULONG NumBytes,
BOOL IsRead,
PSCSI_REQUEST_BLOCK Srb
)
{
NTSTATUS status;
PIRP newIrp;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PIO_STACK_LOCATION nextStack;

KeInitializeEvent(&event, NotificationEvent, FALSE);

newIrp = IoAllocateIrp( TopOfDeviceStack->StackSize, FALSE );
if (NULL == newIrp) {

return STATUS_INSUFFICIENT_RESOURCES;
}

nextStack = IoGetNextIrpStackLocation( newIrp );
nextStack->MajorFunction = IRP_MJ_SCSI;
nextStack->Parameters.Scsi.Srb = Srb;
Srb->OriginalRequest = newIrp;

if(TopOfDeviceStack->Flags & DO_BUFFERED_IO) {

newIrp->AssociatedIrp.SystemBuffer = WriteBuffer;
newIrp->MdlAddress = NULL;

} else if (TopOfDeviceStack->Flags & DO_DIRECT_IO) {

newIrp->MdlAddress = IoAllocateMdl( WriteBuffer,
NumBytes,
FALSE,
FALSE,
(PIRP) NULL );
if (newIrp->MdlAddress == NULL) {
IoFreeIrp( newIrp );
return STATUS_INSUFFICIENT_RESOURCES;
}

__try {

MmProbeAndLockPages( newIrp->MdlAddress,
KernelMode,
(LOCK_OPERATION) (IsRead == TRUE ? IoReadAccess : IoWriteAccess) );

} __except(EXCEPTION_EXECUTE_HANDLER) {

if (newIrp->MdlAddress != NULL) {
IoFreeMdl( newIrp->MdlAddress );
}
IoFreeIrp( newIrp );
return GetExceptionCode();

}
}

IoSetCompletionRoutine(newIrp,
MakeAsynchronousRequestCompletion2,
NULL,
TRUE,
TRUE,
TRUE);

newIrp->UserIosb = &ioStatus;
newIrp->UserEvent = &event;

IoMarkIrpPending(newIrp);

status = IoCallDriver(TopOfDeviceStack, newIrp);

if (status == STATUS_PENDING) {
(VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}

return STATUS_SUCCESS;
}

According my SCSIAdapter filter driver, there are two kinds of device object that I maybe should send the request to. One is the SCSI controller fdo, and the other one is the pdo which stands for disk. Then which device should I send the request to? I will get a error STATUS_IO_DEVICE_ERROR when sending to the fdo, and STATUS_INVALID_DEVICE_REQUEST to the pdo. Someone said I should use IoGetDeviceObjectPointer get the device object which named L"\Device\Scsi\symmpi1Port2Path0Target2Lun0", and send the request it it. But it still didn’t work and the device object the I have got was created by the partmgr so I don’t think it’s right.

Information about the so-called fdo:
kd> ??deviceExtension->NextLowerDriver
struct _DEVICE_OBJECT * 0x82740a40
+0x000 Type : 3
+0x002 Size : 0x58a
+0x004 ReferenceCount : 0
+0x008 DriverObject : 0x82739920 _DRIVER_OBJECT
+0x00c NextDevice : (null)
+0x010 AttachedDevice : 0x82752978 _DEVICE_OBJECT
+0x014 CurrentIrp : (null)
+0x018 Timer : (null)
+0x01c Flags : 0x50
+0x020 Characteristics : 0x100
+0x024 Vpb : (null)
+0x028 DeviceExtension : 0x82740af8
+0x02c DeviceType : 4
+0x030 StackSize : 2 ‘’
+0x034 Queue :
+0x05c AlignmentRequirement : 0
+0x060 DeviceQueue : _KDEVICE_QUEUE
+0x074 Dpc : _KDPC
+0x094 ActiveThreadCount : 0
+0x098 SecurityDescriptor : 0xe14aef70
+0x09c DeviceLock : _KEVENT
+0x0ac SectorSize : 0
+0x0ae Spare1 : 1
+0x0b0 DeviceObjectExtension : 0x82740fd0 _DEVOBJ_EXTENSION
+0x0b4 Reserved : (null)
the driver object for it:
kd> dt 0x82739920 _DRIVER_OBJECT
nt!_DRIVER_OBJECT
+0x000 Type : 4
+0x002 Size : 168
+0x004 DeviceObject : 0x82758030 _DEVICE_OBJECT
+0x008 Flags : 0x12
+0x00c DriverStart : 0xf823d000
+0x010 DriverSize : 0x11000
+0x014 DriverSection : 0x827d0ae0
+0x018 DriverExtension : 0x827399c8 _DRIVER_EXTENSION
+0x01c DriverName : _UNICODE_STRING “\Driver\symmpi”
+0x024 HardwareDatabase : 0x809d8260 _UNICODE_STRING “\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM”
+0x028 FastIoDispatch : (null)
+0x02c DriverInit : 0xf824b005 long symmpi!GsDriverEntry+0
+0x030 DriverStartIo : 0xf8220214 void SCSIPORT!ScsiPortStartIo+0
+0x034 DriverUnload : 0xf822f8a6 void SCSIPORT!ScsiPortUnload+0
+0x038 MajorFunction : [28] 0xf821f27c long SCSIPORT!ScsiPortGlobalDispatch+0

This problem puzzled me for weeks, and I want to know how to send Read10 command irp to a SCSIAdapter device object.

Any help will be grateful!

Hmmm… it is a bit difficult to understand your design, but my guess is
that you are attempting to bypass the class driver and send read/write SRBs
to the adapter, and that is just not going to work. At all.

How could it? That disk pdo enumerated by scsiport (or storport or ataport
etc) is claimed by disk.sys and is the target of IO operations from
disk.sys. The use of the term “claimed” is deliberate, by the way. There is
a ‘claiming’ protocol between class and port driver that attempts to make
‘claimed’ pdos exlusive to the class driver that claimed them. The only way
for other entities to send IO requests to the storage port drivers
directly is by using scsipass through requests from user mode.

Mark Roddy

On Thu, Aug 26, 2010 at 3:58 AM, wrote:

> Any help will be grateful!
>
> —
> NTDEV is sponsored by OSR
>
> For our schedule of WDF, WDM, debugging and other seminars visit:
> http://www.osr.com/seminars
>
> To unsubscribe, visit the List Server section of OSR Online at
> http://www.osronline.com/page.cfm?name=ListServer
>

I agree with Mark that the most easy way to solve your problem is to use scsipass. Both scsipass and Read10 command eventually come to the same dispatch function - StartIo.
However you could try to find a way to directly generate Read10 command. Your request should come through disk.sys who will generate SRB. Install check build versions of disk.sys and scsiport ( or storport whichever you use) and see how IRP_MJ_READ transferred to CDB READ10. You should get the first READ10 after READ_CAPACITY during the boot . You may use an upper disk class filter to see IRP_MJ_READ but I think WinDbg would enough. Do reverse engineering work. It is not easy but if you develop a virtual storage driver you should handle it.

Igor Sharovar