Win 7 64位中的SPTI“模式选择”意外失败

时间:2012-11-30 14:35:32

标签: winapi windows-7 scsi

我正在转换一些旧代码以与自定义SCSI设备通信。原始代码是为WinXP和ASPI编写的,较新的代码需要在Win7和SPTI上运行。我的问题是,新的代码在调用时失败,执行SCSI“模式选择”操作,状态代码为2,这是一个SCSI“检查条件”错误 - 但这不会发生在WinXP下的旧代码中。

通常,当您获得“检查条件”代码时,您可以向设备发出“请求检测”命令以查明发生的情况。不幸的是,这个设备(在我看来)是错误的,当你做一个Request Sense时总是返回“一切都好”。所以我在这里工作。

所以我希望能就SPTI代码的错误提出一些建议,并对任何反馈表示感谢。

以下是我想到的可能影响这一点的一些事情:

  • 设备预期的顺序是“Reserve Unit”,“Rezero Unit”,“Mode Select”,然后是其他一些操作,然后是“Release Unit”。看起来“预留单元”,“Rezero单元”和“释放单元都工作正常,但其他操作失败,因为”模式选择“失败。
  • 对于每个操作,SPTI代码打开并关闭SCSI主机适配器的句柄。我应该打开“预留单位”中的一个手柄并将其打开整个序列吗?
  • 发送到DeviceIoControl()的ioctl是IOCTL_SCSI_PASS_THROUGH。我应该使用IOCTL_SCSI_PASS_THROUGH_DIRECT进行“模式选择”操作吗?这是一个简单的操作,所以我认为更简单的API就足够了,但也许我错了。

有问题的代码是:

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 个答案:

答案 0 :(得分:0)

解决方案证明有两个部分。

首先,sptwb.spt.DataIn需要是SCSI_IOCTL_DATA_OUT而不是SCSI_IOCTL_DATA_IN - 因为当然,“模式选择”告诉设备该做什么,而不是向它询问信息。这将DeviceIoControl()的结果从TRUE更改为FALSE,然后GetLastError()返回值87,表示参数无效。

其次,正如我推测的那样,ioctl事务需要使用IOCTL_SCSI_PASS_THROUGH_DIRECT而不是IOCTL_SCSI_PASS_THROUGH来完成。

使用这两项更改完成所有设置后,“模式选择”命令成功。