Выбор режима SPTI неожиданно завершается ошибкой в ​​64-разрядной версии Win 7

Я нахожусь в середине преобразования некоторого старого кода, чтобы общаться с пользовательским устройством SCSI. Исходный код был написан для WinXP и ASPI, а новый код должен работать на Win7 и SPTI. Моя проблема заключается в том, что более новый код не работает при вызове операции SCSI «Выбор режима» с кодом состояния 2, что является ошибкой SCSI «Проверить условие», но этого не происходит с более старым кодом под WinXP.

Обычно, когда вы получаете код «Проверить состояние», вы можете отправить на устройство команду «Request Sense», чтобы узнать, что произошло. К сожалению, это устройство (на мой взгляд) глючит и всегда возвращает «все в порядке», когда вы выполняете Request Sense. Так что я работаю в темноте здесь.

Поэтому я надеюсь на некоторые предложения о том, что я мог делать неправильно с кодом SPTI, и был бы признателен за любую обратную связь.

Вот несколько вещей, которые, как я думал, могут повлиять на это:

  • Последовательность, которую ожидает устройство: «Резервный блок», «Обнуление блока», «Выбор режима», затем некоторые другие операции, затем «Выпуск блока». Похоже, что «Reserve Unit», «Rezero Unit» и «Release Unit» работают нормально, но другие операции завершаются сбоем из-за сбоя «Mode Select».
  • Для каждой операции код SPTI открывает и закрывает дескриптор хост-адаптера SCSI. Должен ли я открыть дескриптор в «Резервном блоке» и оставить его открытым для всей последовательности?
  • ioctl, отправленный в DeviceIoControl(), — это 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);
}

person Bob Murphy    schedule 30.11.2012    source источник
comment
Я откатил это обратно. Кто-то добавил тег драйвера устройства, но рассматриваемый код не является драйвером устройства, он находится в службе Windows, а само устройство не имеет драйвера Windows, поэтому служба выполняет прямые операции ioctl. Таким образом, откат удаляет этот тег.   -  person Bob Murphy    schedule 05.12.2012


Ответы (1)


Решение оказалось состоящим из двух частей.

Во-первых, 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.

Как только все было правильно настроено с этими двумя изменениями, команда «Выбор режима» сработала.

person Bob Murphy    schedule 30.11.2012