I'm in the middle of converting some older code to talk with a custom SCSI device. The original code was written for WinXP and ASPI, and the newer code needs to work on Win7 and SPTI. My problem is that the newer code fails on a call to do a SCSI "Mode Select" operation with an status code of 2, which is a SCSI "Check Condition" error - but this doesn't happen with the older code under WinXP.
Normally, when you get a "Check Condition" code, you can issue a "Request Sense" command to the device to find out what happened. Unfortunately, this device is (in my opinion) buggy, and always returns "everything is okay" when you do a Request Sense. So I'm working in the dark here.
So I'm hoping for some suggestions on what I could be doing wrong with the SPTI code, and would be grateful for any feedback.
Here are a few things I've thought of that may be affecting this:
The code in question is:
void MSSModeSelect(const ModeSelectRequestPacket& inRequest, StatusResponsePacket& outResponse)
{
IPC_LOG("MSSModeSelect(): PathID=%d, TargetID=%d, LUN=%d", inRequest.m_Device.m_PathId,
inRequest.m_Device.m_TargetId, inRequest.m_Device.m_Lun);
int adapterIndex = inRequest.m_Device.m_PathId;
HANDLE adapterHandle = prvOpenScsiAdapter(inRequest.m_Device.m_PathId);
if (adapterHandle == INVALID_HANDLE_VALUE)
{
outResponse.m_Status = eScsiAdapterErr;
return;
}
SCSI_PASS_THROUGH_WITH_BUFFERS sptwb;
memset(&sptwb, 0, sizeof(sptwb));
#define MODESELECT_BUF_SIZE 32
sptwb.spt.Length = sizeof(SCSI_PASS_THROUGH);
sptwb.spt.PathId = inRequest.m_Device.m_PathId;
sptwb.spt.TargetId = inRequest.m_Device.m_TargetId;
sptwb.spt.Lun = inRequest.m_Device.m_Lun;
sptwb.spt.CdbLength = CDB6GENERIC_LENGTH;
sptwb.spt.SenseInfoLength = 0;
sptwb.spt.DataIn = SCSI_IOCTL_DATA_IN;
sptwb.spt.DataTransferLength = MODESELECT_BUF_SIZE;
sptwb.spt.TimeOutValue = 2;
sptwb.spt.DataBufferOffset =
offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf);
sptwb.spt.Cdb[0] = SCSIOP_MODE_SELECT;
sptwb.spt.Cdb[4] = MODESELECT_BUF_SIZE;
DWORD length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf) +
sptwb.spt.DataTransferLength;
memset(sptwb.ucDataBuf, 0, sizeof(sptwb.ucDataBuf));
sptwb.ucDataBuf[2] = 0x10;
sptwb.ucDataBuf[4] = 0x01;
sptwb.ucDataBuf[5] = 0x04;
ULONG bytesReturned = 0;
BOOL okay = DeviceIoControl(adapterHandle,
IOCTL_SCSI_PASS_THROUGH,
&sptwb,
sizeof(SCSI_PASS_THROUGH),
&sptwb,
length,
&bytesReturned,
FALSE);
DWORD gle = GetLastError();
IPC_LOG(" DeviceIoControl() %s", okay ? "worked" : "failed");
if (okay)
{
outResponse.m_Status = (sptwb.spt.ScsiStatus == 0) ? eOk : ePrinterStatusErr;
}
else
{
outResponse.m_Status = eScsiPermissionsErr;
}
CloseHandle(adapterHandle);
}
The solution proved to have two parts.
First, sptwb.spt.DataIn needed to be SCSI_IOCTL_DATA_OUT rather than SCSI_IOCTL_DATA_IN - because, of course, "Mode Select" is telling the device what to do, rather than asking it for information. This changed the result of DeviceIoControl() from TRUE to FALSE, and GetLastError() then returned a value of 87, indicating an invalid parameter.
Second, as I'd speculated, the ioctl transaction needs to be done using IOCTL_SCSI_PASS_THROUGH_DIRECT rather than IOCTL_SCSI_PASS_THROUGH.
Once everything was set up right with those two changes, the "Mode Select" command succeeded.