1

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

通常,当您获得“检查条件”代码时,您可以向设备发出“请求感知”命令以查明发生了什么。不幸的是,这个设备(在我看来)是有问题的,当你执行请求感知时总是返回“一切正常”。所以我在这里在黑暗中工作。

因此,我希望对我可能在 SPTI 代码中做错的地方提出一些建议,并感谢任何反馈。

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

  • 设备期望的顺序是“Reserve Unit”、“Rezero Unit”、“Mode Select”,然后是一些其他操作,然后是“Release Unit”。看起来“保留单元”、“重新归零单元”和“释放单元”都工作正常,但其他操作失败,因为“模式选择”失败。
  • 对于每个操作,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);
}
4

1 回答 1

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 来完成。

一旦通过这两个更改正确设置了所有内容,“模式选择”命令就成功了。

于 2012-11-30T17:57:00.667 回答