Archive for October, 2014

Perspective

This Article is designated for experienced C++ developers, It is assumed that the reader has basic experience with windows OS driver development.

Introduction

WinUSB is Microsoft user-mode framework for communicating with USB devices.

The main aim of WinUSB is to reduce development cost by exposing a user-mode application level USB API, this, save the time and effort of developing a USB Driver and enables the developer to focus on application logic development.

As of Windows 8.1, Comparing to a fully fetched USB Kernel mode driver, WinUSB has few limitations, one, is the fact that while a USB device might expose multiple Configurations to choose from, WinUSB supports only the default Configuration ( the first one ).

This Article describe an approach enabling WinUSB to use any of the Configurations exposed by the USB device.

Few words about USB

“Universal Serial Bus (USB) is an industry standard developed in the mid-1990s that defines the cables, connectors and communications protocols used in a bus for connection, communication, and power supply between computers and electronic devices.” ( Commenting Wikipedia )

A USB device can expose multiple configurations, at any given moment only a single configuration can be used by the application communicating with the USB device.

While setting up a USB connection, the SW selects the configuration to be used for device communication.

Use this link for a through USB explanation.

Few words about windows drivers

In most cases, a driver is a module ( DLL ) implementing functionality related with operating a specialized HW device, In windows, multiple drivers can be used to operate a single HW device, each, implement a subset of the requiered functionality,
these drivers are grouped in stacks where each IO request is executed from the top most driver to the one bellow, each driver, in it’s turn can modify the IO being executed and execute additional logic, In addition there is a special type of drivers called filter drivers, these are used to ~filter~ the IOs goin in and out of another driver, we will get deeper into details regarding this type of drivers later on.

With USB, there are several types of drivers, The USB host-controller driver, Hub driver and more are provided by the OS, a specialized USB device requires a function driver on top of the Host-Controller & Hub drivers, these implement the specific USB HW Bus & Hub logic, the specialized USB driver will then need to implement only the functionality related with the specific HW device.

A detailed explanation of windows USB architecture can be found in this link.

The WinUSB.sys driver

Commenting msdn, “Windows USB (WinUSB) is a generic driver for USB devices that was developed concurrently with the Windows Driver Frameworks (WDF) for Windows XP with SP2. The WinUSB architecture consists of a kernel-mode driver (Winusb.sys) and a user-mode dynamic link library (Winusb.dll) that exposes WinUSB functions. By using these functions, you can manage USB devices with user-mode software.”

USB 2.0 Driver stack

Implementation

We are going to make WinUSB think it is selecting the first/default configuration while under the hood we will switch the default configuration with the one desired, to achieve that,
we will implement a lower-level filter driver, one that will intercept all URBs sent by WinUSB downwards,
and, when needed, change them.

We connect to the default device queue and in specific intercept the URB_FUNCTION_CONTROL_TRANSFER and URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE URBs where we change the configuration index from the default one requested by WinUSB to the one we want

switch (pUrb->UrbHeader.Function) {
	case URB_FUNCTION_CONTROL_TRANSFER:
		if ((USB_REQUEST_GET_DESCRIPTOR != pUrb->UrbControlTransfer.SetupPacket[1]) || 
		    (USB_CONFIGURATION_DESCRIPTOR_TYPE!=pUrb->UrbControlTransfer.SetupPacket[3]))
			break;
		if (USB_DEFAULT_CFG_INDEX == pUrb->UrbControlTransfer.SetupPacket[2])
			pUrb->UrbControlTransfer.SetupPacket[2] = m_btZeroCfgSwitch;
		else if (m_btZeroCfgSwitch == pUrb->UrbControlTransfer.SetupPacket[2])
			pUrb->UrbControlTransfer.SetupPacket[2] = USB_DEFAULT_CFG_INDEX;
		break;
	case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
		if(USB_CONFIGURATION_DESCRIPTOR_TYPE!=pUrb->UrbControlDescriptorRequest.DescriptorType)
			return TRUE;// This is not what we are looking for...
		if (USB_DEFAULT_CFG_INDEX == pUrb->UrbControlDescriptorRequest.Index) {
			pUrb->UrbControlDescriptorRequest.Index = m_btZeroCfgSwitch;
		} else if (m_btZeroCfgSwitch == pUrb->UrbControlDescriptorRequest.Index) {
			pUrb->UrbControlDescriptorRequest.Index = USB_DEFAULT_CFG_INDEX;
		}
		break;
}

the Figure above show what is needed to switch the default configuration with the desired value, m_btZeroCfgSwitch indicate the configuration index to replace the default with, the two control requests we need to modify
are URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE and USB_REQUEST_GET_DESCRIPTOR where we specifically intercept the extraction of the configuration descriptor.

SetupPacket format is in accordance to Table 9-3 of the USB_3_1 spec shown bellow, for configuration query, the first byte of wValue indicate the index, this is what
we modify to make WinUSB use the configuration we want.

Coding & Concepts

The driver is implemented as a C++ KDMF Lower Filter driver, it consists of a simple C++ class for the device called UsbCfgDevice where most of the logic is implemented and a simple user-mode WinUSB application used to interact with the device.

The WinUSB Use-mode app is using WinUSB API to open the device and verify that the id of the selected configuration is equal to the id of the configuration at index zero ( the one we have overridden ), while this is true for any WinUSB application, when the default CFG is overriden, the value of the selected configuration will be different than one ( assuming the CFGs are numbered incrementally by the HW device ).

Tracing is implemented using the built-in WPP framework, To monitor logging on the debugee create a monitoring session using ‘traceview.exe’ and the driver PDB ( make sure to set logging level to verbose ).

The Driver is HW specific, the HW for which it is installed ( along with WinUSB ) is defined in the associated INF ( Explained next ).

Driver Installation

Driver installation is done using a standard INF file indicating the files and registry entries to be updated on the operating system.

Of specific importance are the following INF sections:

            [Version]
            ...
            Class     = USBDevice
            ClassGUID = {88BAE032-5A81-49f0-BC3D-A4FF138216D6}
            ...

The Class and ClassGUID indicate the type of the driver being installed in accordance with a system-defined device setup classes

            [Standard.NT$ARCH$]
            %DeviceName%=USB_Install, USB\VID_nnnn&PID_nnnn

Defines the HW ( using the Vendor Id and Product Id ) for which the driver is to be installed, this section can include multiple HW definitions, each having a specialized VID and PID ( ‘nnnn’ is replaced with the actual ids )

            [UsbCfgCtrl_AddReg]
            HKR,,"LowerFilters",0x00010000,"UsbCfgCtrl"
            HKR,,"DefaultCfgIndex",0x00010001,"1"

Defines the driver as a lower filter driver ( installed bellow winusb.sys on the driver stack ), and, the Configuration index we want to replace the default with.

Prerequisites

References

www.usb.org USB_3_1 spec USB Architecture
WinUSB Lower filter drivers USB Request Blocks
URB Header Structure INF Files Using traceview.exe
Source Code