1

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 sequence the device expects is "Reserve Unit", "Rezero Unit", "Mode Select", then some other operations, then "Release Unit". It appears "Reserve Unit", "Rezero Unit", and "Release Unit are all working fine, but the other operations fail because "Mode Select" failed.
  • For each operation, the SPTI code opens and closes a handle to the SCSI host adapter. Should I open a handle in "Reserve Unit" and leave it open for the entire sequence?
  • The ioctl sent to DeviceIoControl() is IOCTL_SCSI_PASS_THROUGH. Should I be using IOCTL_SCSI_PASS_THROUGH_DIRECT for the "Mode Select" operation? It's a simple operation, so I figured the simpler API would be adequate for this, but maybe I'm wrong about that.

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);
}
1
  • I rolled this back. Somebody added a "device-driver" tag, but the code in question isn't a device driver, it's in a Windows Service - and the device itself doesn't have a Windows driver, which is why the service does direct ioctl operations. So the rollback removes that tag.
    – Bob Murphy
    Dec 5, 2012 at 0:12

1 Answer 1

0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.