USB Communications Device Class on 64-bit Windows

There is a class of device specified by the USB-IF as a Communication Device, this class was meant to to include devices such as modems, and network adapters. This specification is detailed as the Communications Device Class (CDC).

Ever since USB has replaced RS-232 as the predominant method of peripheral connection there has been a need to emulate serial ports over USB. This class has been hijacked somewhat by other devices that want to expose a serial-like interface to software.

This need is born out of a few requirements, ease of development, ease of use, and backwards compatibility.

Ease of Development

In my eyes one problem with USB is that to communicate with any device requires a driver. Up until recently this required a kernel mode driver. Drivers, kernel mode drivers in particular, are difficult to write and to debug, this can add to the manufacturer's costs as they try to bring a product to market which uses USB.

Beside more specialized devices such as webcams, audio interfaces, mass storage devices there are a very large number of devices that simply need a moderate speed, single channel connection to the PC. These devices are especially suited to implementing the CDC.

Ease of Use

Related to the problems of a custom kernel mode driver is the end-user use case issues. When each manufacturer implements their own driver they also implement their own method of communicating with that driver. This can be via any of an infinite number of methods from shared memory, to a virtual filesystem.

When the device implements the CDC then the device is easily available via a very familiar and well supported serial interface often referred to as a "Virtual Com Port" (VCP). Support for the CDC is usually built into the operating system and the user can begin using the device with little or no driver and software installation.

Backwards Compatibility

Many devices began life as a serial connected device because most embedded processors have built-in UARTs. As these devices have grown with the PC industry they have been required to scale up their interfaces to USB in order to be compatible with contemporary computers. A lot of these devices accomplish this by using a simple Serial-to-USB transceiver chip embedded into the design. I have personally had success converting embedded devices using the FTDI line of products though they do not implement the CDC.

On the other side of the coin is the legacy software that has been written to communicate with devices over the PC's serial ports. When using a CDC device this software can easily continue to function unmodified with devices that have migrated to USB regardless of whether it is using a native USB stack or a Serial-to-USB transceiver.

Downsides

Of course implementing a CDC device is a tradeoff, like a lot of decisions that you have to make in the world of computers. Some of the downsides are the features that make USB so attractive in the first place. One such feature is multiple communication endpoints; USB devices can expose multiple endpoints to allow software on the PC to communicate with multiple subsystems on the device using a single USB link. Another is interrupt endpoints, these endpoints let the USB device notify software on the PC of time-critical events such as button presses and sensor data within a well-known, and small window of latency.

I am currently working on a cross-platform library to expose USB device endpoints in a fashion similar to TCP ports, I am hoping that this will bring the ease of use of CDC and the power of more advanced USB features into one easy to use library.

Installing a CDC Device on Windows

And now the meat of this post. How do we get CDC devices to show up as a VCP on the system?

Windows does have a built-in driver that exposes a VCP for CDC devices, this driver is implemented in the file called usbser.sys. In Windows the driver implementation is only half of the recipe needed for installation. The other half of this recipe is called an INF file, it's job is to describe to Windows how to go about installing the driver and what devices it applies to.

For usbser.sys the INF file is fairly simple and can easily be modified to support your device. You will need to know two things about your device, the Vendor ID (VID) and the Product ID (PID). Every USB device in the world has these numbers and they combine to form an identifier that is unique to that specific device. You can find these numbers by opening device manager and double clicking on your "Unknown device" and selecting the "Details" tab.

[caption id="attachment_120" align="alignnone" width="414" caption="Vendor and Product IDs"]Vendor and Product IDs[/caption]

In this image you can see a device with VID 03EB and PID 2310, as you'll see in the INF file we need the whole string "USB\VID03EB&PID2310" to identify the device.

Here is an INF file that installs the usbser.sys driver for this device:

[Version]
Signature="$Windows NT$"
Class=Ports
ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
Provider=%ProviderName%
DriverVer=10/15/2009,1.0.0.0

[MANUFACTURER]
%ProviderName%=DeviceList, NTx86, NTamd64

[DeviceList.NTx86]
%AtmelEVK1105CDC%=DriverInstall,USB\VID_03eb&PID_2310

[DeviceList.NTamd64]
%AtmelEVK1105CDC%=DriverInstall,USB\VID_03eb&PID_2310

[DriverInstall]
include=mdmcpq.inf
CopyFiles=FakeModemCopyFileSection
AddReg=LowerFilterAddReg,SerialPropPageAddReg

[DriverInstall.Services]
include = mdmcpq.inf
AddService = usbser, 0x00000002, LowerFilter_Service_Inst

; This adds the serial port property tab to the device properties dialog
[SerialPropPageAddReg]
HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"

[Strings]
ProviderName = "CDC Driver"
AtmelEVK1105CDC = "Atmel EVK1105 Virtual Com Port

To use this with your own device make an entry under the [DeviceList.NTx86] and [DeviceList.NTamd64] sections but using your own alias between the %...%, and place your device ID string to the right of the equals, while leaving the rest of the line intact. Then also simply make an entry in the [Strings] section with your alias and a string that you want to show as the name of the device in device manager.

Save the INF file and in device manager right-click on the device, choose "Update Driver Software..", then select "Browse my computer for driver software", navigate to the directory where you saved the INF and click Next.

[caption id="attachment_126" align="alignnone" width="628" caption="CDC Driver Install"]CDC Driver Install[/caption]

Here is a screenshot of the device in device manager with the driver installed.

[caption id="attachment_125" align="alignnone" width="437" caption="Installed Device"]Installed Device[/caption]

You will get a warning that the driver is not signed, well, the fact is that the driver itself is signed, but the INF and driver as a package is not signed. I'll detail in another post the differences, what they mean to 64-bit Windows, and how to actually sign the package.

Speaking of 64-bit, this INF works perfectly well on any 32 or 64-bit Windows from XP up to Windows 7 for any device that implements the CDC.