DeviceIoControl for SCSI INQUIRY command returns error 50

徘徊边缘 提交于 2019-12-11 18:25:54

问题


I am trying to access a USB scanner through the IOCTL commands. This is on Windows 7. I did not deal with IOCTL coding before, so I first tried the following snippet based on what I could find with a quick search.

#include "stdafx.h"
#include <stddef.h>
#include <Windows.h>
#include <ntddscsi.h>
#include <usbscan.h>

typedef struct
{
    SCSI_PASS_THROUGH spt;
    BYTE sense[18];
    BYTE data[36];
} SPTSD;

LPTSTR ErrorMessage(DWORD error)
{
    LPTSTR errorText = NULL;

    FormatMessage(
       FORMAT_MESSAGE_FROM_SYSTEM
       |FORMAT_MESSAGE_ALLOCATE_BUFFER
       |FORMAT_MESSAGE_IGNORE_INSERTS,  
       NULL,
       error,
       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
       (LPTSTR)&errorText,
       0, 
       NULL);

    return errorText;
}

int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE h = CreateFile(L"\\\\.\\Usbscan0", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    if (INVALID_HANDLE_VALUE != h)
    {
        SPTSD sptsd={0};
        sptsd.spt.Length = sizeof (sptsd.spt);
        sptsd.spt.SenseInfoLength = sizeof(sptsd.sense);
        sptsd.spt.DataTransferLength = sizeof(sptsd.data);
        sptsd.spt.SenseInfoOffset = offsetof (SPTSD, sense);
        sptsd.spt.DataBufferOffset = offsetof (SPTSD, data);
        sptsd.spt.TimeOutValue = 30;
        sptsd.spt.DataIn = SCSI_IOCTL_DATA_IN;
        sptsd.spt.CdbLength = 6;
        sptsd.spt.Cdb[0] = 0x12; // SCSI INQUIRY command
        sptsd.spt.Cdb[1] = 0;
        sptsd.spt.Cdb[2] = 0;
        sptsd.spt.Cdb[3] = 0;
        sptsd.spt.Cdb[4] = sizeof(sptsd.data);
        sptsd.spt.Cdb[5] = 0;

        DWORD dwReturnedBytes;
        BOOL b;

        b = DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH, &sptsd, sizeof(sptsd), &sptsd, sizeof(sptsd), &dwReturnedBytes, NULL);

        if (b == 0)
        {
            LPTSTR errortext = ErrorMessage(GetLastError());
            wprintf(L"DeviceIoControl(IOCTL_SCSI_PASS_THROUGH-INQUIRY) failed with error %d : %s\r\n", GetLastError(), errortext);
            LocalFree(errortext);
        }
        else
        {
            wprintf(L"DeviceIoControl(IOCTL_SCSI_PASS_THROUGH-INQUIRY) succeeded\r\n");
            for (int i=0; i<dwReturnedBytes; i++)
            {
                wprintf(L"%02x ", sptsd.data[i]);
            }

            wprintf(L"\r\nEnd of returned data\r\n");
        }

        DEVICE_DESCRIPTOR dd;
        b = DeviceIoControl(h, IOCTL_GET_DEVICE_DESCRIPTOR, &dd, sizeof(dd), &dd, sizeof(dd), &dwReturnedBytes, NULL);

        if (b == 0)
        {
            LPTSTR errortext = ErrorMessage(GetLastError());
            wprintf(L"DeviceIoControl(IOCTL_GET_DEVICE_DESCRIPTOR) failed with error %d : %s\r\n", GetLastError(), errortext);
            LocalFree(errortext);
        }
        else
        {
            wprintf(L"DeviceIoControl(IOCTL_GET_DEVICE_DESCRIPTOR) succeeded\r\n");
            wprintf(L"VendorId = %x, ProductId = %x, Version = %x\r\n", dd.usVendorId, dd.usProductId, dd.usBcdDevice);
            wprintf(L"End of returned data\r\n");
        }

        CloseHandle(h);
    }

    return 0;
}

I tried both 32-bit and 64-bit versions of Windows 7 but the result is the same on both (error 50 : The request is not supported.). Interestingly, second DeviceIoControl call works and returns the VID/PID of the device, along with the firmware version.

Based on the error message, I would think this IOCTL is not supported. However, I looked into it and found out that this IOCTL code is mandatory for all devices, so there must be something I am doing wrong. How should this code be modified so that the INQUIRY command will succeed?


回答1:


According to http://msdn.microsoft.com/en-us/library/ff548569%28v=vs.85%29.aspx, these IOCTL codes are recognized by the kernel-mode still image driver for USB buses.

  • IOCTL_CANCEL_IO
  • IOCTL_GET_CHANNEL_ALIGN_RQST
  • IOCTL_GET_DEVICE_DESCRIPTOR
  • IOCTL_GET_PIPE_CONFIGURATION
  • IOCTL_GET_USB_DESCRIPTOR
  • IOCTL_GET_VERSION
  • IOCTL_READ_REGISTERS
  • IOCTL_RESET_PIPE
  • IOCTL_SEND_USB_REQUEST
  • IOCTL_SET_TIMEOUT
  • IOCTL_WAIT_ON_DEVICE_EVENT IOCTL_WRITE_REGISTERS

My understanding is that any other IOCTL code should be sent via IOCTL_SEND_USB_REQUEST control code. This explains why trying to send a INQURY command using the above code does not work.

EDIT: It was simply a matter of using WriteFile to send the INQUIRY command and ReadFile to read the response. However, there seems an additional issue that I do not understand: The device wants an extra byte after the 6 bytes of the INQUIRY command to send the response. Otherwise, ReadFile will only return a single byte (0x3). I will update this reply again if I figure out what is happening here.



来源:https://stackoverflow.com/questions/23944239/deviceiocontrol-for-scsi-inquiry-command-returns-error-50

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!