Lessons Learned: Developing a production-quality USB stack

Developing the stack was a moderately sized
project by modern software development standards. We started with an
existing embedded USB 2.0 host stack with about 150K lines of C. About
460K lines were added - about 180K lines to support Windows, about 80K
lines to support USB 3.0 (in general), about 40K lines to support the
xHCI host controller architecture, and about 180K lines of test code
(not part of the shipping product). The development phase of the project
spanned the period from the Fall of 2008 to the beginning of 2011.
Production testing and support then continued as customers deployed
their products.

Click on image to enlarge.

Early in the project, we made a fundamental
decision that increased the complexity of the project substantially. We
divided code into two parts: a part that handled all USB-specific
operations, and a portion that was specific to Windows. We enforced this
decision by forbidding the USB-specific modules from using any
Windows-specific APIs or header files.

In practice, this meant
that every API call from the client drivers required that parameters be
translated from the Windows API format to the corresponding format of
our portable code. Since our portable stack is architected somewhat
differently than the Windows stack, this translation was sometimes
non-trivial. This can be seen from the number of lines of code in the
final product. There are 276K lines of portable code for USB 3.0
support, plus 180K lines of code for Windows. In other words, 40% of the
code was strictly for supporting Windows and performing API
translations.

The decision to create the stack from a portable
core plus wrappers for Windows was partially motivated by business
considerations. The product life of a USB 3.0 host stack for Windows was
quite limited, and ended with the introduction of Windows 8. MCCI
wanted to be able to reuse as much of the work effort as possible.
Because of this architecture, we’re already reusing the portable USB
code in our embedded system products, and somewhat to our surprise,
we’re reusing the Windows wrappers as well.

As expected, test
costs were substantial. Although 85% of the final code was in place by
the end of 2010, about 40% of the entire project cost was spent in 2011.
Most of this expense was for compatibility and interoperability testing
and correcting bugs found in this process. Not all of the bugs were
bugs in our code. Early SuperSpeed-capable devices and hubs had quirks
and deviations from the standard that had to be worked around.

Unusually
for a project of this kind, we are able to compare our results to
similar projects undertaken at about the same time by silicon vendors
and by Microsoft. All of the development teams had the same overall
requirements, but each development team approached the problem in a
different way. This allows us to draw some interesting conclusions about
design approaches.