How to Access a USB Device by Using WinUSB Functions
In this topic you'll learn about how an application can call WinUSB Functions to communicate with a USB device. This topic assumes that you have loaded WinUSB (Winusb.sys) as the device's function driver. WinUSB in the device's kernel-mode stack. This driver is included in Windows in the \Windows\System32\drivers folder. For instructions about installing this driver, see WinUSB (Winusb.sys) Installation.
If you are using Winusb.sys as a USB device's function driver, you can call WinUSB Functions from an application to communicate with the device. These functions, exposed by the user-mode DLL Winusb.dll, simplify the communication process. Instead of constructing device I/O control requests to perform standard USB operations (such as configuring the device, sending control requests, and transferring data to or from the device), applications call the equivalent WinUSB function.
Winusb.dll uses the application-supplied data to construct the appropriate device I/O control request, and then sends the request to Winusb.sys for processing. To communicate with the USB stack, the WinUSB function calls the DeviceIoControl function with the appropriate IOCTL that correlates to the application's request. When the request is complete, the WinUSB function passes any information returned by Winusb.sys (such as data from a read request) back to the calling process. If the call to DeviceIoControl is successful, it returns a nonzero value. If the call fails or is pending (not processed immediately), DeviceIoControl returns a zero value. In case of an error, the application can call GetLastError for a more detailed error message.
It is simpler to use WinUSB functions to communicate with a device than it is to implement a driver. However, note the following limitations:
- WinUSB functions allow one application at a time to communicate with the device. If you require more than one application to communicate concurrently with a device, you must implement a function driver.
- WinUSB functions do not support streaming data to or from isochronous endpoints. Isochronous transfers require a kernel-mode function driver.
- WinUSB functions do not support devices that already have kernel-mode support. Examples of such devices include modems and network adapters, which are supported by the telephony API (TAPI) and NDIS, respectively.
- For multifunction devices, you can use the device's INF file to specify either an in-box kernel-mode driver or Winusb.sys for each USB function separately. However, you can specify only one of these options for a particular function, not both.
You can install Winusb.sys as the function driver for a function in a USB composite device. This requires certain hardware ID modifications in the INF. For more information, see "Writing an .Inf File for WinUSB Installation" in WinUSB (Winusb.sys) Installation.
Winusb.sys is also a key part of the link between a UMDF function driver and the associated device. Winusb.sys is installed in the device's kernel-mode stack as an upper filter driver. An application communicates with the device's UMDF function driver to issue read, write, or device I/O control requests. The driver interacts with the framework, which passes the request to Winusb.sys. Winusb.sys then processes the request and passes it to the protocol drivers and ultimately to the device. Any response returns by the reverse path. Winusb.sys also serves as the device stack's Plug and Play and power owner (PPO).
Note WinUSB functions require Windows XP or later. You can use these functions in your C/C++ application to communicate with your USB device. Microsoft does not provide a managed API for WinUSB.
This topic includes a detailed walkthrough of how to use WinUSB functions to communicate with a USB device that is using Winusb.sys as its function driver.
- Prerequisites
- Set up the Project
- Create a File Handle for the Device
- Get a WinUSB Interface Handle for the Device
- Query the Device for USB Descriptors
- Send Control Transfer to the Default Endpoint
- Issue I/O Requests
- Release the Device Handles
- Implement Main
- Related topics
Prerequisites
The following items apply to this walkthrough:
- This information applies to Windows 7, Windows Server 2008, Windows Vista, and Windows XP versions of Windows.
- You have installed Winusb.sys as the device's function driver. For more information about this process, see WinUSB (Winusb.sys) Installation.
- You are familiar with SetupAPI routines that are used for device enumeration. For more information, see SetupAPI.
- The examples in this topic are based on the OSR USB FX2 Learning Kit device. You can use these examples to extend the procedures to other USB devices. The following illustration shows the layout of the OSR USB FX2 device.

Set up the Project
- Include the following header files in your source file.
// Include Windows headers #include <windows.h> #include <stdio.h> #include <tchar.h> #include <strsafe.h> // Include WinUSB headers #include <winusb.h> #include <Usb100.h> #include <Setupapi.h>
The header files, Winusb.h, Usb100.h, and Setupapi.h are included with the WDK under <install_path>\WINDDK\build_number\inc\ddk.
- Add the following libraries that are linked to your application.
// Linked libraries #pragma comment (lib , "setupapi.lib" ) #pragma comment (lib , "winusb.lib" )
The library Winusb.lib is included with the WDK. The version for Windows XP is located under <install_path>\WINDDK\build_number\lib\wxp\i386. There are separate versions of Winusb.lib for Windows Vista for each supported CPU architecture. They are located under the <install_path>\WINDDK\build_number\lib\wlh folder.
- Declare a constant for the device interface identifier that you provided in the INF file for installing Winusb.sys.
// Constant for {D696BFEB-1734-417d-8A04-86D01071C512} static const GUID OSR_DEVICE_INTERFACE = { 0xd696bfeb, 0x1734, 0x417d, { 0x8a, 0x04, 0x86, 0xd0, 0x10, 0x71, 0xc5, 0x12 } };
Create a File Handle for the Device
To access a USB device, you need a valid file handle for the device. You can create this handle by calling CreateFile. This function requests Winusb.sys for a device handle that an application can use to communicate with the device. CreateFile requires the device path for the device for which to request the handle. To obtain the device path, you need the device interface GUID that you specified in the INF file that was used to install Winusb.sys. By using the SetupAPI routines, you can enumerate all devices in the specified device interface class and retrieve the device path of the device.
Use the following steps to create a file handle for the USB device.
- Call SetupDiGetClassDevs to get a handle to the device information set, an array that contains information about all installed devices that matched the specified device interface class. Each element in the array called a device interface corresponds to a device that is installed and registered with the system. The device interface class is identified by passing the device interface GUID that you defined in the INF file. The function returns an HDEVINFO handle to the device information set.
- Call SetupDiEnumDeviceInterfaces to enumerate the device interfaces in the device information set and obtain information about your device interface.
This call requires the following items:
- An initialized caller-allocated SP_DEVICE_INTERFACE_DATA structure that has its cbSize member set to the size of the structure.
- The HDEVINFO handle from step 1.
- The device interface GUID that you defined in the INF file.
SetupDiEnumDeviceInterfaces looks up the device information set array for the specified index of the device interface and fills the initialized SP_DEVICE_INTERFACE_DATA structure with basic data about the interface.
Note To enumerate all the device interfaces in the device information set, call SetupDiEnumDeviceInterfaces in a loop until the function returns FALSE and the error code for the failure is ERROR_NO_MORE_ITEMS. The ERROR_NO_MORE_ITEMS error code can be retrieved by calling GetLastError. With each iteration, increment the member index.Alternately, you can call SetupDiEnumDeviceInfo that enumerates the device information set and returns information about device interface elements, specified by the index, in a caller-allocated SP_DEVINFO_DATA structure. You can then pass a reference to this structure in the DeviceInfoData parameter of the SetupDiEnumDeviceInterfaces function.
- Call SetupDiGetDeviceInterfaceDetail to get detailed data for the device interface. The information is returned in a SP_DEVICE_INTERFACE_DETAIL_DATA structure.
Because the size of the SP_DEVICE_INTERFACE_DETAIL_DATA structure varies, you need to call SetupDiGetDeviceInterfaceDetail two times. The first call gets the buffer size to allocate for the SP_DEVICE_INTERFACE_DETAIL_DATA structure. The second call fills the allocated buffer with detailed information about the interface.
- Call SetupDiGetDeviceInterfaceDetail with DeviceInterfaceDetailData parameter set to NULL. The function returns the correct buffer size in the requiredlength parameter. This call fails with the ERROR_INSUFFICIENT_BUFFER error code. This error code is expected.
- Allocate memory for a SP_DEVICE_INTERFACE_DETAIL_DATA structure based on the correct buffer size that is retrieved in the requiredlength parameter.
- Call SetupDiGetDeviceInterfaceDetail again and pass it a reference to the initialized structure in the DeviceInterfaceDetailData parameter. When the function returns, the structure is filled with detailed information about the interface. The device path is in the SP_DEVICE_INTERFACE_DETAIL_DATA structure's DevicePath member.
- Call CreateFile to create a file handle for the device. For this step, you need a null-terminated string that contains the device path that was received in the SP_DEVICE_INTERFACE_DETAIL_DATA structure's DevicePath member. Pass the device path to CreateFile to obtain a file handle for the device. Make sure that you set the FILE_FLAG_OVERLAPPED flag because WinUSB depends on this setting.
The following example code creates a file handle that supports synchronous read and write access to the device. For more information about how to open a file handle for asynchronous I/O, see the Remarks section in CreateFile.
BOOL GetDeviceHandle (GUID guidDeviceInterface, PHANDLE hDeviceHandle)
{
if (guidDeviceInterface==GUID_NULL)
{
return FALSE;
}
BOOL bResult = TRUE;
HDEVINFO hDeviceInfo;
SP_DEVINFO_DATA DeviceInfoData;
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA pInterfaceDetailData = NULL;
ULONG requiredLength=0;
LPTSTR lpDevicePath = NULL;
DWORD index = 0;
// Get information about all the installed devices for the specified
// device interface class.
hDeviceInfo = SetupDiGetClassDevs(
&guidDeviceInterface,
NULL,
NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDeviceInfo == INVALID_HANDLE_VALUE)
{
// ERROR
printf("Error SetupDiGetClassDevs: %d.\n", GetLastError());
goto done;
}
//Enumerate all the device interfaces in the device information set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (index = 0; SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData); index++)
{
//Reset for this iteration
if (lpDevicePath)
{
LocalFree(lpDevicePath);
}
if (pInterfaceDetailData)
{
LocalFree(pInterfaceDetailData);
}
deviceInterfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
//Get information about the device interface.
bResult = SetupDiEnumDeviceInterfaces(
hDeviceInfo,
&DeviceInfoData,
&guidDeviceInterface,
0,
&deviceInterfaceData);
// Check if last item
if (GetLastError () == ERROR_NO_MORE_ITEMS)
{
break;
}
//Check for some other error
if (!bResult)
{
printf("Error SetupDiEnumDeviceInterfaces: %d.\n", GetLastError());
goto done;
}
//Interface data is returned in SP_DEVICE_INTERFACE_DETAIL_DATA
//which we need to allocate, so we have to call this function twice.
//First to get the size so that we know how much to allocate
//Second, the actual call with the allocated buffer
bResult = SetupDiGetDeviceInterfaceDetail(
hDeviceInfo,
&deviceInterfaceData,
NULL, 0,
&requiredLength,
NULL);
//Check for some other error
if (!bResult)
{
if ((ERROR_INSUFFICIENT_BUFFER==GetLastError()) && (requiredLength>0))
{
//we got the size, allocate buffer
pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredLength);
if (!pInterfaceDetailData)
{
// ERROR
printf("Error allocating memory for the device detail buffer.\n");
goto done;
}
}
else
{
printf("Error SetupDiEnumDeviceInterfaces: %d.\n", GetLastError());
goto done;
}
}
//get the interface detailed data
pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
//Now call it with the correct size and allocated buffer
bResult = SetupDiGetDeviceInterfaceDetail(
hDeviceInfo,
&deviceInterfaceData,
pInterfaceDetailData,
requiredLength,
NULL,
&DeviceInfoData);
//Check for some other error
if (!bResult)
{
printf("Error SetupDiGetDeviceInterfaceDetail: %d.\n", GetLastError());
goto done;
}
//copy device path
size_t nLength = wcslen (pInterfaceDetailData->DevicePath) + 1;
lpDevicePath = (TCHAR *) LocalAlloc (LPTR, nLength * sizeof(TCHAR));
StringCchCopy(lpDevicePath, nLength, pInterfaceDetailData->DevicePath);
lpDevicePath[nLength-1] = 0;
printf("Device path: %s\n", lpDevicePath);
}
if (!lpDevicePath)
{
//Error.
printf("Error %d.", GetLastError());
goto done;
}
//Open the device
*hDeviceHandle = CreateFile (
lpDevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if (*hDeviceHandle == INVALID_HANDLE_VALUE)
{
//Error.
printf("Error %d.", GetLastError());
goto done;
}
done:
LocalFree(lpDevicePath);
LocalFree(pInterfaceDetailData);
bResult = SetupDiDestroyDeviceInfoList(hDeviceInfo);
return bResult;
}
Get a WinUSB Interface Handle for the Device
Next, using the file handle for the device, create a WinUSB interface handle to the first (default) interface on the device. WinUSB routines use this handle to identify the target device instead of the file handle. To obtain a WinUSB interface handle, call WinUsb_Initialize by passing the file handle created in Create a File Handle for the Device. Use the received handle in the subsequent calls to get information from the device, and to send I/O requests to the device.
The following example code initializes WinUSB with the file handle that was created in the previous step and retrieves a pointer to the WinUSB interface handle for the device interface.
BOOL GetWinUSBHandle(HANDLE hDeviceHandle, PWINUSB_INTERFACE_HANDLE phWinUSBHandle)
{
if (hDeviceHandle == INVALID_HANDLE_VALUE)
{
return FALSE;
}
BOOL bResult = WinUsb_Initialize(hDeviceHandle, phWinUSBHandle);
if(!bResult)
{
//Error.
printf("WinUsb_Initialize Error %d.", GetLastError());
return FALSE;
}
return bResult;
}
Query the Device for USB Descriptors
Next, query the device for USB-specific information such as device speed, interface descriptors, related endpoints, and their pipes. The procedure is similar to the one that USB device drivers use. However, the application completes device queries by calling WinUSB functions instead of the WDF framework libraries or any of the Windows WDM USB client support routines.
The following list shows the WinUSB functions that you can call to get USB-specific information:
- Device descriptor
Call WinUsb_QueryDeviceInformation to request information from the device descriptors for the device. To get the device's speed, set DEVICE_SPEED (0x01) in the InformationType parameter. The function returns LowSpeed (0x01) or HighSpeed (0x03).
- Interface descriptors
Call WinUsb_QueryInterfaceSettings and pass the device's interface handles to obtain the corresponding interface descriptors. The WinUSB interface handle corresponds to the first interface. Some USB devices, such as the OSR Fx2 device, support only one interface without any alternative setting. Therefore, for these devices the AlternateSettingNumber parameter is set to zero and the function is called only one time. WinUsb_QueryInterfaceSettings fills the caller-allocated USB_INTERFACE_DESCRIPTOR structure (passed in the UsbAltInterfaceDescriptor parameter) with information about the interface. For example, the number of endpoints in the interface is set in the bNumEndpoints member of USB_INTERFACE_DESCRIPTOR.
For devices that support multiple interfaces, call WinUsb_GetAssociatedInterface to obtain interface handles for associated interfaces by specifying the alternative settings in the AssociatedInterfaceIndex parameter.
- Endpoints
Call WinUsb_QueryPipe to obtain information about each endpoint on each interface. WinUsb_QueryPipe populates the caller-allocated WINUSB_PIPE_INFORMATION structure with information about the specified endpoint's pipe. The endpoints' pipes are identified by a zero-based index, and must be less than the value in the bNumEndpoints member of the interface descriptor that is retrieved in the previous call to WinUsb_QueryInterfaceSettings. The OSR Fx2 device has one interface that has three endpoints. For this device, the function's AlternateInterfaceNumber parameter is set to 0, and the value of the PipeIndex parameter varies from 0 to 2.
To determine the pipe type, examine the WINUSB_PIPE_INFORMATION structure's PipeInfo member. This member is set to one of the USBD_PIPE_TYPE enumeration values: UsbdPipeTypeControl, UsbdPipeTypeIsochronous, UsbdPipeTypeBulk, or UsbdPipeTypeInterrupt. The OSR USB FX2 device supports an interrupt pipe, a bulk-in pipe, and a bulk-out pipe, so PipeInfo is set to either UsbdPipeTypeInterrupt or UsbdPipeTypeBulk. The UsbdPipeTypeBulk value identifies bulk pipes, but does not provide the pipe's direction. The direction information is encoded in the high bit of the pipe address, which is stored in the WINUSB_PIPE_INFORMATION structure's PipeId member. The simplest way to determine the direction of the pipe is to pass the PipeId value to one of the following macros from Usb100.h:
- The
USB_ENDPOINT_DIRECTION_IN (PipeId)macro returns TRUE if the direction is in. - The
USB_ENDPOINT_DIRECTION_OUT(PipeId)macro returns TRUE if the direction is out.
- The
The following example code gets the speed of the device that is specified by the WinUSB interface handle.
BOOL GetUSBDeviceSpeed(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pDeviceSpeed)
{
if (!pDeviceSpeed || hDeviceHandle==INVALID_HANDLE_VALUE)
{
return FALSE;
}
BOOL bResult = TRUE;
ULONG length = sizeof(UCHAR);
bResult = WinUsb_QueryDeviceInformation(hDeviceHandle, DEVICE_SPEED, &length, pDeviceSpeed);
if(!bResult)
{
printf("Error getting device speed: %d.\n", GetLastError());
goto done;
}
if(*pDeviceSpeed == LowSpeed)
{
printf("Device speed: %d (Low speed).\n", *pDeviceSpeed);
goto done;
}
if(*pDeviceSpeed == FullSpeed)
{
printf("Device speed: %d (Full speed).\n", *pDeviceSpeed);
goto done;
}
if(*pDeviceSpeed == HighSpeed)
{
printf("Device speed: %d (High speed).\n", *pDeviceSpeed);
goto done;
}
done:
return bResult;
}
The following example code queries the various descriptors for the USB device that is specified by the WinUSB interface handle. The example function retrieves the types of supported endpoints and their pipe identifiers. The example stores all three PipeId values for later use.
struct PIPE_ID { UCHAR PipeInId; UCHAR PipeOutId; }; BOOL QueryDeviceEndpoints (WINUSB_INTERFACE_HANDLE hDeviceHandle, PIPE_ID* pipeid) { if (hDeviceHandle==INVALID_HANDLE_VALUE) { return FALSE; } BOOL bResult = TRUE; USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; ZeroMemory(&InterfaceDescriptor, sizeof(USB_INTERFACE_DESCRIPTOR)); WINUSB_PIPE_INFORMATION Pipe; ZeroMemory(&Pipe, sizeof(WINUSB_PIPE_INFORMATION)); bResult = WinUsb_QueryInterfaceSettings(hDeviceHandle, 0, &InterfaceDescriptor); if (bResult) { for (int index = 0; index < InterfaceDescriptor.bNumEndpoints; index++) { bResult = WinUsb_QueryPipe(hDeviceHandle, 0, index, &Pipe); if (bResult) { if (Pipe.PipeType == UsbdPipeTypeControl) { printf("Endpoint index: %d Pipe type: Control Pipe ID: %d.\n", index, Pipe.PipeType, Pipe.PipeId); } if (Pipe.PipeType == UsbdPipeTypeIsochronous) { printf("Endpoint index: %d Pipe type: Isochronous Pipe ID: %d.\n", index, Pipe.PipeType, Pipe.PipeId); } if (Pipe.PipeType == UsbdPipeTypeBulk) { if (USB_ENDPOINT_DIRECTION_IN(Pipe.PipeId)) { printf("Endpoint index: %d Pipe type: Bulk Pipe ID: %c.\n", index, Pipe.PipeType, Pipe.PipeId); pipeid->PipeInId = Pipe.PipeId; } if (USB_ENDPOINT_DIRECTION_OUT(Pipe.PipeId)) { printf("Endpoint index: %d Pipe type: Bulk Pipe ID: %c.\n", index, Pipe.PipeType, Pipe.PipeId); pipeid->PipeOutId = Pipe.PipeId; } } if (Pipe.PipeType == UsbdPipeTypeInterrupt) { printf("Endpoint index: %d Pipe type: Interrupt Pipe ID: %d.\n", index, Pipe.PipeType, Pipe.PipeId); } } else { continue; } } } done: return bResult; }
Send Control Transfer to the Default Endpoint
Next, communicate with the device by issuing control request to the default endpoint.
All USB devices have a default endpoint in addition to the endpoints that are associated with interfaces. The primary purpose of the default endpoint is to provide the host with information that it can use to configure the device. However, devices can also use the default endpoint for device-specific purposes. For example, the OSR USB FX2 device uses the default endpoint to control the light bar and seven-segment digital display.
Control commands consist of an 8-byte setup packet, which includes a request code that specifies the particular request, and an optional data buffer. The request codes and buffer formats are vendor defined. In this example, the application sends data to the device to control the light bar. The code to set the light bar is 0xD8, which is defined for convenience as SET_BARGRAPH_DISPLAY. For this request, the device requires a 1-byte data buffer that specifies which elements should be lit by setting the appropriate bits.
The application can set this through the user interface (UI), such as by providing a set of eight check box controls to specify which elements of the light bar should be lit. The specified elements correspond to the appropriate bits in the buffer. To avoid UI code, the example code in this section sets the bits so that alternate lights get lit up.
Use the following steps to issue a control request.
- Allocate a 1-byte data buffer and load the data into the buffer that specifies the elements that should be lit by setting the appropriate bits.
- Construct a setup packet in a caller-allocated WINUSB_SETUP_PACKET structure. Initialize the members to represent the request type and data as follows:
- The RequestType member specifies request direction. It is set to 0, which indicates host-to-device data transfer. For device-to-host transfers, set RequestType to 1.
- The Request member is set to the vendor-defined code for this request, 0xD8. It is defined for convenience as SET_BARGRAPH_DISPLAY.
- The Length member is set to the size of the data buffer.
- The Index and Value members are not required for this request, so they are set to zero.
- Call WinUsb_ControlTransfer to transmit the request to the default endpoint by passing the device's WinUSB interface handle, the setup packet, and the data buffer. The function receives the number of bytes that were transferred to the device in the LengthTransferred parameter.
The following code example sends a control request to the specified USB device to control the lights on the light bar.
BOOL SendDatatoDefaultEndpoint(WINUSB_INTERFACE_HANDLE hDeviceHandle)
{
if (hDeviceHandle==INVALID_HANDLE_VALUE)
{
return FALSE;
}
BOOL bResult = TRUE;
UCHAR bars = 0;
WINUSB_SETUP_PACKET SetupPacket;
ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET));
ULONG cbSent = 0;
//Set bits to light alternate bars
for (short i = 0; i < 7; i+= 2)
{
bars += 1 << i;
}
//Create the setup packet
SetupPacket.RequestType = 0;
SetupPacket.Request = 0xD8;
SetupPacket.Value = 0;
SetupPacket.Index = 0;
SetupPacket.Length = sizeof(UCHAR);
bResult = WinUsb_ControlTransfer(hDeviceHandle, SetupPacket, &bars, sizeof(UCHAR), &cbSent, 0);
if(!bResult)
{
goto done;
}
printf("Data sent: %d \nActual data transferred: %d.\n", sizeof(bars), cbSent);
done:
return bResult;
}
Issue I/O Requests
Next, send data to the device's bulk-in and bulk-out endpoints that can be used for read and write requests, respectively. On the OSR USB FX2 device, these two endpoints are configured for loopback, so the device moves data from the bulk-in endpoint to the bulk-out endpoint. It does not change the value of the data or add any new data. For loopback configuration, a read request reads the data that was sent by the most recent write request. WinUSB provides the following functions for sending write and read requests:
To send a write request
- Allocate a buffer and fill it with the data that you want to write to the device. There is no limitation on the buffer size if the application does not set RAW_IO as the pipe's policy type. WinUSB divides the buffer into appropriately sized chunks, if necessary. If RAW_IO is set, the size of the buffer is limited by the maximum transfer size supported by WinUSB.
- Call WinUsb_WritePipe to write the buffer to the device. Pass the WinUSB interface handle for the device, the pipe identifier for the bulk-out pipe (as described in the Query the Device for USB Descriptors section of this topic), and the buffer. The function returns the number of bytes that are actually written to the device in the bytesWritten parameter. The Overlapped parameter is set to NULL to request a synchronous operation. To perform an asynchronous write request, set Overlapped to a pointer to an OVERLAPPED structure.
The following code example allocates a string and sends it to the bulk-out endpoint of the device.
BOOL WriteToBulkEndpoint(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG* pcbWritten)
{
if (hDeviceHandle==INVALID_HANDLE_VALUE || !pID || !pcbWritten)
{
return FALSE;
}
BOOL bResult = TRUE;
UCHAR szBuffer[] = "Hello World";
ULONG cbSize = strlen(szBuffer);
ULONG cbSent = 0;
bResult = WinUsb_WritePipe(hDeviceHandle, *pID, szBuffer, cbSize, &cbSent, 0);
if(!bResult)
{
goto done;
}
printf("Wrote to pipe %d: %s \nActual data transferred: %d.\n", *pID, szBuffer, cbSent);
*pcbWritten = cbSent;
done:
return bResult;
}
To send a read request
- Call WinUsb_ReadPipe to read data from the bulk-in endpoint of the device. Pass the WinUSB interface handle of the device, the pipe identifier for the bulk-in endpoint, and an appropriately sized empty buffer. When the function returns, the buffer contains the data that was read from the device. The number of bytes that were read is returned in the function's bytesRead parameter. For read requests, the buffer must be a multiple of the maximum packet size.
The following code example reads data from the bulk-in endpoint of the device.
BOOL ReadFromBulkEndpoint(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG cbSize)
{
if (hDeviceHandle==INVALID_HANDLE_VALUE)
{
return FALSE;
}
BOOL bResult = TRUE;
UCHAR* szBuffer = (UCHAR*)LocalAlloc(LPTR, sizeof(UCHAR)*cbSize);
ULONG cbRead = 0;
bResult = WinUsb_ReadPipe(hDeviceHandle, *pID, szBuffer, cbSize, &cbRead, 0);
if(!bResult)
{
goto done;
}
printf("Read from pipe %d: %s \nActual data read: %d.\n", *pID, szBuffer, cbRead);
done:
LocalFree(szBuffer);
return bResult;
}
Release the Device Handles
After you have completed all the required calls to the device, release the file handle and the WinUSB interface handle for the device. For this, call the following functions:
- CloseHandle to release the handle that was created by CreateFile, as described in the Create a File Handle for the Device section of this walkthrough.
- WinUsb_Free to release the WinUSB interface handle for the device, which is returned by WinUsb_Initialize.
Implement Main
The following code example shows the main function of your console application.
int _tmain(int argc, _TCHAR* argv[]) { GUID guidDeviceInterface = OSR_DEVICE_INTERFACE; //in the INF file BOOL bResult = TRUE; PIPE_ID PipeID; HANDLE hDeviceHandle = INVALID_HANDLE_VALUE; WINUSB_INTERFACE_HANDLE hWinUSBHandle = INVALID_HANDLE_VALUE; UCHAR DeviceSpeed; ULONG cbSize = 0; bResult = GetDeviceHandle(guidDeviceInterface, &hDeviceHandle); if(!bResult) { goto done; } bResult = GetWinUSBHandle(hDeviceHandle, &hWinUSBHandle); if(!bResult) { goto done; } bResult = GetUSBDeviceSpeed(hWinUSBHandle, &DeviceSpeed); if(!bResult) { goto done; } bResult = QueryDeviceEndpoints(hWinUSBHandle, &PipeID); if(!bResult) { goto done; } bResult = SendDatatoDefaultEndpoint(hWinUSBHandle); if(!bResult) { goto done; } bResult = WriteToBulkEndpoint(hWinUSBHandle, &PipeID.PipeOutId, &cbSize); if(!bResult) { goto done; } bResult = ReadFromBulkEndpoint(hWinUSBHandle, &PipeID.PipeInId, cbSize); if(!bResult) { goto done; } system("PAUSE"); done: CloseHandle(hDeviceHandle); WinUsb_Free(hWinUSBHandle); return 0; }
Related topics
- WinUSB
- WinUSB Architecture and Modules
- Choosing a driver model for developing a USB client driver
- WinUSB (Winusb.sys) Installation
- WinUSB Functions for Pipe Policy Modification
- WinUSB Power Management
- WinUSB Functions
Send comments about this topic to Microsoft
Build date: 1/21/2013