Hello all,
I am in the process of developing a Storport Miniport driver to interface with custom hardware and a usermode application. I need to call custom IOCTLs in the driver from my usermode application. I’m running Win7x64.
Before I had any hardware ready, I created a virtual storport miniport to get usermode communication working the way I wanted to. I am pretty much just locking usermode pages into memory in kernel-mode.
In my virtual storport miniport’s DriverEntry, I was able to define hwInitData.HwProcessServiceRequest =ProcessServiceRequest; which looked something like this in the driver:
ULONG ProcessServiceRequest( __in PVOID PDevExt, __in PVOID PIrp)
{
PIRP pIrp = (PIRP) PIrp;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(pIrp);
if(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
//Struct that has at minimum an IoControlCode defined as first member
PCOMMAND_IN command = (PCOMMAND_IN) pIrp->AssociatedIrp.SystemBuffer;
IoControlCode which kicks off IOCTL-specific logic>
}
}
and from Usermode I would set up a structure conforming to COMMAND_IN above and call this through:
DeviceIoControl(SPDriverHandle,IOCTL_MINIPORT_PROCESS_SERVICE_IRP, &toPass,sizeof(TESTSTRUCT),&toPass,sizeof(TESTSTRUCT),&bytesReturned,NULL);
This would enter right in to the HwProcessServiceRequest function in the driver and execute whatever code I had defined there. It was beautiful, worked flawlessly, and life was good.
You might already know what is coming, for in my naivete I expected to be able to port this fairly easily to a Storport Miniport that talks to actual hardware. Alas, I am not an experienced driver developer, and I didn’t realize that HwProcessServiceRequest doesn’t exist for a physical-device Storport Miniport. A rework was in order.
It seemed that the best way to communicate through IOCTL to my driver from usermode applications was through using a STORAGE_REQUEST_BLOCK instance with the .DataBuffer member as a pointer to the SrbIOCTL I want to send, with the function field defined as SRB_FUNCTION_IO_CONTROL and appropriate lengths, etc.
Then I will supposedly pass it to the driver with:
DeviceIoControl(SPDriverHandle, IOCTL_SCSI_MINIPORT, &StorReqBlockToSend……);
And I expected it to come out one of these functions I defined in the driver:
hwInitData.HwStartIo = StartIo;
hwInitData.HwBuildIo = BuildIo;
Neither of these functions are entered. Stranger yet is that the DeviceIoControl function returns 0 (Success, supposedly).
Here is my usermode application code. In the comments and unnecessary code are a few other failed attempts that yielded identical results. I used http://msdn.microsoft.com/en-us/library/windows/hardware/hh451474(v=vs.85).aspx (specifically the part regarding SRB_FUNCTION_IO_CONTROL) and other articles (now lost) as references
#define TEST_STORPORT_DRIVER 0xE000
#define IOCTL_TEST CTL_CODE(TEST_STORPORT_DRIVER, 0x8FF, METHOD_NEITHER, FILE_ANY_ACCESS)
//#define IOCTL_TEST CTL_CODE(FILE_DEVICE_MASS_STORAGE, 0x8FF, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
int _tmain(int argc, _TCHAR* argv)
{
HANDLE SPDriverHandle;
SPDriverHandle = CreateFile(L"\\.\Scsi2:", // Name of the NT device to open
GENERIC_READ|GENERIC_WRITE, // Access rights requested
0, // Share access - NONE
0, // Security attributes - not used!
OPEN_EXISTING, // Device must exist to open it.
FILE_FLAG_OVERLAPPED, // Open for overlapped I/O
0); // extended attributes - not used!
if(SPDriverHandle == INVALID_HANDLE_VALUE)
{
DWORD error = GetLastError();
printf(“error:%d.\n”,error);
}
else
{
STORAGE_REQUEST_BLOCK toSend;
SRB_IO_CONTROL Srbioctl;
DWORD bytesReturned = 0;
long retCode = 0;
DWORD error = GetLastError();
printf(“Did that just work? Code: %i\n”, error);
toSend.SrbFunction = 0x02; //Equal to SRB_FUNCTION_IO_CONTROL;
toSend.DataBuffer = &Srbioctl;
toSend.DataTransferLength = sizeof(SRB_IO_CONTROL);
toSend.SrbFlags = 0;
toSend.TimeOutValue = 10;
toSend.Function = 0x02; //Equal to SRB_FUNCTION_IO_CONTROL;
Srbioctl.ControlCode = IOCTL_TEST;
Srbioctl.Length = 0;
Srbioctl.HeaderLength = sizeof(SRB_IO_CONTROL);
Srbioctl.ReturnCode = 0;
Srbioctl.Timeout = 10;
//retCode = DeviceIoControl(SPDriverHandle,IOCTL_SCSI_PASS_THROUGH,
retCode = DeviceIoControl(SPDriverHandle,IOCTL_SCSI_MINIPORT,
&toSend,sizeof(STORAGE_REQUEST_BLOCK),&toSend,sizeof(STORAGE_REQUEST_BLOCK),&bytesReturned,NULL);
printf(“DeviceIoCtl done: Code = %i \n”,retCode);
}
getchar();
return 0;
}
As long as my handle is valid (and I’ve tried the SCSI device handle, the HW ID handle, and even one I built into the driver to reference it by name), both return codes printed to the console are both “0”. No runtime errors or anything is returned to the user to indicate a failure. No bugchecks, debug output (mine), or anything else out of the kernel debugger to indicate that anything happened at all in kernelmode. No driver functions are entered and no errors are encountered. The driver IS loaded at the handles I am trying when I am trying them. Every attempt to make the driver even react to something usermode does in this way has been met with unspectacular failure. I’m not sure what I’m doing wrong, or if I’m hunting down the wrong rabbit-hole entirely.
TL:DR- I thought I had IOCTL Usermode-Driver communication working in a virtual storport miniport, but porting to a physical storport miniport won’t allow me to call IOCTLs in the same way. Above are my failed attempts at using what I think is the “right” method. How do I call IOCTLs inside a Storport Miniport from Usermode?
I appreciate any wisdom or expertise you may be able to bestow upon me! Many thanks!