Need to write driver for USB peripheral device? - windows

I'm working on designing a USB peripheral which will occasionally connect to a Windows PC, and transfer a few KB of data in each direction. There will be a custom PC application that controls the data transfers, using a proprietary protocol (i.e. for the USB payloads).
I see at the following link that Microsoft describes how to write a driver for a USB device. But do I need one?
Developing Windows client drivers for USB devices
The PC application is the only application that we intend to know how to communicate with the device, so there's no need for a driver from an application sharing standpoint.
Can I just bake the custom protocol directly into the application, have the application speak "raw USB" to the device, and do without a separate driver?

"raw USB", no, you can't do that from an application.
But because you control the device also, you can make it appear as one of the device classes for which Windows provides a device driver that's generic enough to do just about anything you want.
Those device classes are HID (Human Interface Device) and "WinUSB". Of these, HID is cross-platform but more limited in capability, WinUSB allows high performance data transfers as well as interrupt endpoints.
Instructions for setting up your device's string descriptors so that Windows automatically binds it to the WinUSB driver are on MSDN
A WinUSB device is a Universal Serial Bus (USB) device whose firmware defines certain Microsoft operating system (OS) feature descriptors that report the compatible ID as "WINUSB".
The purpose of a WinUSB device is to enable Windows to load Winusb.sys as the device's function driver without a custom INF file. For a WinUSB device, you are not required to distribute INF files for your device, making the driver installation process simple for end users.

There is another way with no need to write driver to write what You want to device using WriteFile function:WinUSB, how to do this:
Include WinUsb.h
Add WinUsb.lib to the list of linked libraries.
in Usb100.h some macros.
Use the device interface GUID to obtain the device path. The correct GUID is the one that you specified in the INF that was used to install WinUsb.sys.
Get a handle to the device information set by passing the device interface GUID that you defined in the INF to SetupDiGetClassDevs. The function returns an HDEVINFO handle.
Call SetupDiEnumDeviceInterfaces to enumerate the system’s device interfaces and obtain information on your device interface.
Call SetupDiGetDeviceInterfaceDetail to get detailed data for the device interface.
Call the GetDevicePath function to obtain the device path.
Pass the device path to CreateFile to obtain a file handle for the device. Use ReadFile and Write File to communicate with device!
Pass the file handle to WinUsb_Initialize to initialize WinUSB and obtain a WinUSB handle. You use the device’s WinUSB handle to identify the device when you call WinUSB API functions, not the device’s file handle.
For more advanced solutions - use functions:
WinUsb_QueryDeviceInformation to obtain the device’s speed.
WinUsb_QueryInterfaceSettings to obtain the corresponding interface descriptors. The WinUSB handle corresponds to the first interface.
WinUsb_QueryPipe gets information about each endpoint.
WinUsb_WritePipe writes the buffer to the device - default behavior: zero-length writes are forwarded down the stack. If the transfer length is greater than a maximum transfer length, WinUSB divides the request into smaller requests of maximum transfer length and submits them serially.
more functions and info: http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/winusb_howto.docx
For debugging purposes You probably need:
winusbtrace_tool https://blogs.msdn.microsoft.com/usbcoreblog/2010/02/05/how-to-generate-and-view-a-winusb-debug-trace-log/;
Wireshark https://www.wireshark.org with USBPcap plugin.
Other Example:
http://searchingforbit.blogspot.com/2012/04/winusb-communication-with-stm32-part-1.html.
Sample template comes with Visual Studio.
You need also have knowledge of writing .inf files.
Another easy way to communicate with USB - libusb-win32 https://sourceforge.net/projects/libusb-win32/
My simple console app sends chunks to device (raw data write immediately to device bypassing the stack):
#include "stdafx.h"
#include <SetupAPI.h>
#include <Hidsdi.h>
#include <devguid.h>
#include <winusb.h>
#include <usb.h>
#pragma comment(lib, "hid.lib")
#pragma comment(lib, "setupapi.lib")
#pragma comment(lib, "winusb.lib")
#include <iUString.h>
iString<char> DevicePath;
bool WinusbHandle_Open=false;
bool DeviceHandle_Open = false;
WINUSB_INTERFACE_HANDLE WinusbHandle;
HANDLE DeviceHandle;
UCHAR usb_out_buffer[64];
DEFINE_GUID(GUID_DEVCLASS_WINUSB, 0x88bae032L, 0x5a81, 0x49f0, 0xbc, 0x3d, 0xa4, 0xff, 0x13, 0x82, 0x16, 0xd6);
DEFINE_GUID(GUID_DEVCLASS_STL, 0xf177724dL, 0x74d3, 0x430e, 0x86, 0xb5, 0xf0, 0x36, 0x89, 0x10, 0xeb, 0x23);
GUID winusb_guid;
GUID stl_guid;
bool connectusb();
void disconnectusb();
int main()
{
DWORD n;
DWORD numEvents;
HANDLE rHnd;
WinusbHandle_Open = false;
DeviceHandle_Open = false;
winusb_guid = GUID_DEVCLASS_WINUSB;
stl_guid = GUID_DEVCLASS_STL;
usb_out_buffer[0] = 0;
usb_out_buffer[1] = 1;
usb_out_buffer[2] = 2;
usb_out_buffer[3] = 3;
ULONG bytesWritten;
ULONG timeout;
timeout = 100;
rHnd = GetStdHandle(STD_INPUT_HANDLE);
WinUsb_SetPipePolicy(WinusbHandle, 0x01, PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout);
timeout = TRUE;
WinUsb_SetPipePolicy(WinusbHandle, 0x01, AUTO_CLEAR_STALL, sizeof(ULONG), &timeout);
timeout = TRUE;
WinUsb_SetPipePolicy(WinusbHandle, 0x01, RAW_IO, sizeof(ULONG), &timeout);//Bypasses queuing and error handling to boost performance for multiple read requests.
while (true)
{
if ((!WinusbHandle_Open) || (!WinusbHandle_Open)) { if (!connectusb())Sleep(2000); }
if ((!WinusbHandle_Open) || (!WinusbHandle_Open))continue;
bytesWritten = 0;
if (!WinUsb_WritePipe(WinusbHandle, 0x01, &usb_out_buffer[0], 2, &bytesWritten, NULL))
{
n = GetLastError();
disconnectusb();
}
Sleep(2000);
}
disconnectusb();
return 0;
}
bool connectusb()
{
BOOL bResult = FALSE;
HDEVINFO deviceInfo;
SP_DEVICE_INTERFACE_DATA interfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL;
DWORD n;
SP_DEVINFO_DATA devinfo;
BYTE devdetailbuffer[4096];
bool found;
deviceInfo = SetupDiGetClassDevs(&stl_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (deviceInfo == INVALID_HANDLE_VALUE) { return false; }
found = false;
for (n = 0;; n++)
{
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if (!SetupDiEnumDeviceInterfaces(deviceInfo, NULL, &stl_guid, n, &interfaceData))
{
n = GetLastError();
break;
}
detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)devdetailbuffer;
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
devinfo.cbSize = sizeof(devinfo);
if (!SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, detailData, sizeof(devdetailbuffer), NULL, &devinfo)) { printf("SetupDiGetDeviceInterfaceDetail: %u\n", GetLastError()); break; }
if (IsEqualGUID(devinfo.ClassGuid, winusb_guid))
{
if ((-1 != iStrPos(detailData->DevicePath, "VID_0483")) || (-1 != iStrPos(detailData->DevicePath, "vid_0483")))
{
if ((-1 != iStrPos(detailData->DevicePath, "PID_576B")) || (-1 != iStrPos(detailData->DevicePath, "pid_576b")))
{
DevicePath = detailData->DevicePath;
found = true;
break;
}
}
}
}
SetupDiDestroyDeviceInfoList(deviceInfo);
if (!found)return false;
DeviceHandle = CreateFile(DevicePath.Buffer() ,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (INVALID_HANDLE_VALUE == DeviceHandle) {
n = GetLastError();
}
if (INVALID_HANDLE_VALUE == DeviceHandle) return false;
DeviceHandle_Open = true;
if (!WinUsb_Initialize(DeviceHandle, &WinusbHandle))
{
n = GetLastError();
CloseHandle(DeviceHandle); DeviceHandle_Open = false;
return false;
}
WinusbHandle_Open = true;
return true;
}
void disconnectusb()
{
if (WinusbHandle_Open) { WinUsb_Free(WinusbHandle); WinusbHandle_Open = false; }
if (DeviceHandle_Open) { CloseHandle(DeviceHandle); DeviceHandle_Open = false; }
}

Making your firmware to be enumerated as a WINUSB (winusb generic driver) device makes life easier.
I believe it'd be clear if you have a demo and code so I made one for you :)
My KEIL project using the STM32F4 Discovery board working with WINUSB as an USB CDC device. You can see more information and have the source code from my GitHub.

Related

How to differentiate between devices in the windows HID API?

I need to detect when a gamepad is plugged in for my game (I'm not using a higher-level input API for reasons), how can I do this? I've already enumerated all HID devices and opened files on them (except keyboard, mouse ofc) and can get all the info on the device, but what info do I want? What value(s) will tell me right away that this is an xbox controller, for example, and where are these values?
My code for enumerating over the devices (very messy atm :p):
DWORD required_size = 0, determined_size;
SP_DEVICE_INTERFACE_DATA device_interface_data;
device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
PSP_DEVICE_INTERFACE_DETAIL_DATA device_interface_detail_data;
HANDLE current_device;
PHIDP_PREPARSED_DATA preparsed_data;
WCHAR product_string[128];
for (int i = 0; ; ++i) /* Enumerate HID devices */
{
rv = SetupDiEnumDeviceInterfaces(device_enumeration, NULL, &interface_guid, i, &device_interface_data); /* Does the actual enumeration,
each time we increase index i
to get the next device */
error = GetLastError();
if (error == ERROR_NO_MORE_ITEMS) /* If there are no more devices, break */
break;
else if (!rv && error != ERROR_NO_MORE_ITEMS) /* Otherwise, we have a legit error */
{
cr_print_error(GetLastError());
return EXIT_FAILURE;
}
SetupDiGetDeviceInterfaceDetail(device_enumeration, &device_interface_data, NULL, 0, &required_size, NULL); /* Probing call only to get buffer size,
so error code (122) ignored */
/* Allocate new device detail struct using buffer size we obtained */
determined_size = required_size;
device_interface_detail_data = cr_safe_malloc(required_size);
device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
/* Get detailed info about device */
rv = SetupDiGetDeviceInterfaceDetail(device_enumeration, &device_interface_data, device_interface_detail_data, determined_size, &required_size, NULL);
if (!rv)
{
cr_print_error(GetLastError());
return EXIT_FAILURE;
}
cr_printf("FOUNDD DEVICE: %s\n\n", device_interface_detail_data->DevicePath);
current_device = cr_open_device(device_interface_detail_data->DevicePath);
if (!current_device && GetLastError() == ERROR_ACCESS_DENIED)
goto Done;
cr_printf("OPENED DEVICE: %s\n\n", device_interface_detail_data->DevicePath);
preparsed_data = HidD_GetPreparsedData(current_device, &preparsed_data);
if (!preparsed_data)
{
cr_print_error(GetLastError());
return EXIT_FAILURE;
}
HidD_GetProductString(current_device, product_string, sizeof(product_string));
cr_printf("PRODUCT STRING: %S\n\n", product_string);
HidD_FreePreparsedData(&preparsed_data);
DeleteFile(device_interface_detail_data->DevicePath);
Done:
cr_safe_free(&device_interface_detail_data);
}
SetupDiDestroyDeviceInfoList(device_enumeration);
As you can see I've found the product string, which tells me what the device actually is, but maybe comparing product strings is not the best way to do PnP?
Check if the device ID contains "IG_". If it does, then it's an XInput device.
More info on that. But which device corresponds to which XInput dwUserIndex is more difficult question...
Also you can try to check for XUSB interface:
// {EC87F1E3-C13B-4100-B5F7-8B84D54260CB}
DEFINE_GUID(XUSB_INTERFACE_CLASS_GUID, 0xEC87F1E3, 0xC13B, 0x4100, 0xB5, 0xF7, 0x8B, 0x84, 0xD5, 0x42, 0x60, 0xCB);

How to get a serial number of a Windows disk?

I'm trying to get a serial number of a disk, using IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER:
HANDLE h = CreateFile ("\\\\.\\PhysicalDrive0", GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING,
FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, 0);
if (h != INVALID_HANDLE_VALUE) {
struct {
USHORT Reserved;
USHORT SerialNumberLength;
UCHAR SerialNumber[252];
} dsn;
DWORD nr;
memset(&dsn, '\0', sizeof dsn);
if ((DeviceIoControl(h, IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
NULL, 0, &dsn, sizeof(dsn), &nr, 0))) {
printf("Serial number: %s\n", dsn.SerialNumber);
} else {
printf("No serial number, error %d.\n", (int)GetLastError());
}
}
However, GetLastError() returns ERROR_INVALID_FUNCTION.
The disk does exist, and it has a serial number, see this registry entry:
How can I retrieve the serial number from C code without using the registry?
we can use IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceProperty (Indicates that the caller is querying for the device descriptor, STORAGE_DEVICE_DESCRIPTOR)
and use SerialNumberOffset member of STORAGE_DEVICE_DESCRIPTOR
Specifies the byte offset from the beginning of the structure to a
NULL-terminated ASCII string that contains the device's serial number.
If the device has no serial number, this member is zero.
code can look like this:
ULONG GetSerial(HANDLE hFile)
{
static STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
union {
PVOID buf;
PSTR psz;
PSTORAGE_DEVICE_DESCRIPTOR psdd;
};
ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;
ULONG dwError;
do
{
dwError = ERROR_NO_SYSTEM_RESOURCES;
if (buf = LocalAlloc(0, size))
{
ULONG BytesReturned;
if (DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size, &BytesReturned, 0))
{
if (psdd->Version >= sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
if (psdd->Size > size)
{
size = psdd->Size;
dwError = ERROR_MORE_DATA;
}
else
{
if (psdd->SerialNumberOffset)
{
DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
dwError = NOERROR;
}
else
{
dwError = ERROR_NO_DATA;
}
}
}
else
{
dwError = ERROR_GEN_FAILURE;
}
}
else
{
dwError = GetLastError();
}
LocalFree(buf);
}
} while (dwError == ERROR_MORE_DATA);
return dwError;
}
also for open device we can use CreateFileW (L"\\\\.\\PhysicalDrive0", 0, 0, 0, OPEN_EXISTING, 0, 0); - in place dwDesiredAccess we can use 0 because IOCTL_STORAGE_QUERY_PROPERTY defined as
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - so FILE_ANY_ACCESS - accept any file access and FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING have sense only for file system devices (more general which use cache) - for disk devices - this is irrelevant
I have tried different approaches and figured out that sending IOCTL_STORAGE_QUERY_PROPERTY doesn't work as expected for different USB devices in both User and Kernel mode code. For some USB mass storages it doesn't return serial number. I'd assume that there are 2 correct ways to do that:
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER (a few AV products do that?)
create and send URB_CONTROL_DESCRIPTOR_REQUEST
Update 1.
I saw using IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER in the one file system mini-filter driver that was used like the following:
FltGetDiskDeviceObject( FltObjects->Volume, &pDevice );
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
Device,
...
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
Device,....
I haven't tested this yet in KernelMode code, unfortunately, but trying to make it works in user mode code shows that this IOCTL mostly
is not supported by different devices, maybe this IOCTL is reserved for the future as a standard way to get the serial number and will be
required by USB standards later?
Also, "wmic diskdrive get name, serialnumber" returns in my case for USB Mass Storage incorrect serial number = "E" the same result as we would use IOCTL_STORAGE_QUERY_PROPERTY.
So, the correct way to get the serial number of USB mass storage is creating a USB request block in KernelMode code and using DeviceIoControl to the hub driver in the UserMode code.
USBVIEW (UserMode code) gets serial number by sending IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX ioctl to the HUB driver which
returns USB_NODE_CONNECTION_INFORMATION_EX that contains USB_DEVICE_DESCRIPTOR. iSerialNumber member of USB_DEVICE_DESCRIPTOR is used later in the
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION ioctl request to the hub driver which finally gets serial number.
Another approach I see is maybe using some 3-rd party libraries like libusb to simplicate all these actions...
Update 2.
I took a look at USBSTOR disassembled code. USBSTOR_DeviceControl routine has the following code for the IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER
++v3->CurrentLocation;
++v3->Tail.Overlay.CurrentStackLocation;
v8 = IofCallDriver(*(PDEVICE_OBJECT *)(v6 + 24), v3);
So, it passes the IRP down the stack to the usbhub driver as was expected. So maybe this functionlaty is expected to be realized in
the usbhub driver sometime ? That would be great as for me...

Install winusb driver from user application

Please can anyone give me direct for realization next functional for Windows.
I have USB device which connects to the PC (it is JTAG programmer.) I know VID and PID of this hardware. I need:
1 Check what type of driver this hardware use (detecting winusb driver or not will be enough. Maybe do I need to read registry?)
2 If driver is not winusb I need to install winusb driver for this USB device from my application.
The current driver assigned to the device is stored in the registry, so you could read it directly from there. However, it is probably better to use SetupAPI, an API provided by Microsoft. The function to call is SetupDiGetDeviceRegistryProperty, and the third argument should be SPDRP_SERVICE. This will return the name of the driver as a string. Note that you will need to call several other SetupAPI functions before you have all the pieces of info you need to call SetupDiGetDeviceRegistryProperty.
I have not tried it, but libwdi has features for installing WinUSB onto a device node. It might also have functions for getting the current driver, so you should try using it before you spend too much time learning SetupAPI. The devcon utility from Microsoft (which is open source now) might be another option.
Without knowing the details of what you are doing, I question whether you really need to do this. It might be simpler to provide a signed driver package to users and instruct them to use the "Update Driver Software..." option from the Device Manager to apply it to particular device.
I made first part of task.
#ifdef Q_OS_WIN
DEFINE_GUID(GUID_DEVCLASS_WINUSB,0x88BAE032,0x5A81,0x49f0,
0xBC,0x3D,0xA4,0xFF,0x13,0x82,0x16,0xD6);
#endif
bool WinUSB::isWinUsbDriver(quint16 vid, quint16 pid)
{
#ifndef Q_OS_WIN
Q_UNUSED(vid);
Q_UNUSED(pid);
return true;
#else
HDEVINFO deviceInfoSet;
GUID *guidDev = (GUID*) &GUID_DEVCLASS_WINUSB;
deviceInfoSet = SetupDiGetClassDevs(guidDev, NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE);
DWORD buffersize =4000;
TCHAR buffer [buffersize];
int memberIndex = 0;
bool retval = false;
QString vidPid;
vidPid = "VID_" + QString("%1").arg(vid,4,16,QChar('0')) + "&";
vidPid += "PID_" + QString("%1").arg(pid,4,16,QChar('0'));
while (true)
{
SP_DEVINFO_DATA deviceInfoData;
ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA));
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (SetupDiEnumDeviceInfo(deviceInfoSet, memberIndex, &deviceInfoData) == FALSE) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
break;
}
}
DWORD nSize=0 ;
SetupDiGetDeviceInstanceId (deviceInfoSet, &deviceInfoData, buffer, sizeof(buffer), &nSize);
buffer [nSize] ='\0';
QString str = QString::fromWCharArray(buffer);
if (str.indexOf(vidPid) >= 0) {
retval = true;
break;
}
memberIndex++;
}
if (deviceInfoSet) {
SetupDiDestroyDeviceInfoList(deviceInfoSet);
}
return retval;
#endif
}

How to enable "Better Performance" on an External USB HD programatically in C/C++

How do I enable "Better Performance" on an External USB HD programatically in C/C++.
Specifically I am talking about the device properties pane in the control panel in Microsoft Windows. That enables a form of OS level write caching.
You need to send the IOCTL_DISK_SET_CACHE_INFORMATION Control Code using DeviceIoControl.
I suggest you use the Dskcache.exe tool to configure the "Power Protected" Write Cache option.
With W2K SP3 MS introduced the "Power Protected" Write Cache Option in additon to the "Write Caching" option.
Basically, to have the FS driver to issue Flush/Write-Through commands you will need to set "Write Caching" option to Enabled and
the "Power Protected" option to Disabled (see more info here: http://support.microsoft.com/?kbid=332023).1
.
1 source
This link Provided by Alex K. is my accepted answer: It deals with the IOCTL_DISK_SET_CACHE_INFORMATION DeviceIoControl()
http://blogs.msdn.com/b/dhawan/archive/2009/10/05/enable-or-disable-enable-write-caching-on-disk-behavior-on-disk.aspx
#define _WIN32_WINNT 0x0503
#include <windows.h>
DISK_CACHE_INFORMATION info;
DISK_CACHE_INFORMATION rinfo;
void main(void)
{
DWORD rr;
HANDLE hDevice;
DWORD err;
DWORD returned;
hDevice = CreateFile("\\\\.\\C:", // drive to open
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
// share mode
NULL, // default security attributes
OPEN_EXISTING, // disposition
FILE_ATTRIBUTE_SYSTEM, // file attributes
NULL); // do not copy file attributes
if(hDevice==INVALID_HANDLE_VALUE)
{
return;
}
rr = DeviceIoControl(hDevice,IOCTL_DISK_GET_CACHE_INFORMATION,NULL,
0,(LPVOID)&info,(DWORD)sizeof(info),(LPDWORD)&returned, (LPOVERLAPPED)NULL);
if (!rr)
{
err = GetLastError();
return;
}
info.WriteCacheEnabled = true;
info.ReadCacheEnabled = false;
info.DisablePrefetchTransferLength = 1;
rr = DeviceIoControl(hDevice,IOCTL_DISK_SET_CACHE_INFORMATION,(LPVOID)&info,(DWORD)sizeof(info),
NULL,0,(LPDWORD)&returned,(LPOVERLAPPED)NULL);
if (!rr)
{
err = GetLastError();
return;
}
rr = DeviceIoControl(hDevice,IOCTL_DISK_GET_CACHE_INFORMATION,NULL,0,
(LPVOID)&rinfo,(DWORD)sizeof(rinfo),(LPDWORD)&returned,(LPOVERLAPPED)NULL);
if (!rr)
{
err = GetLastError();
return;
}
CloseHandle(hDevice);
}
Old Information:
Windows 2K did contain a "Power Protected" Write Cache Option, but it was never carried over to Windows XP. Which makes a comment about using Dskcache.exe invalid. Was "Power Protected Mode" ever put back into e.g. Windows Vista? I do not know.

Sending a specific SCSI command to a SCSI device in Windows

Does windows have specific interface by which I can send a specific scsi command such Inquiry to scsi device ? I searched the net, found passing reference to SCSI Pass Through interface. But Its very Vague.
Is there any documentation for that API on how to use it??
#include <iostream>
#include <windows.h>
#include <winioctl.h>
#define ULONG_PTR ULONG
#include <ntddscsi.h> //from SDK
#include <spti.h> //from DDK
using namespace std;
int demo()
{
HANDLE hDisk;
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptdwb;
ULONG length = 0;
DWORD bytesReturn;
BYTE bufDataRead[64*1024+10];
int iRet;
hDisk = CreateFile(path,GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,OPEN_EXISTING,0,NULL
);
if (hDisk ==INVALID_HANDLE_VALUE) {
return 0;
}
ZeroMemory(&sptdwb, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER));
sptdwb.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
sptdwb.sptd.PathId = 0;
sptdwb.sptd.TargetId = 1;
sptdwb.sptd.Lun = 0;
sptdwb.sptd.CdbLength = 6;
sptdwb.sptd.DataIn = SCSI_IOCTL_DATA_IN;
sptdwb.sptd.SenseInfoLength = 24;
sptdwb.sptd.DataTransferLength = 8;
sptdwb.sptd.TimeOutValue = 2;
sptdwb.sptd.DataBuffer = bufDataRead;
sptdwb.sptd.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER,ucSenseBuf);
sptdwb.sptd.Cdb[0] = 0x12;
sptdwb.sptd.Cdb[1] = 0x00;
sptdwb.sptd.Cdb[2] = 0x00;
sptdwb.sptd.Cdb[3] = 0x00;
sptdwb.sptd.Cdb[4] = 0xFF;
sptdwb.sptd.Cdb[5] = 0x00;
length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
iRet = DeviceIoControl(hDisk,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
&sptdwb,
length,
&sptdwb,
length,
&bytesReturn,
NULL);
if (0 == iRet) {
printf("inquiry fail");
return 0;
} else {
//Check returned data in sptdwb.sptd.DataBuffer.
}
return 0;
}
SCSI covers a vast amount of ground. Are you talking to a CD/DVD/Disk/Tape/Scanner or what.
For CD/DVD the best (and only) free reference for setup/read/write commands are to be found here: http://www.t10.org/drafts.htm
Re SPTI, there is some very basic documentation in the old 'Programmers guide to SCSI'. There is an article on an ASPI -> SPTI converter that can be found on the DDJ web site.
Bear in mind that SPTI is simply an API, it imposes nor knows anything about SCSI message content or format.
Brian Sawert, Addison Wesley 1998.
You can send SCSI commands to the SCSI Port driver by sending it an IRP_MJ_SCSI IRP, see http://msdn.microsoft.com/en-us/library/ff565387(VS.85).aspx. However, you will have to construct the SCSI CBD yourself, and I have yet to find a document which describes it.
Anymore, the SCSI commands are broken down into a number of specs. The INQUIRY command is in the SPC spec, while device type specific commands are broken down into several specs (i.e. block, ses, ...).

Resources