SCSI INQUIRY 命令的 DeviceIoControl 返回错误 50
Posted
技术标签:
【中文标题】SCSI INQUIRY 命令的 DeviceIoControl 返回错误 50【英文标题】:DeviceIoControl for SCSI INQUIRY command returns error 50 【发布时间】:2014-05-29 22:46:01 【问题描述】:我正在尝试通过 IOCTL 命令访问 USB 扫描仪。这是在Windows 7上。我之前没有处理过IOCTL编码,所以我首先根据我通过快速搜索找到的内容尝试了以下sn-p。
#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;
我尝试了 32 位和 64 位版本的 Windows 7,但两者的结果相同(错误 50:不支持该请求。)。有趣的是,第二个 DeviceIoControl 调用起作用并返回设备的 VID/PID 以及固件版本。
根据错误消息,我认为不支持此 IOCTL。但是,我调查了一下,发现这个 IOCTL 代码对所有设备都是强制性的,所以一定是我做错了什么。这段代码应该如何修改才能让INQUIRY命令成功?
【问题讨论】:
【参考方案1】:根据http://msdn.microsoft.com/en-us/library/ff548569%28v=vs.85%29.aspx,这些 IOCTL 代码被 USB 总线的内核模式静止图像驱动程序识别。
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我的理解是任何其他 IOCTL 代码都应通过IOCTL_SEND_USB_REQUEST control code 发送。这解释了为什么尝试使用上述代码发送 INQUERY 命令不起作用。
编辑:只需使用WriteFile 发送INQUIRY 命令和ReadFile 读取响应即可。但是,似乎还有一个我不明白的附加问题:设备需要在 INQUIRY 命令的 6 个字节之后有一个额外的字节来发送响应。否则,ReadFile 将只返回一个字节 (0x3)。如果我弄清楚这里发生了什么,我会再次更新此回复。
【讨论】:
以上是关于SCSI INQUIRY 命令的 DeviceIoControl 返回错误 50的主要内容,如果未能解决你的问题,请参考以下文章