我正在转换一些旧代码以与自定义 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);
}