usbcomplete_chapter10
usbcomplete_chapter10
USB Complete
Everything You Need
to Develop Custom USB Peripherals
by Jan Axelson
For more information about the book, code examples, and links to other USB
information and tools, visit Lakeview Research on the web:
www.lvr.com
ISBN 0-9650819-3-1
copyright 1999 by Jan Axelson. All rights reserved.
You may distribute this work (USB Complete, Chapter 10) if you agree to distribute it in
full and unchanged and agree to charge no fee for such distribution with the exception of
reasonable media charges.
How the Host Communicates
10
(as with the keyboard and mouse). The device can be a standard peripheral
type or not, including one-of-a-kind, custom devices.
send data to printers and the MSComm control to communicate with serial
ports. The controls provide a simpler and more bulletproof programming
interface for setting parameters and exchanging data. The underlying code
within the control will likely use API functions to communicate with device
drivers, but using the control insulates application programmers from deal-
ing with the sometimes arcane details of the API calls. However, Visual Basic
doesn’t include a control for generic access to generic USB devices.
Third-party vendors offer controls for specialized tasks such as communicat-
ing with bar-code scanners and other data-acquisition devices. A vendor
may offer a control for communicating with a specific USB peripheral, and
controls for generic USB communications may appear eventually as well.
On the hardware side of the communication, some device drivers are mono-
lithic drivers that handle everything, from communicating with applications
to reading and writing to the ports or memory addresses that connect to the
device’s hardware. Other drivers, including the Windows drivers for USB
devices, use a layered driver model, where each driver in a series performs a
portion of the communication. The top layer communicates with applica-
tions, the bottom layer communicates with the hardware, and in between
there may be one or more additional layers. The layered driver model is
more complicated as a whole, but it actually simplifies the job of writing
drivers because devices can share code for tasks they have in common. The
drivers that handle communications with the system’s USB hardware are
built into Windows 98, so peripheral vendors don’t have to provide these.
Custom Devices
Some peripherals are custom devices intended for use only with specific
applications. One example is the development boards for USB chips, which
are designed for use with a vendor’s monitor application. Other examples are
data-acquisition units, motor controllers, and test instruments. Windows
has no knowledge of these specialized devices, so it has no built-in drivers
for them.
However, just because Windows doesn’t know about a device doesn’t mean
that applications can’t access it. There are several options for communicating
with custom devices, not all requiring a custom driver.
These are the options for drivers for any device under Windows 98:
• A generic driver suitable for communicating with a variety of devices.
Example device: a test instrument whose host uses the bulkusb.sys driver
included in the Windows 98 DDK to receive data in bulk transfers.
• A class driver and a mini-driver that supports the device’s USB interface.
Example device: an HID-class mouse, which uses Windows’ HID-class
driver along with the HID mini-driver that enables the class driver to
communicate with the system’s USB drivers.
• A custom driver written specifically for the device. Example device: a
data-acquisition unit with a driver that defines a series of control requests
that applications can use to configure the unit and read data from it. The
custom driver may be adapted from a generic driver.
Figure 10-1: Windows 98 uses a layered driver model, with separate drivers for
devices and the buses they connect to.
In kernel mode, the code has unrestricted access to system resources, includ-
ing the ability to execute memory-management instructions and control
access to I/O ports. On Pentiums and other x86 processors, kernel mode
corresponds to the CPU’s Ring 0 mode. Figure 10-1 shows the major com-
ponents of user and kernel modes in a USB communication.
Applications and drivers each use their own language to communicate with
the operating system. Applications use Win32 API functions. Drivers com-
municate with each other using structures called I/O request packets (IRPs).
Windows defines a set of IRPs that drivers can use. Each IRP requests or
carries out a single input or output action. A device driver for a USB device
uses IRPs to pass communications to and from the bus drivers that handle
USB communications. The bus drivers in turn use IRPs to pass communica-
tions to and from drivers that manage aspects of communications closer to
the bus. The final bus driver in the series communicates directly with the
hardware. The bus drivers are included with Windows and require no pro-
gramming by applications programmers or device-driver writers.
The USB bus drivers included with Windows 98 are WDM drivers.
Although Windows 98 continues to support VxDs (as well as drivers con-
tained in DLLs), USB devices must use WDM device drivers because their
drivers must communicate with the system’s bus drivers.
The Win32 Driver Model isn’t completely new; it’s mostly a combination of
what was available in Windows 95 and NT. A WDM driver is an NT ker-
nel-mode driver with the addition of Windows 95’s Plug-and-Play and
power-management features. The final editions of Windows 95 (versions
OSR 2.1 and higher) had some support for WDM drivers as well. These
editions weren’t available to retail customers, but were available only to ven-
dors who installed the software on the computers they sold. In Windows 98,
the WDM support was much expanded and improved.
How can two different operating systems, which previously required very
different drivers, now use the same drivers? Windows 98 includes the driver
ntkern.vxd, which tricks WDM drivers into thinking they’re communicating
with an NT-like operating system. All WDM drivers running on Windows
98 require this driver, which is included with Windows 98.
Programming Languages
Application programmers have a choice in programming languages, includ-
ing Visual Basic, Delphi, and Visual C++. To write a USB device driver,
however, you need a tool that is capable of compiling a WDM driver, and
this means using Visual C++.
Layered Drivers
USB communications use a layered driver model, where each layer handles a
piece of the communication process. Dividing communications into layers
is efficient because it enables different devices that have some tasks in com-
mon to use the same driver for those tasks. For example, all kinds of devices
may connect to the USB, so it makes sense to have one set of drivers, acces-
sible to all and included in the operating system, to handle the USB-specific
communications. The alternative would be to have each device driver com-
municate directly with the USB hardware, with much duplication of effort.
Figure 10-2: USB communications use a host controller driver, class driver, hub
driver, and a device driver that may consist of one or more files.
class drivers for HIDs and other common peripherals. (These driver classes
are distinct from the USB’s device classes, although HIDs are considered a
class in both.) The class driver handles functions that are common to all
peripherals in the class.
When a device or subclass has requirements beyond what the class driver
handles, a mini-driver can add the needed capabilities. For example, not all
HIDs have a USB interface, so Windows provides a separate mini-driver
that enables the HID driver to communicate with the USB subsystem when
needed.
Preliminary Requirements
Before an application can communicate with a device, several things must
happen. The device must be attached to the bus. Windows must enumerate
the device and identify the driver for the device. And the application that
will access the device must obtain a handle that identifies the device and
enables communications with it.
Plugging in the cable attaches the device. Windows handles enumeration
automatically when it’s notified of the device’s attachment, as described in
Chapter 5. To identify which driver to use, Windows compares the retrieved
descriptors with the information in its INF files, as described in Chapter 11.
The handle is a unique identifier that Windows assigns to the device. An
application gets the handle by calling the CreateFile API function with a
symbolic link that identifies the device.
Some drivers explicitly define a symbolic link for each device they control.
For example, Cypress’ ezusb.sys driver identifies the first EZ-USB chip as
ezusb-0. If there are additional EZ-USBs, the driver identifies them as
ezusb-1, ezusb-2, and so on up.
Other drivers use a newer method supported by Windows, where the sym-
bolic link contains a globally unique identifier (GUID). The GUID is a
128-bit number that uniquely identifies an object, which may be any system
class, interface, or other entity that the software treats as an object.
Windows defines GUIDs for standard objects such as the HID class. For
unique devices, developers can obtain a GUID using the guidgen.exe pro-
gram included with Visual C++. The GUID is then included in the driver
code.
The guidgen program uses a complex algorithm that takes into account a
machine identifier, the date and time, and other factors that make it
extremely unlikely that another device will end up with an identical GUID.
The algorithm was originally defined by the Open Software Foundation.
The standard format for expressing GUIDs divides the GUID into five sets
of hex characters, separated by hyphens. This is the GUID for the HID
class:
745a17a0-74d3-11d0-b6fe-00a0c90f57da
Applications can use API calls to retrieve class and device GUIDs from the
operating system.
The Response
Many communications will require a response, which may contain data sent
in response to the request or just a packet with a handshake code. This
information travels back to the host in reverse order: through the device’s
hub, onto the bus, and to the PC’s hardware and software. A device driver
may pass a response on to an application, which may display the result or
take other action.
Ending Communications
When an application closes or otherwise decides that it no longer needs to
access the device, it uses the API function CloseHandle to free system
resources.
More Examples
Communications with other USB devices follow a similar pattern, though
there can be differences in how the transfer initiates and in how the device
driver handles communications.
In the above example, the host ignores the device until the user takes an
action. Other examples of a user initiating a transfer are clicking on a USB
drive’s icon to view a disk’s folders in Windows Explorer’s My Computer or
clicking Print in an application to send a file to a USB printer. In each of
these examples, nothing happens until the application requests a communi-
cation and the device driver fills a buffer with data to send or makes a buffer
available for received data.
In some cases, the host continuously sends requests to the device whether or
not an application has requested them. For example, a keyboard driver
causes the host to make periodic requests for keypress data, whether or not
the user has pressed any keys.
The host also sends requests to enumerate devices on system power-up or
device attachment. The device’s hub causes the host to initiate these requests
when the hub notifies the host of the presence of a device. A device can use
the USB’s remote-wakeup feature to initiate a transfer by signaling its hub,
and in turn the host, to request it to resume communications.
As mentioned earlier, some devices use class drivers instead of, or in addition
to, a driver for the specific device. For an HID-class device, applications
may communicate with the system’s HID-class driver, which handles com-
munications for all HIDs. Not all HIDs have a USB interface, but those
that do also use a mini-driver to handle the USB-specific communications.
Some USB devices may use yet another type of driver, called a legacy virtual-
ization driver. To communicate with the keyboard, mouse, and joystick,
Windows 98 uses the virtual device drivers (VxDs) inherited from Windows
95. When one of these peripherals has a USB interface, a legacy virtualiza-
tion driver translates between the device’s HID interface and the VxD. The
legacy virtualization driver is a VxD that knows how to talk to the HID
driver.
someday Windows will have this. But as of Windows 98, the included USB
drivers are limited.
HID Drivers
The first class of USB peripherals suitable for a variety of applications and
fully supported by Windows is the human interface device (HID) class.
Applications can use API calls to identify a device, read and write generic
values, and set and read the states of buttons on the device. The data is for-
matted in reports. The HID class has defined report formats for mice, key-
boards, and joysticks, or you can define your own format.
But an HID doesn’t have to be a standard peripheral type, and it doesn’t
even have to have a human interface. The only requirement is that the
descriptors stored in the device must conform to the requirements of
HID-class descriptors, and the device must send and receive data using
interrupt or control transfers as defined in the HID specification.
The main limitation to HID communications is the available transfer types.
For device-to-host data transfers, HIDs can use interrupt or control trans-
fers. For host-to-device transfers, Windows 98 SE (or any host that complies
with the HID 1.1 or later specification) will use interrupt transfers if an
OUT interrupt pipe is available, or control transfers if not. The original
release of Windows 98 complies only with the HID 1.0 specification and
will use control transfers for all host-to-device transfers.
As Chapter 3 explained, interrupt transfers aren’t the fastest transfer type,
and they don’t have the guaranteed transfer rate of isochronous transfers
(though they do have guaranteed maximum latency). Control transfers have
no guaranteed rate or latency. But even with these limitations, the simplicity
of using the HID functions makes it attractive when the limits are accept-
able.
An alternative to using API functions for accessing HIDs is to use
Microsoft’s DirectX components. DirectX enables control of system hard-
ware, including HIDs. DirectX originated as a tool for game programmers,
but has since expanded to enable accessing any HID. The advantage of
using DirectX is that it provides faster access. Instead of having to poll an
input with ReadFile, you can configure the DirectX software components to
notify an application when data is available to read.
The DirectInput and DirectInput2 components of DirectX enable commu-
nications with HIDs. For Visual-Basic programmers, DirectX version 7
includes support that enables Visual-Basic programmers to use DirectX.
Point-of-Sale Driver
The Point-of-Sale USB driver is another driver that was originally intended
for a specific category of devices, but may be useful to devices outside the
original category. Point-of-sale (POS) devices include bar-code scanners,
displays, receipt printers, and other devices used in sales transactions.
In July 1999, Microsoft released the POSUSB point-of-sale driver for USB
devices. The driver enables host applications to communicate with the
device as if it were connected to a conventional COM port, using Create-
File, ReadFile, and WriteFile. For bidirectional communications, a device
needs one IN endpoint and one OUT endpoint. A device that requires only
one-way communications needs just one IN or OUT endpoint. The end-
points may be configured for bulk or interrupt transfers.
The supporting documentation includes manuals for host application and
firmware programmers. The driver and documentation is available from
Microsoft’s website, and is scheduled to ship with future releases of Win-
dows.
Vendor-supplied Drivers
Another way to communicate with a device is to use a driver supplied by the
chip’s vendor. The ideal is a ready-to-install, general-purpose driver, along
with complete, commented source code in case you want to adapt it for use
with a particular device. The driver should also include documentation that
shows how to use API calls to open a handle to the device and read and write
to it in application code.
But the usefulness of vendor-supplied drivers varies. A driver is less useful if
it turns out to be buggy, doesn’t include the features you need, or has
sketchy documentation that makes it hard to understand and use.
Custom Drivers
The final option is to use a custom driver. Sometimes there is no generic or
vendor driver that includes the transfer types you want to use. Or you may
want to define custom DeviceIOControl codes.
An example of a custom driver is Cypress’ driver for its thermometer appli-
cation in the Starter Kit for the CY7C63001. This driver is written to sup-
port a specific application, rather than for general use. The driver defines
DeviceIOControl codes to get the temperature and button state, set LED
brightness, and read and write to the controller’s RAM and ports. This
driver was written before the release of Windows 98. If the device doesn’t
need to work with hosts running Windows 95, you could instead use the
HID drivers to transfer the information, without having to write a driver.
The EZ-USB is a custom driver as well. The driver supports the vendor-spe-
cific request AnchorLoad, which causes the device to store received firmware
and simulate detaching and reattaching to the bus.
If a chip’s vendor provides source code for a driver, even if you don’t use the
driver as-is, you can modify the code or use portions of it, rather than start-
ing from scratch. Ideally, the source code will include liberal commenting to
help you understand it. It should include instructions that explain how to
compile and install the driver. Even making minor changes or additions to
an existing driver can be difficult if the code isn’t well documented.
Requirements
The minimum requirement for writing a device driver is Microsoft’s Visual
C++, which is capable of compiling WDM drivers. The compiler also
includes a programming environment and a debugger to help during devel-
opment.
Beyond this basic requirement, other tools can help to varying degrees,
including the Windows 98 Device Developer’s Kit (DDK), a subscription to
Microsoft’s Developer’s Network (MSDN), driver toolkits, and advanced
debuggers.
The Windows 98 DDK includes example code and developer-level docu-
mentation for Windows 98. The USB-related documentation includes tuto-
rials on WDM drivers and HIDs and source code for several USB drivers,
including a bulk-transfer driver, an isochronous-transfer driver, a filter
driver, and the usbview utility. The examples can be a useful starting point in
developing your own drivers. You can download the Windows 98 DDK
from Microsoft’s website.
MSDN is Microsoft’s subscription service to massive quantities of docu-
mentation, examples, and developer’s tools for Microsoft products. The top-
ics covered include WDM driver development, USB, and DirectX, with
quarterly updates. There are several levels of subscription that enable you to
get the documentation alone or with varying amounts of Microsoft applica-
tions and development tools. Much of the information and other tools are
also downloadable from Microsoft’s website.
A driver toolkit provides a way to jumpstart driver development by doing as
much of the work for you as possible. Toolkits that support USB drivers are
available from Bluewater Systems and DriverWorks from Compuware
NuMega.
The Libraries
There are two libraries, depending on whether you prefer to program in C
or C++.
C++ programmers can use a library of classes for carrying out the tasks com-
mon to many drivers. For example, the functions of the CUsb class handle
USB-related activities, including finding a device on a system, learning its
capabilities, and transferring data using control, bulk, interrupt, and isoch-
ronous transfers. The CPnPDevice class handles Plug-and-Play functions,
and the CRegistry class handles saving and retrieving information from the
system registry.
Figure 10-3: To create a driver with WinDK’s Wizard, select one of the WinDK
options from Visual C++’s New menu.
C programmers can accomplish the same things using the library of C func-
tions.
Figure 10-4: WinDK’s Wizard queries you about your driver, then does as much
of the work for you as possible.
The documentation file lists each file created by the Wizard and explains its
purpose.
The process is similar if you’re using Compuware NuMega’s DriverWorks.
There’s a Wizard that walks you through the process of defining your driver,
then produces code to match what you’ve specified.
Another tool that most driver developers find essential is a debugger that
enables testing of the driver code using breakpoints, single stepping, and
other standard techniques. The Professional and higher levels of MSDN
subscriptions include the WinDbg debugger. Compuware NuMega’s Soft-
ICE debugger enables debugging of WDM drivers using a single Windows
98 system.